Move xml_add utilities to xml_add.c source
[yaz-moved-to-github.git] / src / sc.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file sc.c
7  * \brief Windows Service Control
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #ifdef WIN32
14 #include <windows.h>
15 #include <tchar.h>
16 #include <direct.h>
17 #endif
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <yaz/xmalloc.h>
22 #include <yaz/log.h>
23 #include <yaz/sc.h>
24 #include <yaz/wrbuf.h>
25
26 struct sc_s {
27     int install_flag;
28     int start_flag;
29     int remove_flag;
30     int run_flag;
31     char *service_name;
32     char *display_name;
33     int (*sc_main)(yaz_sc_t s, int argc, char **argv);
34     void (*sc_stop)(yaz_sc_t s);
35     int argc;
36     char **argv;
37 #ifdef WIN32
38     SERVICE_STATUS_HANDLE   gSvcStatusHandle;
39     SERVICE_STATUS          gSvcStatus;
40 #endif
41 };
42
43
44 yaz_sc_t yaz_sc_create(const char *service_name, const char *display_name)
45 {
46     yaz_sc_t s = (yaz_sc_t) xmalloc(sizeof(*s));
47
48     s->service_name = service_name ? xstrdup(service_name) : 0;
49     s->display_name = display_name ? xstrdup(display_name) : 0;
50     s->install_flag = 0;
51     s->start_flag = 0;
52     s->remove_flag = 0;
53     s->run_flag = 0;
54     s->sc_main = 0;
55     s->sc_stop = 0;
56 #ifdef WIN32
57     s->gSvcStatusHandle = 0;
58 #endif
59     return s;
60 }
61
62 #ifdef WIN32
63 static void parse_args(yaz_sc_t s, int *argc_p, char ***argv_p)
64 {
65     int skip_opt = 0;
66     const char *log_file = 0;
67     int i;
68
69     /* now look for the service arguments */
70     skip_opt = 0;
71     for (i = 1; i < *argc_p; i++)
72     {
73         const char *opt = (*argv_p)[i];
74         if (!strcmp(opt, "-install"))
75         {
76             s->install_flag = 1;
77             skip_opt = 1;
78             break;
79         }
80         else if (!strcmp(opt, "-installa"))
81         {
82             s->install_flag = 1;
83             s->start_flag = 1;
84             skip_opt = 1;
85             break;
86         }
87         else if (!strcmp(opt, "-remove"))
88         {
89             s->remove_flag = 1;
90             skip_opt = 1;
91             break;
92         }
93         else if (!strcmp(opt, "-run") && i < *argc_p-1)
94         {
95             /* -run dir */
96             const char *dir = (*argv_p)[i+1];
97             s->run_flag = 1;
98             chdir(dir);
99             skip_opt = 2;
100             break;
101         }
102     }
103     *argc_p = *argc_p - skip_opt;
104     for (; i < *argc_p; i++)
105         (*argv_p)[i] = (*argv_p)[i + skip_opt];
106
107     /* now look for the service arguments */
108     /* we must have a YAZ log file to work with */
109     skip_opt = 0;
110     for (i = 1; i < *argc_p; i++)
111     {
112         const char *opt = (*argv_p)[i];
113         if (opt[0] == '-' && opt[1] == 'l')
114         {
115             if (opt[2])
116             {
117                 log_file = opt+2;
118                 skip_opt = 1;
119                 break;
120             }
121             else if (i < *argc_p - 1)
122             {
123                 log_file = (*argv_p)[i+1];
124                 skip_opt = 2;
125                 break;
126             }
127         }
128     }
129     if (log_file)
130         yaz_log_init_file(log_file);
131     else
132     {
133         if (s->install_flag)
134         {
135             yaz_log(YLOG_FATAL, "Must specify -l logfile for service to install");
136             exit(1);
137         }
138     }
139     if (s->run_flag)
140     {   /* remove  -l logfile for a running service */
141         *argc_p = *argc_p - skip_opt;
142         for (; i < *argc_p; i++)
143             (*argv_p)[i] = (*argv_p)[i + skip_opt];
144
145     }
146 }
147
148 VOID sc_ReportSvcStatus(yaz_sc_t s,
149                         DWORD dwCurrentState,
150                         DWORD dwWin32ExitCode,
151                         DWORD dwWaitHint)
152 {
153     if (s->gSvcStatusHandle)
154     {
155         static DWORD dwCheckPoint = 1;
156
157         // Fill in the SERVICE_STATUS structure.
158
159         s->gSvcStatus.dwCurrentState = dwCurrentState;
160         s->gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
161         s->gSvcStatus.dwWaitHint = dwWaitHint;
162
163         if (dwCurrentState == SERVICE_START_PENDING)
164             s->gSvcStatus.dwControlsAccepted = 0;
165         else
166             s->gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
167
168         if ( (dwCurrentState == SERVICE_RUNNING) ||
169              (dwCurrentState == SERVICE_STOPPED) )
170             s->gSvcStatus.dwCheckPoint = 0;
171         else
172             s->gSvcStatus.dwCheckPoint = dwCheckPoint++;
173
174         // Report the status of the service to the SCM.
175         SetServiceStatus(s->gSvcStatusHandle, &s->gSvcStatus );
176     }
177 }
178
179 static yaz_sc_t global_sc = 0;
180
181 VOID WINAPI sc_SvcCtrlHandler(DWORD dwCtrl)
182 {
183     switch(dwCtrl)
184     {
185     case SERVICE_CONTROL_STOP:
186         yaz_log(YLOG_LOG, "Service %s to stop", global_sc->service_name);
187         sc_ReportSvcStatus(global_sc, SERVICE_STOP_PENDING, NO_ERROR, 0);
188         global_sc->sc_stop(global_sc);
189         sc_ReportSvcStatus(global_sc, SERVICE_STOPPED, NO_ERROR, 0);
190         return;
191     case SERVICE_CONTROL_INTERROGATE:
192         break;
193     default:
194         break;
195     }
196 }
197
198 static void WINAPI sc_service_main(DWORD argc, char **argv)
199 {
200     yaz_sc_t s = global_sc;
201     int ret_code;
202
203     yaz_log(YLOG_LOG, "Service %s starting", s->service_name);
204
205     s->gSvcStatusHandle = RegisterServiceCtrlHandler(
206         s->service_name, sc_SvcCtrlHandler);
207
208     if (!s->gSvcStatusHandle)
209     {
210         yaz_log(YLOG_FATAL|YLOG_ERRNO, "RegisterServiceCtrlHandler");
211         return;
212     }
213
214     s->gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
215     s->gSvcStatus.dwServiceSpecificExitCode = 0;
216
217     sc_ReportSvcStatus(s, SERVICE_START_PENDING, NO_ERROR, 3000);
218
219     ret_code = s->sc_main(s, s->argc, s->argv);
220
221     sc_ReportSvcStatus(s, SERVICE_STOPPED,
222                        ret_code ? ERROR_SERVICE_SPECIFIC_ERROR : NO_ERROR, ret_code);
223 }
224 #endif
225
226 void yaz_sc_running(yaz_sc_t s)
227 {
228 #ifdef WIN32
229     sc_ReportSvcStatus(s, SERVICE_RUNNING, NO_ERROR, 0);
230 #endif
231 }
232
233 int yaz_sc_program(yaz_sc_t s, int argc, char **argv,
234                    int (*sc_main)(yaz_sc_t s, int argc, char **argv),
235                    void (*sc_stop)(yaz_sc_t s))
236
237 {
238     s->sc_main = sc_main;
239     s->sc_stop = sc_stop;
240 #ifdef WIN32
241     parse_args(s, &argc, &argv);
242
243     if (s->install_flag || s->remove_flag)
244     {
245         SC_HANDLE manager = OpenSCManager(NULL /* machine */,
246                                           NULL /* database */,
247                                           SC_MANAGER_ALL_ACCESS);
248         if (manager == NULL)
249         {
250             yaz_log(YLOG_FATAL|YLOG_ERRNO, "OpenSCManager failed");
251             exit(1);
252         }
253         if (s->install_flag)
254         {
255             SC_HANDLE schService = 0;
256             TCHAR szPath[2048];
257             int i;
258             WRBUF w = wrbuf_alloc();
259             char cwdstr[_MAX_PATH];
260
261             if (!_getcwd(cwdstr, sizeof(cwdstr)))
262                 strcpy (cwdstr, ".");
263
264             if (GetModuleFileName(NULL, szPath, 2048) == 0)
265             {
266                 yaz_log(YLOG_FATAL, "GetModuleFileName failed");
267                 exit(1);
268             }
269             wrbuf_puts(w, szPath);
270             for (i = 1; i < argc; i++)
271             {
272                 wrbuf_puts(w, " ");
273                 if (strchr(argv[i], ' '))
274                     wrbuf_puts(w, "\"");
275                 wrbuf_puts(w, argv[i]);
276                 if (strchr(argv[i], ' '))
277                     wrbuf_puts(w, "\"");
278             }
279             wrbuf_puts(w, " -run \"");
280             wrbuf_puts(w, cwdstr);
281             wrbuf_puts(w, "\"");
282             yaz_log(YLOG_LOG, "path: %s", wrbuf_cstr(w));
283
284             schService =
285                 CreateService(
286                     manager,          /* SCM database */
287                     TEXT(s->service_name), /* name of service */
288                     TEXT(s->display_name), /* service name to display */
289                     SERVICE_ALL_ACCESS,        /* desired access */
290                     SERVICE_WIN32_OWN_PROCESS, /* service type */
291                     s->start_flag ?
292                     SERVICE_AUTO_START : SERVICE_DEMAND_START, /* start type */
293                     SERVICE_ERROR_NORMAL,      /* error control type */
294                     wrbuf_cstr(w),             /* path to service's binary */
295                     NULL,                      /* no load ordering group */
296                     NULL,                      /* no tag identifier */
297                     NULL,                      /* no dependencies */
298                     NULL,                      /* LocalSystem account */
299                     NULL);                     /* no password */
300             if (schService == NULL)
301             {
302                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Service %s could not be installed",
303                         s->service_name);
304                 CloseServiceHandle(manager);
305                 exit(1);
306             }
307             yaz_log(YLOG_LOG, "Installed service %s", s->service_name);
308             CloseServiceHandle(schService);
309             wrbuf_destroy(w);
310         }
311         else if (s->remove_flag)
312         {
313             SC_HANDLE schService = 0;
314             SERVICE_STATUS serviceStatus;
315
316             schService = OpenService(manager, TEXT(s->service_name), SERVICE_ALL_ACCESS);
317             if (schService == NULL)
318             {
319                 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Service %s could not be opened",
320                         s->service_name);
321                 CloseServiceHandle(manager);
322                 exit(1);
323             }
324             /* try to stop the service */
325             if (ControlService(schService, SERVICE_CONTROL_STOP, &serviceStatus))
326             {
327                 yaz_log(YLOG_LOG, "Service %s being stopped", s->service_name);
328                 Sleep(1000);
329                 while (QueryServiceStatus(schService, &serviceStatus))
330                 {
331                     if (serviceStatus.dwCurrentState != SERVICE_STOP_PENDING)
332                         break;
333                     Sleep( 1000 );
334                 }
335                 if (serviceStatus.dwCurrentState != SERVICE_STOPPED)
336                     yaz_log(YLOG_LOG|YLOG_FATAL, "Service failed to stop");
337             }
338             if (DeleteService(schService))
339                 yaz_log(YLOG_LOG, "Service %s removed", s->service_name);
340             else
341                 yaz_log(YLOG_FATAL, "Service %s could not be removed", s->service_name);
342             CloseServiceHandle(schService);
343         }
344         CloseServiceHandle(manager);
345         exit(0);
346     }
347     global_sc = s;
348     if (s->run_flag)
349     {
350         SERVICE_TABLE_ENTRY dt[2];
351
352         dt[0].lpServiceName = s->service_name;
353         dt[0].lpServiceProc = sc_service_main;
354         dt[1].lpServiceName = 0;
355         dt[1].lpServiceProc = 0;
356
357         s->argc = argc;
358         s->argv = argv;
359         if (!StartServiceCtrlDispatcher(dt))
360         {
361             yaz_log(YLOG_FATAL|YLOG_ERRNO, "Service %s could not be controlled",
362                     s->service_name);
363         }
364         return 0;
365     }
366 #endif /* WIN32 */
367     /* run the program standalone (with no service) */
368     return s->sc_main(s, argc, argv);
369 }
370
371 void yaz_sc_destroy(yaz_sc_t *s)
372 {
373     xfree((*s)->service_name);
374     xfree((*s)->display_name);
375     xfree(*s);
376     *s = 0;
377 }
378
379 /*
380  * Local variables:
381  * c-basic-offset: 4
382  * c-file-style: "Stroustrup"
383  * indent-tabs-mode: nil
384  * End:
385  * vim: shiftwidth=4 tabstop=8 expandtab
386  */
387