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