net-snmp 5.7
|
00001 /* 00002 * Windows Service related function definitions 00003 * By Raju Krishnappa(raju_krishnappa@yahoo.com) 00004 * 00005 */ 00006 00007 #ifdef WIN32 00008 00009 #include <net-snmp/net-snmp-config.h> 00010 00011 #include <windows.h> 00012 #include <tchar.h> 00013 00014 #include <stdio.h> /* sprintf */ 00015 #include <process.h> /* beginthreadex */ 00016 00017 #include <net-snmp/library/winservice.h> 00018 00019 #ifdef mingw32 /* MinGW doesn't fully support exception handling. */ 00020 00021 #define TRY if(1) 00022 #define LEAVE goto labelFIN 00023 #define FINALLY do { \ 00024 labelFIN: \ 00025 ; \ 00026 } while(0); if(1) 00027 00028 #else 00029 00030 #define TRY __try 00031 #define LEAVE __leave 00032 #define FINALLY __finally 00033 00034 #endif /* mingw32 */ 00035 00036 00037 #define CountOf(arr) ( sizeof(arr) / sizeof(arr[0]) ) 00038 00039 00040 #if defined(WIN32) && defined(HAVE_WIN32_PLATFORM_SDK) && !defined(mingw32) 00041 #pragma comment(lib, "iphlpapi.lib") 00042 #ifdef USING_WINEXTDLL_MODULE 00043 #pragma comment(lib, "snmpapi.lib") 00044 #pragma comment(lib, "mgmtapi.lib") 00045 #endif 00046 #endif 00047 00048 00049 /* 00050 * External global variables used here 00051 */ 00052 00053 /* 00054 * Application Name 00055 * This should be declared by the application, which wants to register as 00056 * windows service 00057 */ 00058 extern LPTSTR app_name_long; 00059 00060 /* 00061 * Declare global variable 00062 */ 00063 00064 /* 00065 * Flag to indicate whether process is running as Service 00066 */ 00067 BOOL g_fRunningAsService = FALSE; 00068 00069 /* 00070 * Variable to maintain Current Service status 00071 */ 00072 static SERVICE_STATUS ServiceStatus; 00073 00074 /* 00075 * Service Handle 00076 */ 00077 static SERVICE_STATUS_HANDLE hServiceStatus = 0L; 00078 00079 /* 00080 * Service Table Entry 00081 */ 00082 SERVICE_TABLE_ENTRY ServiceTableEntry[] = { 00083 {NULL, ServiceMain}, /* Service Main function */ 00084 {NULL, NULL} 00085 }; 00086 00087 /* 00088 * Handle to Thread, to implement Pause, Resume and Stop functions 00089 */ 00090 static HANDLE hServiceThread = NULL; /* Thread Handle */ 00091 00092 /* 00093 * Holds calling partys Function Entry point, that should start 00094 * when entering service mode 00095 */ 00096 static INT (*ServiceEntryPoint) (INT Argc, LPTSTR Argv[]) = 0L; 00097 00098 /* 00099 * To hold Stop Function address, to be called when STOP request 00100 * received from the SCM 00101 */ 00102 static VOID (*StopFunction) (VOID) = 0L; 00103 00104 00105 /* 00106 * To update windows service status to SCM 00107 */ 00108 static BOOL UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode, 00109 DWORD dwWaitHint); 00110 00111 /* 00112 * To Report current service status to SCM 00113 */ 00114 static BOOL ReportCurrentServiceStatus (VOID); 00115 00116 VOID 00117 ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet); 00118 00119 /* 00120 * To register as Windows Service with SCM(Service Control Manager) 00121 * Input - Service Name, Service Display Name,Service Description and 00122 * Service startup arguments 00123 */ 00124 int 00125 RegisterService (LPCTSTR lpszServiceName, LPCTSTR lpszServiceDisplayName, 00126 LPCTSTR lpszServiceDescription, 00127 InputParams * StartUpArg, int quiet) /* Startup argument to the service */ 00128 { 00129 TCHAR szServicePath[MAX_PATH]; /* To hold module File name */ 00130 TCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */ 00131 TCHAR szServiceCommand[MAX_PATH + 9]; /* Command to execute */ 00132 SC_HANDLE hSCManager = NULL; 00133 SC_HANDLE hService = NULL; 00134 TCHAR szRegAppLogKey[] = 00135 _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"); 00136 TCHAR szRegKey[512]; 00137 HKEY hKey = NULL; /* Key to registry entry */ 00138 HKEY hParamKey = NULL; /* To store startup parameters */ 00139 DWORD dwData; /* Type of logging supported */ 00140 DWORD i, j; /* Loop variables */ 00141 int exitStatus = 0; 00142 GetModuleFileName (NULL, szServicePath, MAX_PATH); 00143 TRY 00144 { 00145 00146 /* 00147 * Open Service Control Manager handle 00148 */ 00149 hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE); 00150 if (hSCManager == NULL) 00151 { 00152 ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet); 00153 exitStatus = SERVICE_ERROR_SCM_OPEN; 00154 LEAVE; 00155 } 00156 00157 /* 00158 * Generate the command to be executed by the SCM 00159 */ 00160 _sntprintf (szServiceCommand, CountOf(szServiceCommand), _T("%s %s"), szServicePath, _T ("-service")); 00161 00162 /* 00163 * Create the desired service 00164 */ 00165 hService = CreateService (hSCManager, lpszServiceName, lpszServiceDisplayName, 00166 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, 00167 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szServiceCommand, 00168 NULL, /* load-order group */ 00169 NULL, /* group member tag */ 00170 NULL, /* dependencies */ 00171 NULL, /* account */ 00172 NULL); /* password */ 00173 if (hService == NULL) 00174 { 00175 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), 00176 _T ("Can't create service"), lpszServiceDisplayName); 00177 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00178 00179 exitStatus = SERVICE_ERROR_CREATE_SERVICE; 00180 LEAVE; 00181 } 00182 00183 /* 00184 * Create registry entries for the event log 00185 */ 00186 /* 00187 * Create registry Application event log key 00188 */ 00189 _tcscpy (szRegKey, szRegAppLogKey); 00190 _tcscat (szRegKey, lpszServiceName); 00191 00192 /* 00193 * Create registry key 00194 */ 00195 if (RegCreateKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS) 00196 { 00197 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), 00198 _T ("is unable to create registry entries"), lpszServiceDisplayName); 00199 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00200 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; 00201 LEAVE; 00202 } 00203 00204 /* 00205 * Add Event ID message file name to the 'EventMessageFile' subkey 00206 */ 00207 RegSetValueEx (hKey, _T("EventMessageFile"), 0, REG_EXPAND_SZ, 00208 (CONST BYTE *) szServicePath, 00209 _tcslen (szServicePath) + sizeof (TCHAR)); 00210 00211 /* 00212 * Set the supported types flags. 00213 */ 00214 dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; 00215 RegSetValueEx (hKey, _T("TypesSupported"), 0, REG_DWORD, 00216 (CONST BYTE *) & dwData, sizeof (DWORD)); 00217 00218 /* 00219 * Close Registry key 00220 */ 00221 RegCloseKey (hKey); 00222 00223 /* 00224 * Set Service Description String and save startup parameters if present 00225 */ 00226 if (lpszServiceDescription != NULL || StartUpArg->Argc > 2) 00227 { 00228 /* 00229 * Create Registry Key path 00230 */ 00231 _tcscpy (szRegKey, _T ("SYSTEM\\CurrentControlSet\\Services\\")); 00232 _tcscat (szRegKey, app_name_long); 00233 hKey = NULL; 00234 00235 /* 00236 * Open Registry key using Create and Set access. 00237 */ 00238 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_WRITE, 00239 &hKey) != ERROR_SUCCESS) 00240 { 00241 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), 00242 _T ("is unable to create registry entries"), 00243 lpszServiceDisplayName); 00244 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00245 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; 00246 LEAVE; 00247 } 00248 00249 /* 00250 * Create description subkey and the set value 00251 */ 00252 if (lpszServiceDescription != NULL) 00253 { 00254 if (RegSetValueEx (hKey, _T("Description"), 0, REG_SZ, 00255 (CONST BYTE *) lpszServiceDescription, 00256 _tcslen (lpszServiceDescription) + 00257 sizeof (TCHAR)) != ERROR_SUCCESS) 00258 { 00259 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), 00260 _T ("is unable to create registry entries"), 00261 lpszServiceDisplayName); 00262 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00263 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; 00264 LEAVE; 00265 }; 00266 } 00267 00268 /* 00269 * Save startup arguments if they are present 00270 */ 00271 if (StartUpArg->Argc > 2) 00272 { 00273 /* 00274 * Create Subkey parameters 00275 */ 00276 if (RegCreateKeyEx 00277 (hKey, _T("Parameters"), 0, NULL, 00278 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, 00279 &hParamKey, NULL) != ERROR_SUCCESS) 00280 { 00281 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), 00282 _T ("is unable to create registry entries"), 00283 lpszServiceDisplayName); 00284 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00285 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; 00286 LEAVE; 00287 } 00288 00289 /* 00290 * Save parameters 00291 */ 00292 00293 /* 00294 * Loop through arguments 00295 */ 00296 if (quiet) /* Make sure we don't store -quiet arg */ 00297 i = 3; 00298 else 00299 i = 2; 00300 00301 for (j = 1; i < StartUpArg->Argc; i++, j++) 00302 { 00303 _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T ("Param"), j); 00304 00305 /* 00306 * Create registry key 00307 */ 00308 if (RegSetValueEx 00309 (hParamKey, szRegKey, 0, REG_SZ, 00310 (CONST BYTE *) StartUpArg->Argv[i], 00311 _tcslen (StartUpArg->Argv[i]) + 00312 sizeof (TCHAR)) != ERROR_SUCCESS) 00313 { 00314 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), 00315 _T ("is unable to create registry entries"), 00316 lpszServiceDisplayName); 00317 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00318 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES; 00319 LEAVE; 00320 }; 00321 } 00322 } 00323 00324 /* 00325 * Everything is set, delete hKey 00326 */ 00327 RegCloseKey (hParamKey); 00328 RegCloseKey (hKey); 00329 } 00330 00331 /* 00332 * Ready to log messages 00333 */ 00334 00335 /* 00336 * Successfully registered as service 00337 */ 00338 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), lpszServiceName, 00339 _T ("successfully registered as a service")); 00340 00341 /* 00342 * Log message to eventlog 00343 */ 00344 ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet); 00345 } 00346 00347 FINALLY 00348 { 00349 if (hSCManager) 00350 CloseServiceHandle (hSCManager); 00351 if (hService) 00352 CloseServiceHandle (hService); 00353 if (hKey) 00354 RegCloseKey (hKey); 00355 if (hParamKey) 00356 RegCloseKey (hParamKey); 00357 } 00358 return (exitStatus); 00359 } 00360 00361 /* 00362 * Unregister the service with the Windows SCM 00363 * Input - ServiceName 00364 */ 00365 int 00366 UnregisterService (LPCTSTR lpszServiceName, int quiet) 00367 { 00368 TCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */ 00369 SC_HANDLE hSCManager = NULL; /* SCM handle */ 00370 SC_HANDLE hService = NULL; /* Service Handle */ 00371 SERVICE_STATUS sStatus; 00372 TCHAR szRegAppLogKey[] = 00373 _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"); 00374 TCHAR szRegKey[512]; 00375 int exitStatus = 0; 00376 /* HKEY hKey = NULL; ?* Key to registry entry */ 00377 TRY 00378 { 00379 /* 00380 * Open Service Control Manager 00381 */ 00382 hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE); 00383 if (hSCManager == NULL) 00384 { 00385 ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet); 00386 exitStatus = SERVICE_ERROR_SCM_OPEN; 00387 LEAVE; 00388 } 00389 00390 /* 00391 * Open registered service 00392 */ 00393 hService = OpenService (hSCManager, lpszServiceName, SERVICE_ALL_ACCESS); 00394 if (hService == NULL) 00395 { 00396 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), _T ("Can't open service"), 00397 lpszServiceName); 00398 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet); 00399 exitStatus = SERVICE_ERROR_OPEN_SERVICE; 00400 LEAVE; 00401 } 00402 00403 /* 00404 * Query service status 00405 * If running stop before deleting 00406 */ 00407 if (QueryServiceStatus (hService, &sStatus)) 00408 { 00409 if (sStatus.dwCurrentState == SERVICE_RUNNING 00410 || sStatus.dwCurrentState == SERVICE_PAUSED) 00411 { 00412 ControlService (hService, SERVICE_CONTROL_STOP, &sStatus); 00413 } 00414 }; 00415 00416 /* 00417 * Delete the service 00418 */ 00419 if (DeleteService (hService) == FALSE) 00420 { 00421 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), _T ("Can't delete service"), 00422 lpszServiceName); 00423 00424 /* 00425 * Log message to eventlog 00426 */ 00427 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 0, quiet); 00428 LEAVE; 00429 } 00430 00431 /* 00432 * Log "Service deleted successfully " message to eventlog 00433 */ 00434 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), lpszServiceName, _T ("service deleted")); 00435 ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet); 00436 00437 /* 00438 * Delete registry entries for EventLog 00439 */ 00440 _tcscpy (szRegKey, szRegAppLogKey); 00441 _tcscat (szRegKey, lpszServiceName); 00442 RegDeleteKey (HKEY_LOCAL_MACHINE, szRegKey); 00443 } 00444 00445 /* 00446 * Delete the handles 00447 */ 00448 FINALLY 00449 { 00450 if (hService) 00451 CloseServiceHandle (hService); 00452 if (hSCManager) 00453 CloseServiceHandle (hSCManager); 00454 } 00455 return (exitStatus); 00456 } 00457 00458 /* 00459 * Write a message to the Windows event log. 00460 */ 00461 VOID 00462 WriteToEventLog (WORD wType, LPCTSTR pszFormat, ...) 00463 { 00464 TCHAR szMessage[512]; 00465 LPCTSTR LogStr[1]; 00466 va_list ArgList; 00467 HANDLE hEventSource = NULL; 00468 00469 va_start (ArgList, pszFormat); 00470 _vsntprintf (szMessage, CountOf(szMessage), pszFormat, ArgList); 00471 va_end (ArgList); 00472 LogStr[0] = szMessage; 00473 hEventSource = RegisterEventSource (NULL, app_name_long); 00474 if (hEventSource == NULL) 00475 return; 00476 ReportEvent (hEventSource, wType, 0, 00477 DISPLAY_MSG, 00478 NULL, 1, 0, LogStr, NULL); 00479 DeregisterEventSource (hEventSource); 00480 } 00481 00482 /* 00483 * Pre-process the second command-line argument from the user. 00484 * Service related options are: 00485 * -register - registers the service 00486 * -unregister - unregisters the service 00487 * -service - run as service 00488 * other command-line arguments are ignored here. 00489 * 00490 * Return: Type indicating the option specified 00491 */ 00492 INT 00493 ParseCmdLineForServiceOption (int argc, TCHAR * argv[], int *quiet) 00494 { 00495 int nReturn = RUN_AS_CONSOLE; /* default is to run as a console application */ 00496 00497 if (argc >= 2) 00498 { 00499 00500 /* 00501 * second argument present 00502 */ 00503 if (lstrcmpi (_T ("-register"), argv[1]) == 0) 00504 { 00505 nReturn = REGISTER_SERVICE; 00506 } 00507 00508 else if (lstrcmpi (_T ("-unregister"), argv[1]) == 0) 00509 { 00510 nReturn = UN_REGISTER_SERVICE; 00511 } 00512 00513 else if (lstrcmpi (_T ("-service"), argv[1]) == 0) 00514 { 00515 nReturn = RUN_AS_SERVICE; 00516 } 00517 } 00518 00519 if (argc >= 3) 00520 { 00521 /* 00522 * third argument present 00523 */ 00524 if (lstrcmpi (_T ("-quiet"), argv[2]) == 0) 00525 { 00526 *quiet = 1; 00527 } 00528 } 00529 00530 return nReturn; 00531 } 00532 00533 /* 00534 * Write error message to event log, console or pop-up window. 00535 * 00536 * If useGetLastError is 1, the last error returned from GetLastError() 00537 * is appended to pszMessage, separated by a ": ". 00538 * 00539 * eventLogType: MessageBox equivalent: 00540 * 00541 * EVENTLOG_INFORMATION_TYPE MB_ICONASTERISK 00542 * EVENTLOG_WARNING_TYPE MB_ICONEXCLAMATION 00543 * EVENTLOG_ERROR_TYPE MB_ICONSTOP 00544 * 00545 */ 00546 VOID 00547 ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet) 00548 { 00549 HANDLE hEventSource = NULL; 00550 TCHAR pszMessageFull[MAX_STR_SIZE]; /* Combined pszMessage and GetLastError */ 00551 00552 /* 00553 * If useGetLastError enabled, generate text from GetLastError() and append to 00554 * pszMessageFull 00555 */ 00556 if (useGetLastError) { 00557 LPTSTR pErrorMsgTemp = NULL; 00558 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 00559 FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), 00560 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 00561 (LPTSTR) & pErrorMsgTemp, 0, NULL); 00562 00563 _sntprintf (pszMessageFull, CountOf(pszMessageFull), _T("%s: %s"), pszMessage, pErrorMsgTemp); 00564 if (pErrorMsgTemp) { 00565 LocalFree (pErrorMsgTemp); 00566 pErrorMsgTemp = NULL; 00567 } 00568 } 00569 else { 00570 _sntprintf (pszMessageFull, CountOf(pszMessageFull), _T("%s"), pszMessage); 00571 } 00572 00573 hEventSource = RegisterEventSource (NULL, app_name_long); 00574 if (hEventSource != NULL) { 00575 LPCTSTR LogStr[1]; 00576 LogStr[0] = pszMessageFull; 00577 00578 if (ReportEvent (hEventSource, 00579 eventLogType, 00580 0, 00581 DISPLAY_MSG, /* just output the text to the event log */ 00582 NULL, 00583 1, 00584 0, 00585 LogStr, 00586 NULL)) { 00587 } 00588 else { 00589 LPTSTR pErrorMsgTemp = NULL; 00590 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 00591 FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), 00592 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 00593 (LPTSTR) & pErrorMsgTemp, 0, NULL); 00594 _ftprintf(stderr,_T("Could NOT lot to Event Log. Error returned from ReportEvent(): %s\n"),pErrorMsgTemp); 00595 if (pErrorMsgTemp) { 00596 LocalFree (pErrorMsgTemp); 00597 pErrorMsgTemp = NULL; 00598 } 00599 } 00600 DeregisterEventSource (hEventSource); 00601 } 00602 00603 if (quiet) { 00604 _ftprintf(stderr,_T("%s\n"),pszMessageFull); 00605 } 00606 else { 00607 switch (eventLogType) { 00608 case EVENTLOG_INFORMATION_TYPE: 00609 MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONASTERISK); 00610 break; 00611 case EVENTLOG_WARNING_TYPE: 00612 MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONEXCLAMATION); 00613 break; 00614 case EVENTLOG_ERROR_TYPE: 00615 MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONSTOP); 00616 break; 00617 default: 00618 MessageBox (NULL, pszMessageFull, app_name_long, EVENTLOG_WARNING_TYPE); 00619 break; 00620 } 00621 } 00622 } 00623 00624 /* 00625 * Update current service status. 00626 * Sends the current service status to the SCM. Also updates 00627 * the global service status structure. 00628 */ 00629 static BOOL 00630 UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode, DWORD dwWaitHint) 00631 { 00632 DWORD static dwCheckpoint = 1; 00633 DWORD dwControls = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; 00634 if (g_fRunningAsService == FALSE) 00635 return FALSE; 00636 ZeroMemory (&ServiceStatus, sizeof (ServiceStatus)); 00637 ServiceStatus.dwServiceType = SERVICE_WIN32; 00638 ServiceStatus.dwCurrentState = dwStatus; 00639 ServiceStatus.dwWaitHint = dwWaitHint; 00640 if (dwErrorCode) 00641 { 00642 ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; 00643 ServiceStatus.dwServiceSpecificExitCode = dwErrorCode; 00644 } 00645 00646 /* 00647 * special cases that depend on the new state 00648 */ 00649 switch (dwStatus) 00650 { 00651 case SERVICE_START_PENDING: 00652 dwControls = 0; 00653 break; 00654 case SERVICE_RUNNING: 00655 case SERVICE_STOPPED: 00656 dwCheckpoint = 0; 00657 break; 00658 } 00659 ServiceStatus.dwCheckPoint = dwCheckpoint++; 00660 ServiceStatus.dwControlsAccepted = dwControls; 00661 return ReportCurrentServiceStatus (); 00662 } 00663 00664 /* 00665 * Reports current service status to SCM 00666 */ 00667 static BOOL 00668 ReportCurrentServiceStatus () 00669 { 00670 return SetServiceStatus (hServiceStatus, &ServiceStatus); 00671 } 00672 00673 /* 00674 * ServiceMain function. 00675 */ 00676 VOID WINAPI 00677 ServiceMain (DWORD argc, LPTSTR argv[]) 00678 { 00679 SECURITY_ATTRIBUTES SecurityAttributes; 00680 unsigned threadId; 00681 00682 /* 00683 * Input arguments 00684 */ 00685 DWORD ArgCount = 0; 00686 LPTSTR *ArgArray = NULL; 00687 TCHAR szRegKey[512]; 00688 HKEY hParamKey = NULL; 00689 DWORD TotalParams = 0; 00690 DWORD i; 00691 InputParams ThreadInputParams; 00692 00693 /* 00694 * Build the Input parameters to pass to worker thread 00695 */ 00696 00697 /* 00698 * SCM sends Service Name as first arg, increment to point 00699 * arguments user specified while starting control agent 00700 */ 00701 00702 /* 00703 * Read registry parameter 00704 */ 00705 ArgCount = 1; 00706 00707 /* 00708 * Create registry key path 00709 */ 00710 _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%s\\%s"), 00711 _T ("SYSTEM\\CurrentControlSet\\Services\\"), app_name_long, 00712 _T("Parameters")); 00713 if (RegOpenKeyEx 00714 (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS, &hParamKey) == ERROR_SUCCESS) 00715 { 00716 00717 /* 00718 * Read startup configuration information 00719 */ 00720 /* 00721 * Find number of subkeys inside parameters 00722 */ 00723 if (RegQueryInfoKey (hParamKey, NULL, NULL, 0, 00724 NULL, NULL, NULL, &TotalParams, 00725 NULL, NULL, NULL, NULL) == ERROR_SUCCESS) 00726 { 00727 if (TotalParams != 0) 00728 { 00729 ArgCount += TotalParams; 00730 00731 /* 00732 * Allocate memory to hold strings 00733 */ 00734 ArgArray = calloc(ArgCount, sizeof(ArgArray[0])); 00735 if (ArgArray == 0) 00736 { 00737 WriteToEventLog (EVENTLOG_ERROR_TYPE, 00738 _T ("Resource failure")); 00739 return; 00740 } 00741 00742 /* 00743 * Copy first argument 00744 */ 00745 ArgArray[0] = _tcsdup (argv[0]); 00746 for (i = 1; i <= TotalParams; i++) 00747 { 00748 DWORD dwErrorcode; 00749 DWORD nSize; 00750 DWORD nRegkeyType; 00751 TCHAR *szValue; 00752 00753 /* 00754 * Create Subkey value name 00755 */ 00756 _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T("Param"), i); 00757 00758 /* 00759 * Query subkey. 00760 */ 00761 nSize = 0; 00762 dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL, 00763 &nRegkeyType, NULL, &nSize); 00764 if (dwErrorcode == ERROR_SUCCESS) { 00765 if (nRegkeyType == REG_SZ || nRegkeyType == REG_EXPAND_SZ) { 00766 szValue = malloc(nSize + sizeof(szValue[0])); 00767 if (szValue) { 00768 dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL, 00769 &nRegkeyType, (LPBYTE)szValue, &nSize); 00770 if (dwErrorcode == ERROR_SUCCESS) { 00771 szValue[nSize] = 0; 00772 ArgArray[i] = szValue; 00773 } else { 00774 free(szValue); 00775 WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode); 00776 } 00777 } else 00778 WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: out of memory"), szRegKey); 00779 } else 00780 WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Type %ld of registry key %s is incorrect"), nRegkeyType, szRegKey); 00781 } else 00782 WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode); 00783 00784 if (!ArgArray[i]) { 00785 TotalParams = ArgCount = i; 00786 break; 00787 } 00788 } 00789 } 00790 } 00791 RegCloseKey (hParamKey); 00792 } 00793 if (ArgCount == 1) 00794 { 00795 00796 /* 00797 * No startup args are given 00798 */ 00799 ThreadInputParams.Argc = argc; 00800 ThreadInputParams.Argv = argv; 00801 } 00802 00803 else 00804 { 00805 ThreadInputParams.Argc = ArgCount; 00806 ThreadInputParams.Argv = ArgArray; 00807 } 00808 00809 /* 00810 * Register Service Control Handler 00811 */ 00812 hServiceStatus = RegisterServiceCtrlHandler (app_name_long, ControlHandler); 00813 if (hServiceStatus == 0) 00814 { 00815 WriteToEventLog (EVENTLOG_ERROR_TYPE, 00816 _T ("RegisterServiceCtrlHandler failed")); 00817 return; 00818 } 00819 00820 /* 00821 * Update the service status to START_PENDING. 00822 */ 00823 UpdateServiceStatus (SERVICE_START_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); 00824 00825 /* 00826 * Start the worker thread, which does the majority of the work . 00827 */ 00828 TRY 00829 { 00830 if (SetSimpleSecurityAttributes (&SecurityAttributes) == FALSE) 00831 { 00832 WriteToEventLog (EVENTLOG_ERROR_TYPE, 00833 _T ("Couldn't init security attributes")); 00834 LEAVE; 00835 } 00836 hServiceThread = 00837 (void *) _beginthreadex (&SecurityAttributes, 0, 00838 ThreadFunction, 00839 (void *) &ThreadInputParams, 0, &threadId); 00840 if (hServiceThread == NULL) 00841 { 00842 WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start worker thread")); 00843 LEAVE; 00844 } 00845 00846 /* 00847 * Set service status to SERVICE_RUNNING. 00848 */ 00849 UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); 00850 00851 /* 00852 * Wait until the worker thread finishes. 00853 */ 00854 WaitForSingleObject (hServiceThread, INFINITE); 00855 } 00856 FINALLY 00857 { 00858 /* 00859 * Release resources 00860 */ 00861 UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL); 00862 if (hServiceThread) 00863 CloseHandle (hServiceThread); 00864 FreeSecurityAttributes (&SecurityAttributes); 00865 00866 /* 00867 * Free allocated argument list 00868 */ 00869 if (ArgCount > 1 && ArgArray != NULL) 00870 { 00871 /* 00872 * Free all strings 00873 */ 00874 for (i = 0; i < ArgCount; i++) 00875 { 00876 free (ArgArray[i]); 00877 } 00878 free (ArgArray); 00879 } 00880 } 00881 } 00882 00883 /* 00884 * Function to start as Windows service 00885 * The calling party should specify their entry point as input parameter 00886 * Returns TRUE if the Service is started successfully 00887 */ 00888 BOOL 00889 RunAsService (INT (*ServiceFunction) (INT, LPTSTR *)) 00890 { 00891 00892 /* 00893 * Set the ServiceEntryPoint 00894 */ 00895 ServiceEntryPoint = ServiceFunction; 00896 00897 /* 00898 * By default, mark as Running as a service 00899 */ 00900 g_fRunningAsService = TRUE; 00901 00902 /* 00903 * Initialize ServiceTableEntry table 00904 */ 00905 ServiceTableEntry[0].lpServiceName = app_name_long; /* Application Name */ 00906 00907 /* 00908 * Call SCM via StartServiceCtrlDispatcher to run as Service 00909 * * If the function returns TRUE we are running as Service, 00910 */ 00911 if (StartServiceCtrlDispatcher (ServiceTableEntry) == FALSE) 00912 { 00913 g_fRunningAsService = FALSE; 00914 00915 /* 00916 * Some other error has occurred. 00917 */ 00918 WriteToEventLog (EVENTLOG_ERROR_TYPE, 00919 _T ("Couldn't start service - %s"), app_name_long); 00920 } 00921 return g_fRunningAsService; 00922 } 00923 00924 /* 00925 * Service control handler function 00926 * Responds to SCM commands/requests 00927 * This service handles 4 commands 00928 * - interrogate, pause, continue and stop. 00929 */ 00930 VOID WINAPI 00931 ControlHandler (DWORD dwControl) 00932 { 00933 switch (dwControl) 00934 { 00935 case SERVICE_CONTROL_INTERROGATE: 00936 ProcessServiceInterrogate (); 00937 break; 00938 00939 case SERVICE_CONTROL_PAUSE: 00940 ProcessServicePause (); 00941 break; 00942 00943 case SERVICE_CONTROL_CONTINUE: 00944 ProcessServiceContinue (); 00945 break; 00946 00947 case SERVICE_CONTROL_STOP: 00948 ProcessServiceStop (); 00949 break; 00950 } 00951 } 00952 00953 /* 00954 * To stop the service. 00955 * If a stop function was registered, invoke it, 00956 * otherwise terminate the worker thread. 00957 * After stopping, Service status is set to STOP in 00958 * main loop 00959 */ 00960 VOID 00961 ProcessServiceStop (VOID) 00962 { 00963 UpdateServiceStatus (SERVICE_STOP_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); 00964 00965 if (StopFunction != NULL) 00966 { 00967 (*StopFunction) (); 00968 } 00969 00970 else 00971 { 00972 TerminateThread (hServiceThread, 0); 00973 } 00974 } 00975 00976 /* 00977 * Returns the current state of the service to the SCM. 00978 */ 00979 VOID 00980 ProcessServiceInterrogate (VOID) 00981 { 00982 ReportCurrentServiceStatus (); 00983 } 00984 00985 /* 00986 * To Create a security descriptor with a NULL ACL, which 00987 * allows unlimited access. Returns a SECURITY_ATTRIBUTES 00988 * structure that contains the security descriptor. 00989 * The structure contains a dynamically allocated security 00990 * descriptor that must be freed either manually, or by 00991 * calling FreeSecurityAttributes 00992 */ 00993 BOOL 00994 SetSimpleSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr) 00995 { 00996 BOOL fReturn = FALSE; 00997 SECURITY_DESCRIPTOR *pSecurityDesc = NULL; 00998 00999 /* 01000 * If an invalid address is passed as a parameter, return 01001 * FALSE right away. 01002 */ 01003 if (!pSecurityAttr) 01004 return FALSE; 01005 pSecurityDesc = 01006 (SECURITY_DESCRIPTOR *) LocalAlloc (LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); 01007 if (!pSecurityDesc) 01008 return FALSE; 01009 fReturn = 01010 InitializeSecurityDescriptor (pSecurityDesc, SECURITY_DESCRIPTOR_REVISION); 01011 if (fReturn != FALSE) 01012 { 01013 fReturn = SetSecurityDescriptorDacl (pSecurityDesc, TRUE, NULL, FALSE); 01014 } 01015 if (fReturn != FALSE) 01016 { 01017 pSecurityAttr->nLength = sizeof (SECURITY_ATTRIBUTES); 01018 pSecurityAttr->lpSecurityDescriptor = pSecurityDesc; 01019 pSecurityAttr->bInheritHandle = TRUE; 01020 } 01021 01022 else 01023 { 01024 /* 01025 * Couldn't initialize or set security descriptor. 01026 */ 01027 LocalFree (pSecurityDesc); 01028 } 01029 return fReturn; 01030 } 01031 01032 /* 01033 * This function Frees the security descriptor, if any was created. 01034 */ 01035 VOID 01036 FreeSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr) 01037 { 01038 if (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor) 01039 LocalFree (pSecurityAttr->lpSecurityDescriptor); 01040 } 01041 01042 /* 01043 * This function runs in the worker thread 01044 * until an exit is forced, or until the SCM issues the STOP command. 01045 * Invokes registered service function 01046 * Returns when called registered function returns 01047 * 01048 * Input: 01049 * lpParam contains argc and argv, pass to service main function 01050 */ 01051 unsigned WINAPI 01052 ThreadFunction (LPVOID lpParam) 01053 { 01054 InputParams * pInputArg = (InputParams *) lpParam; 01055 return (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv); 01056 } 01057 01058 /* 01059 * This function is called to register an application-specific function 01060 * which is invoked when the SCM stops the worker thread. 01061 */ 01062 VOID 01063 RegisterStopFunction (VOID (*StopFunc) (VOID)) 01064 { 01065 StopFunction = StopFunc; 01066 } 01067 01068 /* 01069 * SCM pause command invokes this function 01070 * If the service is not running, this function does nothing. 01071 * Otherwise, suspend the worker thread and update the status. 01072 */ 01073 VOID 01074 ProcessServicePause (VOID) 01075 { 01076 if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) 01077 { 01078 UpdateServiceStatus (SERVICE_PAUSE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); 01079 01080 if (SuspendThread (hServiceThread) != -1) 01081 { 01082 UpdateServiceStatus (SERVICE_PAUSED, NO_ERROR, SCM_WAIT_INTERVAL); 01083 } 01084 } 01085 } 01086 01087 /* 01088 * SCM resume command invokes this function 01089 * If the service is not paused, this function does nothing. 01090 * Otherwise, resume the worker thread and update the status. 01091 */ 01092 VOID 01093 ProcessServiceContinue (VOID) 01094 { 01095 if (ServiceStatus.dwCurrentState == SERVICE_PAUSED) 01096 { 01097 UpdateServiceStatus (SERVICE_CONTINUE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); 01098 01099 if (ResumeThread (hServiceThread) != -1) 01100 { 01101 UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); 01102 } 01103 } 01104 } 01105 01106 #endif /* WIN32 */ 01107 01108