| /* |
| * Windows Service related function definitions |
| * By Raju Krishnappa(raju_krishnappa@yahoo.com) |
| * |
| */ |
| |
| #ifdef WIN32 |
| |
| #include <net-snmp/net-snmp-config.h> |
| |
| #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]) ) |
| |
| |
| #if defined(WIN32) && defined(HAVE_WIN32_PLATFORM_SDK) && !defined(mingw32) |
| #pragma comment(lib, "iphlpapi.lib") |
| #endif
|
| #if defined(WIN32) && !defined(mingw32)
|
| #ifdef USING_WINEXTDLL_MODULE |
| #pragma comment(lib, "snmpapi.lib") |
| #pragma comment(lib, "mgmtapi.lib") |
| #endif |
| #endif |
| |
| |
| /* |
| * 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; |
| |
| |
| /* |
| * To update windows service status to SCM |
| */ |
| static BOOL UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode, |
| DWORD dwWaitHint); |
| |
| /* |
| * To Report current service status to SCM |
| */ |
| static BOOL ReportCurrentServiceStatus (VOID); |
| |
| 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 the 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 the event log |
| */ |
| /* |
| * 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); |
| } |
| |
| /* |
| * Write a message to the Windows event log. |
| */ |
| VOID |
| WriteToEventLog (WORD wType, LPCTSTR pszFormat, ...) |
| { |
| TCHAR szMessage[512]; |
| LPCTSTR 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, |
| 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; /* default is to run as a console application */ |
| |
| 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) |
| { |
| 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) { |
| LPTSTR pErrorMsgTemp = NULL; |
| 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) { |
| LPCTSTR LogStr[1]; |
| LogStr[0] = pszMessageFull; |
| |
| if (ReportEvent (hEventSource, |
| eventLogType, |
| 0, |
| DISPLAY_MSG, /* just output the text to the event log */ |
| NULL, |
| 1, |
| 0, |
| LogStr, |
| NULL)) { |
| } |
| else { |
| LPTSTR pErrorMsgTemp = NULL; |
| 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; |
| } |
| } |
| } |
| |
| /* |
| * 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); |
| } |
| |
| /* |
| * ServiceMain function. |
| */ |
| VOID WINAPI |
| ServiceMain (DWORD argc, LPTSTR argv[]) |
| { |
| SECURITY_ATTRIBUTES SecurityAttributes; |
| unsigned threadId; |
| |
| /* |
| * Input arguments |
| */ |
| DWORD ArgCount = 0; |
| LPTSTR *ArgArray = NULL; |
| TCHAR szRegKey[512]; |
| HKEY hParamKey = NULL; |
| 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 control 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 = calloc(ArgCount, sizeof(ArgArray[0])); |
| 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++) |
| { |
| DWORD dwErrorcode; |
| DWORD nSize; |
| DWORD nRegkeyType; |
| TCHAR *szValue; |
| |
| /* |
| * Create Subkey value name |
| */ |
| _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T("Param"), i); |
| |
| /* |
| * Query subkey. |
| */ |
| nSize = 0; |
| dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL, |
| &nRegkeyType, NULL, &nSize); |
| if (dwErrorcode == ERROR_SUCCESS) { |
| if (nRegkeyType == REG_SZ || nRegkeyType == REG_EXPAND_SZ) { |
| szValue = malloc(nSize + sizeof(szValue[0])); |
| if (szValue) { |
| dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL, |
| &nRegkeyType, (LPBYTE)szValue, &nSize); |
| if (dwErrorcode == ERROR_SUCCESS) { |
| szValue[nSize] = 0; |
| ArgArray[i] = szValue; |
| } else { |
| free(szValue); |
| WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode); |
| } |
| } else |
| WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: out of memory"), szRegKey); |
| } else |
| WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Type %ld of registry key %s is incorrect"), nRegkeyType, szRegKey); |
| } else |
| WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode); |
| |
| if (!ArgArray[i]) { |
| TotalParams = ArgCount = i; |
| break; |
| } |
| } |
| } |
| } |
| RegCloseKey (hParamKey); |
| } |
| if (ArgCount == 1) |
| { |
| |
| /* |
| * No startup args 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); |
| |
| /* |
| * Start the worker thread, which does the 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 SERVICE_RUNNING. |
| */ |
| UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); |
| |
| /* |
| * Wait until the worker thread finishes. |
| */ |
| WaitForSingleObject (hServiceThread, INFINITE); |
| } |
| FINALLY |
| { |
| /* |
| * Release resources |
| */ |
| UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL); |
| if (hServiceThread) |
| CloseHandle (hServiceThread); |
| FreeSecurityAttributes (&SecurityAttributes); |
| |
| /* |
| * Free allocated argument list |
| */ |
| if (ArgCount > 1 && ArgArray != NULL) |
| { |
| /* |
| * Free 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 */ |
| |
| |