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