/*	cvsnt lockserver
    Copyright (C) 2004-5 Tony Hoyle and March-Hare Software Ltd

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <config.h>

#ifdef _WIN32
#include <process.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <vector>
#ifndef _WIN32
#include <unistd.h>
#endif

#include <cvsapi.h>
#include <cvstools.h>

#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif

#include "LockService.h"

bool g_bStop = false;
extern bool g_bTestMode;
#ifdef HAVE_MDNS
bool use_mdns = true;
#endif
static int m_port;
static int m_pserver_port;

#ifdef _WIN32
BOOL NotifySCM(DWORD dwState, DWORD dwWin32ExitCode, DWORD dwProgress);
#endif

#if !defined(_WIN32) && !defined(TRUE)
#undef FALSE
#undef TRUE
enum { FALSE, TRUE };
#endif

#ifdef _WIN32
static void thread_proc(void* param)
#else
static void *thread_proc(void* param)
#endif
{
	CSocketIOPtr s = (CSocketIO*)param;
	cvs::string line;
	int v=1;

	s->setnodelay(true);
	s->blocking(true);

#ifdef _WIN32
	if(g_bTestMode)
		printf("Starting thread %08x\n",GetCurrentThreadId());
#endif
	
	OpenLockClient(s);

	while(s->getline(line))
	{
		if(!ParseLockCommand(s,line.c_str()))
			break;
	}

	CloseLockClient(s);

#ifdef _WIN32
	if(g_bTestMode)
		printf("Terminating thread %08x\n",GetCurrentThreadId());
#endif
#ifndef _WIN32
	return NULL;
#endif
}

#ifdef HAVE_MDNS
static void _mdns_thread_proc(void* param)
{
	int nResp;
	CMdnsHelperBase::mdnsType eResp = CMdnsHelperBase::mdnsDefault;
	char serverName[256];

  	if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","ResponderType",nResp))
	{
		switch(nResp)
		{
		case 0:
			if(g_bTestMode)
				printf("mdns disabled\n");
			return;
		case 1:
			eResp = CMdnsHelperBase::mdnsMini;
			if(g_bTestMode)
				printf("Using internal mdns Responder\n");
			break;
		case 2:
			eResp = CMdnsHelperBase::mdnsApple;
			if(g_bTestMode)
				printf("Using Apple mdns Responder\n");
			break;
		case 3:
			eResp = CMdnsHelperBase::mdnsHowl;
			if(g_bTestMode)
				printf("Using Howl mdns Responder\n");
			break;
		default:
			eResp = CMdnsHelperBase::mdnsMini;
			if(g_bTestMode)
				printf("Using internal mdns Responder\n");
			break;
		}
	}
	else
	{
		if(g_bTestMode)
			printf("Using default mdns Responder\n");
	}

	if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","ServerName",serverName,sizeof(serverName)))
	{
		if(!gethostname(serverName,sizeof(serverName)))
		{
			char *p=strchr(serverName,'.');
			if(p) *p='\0';
		}
		else
			strcpy(serverName,"unknown");
	}

	CMdnsHelperBase *mdns = CMdnsHelperBase::Alloc(eResp,CGlobalSettings::GetLibraryDirectory(CGlobalSettings::GLDMdns));

	if(mdns && !mdns->open())
	{
		if(g_bTestMode)
			printf("Publishing mdns records for %s._cvspserver._tcp\n",serverName);
		mdns->publish(serverName,"_cvspserver._tcp",NULL,m_pserver_port,NULL);

		while(!g_bStop)
		{
			mdns->step();
#ifdef _WIN32
			Sleep(10);
#else
			usleep(10);
#endif
		}
		mdns->close();
	}
	else
	{
		if(g_bTestMode)
			printf("mdns initialisation failed\n");
	}
	delete mdns;
}

#ifdef _WIN32
static void mdns_thread_proc(void* param)
#else
static void *mdns_thread_proc(void* param)
#endif
{
	_mdns_thread_proc(param);
#ifndef _WIN32
	return NULL;
#endif
}
#endif

static int start_thread(CSocketIO* sock)
{
#ifdef _WIN32
	_beginthread(thread_proc,0,(void*)sock);
	return 0;
#elif HAVE_PTHREAD_H
	pthread_t         a_thread = 0;
	pthread_create( &a_thread, NULL, thread_proc, 
               (void *)sock);	
	pthread_detach( a_thread );
#else
	thread_proc((void*)sock);
#endif
}

#ifdef HAVE_MDNS
static int start_mdns_thread()
{
#ifdef _WIN32
	_beginthread(mdns_thread_proc,0,NULL);
	return 0;
#elif HAVE_PTHREAD_H
	pthread_t         a_thread = 0;
	pthread_create( &a_thread, NULL, mdns_thread_proc, NULL);	
	pthread_detach( a_thread );
#else
	CServerIo::log(CServerIo::logNotice,"mdns publish not done due to lack of thread support");
	// Don't do mdns
#endif
}
#endif

void run_server(int port, int seq, int local_only)
{
	CSocketIO listen_sock;
    char szLockServer[64];
	int nResp;

	m_port = port;

#ifdef HAVE_MDNS
    if(!CGlobalSettings::GetGlobalValue("cvsnt","PServer","ResponderType",nResp))
		use_mdns = nResp?true:false;
	else
		use_mdns = true;
#endif

	sprintf(szLockServer,"%d",m_port);

	if(g_bTestMode)
	{
		printf("Initialising socket...\n");
	}
#if defined( __HP_aCC ) && defined ( __ia64 )
	if(!listen_sock.create("127.0.0.1",szLockServer,local_only?true:false))
#else
	if(!listen_sock.create(NULL,szLockServer,local_only?true:false))
#endif
	{
		if(g_bTestMode)
			printf("Couldn't create listening socket... %s",listen_sock.error());
		CServerIo::log(CServerIo::logError,"Failed to create listening socket: %s",listen_sock.error());
		return;
	}

#ifdef _WIN32
	if(!g_bTestMode)
		NotifySCM(SERVICE_START_PENDING, 0, seq++);
#endif

	if(g_bTestMode)
			printf("Starting lock server on port %d/tcp...\n",m_port);

	if(!listen_sock.bind())
	{
		if(g_bTestMode)
			printf("Couldn't bind listening socket... %s\n",listen_sock.error());
		CServerIo::log(CServerIo::logError,"Failed to bind listening socket: %s",listen_sock.error());
#ifdef _WIN32
		if(!g_bTestMode)
			NotifySCM(SERVICE_STOPPED,0,1);
#endif
		return;
	}

	g_bStop=FALSE;

	InitServer();

#ifdef _WIN32
	if(!g_bTestMode)
		NotifySCM(SERVICE_START_PENDING, 0, seq++);
#endif

#ifdef HAVE_MDNS
	if(use_mdns)
	{
		m_pserver_port = 2401;
		CGlobalSettings::GetGlobalValue("cvsnt","PServer","PServerPort", m_pserver_port);
		start_mdns_thread();
	}
#endif

	// Process running, wait for closedown
	CServerIo::log(CServerIo::logNotice,"CVS Lock service initialised successfully");
#ifdef _WIN32
	if(!g_bTestMode)
		NotifySCM(SERVICE_RUNNING, 0, 0);
#endif

	while(!g_bStop && listen_sock.accept(1000))
	{
		for(size_t n=0; n<listen_sock.accepted_sockets().size(); n++)
			start_thread(listen_sock.accepted_sockets()[n].Detach());
	}
	g_bStop = true;
	CloseServer();
}


syntax highlighted by Code2HTML, v. 0.9.1