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