]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: introduce shared daemon startup code
authorRafael Fonseca <r4f4rfs@gmail.com>
Thu, 26 Mar 2020 15:18:00 +0000 (16:18 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Fri, 27 Mar 2020 15:22:49 +0000 (16:22 +0100)
Several daemons have similar code around general daemon startup code.
Let's move it into a file and share it among them.

Signed-off-by: Rafael Fonseca <r4f4rfs@gmail.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
build-aux/syntax-check.mk
po/POTFILES.in
src/libvirt_private.syms
src/util/Makefile.inc.am
src/util/virdaemon.c [new file with mode: 0644]
src/util/virdaemon.h [new file with mode: 0644]

index 2a38c03ba9e025fbf483603946cc78f7a375e8d7..6ddf34a332a68f93c74fdcd961b5edaf8fc6b989 100644 (file)
@@ -2016,7 +2016,7 @@ exclude_file_name_regexp--sc_prohibit_close = \
 exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \
   (^tests/(virhostcpu|virpcitest)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$)
 
-_src2=src/(util/vircommand|libvirt|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon|remote/remote_daemon)
+_src2=src/(util/(vircommand|virdaemon)|libvirt|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon|remote/remote_daemon)
 exclude_file_name_regexp--sc_prohibit_fork_wrappers = \
   (^($(_src2)|tests/testutils)\.c$$)
 
index 6103d4ca4a0b185b2bafc884be8cbbb4042887f6..551411a85485511a2251033b2ea7e87c69aa3a70 100644 (file)
 @SRCDIR@/src/util/vircommand.c
 @SRCDIR@/src/util/virconf.c
 @SRCDIR@/src/util/vircrypto.c
+@SRCDIR@/src/util/virdaemon.c
 @SRCDIR@/src/util/virdbus.c
 @SRCDIR@/src/util/virdnsmasq.c
 @SRCDIR@/src/util/virerror.c
index 3f032c7963ff7797f274df61ac65601b8c3be5cc..e276f55bb1e21ec982aedd686a79d0d30b7b473b 100644 (file)
@@ -1906,6 +1906,12 @@ virCryptoHashString;
 virCryptoHaveCipher;
 
 
+# util/virdaemon.h
+virDaemonForkIntoBackground;
+virDaemonSetupLogging;
+virDaemonUnixSocketPaths;
+
+
 # util/virdbus.h
 virDBusCallMethod;
 virDBusCloseSystemBus;
index 718b11a5f4c6d9d82dac67d25183f82e23817e81..5bc60cb5ead4eb783777bc486b448893eceadd21 100644 (file)
@@ -42,6 +42,8 @@ UTIL_SOURCES = \
        util/virconf.h \
        util/vircrypto.c \
        util/vircrypto.h \
+       util/virdaemon.c \
+       util/virdaemon.h \
        util/virdbus.c \
        util/virdbus.h \
        util/virdbuspriv.h \
diff --git a/src/util/virdaemon.c b/src/util/virdaemon.c
new file mode 100644 (file)
index 0000000..789adc6
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * virdaemon.c: shared daemon setup code
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include "virdaemon.h"
+#include "virutil.h"
+#include "virfile.h"
+#include "virlog.h"
+#include "viralloc.h"
+
+#include "configmake.h"
+
+int
+virDaemonForkIntoBackground(const char *argv0)
+{
+    int statuspipe[2];
+    if (virPipeQuiet(statuspipe) < 0)
+        return -1;
+
+    pid_t pid = fork();
+    switch (pid) {
+    case 0:
+        {
+            /* intermediate child */
+            int stdinfd = -1;
+            int stdoutfd = -1;
+            int nextpid;
+
+            VIR_FORCE_CLOSE(statuspipe[0]);
+
+            if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
+                goto cleanup;
+            if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0)
+                goto cleanup;
+            if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
+                goto cleanup;
+            if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
+                goto cleanup;
+            if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
+                goto cleanup;
+            if (VIR_CLOSE(stdinfd) < 0)
+                goto cleanup;
+            if (VIR_CLOSE(stdoutfd) < 0)
+                goto cleanup;
+
+            if (setsid() < 0)
+                goto cleanup;
+
+            nextpid = fork();
+            switch (nextpid) {
+            case 0: /* grandchild */
+                return statuspipe[1];
+            case -1: /* error */
+                goto cleanup;
+            default: /* intermediate child succeeded */
+                _exit(EXIT_SUCCESS);
+            }
+
+         cleanup:
+            VIR_FORCE_CLOSE(stdoutfd);
+            VIR_FORCE_CLOSE(stdinfd);
+            VIR_FORCE_CLOSE(statuspipe[1]);
+            _exit(EXIT_FAILURE);
+
+        }
+
+    case -1: /* error in parent */
+        goto error;
+
+    default:
+        {
+            /* parent */
+            int got, exitstatus = 0;
+            int ret;
+            char status;
+
+            VIR_FORCE_CLOSE(statuspipe[1]);
+
+            /* We wait to make sure the first child forked successfully */
+            if ((got = waitpid(pid, &exitstatus, 0)) < 0 ||
+                got != pid ||
+                exitstatus != 0) {
+                goto error;
+            }
+
+            /* If we got here, then the grandchild was spawned, so we
+             * must exit. Block until the second child initializes
+             * successfully */
+            ret = saferead(statuspipe[0], &status, 1);
+
+            VIR_FORCE_CLOSE(statuspipe[0]);
+
+            if (ret != 1) {
+                fprintf(stderr,
+                        _("%s: error: unable to determine if daemon is "
+                          "running: %s\n"), argv0,
+                        g_strerror(errno));
+                exit(EXIT_FAILURE);
+            } else if (status != 0) {
+                fprintf(stderr,
+                        _("%s: error: %s. Check /var/log/messages or run without "
+                          "--daemon for more info.\n"), argv0,
+                        virDaemonErrTypeToString(status));
+                exit(EXIT_FAILURE);
+            }
+            _exit(EXIT_SUCCESS);
+        }
+    }
+
+ error:
+    VIR_FORCE_CLOSE(statuspipe[0]);
+    VIR_FORCE_CLOSE(statuspipe[1]);
+    return -1;
+}
+
+
+/*
+ * Set up the logging environment
+ * By default if daemonized all errors go to the logfile libvirtd.log,
+ * but if verbose or error debugging is asked for then also output
+ * informational and debug messages. Default size if 64 kB.
+ */
+void
+virDaemonSetupLogging(const char *daemon_name,
+                      unsigned int log_level,
+                      char *log_filters,
+                      char *log_outputs,
+                      bool privileged,
+                      bool verbose,
+                      bool godaemon)
+{
+    virLogReset();
+
+    /*
+     * Libvirtd's order of precedence is:
+     * cmdline > environment > config
+     *
+     * Given the precedence, we must process the variables in the opposite
+     * order, each one overriding the previous.
+     */
+    if (log_level != 0)
+        virLogSetDefaultPriority(log_level);
+
+    /* In case the config is empty, both filters and outputs will become empty,
+     * however we can't start with empty outputs, thus we'll need to define and
+     * setup a default one.
+     */
+    ignore_value(virLogSetFilters(log_filters));
+    ignore_value(virLogSetOutputs(log_outputs));
+
+    /* If there are some environment variables defined, use those instead */
+    virLogSetFromEnv();
+
+    /*
+     * Command line override for --verbose
+     */
+    if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
+        virLogSetDefaultPriority(VIR_LOG_INFO);
+
+    /* Define the default output. This is only applied if there was no setting
+     * from either the config or the environment.
+     */
+    virLogSetDefaultOutput(daemon_name, godaemon, privileged);
+
+    if (virLogGetNbOutputs() == 0)
+        virLogSetOutputs(virLogGetDefaultOutput());
+}
+
+
+int
+virDaemonUnixSocketPaths(const char *sock_prefix,
+                         bool privileged,
+                         char *unix_sock_dir,
+                         char **sockfile,
+                         char **rosockfile,
+                         char **admsockfile)
+{
+    int ret = -1;
+    char *rundir = NULL;
+
+    if (unix_sock_dir) {
+        if (sockfile)
+            *sockfile = g_strdup_printf("%s/%s-sock", unix_sock_dir, sock_prefix);
+
+        if (privileged) {
+            if (rosockfile)
+                *rosockfile = g_strdup_printf("%s/%s-sock-ro",
+                                              unix_sock_dir, sock_prefix);
+            if (admsockfile)
+                *admsockfile = g_strdup_printf("%s/%s-admin-sock",
+                                               unix_sock_dir, sock_prefix);
+        }
+    } else {
+        if (privileged) {
+            if (sockfile)
+                *sockfile = g_strdup_printf("%s/libvirt/%s-sock",
+                                            RUNSTATEDIR, sock_prefix);
+            if (rosockfile)
+                *rosockfile = g_strdup_printf("%s/libvirt/%s-sock-ro",
+                                              RUNSTATEDIR, sock_prefix);
+            if (admsockfile)
+                *admsockfile = g_strdup_printf("%s/libvirt/%s-admin-sock",
+                                               RUNSTATEDIR, sock_prefix);
+        } else {
+            mode_t old_umask;
+
+            rundir = virGetUserRuntimeDirectory();
+
+            old_umask = umask(077);
+            if (virFileMakePath(rundir) < 0) {
+                umask(old_umask);
+                goto cleanup;
+            }
+            umask(old_umask);
+
+            if (sockfile)
+                *sockfile = g_strdup_printf("%s/%s-sock", rundir, sock_prefix);
+            if (admsockfile)
+                *admsockfile = g_strdup_printf("%s/%s-admin-sock", rundir, sock_prefix);
+        }
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(rundir);
+    return ret;
+}
diff --git a/src/util/virdaemon.h b/src/util/virdaemon.h
new file mode 100644 (file)
index 0000000..d032b8d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * virdaemon.h: shared daemon setup code
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "virenum.h"
+
+enum {
+    VIR_DAEMON_ERR_NONE = 0,
+    VIR_DAEMON_ERR_PIDFILE,
+    VIR_DAEMON_ERR_RUNDIR,
+    VIR_DAEMON_ERR_INIT,
+    VIR_DAEMON_ERR_SIGNAL,
+    VIR_DAEMON_ERR_PRIVS,
+    VIR_DAEMON_ERR_NETWORK,
+    VIR_DAEMON_ERR_CONFIG,
+    VIR_DAEMON_ERR_HOOKS,
+    VIR_DAEMON_ERR_REEXEC,
+    VIR_DAEMON_ERR_AUDIT,
+    VIR_DAEMON_ERR_DRIVER,
+
+    VIR_DAEMON_ERR_LAST
+};
+
+VIR_ENUM_DECL(virDaemonErr);
+VIR_ENUM_IMPL(virDaemonErr,
+              VIR_DAEMON_ERR_LAST,
+              "Initialization successful",
+              "Unable to obtain pidfile",
+              "Unable to create rundir",
+              "Unable to initialize libvirt",
+              "Unable to setup signal handlers",
+              "Unable to drop privileges",
+              "Unable to initialize network sockets",
+              "Unable to load configuration file",
+              "Unable to look for hook scripts",
+              "Unable to re-execute daemon",
+              "Unable to initialize audit system",
+              "Unable to initialize driver",
+);
+
+int virDaemonForkIntoBackground(const char *argv0);
+
+void virDaemonSetupLogging(const char *daemon_name,
+                           unsigned int log_level,
+                           char *log_filters,
+                           char *log_outputs,
+                           bool privileged,
+                           bool verbose,
+                           bool godaemon);
+
+int virDaemonUnixSocketPaths(const char *sock_prefix,
+                             bool privileged,
+                             char *unix_sock_dir,
+                             char **sockfile,
+                             char **rosockfile,
+                             char **adminSockfile);