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