The following code does not add any users yet. The visioned workflow that this piece of code should work with is: * Create a windows service through a startup script with a tool like 'sc' ex: sc create ovsdb-server binpath= "C:\openvswitch\usr\sbin\ovsdb-server.exe -vconsole:off -vsyslog:off -vfile:info --remote=ptcp:6632:127.0.0.1 --log-file --service-monitor --service"
* Start the service from the startup script. ex: sc start ovsdb-server * Terminate the service during shutdown process. ex: sc stop ovsdb-server * Abrupt termination will restart the service. Signed-off-by: Gurucharan Shetty <gshe...@nicira.com> --- lib/automake.mk | 12 +- lib/daemon-windows.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/daemon.c | 19 +++ lib/daemon.h | 31 ++++- lib/service-syn.man | 3 + lib/service.man | 11 ++ 6 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 lib/daemon-windows.c create mode 100644 lib/service-syn.man create mode 100644 lib/service.man diff --git a/lib/automake.mk b/lib/automake.mk index 449d2c5..0617136 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -40,7 +40,6 @@ lib_libopenvswitch_la_SOURCES = \ lib/crc32c.h \ lib/csum.c \ lib/csum.h \ - lib/daemon.c \ lib/daemon.h \ lib/dhcp.h \ lib/dummy.c \ @@ -237,6 +236,15 @@ lib_libopenvswitch_la_SOURCES = \ lib/vswitch-idl.h \ lib/vtep-idl.c \ lib/vtep-idl.h + +if WIN32 +lib_libopenvswitch_la_SOURCES += \ + lib/daemon-windows.c +else +lib_libopenvswitch_la_SOURCES += \ + lib/daemon.c +endif + EXTRA_DIST += \ lib/stdio.h.in \ lib/string.h.in @@ -328,6 +336,8 @@ MAN_FRAGMENTS += \ lib/memory-unixctl.man \ lib/ofp-version.man \ lib/ovs.tmac \ + lib/service.man \ + lib/service-syn.man \ lib/ssl-bootstrap.man \ lib/ssl-bootstrap-syn.man \ lib/ssl-peer-ca-cert.man \ diff --git a/lib/daemon-windows.c b/lib/daemon-windows.c new file mode 100644 index 0000000..2ccf330 --- /dev/null +++ b/lib/daemon-windows.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2014 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include "daemon.h" +#include <stdio.h> +#include <stdlib.h> +#include "vlog.h" + +#pragma comment(lib, "advapi32") + +VLOG_DEFINE_THIS_MODULE(daemon); + +static bool detach; /* Was --service specified? */ +static bool detached; /* Have we already detached? */ + +/* --service-monitor: Should the service be restarted if it dies + * unexpectedly? */ +static bool monitor; + +/* Handle to the Services Manager and the created service. */ +static SC_HANDLE manager, service; + +/* Handle to the status information structure for the current service. */ +SERVICE_STATUS_HANDLE hstatus; + +/* Hold the service's current status. */ +SERVICE_STATUS service_status; + +/* Hold the arguments sent to the main function. */ +static int sargc; +static char ***sargvp; + +static void check_service(void); +static void handle_scm_callback(void); +static void init_service_status(void); +static void set_config_failure_actions(void); + +extern int main(int argc, char *argv[]); + +void +daemon_usage(void) +{ + printf( + "\nService options:\n" + " --service run in background as a service.\n" + " --service-monitor restart the service in case of an " + "unexpected failure. \n", + ovs_rundir(), program_name); +} + +/* Registers the call-back and configures the actions in case of a failure + * with the windows services manager. */ +void +service_start(int *argcp, char **argvp[]) +{ + LPVOID msg_buf; + int argc = *argcp; + char **argv = *argvp; + int i; + SERVICE_TABLE_ENTRY service_table[] = { + {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main}, + {NULL, NULL} + }; + + /* 'detached' is 'false' when service_start() is called the first time. + * It is 'true', when it is called the second time by the windows services + * manager. */ + if (detached) { + init_service_status(); + + /* Register the control handler. This function is called by the service + * manager to stop the service. */ + hstatus = RegisterServiceCtrlHandler(program_name, + (LPHANDLER_FUNCTION)control_handler); + if (!hstatus) { + ovs_lasterror_to_string((LPTSTR)&msg_buf); + VLOG_FATAL("Failed to register the service control handler (%s).", + msg_buf); + } + + if (monitor) { + set_config_failure_actions(); + } + + /* When the service control manager does the call back, it does not + * send the same arguments as sent to the main function during the + * service start. So, use the arguments passed over during the first + * time. */ + *argcp = sargc; + *argvp = *sargvp; + + /* XXX: Windows implementation cannot have a unixctl commands in the + * traditional sense of unix domain sockets. If an implementation is + * done that involves 'unixctl' vlog commands the following call is + * needed to make sure that the unixctl commands for vlog get + * registered in a daemon, even before the first log message. */ + vlog_init(); + + return; + } + + assert_single_threaded(); + + /* A reference to arguments passed to the main function the first time. + * We need it after the call-back from service control manager. */ + sargc = argc; + sargvp = argvp; + + /* We are only interested in the '--service' and '--service-monitor' + * options before the call-back from the service control manager. */ + for (i = 0; i < argc; i ++) { + if (!strcmp(argv[i], "--service")) { + detach = true; + } else if (!strcmp(argv[i], "--service-monitor")) { + monitor = true; + } + } + + /* If '--service' is not a command line option, run in foreground. */ + if (!detach) { + return; + } + + /* If we have been configured to run as a service, then that service + * should already have been created either manually or through a start up + * script. */ + check_service(); + + detached = true; + + /* StartServiceCtrlDispatcher blocks and returns after the service is + * stopped. */ + if (!StartServiceCtrlDispatcher(service_table)) { + ovs_lasterror_to_string((LPTSTR)&msg_buf); + VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf); + } + exit(0); +} + +/* This function is registered with the windows services manager through + * a call to RegisterServiceCtrlHandler() and will be called by the windows + * services manager asynchronously to stop the service. */ +void +control_handler(DWORD request) +{ + switch (request) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + service_status.dwCurrentState = SERVICE_STOPPED; + service_status.dwWin32ExitCode = NO_ERROR; + break; + + default: + break; + } +} + +/* Return 'true' if the windows services manager has called the + * control_handler() and asked the program to terminate. */ +bool +should_service_stop(void) +{ + if (detached && service_status.dwCurrentState != SERVICE_RUNNING) { + return true; + } + return false; +} + +/* Set the service as stopped. The control manager will terminate the + * service soon after this call. Hence, this should ideally be the last + * call before termination. */ +void +service_stop() +{ + service_status.dwCurrentState = SERVICE_STOPPED; + service_status.dwWin32ExitCode = NO_ERROR; + SetServiceStatus(hstatus, &service_status); +} + +/* Call this function to signal that the daemon is ready. init_service() + * or control_handler() has already initalized the + * service_status.dwCurrentState .*/ +static void +service_complete(void) +{ + if (hstatus) { + SetServiceStatus(hstatus, &service_status); + } +} + +/* Check whether 'program_name' has been created as a service. */ +static void +check_service() +{ + LPVOID msg_buf; + + /* Establish a connection to the local service control manager. */ + manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); + if (!manager) { + ovs_lasterror_to_string((LPTSTR)&msg_buf); + VLOG_FATAL("Failed to open the service control manager (%s).", + msg_buf); + } + + service = OpenService(manager, program_name, SERVICE_ALL_ACCESS); + if (!service) { + ovs_lasterror_to_string((LPTSTR)&msg_buf); + VLOG_FATAL("Failed to open service (%s).", msg_buf); + } +} + +/* Service status of a service can be checked asynchronously through + * tools like 'sc' or through windows services manager and is set + * through a call to SetServiceStatus(). */ +static void +init_service_status() +{ + /* The service runs in its own process. */ + service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + + /* The control codes the service accepts. */ + service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN; + + /* Initialize the current state as SERVICE_RUNNING. */ + service_status.dwCurrentState = SERVICE_RUNNING; + + /* The exit code to indicate if there was an error. */ + service_status.dwWin32ExitCode = NO_ERROR; + + /* The checkpoint value the service increments periodically. Set as 0 + * as we do not plan to periodically increment the value. */ + service_status.dwCheckPoint = 0; + + /* The estimated time required for the stop operation in ms. */ + service_status.dwWaitHint = 1000; +} + +/* In case of an unexpected termination, configure the action to be + * taken. */ +static void +set_config_failure_actions() +{ + /* In case of a failure, restart the process the first two times + * After 'dwResetPeriod', the failure count is reset. */ + SC_ACTION fail_action[3] = { + {SC_ACTION_RESTART, 0}, + {SC_ACTION_RESTART, 0}, + {SC_ACTION_NONE, 0} + }; + SERVICE_FAILURE_ACTIONS service_fail_action; + LPVOID msg_buf; + + /* Reset failure count after (in seconds). */ + service_fail_action.dwResetPeriod = 10; + + /* Reboot message. */ + service_fail_action.lpRebootMsg = NULL; + + /* The command line of the process. */ + service_fail_action.lpCommand = NULL; + + /* Number of elements in 'fail_actions'. */ + service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]); + + /* A pointer to an array of SC_ACTION structures. */ + service_fail_action.lpsaActions = fail_action; + + if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, + &service_fail_action)) { + ovs_lasterror_to_string((LPTSTR)&msg_buf); + VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf); + } +} + + +/* Stub functions to handle daemonize related calls in non-windows platform. */ +bool +get_detach() +{ + return false; +} + +void +daemon_save_fd(int fd OVS_UNUSED) +{ +} + +void +daemonize(void) +{ +} + +void daemonize_start(void) +{ +} + +void +daemonize_complete(void) +{ + service_complete(); +} diff --git a/lib/daemon.c b/lib/daemon.c index ab579b6..f9290ef 100644 --- a/lib/daemon.c +++ b/lib/daemon.c @@ -717,3 +717,22 @@ check_already_running(void) pidfile, ovs_strerror(-pid)); } } + + +/* stub functions for non-windows platform. */ + +void +service_start(int *argc OVS_UNUSED, char **argv[] OVS_UNUSED) +{ +} + +void +service_stop(void) +{ +} + +bool +should_service_stop(void) +{ + return false; +} diff --git a/lib/daemon.h b/lib/daemon.h index 57f8514..ad57362 100644 --- a/lib/daemon.h +++ b/lib/daemon.h @@ -21,6 +21,7 @@ #include <stdbool.h> #include <sys/types.h> +#ifndef _WIN32 #define DAEMON_OPTION_ENUMS \ OPT_DETACH, \ OPT_NO_CHDIR, \ @@ -56,17 +57,39 @@ daemon_set_monitor(); \ break; +void set_detach(void); +void daemon_set_monitor(void); void set_pidfile(const char *name); void set_no_chdir(void); -void set_detach(void); +void ignore_existing_pidfile(void); +pid_t read_pidfile(const char *name); +#else +#define DAEMON_OPTION_ENUMS \ + OPT_SERVICE, \ + OPT_SERVICE_MONITOR + +#define DAEMON_LONG_OPTIONS \ + {"service", no_argument, NULL, OPT_SERVICE}, \ + {"service-monitor", no_argument, NULL, OPT_SERVICE_MONITOR} + +#define DAEMON_OPTION_HANDLERS \ + case OPT_SERVICE: \ + break; \ + \ + case OPT_SERVICE_MONITOR: \ + break; + +void control_handler(DWORD request); +#endif /* _WIN32 */ + bool get_detach(void); -void daemon_set_monitor(void); void daemon_save_fd(int fd); void daemonize(void); void daemonize_start(void); void daemonize_complete(void); -void ignore_existing_pidfile(void); void daemon_usage(void); -pid_t read_pidfile(const char *name); +void service_start(int *argcp, char **argvp[]); +void service_stop(void); +bool should_service_stop(void); #endif /* daemon.h */ diff --git a/lib/service-syn.man b/lib/service-syn.man new file mode 100644 index 0000000..d029f22 --- /dev/null +++ b/lib/service-syn.man @@ -0,0 +1,3 @@ +.IP "Service options:" +[\fB\-\-service\fR] +[\fB\-\-service\-monitor\fR] diff --git a/lib/service.man b/lib/service.man new file mode 100644 index 0000000..c7927af --- /dev/null +++ b/lib/service.man @@ -0,0 +1,11 @@ +.TP +\fB\-\-service\fR +Causes \fB\*(PN\fR to run as a service in the background. The service +should already have been created through external tools like \fBSC.exe\fR. +. +.TP +\fB\-\-service\-monitor\fR +Causes the \fB\*(PN\fR service to be automatically restarted by the windows +services manager if the service dies or exits for unexpected reasons. +.IP +When \fB\-\-service\fR is not specified, this option has no effect. -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev