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