Source-Changes-HG archive

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

[src/trunk]: src/usr.sbin/nfsd When daemonizing, delay the parent exit until ...



details:   https://anonhg.NetBSD.org/src/rev/c07da228ac13
branches:  trunk
changeset: 328199:c07da228ac13
user:      gson <gson%NetBSD.org@localhost>
date:      Sat Mar 29 13:50:53 2014 +0000

description:
When daemonizing, delay the parent exit until the daemon is
ready to provide service.  Fixes one of the race conditions
of PR misc/48282.

diffstat:

 usr.sbin/nfsd/nfsd.c |  138 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 131 insertions(+), 7 deletions(-)

diffs (187 lines):

diff -r a3457fbabb71 -r c07da228ac13 usr.sbin/nfsd/nfsd.c
--- a/usr.sbin/nfsd/nfsd.c      Sat Mar 29 12:55:11 2014 +0000
+++ b/usr.sbin/nfsd/nfsd.c      Sat Mar 29 13:50:53 2014 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nfsd.c,v 1.61 2012/08/15 00:16:06 joerg Exp $  */
+/*     $NetBSD: nfsd.c,v 1.62 2014/03/29 13:50:53 gson Exp $   */
 
 /*
  * Copyright (c) 1989, 1993, 1994
@@ -42,7 +42,7 @@
 #if 0
 static char sccsid[] = "@(#)nfsd.c     8.9 (Berkeley) 3/29/95";
 #else
-__RCSID("$NetBSD: nfsd.c,v 1.61 2012/08/15 00:16:06 joerg Exp $");
+__RCSID("$NetBSD: nfsd.c,v 1.62 2014/03/29 13:50:53 gson Exp $");
 #endif
 #endif /* not lint */
 
@@ -69,6 +69,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
+#include <paths.h>
 #include <pwd.h>
 #include <pthread.h>
 #include <signal.h>
@@ -249,6 +250,124 @@
 }
 
 /*
+ * The functions daemon2_fork() and daemon2_detach() below provide
+ * functionality similar to daemon(3) but split into two phases.
+ * daemon2_fork() is called early, before creating resources that
+ * cannot be inherited across a fork, such as threads or kqueues.
+ * When the daemon is ready to provide service, daemon2_detach()
+ * is called to complete the daemonization and signal the parent
+ * process to exit.
+ *
+ * These functions could potentially be moved to a library and 
+ * shared by other daemons.
+ *
+ * The return value from daemon2_fork() is a file descriptor to
+ * be passed as the first argument to daemon2_detach().
+ */
+
+static int
+daemon2_fork(void)
+{
+        int i;
+        int fd;
+        int r;
+        pid_t pid;
+        int detach_msg_pipe[2];
+
+        /*
+         * Set up a pipe for singalling the parent, making sure the
+         * write end does not get allocated one of the file
+         * descriptors that may be closed in daemon2_detach().  The
+         * read end does not need such protection.
+         */
+        for (i = 0; i < 3; i++) {
+                r = pipe2(detach_msg_pipe, O_CLOEXEC|O_NOSIGPIPE);
+                if (r < 0)
+                        return -1;
+                if (detach_msg_pipe[1] <= STDERR_FILENO &&
+                    (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+                        (void)dup2(fd, detach_msg_pipe[0]);
+                        (void)dup2(fd, detach_msg_pipe[1]);
+                        if (fd > STDERR_FILENO)
+                                (void)close(fd);
+                        continue;
+                }
+                break;
+        }
+
+        pid = fork();
+        switch (pid) {
+        case -1:
+                return -1;
+        case 0:
+                /* child */
+                (void)close(detach_msg_pipe[0]);
+                return detach_msg_pipe[1];
+        default:
+                break;
+       }
+
+       /* Parent */
+       (void)close(detach_msg_pipe[1]);
+
+       for (;;) {
+               ssize_t nread;
+               char dummy;
+               nread = read(detach_msg_pipe[0], &dummy, 1);
+               if (nread < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       _exit(1);
+               } else if (nread == 0) {
+                       _exit(1);
+               } else { /* nread > 0 */
+                       _exit(0);
+               }
+       }
+}
+
+static int
+daemon2_detach(int parentfd, int nochdir, int noclose)
+{
+       int fd;
+
+       if (setsid() == -1)
+               return -1;
+
+       if (!nochdir)
+               (void)chdir("/");
+
+       if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+               (void)dup2(fd, STDIN_FILENO);
+               (void)dup2(fd, STDOUT_FILENO);
+               (void)dup2(fd, STDERR_FILENO);
+               if (fd > STDERR_FILENO)
+                       (void)close(fd);
+       }
+
+       while (1) {
+               ssize_t r = write(parentfd, "", 1);
+               if (r < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       else if (errno == EPIPE)
+                               break;
+                       else
+                               return -1;
+               } else if (r == 0) {
+                       /* Should not happen */
+                       return -1;
+               } else {
+                       break;
+               }
+       }
+
+       (void)close(parentfd);
+
+       return 0;
+}
+
+/*
  * Nfs server daemon mostly just a user context for nfssvc()
  *
  * 1 - do file descriptor and signal cleanup
@@ -279,6 +398,7 @@
        int tcpflag, udpflag;
        int ip6flag, ip4flag;
        int s, compat;
+       int parent_fd = -1;
 
 #define        DEFNFSDCNT       4
        nfsdcnt = DEFNFSDCNT;
@@ -348,11 +468,7 @@
        }
 
        if (debug == 0) {
-               daemon(0, 0);
-               (void)signal(SIGHUP, SIG_IGN);
-               (void)signal(SIGINT, SIG_IGN);
-               (void)signal(SIGQUIT, SIG_IGN);
-               (void)signal(SIGSYS, nonfs);
+               parent_fd = daemon2_fork();
        }
 
        openlog("nfsd", LOG_PID, LOG_DAEMON);
@@ -401,6 +517,14 @@
 
        pthread_setname_np(pthread_self(), "master", NULL);
 
+       if (debug == 0) {
+               daemon2_detach(parent_fd, 0, 0);
+               (void)signal(SIGHUP, SIG_IGN);
+               (void)signal(SIGINT, SIG_IGN);
+               (void)signal(SIGQUIT, SIG_IGN);
+               (void)signal(SIGSYS, nonfs);
+       }
+
        /*
         * Loop forever accepting connections and passing the sockets
         * into the kernel for the mounts.



Home | Main Index | Thread Index | Old Index