winservice.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  1. #if 0 //def _WIN32
  2. #include "net-snmp/net-snmp-config.h"
  3. #include "net-snmp/library/system.h"
  4. #include <stdlib.h> /* calloc() */
  5. #include <stdio.h> /* sprintf() */
  6. #include <windows.h>
  7. #include <process.h> /* _beginthreadex() */
  8. #include "net-snmp/library/winservice.h"
  9. #ifdef mingw32 /* MinGW doesn't fully support exception handling. */
  10. #define TRY if(1)
  11. #define LEAVE goto labelFIN
  12. #define FINALLY do { \
  13. labelFIN: \
  14. ; \
  15. } while(0); if(1)
  16. #else
  17. #define TRY __try
  18. #define LEAVE __leave
  19. #define FINALLY __finally
  20. #endif /* mingw32 */
  21. #if defined(WIN32) && defined(HAVE_WIN32_PLATFORM_SDK) && !defined(mingw32)
  22. #pragma comment(lib, "iphlpapi.lib")
  23. #endif
  24. #if defined(WIN32) && !defined(mingw32)
  25. #ifdef USING_WINEXTDLL_MODULE
  26. #pragma comment(lib, "snmpapi.lib")
  27. #pragma comment(lib, "mgmtapi.lib")
  28. #endif
  29. #endif
  30. /*
  31. * Error levels returned when registering or unregistering the service
  32. */
  33. enum service_status {
  34. SERVICE_ERROR_NONE = 0,
  35. SERVICE_ERROR_SCM_OPEN = 1, /* Can not open SCM */
  36. SERVICE_ERROR_CREATE_SERVICE = 2, /* Can not create service */
  37. SERVICE_ERROR_CREATE_REGISTRY_ENTRIES = 3, /* Can not create registry entries */
  38. SERVICE_ERROR_OPEN_SERVICE = 4, /* Can not open service (service does not exist) */
  39. };
  40. /*
  41. * Define Message catalog ID
  42. * MessageId: DISPLAY_MSG
  43. * MessageText: %1.
  44. */
  45. enum { DISPLAY_MSG = 0x00000064L };
  46. /*
  47. * Hint Value to SCM to wait before sending successive commands to service
  48. */
  49. enum { SCM_WAIT_INTERVAL = 7000 };
  50. static void WINAPI ServiceMain(DWORD argc, char *argv[]);
  51. static void WINAPI ControlHandler(DWORD dwControl);
  52. static void ProcessServiceStop(void);
  53. static void ProcessServicePause(void);
  54. static void ProcessServiceContinue(void);
  55. static void ProcessServiceInterrogate(void);
  56. static BOOL SetSimpleSecurityAttributes(SECURITY_ATTRIBUTES
  57. *pSecurityAttr);
  58. static void FreeSecurityAttributes(SECURITY_ATTRIBUTES *pSecurityAttr);
  59. static unsigned WINAPI ThreadFunction(void *lpParam);
  60. /*
  61. * External global variables used here
  62. */
  63. /*
  64. * Application Name
  65. * This should be declared by the application, which wants to register as
  66. * windows service
  67. */
  68. extern "C" {
  69. char *m_pApp_name_long;
  70. }
  71. /*
  72. * Declare global variable
  73. */
  74. /*
  75. * Flag to indicate whether process is running as Service
  76. */
  77. static BOOL g_fRunningAsService;
  78. /*
  79. * Variable to maintain Current Service status
  80. */
  81. static SERVICE_STATUS ServiceStatus;
  82. /*
  83. * Service Handle
  84. */
  85. static SERVICE_STATUS_HANDLE hServiceStatus;
  86. /*
  87. * Service Table Entry
  88. */
  89. SERVICE_TABLE_ENTRY ServiceTableEntry[] = {
  90. { NULL, ServiceMain }, /* Service Main function */
  91. { NULL, NULL }
  92. };
  93. /*
  94. * Handle to Thread, to implement Pause, Resume and Stop functions
  95. */
  96. static HANDLE hServiceThread = NULL; /* Thread Handle */
  97. /*
  98. * Holds calling partys Function Entry point, that should start
  99. * when entering service mode
  100. */
  101. static int(*ServiceEntryPoint)(int Argc, char *Argv[]);
  102. /*
  103. * To hold Stop Function address, to be called when STOP request
  104. * received from the SCM
  105. */
  106. static void(*StopFunction)(void);
  107. /*
  108. * To update windows service status to SCM
  109. */
  110. static BOOL UpdateServiceStatus(DWORD dwStatus, DWORD dwErrorCode,
  111. DWORD dwWaitHint);
  112. /*
  113. * To Report current service status to SCM
  114. */
  115. static BOOL ReportCurrentServiceStatus(void);
  116. void ProcessError(WORD eventLogType, const char *pszMessage,
  117. int useGetLastError, int quiet);
  118. #ifndef HAVE__BEGINTHREADEX
  119. static uintptr_t
  120. _beginthreadex(void *security, unsigned stack_size,
  121. unsigned(__stdcall *start_address) (void *), void *arglist,
  122. unsigned initflag, unsigned *thrdaddr)
  123. {
  124. return (uintptr_t)CreateThread(security, stack_size,
  125. (LPTHREAD_START_ROUTINE)start_address,
  126. arglist, initflag, (LPDWORD)thrdaddr);
  127. }
  128. #if 0
  129. static void
  130. _endthreadex(unsigned retval)
  131. {
  132. ExitThread(retval);
  133. }
  134. #endif
  135. #endif
  136. /*
  137. * To register as Windows Service with SCM(Service Control Manager)
  138. * Input - Service Name, Service Display Name,Service Description and
  139. * Service startup arguments
  140. */
  141. int
  142. RegisterService(const char *lpszServiceName,
  143. const char *lpszServiceDisplayName,
  144. const char *lpszServiceDescription,
  145. InputParams *StartUpArg, int quiet)
  146. { /* Startup argument to the service */
  147. char szServicePath[MAX_PATH]; /* To hold module File name */
  148. char MsgErrorString[1024]; /* Message or Error string */
  149. char szServiceCommand[MAX_PATH + 9]; /* Command to execute */
  150. SC_HANDLE hSCManager = NULL;
  151. SC_HANDLE hService = NULL;
  152. static const char szRegAppLogKey[] =
  153. "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
  154. char szRegKey[512];
  155. HKEY hKey = NULL; /* Key to registry entry */
  156. HKEY hParamKey = NULL; /* To store startup parameters */
  157. DWORD dwData; /* Type of logging supported */
  158. int i, j; /* Loop variables */
  159. int exitStatus = 0;
  160. GetModuleFileName(NULL, szServicePath, MAX_PATH);
  161. TRY{
  162. /*
  163. * Open Service Control Manager handle
  164. */
  165. hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  166. if (hSCManager == NULL) {
  167. ProcessError(EVENTLOG_ERROR_TYPE,
  168. "Can't open SCM (Service Control Manager)", 1,
  169. quiet);
  170. exitStatus = SERVICE_ERROR_SCM_OPEN;
  171. LEAVE;
  172. }
  173. /*
  174. * Generate the command to be executed by the SCM
  175. */
  176. snprintf(szServiceCommand, sizeof(szServiceCommand),
  177. "\"%s\" %s", szServicePath, "-service");
  178. /*
  179. * Create the desired service
  180. */
  181. hService = CreateService(hSCManager, lpszServiceName, lpszServiceDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szServiceCommand, NULL, /* load-order group */
  182. NULL, /* group member tag */
  183. NULL, /* dependencies */
  184. NULL, /* account */
  185. NULL); /* password */
  186. if (hService == NULL) {
  187. snprintf(MsgErrorString, sizeof(MsgErrorString),
  188. "%s %s", "Can't create service",
  189. lpszServiceDisplayName);
  190. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  191. exitStatus = SERVICE_ERROR_CREATE_SERVICE;
  192. LEAVE;
  193. }
  194. /*
  195. * Create registry entries for the event log
  196. */
  197. /*
  198. * Create registry Application event log key
  199. */
  200. snprintf(szRegKey, sizeof(szRegKey), "%s%s", szRegAppLogKey,
  201. lpszServiceName);
  202. /*
  203. * Create registry key
  204. */
  205. if (RegCreateKey(HKEY_LOCAL_MACHINE, szRegKey, &hKey) !=
  206. ERROR_SUCCESS) {
  207. snprintf(MsgErrorString, sizeof(MsgErrorString),
  208. "%s %s",
  209. "is unable to create registry entries",
  210. lpszServiceDisplayName);
  211. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  212. exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  213. LEAVE;
  214. }
  215. /*
  216. * Add Event ID message file name to the 'EventMessageFile' subkey
  217. */
  218. RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ,
  219. (CONST BYTE *)szServicePath,
  220. strlen(szServicePath) + sizeof(char));
  221. /*
  222. * Set the supported types flags.
  223. */
  224. dwData =
  225. EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
  226. EVENTLOG_INFORMATION_TYPE;
  227. RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD,
  228. (CONST BYTE *)&dwData, sizeof(dwData));
  229. /*
  230. * Close Registry key
  231. */
  232. RegCloseKey(hKey);
  233. /*
  234. * Set Service Description String and save startup parameters if present
  235. */
  236. if (lpszServiceDescription != NULL || StartUpArg->Argc > 2) {
  237. /*
  238. * Create Registry Key path
  239. */
  240. snprintf(szRegKey, sizeof(szRegKey),
  241. "SYSTEM\\CurrentControlSet\\Services\\%s",
  242. m_pApp_name_long);
  243. hKey = NULL;
  244. /*
  245. * Open Registry key using Create and Set access.
  246. */
  247. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_WRITE,
  248. &hKey) != ERROR_SUCCESS) {
  249. snprintf(MsgErrorString, sizeof(MsgErrorString),
  250. "%s %s",
  251. "is unable to create registry entries",
  252. lpszServiceDisplayName);
  253. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 1,
  254. quiet);
  255. exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  256. LEAVE;
  257. }
  258. /*
  259. * Create description subkey and the set value
  260. */
  261. if (lpszServiceDescription != NULL) {
  262. if (RegSetValueEx(hKey, "Description", 0, REG_SZ,
  263. (CONST BYTE *)lpszServiceDescription,
  264. strlen(lpszServiceDescription) +
  265. sizeof(char)) != ERROR_SUCCESS) {
  266. snprintf(MsgErrorString, sizeof(MsgErrorString),
  267. "%s %s",
  268. "is unable to create registry entries",
  269. lpszServiceDisplayName);
  270. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 1,
  271. quiet);
  272. exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  273. LEAVE;
  274. };
  275. }
  276. /*
  277. * Save startup arguments if they are present
  278. */
  279. if (StartUpArg->Argc > 2) {
  280. /*
  281. * Create Subkey parameters
  282. */
  283. if (RegCreateKeyEx
  284. (hKey, "Parameters", 0, NULL,
  285. REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
  286. &hParamKey, NULL) != ERROR_SUCCESS) {
  287. snprintf(MsgErrorString, sizeof(MsgErrorString),
  288. "%s %s",
  289. "is unable to create registry entries",
  290. lpszServiceDisplayName);
  291. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 1,
  292. quiet);
  293. exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  294. LEAVE;
  295. }
  296. /*
  297. * Save parameters
  298. */
  299. /*
  300. * Loop through arguments
  301. */
  302. if (quiet) /* Make sure we don't store -quiet arg */
  303. i = 3;
  304. else
  305. i = 2;
  306. for (j = 1; i < StartUpArg->Argc; i++, j++) {
  307. snprintf(szRegKey, sizeof(szRegKey), "%s%d", "Param",
  308. j);
  309. /*
  310. * Create registry key
  311. */
  312. if (RegSetValueEx
  313. (hParamKey, szRegKey, 0, REG_SZ,
  314. (CONST BYTE *)StartUpArg->Argv[i],
  315. strlen(StartUpArg->Argv[i]) +
  316. sizeof(char)) != ERROR_SUCCESS) {
  317. snprintf(MsgErrorString, sizeof(MsgErrorString),
  318. "%s %s",
  319. "is unable to create registry entries",
  320. lpszServiceDisplayName);
  321. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString,
  322. 1, quiet);
  323. exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  324. LEAVE;
  325. };
  326. }
  327. }
  328. /*
  329. * Everything is set, delete hKey
  330. */
  331. RegCloseKey(hParamKey);
  332. RegCloseKey(hKey);
  333. }
  334. /*
  335. * Ready to log messages
  336. */
  337. /*
  338. * Successfully registered as service
  339. */
  340. snprintf(MsgErrorString, sizeof(MsgErrorString), "%s %s",
  341. lpszServiceName, "successfully registered as a service");
  342. /*
  343. * Log message to eventlog
  344. */
  345. ProcessError(EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet);
  346. }
  347. FINALLY{
  348. if (hSCManager)
  349. CloseServiceHandle(hSCManager);
  350. if (hService)
  351. CloseServiceHandle(hService);
  352. if (hKey)
  353. RegCloseKey(hKey);
  354. if (hParamKey)
  355. RegCloseKey(hParamKey);
  356. }
  357. return (exitStatus);
  358. }
  359. /*
  360. * Unregister the service with the Windows SCM
  361. * Input - ServiceName
  362. */
  363. int
  364. UnregisterService(const char *lpszServiceName, int quiet)
  365. {
  366. char MsgErrorString[1024]; /* Message or Error string */
  367. SC_HANDLE hSCManager = NULL; /* SCM handle */
  368. SC_HANDLE hService = NULL; /* Service Handle */
  369. SERVICE_STATUS sStatus;
  370. static const char szRegAppLogKey[] =
  371. "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
  372. char szRegKey[512];
  373. int exitStatus = 0;
  374. /*
  375. * HKEY hKey = NULL; ?* Key to registry entry
  376. */
  377. TRY{
  378. /*
  379. * Open Service Control Manager
  380. */
  381. hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  382. if (hSCManager == NULL) {
  383. ProcessError(EVENTLOG_ERROR_TYPE,
  384. "Can't open SCM (Service Control Manager)", 1,
  385. quiet);
  386. exitStatus = SERVICE_ERROR_SCM_OPEN;
  387. LEAVE;
  388. }
  389. /*
  390. * Open registered service
  391. */
  392. hService =
  393. OpenService(hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);
  394. if (hService == NULL) {
  395. snprintf(MsgErrorString, sizeof(MsgErrorString),
  396. "%s %s", "Can't open service", lpszServiceName);
  397. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  398. exitStatus = SERVICE_ERROR_OPEN_SERVICE;
  399. LEAVE;
  400. }
  401. /*
  402. * Query service status
  403. * If running stop before deleting
  404. */
  405. if (QueryServiceStatus(hService, &sStatus)) {
  406. if (sStatus.dwCurrentState == SERVICE_RUNNING
  407. || sStatus.dwCurrentState == SERVICE_PAUSED) {
  408. ControlService(hService, SERVICE_CONTROL_STOP, &sStatus);
  409. }
  410. };
  411. /*
  412. * Delete the service
  413. */
  414. if (DeleteService(hService) == FALSE) {
  415. snprintf(MsgErrorString, sizeof(MsgErrorString),
  416. "%s %s", "Can't delete service", lpszServiceName);
  417. /*
  418. * Log message to eventlog
  419. */
  420. ProcessError(EVENTLOG_ERROR_TYPE, MsgErrorString, 0, quiet);
  421. LEAVE;
  422. }
  423. /*
  424. * Log "Service deleted successfully " message to eventlog
  425. */
  426. snprintf(MsgErrorString, sizeof(MsgErrorString), "%s %s",
  427. lpszServiceName, "service deleted");
  428. ProcessError(EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet);
  429. /*
  430. * Delete registry entries for EventLog
  431. */
  432. snprintf(szRegKey, sizeof(szRegKey), "%s%s", szRegAppLogKey,
  433. lpszServiceName);
  434. RegDeleteKey(HKEY_LOCAL_MACHINE, szRegKey);
  435. }
  436. /*
  437. * Delete the handles
  438. */
  439. FINALLY{
  440. if (hService)
  441. CloseServiceHandle(hService);
  442. if (hSCManager)
  443. CloseServiceHandle(hSCManager);
  444. }
  445. return (exitStatus);
  446. }
  447. /*
  448. * Write a message to the Windows event log.
  449. */
  450. static void
  451. WriteToEventLog(WORD wType, const char *pszFormat, ...)
  452. {
  453. char szMessage[512];
  454. const char *LogStr[1];
  455. va_list ArgList;
  456. HANDLE hEventSource = NULL;
  457. va_start(ArgList, pszFormat);
  458. vsnprintf(szMessage, sizeof(szMessage), pszFormat, ArgList);
  459. va_end(ArgList);
  460. LogStr[0] = szMessage;
  461. hEventSource = RegisterEventSource(NULL, m_pApp_name_long);
  462. if (hEventSource == NULL)
  463. return;
  464. ReportEvent(hEventSource, wType, 0,
  465. DISPLAY_MSG, NULL, 1, 0, LogStr, NULL);
  466. DeregisterEventSource(hEventSource);
  467. }
  468. /*
  469. * Pre-process the second command-line argument from the user.
  470. * Service related options are:
  471. * -register - registers the service
  472. * -unregister - unregisters the service
  473. * -service - run as service
  474. * other command-line arguments are ignored here.
  475. *
  476. * Return: Type indicating the option specified
  477. */
  478. enum net_snmp_cmd_line_action
  479. ParseCmdLineForServiceOption(int argc, char *argv[], int *quiet)
  480. {
  481. enum net_snmp_cmd_line_action nReturn = RUN_AS_CONSOLE; /* default is to run as a console application */
  482. if (argc >= 2) {
  483. /*
  484. * second argument present
  485. */
  486. if (strcasecmp("-register", argv[1]) == 0) {
  487. nReturn = REGISTER_SERVICE;
  488. }
  489. else if (strcasecmp("-unregister", argv[1]) == 0) {
  490. nReturn = UN_REGISTER_SERVICE;
  491. }
  492. else if (strcasecmp("-service", argv[1]) == 0) {
  493. nReturn = RUN_AS_SERVICE;
  494. }
  495. }
  496. #if 1
  497. if (argc >= 3) {
  498. /*
  499. * third argument present
  500. */
  501. if (strcasecmp("-quiet", argv[2]) == 0) {
  502. *quiet = 1;
  503. }
  504. }
  505. #endif
  506. return nReturn;
  507. }
  508. /*
  509. * Write error message to event log, console or pop-up window.
  510. *
  511. * If useGetLastError is 1, the last error returned from GetLastError()
  512. * is appended to pszMessage, separated by a ": ".
  513. *
  514. * eventLogType: MessageBox equivalent:
  515. *
  516. * EVENTLOG_INFORMATION_TYPE MB_ICONASTERISK
  517. * EVENTLOG_WARNING_TYPE MB_ICONEXCLAMATION
  518. * EVENTLOG_ERROR_TYPE MB_ICONSTOP
  519. *
  520. */
  521. void
  522. ProcessError(WORD eventLogType, const char *pszMessage,
  523. int useGetLastError, int quiet)
  524. {
  525. HANDLE hEventSource = NULL;
  526. char pszMessageFull[1024]; /* Combined pszMessage and GetLastError */
  527. /*
  528. * If useGetLastError enabled, generate text from GetLastError() and append to
  529. * pszMessageFull
  530. */
  531. if (useGetLastError) {
  532. char *pErrorMsgTemp = NULL;
  533. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  534. FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
  535. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  536. (LPTSTR)&pErrorMsgTemp, 0, NULL);
  537. snprintf(pszMessageFull, sizeof(pszMessageFull), "%s: %s",
  538. pszMessage, pErrorMsgTemp);
  539. if (pErrorMsgTemp) {
  540. LocalFree(pErrorMsgTemp);
  541. pErrorMsgTemp = NULL;
  542. }
  543. }
  544. else {
  545. snprintf(pszMessageFull, sizeof(pszMessageFull), "%s", pszMessage);
  546. }
  547. hEventSource = RegisterEventSource(NULL, m_pApp_name_long);
  548. if (hEventSource != NULL) {
  549. const char *LogStr[1];
  550. LogStr[0] = pszMessageFull;
  551. if (ReportEvent(hEventSource, eventLogType, 0, DISPLAY_MSG, /* just output the text to the event log */
  552. NULL, 1, 0, LogStr, NULL)) {
  553. }
  554. else {
  555. char *pErrorMsgTemp = NULL;
  556. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  557. FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
  558. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  559. (LPTSTR)&pErrorMsgTemp, 0, NULL);
  560. fprintf(stderr,
  561. "Could NOT lot to Event Log. Error returned from ReportEvent(): %s\n",
  562. pErrorMsgTemp);
  563. if (pErrorMsgTemp) {
  564. LocalFree(pErrorMsgTemp);
  565. pErrorMsgTemp = NULL;
  566. }
  567. }
  568. DeregisterEventSource(hEventSource);
  569. }
  570. if (quiet) {
  571. fprintf(stderr, "%s\n", pszMessageFull);
  572. }
  573. else {
  574. switch (eventLogType) {
  575. case EVENTLOG_INFORMATION_TYPE:
  576. MessageBox(NULL, pszMessageFull, m_pApp_name_long,
  577. MB_ICONASTERISK);
  578. break;
  579. case EVENTLOG_WARNING_TYPE:
  580. MessageBox(NULL, pszMessageFull, m_pApp_name_long,
  581. MB_ICONEXCLAMATION);
  582. break;
  583. case EVENTLOG_ERROR_TYPE:
  584. MessageBox(NULL, pszMessageFull, m_pApp_name_long, MB_ICONSTOP);
  585. break;
  586. default:
  587. MessageBox(NULL, pszMessageFull, m_pApp_name_long,
  588. EVENTLOG_WARNING_TYPE);
  589. break;
  590. }
  591. }
  592. }
  593. /*
  594. * Update current service status.
  595. * Sends the current service status to the SCM. Also updates
  596. * the global service status structure.
  597. */
  598. static BOOL
  599. UpdateServiceStatus(DWORD dwStatus, DWORD dwErrorCode, DWORD dwWaitHint)
  600. {
  601. static DWORD dwCheckpoint = 1;
  602. DWORD dwControls =
  603. SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
  604. if (g_fRunningAsService == FALSE)
  605. return FALSE;
  606. ZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
  607. ServiceStatus.dwServiceType = SERVICE_WIN32;
  608. ServiceStatus.dwCurrentState = dwStatus;
  609. ServiceStatus.dwWaitHint = dwWaitHint;
  610. if (dwErrorCode) {
  611. ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  612. ServiceStatus.dwServiceSpecificExitCode = dwErrorCode;
  613. }
  614. /*
  615. * special cases that depend on the new state
  616. */
  617. switch (dwStatus) {
  618. case SERVICE_START_PENDING:
  619. dwControls = 0;
  620. break;
  621. case SERVICE_RUNNING:
  622. case SERVICE_STOPPED:
  623. dwCheckpoint = 0;
  624. break;
  625. }
  626. ServiceStatus.dwCheckPoint = dwCheckpoint++;
  627. ServiceStatus.dwControlsAccepted = dwControls;
  628. return ReportCurrentServiceStatus();
  629. }
  630. /*
  631. * Reports current service status to SCM
  632. */
  633. static BOOL
  634. ReportCurrentServiceStatus()
  635. {
  636. return SetServiceStatus(hServiceStatus, &ServiceStatus);
  637. }
  638. /*
  639. * ServiceMain function.
  640. */
  641. static void WINAPI
  642. ServiceMain(DWORD argc, char *argv[])
  643. {
  644. SECURITY_ATTRIBUTES SecurityAttributes;
  645. unsigned threadId;
  646. /*
  647. * Input arguments
  648. */
  649. DWORD ArgCount = 0;
  650. char **ArgArray = NULL;
  651. char szRegKey[512];
  652. HKEY hParamKey = NULL;
  653. DWORD TotalParams = 0;
  654. int i;
  655. InputParams ThreadInputParams;
  656. /*
  657. * Build the Input parameters to pass to worker thread
  658. */
  659. /*
  660. * SCM sends Service Name as first arg, increment to point
  661. * arguments user specified while starting control agent
  662. */
  663. /*
  664. * Read registry parameter
  665. */
  666. ArgCount = 1;
  667. /*
  668. * Create registry key path
  669. */
  670. snprintf(szRegKey, sizeof(szRegKey), "%s%s\\%s",
  671. "SYSTEM\\CurrentControlSet\\Services\\", m_pApp_name_long,
  672. "Parameters");
  673. if (RegOpenKeyEx
  674. (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS,
  675. &hParamKey) == ERROR_SUCCESS) {
  676. /*
  677. * Read startup configuration information
  678. */
  679. /*
  680. * Find number of subkeys inside parameters
  681. */
  682. if (RegQueryInfoKey(hParamKey, NULL, NULL, 0,
  683. NULL, NULL, NULL, &TotalParams,
  684. NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
  685. if (TotalParams != 0) {
  686. ArgCount += TotalParams;
  687. /*
  688. * Allocate memory to hold strings
  689. */
  690. ArgArray = (char**)calloc(ArgCount, sizeof(ArgArray[0]));
  691. if (ArgArray == 0) {
  692. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  693. "Resource failure");
  694. return;
  695. }
  696. /*
  697. * Copy first argument
  698. */
  699. ArgArray[0] = strdup(argv[0]);
  700. for (i = 1; i <= TotalParams; i++) {
  701. DWORD dwErrorcode;
  702. DWORD nSize;
  703. DWORD nRegkeyType;
  704. char *szValue;
  705. /*
  706. * Create Subkey value name
  707. */
  708. snprintf(szRegKey, sizeof(szRegKey), "%s%d", "Param",
  709. i);
  710. /*
  711. * Query subkey.
  712. */
  713. nSize = 0;
  714. dwErrorcode =
  715. RegQueryValueEx(hParamKey, szRegKey, NULL,
  716. &nRegkeyType, NULL, &nSize);
  717. if (dwErrorcode == ERROR_SUCCESS) {
  718. if (nRegkeyType == REG_SZ
  719. || nRegkeyType == REG_EXPAND_SZ) {
  720. szValue = (char*)malloc(nSize + sizeof(szValue[0]));
  721. if (szValue) {
  722. dwErrorcode =
  723. RegQueryValueEx(hParamKey, szRegKey,
  724. NULL, &nRegkeyType,
  725. (LPBYTE)szValue,
  726. &nSize);
  727. if (dwErrorcode == ERROR_SUCCESS) {
  728. szValue[nSize] = 0;
  729. ArgArray[i] = szValue;
  730. }
  731. else {
  732. free(szValue);
  733. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  734. "Querying registry key %s failed: error code %ld",
  735. szRegKey, dwErrorcode);
  736. }
  737. }
  738. else
  739. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  740. "Querying registry key %s failed: out of memory",
  741. szRegKey);
  742. }
  743. else
  744. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  745. "Type %ld of registry key %s is incorrect",
  746. nRegkeyType, szRegKey);
  747. }
  748. else
  749. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  750. "Querying registry key %s failed: error code %ld",
  751. szRegKey, dwErrorcode);
  752. if (!ArgArray[i]) {
  753. TotalParams = ArgCount = i;
  754. break;
  755. }
  756. }
  757. }
  758. }
  759. RegCloseKey(hParamKey);
  760. }
  761. if (ArgCount == 1) {
  762. /*
  763. * No startup args are given
  764. */
  765. ThreadInputParams.Argc = argc;
  766. ThreadInputParams.Argv = argv;
  767. }
  768. else {
  769. ThreadInputParams.Argc = ArgCount;
  770. ThreadInputParams.Argv = ArgArray;
  771. }
  772. /*
  773. * Register Service Control Handler
  774. */
  775. hServiceStatus =
  776. RegisterServiceCtrlHandler(m_pApp_name_long, ControlHandler);
  777. if (hServiceStatus == 0) {
  778. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  779. "RegisterServiceCtrlHandler failed");
  780. return;
  781. }
  782. /*
  783. * Update the service status to START_PENDING.
  784. */
  785. UpdateServiceStatus(SERVICE_START_PENDING, NO_ERROR,
  786. SCM_WAIT_INTERVAL);
  787. /*
  788. * Start the worker thread, which does the majority of the work .
  789. */
  790. TRY{
  791. if (SetSimpleSecurityAttributes(&SecurityAttributes) == FALSE) {
  792. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  793. "Couldn't init security attributes");
  794. LEAVE;
  795. }
  796. hServiceThread =
  797. (void *)_beginthreadex(&SecurityAttributes, 0,
  798. ThreadFunction,
  799. (void *)&ThreadInputParams, 0,
  800. &threadId);
  801. if (hServiceThread == NULL) {
  802. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  803. "Couldn't start worker thread");
  804. LEAVE;
  805. }
  806. /*
  807. * Set service status to SERVICE_RUNNING.
  808. */
  809. UpdateServiceStatus(SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL);
  810. /*
  811. * Wait until the worker thread finishes.
  812. */
  813. WaitForSingleObject(hServiceThread, INFINITE);
  814. }
  815. FINALLY{
  816. /*
  817. * Release resources
  818. */
  819. UpdateServiceStatus(SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL);
  820. if (hServiceThread)
  821. CloseHandle(hServiceThread);
  822. FreeSecurityAttributes(&SecurityAttributes);
  823. /*
  824. * Free allocated argument list
  825. */
  826. if (ArgCount > 1 && ArgArray != NULL) {
  827. /*
  828. * Free all strings
  829. */
  830. for (i = 0; i < ArgCount; i++) {
  831. free(ArgArray[i]);
  832. }
  833. free(ArgArray);
  834. }
  835. }
  836. }
  837. /*
  838. * Function to start as Windows service
  839. * The calling party should specify their entry point as input parameter
  840. * Returns TRUE if the Service is started successfully
  841. */
  842. BOOL
  843. RunAsService(int(*ServiceFunction)(int, char **))
  844. {
  845. /*
  846. * Set the ServiceEntryPoint
  847. */
  848. ServiceEntryPoint = ServiceFunction;
  849. /*
  850. * By default, mark as Running as a service
  851. */
  852. g_fRunningAsService = TRUE;
  853. /*
  854. * Initialize ServiceTableEntry table
  855. */
  856. ServiceTableEntry[0].lpServiceName = m_pApp_name_long; /* Application Name */
  857. /*
  858. * Call SCM via StartServiceCtrlDispatcher to run as Service
  859. * * If the function returns TRUE we are running as Service,
  860. */
  861. if (StartServiceCtrlDispatcher(ServiceTableEntry) == FALSE) {
  862. g_fRunningAsService = FALSE;
  863. /*
  864. * Some other error has occurred.
  865. */
  866. WriteToEventLog(EVENTLOG_ERROR_TYPE,
  867. "Couldn't start service - %s", m_pApp_name_long);
  868. }
  869. return g_fRunningAsService;
  870. }
  871. /*
  872. * Service control handler function
  873. * Responds to SCM commands/requests
  874. * This service handles 4 commands
  875. * - interrogate, pause, continue and stop.
  876. */
  877. void WINAPI
  878. ControlHandler(DWORD dwControl)
  879. {
  880. switch (dwControl) {
  881. case SERVICE_CONTROL_INTERROGATE:
  882. ProcessServiceInterrogate();
  883. break;
  884. case SERVICE_CONTROL_PAUSE:
  885. ProcessServicePause();
  886. break;
  887. case SERVICE_CONTROL_CONTINUE:
  888. ProcessServiceContinue();
  889. break;
  890. case SERVICE_CONTROL_STOP:
  891. ProcessServiceStop();
  892. break;
  893. }
  894. }
  895. /*
  896. * To stop the service.
  897. * If a stop function was registered, invoke it,
  898. * otherwise terminate the worker thread.
  899. * After stopping, Service status is set to STOP in
  900. * main loop
  901. */
  902. void
  903. ProcessServiceStop(void)
  904. {
  905. UpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
  906. if (StopFunction != NULL) {
  907. (*StopFunction) ();
  908. }
  909. else {
  910. TerminateThread(hServiceThread, 0);
  911. }
  912. }
  913. /*
  914. * Returns the current state of the service to the SCM.
  915. */
  916. void
  917. ProcessServiceInterrogate(void)
  918. {
  919. ReportCurrentServiceStatus();
  920. }
  921. /*
  922. * To Create a security descriptor with a NULL ACL, which
  923. * allows unlimited access. Returns a SECURITY_ATTRIBUTES
  924. * structure that contains the security descriptor.
  925. * The structure contains a dynamically allocated security
  926. * descriptor that must be freed either manually, or by
  927. * calling FreeSecurityAttributes
  928. */
  929. BOOL
  930. SetSimpleSecurityAttributes(SECURITY_ATTRIBUTES *pSecurityAttr)
  931. {
  932. BOOL fReturn = FALSE;
  933. SECURITY_DESCRIPTOR *pSecurityDesc = NULL;
  934. /*
  935. * If an invalid address is passed as a parameter, return
  936. * FALSE right away.
  937. */
  938. if (!pSecurityAttr)
  939. return FALSE;
  940. pSecurityDesc =
  941. (SECURITY_DESCRIPTOR *)LocalAlloc(LPTR,
  942. SECURITY_DESCRIPTOR_MIN_LENGTH);
  943. if (!pSecurityDesc)
  944. return FALSE;
  945. fReturn =
  946. InitializeSecurityDescriptor(pSecurityDesc,
  947. SECURITY_DESCRIPTOR_REVISION);
  948. if (fReturn != FALSE) {
  949. fReturn =
  950. SetSecurityDescriptorDacl(pSecurityDesc, TRUE, NULL, FALSE);
  951. }
  952. if (fReturn != FALSE) {
  953. pSecurityAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
  954. pSecurityAttr->lpSecurityDescriptor = pSecurityDesc;
  955. pSecurityAttr->bInheritHandle = TRUE;
  956. }
  957. else {
  958. /*
  959. * Couldn't initialize or set security descriptor.
  960. */
  961. LocalFree(pSecurityDesc);
  962. }
  963. return fReturn;
  964. }
  965. /*
  966. * This function Frees the security descriptor, if any was created.
  967. */
  968. void
  969. FreeSecurityAttributes(SECURITY_ATTRIBUTES *pSecurityAttr)
  970. {
  971. if (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor)
  972. LocalFree(pSecurityAttr->lpSecurityDescriptor);
  973. }
  974. /*
  975. * This function runs in the worker thread
  976. * until an exit is forced, or until the SCM issues the STOP command.
  977. * Invokes registered service function
  978. * Returns when called registered function returns
  979. *
  980. * Input:
  981. * lpParam contains argc and argv, pass to service main function
  982. */
  983. unsigned WINAPI
  984. ThreadFunction(void *lpParam)
  985. {
  986. InputParams *pInputArg = (InputParams *)lpParam;
  987. return (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv);
  988. }
  989. /*
  990. * This function is called to register an application-specific function
  991. * which is invoked when the SCM stops the worker thread.
  992. */
  993. void
  994. RegisterStopFunction(void(*StopFunc)(void))
  995. {
  996. StopFunction = StopFunc;
  997. }
  998. /*
  999. * SCM pause command invokes this function
  1000. * If the service is not running, this function does nothing.
  1001. * Otherwise, suspend the worker thread and update the status.
  1002. */
  1003. void
  1004. ProcessServicePause(void)
  1005. {
  1006. if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
  1007. UpdateServiceStatus(SERVICE_PAUSE_PENDING, NO_ERROR,
  1008. SCM_WAIT_INTERVAL);
  1009. if (SuspendThread(hServiceThread) != -1) {
  1010. UpdateServiceStatus(SERVICE_PAUSED, NO_ERROR,
  1011. SCM_WAIT_INTERVAL);
  1012. }
  1013. }
  1014. }
  1015. /*
  1016. * SCM resume command invokes this function
  1017. * If the service is not paused, this function does nothing.
  1018. * Otherwise, resume the worker thread and update the status.
  1019. */
  1020. void
  1021. ProcessServiceContinue(void)
  1022. {
  1023. if (ServiceStatus.dwCurrentState == SERVICE_PAUSED) {
  1024. UpdateServiceStatus(SERVICE_CONTINUE_PENDING, NO_ERROR,
  1025. SCM_WAIT_INTERVAL);
  1026. if (ResumeThread(hServiceThread) != -1) {
  1027. UpdateServiceStatus(SERVICE_RUNNING, NO_ERROR,
  1028. SCM_WAIT_INTERVAL);
  1029. }
  1030. }
  1031. }
  1032. #endif /* WIN32 */