added files
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / winservice.c
1 /*
2  * \r
3  * * Windows Service related function definitions\r
4  * * By Raju Krishnappa(raju_krishnappa@yahoo.com)\r
5  * *\r
6  */  \r
7     \r
8 #include <windows.h>\r
9 #include <tchar.h>\r
10     \r
11 #include <stdio.h>   /* sprintf */\r
12 #include <process.h>  /* beginthreadex  */\r
13     \r
14 #include <net-snmp/library/winservice.h>\r
15     \r
16     /*
17      * \r
18      * * External global variables used here\r
19      */ \r
20     \r
21     /*
22      * Application Name 
23      */ \r
24     /*
25      * This should be decalred by the application, which wants to register as\r
26      * * windows servcie\r
27      */ \r
28 extern LPTSTR   g_szAppName;
29 \r\r
30     /*
31      * \r
32      * * Declare global variable\r
33      */ \r
34     \r
35     /*
36      * Flag to indicate, whether process is running as Service 
37      */ \r
38     BOOL g_fRunningAsService = FALSE;
39 \r\r
40     /*
41      * Varibale to maintain Current Service status 
42      */ \r
43 static SERVICE_STATUS ServiceStatus;
44 \r\r
45     /*
46      * Service Handle 
47      */ \r
48 static SERVICE_STATUS_HANDLE hServiceStatus = 0L;
49 \r\r
50     /*
51      * Service Table Entry 
52      */ \r
53     SERVICE_TABLE_ENTRY ServiceTableEntry[] = {
54     \rNULL, ServiceMain, /* Service Main function */ \r
55 NULL, NULL};
56
57 \r\r
58     /*
59      * Handle to Thread, to implement Pause,Resume and stop funcitonality 
60      */ \r
61 static HANDLE   hServiceThread = NULL;  /* Thread Handle */
62 \r\r\r
63     /*
64      * Holds calling partys Function Entry point, that should started \r
65      * * when entered to service mode\r
66      */ \r
67 static          INT(*ServiceEntryPoint) (INT Argc, LPTSTR Argv[]) = 0L;
68 \r\r
69     /*
70      * \r
71      * * To hold Stop Function address, to be called when STOP request\r
72      * * recived from the SCM\r
73      */ \r
74 static          VOID(*StopFunction) () = 0L;
75 \r\r\r
76     /*
77      * \r
78      * * To register as Windows Service with SCM(Service Control Manager)\r
79      * * Input - Service Name, Serivce Display Name,Service Description and\r
80      * * Service startup arguments\r
81      */ \r
82     \rVOID RegisterService(LPCTSTR lpszServiceName, \rLPCTSTR lpszServiceDisplayName, \rLPCTSTR lpszServiceDescription, \rInputParams * StartUpArg) /* Startup argument to the service */
83     \r
84 {
85     \rTCHAR szServicePath[MAX_PATH];     /* To hold module File name */
86     \rTCHAR MsgErrorString[MAX_STR_SIZE];        /* Message or Error string */
87     \rTCHAR szServiceCommand[MAX_PATH + 9];      /* Command to execute */
88     \r\rSC_HANDLE hSCManager = NULL;
89     \rSC_HANDLE hService = NULL;
90     \r\rTCHAR szRegAppLogKey[] =
91         "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
92     \rTCHAR szRegKey[512];
93     \rHKEY hKey = NULL;          /* Key to registry entry */
94     \rHKEY hParamKey = NULL;     /* To store startup parameters */
95     \rDWORD dwData;              /* Type of logging supported */
96     \r\rDWORD i, j;               /* Loop variables */
97     \r\rGetModuleFileName(NULL, szServicePath, MAX_PATH);
98     \r__try \r {
99         \r
100             /*
101              * Open Service Control Manager handle 
102              */ \r
103             hSCManager =
104             OpenSCManager(NULL, \rNULL, \rSC_MANAGER_CREATE_SERVICE);
105         \rif (hSCManager == NULL)
106             \r {
107             \rDisplayError(_T("Can't open SCM"));
108             \r__leave;
109             \r}
110         \r\r
111             /*
112              * Generate the Command to be executed by SCM 
113              */ \r
114             _stprintf(szServiceCommand, "%s %s", szServicePath,
115                       _T("-service"));
116         \r\r
117             /*
118              * Create the Desired service 
119              */ \r
120             hService = CreateService(hSCManager, \rlpszServiceName, \rlpszServiceDisplayName, \rSERVICE_ALL_ACCESS, \rSERVICE_WIN32_OWN_PROCESS, \rSERVICE_AUTO_START, \rSERVICE_ERROR_NORMAL, \rszServiceCommand, \rNULL,      /* load-order group */\r
121                                      NULL,      /* group member tag */\r
122                                      NULL,      /* dependencies */\r
123                                      NULL,      /* account */\r
124                                      NULL);     /* password */\r
125         \rif (hService == NULL)
126             \r {
127             \r
128                 /*
129                  * Generate Error String 
130                  */ \r
131                 _stprintf(MsgErrorString, "%s %s",
132                           _T("Can't Create Service"),
133                           lpszServiceDisplayName);
134             \rDisplayError(MsgErrorString);
135             \r__leave;
136             \r}
137         \r\r
138             /*
139              * Create registry entires for EventLog 
140              */ \r
141             /*
142              * Create registry Application event log key 
143              */ \r
144             _tcscpy(szRegKey, szRegAppLogKey);
145         \r_tcscat(szRegKey, lpszServiceName);
146         \r\r
147             /*
148              * Create registry key 
149              */ \r
150             if (RegCreateKey(HKEY_LOCAL_MACHINE, szRegKey, &hKey) !=
151                 ERROR_SUCCESS)
152             \r {
153             \r_stprintf(MsgErrorString, "%s %s",
154                        _T("Unable to create registry entires"),
155                        lpszServiceDisplayName);
156             \rDisplayError(MsgErrorString);
157             \r__leave;
158             \r}
159         \r\r
160             /*
161              * Add Event ID message file name to the 'EventMessageFile' subkey 
162              */ \r
163             RegSetValueEx(hKey, \r"EventMessageFile", \r0, \rREG_EXPAND_SZ, \r
164                           (CONST BYTE *) szServicePath,
165                           \r_tcslen(szServicePath) + sizeof(TCHAR));
166         \r\r
167             /*
168              * Set the supported types flags. 
169              */ \r
170             dwData =
171             EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
172             EVENTLOG_INFORMATION_TYPE;
173         \rRegSetValueEx(hKey, \r"TypesSupported", \r0, \rREG_DWORD,
174                        \r(CONST BYTE *) & dwData, \rsizeof(DWORD));
175         \r\r
176             /*
177              * Close Registry key 
178              */ \r
179             RegCloseKey(hKey);
180         \r\r
181             /*
182              * Set Service Description String  and save startup parameters if present
183              */ \r
184             if (lpszServiceDescription != NULL || StartUpArg->Argc > 2)
185             \r {
186             \r
187                 /*
188                  * Create Registry Key path 
189                  */ \r
190                 _tcscpy(szRegKey,
191                         _T("SYSTEM\\CurrentControlSet\\Services\\"));
192             \r_tcscat(szRegKey, g_szAppName);
193             \r\rhKey = NULL;
194             \r
195                 /*
196                  * Open Registry key 
197                  */ \r
198                 if (RegOpenKeyEx
199                     (HKEY_LOCAL_MACHINE, \rszRegKey, \r0, \rKEY_WRITE,
200                      /*
201                       * Create and Set access 
202                       */ \r
203                      &hKey) != ERROR_SUCCESS)
204                 \r {
205                 \r_stprintf(MsgErrorString, "%s %s",
206                            _T("Unable to create registry entires"),
207                            lpszServiceDisplayName);
208                 \rDisplayError(MsgErrorString);
209                 \r__leave;
210                 \r}
211             \r\r
212                 /*
213                  * Create description subkey and the set value 
214                  */ \r
215                 if (lpszServiceDescription != NULL)
216                 \r {
217                 \rif (RegSetValueEx(hKey, \r"Description", \r0, \rREG_SZ, \r
218                                    (CONST BYTE *) lpszServiceDescription,
219                                    \r_tcslen(lpszServiceDescription) +
220                                    sizeof(TCHAR)) != ERROR_SUCCESS)
221                     \r {
222                     \r_stprintf(MsgErrorString, "%s %s",
223                                _T("Unable to create registry entires"),
224                                lpszServiceDisplayName);
225                     \rDisplayError(MsgErrorString);
226                     \r__leave;
227                     \r};
228                 \r}
229             \r\r
230                 /*
231                  * Save startup arguments if they are present 
232                  */ \r
233                 if (StartUpArg->Argc > 2)
234                 \r {
235                 \r
236                     /*
237                      * Create Subkey parameters 
238                      */ \r
239                     if (RegCreateKeyEx
240                         (hKey, "Parameters", 0, NULL,
241                          \rREG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
242                          &hParamKey, NULL) != ERROR_SUCCESS)
243                     \r {
244                     \r_stprintf(MsgErrorString, "%s %s",
245                                _T("Unable to create registry entires"),
246                                lpszServiceDisplayName);
247                     \rDisplayError(MsgErrorString);
248                     \r__leave;
249                     \r}
250                 \r
251                     /*
252                      * Save parameters 
253                      */ \r
254                     \r
255                     /*
256                      * Loop through arguments 
257                      */ \r
258                     for (i = 2, j = 1; i < StartUpArg->Argc; i++, j++)
259                     \r {
260                     \r_stprintf(szRegKey, "%s%d", _T("Param"), j);
261                     \r
262                         /*
263                          * Create registry key 
264                          */ \r
265                         if (RegSetValueEx
266                             (hParamKey, \rszRegKey, \r0, \rREG_SZ,
267                              \r(CONST BYTE *) StartUpArg->Argv[i],
268                              \r_tcslen(StartUpArg->Argv[i]) +
269                              sizeof(TCHAR)) != ERROR_SUCCESS)
270                         \r {
271                         \r_stprintf(MsgErrorString, "%s %s",
272                                    _T("Unable to create registry entires"),
273                                    lpszServiceDisplayName);
274                         \rDisplayError(MsgErrorString);
275                         \r__leave;
276                         \r};
277                     \r}
278                 \r}
279             \r\r
280                 /*
281                  * Everything is set, delete hKey 
282                  */ \r
283                 RegCloseKey(hParamKey);
284             \rRegCloseKey(hKey);
285             \r}
286         \r\r
287             /*
288              * Ready to Log messages 
289              */ \r
290             \r
291             /*
292              * Successfully registered as service 
293              */ \r
294             _stprintf(MsgErrorString, "%s %s", lpszServiceName,
295                       _T("- Successfully registered as Service"));
296         \r\r
297             /*
298              * Log message to eventlog 
299              */ \r
300             WriteToEventLog(EVENTLOG_INFORMATION_TYPE, MsgErrorString);
301         \r\rMessageBox(NULL, \rMsgErrorString, \rg_szAppName,
302                      \rMB_ICONINFORMATION);
303     \r}
304     \r__finally \r {
305         \rif (hSCManager)
306             CloseServiceHandle(hSCManager);
307         \rif (hService)
308             CloseServiceHandle(hService);
309         \rif (hKey)
310             RegCloseKey(hKey);
311         \rif (hParamKey)
312             RegCloseKey(hParamKey);
313     \r}
314 \r}
315
316 \r\r\r
317     /*
318      * \r
319      * * Unregister the service with the  Windows SCM \r
320      * * Input - ServiceName\r
321      * *\r
322      */ \r
323     \rVOID UnregisterService(LPCSTR lpszServiceName) \r
324 {
325     \rTCHAR MsgErrorString[MAX_STR_SIZE];        /* Message or Error string */
326     \r\rSC_HANDLE hSCManager = NULL;      /* SCM handle */
327     \rSC_HANDLE hService = NULL; /* Service Handle */
328     \r\rSERVICE_STATUS sStatus;
329     \rTCHAR szRegAppLogKey[] =
330         "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
331     \rTCHAR szRegKey[512];
332     \rHKEY hKey = NULL;          /* Key to registry entry */
333     \r\r__try \r {
334         \r
335             /*
336              * Open Service Control Manager 
337              */ \r
338             hSCManager =
339             OpenSCManager(NULL, \rNULL, \rSC_MANAGER_CREATE_SERVICE);
340         \rif (hSCManager == NULL)
341             \r {
342             \r
343                 /*
344                  * Error while opening SCM 
345                  */ \r
346                 \rMessageBox(NULL, \r_T("Can't open SCM"), \rg_szAppName,
347                             \rMB_ICONHAND);
348             \r__leave;
349             \r}
350         \r\r
351             /*
352              * Open registered service 
353              */ \r
354             \rhService =
355             OpenService(hSCManager, \rlpszServiceName, \rSERVICE_ALL_ACCESS);
356         \r\rif (hService == NULL)
357             \r {
358             \r_stprintf(MsgErrorString, "%s %s", _T("Can't open service"),
359                        lpszServiceName);
360             \rMessageBox(NULL, \rMsgErrorString, \rg_szAppName, \rMB_ICONHAND);
361             \r__leave;
362             \r}
363         \r\r
364             /*
365              * Query service status 
366              */ \r
367             /*
368              * If running stop before deleting 
369              */ \r
370             if (QueryServiceStatus(hService, &sStatus))
371             \r {
372             \rif (sStatus.dwCurrentState == SERVICE_RUNNING
373                  || \rsStatus.dwCurrentState == SERVICE_PAUSED)
374                 \r {
375                 \r
376                     /*
377                      * Shutdown the service 
378                      */ \r
379                     ControlService(hService, SERVICE_CONTROL_STOP,
380                                    &sStatus);
381                 \r}
382             \r};
383         \r\r
384             /*
385              * Delete the service  
386              */ \r
387             if (DeleteService(hService) == FALSE)
388             \r {
389             \r_stprintf(MsgErrorString, "%s %s", _T("Can't delete service"),
390                        lpszServiceName);
391             \rMessageBox(NULL, \rMsgErrorString, \rg_szAppName, \rMB_ICONHAND);
392             \r
393                 /*
394                  * Log message to eventlog 
395                  */ \r
396                 WriteToEventLog(EVENTLOG_INFORMATION_TYPE, MsgErrorString);
397             \r__leave;
398             \r}
399         \r\r
400             /*
401              * Service deleted successfully 
402              */ \r
403             _stprintf(MsgErrorString, "%s %s", lpszServiceName,
404                       _T("- Service deleted"));
405         \r\r
406             /*
407              * Log message to eventlog 
408              */ \r
409             WriteToEventLog(EVENTLOG_INFORMATION_TYPE, MsgErrorString);
410         \r\r
411             /*
412              * Delete registry entires for EventLog 
413              */ \r
414             _tcscpy(szRegKey, szRegAppLogKey);
415         \r_tcscat(szRegKey, lpszServiceName);
416         \rRegDeleteKey(HKEY_LOCAL_MACHINE, szRegKey);
417         \r\rMessageBox(NULL, \rMsgErrorString, \rg_szAppName,
418                      \rMB_ICONINFORMATION);
419     \r}
420     \r\r
421         /*
422          * Delete the handles 
423          */ \r
424         __finally \r {
425         \rif (hService)
426             CloseServiceHandle(hService);
427         \rif (hSCManager)
428             CloseServiceHandle(hSCManager);
429     \r}
430 \r}
431
432 \r\r\r
433     /*
434      * \r
435      * * To write message to Windows Event log\r
436      * * Input - Event Type, Message string\r
437      * *\r
438      */ \r
439     \rVOID WriteToEventLog(WORD wType, LPCTSTR pszFormat,...) \r
440 {
441     \rTCHAR szMessage[512];
442     \rLPTSTR LogStr[1];
443     \rva_list ArgList;
444     \rHANDLE hEventSource = NULL;
445     \r\rva_start(ArgList, pszFormat);
446     \r_vstprintf(szMessage, pszFormat, ArgList);
447     \rva_end(ArgList);
448     \r\rLogStr[0] = szMessage;
449     \r\r\rhEventSource = RegisterEventSource(NULL, g_szAppName);
450     \rif (hEventSource == NULL)
451         return;
452     \r\rReportEvent(hEventSource, \rwType, \r0, \rDISPLAY_MSG, /* To Just output the text to event log */ \r
453                   NULL, \r1, \r0, \rLogStr, \rNULL);
454     \r\rDeregisterEventSource(hEventSource);
455     \r\rif (!g_fRunningAsService)
456         \r {
457         \r
458             /*
459              * We are running in command mode, output the string 
460              */ \r
461             _putts(szMessage);
462         \r}
463 \r}
464
465 \r\r\r
466     /*
467      * \r
468      * * Handle command-line arguments from the user. \r
469      * *     Serivce related options are:\r
470      * *     -register       - registers the service\r
471      * *     -unregister     - unregisters the service\r
472      * *     -service        - run as serivce\r
473      * *     other command-line arguments are unaltered/ignored.\r
474      * *     They should supplied as first arguments(other wise they will be ignored\r
475      * * Return: Type indicating the option specified\r
476      */ \r
477     \rINT ParseCmdLineForServiceOption(int argc, TCHAR * argv[]) \r
478 {
479     \rint            nReturn = RUN_AS_CONSOLE;   /* Defualted to run as console */
480     \r\rif (argc >= 2)
481         \r {
482         \r
483             /*
484              * second argument present 
485              */ \r
486             if (lstrcmpi(_T("-register"), argv[1]) == 0)
487             \r {
488             \rnReturn = REGISTER_SERVICE;
489             \r}
490         \r
491         else if (lstrcmpi(_T("-unregister"), argv[1]) == 0)
492             \r {
493             \rnReturn = UN_REGISTER_SERVICE;
494             \r}
495         \r
496         else if (lstrcmpi(_T("-service"), argv[1]) == 0)
497             \r {
498             \rnReturn = RUN_AS_SERVICE;
499             \r}
500         \r}
501     \rreturn nReturn;
502 \r}
503
504 \r\r
505     /*
506      * \r
507      * * To Display an error message describing the last system error\r
508      * * message, along with a title passed as a parameter.\r
509      */ \r
510     VOID DisplayError(LPCTSTR pszTitle) \r
511 {
512     \rLPVOID pErrorMsg;
513     \r\r
514         /*
515          * Build Error String 
516          */ \r
517         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
518                       \rFORMAT_MESSAGE_FROM_SYSTEM, \rNULL, \rGetLastError(),
519                       \rMAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
520                       \r(LPTSTR) & pErrorMsg, \r0, \rNULL);
521     \rif (g_fRunningAsService != FALSE)
522         \r {
523         \rWriteToEventLog(EVENTLOG_ERROR_TYPE, pErrorMsg);
524         \r}
525     \r
526     else
527         \r {
528         \rMessageBox(NULL, pErrorMsg, pszTitle, MB_ICONHAND);
529         \r}
530     \r\rLocalFree(pErrorMsg);
531 \r}
532
533 \r\r\r
534     /*
535      * \r
536      * *  To update current service status \r
537      * *  Sends the current service status to the SCM. Also updates\r
538      * *  the global service status structure.\r
539      */ \r
540 static          BOOL
541 UpdateServiceStatus(DWORD dwStatus, \rDWORD dwErrorCode,
542                     \rDWORD dwWaitHint) \r
543 {
544     \rBOOL fReturn = FALSE;
545     \rDWORD static   dwCheckpoint = 1;
546     \rDWORD dwControls =
547         SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
548     \r\rif (g_fRunningAsService == FALSE)
549         return FALSE;
550     \r\rZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
551     \rServiceStatus.dwServiceType = SERVICE_WIN32;
552     \rServiceStatus.dwCurrentState = dwStatus;
553     \rServiceStatus.dwWaitHint = dwWaitHint;
554     \r\rif (dwErrorCode)
555         \r {
556         \rServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
557         \rServiceStatus.dwServiceSpecificExitCode = dwErrorCode;
558         \r}
559     \r
560         /*
561          * special cases that depend on the new state 
562          */ \r
563         switch (dwStatus)
564         \r {
565     \rcase SERVICE_START_PENDING:
566         \rdwControls = 0;
567         \rbreak;
568     \r\rcase SERVICE_RUNNING:
569     \rcase SERVICE_STOPPED:
570         \rdwCheckpoint = 0;
571         \rbreak;
572         \r}
573     \rServiceStatus.dwCheckPoint = dwCheckpoint++;
574     \rServiceStatus.dwControlsAccepted = dwControls;
575     \r\rreturn ReportCurrentServiceStatus();
576 \r}
577
578 \r\r\r
579     /*
580      * \r
581      * * Reports current Service status to SCM\r
582      */ \r
583 static          BOOL
584 ReportCurrentServiceStatus() \r
585 {
586     \rreturn SetServiceStatus(hServiceStatus, &ServiceStatus);
587 \r}
588
589 \r\r\r
590     /*
591      * \r
592      * * The ServiceMain function to start service.\r
593      */ \r
594     VOID WINAPI ServiceMain(DWORD argc, LPTSTR argv[]) \r
595 {
596     \r\rSECURITY_ATTRIBUTES SecurityAttributes;
597     \rDWORD dwThreadId;
598     \r\r
599         /*
600          * Input Arguments to function startup 
601          */ \r
602         DWORD ArgCount = 0;
603     \rLPTSTR * ArgArray = NULL;
604     \rTCHAR szRegKey[512];
605     \rTCHAR szValue[128];
606     \rDWORD nSize;
607     \rHKEY hParamKey = NULL;     /* To read startup parameters */
608     \rDWORD TotalParams = 0;
609     \rDWORD i;
610     \rInputParams ThreadInputParams;
611     \r\r
612         /*
613          * Build the Input parameters to pass to thread 
614          */ \r
615         \r
616         /*
617          * SCM sends Service Name as first arg, increment to point\r
618          * * arguments user specified while starting contorl agent\r
619          */ \r
620         \r\r
621         /*
622          * Read registry parameter 
623          */ \r
624         /*
625          * Initialize count to 1 
626          */ \r
627         ArgCount = 1;
628     \r\r
629         /*
630          * Create Registry Key path 
631          */ \r
632         _stprintf(szRegKey, "%s%s\\%s",
633                   _T("SYSTEM\\CurrentControlSet\\Services\\"), g_szAppName,
634                   "Parameters");
635     \rif (RegOpenKeyEx
636          (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS,
637           &hParamKey) == ERROR_SUCCESS)
638         \r {
639         \r\r
640             /*
641              * Read startup Configuration information 
642              */ \r
643             /*
644              * Find number of subkeys inside parameters 
645              */ \r
646             \rif (RegQueryInfoKey
647                  (hParamKey, NULL, NULL, 0, NULL, NULL, NULL, &TotalParams,
648                   NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
649             \r {
650             \rif (TotalParams != 0)
651                 \r {
652                 \rArgCount += TotalParams;
653                 \r\r
654                     /*
655                      * Allocate memory to hold strings 
656                      */ \r
657                     ArgArray =
658                     (LPTSTR *) malloc(sizeof(LPTSTR) * ArgCount);
659                 \r\r
660                     /*
661                      * Copy first argument 
662                      */ \r
663                     ArgArray[0] = _tcsdup(argv[0]);
664                 \r\rfor (i = 1; i <= TotalParams; i++)
665                     \r {
666                     \r
667                         /*
668                          * Create Subkey value name 
669                          */ \r
670                         _stprintf(szRegKey, "%s%d", "Param", i);
671                     \r\r
672                         /*
673                          * Set size 
674                          */ \r
675                         nSize = 128;
676                     \rRegQueryValueEx(hParamKey, szRegKey, 0, NULL,
677                                      (LPBYTE) & szValue, &nSize);
678                     \rArgArray[i] = _tcsdup(szValue);
679                     \r}
680                 \r}
681             \r}
682         \rRegCloseKey(hParamKey);
683         \r}
684     \r\r\rif (ArgCount == 1)
685         \r {
686         \r
687             /*
688              * No statup agrs are given 
689              */ \r
690             ThreadInputParams.Argc = argc;
691         \rThreadInputParams.Argv = argv;
692         \r}
693     \r
694     else
695         \r {
696         \rThreadInputParams.Argc = ArgCount;
697         \rThreadInputParams.Argv = ArgArray;
698         \r}
699     \r\r
700         /*
701          * Register Serivce Control Handler 
702          */ \r
703         hServiceStatus =
704         RegisterServiceCtrlHandler(g_szAppName, \rControlHandler);
705     \rif (hServiceStatus == 0)
706         \r {
707         \rWriteToEventLog(EVENTLOG_ERROR_TYPE,
708                          \r_T("RegisterServiceCtrlHandler failed"));
709         \rreturn;
710         \r}
711     \r\r
712         /*
713          * Update the service status to START_PENDING 
714          */ \r
715         UpdateServiceStatus(SERVICE_START_PENDING, \rNO_ERROR,
716                             \rSCM_WAIT_INTERVAL);
717     \r\r
718         /*
719          * Spin of worker thread, which does majority of the work 
720          */ \r
721         \r__try \r {
722         \rif (SetSimpleSecurityAttributes(&SecurityAttributes) == FALSE)
723             \r {
724             \rWriteToEventLog(EVENTLOG_ERROR_TYPE,
725                              \r_T("Couldn't init security attributes"));
726             \r__leave;
727             \r}
728         \rhServiceThread =
729             (void *) _beginthreadex(&SecurityAttributes, \r0,
730                                     \rThreadFunction,
731                                     \r(void *) &ThreadInputParams, \r0,
732                                     \r&dwThreadId);
733         \rif (hServiceThread == NULL)
734             \r {
735             \rWriteToEventLog(EVENTLOG_ERROR_TYPE,
736                              \r_T("Couldn't start worker thread"));
737             \r__leave;
738             \r}
739         \r\r
740             /*
741              * Set Service Status to Running 
742              */ \r
743             UpdateServiceStatus(SERVICE_RUNNING, \rNO_ERROR,
744                                 \rSCM_WAIT_INTERVAL);
745         \r\r
746             /*
747              * Wait for termination event and worker thread to\r
748              * * spin down.\r
749              */ \r
750             \rWaitForSingleObject(hServiceThread, INFINITE);
751     \r}
752     \r__finally \r {
753         \r
754             /*
755              * Release resources 
756              */ \r
757             UpdateServiceStatus(SERVICE_STOPPED, \rNO_ERROR,
758                                 \rSCM_WAIT_INTERVAL);
759         \r\rif (hServiceThread)
760             \rCloseHandle(hServiceThread);
761         \rFreeSecurityAttributes(&SecurityAttributes);
762         \r\r
763             /*
764              * Delete allocated argument list 
765              */ \r
766             if (ArgCount > 1 && ArgArray != NULL)
767             \r {
768             \r
769                 /*
770                  * Delete all strings 
771                  */ \r
772                 for (i = 0; i < ArgCount; i++)
773                 \r {
774                 \rfree(ArgArray[i]);
775                 \r}
776             \rfree(ArgArray);
777             \r}
778     \r}
779 \r}
780
781 \r\r
782     /*
783      * \r
784      * * Function to start as Windows service\r
785      * * The calling party should specify their entry point as input parameter\r
786      * * Returns TRUE if the Service is started successfully\r
787      */ \r
788     \rBOOL RunAsService(INT(*ServiceFunction) (INT, LPTSTR *)) \r
789 {
790     \r\r
791         /*
792          * Set the ServiceEntryPoint 
793          */ \r
794         ServiceEntryPoint = ServiceFunction;
795     \r\r
796         /*
797          * By default, mark as Running as a service 
798          */ \r
799         g_fRunningAsService = TRUE;
800     \r\r
801         /*
802          * Initialize ServiceTableEntry table 
803          */ \r
804         ServiceTableEntry[0].lpServiceName = g_szAppName;       /* Application Name */
805     \r\r
806         /*
807          * Call SCM via StartServiceCtrlDispatcher to run as Service \r
808          * * If the function returns TRUE we are running as Service, \r
809          */ \r
810         \rif (StartServiceCtrlDispatcher(ServiceTableEntry) == FALSE)
811         \r {
812         \rg_fRunningAsService = FALSE;
813         \r\r
814             /*
815              * Some other error has occurred. 
816              */ \r
817             WriteToEventLog(EVENTLOG_ERROR_TYPE,
818                             \r_T("Couldn't start service - %s"),
819                             g_szAppName);
820         \r\r}
821     \r\rreturn g_fRunningAsService;
822 \r}
823
824 \r\r\r
825     /*
826      * \r
827      * * Service control handler function\r
828      * * Responds to SCM commands/requests\r
829      * * The service handles 4 commands\r
830      * * commands - interrogate,pause, continue and stop.\r
831      */ \r
832     VOID WINAPI ControlHandler(DWORD dwControl) \r
833 {
834     \rswitch (dwControl)
835         \r {
836     \rcase SERVICE_CONTROL_STOP:
837         \rProcessServiceStop();  /* To stop the service */
838         \rbreak;
839     \rcase SERVICE_CONTROL_INTERROGATE:
840         \rProcessServiceInterrogate();   /* Report Current state of the Service */
841         \rbreak;
842     \rcase SERVICE_CONTROL_PAUSE:
843         \rProcessServicePause(); /* To puase service */
844         \rbreak;
845     \rcase SERVICE_CONTROL_CONTINUE:
846         \rProcessServiceContinue();      /* To continue Service */
847         \rbreak;
848         \r}
849 \r}
850
851 \r\r
852     /*
853      * \r
854      * * To stop the service.  This invokes registered\r
855      * * stop function to stop the service(gracefull exit)\r
856      * * After stopping, Service status is set to STOP in \r
857      * * main loop\r
858      */ \r
859     VOID ProcessServiceStop(VOID) \r
860 {
861     \rUpdateServiceStatus(SERVICE_STOP_PENDING, \rNO_ERROR,
862                          \rSCM_WAIT_INTERVAL);
863     \r\r
864         /*
865          * Invoke registered Stop funciton 
866          */ \r
867         if (StopFunction != NULL)
868         \r {
869         \r(*StopFunction) ();
870         \r}
871     \r
872     else
873         \r {
874         \r
875             /*
876              * There is no registered stop function, so terminate the thread 
877              */ \r
878             TerminateThread(hServiceThread, 0);
879         \r}
880 \r}
881
882 \r\r\r
883     /*
884      * \r
885      * * Returns the current state of the service to the SCM.\r
886      */ \r
887     VOID ProcessServiceInterrogate(VOID) \r
888 {
889     \rReportCurrentServiceStatus();
890 \r}
891
892 \r\r\r
893     /*
894      * \r
895      * * To Create a security descriptor with a NULL ACL, which\r
896      * * allows unlimited access. Returns a SECURITY_ATTRIBUTES\r
897      * * structure that contains the security descriptor.\r
898      * * The structure contains a dynamically allocated security\r
899      * * descriptor that must be freed; either manually, or by\r
900      * * calling FreeSecurityAttributes \r
901      */ \r
902     BOOL SetSimpleSecurityAttributes(SECURITY_ATTRIBUTES * pSecurityAttr) \r
903 {
904     \rBOOL fReturn = FALSE;
905     \rSECURITY_DESCRIPTOR * pSecurityDesc = NULL;
906     \r\r
907         /*
908          * If an invalid address passed as a parameter, return\r
909          * * FALSE right away. \r
910          */ \r
911         \rif (!pSecurityAttr)
912         return FALSE;
913     \r\rpSecurityDesc =
914         (SECURITY_DESCRIPTOR *) LocalAlloc(LPTR,
915                                            \rSECURITY_DESCRIPTOR_MIN_LENGTH);
916     \rif (!pSecurityDesc)
917         return FALSE;
918     \r\rfReturn =
919         InitializeSecurityDescriptor(pSecurityDesc,
920                                      \rSECURITY_DESCRIPTOR_REVISION);
921     \rif (fReturn != FALSE)
922         \r {
923         \rfReturn =
924             SetSecurityDescriptorDacl(pSecurityDesc, TRUE, NULL, FALSE);
925         \r}
926     \r\rif (fReturn != FALSE)
927         \r {
928         \rpSecurityAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
929         \rpSecurityAttr->lpSecurityDescriptor = pSecurityDesc;
930         \rpSecurityAttr->bInheritHandle = TRUE;
931         \r}
932     \r
933     else
934         \r {
935         \r
936             /*
937              * Couldn't initialize or set security descriptor. 
938              */ \r
939             LocalFree(pSecurityDesc);
940         \r}
941     \rreturn fReturn;
942 \r}
943
944 \r\r
945     /*
946      * \r
947      * * This funciton Frees the security descriptor owned by a SECURITY_ATTRIBUTES\r
948      * * structure.\r
949      */ \r
950     VOID FreeSecurityAttributes(SECURITY_ATTRIBUTES * pSecurityAttr) \r
951 {
952     \rif (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor)
953         \rLocalFree(pSecurityAttr->lpSecurityDescriptor);
954 \r}
955
956 \r\r
957     /*
958      * TheadFunction\r
959      * * This function is spawn as thread.\r
960      * * Invokes registered service function\r
961      * * Returns when called registered function returns\r
962      */ \r
963     DWORD WINAPI ThreadFunction(LPVOID lpParam) \r
964 {
965     \r
966         /*
967          * lpParam contains argc and argv, pass to service main function 
968          */ \r
969         \r
970         /*
971          * Declare pointer to InputParams 
972          */ \r
973         InputParams * pInputArg;
974     \rpInputArg = (InputParams *) lpParam;
975     \rreturn (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv);
976 \r}
977
978 \r\r
979     /*
980      * \r
981      * * To register STOP function with the framework\r
982      * * This function will be inovked when SCM sends\r
983      * * STOP command\r
984      */ \r
985     \rVOID RegisterStopFunction(void (*StopFunc) ()) \r
986 {
987     \rStopFunction = StopFunc;
988 \r\r\r\r
989
990     /*
991      * \r
992      * * To Pause the service whec SCM sends pause command\r
993      * * Invokes PauseThread on worker Thread handle, only\r
994      * * when Service status is Running\r
995      */ \r
996     VOID ProcessServicePause(VOID) \r
997 {
998     \r\rif (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
999         \r {
1000         \rUpdateServiceStatus(SERVICE_PAUSE_PENDING, \rNO_ERROR,
1001                              \rSCM_WAIT_INTERVAL);
1002         \r\r
1003             /*
1004              * Invoke Thread pause on ThreadHandle 
1005              */ \r
1006             if (SuspendThread(hServiceThread) != -1)
1007             \r {
1008             \rUpdateServiceStatus(SERVICE_PAUSED, \rNO_ERROR,
1009                                  \rSCM_WAIT_INTERVAL);
1010             \r}
1011         \r\r}
1012 \r}
1013
1014 \r\r\r\r
1015     /*
1016      * \r
1017      * * To Continue paused service\r
1018      * * Invoke ResumeThread, if thread is paused\r
1019      */ \r
1020     VOID ProcessServiceContinue(VOID) \r
1021 {
1022     \r\rif (ServiceStatus.dwCurrentState == SERVICE_PAUSED)
1023         \r {
1024         \rUpdateServiceStatus(SERVICE_CONTINUE_PENDING, \rNO_ERROR,
1025                              \rSCM_WAIT_INTERVAL);
1026         \r\r\r
1027             /*
1028              * Invoke Thread pause on ThreadHandle 
1029              */ \r
1030             if (ResumeThread(hServiceThread) != -1)
1031             \r {
1032             \rUpdateServiceStatus(SERVICE_RUNNING, \rNO_ERROR,
1033                                  \rSCM_WAIT_INTERVAL);
1034             \r}
1035         \r\r}
1036 \r}
1037
1038 \r