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