9119d9
From 31f55c6f1cbe6ed6ac9891ea47c3823ed18f1921 Mon Sep 17 00:00:00 2001
9119d9
Message-Id: <31f55c6f1cbe6ed6ac9891ea47c3823ed18f1921@dist-git>
9119d9
From: Martin Kletzander <mkletzan@redhat.com>
9119d9
Date: Wed, 17 Sep 2014 17:11:04 +0200
9119d9
Subject: [PATCH] rpc: make daemon spawning a bit more intelligent
9119d9
9119d9
This way it behaves more like the daemon itself does (acquiring a
9119d9
pidfile, deleting the socket before binding, etc.).
9119d9
9119d9
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=927369
9119d9
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1138604
9119d9
9119d9
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
9119d9
(cherry picked from commit bd9ad91a4036649645fffb1598213339263478de)
9119d9
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
9119d9
---
9119d9
 src/rpc/virnetsocket.c | 67 ++++++++++++++++++++++++++++++++++++++++++++------
9119d9
 1 file changed, 59 insertions(+), 8 deletions(-)
9119d9
9119d9
diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
9119d9
index 306c9ea..5feccf6 100644
9119d9
--- a/src/rpc/virnetsocket.c
9119d9
+++ b/src/rpc/virnetsocket.c
9119d9
@@ -51,9 +51,11 @@
9119d9
 #include "virlog.h"
9119d9
 #include "virfile.h"
9119d9
 #include "virthread.h"
9119d9
+#include "virpidfile.h"
9119d9
 #include "virprobe.h"
9119d9
 #include "virprocess.h"
9119d9
 #include "virstring.h"
9119d9
+#include "dirname.h"
9119d9
 #include "passfd.h"
9119d9
 
9119d9
 #if WITH_SSH2
9119d9
@@ -544,7 +546,10 @@ int virNetSocketNewConnectUNIX(const char *path,
9119d9
                                const char *binary,
9119d9
                                virNetSocketPtr *retsock)
9119d9
 {
9119d9
+    char *binname = NULL;
9119d9
+    char *pidpath = NULL;
9119d9
     int fd, passfd = -1;
9119d9
+    int pidfd = -1;
9119d9
     virSocketAddr localAddr;
9119d9
     virSocketAddr remoteAddr;
9119d9
 
9119d9
@@ -583,16 +588,46 @@ int virNetSocketNewConnectUNIX(const char *path,
9119d9
             goto error;
9119d9
         }
9119d9
 
9119d9
+        if (!(binname = last_component(binary)) || binname[0] == '\0') {
9119d9
+            virReportError(VIR_ERR_INTERNAL_ERROR,
9119d9
+                           _("Cannot determine basename for binary '%s'"),
9119d9
+                           binary);
9119d9
+            goto error;
9119d9
+        }
9119d9
+
9119d9
+        if (virPidFileConstructPath(false, NULL, binname, &pidpath) < 0)
9119d9
+            goto error;
9119d9
+
9119d9
+        pidfd = virPidFileAcquirePath(pidpath, false, getpid());
9119d9
+        if (pidfd < 0) {
9119d9
+            /*
9119d9
+             * This can happen in a very rare case of two clients spawning two
9119d9
+             * daemons at the same time, and the error in the logs that gets
9119d9
+             * reset here can be a clue to some future debugging.
9119d9
+             */
9119d9
+            virResetLastError();
9119d9
+            spawnDaemon = false;
9119d9
+            goto retry;
9119d9
+        }
9119d9
+
9119d9
         if ((passfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
9119d9
             virReportSystemError(errno, "%s", _("Failed to create socket"));
9119d9
             goto error;
9119d9
         }
9119d9
 
9119d9
         /*
9119d9
-         * We have to fork() here, because umask() is set
9119d9
-         * per-process, chmod() is racy and fchmod() has undefined
9119d9
-         * behaviour on sockets according to POSIX, so it doesn't
9119d9
-         * work outside Linux.
9119d9
+         * We already even acquired the pidfile, so no one else should be using
9119d9
+         * @path right now.  So we're OK to unlink it and paying attention to
9119d9
+         * the return value makes no real sense here.  Only if it's not an
9119d9
+         * abstract socket, of course.
9119d9
+         */
9119d9
+        if (path[0] != '@')
9119d9
+            unlink(path);
9119d9
+
9119d9
+        /*
9119d9
+         * We have to fork() here, because umask() is set per-process, chmod()
9119d9
+         * is racy and fchmod() has undefined behaviour on sockets according to
9119d9
+         * POSIX, so it doesn't work outside Linux.
9119d9
          */
9119d9
         if ((pid = virFork()) < 0)
9119d9
             goto error;
9119d9
@@ -610,12 +645,16 @@ int virNetSocketNewConnectUNIX(const char *path,
9119d9
 
9119d9
         if (status != EXIT_SUCCESS) {
9119d9
             /*
9119d9
-             * OK, so the subprocces failed to bind() the socket.  This may mean
9119d9
-             * that another daemon was starting at the same time and succeeded
9119d9
-             * with its bind().  So we'll try connecting again, but this time
9119d9
-             * without spawning the daemon.
9119d9
+             * OK, so the child failed to bind() the socket.  This may mean that
9119d9
+             * another daemon was starting at the same time and succeeded with
9119d9
+             * its bind() (even though it should not happen because we using a
9119d9
+             * pidfile for the race).  So we'll try connecting again, but this
9119d9
+             * time without spawning the daemon.
9119d9
              */
9119d9
             spawnDaemon = false;
9119d9
+            virPidFileDeletePath(pidpath);
9119d9
+            VIR_FORCE_CLOSE(pidfd);
9119d9
+            VIR_FORCE_CLOSE(passfd);
9119d9
             goto retry;
9119d9
         }
9119d9
 
9119d9
@@ -632,6 +671,12 @@ int virNetSocketNewConnectUNIX(const char *path,
9119d9
             goto error;
9119d9
         }
9119d9
 
9119d9
+        /*
9119d9
+         * Do we need to eliminate the super-rare race here any more?  It would
9119d9
+         * need incorporating the following VIR_FORCE_CLOSE() into a
9119d9
+         * virCommandHook inside a virNetSocketForkDaemon().
9119d9
+         */
9119d9
+        VIR_FORCE_CLOSE(pidfd);
9119d9
         if (virNetSocketForkDaemon(binary, passfd) < 0)
9119d9
             goto error;
9119d9
     }
9119d9
@@ -645,11 +690,17 @@ int virNetSocketNewConnectUNIX(const char *path,
9119d9
     if (!(*retsock = virNetSocketNew(&localAddr, &remoteAddr, true, fd, -1, 0)))
9119d9
         goto error;
9119d9
 
9119d9
+    VIR_FREE(pidpath);
9119d9
+
9119d9
     return 0;
9119d9
 
9119d9
  error:
9119d9
+    if (pidfd >= 0)
9119d9
+        virPidFileDeletePath(pidpath);
9119d9
+    VIR_FREE(pidpath);
9119d9
     VIR_FORCE_CLOSE(fd);
9119d9
     VIR_FORCE_CLOSE(passfd);
9119d9
+    VIR_FORCE_CLOSE(pidfd);
9119d9
     if (spawnDaemon)
9119d9
         unlink(path);
9119d9
     return -1;
9119d9
-- 
9119d9
2.1.0
9119d9