/*--------------------------------------------------------------
 *
 * FILE:			SKeys.c
 *
 * PURPOSE:		The main interface routines between the service
 *					manager and the Serial Keys program.
 *
 * CREATION:		June 1994
 *
 * COPYRIGHT:		Black Diamond Software (C) 1994
 *
 * AUTHOR:			Ronald Moak
 *
 * NOTES:
 *
 * This file, and all others associated with it contains trade secrets
 * and information that is proprietary to Black Diamond Software.
 * It may not be copied copied or distributed to any person or firm
 * without the express written permission of Black Diamond Software.
 * This permission is available only in the form of a Software Source
 * License Agreement.
 *
 * $Header: %Z% %F% %H% %T% %I%
 *
 *--- Includes  ---------------------------------------------------------*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>
#include "vars.h"
#include "w95trace.c"

#define DEFDATA	1
#include "sk_defs.h"
#include "sk_comm.h"
#include "sk_reg.h"
#include "sk_dll.h"
#include "sk_login.h"

#include	"sk_ex.h"

#include	"..\skdll\skeys.h"

#define LONGSTRINGSIZE 1024

#define WAITMAX 0x7FFFFFFF

#define RUNNINGEVENT TEXT("SkeysRunning")

#if defined(DEBUG) && 0
	// give us a long time to startup in case we're debugging
	#define WAITSTARTUP WAITMAX  
#else
	// normal startup time
	#define WAITSTARTUP 60000
#endif


// --- Local Variables  --------------------------------------------------

static SERVICE_STATUS_HANDLE   s_sshStatusHandle;
static SERVICE_STATUS          s_ssStatus;       // current status of the service

PTSTR SERVICENAME = TEXT("SerialKeys");
PTSTR SKEYSUSERINITCMD = TEXT("SKEYS /I");
PTSTR WINLOGONPATH = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
PTSTR USERINIT = TEXT("Userinit");
PTSTR USERINITCMDSEP = TEXT(",");

DWORD   s_dwServiceCommand;

static HANDLE s_hEventServiceRequest = NULL;
static HANDLE s_hEventServiceRequestReady = NULL;
static HANDLE s_hEventServiceTerminate = NULL;
static HANDLE s_hEventSkeysServiceRunning = NULL;

void DoService();
void DoInit();
void InstallUserInit();
BOOL IsSerialKeysAutoStart();


//--- SCM Function Prototypes  ------------------------------------------------
//
// Note:	The following fuctions manage the connection of the service
//			with the Service Contol Manager.

void	PostEventLog(LPTSTR lpszMsg,DWORD Error);

VOID	ServiceMain(DWORD dwArgc, LPTSTR *ppszArgv);

VOID	StopSerialKeys(LPTSTR lpszMsg);
BOOL	ReportStatusToSCMgr(DWORD dwCurrentState,
                            DWORD dwWin32ExitCode,
                            DWORD dwCheckPoint,
                            DWORD dwWaitHint);

LPHANDLER_FUNCTION ServiceCtrl(DWORD dwCtrlCode);

// Service Routines -----------------------------------------------
//
// Note:	The following fuctions manage the internal control of the
//			Service
static void InitReg();
static BOOL	InitService();
static void PauseService();
static void	ProcessService();
static void	ResumeService();
static void	TerminateService();

static void	ProcessLogout(DWORD dwCtrlType);
static BOOL	InstallLogout();
static BOOL	TerminateLogout();
static void EnableService(BOOL fOn);

// CONSIDER - Removing this code.  It only gets executed when SKeys is 
// run from the command line.  When run as a service, ServiceMain is
// called when the service is started.  The sources file pulls in
// winmain from the runtime lib.  DoInit and DoService could also be
// removed with _tWinMain.

int WINAPI _tWinMain(
    HINSTANCE hInstance,	
    HINSTANCE hPrevInstance,
    PTSTR pszCmdLine,	
    int nCmdShow)
{

	if ((TEXT('/') == pszCmdLine[0] || TEXT('-') == pszCmdLine[0]) &&
  		(TEXT('I') == pszCmdLine[1] || TEXT('i') == pszCmdLine[1]))
	{
        DoInit();
	}
	else
	{
		DoService();
	}

	ExitProcess(0);
	return(0);
}

/*---------------------------------------------------------------
 *
 * FUNCTION	DoInit()
 *
 *	TYPE		Global
 *
 * PURPOSE		This function is called to read skeys configuration
 *              from HKEY_CURRENT_USER at logon session startup and
 *              send the information to the service
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
void DoInit()
{
    HANDLE hEventSkeysServiceRunning = NULL;
    PSECURITY_DESCRIPTOR pSD;
    SECURITY_ATTRIBUTES sa;

    pSD = CreateSd(SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE);
    if (pSD)
    {
		sa.nLength = sizeof(sa);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = pSD;

		hEventSkeysServiceRunning = CreateEvent(
			&sa,	// Security
			TRUE,	// Manual reset?
			FALSE,  // initial state - not signaled
			RUNNINGEVENT);  // name

        free(pSD);
    }

    if (NULL != hEventSkeysServiceRunning)
	{
		DWORD dwWait;

		dwWait = WaitForSingleObject(hEventSkeysServiceRunning, 60 * 1000);

		if (WAIT_OBJECT_0 == dwWait)
		{
			SKEY_SystemParametersInfo((UINT)SK_SPI_INITUSER, 0, NULL, 0);
		}
        CloseHandle(hEventSkeysServiceRunning);
    }
	
	return;
}


/*---------------------------------------------------------------
 *
 *		SCM Interface Functions
 *
/*---------------------------------------------------------------
 *
 * FUNCTION	DoService()
 *
 *	TYPE		Global
 *
 * PURPOSE		all DoService does is call StartServiceCtrlDispatcher
 *				to register the main service thread.  When the
 *				API returns, the service has stopped, so exit.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
void DoService()
{
	SERVICE_TABLE_ENTRY dispatchTable[] =
	{
		{ SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
		{ NULL, NULL }
	};
    PSECURITY_DESCRIPTOR pSD;
    SECURITY_ATTRIBUTES sa;

    s_hEventServiceRequest = CreateEvent(
		NULL,	// Security
		FALSE,	// Manual reset?
		FALSE,  // initial state - not signaled
		NULL);  // name

    s_hEventServiceRequestReady = CreateEvent(
		NULL,	// Security
		FALSE,	// Manual reset?
		TRUE,  // initial state - signaled (can accept one request even before ready)
		NULL);  // name

    s_hEventServiceTerminate = CreateEvent(
		NULL,	// Security
		TRUE,	// Manual reset?
		FALSE,  // initial state - not signaled
		NULL);  // name

    s_hEventSkeysServiceRunning = NULL;

    pSD = CreateSd(SYNCHRONIZE|EVENT_MODIFY_STATE|GENERIC_READ|GENERIC_WRITE);
    DBPRINTF(TEXT("DoService:  CreateSd %s\r\n"), (pSD)?TEXT("Succeeded"):TEXT("Failed"));
    if (pSD)
	{
		sa.nLength = sizeof(sa);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = pSD;

		s_hEventSkeysServiceRunning = CreateEvent(
			&sa,	// Security
			TRUE,	// Manual reset?
			FALSE,  // initial state - not signaled
			RUNNINGEVENT);  // name

        free(pSD);
	}

    if (NULL != s_hEventServiceRequest &&
		NULL != s_hEventServiceRequestReady &&
		NULL != s_hEventServiceTerminate &&
		NULL != s_hEventSkeysServiceRunning)
	{
        DBPRINTF(TEXT("DoService:  calling StartServiceCtrlDispatcher... \r\n"));
		if (!StartServiceCtrlDispatcher(dispatchTable))
        {
            DBPRINTF(TEXT("DoService:  StartServiceCtrlDispatcher FAILED\r\n"));
			StopSerialKeys(TEXT("StartServiceCtrlDispatcher failed."));
        }
    }
	else
	{
        DBPRINTF(TEXT("DoService:  Unable to create event %p %p %p %p\r\n"), s_hEventServiceRequest, s_hEventServiceRequestReady, s_hEventServiceTerminate, s_hEventSkeysServiceRunning);
		StopSerialKeys(TEXT("Unable to create event."));
	}
	
	if (NULL != s_hEventServiceRequest)
	{
	    CloseHandle(s_hEventServiceRequest);
	}

	if (NULL != s_hEventServiceRequestReady)
	{
        CloseHandle(s_hEventServiceRequestReady);
	}

	if (NULL != s_hEventServiceTerminate)
	{
        CloseHandle(s_hEventServiceTerminate);
	}

	if (NULL != s_hEventSkeysServiceRunning)
	{
		ResetEvent(s_hEventSkeysServiceRunning);
        CloseHandle(s_hEventSkeysServiceRunning);
	}
}

/*---------------------------------------------------------------
 *
 * FUNCTION	ServiceMain()
 *
 *	TYPE		Global
 *
 * PURPOSE		this function takes care of actually starting the service,
 *				informing the service controller at each step along the way.
 *				After launching the worker thread, it waits on the event
 *				that the worker thread will signal at its termination.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
VOID ServiceMain(DWORD dwArgc, LPTSTR *ppszArgv)
{
	DBPRINTF(TEXT("ServiceMain()\r\n"));

	//
	// SERVICE_STATUS members that don't change
	s_ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	s_ssStatus.dwServiceSpecificExitCode = 0;
    
	//
	// register our service control handler:
	s_sshStatusHandle = RegisterServiceCtrlHandler(
			SERVICENAME,
			(LPHANDLER_FUNCTION) ServiceCtrl);

	if (!s_sshStatusHandle)
	{
		TerminateService(GetLastError());
		return;
	}

	// report the status to Service Control Manager.
	ReportStatusToSCMgr(
				SERVICE_START_PENDING,	// service state
				NO_ERROR,				// exit code
				1,						// checkpoint
				WAITSTARTUP);			// wait hint

#if defined(DEBUG) && 0  /////////////////////////////////////////////////
	// This debug code gives us time to attach a debugger

    {
		int i;

		for (i = 0; i < 180; i++)  // 180 sec = 3 min
		{
			Sleep(1000);  // one second
		}
    }
#endif ////////////////////////////////////////////////////////

	InitReg();
	GetUserValues(REG_DEF);

////EnableService(skNewKey.dwFlags & SERKF_SERIALKEYSON);

	if (!InitService())					// Did Service Initiate successfully?
	{
		TerminateService(GetLastError());		// No Terminate With Error
		return;
	}

	ReportStatusToSCMgr(	// report status to service manager.
		SERVICE_RUNNING,	// service state
		NO_ERROR,			// exit code
		0,					// checkpoint
		0);					// wait hint
	
	SetEvent(s_hEventSkeysServiceRunning);

	ProcessService();					// Process the Service
	TerminateService(0);				// Terminate
	return;
}


BOOL IsSerialKeysAutoStart()
{
	BOOL fAutoStart = FALSE;
	BOOL fOk;

	SC_HANDLE   schService = NULL;
	SC_HANDLE   schSCManager = NULL;

	schSCManager = OpenSCManager(   // Open Service Manager
		NULL,                       // machine (NULL == local)
	    NULL,                       // database (NULL == default)
		MAXIMUM_ALLOWED);

	if (NULL != schSCManager)  // Did Open Service succeed?
	{
	    schService = OpenService(
			    schSCManager ,
			    __TEXT("SerialKeys"),
			    MAXIMUM_ALLOWED);

	    if (NULL != schService)
		{
			BYTE abServiceConfig[1024];
			LPQUERY_SERVICE_CONFIG pqsc = (LPQUERY_SERVICE_CONFIG)abServiceConfig;
			DWORD cbBytesNeeded;

			fOk = QueryServiceConfig(
				schService,
				pqsc,
				sizeof(abServiceConfig),
				&cbBytesNeeded);

			if (fOk)
			{
				fAutoStart = (SERVICE_AUTO_START == pqsc->dwStartType);
			}
	        CloseServiceHandle(schService);
		}
        CloseServiceHandle(schSCManager);
    }

	return fAutoStart;
}


void InstallUserInit()
{
	BOOL fOk = FALSE;
    HKEY  hkey;
	LONG lErr;
	DWORD dwType;
	TCHAR szUserinit[LONGSTRINGSIZE];
	DWORD cbData = sizeof(szUserinit);

	lErr = RegOpenKeyEx(
		HKEY_LOCAL_MACHINE,
        WINLOGONPATH,
		REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        &hkey); 
	
    if (ERROR_SUCCESS == lErr)
	{
		lErr = RegQueryValueEx(
                hkey,
                USERINIT,
                0,
                &dwType,
                (LPBYTE)szUserinit,
                &cbData);
		szUserinit[LONGSTRINGSIZE-1]='\0';

		if (ERROR_SUCCESS == lErr && dwType == REG_SZ)
		{
			// check to see if we are already installed and if we have
			// enough room to install
			// the + 2 allows for the terminating null and for the command seperator char

			if (NULL == _tcsstr(szUserinit, SKEYSUSERINITCMD) &&
				    lstrlen(szUserinit) + lstrlen(SKEYSUSERINITCMD) + 2 < 
					        ARRAY_SIZE(szUserinit))
			{
				lstrcat(szUserinit, USERINITCMDSEP);
				lstrcat(szUserinit, SKEYSUSERINITCMD);

				RegSetValueEx(
					hkey,
					USERINIT,
                    0,
					REG_SZ,
				    (CONST LPBYTE)szUserinit,
				    (lstrlen(szUserinit) + 1) * 
					    sizeof(*szUserinit));
			}
		}
		RegCloseKey(hkey);
	}
    return;
}

void RemoveUserInit()
{
	BOOL fOk = FALSE;
    HKEY  hkey;
	LONG lErr;
	DWORD dwType;
	TCHAR szUserinit[LONGSTRINGSIZE];
	PTSTR pszDest;
	PTSTR pszSrc;
	DWORD cbData = sizeof(szUserinit);

	lErr = RegOpenKeyEx(
		HKEY_LOCAL_MACHINE,
        WINLOGONPATH,
		REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        &hkey); 
	
    if (ERROR_SUCCESS == lErr)
	{
		lErr = RegQueryValueEx(
                hkey,
                USERINIT,
                0,
                &dwType,
                (LPBYTE)szUserinit,
                &cbData);
		szUserinit[LONGSTRINGSIZE-1]='\0';

		if (ERROR_SUCCESS == lErr && dwType == REG_SZ)
		{

			// check to see if we are already installed
			pszDest = _tcsstr(szUserinit, SKEYSUSERINITCMD);
			if (NULL != pszDest)
			{
				pszSrc =_tcsstr(pszDest, USERINITCMDSEP);
				if (NULL != pszSrc)
				{
					_tcscpy(pszDest, pszSrc+1);
				}
				else
				{
					while(szUserinit < pszDest && *SKEYSUSERINITCMD != *pszDest)
					{
						--pszDest;
					}
					*pszDest = 0;  // null terminate
				}
			}

			RegSetValueEx(
				hkey,
				USERINIT,
                0,
				REG_SZ,
				(CONST LPBYTE)szUserinit,
				(lstrlen(szUserinit) + 1) * 
					sizeof(*szUserinit));
		}
		RegCloseKey(hkey);
	}
    return;
}

static void EnableService(BOOL fOn)
{
	SC_HANDLE   schService = NULL;
	SC_HANDLE   schSCManager = NULL;

	schSCManager = OpenSCManager(   // Open Service Manager
		NULL,                       // machine (NULL == local)
	    NULL,                       // database (NULL == default)
		MAXIMUM_ALLOWED);

	if (NULL != schSCManager)  // Did Open Service succeed?
	{
	    schService = OpenService(
			    schSCManager ,
			    __TEXT("SerialKeys"),
			    SERVICE_CHANGE_CONFIG | SERVICE_STOP);

	    if (NULL != schService)
		{
			ChangeServiceConfig(
				schService,
				SERVICE_WIN32_OWN_PROCESS,
				(fOn) ? SERVICE_AUTO_START : SERVICE_DEMAND_START,
				SERVICE_NO_CHANGE,		// severity if service fails to start 
				NULL,					// pointer to service binary file name 
				NULL,					// pointer to load ordering group name 
				NULL,					// pointer to variable to get tag identifier 
				NULL,					// pointer to array of dependency names 
				NULL,					// pointer to account name of service 
				NULL,					// pointer to password for service account  
				__TEXT("SerialKeys"));	// name to display 

	        CloseServiceHandle(schService);
		}

        CloseServiceHandle(schSCManager);
    }

	if (fOn)
	{
		InstallUserInit();
	}
	else
	{
		RemoveUserInit();
	}

    return;
}


//---------------------------------------------------------------
//
// FUNCTION	void ServiceCtrl(DWORD dwCtrlCode)
//
//	TYPE		Global
//
// PURPOSE		this function is called by the Service Controller whenever
//				someone calls ControlService in reference to our service.
//
// INPUTS		DWORD dwCtrlCode -
//
// RETURNS		None
//
//-----------------------------------------------------------------
LPHANDLER_FUNCTION ServiceCtrl(DWORD dwCtrlCode)
{
	DWORD	dwState = SERVICE_RUNNING;
	DWORD	dwWait = 0;

	DBPRINTF(TEXT("ServiceCtrl()\r\n"));

	// Handle the requested control code.

	switch(dwCtrlCode)
	{
		case SERVICE_CONTROL_PAUSE:			// Pause the service if it is running.
			if (s_ssStatus.dwCurrentState == SERVICE_RUNNING)
			{
				PauseService();
				dwState = SERVICE_PAUSED;
			}
			break;

		case SERVICE_CONTROL_CONTINUE:		// Resume the paused service.
			if (s_ssStatus.dwCurrentState == SERVICE_PAUSED)
			{
				ResumeService();
				dwState = SERVICE_RUNNING;
			}
			break;

		case SERVICE_CONTROL_STOP:			// Stop the service.
			// Report the status, specifying the checkpoint and waithint,
			//  before setting the termination event.
			if (s_ssStatus.dwCurrentState == SERVICE_RUNNING)
			{
				dwState = SERVICE_STOP_PENDING;
				dwWait = 20000;
				SetEvent(s_hEventServiceTerminate);
			}
			break;

		case SERVICE_CONTROL_INTERROGATE:	// Update the service status.
		default:							// invalid control code
			break;
    }
	// send a status response.
    ReportStatusToSCMgr(dwState, NO_ERROR, 0, dwWait);
	 return(0);
}

/*---------------------------------------------------------------
 *
 * FUNCTION	BOOL		ReportStatusToSCMgr()
 *
 *	TYPE		Global
 *
 * PURPOSE		This function is called by the ServMainFunc() and
 *				ServCtrlHandler() functions to update the service's status
 *				to the service control manager.
 *
 * INPUTS		DWORD	dwCurrentState
 *				DWORD	dwWin32ExitCode
 *				DWORD	dwCheckPoint
 *				DWORD	dwWaitHint
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
                    DWORD dwWin32ExitCode,
                    DWORD dwCheckPoint,
                    DWORD dwWaitHint)
{
	BOOL fResult;

#ifdef DEBUG
{
	switch (dwCurrentState)
	{
		case SERVICE_START_PENDING:
			DBPRINTF(TEXT("ReportStatusToSCMgr(SERVICE_START_PENDING:)\r\n"));
			break;
		case SERVICE_PAUSED:
			DBPRINTF(TEXT("ReportStatusToSCMgr(SERVICE_PAUSED:)\r\n"));
			break;
		case SERVICE_CONTINUE_PENDING:
			DBPRINTF(TEXT("ReportStatusToSCMgr(SERVICE_CONTINUE_PENDING:)\r\n"));
			break;
		case SERVICE_STOP_PENDING:
			DBPRINTF(TEXT("ReportStatusToSCMgr(SERVICE_STOP_PENDING:)\r\n"));
			break;
		case SERVICE_STOPPED:
			DBPRINTF(TEXT("ReportStatusToSCMgr(SERVICE_STOPPED:)\r\n"));
			break;
		case SERVICE_RUNNING:
			DBPRINTF(TEXT("ReportStatusToSCMgr(SERVICE_RUNNING:)\r\n"));
			break;

		default:
			DBPRINTF(TEXT("ReportStatusToSCMgr(ERROR - SERVICE_UNKNOWN)\r\n"));
			break;
	}
}
#endif


    switch (dwCurrentState)
	{
	case SERVICE_STOPPED:
	case SERVICE_START_PENDING:
	case SERVICE_STOP_PENDING:
		s_ssStatus.dwControlsAccepted = 0;
		break;
    default:
    	s_ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |	SERVICE_ACCEPT_PAUSE_CONTINUE;
		break;
	}

	// These SERVICE_STATUS members are set from parameters.
	s_ssStatus.dwCurrentState	= dwCurrentState;
	s_ssStatus.dwWin32ExitCode	= dwWin32ExitCode;
	s_ssStatus.dwCheckPoint		= dwCheckPoint;
	s_ssStatus.dwWaitHint		= dwWaitHint;

	// Report the status of the service to the service control manager.

	fResult = SetServiceStatus(
		s_sshStatusHandle,				// service reference handle
		&s_ssStatus); 					// SERVICE_STATUS structure

	if (!fResult)
	{
		StopSerialKeys(TEXT("SetServiceStatus")); // If an error occurs, stop the service.
	}
	return fResult;
}

/*---------------------------------------------------------------
 *
 * FUNCTION	void StopSerialKeys(LPTSTR lpszMsg)
 *
 *	TYPE		Global
 *
 * PURPOSE		The StopSerialKeys function can be used by any thread
 *				to report an error, or stop the service.
 *
 * INPUTS		LPTSTR lpszMsg -
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
VOID StopSerialKeys(LPTSTR lpszMsg)
{
	DBPRINTF(TEXT("StopSerialKeys()\r\n"));

	PostEventLog(lpszMsg,GetLastError());	// Post to Event Log
	SetEvent(s_hEventServiceTerminate);
}

/*---------------------------------------------------------------
 *
 * FUNCTION	void PostEventLog(LPTSTR lpszMsg, DWORD Error)
 *
 *	TYPE		Local
 *
 * PURPOSE		This function post strings to the Event Log
 *
 * INPUTS		LPTSTR lpszMsg - String to send
 *				DWORD Error		- Error Code (if 0 no error)
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
void PostEventLog(LPTSTR lpszMsg,DWORD Error)
{
	WORD 	ErrType = EVENTLOG_INFORMATION_TYPE;
	WORD	ErrStrings = 0;

	TCHAR   szMsg[256];
	HANDLE  hEventSource;
	LPTSTR  lpszStrings[2];

	DBPRINTF(TEXT("PostEventLog()\r\n"));

	lpszStrings[0] = lpszMsg;

	if (Error)
	{
		ErrType = EVENTLOG_ERROR_TYPE;
		ErrStrings = 2;
		wsprintf(szMsg, TEXT("SerialKeys error: %d"), Error);
		lpszStrings[0] = szMsg;
		lpszStrings[1] = lpszMsg;
	}

	hEventSource = RegisterEventSource(NULL,SERVICENAME);

	if (hEventSource != NULL)
	{
		ReportEvent
		(
			hEventSource,		// handle of event source
			ErrType,			// event type
			0,					// event category
			0,					// event ID
			NULL,				// current user's SID
			ErrStrings,			// strings in lpszStrings
			0,					// no bytes of raw data
			lpszStrings,		// array of error strings
			NULL				// no raw data
		);

		(VOID) DeregisterEventSource(hEventSource);
	}
}

/*---------------------------------------------------------------
 *
 *		Internal Service Control Functions
 *
/*---------------------------------------------------------------
 *
 * FUNCTION	void InitService()
 *
 * PURPOSE		This function Initializes the Service & starts the
 *				major threads of the service.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static BOOL InitService()
{
	DBPRINTF(TEXT("InitService()\r\n"));

	InstallLogout();

	if (!InitDLL())
		return(FALSE);

	if (!InitLogin())
		return(FALSE);

	if (!InitComm())
		return(FALSE);

	DoServiceCommand(SC_LOG_IN);	// Set ProcessService to Login Serial Keys

	return(TRUE);
}


static void InitReg()
{
	// Set Structure pointers to Buffers
	skNewKey.cbSize = sizeof(skNewKey);
	skNewKey.lpszActivePort = szNewActivePort;
	skNewKey.lpszPort = szNewPort;

	skCurKey.cbSize = sizeof(skCurKey);
	skCurKey.lpszActivePort = szCurActivePort;
	skCurKey.lpszPort = szCurPort;

	// Set Default Values
	skNewKey.dwFlags = SERKF_AVAILABLE;

	skNewKey.iBaudRate = 300;
	skNewKey.iPortState = 2;
	lstrcpy(szNewPort,TEXT("COM1:"));
	lstrcpy(szNewActivePort,TEXT("COM1:"));
}

/*---------------------------------------------------------------
 *
 * FUNCTION	void PauseService()
 *
 * PURPOSE		This function is called to pause the service
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static void PauseService()
{
	DBPRINTF(TEXT("PauseService()\r\n"));

	SuspendDLL();
	SuspendComm();
	SuspendLogin();
}


/*---------------------------------------------------------------
 *
 * FUNCTION	void DoServiceCommand()
 *
 * PURPOSE		Passes a command to the service thread
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
void DoServiceCommand(DWORD dwServiceCommand)
{
	DWORD dwWaitRet;

    dwWaitRet = WaitForSingleObject(s_hEventServiceRequestReady, 10*1000);
    
	if (WAIT_OBJECT_0 == dwWaitRet)
	{
		s_dwServiceCommand = dwServiceCommand;
		SetEvent(s_hEventServiceRequest);
	}
    else
	{
		DBPRINTF(TEXT("DoServiceCommand - wait failed or timed-out, request ignored\r\n"));
	}
}


/*---------------------------------------------------------------
 *
 * FUNCTION	void ProcessService()
 *
 * PURPOSE		This function is the main service thread for Serial
 *				Keys. 	Is monitors the status of the other theads
 *				and responds to their request.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static void ProcessService()
{
    DWORD dwServiceCommand;
	DWORD dwWaitRet;
	typedef enum {
		iheventServiceRequest,    
		iheventServiceTerminate
	};
	HANDLE ahevent[2] = {s_hEventServiceRequest, s_hEventServiceTerminate};

 	DBPRINTF(TEXT("ProcessService()\r\n"));
    
    dwWaitRet = WaitForMultipleObjects(ARRAY_SIZE(ahevent), ahevent, 
		FALSE,  // wait all?
		INFINITE);

	//  This loop will terminate when iheventServiceTerminate is signaled or
	//  WaitForMultipleObjects fails

	while (iheventServiceRequest == dwWaitRet - WAIT_OBJECT_0)
	{
		dwServiceCommand = s_dwServiceCommand;
    	SetEvent(s_hEventServiceRequestReady);

		switch (dwServiceCommand)
		{
			case SC_LOG_OUT:				// Login to New User
				DBPRINTF(TEXT("---- User Logging Out\r\n"));
				StopComm();			// Stop SerialKey Processing
				if(GetUserValues(REG_DEF))	// Get Default values & Do we Start?
				{
					EnableService(skNewKey.dwFlags & SERKF_SERIALKEYSON);
					StartComm();			// Yes - Process SerialKey
				}
				break;

			case SC_LOG_IN:					// Login to New User
				DBPRINTF(TEXT("---- User Logging In\r\n"));
				StopComm();			// Stop SerialKey Processing
				if(GetUserValues(REG_DEF)) 
				{	
					EnableService(skNewKey.dwFlags & SERKF_SERIALKEYSON);
					StartComm();			// Yes - Process SerialKey
				}
				break;

			case SC_CHANGE_COMM: 			// Change Comm Configuration
				DBPRINTF(TEXT("---- Making Comm Change\r\n"));
				StopComm();			// Stop SerialKey Processing
				StartComm();				// Restart SerialKey Processing
				break;

			case SC_DISABLE_SKEY:		 	// Disable Serial Keys
				DBPRINTF(TEXT("---- Disable Serial Keys\r\n"));
				StopComm();
				break;

			case SC_ENABLE_SKEY:			// Enable Serial Keys
				DBPRINTF(TEXT("---- Enable Serial Keys\r\n"));
				StartComm();
				break;
		}
		dwWaitRet = WaitForMultipleObjects(ARRAY_SIZE(ahevent), ahevent, 
			FALSE,  // wait all?
			INFINITE);
	}
}

/*---------------------------------------------------------------
 *
 * FUNCTION	void ResumeService()
 *
 * PURPOSE		This function is called to restore the service
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static void ResumeService()
{
	DBPRINTF(TEXT("ResumeService()\r\n"));

	ResumeDLL();
	ResumeComm();
	ResumeLogin();
}

//---------------------------------------------------------------
//
// FUNCTION	void TerminateService(DWORD Error)
//
//	TYPE		Local
//
// PURPOSE		This function is called by ServiceMain to terminate
//				the server.  It closes all of the open handles &
//				and reports the service is stopped.
//
// INPUTS		DWORD Error - Any Errors that could abort the
//				Service. 0 = Normal Stop
//
// RETURNS		None
//
//---------------------------------------------------------------

static void TerminateService(DWORD Error)
{
	DBPRINTF(TEXT("TerminateService()\r\n"));

	TerminateLogout();						// Remove Logout Monitoring

	TerminateComm();						// Init Comm Thread Shutdown

	TerminateDLL();							// Init DLL Thread Shutdown

	TerminateLogin();						// Init Login Thread Shutdown

	// Loop untill all of the Threads are shut down.

	while (!DoneLogin()) 					// Loop until Login Thread is terminated
		Sleep(250);							// Sleep 

	while (!DoneDLL())	 					// Loop until DLL Thread is terminated
		Sleep(250);							// Sleep 


    // reload registery values to insure we have the current values
	GetUserValues(REG_DEF);

	EnableService(skNewKey.dwFlags & SERKF_SERIALKEYSON);

	// Report the status is stopped
	if (s_sshStatusHandle)
		(VOID)ReportStatusToSCMgr(SERVICE_STOPPED,Error,0,0);
}

/*---------------------------------------------------------------
 *
 *	Logout Functions - Process Logout request
 *
/*---------------------------------------------------------------
 *
 * FUNCTION	void InstallLogout()
 *
 * PURPOSE		This function installs a Control Handler to process
 *				logout events.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static BOOL InstallLogout()
{
	DBPRINTF(TEXT("InstallLogout()\r\n"));

	return(SetConsoleCtrlHandler((PHANDLER_ROUTINE)ProcessLogout,TRUE));
}

/*---------------------------------------------------------------
 *
 * FUNCTION	void TerminateLogout()
 *
 * PURPOSE		This function Removes a Control Handler to process
 *				logout events.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static BOOL TerminateLogout()
{
	DBPRINTF(TEXT("TerminateLogout()\r\n"));

	return(SetConsoleCtrlHandler((PHANDLER_ROUTINE)ProcessLogout,FALSE));
}

/*---------------------------------------------------------------
 *
 * FUNCTION	void ProcessLogout()
 *
 * PURPOSE		This function processes	logout events.
 *
 * INPUTS		None
 *
 * RETURNS		None
 *
 *---------------------------------------------------------------*/
static void ProcessLogout(DWORD dwCtrlType)
{
	DBPRINTF(TEXT("ProcessLogout()\r\n"));

	if (dwCtrlType == CTRL_LOGOFF_EVENT)
	{
		DoServiceCommand(SC_LOG_OUT);

		// we'll do this each time the currently logged in user logs out
	}
}