render / rpms / libvirt

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