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