qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] rework daemonizing logic in qemu-nbd


From: Michael Tokarev
Subject: [Qemu-devel] [PATCH] rework daemonizing logic in qemu-nbd
Date: Sat, 14 Jan 2012 16:39:28 +0400

qemu-nbd uses daemon(3) routine to daemonize, and while
at it, it uses several hacks to make daemon(3) to work
as intended.  Instead of all these hacks, implement
daemon(3) functionality (which is a very simple function)
directly but in a way which is much more suitable for the
use case. It lets us to remove several hacks completely,
and stop using daemon() which is marked as deprecated
on e.g. MacOS.  Some more hacks around daemon(3) will
be removed in subsequent series.

Signed-Off-By: Michael Tokarev <address@hidden>
---
 qemu-nbd.c |   83 ++++++++++++++++++++++++++++++++++++------------------------
 1 files changed, 50 insertions(+), 33 deletions(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index e76c782..6a84cde 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -32,6 +32,7 @@
 #include <signal.h>
 #include <libgen.h>
 #include <pthread.h>
+#include <sys/wait.h>
 
 #define SOCKET_PATH    "/var/lock/qemu-nbd-%s"
 
@@ -415,54 +416,58 @@ int main(int argc, char **argv)
     }
 
     if (device && !verbose) {
-        int stderr_fd[2];
+        int cpipe[2];
         pid_t pid;
-        int ret;
 
-        if (qemu_pipe(stderr_fd) == -1) {
+        if (qemu_pipe(cpipe) == -1) {
             err(EXIT_FAILURE, "Error setting up communication pipe");
         }
 
         /* Now daemonize, but keep a communication channel open to
-         * print errors and exit with the proper status code.
+         * pass errors.
          */
         pid = fork();
         if (pid == 0) {
-            close(stderr_fd[0]);
-            ret = qemu_daemon(0, 0);
-
-            /* Temporarily redirect stderr to the parent's pipe...  */
-            dup2(stderr_fd[1], STDERR_FILENO);
-            if (ret == -1) {
+            int nullfd = open("/dev/null", O_RDWR);
+            if (nullfd < 0 || setsid() < 0) {
                 err(EXIT_FAILURE, "Failed to daemonize");
             }
-
-            /* ... close the descriptor we inherited and go on.  */
-            close(stderr_fd[1]);
-        } else {
-            bool errors = false;
-            char *buf;
-
-            /* In the parent.  Print error messages from the child until
-             * it closes the pipe.
+            /* redirect stdin from /dev/null,
+             * stdout (temporarily) to the pipe to parent,
+             * and keep stderr for error report.
+             * When initializing is done, redirect stdout and stderr
+             * to /dev/null (stdin).
              */
-            close(stderr_fd[1]);
-            buf = g_malloc(1024);
-            while ((ret = read(stderr_fd[0], buf, 1024)) > 0) {
-                errors = true;
-                ret = qemu_write_full(STDERR_FILENO, buf, ret);
-                if (ret == -1) {
-                    exit(EXIT_FAILURE);
-                }
+            if (nullfd != STDIN_FILENO) {
+                dup2(nullfd, STDIN_FILENO);
+                close(nullfd);
             }
-            if (ret == -1) {
-                err(EXIT_FAILURE, "Cannot read from daemon");
+            close(cpipe[0]);
+            if (cpipe[1] != STDOUT_FILENO) {
+                dup2(cpipe[1], STDOUT_FILENO);
+                close(cpipe[1]);
             }
-
-            /* Usually the daemon should not print any message.
-             * Exit with zero status in that case.
+        } else if (pid < 0) {
+            err(EXIT_FAILURE, "Failed to daemonize");
+        } else {
+            close(cpipe[1]);
+            /* In parent, just a dummy read till the pipe gets closed.
+             * When it does, check process exit status using waitpid().
              */
-            exit(errors);
+            ret = read(cpipe[0], &ret, sizeof(ret));
+            pid = waitpid(pid, &ret, WNOHANG);
+            if (pid < 0) {
+                err(EXIT_FAILURE, "Cannot read from daemon");
+            }
+            return
+              /* waitpid(pid, WNOHANG) returns 0 if the process
+               * in question did not change state. In this case
+               * we assume our child successfully initialized and
+               * is now running, so exit succcessfully here.
+               */
+              pid == 0 ? 0 :
+              /* else our child exited, so return its exit status */
+              WIFEXITED(ret) ? WEXITSTATUS(ret) : 1;
         }
     }
 
@@ -527,6 +532,18 @@ int main(int argc, char **argv)
     qemu_set_fd_handler2(sockfd, nbd_can_accept, nbd_accept, NULL,
                          (void *)(uintptr_t)sockfd);
 
+    /* now complete the daemonizing procedure.
+     */
+    if (device && !verbose) {
+        if (chdir("/") < 0) {
+            err(EXIT_FAILURE, "unable to chdir to /");
+        }
+        /* this redirects stderr to /dev/null */
+        dup2(STDIN_FILENO, STDERR_FILENO);
+        /* this redirects stdout to /dev/null too, and closes parent pipe */
+        dup2(STDIN_FILENO, STDOUT_FILENO);
+    }
+
     do {
         main_loop_wait(false);
     } while (!sigterm_reported && (persistent || !nbd_started || nb_fds > 0));
-- 
1.7.2.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]