Fix: don't use daemon() to daemonize
authorDavid Goulet <dgoulet@efficios.com>
Tue, 12 Nov 2013 17:34:14 +0000 (12:34 -0500)
committerDavid Goulet <dgoulet@efficios.com>
Tue, 12 Nov 2013 19:52:03 +0000 (14:52 -0500)
This is done because now we can control when the parent should return
from the fork(). With this patch, it only returns when the session
daemon is ready to operate meaning it can receive commands.

The daemonize process for the child mimicks daemon(3) as in it sets the
setsid, chdir to / and dup 0, 1 and 2 into /dev/null.

Acked-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: David Goulet <dgoulet@efficios.com>
src/bin/lttng-sessiond/main.c

index 963f75e49ada100fc3af7944ba7bb06d4644203d..bedc61c110654744223f25de55f1713c97be8abd 100644 (file)
@@ -20,6 +20,7 @@
 #include <getopt.h>
 #include <grp.h>
 #include <limits.h>
+#include <paths.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdio.h>
@@ -76,8 +77,12 @@ static int opt_daemon;
 static int opt_no_kernel;
 static int is_root;                    /* Set to 1 if the daemon is running as root */
 static pid_t ppid;          /* Parent PID for --sig-parent option */
+static pid_t child_ppid;    /* Internal parent PID use with daemonize. */
 static char *rundir;
 
+/* Set to 1 when a SIGUSR1 signal is received. */
+static int recv_child_signal;
+
 /*
  * Consumer daemon specific control data. Every value not initialized here is
  * set to 0 by the static definition.
@@ -3696,11 +3701,17 @@ static void *thread_manage_clients(void *data)
 
        /*
         * Notify parent pid that we are ready to accept command for client side.
+        * This ppid is the one from the external process that spawned us.
         */
        if (opt_sig_parent) {
                kill(ppid, SIGUSR1);
        }
 
+       /* Notify the parent of the fork() process that we are ready. */
+       if (opt_daemon) {
+               kill(child_ppid, SIGUSR1);
+       }
+
        if (testpoint(thread_manage_clients_before_loop)) {
                goto error;
        }
@@ -4342,6 +4353,9 @@ static void sighandler(int sig)
                DBG("SIGTERM caught");
                stop_threads();
                break;
+       case SIGUSR1:
+               CMM_STORE_SHARED(recv_child_signal, 1);
+               break;
        default:
                break;
        }
@@ -4380,7 +4394,12 @@ static int set_signal_handler(void)
                return ret;
        }
 
-       DBG("Signal handler set for SIGTERM, SIGPIPE and SIGINT");
+       if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) {
+               PERROR("sigaction");
+               return ret;
+       }
+
+       DBG("Signal handler set for SIGTERM, SIGUSR1, SIGPIPE and SIGINT");
 
        return ret;
 }
@@ -4464,6 +4483,95 @@ error:
        return;
 }
 
+/*
+ * Daemonize this process by forking and making the parent wait for the child
+ * to signal it indicating readiness. Once received, the parent successfully
+ * quits.
+ *
+ * The child process undergoes the same action that daemon(3) does meaning
+ * setsid, chdir, and dup /dev/null into 0, 1 and 2.
+ *
+ * Return 0 on success else -1 on error.
+ */
+static int daemonize(void)
+{
+       int ret;
+       pid_t pid;
+
+       /* Get parent pid of this process. */
+       child_ppid = getppid();
+
+       pid = fork();
+       if (pid < 0) {
+               PERROR("fork");
+               goto error;
+       } else if (pid == 0) {
+               int fd;
+               pid_t sid;
+
+               /* Child */
+
+               /*
+                * Get the newly created parent pid so we can signal that process when
+                * we are ready to operate.
+                */
+               child_ppid = getppid();
+
+               sid = setsid();
+               if (sid < 0) {
+                       PERROR("setsid");
+                       goto error;
+               }
+
+               /* Try to change directory to /. If we can't well at least notify. */
+               ret = chdir("/");
+               if (ret < 0) {
+                       PERROR("chdir");
+               }
+
+               fd = open(_PATH_DEVNULL, O_RDWR, 0);
+               if (fd < 0) {
+                       PERROR("open %s", _PATH_DEVNULL);
+                       /* Let 0, 1 and 2 open since we can't bind them to /dev/null. */
+               } else {
+                       (void) dup2(fd, STDIN_FILENO);
+                       (void) dup2(fd, STDOUT_FILENO);
+                       (void) dup2(fd, STDERR_FILENO);
+                       if (fd > 2) {
+                               ret = close(fd);
+                               if (ret < 0) {
+                                       PERROR("close");
+                               }
+                       }
+               }
+               goto end;
+       } else {
+               /* Parent */
+
+               /*
+                * Waiting for child to notify this parent that it can exit. Note that
+                * sleep() is interrupted before the 1 second delay as soon as the
+                * signal is received, so it will not cause visible delay for the
+                * user.
+                */
+               while (!CMM_LOAD_SHARED(recv_child_signal)) {
+                       sleep(1);
+               }
+
+               /*
+                * From this point on, the parent can exit and the child is now an
+                * operationnal session daemon ready to serve clients and applications.
+                */
+               exit(EXIT_SUCCESS);
+       }
+
+end:
+       return 0;
+
+error:
+       return -1;
+}
+
 /*
  * main
  */
@@ -4477,6 +4585,10 @@ int main(int argc, char **argv)
 
        rcu_register_thread();
 
+       if ((ret = set_signal_handler()) < 0) {
+               goto error;
+       }
+
        setup_consumerd_path();
 
        page_size = sysconf(_SC_PAGESIZE);
@@ -4496,20 +4608,15 @@ int main(int argc, char **argv)
        if (opt_daemon) {
                int i;
 
-               /*
-                * fork
-                * child: setsid, close FD 0, 1, 2, chdir /
-                * parent: exit (if fork is successful)
-                */
-               ret = daemon(0, 0);
+               ret = daemonize();
                if (ret < 0) {
-                       PERROR("daemon");
                        goto error;
                }
+
                /*
-                * We are in the child. Make sure all other file
-                * descriptors are closed, in case we are called with
-                * more opened file descriptors than the standard ones.
+                * We are in the child. Make sure all other file descriptors are
+                * closed, in case we are called with more opened file descriptors than
+                * the standard ones.
                 */
                for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
                        (void) close(i);
@@ -4702,10 +4809,6 @@ int main(int argc, char **argv)
                goto exit;
        }
 
-       if ((ret = set_signal_handler()) < 0) {
-               goto exit;
-       }
-
        /* Setup the needed unix socket */
        if ((ret = init_daemon_socket()) < 0) {
                goto exit;
This page took 0.032108 seconds and 4 git commands to generate.