YAZ  5.34.0
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 
26 struct sc_s {
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)
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 
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 
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
WRBUF wrbuf_alloc(void)
construct WRBUF
Definition: wrbuf.c:25
const char * wrbuf_cstr(WRBUF b)
returns WRBUF content as C-string
Definition: wrbuf.c:281
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