mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-03 03:25:20 +00:00
rpc: pass listen FD to the daemon being started
This eliminates the need for active waiting. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=927369 Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
This commit is contained in:
parent
62f263a73e
commit
1b807f92db
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* virnetsocket.c: generic network socket handling
|
* virnetsocket.c: generic network socket handling
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2013 Red Hat, Inc.
|
* Copyright (C) 2006-2014 Red Hat, Inc.
|
||||||
* Copyright (C) 2006 Daniel P. Berrange
|
* Copyright (C) 2006 Daniel P. Berrange
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
@ -121,7 +121,7 @@ VIR_ONCE_GLOBAL_INIT(virNetSocket)
|
|||||||
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
static int virNetSocketForkDaemon(const char *binary)
|
static int virNetSocketForkDaemon(const char *binary, int passfd)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
virCommandPtr cmd = virCommandNewArgList(binary,
|
virCommandPtr cmd = virCommandNewArgList(binary,
|
||||||
@ -134,6 +134,10 @@ static int virNetSocketForkDaemon(const char *binary)
|
|||||||
virCommandAddEnvPassBlockSUID(cmd, "XDG_RUNTIME_DIR", NULL);
|
virCommandAddEnvPassBlockSUID(cmd, "XDG_RUNTIME_DIR", NULL);
|
||||||
virCommandClearCaps(cmd);
|
virCommandClearCaps(cmd);
|
||||||
virCommandDaemonize(cmd);
|
virCommandDaemonize(cmd);
|
||||||
|
if (passfd) {
|
||||||
|
virCommandPassFD(cmd, passfd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
|
||||||
|
virCommandPassListenFDs(cmd);
|
||||||
|
}
|
||||||
ret = virCommandRun(cmd, NULL);
|
ret = virCommandRun(cmd, NULL);
|
||||||
virCommandFree(cmd);
|
virCommandFree(cmd);
|
||||||
return ret;
|
return ret;
|
||||||
@ -540,10 +544,10 @@ int virNetSocketNewConnectUNIX(const char *path,
|
|||||||
const char *binary,
|
const char *binary,
|
||||||
virNetSocketPtr *retsock)
|
virNetSocketPtr *retsock)
|
||||||
{
|
{
|
||||||
|
char *buf = NULL;
|
||||||
|
int fd, passfd;
|
||||||
virSocketAddr localAddr;
|
virSocketAddr localAddr;
|
||||||
virSocketAddr remoteAddr;
|
virSocketAddr remoteAddr;
|
||||||
int fd;
|
|
||||||
int retries = 0;
|
|
||||||
|
|
||||||
memset(&localAddr, 0, sizeof(localAddr));
|
memset(&localAddr, 0, sizeof(localAddr));
|
||||||
memset(&remoteAddr, 0, sizeof(remoteAddr));
|
memset(&remoteAddr, 0, sizeof(remoteAddr));
|
||||||
@ -570,25 +574,65 @@ int virNetSocketNewConnectUNIX(const char *path,
|
|||||||
remoteAddr.data.un.sun_path[0] = '\0';
|
remoteAddr.data.un.sun_path[0] = '\0';
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
if (connect(fd, &remoteAddr.data.sa, remoteAddr.len) < 0) {
|
if (connect(fd, &remoteAddr.data.sa, remoteAddr.len) < 0 && !spawnDaemon) {
|
||||||
if ((errno == ECONNREFUSED ||
|
virReportSystemError(errno, _("Failed to connect socket to '%s'"),
|
||||||
errno == ENOENT) &&
|
path);
|
||||||
spawnDaemon && retries < 20) {
|
goto error;
|
||||||
VIR_DEBUG("Connection refused for %s, trying to spawn %s",
|
} else if (spawnDaemon) {
|
||||||
path, binary);
|
int status = 0;
|
||||||
if (retries == 0 &&
|
pid_t pid = 0;
|
||||||
virNetSocketForkDaemon(binary) < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
retries++;
|
if ((passfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||||
usleep(1000 * 100 * retries);
|
virReportSystemError(errno, "%s", _("Failed to create socket"));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
umask(0077);
|
||||||
|
if (bind(passfd, &remoteAddr.data.sa, remoteAddr.len) < 0)
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virProcessWait(pid, &status, false) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (status != EXIT_SUCCESS) {
|
||||||
|
/*
|
||||||
|
* OK, so the subprocces failed to bind() the socket. This may mean
|
||||||
|
* that another daemon was starting at the same time and succeeded
|
||||||
|
* with its bind(). So we'll try connecting again, but this time
|
||||||
|
* without spawning the daemon.
|
||||||
|
*/
|
||||||
|
spawnDaemon = false;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
virReportSystemError(errno,
|
if (listen(passfd, 0) < 0) {
|
||||||
_("Failed to connect socket to '%s'"),
|
virReportSystemError(errno, "%s",
|
||||||
path);
|
_("Failed to listen on socket that's about "
|
||||||
goto error;
|
"to be passed to the daemon"));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(fd, &remoteAddr.data.sa, remoteAddr.len) < 0) {
|
||||||
|
virReportSystemError(errno, _("Failed to connect socket to '%s'"),
|
||||||
|
path);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virNetSocketForkDaemon(binary, passfd) < 0)
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
localAddr.len = sizeof(localAddr.data);
|
localAddr.len = sizeof(localAddr.data);
|
||||||
@ -603,7 +647,10 @@ int virNetSocketNewConnectUNIX(const char *path,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
VIR_FREE(buf);
|
||||||
VIR_FORCE_CLOSE(fd);
|
VIR_FORCE_CLOSE(fd);
|
||||||
|
if (spawnDaemon)
|
||||||
|
unlink(path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
Loading…
Reference in New Issue
Block a user