X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdaemon-windows.c;h=4d33b8c09d9929dc99992b12c9bf470dee03c9ec;hb=d6bc33f37a218832456fed11fa1796e6b7e93102;hp=60e028bbb4e07cd45580aa2331046bc8e7c762e3;hpb=fda546bd0b1737bf3beeef69b32d0df6dc9e0ad4;p=sliver-openvswitch.git diff --git a/lib/daemon-windows.c b/lib/daemon-windows.c index 60e028bbb..4d33b8c09 100644 --- a/lib/daemon-windows.c +++ b/lib/daemon-windows.c @@ -21,17 +21,22 @@ #include "poll-loop.h" #include "vlog.h" -#pragma comment(lib, "advapi32") +VLOG_DEFINE_THIS_MODULE(daemon_windows); -VLOG_DEFINE_THIS_MODULE(daemon); - -static bool detach; /* Was --service specified? */ -static bool detached; /* Have we already detached? */ +static bool service_create; /* Was --service specified? */ +static bool service_started; /* Have we dispatched service to start? */ /* --service-monitor: Should the service be restarted if it dies * unexpectedly? */ static bool monitor; +static bool detach; /* Was --detach specified? */ +static bool detached; /* Running as the child process. */ +static HANDLE write_handle; /* End of pipe to write to parent. */ + +static char *pidfile; /* --pidfile: Name of pidfile (null if none). */ +static FILE *filep_pidfile; /* File pointer to access the pidfile. */ + /* Handle to the Services Manager and the created service. */ static SC_HANDLE manager, service; @@ -53,6 +58,8 @@ static void handle_scm_callback(void); static void init_service_status(void); static void set_config_failure_actions(void); +static bool detach_process(int argc, char *argv[]); + extern int main(int argc, char *argv[]); void @@ -79,10 +86,18 @@ service_start(int *argcp, char **argvp[]) {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) { + /* If one of the command line option is "--detach", we create + * a new process in case of parent, wait for child to start and exit. + * In case of the child, we just return. We should not be creating a + * service in either case. */ + if (detach_process(argc, argv)) { + return; + } + + /* 'service_started' 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 (service_started) { init_service_status(); wevent = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -135,14 +150,14 @@ service_start(int *argcp, char **argvp[]) * options before the call-back from the service control manager. */ for (i = 0; i < argc; i ++) { if (!strcmp(argv[i], "--service")) { - detach = true; + service_create = true; } else if (!strcmp(argv[i], "--service-monitor")) { monitor = true; } } /* If '--service' is not a command line option, run in foreground. */ - if (!detach) { + if (!service_create) { return; } @@ -151,7 +166,7 @@ service_start(int *argcp, char **argvp[]) * script. */ check_service(); - detached = true; + service_started = true; /* StartServiceCtrlDispatcher blocks and returns after the service is * stopped. */ @@ -186,7 +201,7 @@ control_handler(DWORD request) bool should_service_stop(void) { - if (detached) { + if (service_started) { if (service_status.dwCurrentState != SERVICE_RUNNING) { return true; } else { @@ -303,30 +318,190 @@ set_config_failure_actions() } } - -/* Stub functions to handle daemonize related calls in non-windows platform. */ +/* When a daemon is passed the --detach option, we create a new + * process and pass an additional non-documented option called --pipe-handle. + * Through this option, the parent passes one end of a pipe handle. */ +void +set_pipe_handle(const char *pipe_handle) +{ + write_handle = (HANDLE) atoi(pipe_handle); +} + +/* If one of the command line option is "--detach", creates + * a new process in case of parent, waits for child to start and exits. + * In case of the child, returns. */ +static bool +detach_process(int argc, char *argv[]) +{ + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; + PROCESS_INFORMATION pi; + HANDLE read_pipe, write_pipe; + char *buffer; + int error, i; + char ch; + + /* We are only interested in the '--detach' and '--pipe-handle'. */ + for (i = 0; i < argc; i ++) { + if (!strcmp(argv[i], "--detach")) { + detach = true; + } else if (!strncmp(argv[i], "--pipe-handle", 13)) { + /* If running as a child, return. */ + detached = true; + return true; + } + } + + /* Nothing to do if the option --detach is not set. */ + if (!detach) { + return false; + } + + /* Set the security attribute such that a process created will + * inherit the pipe handles. */ + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* Create an anonymous pipe to communicate with the child. */ + error = CreatePipe(&read_pipe, &write_pipe, &sa, 0); + if (!error) { + VLOG_FATAL("CreatePipe failed (%s)", ovs_lasterror_to_string()); + } + + GetStartupInfo(&si); + + /* To the child, we pass an extra argument '--pipe-handle=write_pipe' */ + buffer = xasprintf("%s %s=%ld", GetCommandLine(), "--pipe-handle", + write_pipe); + + /* Create a detached child */ + error = CreateProcess(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, + NULL, NULL, &si, &pi); + if (!error) { + VLOG_FATAL("CreateProcess failed (%s)", ovs_lasterror_to_string()); + } + + /* Close one end of the pipe in the parent. */ + CloseHandle(write_pipe); + + /* Block and wait for child to say it is ready. */ + error = ReadFile(read_pipe, &ch, 1, NULL, NULL); + if (!error) { + VLOG_FATAL("Failed to read from child (%s)", + ovs_lasterror_to_string()); + } + /* The child has successfully started and is ready. */ + exit(0); +} + +/* Will daemonize() really detach? */ bool get_detach() { - return false; + return detach; } void -daemon_save_fd(int fd OVS_UNUSED) +daemonize(void) { + daemonize_start(); + daemonize_complete(); } -void -daemonize(void) +static void +unlink_pidfile(void) +{ + if (filep_pidfile) { + fclose(filep_pidfile); + } + if (pidfile) { + unlink(pidfile); + } +} + +/* If a pidfile has been configured, creates it and stores the running + * process's pid in it. Ensures that the pidfile will be deleted when the + * process exits. */ +static void +make_pidfile(void) { + int error; + + error = GetFileAttributes(pidfile); + if (error != INVALID_FILE_ATTRIBUTES) { + /* pidfile exists. Try to unlink() it. */ + error = unlink(pidfile); + if (error) { + VLOG_FATAL("Failed to delete existing pidfile %s (%s)", pidfile, + ovs_strerror(errno)); + } + } + + filep_pidfile = fopen(pidfile, "w"); + if (filep_pidfile == NULL) { + VLOG_FATAL("failed to open %s (%s)", pidfile, ovs_strerror(errno)); + } + + fatal_signal_add_hook(unlink_pidfile, NULL, NULL, true); + + fprintf(filep_pidfile, "%d\n", _getpid()); + if (fflush(filep_pidfile) == EOF) { + VLOG_FATAL("Failed to write into the pidfile %s", pidfile); + } + + /* Don't close the pidfile till the process exits. */ } void daemonize_start(void) { + if (pidfile) { + make_pidfile(); + } } void daemonize_complete(void) { + /* If running as a child because '--detach' option was specified, + * communicate with the parent to inform that the child is ready. */ + if (detached) { + int error; + + close_standard_fds(); + + error = WriteFile(write_handle, "a", 1, NULL, NULL); + if (!error) { + VLOG_FATAL("Failed to communicate with the parent (%s)", + ovs_lasterror_to_string()); + } + } + service_complete(); } + +/* Returns the file name that would be used for a pidfile if 'name' were + * provided to set_pidfile(). The caller must free the returned string. */ +static char * +make_pidfile_name(const char *name) +{ + if (name && strchr(name, ':')) { + return strdup(name); + } else { + return xasprintf("%s/%s.pid", ovs_rundir(), program_name); + } +} + +/* Sets up a following call to daemonize() to create a pidfile named 'name'. + * If 'name' begins with '/', then it is treated as an absolute path. + * Otherwise, it is taken relative to RUNDIR, which is $(prefix)/var/run by + * default. + * + * If 'name' is null, then program_name followed by ".pid" is used. */ +void +set_pidfile(const char *name) +{ + assert_single_threaded(); + free(pidfile); + pidfile = make_pidfile_name(name); +}