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