diff options
Diffstat (limited to 'snmplib/winservice.c')
-rw-r--r-- | snmplib/winservice.c | 1061 |
1 files changed, 1061 insertions, 0 deletions
diff --git a/snmplib/winservice.c b/snmplib/winservice.c new file mode 100644 index 0000000..864ca64 --- /dev/null +++ b/snmplib/winservice.c @@ -0,0 +1,1061 @@ +/* + * Windows Service related function definitions + * By Raju Krishnappa(raju_krishnappa@yahoo.com) + * + */ + +#ifdef WIN32 + +#include <windows.h> +#include <tchar.h> + +#include <stdio.h> /* sprintf */ +#include <process.h> /* beginthreadex */ + +#include <net-snmp/library/winservice.h> + +#ifdef mingw32 /* MinGW doesn't fully support exception handling. */ + +#define TRY if(1) +#define LEAVE goto labelFIN +#define FINALLY do { \ +labelFIN: \ + ; \ +} while(0); if(1) + +#else + +#define TRY __try +#define LEAVE __leave +#define FINALLY __finally + +#endif /* mingw32 */ + + +#define CountOf(arr) ( sizeof(arr) / sizeof(arr[0]) ) + + + /* + * External global variables used here + */ + + /* + * Application Name + * This should be declared by the application, which wants to register as + * windows service + */ +extern LPTSTR app_name_long; + + /* + * Declare global variable + */ + + /* + * Flag to indicate whether process is running as Service + */ +BOOL g_fRunningAsService = FALSE; + + /* + * Variable to maintain Current Service status + */ +static SERVICE_STATUS ServiceStatus; + + /* + * Service Handle + */ +static SERVICE_STATUS_HANDLE hServiceStatus = 0L; + + /* + * Service Table Entry + */ +SERVICE_TABLE_ENTRY ServiceTableEntry[] = { + {NULL, ServiceMain}, /* Service Main function */ + {NULL, NULL} +}; + + /* + * Handle to Thread, to implement Pause, Resume and Stop functions + */ +static HANDLE hServiceThread = NULL; /* Thread Handle */ + + /* + * Holds calling partys Function Entry point, that should start + * when entering service mode + */ +static INT (*ServiceEntryPoint) (INT Argc, LPTSTR Argv[]) = 0L; + + /* + * To hold Stop Function address, to be called when STOP request + * received from the SCM + */ +static VOID (*StopFunction) (VOID) = 0L; + +VOID +ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet); + + /* + * To register as Windows Service with SCM(Service Control Manager) + * Input - Service Name, Service Display Name,Service Description and + * Service startup arguments + */ +int +RegisterService (LPCTSTR lpszServiceName, LPCTSTR lpszServiceDisplayName, + LPCTSTR lpszServiceDescription, + InputParams * StartUpArg, int quiet) /* Startup argument to the service */ +{ + TCHAR szServicePath[MAX_PATH]; /* To hold module File name */ + TCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */ + TCHAR szServiceCommand[MAX_PATH + 9]; /* Command to execute */ + SC_HANDLE hSCManager = NULL; + SC_HANDLE hService = NULL; + TCHAR szRegAppLogKey[] = + _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"); + TCHAR szRegKey[512]; + HKEY hKey = NULL; /* Key to registry entry */ + HKEY hParamKey = NULL; /* To store startup parameters */ + DWORD dwData; /* Type of logging supported */ + DWORD i, j; /* Loop variables */ + int exitStatus = 0; + GetModuleFileName (NULL, szServicePath, MAX_PATH); + TRY + { + + /* + * Open Service Control Manager handle + */ + hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (hSCManager == NULL) + { + ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet); + exitStatus = SERVICE_ERROR_SCM_OPEN; + LEAVE; + } + + /* + * Generate the Command to be executed by SCM + */ + _sntprintf (szServiceCommand, CountOf(szServiceCommand), _T("%s %s"), szServicePath, _T ("-service")); + + /* + * Create the Desired service + */ + hService = CreateService (hSCManager, lpszServiceName, lpszServiceDisplayName, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szServiceCommand, + NULL, /* load-order group */ + NULL, /* group member tag */ + NULL, /* dependencies */ + NULL, /* account */ + NULL); /* password */ + if (hService == NULL) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), + _T ("Can't create service"), lpszServiceDisplayName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + + exitStatus = SERVICE_ERROR_CREATE_SERVICE; + LEAVE; + } + + /* + * Create registry entries for EventLog + */ + /* + * Create registry Application event log key + */ + _tcscpy (szRegKey, szRegAppLogKey); + _tcscat (szRegKey, lpszServiceName); + + /* + * Create registry key + */ + if (RegCreateKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), + _T ("is unable to create registry entries"), lpszServiceDisplayName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; + LEAVE; + } + + /* + * Add Event ID message file name to the 'EventMessageFile' subkey + */ + RegSetValueEx (hKey, _T("EventMessageFile"), 0, REG_EXPAND_SZ, + (CONST BYTE *) szServicePath, + _tcslen (szServicePath) + sizeof (TCHAR)); + + /* + * Set the supported types flags. + */ + dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + RegSetValueEx (hKey, _T("TypesSupported"), 0, REG_DWORD, + (CONST BYTE *) & dwData, sizeof (DWORD)); + + /* + * Close Registry key + */ + RegCloseKey (hKey); + + /* + * Set Service Description String and save startup parameters if present + */ + if (lpszServiceDescription != NULL || StartUpArg->Argc > 2) + { + /* + * Create Registry Key path + */ + _tcscpy (szRegKey, _T ("SYSTEM\\CurrentControlSet\\Services\\")); + _tcscat (szRegKey, app_name_long); + hKey = NULL; + + /* + * Open Registry key using Create and Set access. + */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_WRITE, + &hKey) != ERROR_SUCCESS) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), + _T ("is unable to create registry entries"), + lpszServiceDisplayName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; + LEAVE; + } + + /* + * Create description subkey and the set value + */ + if (lpszServiceDescription != NULL) + { + if (RegSetValueEx (hKey, _T("Description"), 0, REG_SZ, + (CONST BYTE *) lpszServiceDescription, + _tcslen (lpszServiceDescription) + + sizeof (TCHAR)) != ERROR_SUCCESS) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), + _T ("is unable to create registry entries"), + lpszServiceDisplayName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; + LEAVE; + }; + } + + /* + * Save startup arguments if they are present + */ + if (StartUpArg->Argc > 2) + { + /* + * Create Subkey parameters + */ + if (RegCreateKeyEx + (hKey, _T("Parameters"), 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, + &hParamKey, NULL) != ERROR_SUCCESS) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), + _T ("is unable to create registry entries"), + lpszServiceDisplayName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; + LEAVE; + } + + /* + * Save parameters + */ + + /* + * Loop through arguments + */ + if (quiet) /* Make sure we don't store -quiet arg */ + i = 3; + else + i = 2; + + for (j = 1; i < StartUpArg->Argc; i++, j++) + { + _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T ("Param"), j); + + /* + * Create registry key + */ + if (RegSetValueEx + (hParamKey, szRegKey, 0, REG_SZ, + (CONST BYTE *) StartUpArg->Argv[i], + _tcslen (StartUpArg->Argv[i]) + + sizeof (TCHAR)) != ERROR_SUCCESS) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), + _T ("is unable to create registry entries"), + lpszServiceDisplayName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; + LEAVE; + }; + } + } + + /* + * Everything is set, delete hKey + */ + RegCloseKey (hParamKey); + RegCloseKey (hKey); + } + + /* + * Ready to Log messages + */ + + /* + * Successfully registered as service + */ + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), lpszServiceName, + _T ("successfully registered as a service")); + + /* + * Log message to eventlog + */ + ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet); + } + + FINALLY + { + if (hSCManager) + CloseServiceHandle (hSCManager); + if (hService) + CloseServiceHandle (hService); + if (hKey) + RegCloseKey (hKey); + if (hParamKey) + RegCloseKey (hParamKey); + } + return (exitStatus); +} + + /* + * Unregister the service with the Windows SCM + * Input - ServiceName + */ +int +UnregisterService (LPCTSTR lpszServiceName, int quiet) +{ + TCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */ + SC_HANDLE hSCManager = NULL; /* SCM handle */ + SC_HANDLE hService = NULL; /* Service Handle */ + SERVICE_STATUS sStatus; + TCHAR szRegAppLogKey[] = + _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"); + TCHAR szRegKey[512]; + int exitStatus = 0; +/* HKEY hKey = NULL; ?* Key to registry entry */ + TRY + { + /* + * Open Service Control Manager + */ + hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (hSCManager == NULL) + { + ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet); + exitStatus = SERVICE_ERROR_SCM_OPEN; + LEAVE; + } + + /* + * Open registered service + */ + hService = OpenService (hSCManager, lpszServiceName, SERVICE_ALL_ACCESS); + if (hService == NULL) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), _T ("Can't open service"), + lpszServiceName); + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); + exitStatus = SERVICE_ERROR_OPEN_SERVICE; + LEAVE; + } + + /* + * Query service status + * If running stop before deleting + */ + if (QueryServiceStatus (hService, &sStatus)) + { + if (sStatus.dwCurrentState == SERVICE_RUNNING + || sStatus.dwCurrentState == SERVICE_PAUSED) + { + ControlService (hService, SERVICE_CONTROL_STOP, &sStatus); + } + }; + + /* + * Delete the service + */ + if (DeleteService (hService) == FALSE) + { + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), _T ("Can't delete service"), + lpszServiceName); + + /* + * Log message to eventlog + */ + ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 0, quiet); + LEAVE; + } + + /* + * Log "Service deleted successfully " message to eventlog + */ + _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), lpszServiceName, _T ("service deleted")); + ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet); + + /* + * Delete registry entries for EventLog + */ + _tcscpy (szRegKey, szRegAppLogKey); + _tcscat (szRegKey, lpszServiceName); + RegDeleteKey (HKEY_LOCAL_MACHINE, szRegKey); + } + + /* + * Delete the handles + */ + FINALLY + { + if (hService) + CloseServiceHandle (hService); + if (hSCManager) + CloseServiceHandle (hSCManager); + } + return (exitStatus); +} + + /* + * To write message to Windows Event log + * Input - Event Type, Message string + */ +VOID +WriteToEventLog (WORD wType, LPCTSTR pszFormat, ...) +{ + TCHAR szMessage[512]; + LPTSTR LogStr[1]; + va_list ArgList; + HANDLE hEventSource = NULL; + va_start (ArgList, pszFormat); + _vsntprintf (szMessage, CountOf(szMessage), pszFormat, ArgList); + va_end (ArgList); + LogStr[0] = szMessage; + hEventSource = RegisterEventSource (NULL, app_name_long); + if (hEventSource == NULL) + return; + ReportEvent (hEventSource, wType, 0, + DISPLAY_MSG, /* To Just output the text to event log */ + NULL, 1, 0, LogStr, NULL); + DeregisterEventSource (hEventSource); +} + + /* + * Pre-process the second command-line argument from the user. + * Service related options are: + * -register - registers the service + * -unregister - unregisters the service + * -service - run as service + * other command-line arguments are ignored here. + * + * Return: Type indicating the option specified + */ +INT +ParseCmdLineForServiceOption (int argc, TCHAR * argv[], int *quiet) +{ + int nReturn = RUN_AS_CONSOLE; /* Defualted to run as console */ + + if (argc >= 2) + { + + /* + * second argument present + */ + if (lstrcmpi (_T ("-register"), argv[1]) == 0) + { + nReturn = REGISTER_SERVICE; + } + + else if (lstrcmpi (_T ("-unregister"), argv[1]) == 0) + { + nReturn = UN_REGISTER_SERVICE; + } + + else if (lstrcmpi (_T ("-service"), argv[1]) == 0) + { + nReturn = RUN_AS_SERVICE; + } + } + + if (argc >= 3) + { + /* + * third argument present + */ + if (lstrcmpi (_T ("-quiet"), argv[2]) == 0) + { + *quiet = 1; + } + } + + return nReturn; +} + + /* + * Write error message to Event Log, console or pop-up window + * + * If useGetLastError is 1, the last error returned from GetLastError() + * is appended to pszMessage, separated by a ": ". + * + * eventLogType: MessageBox equivalent: + * + * EVENTLOG_INFORMATION_TYPE MB_ICONASTERISK + * EVENTLOG_WARNING_TYPE MB_ICONEXCLAMATION + * EVENTLOG_ERROR_TYPE MB_ICONSTOP + * + */ +VOID +ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet) +{ + LPTSTR pErrorMsgTemp = NULL; + HANDLE hEventSource = NULL; + TCHAR pszMessageFull[MAX_STR_SIZE]; /* Combined pszMessage and GetLastError */ + + /* + * If useGetLastError enabled, generate text from GetLastError() and append to + * pszMessageFull + */ + if (useGetLastError) { + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & pErrorMsgTemp, 0, NULL); + + _sntprintf (pszMessageFull, CountOf(pszMessageFull), _T("%s: %s"), pszMessage, pErrorMsgTemp); + if (pErrorMsgTemp) { + LocalFree (pErrorMsgTemp); + pErrorMsgTemp = NULL; + } + } + else { + _sntprintf (pszMessageFull, CountOf(pszMessageFull), _T("%s"), pszMessage); + } + + hEventSource = RegisterEventSource (NULL, app_name_long); + if (hEventSource != NULL) { + pErrorMsgTemp = pszMessageFull; + + if (ReportEvent (hEventSource, + eventLogType, + 0, + DISPLAY_MSG, /* To Just output the text to event log */ + NULL, + 1, + 0, + &pErrorMsgTemp, + NULL)) { + } + else { + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & pErrorMsgTemp, 0, NULL); + _ftprintf(stderr,_T("Could NOT lot to Event Log. Error returned from ReportEvent(): %s\n"),pErrorMsgTemp); + if (pErrorMsgTemp) { + LocalFree (pErrorMsgTemp); + pErrorMsgTemp = NULL; + } + } + DeregisterEventSource (hEventSource); + } + + if (quiet) { + _ftprintf(stderr,_T("%s\n"),pszMessageFull); + } + else { + switch (eventLogType) { + case EVENTLOG_INFORMATION_TYPE: + MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONASTERISK); + break; + case EVENTLOG_WARNING_TYPE: + MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONEXCLAMATION); + break; + case EVENTLOG_ERROR_TYPE: + MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONSTOP); + break; + default: + MessageBox (NULL, pszMessageFull, app_name_long, EVENTLOG_WARNING_TYPE); + break; + } + } + + LocalFree (pErrorMsgTemp); +} + + /* + * To update current service status + * Sends the current service status to the SCM. Also updates + * the global service status structure. + */ +static BOOL +UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode, DWORD dwWaitHint) +{ + DWORD static dwCheckpoint = 1; + DWORD dwControls = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + if (g_fRunningAsService == FALSE) + return FALSE; + ZeroMemory (&ServiceStatus, sizeof (ServiceStatus)); + ServiceStatus.dwServiceType = SERVICE_WIN32; + ServiceStatus.dwCurrentState = dwStatus; + ServiceStatus.dwWaitHint = dwWaitHint; + if (dwErrorCode) + { + ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + ServiceStatus.dwServiceSpecificExitCode = dwErrorCode; + } + + /* + * special cases that depend on the new state + */ + switch (dwStatus) + { + case SERVICE_START_PENDING: + dwControls = 0; + break; + case SERVICE_RUNNING: + case SERVICE_STOPPED: + dwCheckpoint = 0; + break; + } + ServiceStatus.dwCheckPoint = dwCheckpoint++; + ServiceStatus.dwControlsAccepted = dwControls; + return ReportCurrentServiceStatus (); +} + + /* + * Reports current Service status to SCM + */ +static BOOL +ReportCurrentServiceStatus () +{ + return SetServiceStatus (hServiceStatus, &ServiceStatus); +} + + /* + * The ServiceMain function to start service. + */ +VOID WINAPI +ServiceMain (DWORD argc, LPTSTR argv[]) +{ + SECURITY_ATTRIBUTES SecurityAttributes; + unsigned threadId; + + /* + * Input Arguments to function startup + */ + DWORD ArgCount = 0; + LPTSTR *ArgArray = NULL; + TCHAR szRegKey[512]; + TCHAR szValue[128]; + DWORD nSize; + HKEY hParamKey = NULL; /* To read startup parameters */ + DWORD TotalParams = 0; + DWORD i; + InputParams ThreadInputParams; + + /* + * Build the Input parameters to pass to worker thread + */ + + /* + * SCM sends Service Name as first arg, increment to point + * arguments user specified while starting contorl agent + */ + + /* + * Read registry parameter + */ + ArgCount = 1; + + /* + * Create Registry Key path + */ + _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%s\\%s"), + _T ("SYSTEM\\CurrentControlSet\\Services\\"), app_name_long, + _T("Parameters")); + if (RegOpenKeyEx + (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS, &hParamKey) == ERROR_SUCCESS) + { + + /* + * Read startup Configuration information + */ + /* + * Find number of subkeys inside parameters + */ + if (RegQueryInfoKey (hParamKey, NULL, NULL, 0, + NULL, NULL, NULL, &TotalParams, + NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + { + if (TotalParams != 0) + { + ArgCount += TotalParams; + + /* + * Allocate memory to hold strings + */ + ArgArray = (LPTSTR *) malloc (sizeof (LPTSTR) * ArgCount); + if (ArgArray == 0) + { + WriteToEventLog (EVENTLOG_ERROR_TYPE, + _T ("Resource failure")); + return; + } + + /* + * Copy first argument + */ + ArgArray[0] = _tcsdup (argv[0]); + for (i = 1; i <= TotalParams; i++) + { + + /* + * Create Subkey value name + */ + _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T("Param"), i); + + /* + * Set size + */ + nSize = 128; + RegQueryValueEx (hParamKey, szRegKey, 0, NULL, + (LPBYTE) & szValue, &nSize); + ArgArray[i] = _tcsdup (szValue); + } + } + } + RegCloseKey (hParamKey); + } + if (ArgCount == 1) + { + + /* + * No statup agrs are given + */ + ThreadInputParams.Argc = argc; + ThreadInputParams.Argv = argv; + } + + else + { + ThreadInputParams.Argc = ArgCount; + ThreadInputParams.Argv = ArgArray; + } + + /* + * Register Service Control Handler + */ + hServiceStatus = RegisterServiceCtrlHandler (app_name_long, ControlHandler); + if (hServiceStatus == 0) + { + WriteToEventLog (EVENTLOG_ERROR_TYPE, + _T ("RegisterServiceCtrlHandler failed")); + return; + } + + /* + * Update the service status to START_PENDING + */ + UpdateServiceStatus (SERVICE_START_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); + + /* + * Spin of worker thread, which does majority of the work + */ + TRY + { + if (SetSimpleSecurityAttributes (&SecurityAttributes) == FALSE) + { + WriteToEventLog (EVENTLOG_ERROR_TYPE, + _T ("Couldn't init security attributes")); + LEAVE; + } + hServiceThread = + (void *) _beginthreadex (&SecurityAttributes, 0, + ThreadFunction, + (void *) &ThreadInputParams, 0, &threadId); + if (hServiceThread == NULL) + { + WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start worker thread")); + LEAVE; + } + + /* + * Set Service Status to Running + */ + UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); + + /* + * Wait for termination event and worker thread to + * * spin down. + */ + WaitForSingleObject (hServiceThread, INFINITE); + } + FINALLY + { + /* + * Release resources + */ + UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL); + if (hServiceThread) + CloseHandle (hServiceThread); + FreeSecurityAttributes (&SecurityAttributes); + + /* + * Delete allocated argument list + */ + if (ArgCount > 1 && ArgArray != NULL) + { + /* + * Delete all strings + */ + for (i = 0; i < ArgCount; i++) + { + free (ArgArray[i]); + } + free (ArgArray); + } + } +} + + /* + * Function to start as Windows service + * The calling party should specify their entry point as input parameter + * Returns TRUE if the Service is started successfully + */ +BOOL +RunAsService (INT (*ServiceFunction) (INT, LPTSTR *)) +{ + + /* + * Set the ServiceEntryPoint + */ + ServiceEntryPoint = ServiceFunction; + + /* + * By default, mark as Running as a service + */ + g_fRunningAsService = TRUE; + + /* + * Initialize ServiceTableEntry table + */ + ServiceTableEntry[0].lpServiceName = app_name_long; /* Application Name */ + + /* + * Call SCM via StartServiceCtrlDispatcher to run as Service + * * If the function returns TRUE we are running as Service, + */ + if (StartServiceCtrlDispatcher (ServiceTableEntry) == FALSE) + { + g_fRunningAsService = FALSE; + + /* + * Some other error has occurred. + */ + WriteToEventLog (EVENTLOG_ERROR_TYPE, + _T ("Couldn't start service - %s"), app_name_long); + } + return g_fRunningAsService; +} + + /* + * Service control handler function + * Responds to SCM commands/requests + * This service handles 4 commands + * - interrogate, pause, continue and stop. + */ +VOID WINAPI +ControlHandler (DWORD dwControl) +{ + switch (dwControl) + { + case SERVICE_CONTROL_INTERROGATE: + ProcessServiceInterrogate (); + break; + + case SERVICE_CONTROL_PAUSE: + ProcessServicePause (); + break; + + case SERVICE_CONTROL_CONTINUE: + ProcessServiceContinue (); + break; + + case SERVICE_CONTROL_STOP: + ProcessServiceStop (); + break; + } +} + + /* + * To stop the service. + * If a stop function was registered, invoke it, + * otherwise terminate the worker thread. + * After stopping, Service status is set to STOP in + * main loop + */ +VOID +ProcessServiceStop (VOID) +{ + UpdateServiceStatus (SERVICE_STOP_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); + + if (StopFunction != NULL) + { + (*StopFunction) (); + } + + else + { + TerminateThread (hServiceThread, 0); + } +} + + /* + * Returns the current state of the service to the SCM. + */ +VOID +ProcessServiceInterrogate (VOID) +{ + ReportCurrentServiceStatus (); +} + + /* + * To Create a security descriptor with a NULL ACL, which + * allows unlimited access. Returns a SECURITY_ATTRIBUTES + * structure that contains the security descriptor. + * The structure contains a dynamically allocated security + * descriptor that must be freed either manually, or by + * calling FreeSecurityAttributes + */ +BOOL +SetSimpleSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr) +{ + BOOL fReturn = FALSE; + SECURITY_DESCRIPTOR *pSecurityDesc = NULL; + + /* + * If an invalid address is passed as a parameter, return + * FALSE right away. + */ + if (!pSecurityAttr) + return FALSE; + pSecurityDesc = + (SECURITY_DESCRIPTOR *) LocalAlloc (LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!pSecurityDesc) + return FALSE; + fReturn = + InitializeSecurityDescriptor (pSecurityDesc, SECURITY_DESCRIPTOR_REVISION); + if (fReturn != FALSE) + { + fReturn = SetSecurityDescriptorDacl (pSecurityDesc, TRUE, NULL, FALSE); + } + if (fReturn != FALSE) + { + pSecurityAttr->nLength = sizeof (SECURITY_ATTRIBUTES); + pSecurityAttr->lpSecurityDescriptor = pSecurityDesc; + pSecurityAttr->bInheritHandle = TRUE; + } + + else + { + /* + * Couldn't initialize or set security descriptor. + */ + LocalFree (pSecurityDesc); + } + return fReturn; +} + + /* + * This function Frees the security descriptor, if any was created. + */ +VOID +FreeSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr) +{ + if (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor) + LocalFree (pSecurityAttr->lpSecurityDescriptor); +} + + /* + * This function runs in the worker thread + * until an exit is forced, or until the SCM issues the STOP command. + * Invokes registered service function + * Returns when called registered function returns + * + * Input: + * lpParam contains argc and argv, pass to service main function + */ +unsigned WINAPI +ThreadFunction (LPVOID lpParam) +{ + InputParams * pInputArg = (InputParams *) lpParam; + return (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv); +} + + /* + * This function is called to register an application-specific function + * which is invoked when the SCM stops the worker thread. + */ +VOID +RegisterStopFunction (VOID (*StopFunc) (VOID)) +{ + StopFunction = StopFunc; +} + + /* + * SCM pause command invokes this function + * If the service is not running, this function does nothing. + * Otherwise, suspend the worker thread and update the status. + */ +VOID +ProcessServicePause (VOID) +{ + if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) + { + UpdateServiceStatus (SERVICE_PAUSE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); + + if (SuspendThread (hServiceThread) != -1) + { + UpdateServiceStatus (SERVICE_PAUSED, NO_ERROR, SCM_WAIT_INTERVAL); + } + } +} + + /* + * SCM resume command invokes this function + * If the service is not paused, this function does nothing. + * Otherwise, resume the worker thread and update the status. + */ +VOID +ProcessServiceContinue (VOID) +{ + if (ServiceStatus.dwCurrentState == SERVICE_PAUSED) + { + UpdateServiceStatus (SERVICE_CONTINUE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); + + if (ResumeThread (hServiceThread) != -1) + { + UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); + } + } +} + +#endif /* WIN32 */ + + |