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