YAZ 5.35.1
sc.c
Go to the documentation of this file.
1/* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
4 */
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
26struct sc_s {
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
44yaz_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
63static 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)
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
148VOID 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
179static yaz_sc_t global_sc = 0;
180
181VOID 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
198static 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
227{
228#ifdef WIN32
229 sc_ReportSvcStatus(s, SERVICE_RUNNING, NO_ERROR, 0);
230#endif
231}
232
233int 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
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
int opt
Definition initopt.c:19
FILE * log_file
Definition log.c:48
void yaz_log(int level, const char *fmt,...)
Writes log message.
Definition log.c:487
void yaz_log_init_file(const char *fname)
sets log file
Definition log.c:168
Logging utility.
#define YLOG_FATAL
log level: fatal
Definition log.h:42
#define YLOG_ERRNO
log level: append system error message
Definition log.h:50
#define YLOG_LOG
log level: log (regular)
Definition log.h:48
void yaz_sc_destroy(yaz_sc_t *s)
frees service control handle
Definition sc.c:371
int yaz_sc_program(yaz_sc_t s, int argc, char **argv, int(*sc_main)(yaz_sc_t s, int argc, char **argv), void(*sc_stop)(yaz_sc_t s))
registers service controlled program
Definition sc.c:233
yaz_sc_t yaz_sc_create(const char *service_name, const char *display_name)
creates service handle
Definition sc.c:44
void yaz_sc_running(yaz_sc_t s)
signals that sc_main applicatio starts running
Definition sc.c:226
Header for Windows Service Control utility.
struct sc_s * yaz_sc_t
Definition sc.h:41
Definition sc.c:26
int start_flag
Definition sc.c:28
void(* sc_stop)(yaz_sc_t s)
Definition sc.c:34
int remove_flag
Definition sc.c:29
int(* sc_main)(yaz_sc_t s, int argc, char **argv)
Definition sc.c:33
int install_flag
Definition sc.c:27
int argc
Definition sc.c:35
char * display_name
Definition sc.c:32
int run_flag
Definition sc.c:30
char ** argv
Definition sc.c:36
char * service_name
Definition sc.c:31
string buffer
Definition wrbuf.h:43
void wrbuf_destroy(WRBUF b)
destroy WRBUF and its buffer
Definition wrbuf.c:38
const char * wrbuf_cstr(WRBUF b)
returns WRBUF content as C-string
Definition wrbuf.c:299
WRBUF wrbuf_alloc(void)
construct WRBUF
Definition wrbuf.c:25
void wrbuf_puts(WRBUF b, const char *buf)
appends C-string to WRBUF
Definition wrbuf.c:89
Header for WRBUF (growing buffer)
Header for memory handling functions.
#define xstrdup(s)
utility macro which calls xstrdup_f
Definition xmalloc.h:55
#define xfree(x)
utility macro which calls xfree_f
Definition xmalloc.h:53
#define xmalloc(x)
utility macro which calls malloc_f
Definition xmalloc.h:49