]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/daemon-windows.c
cirrus: Use FreeBSD 12.2.
[mirror_ovs.git] / lib / daemon-windows.c
index a00278ebb0bef7274ce5fa929959ba70ce808a04..7e5f264f5b9e7de52f51c5e16c2e887e2ba1bf4d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Nicira, Inc.
+ * Copyright (c) 2014, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include <config.h>
 #include "daemon.h"
+#include "daemon-private.h"
 #include <stdio.h>
+#include <io.h>
 #include <stdlib.h>
-#include "poll-loop.h"
-#include "vlog.h"
+#include <unistd.h>
+#include "dirs.h"
+#include "fatal-signal.h"
+#include "ovs-thread.h"
+#include "openvswitch/poll-loop.h"
+#include "openvswitch/vlog.h"
 
-VLOG_DEFINE_THIS_MODULE(daemon);
+VLOG_DEFINE_THIS_MODULE(daemon_windows);
+
+/* Constants for flock function */
+#define        LOCK_SHARED     0x0                     /* Shared lock. */
+#define        LOCK_UNLOCK     0x80000000              /* Unlock. Custom value. */
 
 static bool service_create;          /* Was --service specified? */
 static bool service_started;         /* Have we dispatched service to start? */
@@ -30,6 +40,13 @@ static bool service_started;         /* Have we dispatched service to start? */
  * unexpectedly? */
 static bool monitor;
 
+bool detach;                 /* Was --detach specified? */
+static bool detached;        /* Running as the child process. */
+static HANDLE write_handle;  /* End of pipe to write to parent. */
+
+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 +68,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
@@ -60,8 +79,15 @@ daemon_usage(void)
         "\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);
+                                   "unexpected failure. \n");
+}
+
+/* Sets up a following call to service_start() to detach from the foreground
+ * session, running this process in the background.  */
+void
+set_detach(void)
+{
+    detach = true;
 }
 
 /* Registers the call-back and configures the actions in case of a failure
@@ -77,6 +103,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. */
@@ -89,7 +123,7 @@ service_start(int *argcp, char **argvp[])
             VLOG_FATAL("Failed to create a event (%s).", msg_buf);
         }
 
-        poll_fd_wait_event(0, wevent, POLLIN);
+        poll_wevent_wait(wevent);
 
         /* Register the control handler. This function is called by the service
          * manager to stop the service. */
@@ -112,12 +146,11 @@ service_start(int *argcp, char **argvp[])
         *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();
+        /* Enable default error mode so we can take advantage of WER
+         * (Windows Error Reporting) crash dumps.
+         * Being a service it does not allow for WER window pop-up.
+         * XXX implement our on crash dump collection mechanism. */
+        SetErrorMode(0);
 
         return;
     }
@@ -172,6 +205,7 @@ control_handler(DWORD request)
         service_status.dwCurrentState = SERVICE_STOPPED;
         service_status.dwWin32ExitCode = NO_ERROR;
         SetEvent(wevent);
+        SetServiceStatus(hstatus, &service_status);
         break;
 
     default:
@@ -188,7 +222,7 @@ should_service_stop(void)
         if (service_status.dwCurrentState != SERVICE_RUNNING) {
             return true;
         } else {
-            poll_fd_wait_event(0, wevent, POLLIN);
+            poll_wevent_wait(wevent);
         }
     }
     return false;
@@ -200,6 +234,11 @@ should_service_stop(void)
 void
 service_stop()
 {
+    if (!service_started) {
+        return;
+    }
+    fatal_signal_atexit_handler();
+
     ResetEvent(wevent);
     CloseHandle(wevent);
 
@@ -301,30 +340,214 @@ set_config_failure_actions()
     }
 }
 
-\f
-/* Stub functions to handle daemonize related calls in non-windows platform. */
-bool
-get_detach()
+/* 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)
 {
-    return false;
+    write_handle = (HANDLE) atoi(pipe_handle);
 }
 
-void
-daemon_save_fd(int fd OVS_UNUSED)
+/* 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 (!detach && !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);
 }
 
-void
-daemonize(void)
+static void
+flock(FILE* fd, int operation)
 {
+    HANDLE hFile;
+    OVERLAPPED ov = {0};
+
+    hFile = (HANDLE)_get_osfhandle(fileno(fd));
+    if (hFile == INVALID_HANDLE_VALUE) {
+        VLOG_FATAL("Failed to get PID file handle (%s).",
+                   ovs_strerror(errno));
+    }
+
+    if (operation & LOCK_UNLOCK) {
+        if (UnlockFileEx(hFile, 0, 1, 0, &ov) == 0) {
+            VLOG_FATAL("Failed to unlock PID file (%s).",
+                       ovs_lasterror_to_string());
+        }
+    } else {
+       /* Use LOCKFILE_FAIL_IMMEDIATELY flag to avoid hang of another daemon that tries to
+           acquire exclusive lock over the same PID file */
+        if (LockFileEx(hFile, operation | LOCKFILE_FAIL_IMMEDIATELY,
+                       0, 1, 0, &ov) == FALSE) {
+            VLOG_FATAL("Failed to lock PID file (%s).",
+                       ovs_lasterror_to_string());
+        }
+    }
+}
+
+static void
+unlink_pidfile(void)
+{
+    if (filep_pidfile) {
+        /* Remove the shared lock on file */
+        flock(filep_pidfile, LOCK_UNLOCK);
+        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));
+    }
+
+    flock(filep_pidfile, LOCKFILE_EXCLUSIVE_LOCK);
+
+    fatal_signal_add_hook(unlink_pidfile, NULL, NULL, true);
+
+    fprintf(filep_pidfile, "%ld\n", (long int) getpid());
+    if (fflush(filep_pidfile) == EOF) {
+        VLOG_FATAL("Failed to write into the pidfile %s", pidfile);
+    }
+
+    flock(filep_pidfile, LOCK_SHARED);
+    /* This will remove the exclusive lock. The shared lock will remain */
+    flock(filep_pidfile, LOCK_UNLOCK);
+
+    /* Don't close the pidfile till the process exits. */
 }
 
-void daemonize_start(void)
+void
+daemonize_start(bool access_datapath OVS_UNUSED)
 {
+    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();
 }
+
+void
+daemon_become_new_user(bool access_datapath OVS_UNUSED)
+{
+}
+
+/* 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. */
+char *
+make_pidfile_name(const char *name)
+{
+    if (name) {
+        if (strchr(name, ':')) {
+            return xstrdup(name);
+        } else {
+            return xasprintf("%s/%s", ovs_rundir(), name);
+        }
+    } else {
+        return xasprintf("%s/%s.pid", ovs_rundir(), program_name);
+    }
+}
+
+void
+daemon_set_new_user(const char *user_spec OVS_UNUSED)
+{
+    VLOG_FATAL("--user options is not currently supported.");
+}