--- /dev/null
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2008 Index Data
+ * See the file LICENSE for details.
+ */
+
+#ifdef WIN32
+#include <windows.h>
+#include <tchar.h>
+#include <direct.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <yaz/xmalloc.h>
+#include <yaz/log.h>
+#include <yaz/sc.h>
+#include <yaz/wrbuf.h>
+
+struct sc_s {
+ int install_flag;
+ int start_flag;
+ int remove_flag;
+ int run_flag;
+ char *service_name;
+ char *display_name;
+ int (*sc_main)(yaz_sc_t s, int argc, char **argv);
+ void (*sc_stop)(yaz_sc_t s);
+ SERVICE_STATUS_HANDLE gSvcStatusHandle;
+ SERVICE_STATUS gSvcStatus;
+};
+
+
+yaz_sc_t yaz_sc_create(const char *service_name, const char *display_name)
+{
+ yaz_sc_t s = xmalloc(sizeof(*s));
+
+ s->service_name = xstrdup(service_name);
+ s->display_name = xstrdup(display_name);
+ s->install_flag = 0;
+ s->start_flag = 0;
+ s->remove_flag = 0;
+ s->run_flag = 0;
+ s->sc_main = 0;
+ s->sc_stop = 0;
+ s->gSvcStatusHandle = 0;
+ return s;
+}
+
+static void parse_args(yaz_sc_t s, int *argc_p, char ***argv_p)
+{
+ int skip_opt = 0;
+ const char *log_file = 0;
+ int i;
+
+ /* now look for the service arguments */
+ skip_opt = 0;
+ for (i = 1; i < *argc_p; i++)
+ {
+ const char *opt = (*argv_p)[i];
+ if (!strcmp(opt, "-install"))
+ {
+ s->install_flag = 1;
+ skip_opt = 1;
+ break;
+ }
+ else if (!strcmp(opt, "-installa"))
+ {
+ s->install_flag = 1;
+ s->start_flag = 1;
+ skip_opt = 1;
+ break;
+ }
+ else if (!strcmp(opt, "-remove"))
+ {
+ s->remove_flag = 1;
+ skip_opt = 1;
+ break;
+ }
+ else if (!strcmp(opt, "-run") && i < *argc_p-1)
+ {
+ /* -run dir */
+ const char *dir = (*argv_p)[i+1];
+ s->run_flag = 1;
+ chdir(dir);\r
+ skip_opt = 2;
+ break;
+ }
+ }
+ *argc_p -= skip_opt;
+ for (; i < *argc_p; i++)
+ (*argv_p)[i] = (*argv_p)[i + skip_opt];
+
+ /* we must have a YAZ log file to work with */
+ for (i = 1; i < *argc_p; i++)
+ {
+ const char *opt = (*argv_p)[i];
+ if (opt[0] == '-' && opt[1] == 'l')
+ {
+ if (opt[2])
+ {
+ log_file = opt+2;
+ skip_opt = 1;
+ break;
+ }
+ else if (i < *argc_p - 1)
+ {
+ log_file = (*argv_p)[i+1];
+ skip_opt = 2;
+ break;
+ }
+ }
+ }
+ if (log_file)
+ yaz_log_init_file(log_file);
+ else
+ {
+ if (s->install_flag)
+ {
+ yaz_log(YLOG_FATAL, "Must specify -l logfile for service to install");
+ exit(1);
+ }
+ }
+ if (s->run_flag)
+ { /* remove -l logfile for a running service */
+ for (; i < *argc_p; i++)
+ (*argv_p)[i] = (*argv_p)[i + skip_opt];
+ }
+}
+
+VOID sc_ReportSvcStatus(yaz_sc_t s,
+ DWORD dwCurrentState,
+ DWORD dwWin32ExitCode,
+ DWORD dwWaitHint)
+{
+ if (s->gSvcStatusHandle)
+ {
+ static DWORD dwCheckPoint = 1;
+
+ // Fill in the SERVICE_STATUS structure.
+
+ s->gSvcStatus.dwCurrentState = dwCurrentState;
+ s->gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
+ s->gSvcStatus.dwWaitHint = dwWaitHint;
+
+ if (dwCurrentState == SERVICE_START_PENDING)
+ s->gSvcStatus.dwControlsAccepted = 0;
+ else
+ s->gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ if ( (dwCurrentState == SERVICE_RUNNING) ||
+ (dwCurrentState == SERVICE_STOPPED) )
+ s->gSvcStatus.dwCheckPoint = 0;
+ else
+ s->gSvcStatus.dwCheckPoint = dwCheckPoint++;
+
+ // Report the status of the service to the SCM.
+ SetServiceStatus(s->gSvcStatusHandle, &s->gSvcStatus );
+ }
+}
+
+static yaz_sc_t global_sc = 0;
+
+VOID WINAPI sc_SvcCtrlHandler(DWORD dwCtrl)
+{
+ switch(dwCtrl)
+ {
+ case SERVICE_CONTROL_STOP:
+ yaz_log(YLOG_LOG, "Service %s to stop", global_sc->service_name);
+ sc_ReportSvcStatus(global_sc, SERVICE_STOP_PENDING, NO_ERROR, 0);
+ global_sc->sc_stop(global_sc);
+ sc_ReportSvcStatus(global_sc, SERVICE_STOPPED, NO_ERROR, 0);
+ return;
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+ default:
+ break;
+ }
+}
+
+static void WINAPI sc_service_main(DWORD argc, char **argv)
+{
+ yaz_sc_t s = global_sc;
+ int ret_code;
+
+ yaz_log(YLOG_LOG, "Service %s starting", s->service_name);
+
+ s->gSvcStatusHandle = RegisterServiceCtrlHandler(
+ s->service_name, sc_SvcCtrlHandler);
+
+ if (!s->gSvcStatusHandle)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "RegisterServiceCtrlHandler");
+ return;
+ }
+
+ s->gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ s->gSvcStatus.dwServiceSpecificExitCode = 0;
+
+ sc_ReportSvcStatus(s, SERVICE_START_PENDING, NO_ERROR, 3000);
+
+ ret_code = s->sc_main(s, argc, argv);
+
+ sc_ReportSvcStatus(s, SERVICE_STOPPED,
+ ret_code ? ERROR_SERVICE_SPECIFIC_ERROR : NO_ERROR, ret_code);
+}
+
+void yaz_sc_running(yaz_sc_t s)
+{
+ sc_ReportSvcStatus(s, SERVICE_RUNNING, NO_ERROR, 0);
+}
+
+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))
+
+{
+ s->sc_main = sc_main;
+ s->sc_stop = sc_stop;
+ parse_args(s, &argc, &argv);
+
+ if (s->install_flag || s->remove_flag)
+ {
+ SC_HANDLE manager = OpenSCManager(NULL /* machine */,
+ NULL /* database */,
+ SC_MANAGER_ALL_ACCESS);
+ if (manager == NULL)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "OpenSCManager failed");
+ exit(1);
+ }
+ if (s->install_flag)
+ {
+ SC_HANDLE schService = 0;
+ TCHAR szPath[2048];
+ int i;
+ WRBUF w = wrbuf_alloc();
+ char cwdstr[_MAX_PATH];
+
+ if (!_getcwd(cwdstr, sizeof(cwdstr)))
+ strcpy (cwdstr, ".");
+
+ if (GetModuleFileName(NULL, szPath, 2048) == 0)
+ {
+ yaz_log(YLOG_FATAL, "GetModuleFileName failed");
+ exit(1);
+ }
+ wrbuf_puts(w, szPath);
+ for (i = 1; i < argc; i++)
+ {
+ wrbuf_puts(w, " ");
+ wrbuf_puts(w, argv[i]);
+ }
+ wrbuf_puts(w, " -run \"");
+ wrbuf_puts(w, cwdstr);
+ wrbuf_puts(w, "\"");
+ yaz_log(YLOG_LOG, "path: %s", wrbuf_cstr(w));
+
+ schService =
+ CreateService(
+ manager, /* SCM database */
+ TEXT(s->service_name), /* name of service */
+ TEXT(s->display_name), /* service name to display */
+ SERVICE_ALL_ACCESS, /* desired access */
+ SERVICE_WIN32_OWN_PROCESS, /* service type */
+ s->start_flag ?
+ SERVICE_AUTO_START : SERVICE_DEMAND_START, /* start type */
+ SERVICE_ERROR_NORMAL, /* error control type */
+ wrbuf_cstr(w), /* path to service's binary */
+ NULL, /* no load ordering group */
+ NULL, /* no tag identifier */
+ NULL, /* no dependencies */
+ NULL, /* LocalSystem account */
+ NULL); /* no password */
+ if (schService == NULL)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Service %s could not be installed",
+ s->service_name);
+ CloseServiceHandle(manager);
+ exit(1);
+ }
+ yaz_log(YLOG_LOG, "Installed service %s", s->service_name);
+ CloseServiceHandle(schService);
+ wrbuf_destroy(w);
+ }
+ else if (s->remove_flag)
+ {
+ SC_HANDLE schService = 0;
+ SERVICE_STATUS serviceStatus;
+
+ schService = OpenService(manager, TEXT(s->service_name), SERVICE_ALL_ACCESS);
+ if (schService == NULL)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Service %s could not be opened",
+ s->service_name);
+ CloseServiceHandle(manager);
+ exit(1);
+ }
+ /* try to stop the service */
+ if (ControlService(schService, SERVICE_CONTROL_STOP, &serviceStatus))
+ {
+ yaz_log(YLOG_LOG, "Service %s being stopped", s->service_name);
+ Sleep(1000);
+ while (QueryServiceStatus(schService, &serviceStatus))
+ {
+ if (serviceStatus.dwCurrentState != SERVICE_STOP_PENDING)
+ break;
+ Sleep( 1000 );
+ }
+ if (serviceStatus.dwCurrentState != SERVICE_STOPPED)
+ yaz_log(YLOG_LOG|YLOG_FATAL, "Service failed to stop");
+ }
+ if (DeleteService(schService))
+ yaz_log(YLOG_LOG, "Service %s removed", s->service_name);
+ else
+ yaz_log(YLOG_FATAL, "Service %s could not be removed", s->service_name);
+ CloseServiceHandle(schService);
+ }
+ CloseServiceHandle(manager);
+ exit(0);
+ }
+ global_sc = s;
+ if (s->run_flag)
+ {
+ SERVICE_TABLE_ENTRY dt[2];
+
+ dt[0].lpServiceName = s->service_name;
+ dt[0].lpServiceProc = sc_service_main;
+ dt[1].lpServiceName = 0;
+ dt[1].lpServiceProc = 0;
+
+ if (!StartServiceCtrlDispatcher(dt))
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Service %s could not be controlled",
+ s->service_name);
+ }
+ }
+ else
+ {
+ /* run the program standalone (with no service) */
+ return s->sc_main(s, argc, argv);
+ }
+ return 0;
+}
+
+void yaz_sc_destroy(yaz_sc_t *s)
+{
+ xfree((*s)->service_name);
+ xfree((*s)->display_name);
+ xfree(*s);
+ *s = 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+