[ovs-dev] [PATCH 05/11] daemon-windows: Implement --detach option for Windows.

Gurucharan Shetty shettyg at nicira.com
Fri Apr 18 18:04:12 UTC 2014


When "--detach" is specified, a daemon will create a new
process with the same command line options as the parent.
Additionally, an undocumented command line option "--pipe-handle"
is passed to child. Once the child is ready to handle external
commands, it communicates to the parent that it is ready (using
the pipe handle). The parent exits. This lets us run the daemons
in background. This will also help the unit tests because currently
most of the unit tests pass the '--detach' option to the daemons.

Signed-off-by: Gurucharan Shetty <gshetty at nicira.com>
---
 lib/daemon-windows.c |  107 +++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/daemon.h         |   26 ++++++++----
 2 files changed, 124 insertions(+), 9 deletions(-)

diff --git a/lib/daemon-windows.c b/lib/daemon-windows.c
index a00278e..baa7520 100644
--- a/lib/daemon-windows.c
+++ b/lib/daemon-windows.c
@@ -30,6 +30,10 @@ 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; /* pipe handle to write to the parent. */
+
 /* Handle to the Services Manager and the created service. */
 static SC_HANDLE manager, service;
 
@@ -51,6 +55,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 +83,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,8 +315,86 @@ 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;
+    }
+
+    /* We are running as a parent. */
+
+    /* 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()
 {
@@ -326,5 +418,16 @@ void daemonize_start(void)
 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();
 }
diff --git a/lib/daemon.h b/lib/daemon.h
index 38ec3ad..f4dced3 100644
--- a/lib/daemon.h
+++ b/lib/daemon.h
@@ -31,9 +31,9 @@
 
  * The DAEMON_OPTION_ENUMS, DAEMON_LONG_OPTIONS and DAEMON_OPTION_HANDLERS
  * macros are useful for parsing command-line options in individual utilities.
- * For e.g., the command-line option "--detach" is recognized on Linux
- * and results in calling the set_detach() function. The same option is not
- * recognized on Windows platform.
+ * For e.g., the command-line option "--monitor" is recognized on Linux
+ * and results in calling the daemon_set_monitor() function. The same option is
+ * not recognized on Windows platform.
  */
 
 #ifndef _WIN32
@@ -79,15 +79,26 @@ void set_no_chdir(void);
 void ignore_existing_pidfile(void);
 pid_t read_pidfile(const char *name);
 #else
-#define DAEMON_OPTION_ENUMS                     \
-    OPT_SERVICE,                                \
+#define DAEMON_OPTION_ENUMS                    \
+    OPT_DETACH,                                \
+    OPT_PIPE_HANDLE,                           \
+    OPT_SERVICE,                               \
     OPT_SERVICE_MONITOR
 
-#define DAEMON_LONG_OPTIONS                                             \
-        {"service",            no_argument, NULL, OPT_SERVICE},         \
+#define DAEMON_LONG_OPTIONS                                               \
+        {"detach",             no_argument, NULL, OPT_DETACH},            \
+        {"pipe-handle",        required_argument, NULL, OPT_PIPE_HANDLE}, \
+        {"service",            no_argument, NULL, OPT_SERVICE},           \
         {"service-monitor",    no_argument, NULL, OPT_SERVICE_MONITOR}
 
 #define DAEMON_OPTION_HANDLERS                  \
+        case OPT_DETACH:                        \
+            break;                              \
+                                                \
+        case OPT_PIPE_HANDLE:                   \
+            set_pipe_handle(optarg);            \
+            break;                              \
+                                                \
         case OPT_SERVICE:                       \
             break;                              \
                                                 \
@@ -95,6 +106,7 @@ pid_t read_pidfile(const char *name);
             break;
 
 void control_handler(DWORD request);
+void set_pipe_handle(const char *pipe_handle);
 #endif /* _WIN32 */
 
 bool get_detach(void);
-- 
1.7.9.5




More information about the dev mailing list