X-Git-Url: http://git.onelab.eu/?p=sliver-openvswitch.git;a=blobdiff_plain;f=lib%2Fdaemon-windows.c;h=f059fc519dc5a94df3c964d4b85d956cd2dac6ee;hp=a00278ebb0bef7274ce5fa929959ba70ce808a04;hb=a91dc4447bb6c6bd8807aaaa4e00af0abe4fa5c7;hpb=69b17834a47300d13e86c680f03800d107ab9b1e diff --git a/lib/daemon-windows.c b/lib/daemon-windows.c index a00278ebb..f059fc519 100644 --- a/lib/daemon-windows.c +++ b/lib/daemon-windows.c @@ -21,7 +21,7 @@ #include "poll-loop.h" #include "vlog.h" -VLOG_DEFINE_THIS_MODULE(daemon); +VLOG_DEFINE_THIS_MODULE(daemon_windows); static bool service_create; /* Was --service specified? */ static bool service_started; /* Have we dispatched service to start? */ @@ -30,6 +30,13 @@ static bool service_started; /* Have we dispatched service to start? */ * 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; @@ -51,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 @@ -77,6 +86,14 @@ service_start(int *argcp, char **argvp[]) {NULL, NULL} }; + /* 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. */ @@ -301,12 +318,88 @@ 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 @@ -317,14 +410,100 @@ daemon_save_fd(int fd OVS_UNUSED) void daemonize(void) { + daemonize_start(); + daemonize_complete(); +} + +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; + 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); +}