rpc: make daemon spawning a bit more intelligent

This way it behaves more like the daemon itself does (acquiring a
pidfile, deleting the socket before binding, etc.).

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=927369
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1138604

Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
(cherry picked from commit bd9ad91a4036649645fffb1598213339263478de)
This commit is contained in:
Martin Kletzander 2014-09-07 20:41:11 +02:00 committed by Eric Blake
parent 818c5c6b83
commit 9281ca9901

View File

@ -51,9 +51,11 @@
#include "virlog.h" #include "virlog.h"
#include "virfile.h" #include "virfile.h"
#include "virthread.h" #include "virthread.h"
#include "virpidfile.h"
#include "virprobe.h" #include "virprobe.h"
#include "virprocess.h" #include "virprocess.h"
#include "virstring.h" #include "virstring.h"
#include "dirname.h"
#include "passfd.h" #include "passfd.h"
#if WITH_SSH2 #if WITH_SSH2
@ -544,7 +546,10 @@ int virNetSocketNewConnectUNIX(const char *path,
const char *binary, const char *binary,
virNetSocketPtr *retsock) virNetSocketPtr *retsock)
{ {
char *binname = NULL;
char *pidpath = NULL;
int fd, passfd = -1; int fd, passfd = -1;
int pidfd = -1;
virSocketAddr localAddr; virSocketAddr localAddr;
virSocketAddr remoteAddr; virSocketAddr remoteAddr;
@ -583,16 +588,46 @@ int virNetSocketNewConnectUNIX(const char *path,
goto error; goto error;
} }
if (!(binname = last_component(binary)) || binname[0] == '\0') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Cannot determine basename for binary '%s'"),
binary);
goto error;
}
if (virPidFileConstructPath(false, NULL, binname, &pidpath) < 0)
goto error;
pidfd = virPidFileAcquirePath(pidpath, false, getpid());
if (pidfd < 0) {
/*
* This can happen in a very rare case of two clients spawning two
* daemons at the same time, and the error in the logs that gets
* reset here can be a clue to some future debugging.
*/
virResetLastError();
spawnDaemon = false;
goto retry;
}
if ((passfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { if ((passfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
virReportSystemError(errno, "%s", _("Failed to create socket")); virReportSystemError(errno, "%s", _("Failed to create socket"));
goto error; goto error;
} }
/* /*
* We have to fork() here, because umask() is set * We already even acquired the pidfile, so no one else should be using
* per-process, chmod() is racy and fchmod() has undefined * @path right now. So we're OK to unlink it and paying attention to
* behaviour on sockets according to POSIX, so it doesn't * the return value makes no real sense here. Only if it's not an
* work outside Linux. * abstract socket, of course.
*/
if (path[0] != '@')
unlink(path);
/*
* We have to fork() here, because umask() is set per-process, chmod()
* is racy and fchmod() has undefined behaviour on sockets according to
* POSIX, so it doesn't work outside Linux.
*/ */
if ((pid = virFork()) < 0) if ((pid = virFork()) < 0)
goto error; goto error;
@ -610,12 +645,16 @@ int virNetSocketNewConnectUNIX(const char *path,
if (status != EXIT_SUCCESS) { if (status != EXIT_SUCCESS) {
/* /*
* OK, so the subprocces failed to bind() the socket. This may mean * OK, so the child failed to bind() the socket. This may mean that
* that another daemon was starting at the same time and succeeded * another daemon was starting at the same time and succeeded with
* with its bind(). So we'll try connecting again, but this time * its bind() (even though it should not happen because we using a
* without spawning the daemon. * pidfile for the race). So we'll try connecting again, but this
* time without spawning the daemon.
*/ */
spawnDaemon = false; spawnDaemon = false;
virPidFileDeletePath(pidpath);
VIR_FORCE_CLOSE(pidfd);
VIR_FORCE_CLOSE(passfd);
goto retry; goto retry;
} }
@ -632,6 +671,12 @@ int virNetSocketNewConnectUNIX(const char *path,
goto error; goto error;
} }
/*
* Do we need to eliminate the super-rare race here any more? It would
* need incorporating the following VIR_FORCE_CLOSE() into a
* virCommandHook inside a virNetSocketForkDaemon().
*/
VIR_FORCE_CLOSE(pidfd);
if (virNetSocketForkDaemon(binary, passfd) < 0) if (virNetSocketForkDaemon(binary, passfd) < 0)
goto error; goto error;
} }
@ -645,11 +690,17 @@ int virNetSocketNewConnectUNIX(const char *path,
if (!(*retsock = virNetSocketNew(&localAddr, &remoteAddr, true, fd, -1, 0))) if (!(*retsock = virNetSocketNew(&localAddr, &remoteAddr, true, fd, -1, 0)))
goto error; goto error;
VIR_FREE(pidpath);
return 0; return 0;
error: error:
if (pidfd >= 0)
virPidFileDeletePath(pidpath);
VIR_FREE(pidpath);
VIR_FORCE_CLOSE(fd); VIR_FORCE_CLOSE(fd);
VIR_FORCE_CLOSE(passfd); VIR_FORCE_CLOSE(passfd);
VIR_FORCE_CLOSE(pidfd);
if (spawnDaemon) if (spawnDaemon)
unlink(path); unlink(path);
return -1; return -1;