267da8b1db9df31429669ec0c04c3151ff3d9b87
[yaz-moved-to-github.git] / src / service.c
1 /*
2  * NT Service interface Utility.
3  *  Based on code written by
4  *     Chas Woodfield, Fretwell Downing Informatics.
5  * $Id: service.c,v 1.3 2005-06-25 15:46:04 adam Exp $
6  */
7
8 /**
9  * \file service.c
10  * \brief Implements NT service handling for GFS.
11  */
12
13 #ifdef WIN32
14
15 #include <windows.h>
16 #include <stdio.h>
17 #include <tchar.h>
18 #include <direct.h>
19
20 #include "service.h"
21
22 static AppService *pService = NULL;
23 static BOOL bRunAsService = TRUE;
24 static void *pAppHandle = NULL;
25
26 /* Private functions to this module */
27 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
28 void Service_Delete();
29 void Service_Initialize();
30 BOOL NotifyServiceController();
31 BOOL UpdateServiceStatus(DWORD Status);
32 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
33 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
34 void CmdRemoveService();
35 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
36 BOOL CheckServiceArguments(int argc, char *argv[]);
37
38 /* Callback functions for thee service manager */
39 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
40 void WINAPI ServiceControlHandler(DWORD fdwControl);
41
42 /* Function to handle Ctrl + C etc... */
43 BOOL EventHandlerRoutine(DWORD dwCtrlType);
44
45 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
46 {
47     pService = malloc(sizeof(AppService));
48     pService->pAppName = pAppName;
49     pService->pServiceName = pServiceName;
50     pService->pServiceDisplayName = pServiceDisplayName;
51     pService->pDependancies = pDependancies;
52     pService->hService = 0;
53     pService->ServiceTable[0].lpServiceName = pServiceName; 
54     pService->ServiceTable[0].lpServiceProc = ServiceMain; 
55     pService->ServiceTable[1].lpServiceName = NULL; 
56     pService->ServiceTable[1].lpServiceProc = NULL; 
57     pService->argc = argc;
58     pService->argv = argv;
59 }
60
61 void Service_Delete()
62 {
63     if (pService != NULL)
64     {
65         /* Mark the service as stopping */
66         UpdateServiceStatus(SERVICE_STOP_PENDING);
67
68         /* Stop the service */
69         StopAppService(pAppHandle);
70
71         /* Service has now stopped */
72         UpdateServiceStatus(SERVICE_STOPPED);
73
74         /* Free the memory */
75         free(pService);
76         pService = NULL;
77     }
78 }
79
80 void Service_Initialize()
81 {
82     if (pService != NULL)
83     {
84         /* Register ourselves with the control dispatcher */
85         StartServiceCtrlDispatcher(pService->ServiceTable);
86     }
87 }
88
89 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
90 {
91     if (pService != NULL)
92     {
93         if (NotifyServiceController())
94         {
95             /* Set the status to pending */
96             UpdateServiceStatus(SERVICE_START_PENDING);
97
98             /* Lets attempt to start the service */
99             if (StartAppService(pAppHandle, pService->argc, pService->argv))
100             {
101                 /* Service is now up and running */
102                 UpdateServiceStatus(SERVICE_RUNNING);
103
104                 /* Lets wait for our clients */
105                 RunAppService(pAppHandle);
106             }
107             else
108             {
109                 FailServiceStart(GetLastError(), 0);
110                 Service_Delete();
111             }
112         }
113     }
114 }
115
116 BOOL NotifyServiceController()
117 {
118     if (pService == NULL)
119     {
120         return(FALSE);
121     }
122     else
123     {
124         if (bRunAsService)
125         {
126             pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
127             pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
128             pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
129             pService->ServiceStatus.dwWin32ExitCode = 0;
130             pService->ServiceStatus.dwServiceSpecificExitCode = 0;
131             pService->ServiceStatus.dwCheckPoint = 0;
132             pService->ServiceStatus.dwWaitHint = 0;
133             pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
134
135             if (pService->hService)
136                 UpdateServiceStatus(SERVICE_START_PENDING);
137             else
138                 return(FALSE);
139         }
140         return(TRUE);
141     }
142 }
143
144 void WINAPI ServiceControlHandler(DWORD fdwControl)
145 {
146     if (pService != NULL)
147     {
148         switch (fdwControl)
149         {
150             case SERVICE_CONTROL_STOP:
151                 /* Update the service status to be pending */
152                 Service_Delete();
153                 break;
154
155             case SERVICE_CONTROL_INTERROGATE:
156                 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
157                 break;
158
159             default:
160                 break;
161         }
162     }
163 }
164
165 BOOL UpdateServiceStatus(DWORD Status)
166 {
167     if (pService != NULL)
168     {
169         if (pService->hService)
170         {
171             pService->ServiceStatus.dwCurrentState = Status;
172             if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
173             {
174                 pService->ServiceStatus.dwCheckPoint ++;
175                 pService->ServiceStatus.dwWaitHint = 5000;    /* 5 sec.*/
176             }
177             else
178             {
179                 pService->ServiceStatus.dwCheckPoint = 0;
180                 pService->ServiceStatus.dwWaitHint = 0;
181             }
182
183             return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
184         }
185     }
186
187     return(FALSE);
188 }
189
190 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
191 {
192     if (pService != NULL)
193     {
194         pService->ServiceStatus.dwWin32ExitCode = Win32Code;
195         pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
196         UpdateServiceStatus(SERVICE_STOPPED);
197     }
198 }
199
200 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
201 {
202     if (pService != NULL)
203     {
204         SC_HANDLE   schService;
205         SC_HANDLE   schSCManager;
206
207         TCHAR szPath[2048];
208
209         if (GetModuleFileName(NULL, szPath, 512) == 0)
210         {
211             _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
212         }
213         else
214         {
215             int i;
216             char cwdstr[_MAX_PATH];
217
218             if (!_getcwd(cwdstr, sizeof(cwdstr)))
219                 strcpy (cwdstr, ".");
220
221             strcat (szPath, TEXT(" -runservice \""));
222             strcat (szPath, cwdstr);
223             strcat (szPath, "\"");
224
225             for (i = 1; i < argc; i++)
226             {
227                 /* We will add the given command line arguments to the command */
228                 /* We are not interested in the install and remove options */
229                 if ((stricmp("-install", argv[i]) != 0) &&
230                     (stricmp("-installa", argv[i]) != 0) &&
231                     (stricmp("-remove", argv[i]) != 0))
232                 {
233                     strcat(szPath, TEXT(" "));
234                     strcat(szPath, argv[i]);
235                 }
236             }
237
238             schSCManager = OpenSCManager(NULL,                   /* machine (NULL == local) */
239                                          NULL,                   /* database (NULL == default) */
240                                          SC_MANAGER_ALL_ACCESS); /* access required */
241             if (schSCManager)
242             {
243                 schService = CreateService(schSCManager,               /* SCManager database */
244                                            TEXT(pService->pServiceName),        /* name of service */
245                                            TEXT(pService->pServiceDisplayName), /* name to display */
246                                            SERVICE_ALL_ACCESS,         /* desired access */
247                                            SERVICE_WIN32_OWN_PROCESS,  /* service type */
248                                            bAutoStart ? SERVICE_AUTO_START :
249                                                         SERVICE_DEMAND_START, /* start type */
250                                            SERVICE_ERROR_NORMAL,       /* error control type */
251                                            szPath,                     /* service's binary */
252                                            NULL,                       /* no load ordering group */
253                                            NULL,                       /* no tag identifier */
254                                            TEXT(pService->pDependancies),       /* dependencies */
255                                            NULL,                       /* LocalSystem account */
256                                            NULL);                      /* no password */
257
258                 if (schService)
259                 {
260                     _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
261                     CloseServiceHandle(schService);
262                 }
263                 else
264                 {
265                     _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
266                 }
267
268                 CloseServiceHandle(schSCManager);
269             }
270             else
271                 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
272         }
273     }
274 }
275
276 void CmdRemoveService()
277 {
278     if (pService != NULL)
279     {
280         SC_HANDLE   schService;
281         SC_HANDLE   schSCManager;
282
283         schSCManager = OpenSCManager(NULL,                   /* machine (NULL == local) */
284                                      NULL,                   /* database (NULL == default) */
285                                      SC_MANAGER_ALL_ACCESS); /* access required */
286         if (schSCManager)
287         {
288             schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
289
290             if (schService)
291             {
292                 /* try to stop the service */
293                 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
294                 {
295                     _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
296                     Sleep(1000);
297
298                     while (QueryServiceStatus(schService, &pService->ServiceStatus))
299                     {
300                         if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
301                         {
302                             _tprintf(TEXT("."));
303                             Sleep( 1000 );
304                         }
305                         else
306                             break;
307                     }
308
309                     if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
310                         _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
311                     else
312                         _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
313
314                 }
315
316                 /* now remove the service */
317                 if(DeleteService(schService))
318                     _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
319                 else
320                     _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
321
322                 CloseServiceHandle(schService);
323             }
324             else
325                 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
326
327             CloseServiceHandle(schSCManager);
328         }
329         else
330             _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
331     }
332 }
333
334 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
335 {
336     DWORD dwRet;
337     LPTSTR lpszTemp = NULL;
338
339     dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
340                           NULL,
341                           GetLastError(),
342                           LANG_NEUTRAL,
343                           (LPTSTR)&lpszTemp,
344                           0,
345                           NULL);
346
347     /* supplied buffer is not long enough */
348     if (!dwRet || ((long)dwSize < (long)dwRet + 14))
349         lpszBuf[0] = TEXT('\0');
350     else
351     {
352         lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0');  /* remove cr and newline character */
353         _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
354     }
355
356     if (lpszTemp)
357         LocalFree((HLOCAL)lpszTemp);
358
359     return(lpszBuf);
360 }
361
362 BOOL CheckServiceArguments(int argc, char *argv[])
363 {
364     int i;
365
366     /* Lets process the arguments */
367     for (i = 1; i < argc; i++)
368     {
369         if (stricmp("-install", argv[i]) == 0)
370         {
371             /* They want to install the service */
372             CmdInstallService(argc, argv, FALSE);
373
374             /* We don't carry on, after we have installed the service */
375             return(FALSE);
376         }
377         else if (stricmp("-installa", argv[i]) == 0)
378         {
379             /* They want to install the service */
380             CmdInstallService(argc, argv, TRUE);
381
382             /* We don't carry on, after we have installed the service */
383             return(FALSE);
384         }
385         else if (stricmp("-remove", argv[i]) == 0)
386         {
387             /* Here they want to remove it */
388             CmdRemoveService();
389
390             /* We don't carry on, after we have removed the service */
391             return(FALSE);
392         }
393         else if (stricmp ("-runservice", argv[i]) == 0)
394         {
395             /* We can carry on, if we reached here */
396             chdir(argv[i+1]);
397             argv[i] = "";
398             argv[i+1] = "";
399             return(TRUE);
400         }
401     }
402     bRunAsService = FALSE;
403     return(TRUE);
404 }
405
406 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
407 {
408     BOOL bDeleteService = TRUE;
409     BOOL bResult = FALSE;
410
411     /* Save the handle for later use */
412     pAppHandle = pHandle;
413
414     /* Create our service class */
415     Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
416
417     if (CheckServiceArguments(argc, argv))
418     {
419         if (bRunAsService)
420         {
421             /* No need to set the console control handler, as the service manager handles all this for us */
422             Service_Initialize();
423             bDeleteService = FALSE;
424         }
425         else
426         {
427             /* Set the console control handler for exiting the program */
428             SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
429
430             /* Now do the main work */
431             ServiceMain(argc, argv);
432         }
433
434         /* We have been successful initializing, so let the caller know */
435         bResult = TRUE;
436     }
437
438     if (bDeleteService)
439     {
440         /* Finished with the service now */
441         Service_Delete();
442     }
443     return(bResult);
444 }
445
446 BOOL EventHandlerRoutine(DWORD dwCtrlType)
447 {
448     /* This routine dosn't seem to get called all the time, Why ??? */
449     switch (dwCtrlType)
450     {
451         case CTRL_C_EVENT:        /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
452         case CTRL_BREAK_EVENT:    /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
453         case CTRL_CLOSE_EVENT:    /* A signal that the system sends to all processes attached to a console when the user closes the console (either by choosing the Close command from the console window's System menu, or by choosing the End Task command from the Task List).*/
454         case CTRL_LOGOFF_EVENT:   /* A signal that the system sends to all console processes when a user is logging off. This signal does not indicate which user is logging off, so no assumptions can be made.*/
455         case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
456             /* We are basically shutting down, so call Service_Delete */
457             Service_Delete();
458             return(FALSE);
459             break;
460
461         default:
462             /* we are not handling this one, so return FALSE */
463             return(FALSE);
464     }
465 }
466 #endif
467 /*
468  * Local variables:
469  * c-basic-offset: 4
470  * indent-tabs-mode: nil
471  * End:
472  * vim: shiftwidth=4 tabstop=8 expandtab
473  */
474