util: add virCommandPassListenFDs() function

That sets a new flag, but that flag does mean the child will get
LISTEN_FDS and LISTEN_PID environment variables properly set and
passed FDs reordered so that it corresponds with LISTEN_FDS (they must
start right after STDERR_FILENO).

Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
This commit is contained in:
Martin Kletzander
2014-07-15 17:07:02 +02:00
parent 241ac07124
commit 62f263a73e
5 changed files with 166 additions and 1 deletions

View File

@@ -1146,6 +1146,7 @@ virCommandNewArgList;
virCommandNewArgs;
virCommandNonblockingFDs;
virCommandPassFD;
virCommandPassListenFDs;
virCommandRawStatus;
virCommandRequireHandshake;
virCommandRun;

View File

@@ -66,6 +66,7 @@ enum {
VIR_EXEC_CLEAR_CAPS = (1 << 2),
VIR_EXEC_RUN_SYNC = (1 << 3),
VIR_EXEC_ASYNC_IO = (1 << 4),
VIR_EXEC_LISTEN_FDS = (1 << 5),
};
typedef struct _virCommandFD virCommandFD;
@@ -200,6 +201,78 @@ virCommandFDSet(virCommandPtr cmd,
return 0;
}
static void
virCommandReorderFDs(virCommandPtr cmd)
{
int maxfd = 0;
int openmax = 0;
size_t i = 0;
if (!cmd || cmd->has_error || !cmd->npassfd)
return;
for (i = 0; i < cmd->npassfd; i++)
maxfd = MAX(cmd->passfd[i].fd, maxfd);
openmax = sysconf(_SC_OPEN_MAX);
if (openmax < 0 ||
maxfd + cmd->npassfd > openmax)
goto error;
/*
* Simple two-pass sort, nothing fancy. This is not designed for
* anything else than passing around 2 FDs into the child.
*
* So first dup2() them somewhere else.
*/
for (i = 0; i < cmd->npassfd; i++) {
int newfd = maxfd + i + 1;
int oldfd = cmd->passfd[i].fd;
if (dup2(oldfd, newfd) != newfd) {
virReportSystemError(errno,
_("Cannot dup2() fd %d before "
"passing it to the child"),
oldfd);
goto error;
}
VIR_FORCE_CLOSE(cmd->passfd[i].fd);
}
VIR_DEBUG("First reorder pass done");
/*
* And then dup2() them in orderly manner.
*/
for (i = 0; i < cmd->npassfd; i++) {
int newfd = STDERR_FILENO + i + 1;
int oldfd = maxfd + i + 1;
if (dup2(oldfd, newfd) != newfd) {
virReportSystemError(errno,
_("Cannot dup2() fd %d before "
"passing it to the child"),
oldfd);
goto error;
}
if (virSetInherit(newfd, true) < 0) {
virReportSystemError(errno,
_("Cannot set O_CLOEXEC on fd %d before "
"passing it to the child"),
newfd);
goto error;
}
VIR_FORCE_CLOSE(oldfd);
cmd->passfd[i].fd = newfd;
}
VIR_DEBUG("Second reorder pass done");
return;
error:
cmd->has_error = -1;
return;
}
#ifndef WIN32
/**
@@ -678,6 +751,15 @@ virExec(virCommandPtr cmd)
goto fork_error;
}
if (cmd->flags & VIR_EXEC_LISTEN_FDS) {
virCommandReorderFDs(cmd);
virCommandAddEnvFormat(cmd, "LISTEN_PID=%u", getpid());
virCommandAddEnvFormat(cmd, "LISTEN_FDS=%zu", cmd->npassfd);
if (cmd->has_error)
goto fork_error;
}
/* Close logging again to ensure no FDs leak to child */
virLogReset();
@@ -918,6 +1000,23 @@ virCommandPassFD(virCommandPtr cmd, int fd, unsigned int flags)
}
}
/**
* virCommandPassListenFDs:
* @cmd: the command to modify
*
* Pass LISTEN_FDS and LISTEN_PID environment variables into the
* child. LISTEN_PID has the value of the child's PID and LISTEN_FDS
* is a number of passed file descriptors starting from 3.
*/
void
virCommandPassListenFDs(virCommandPtr cmd)
{
if (!cmd || cmd->has_error)
return;
cmd->flags |= VIR_EXEC_LISTEN_FDS;
}
/**
* virCommandSetPidFile:
* @cmd: the command to modify

View File

@@ -1,7 +1,7 @@
/*
* vircommand.h: Child command execution
*
* Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -60,6 +60,8 @@ void virCommandPassFD(virCommandPtr cmd,
int fd,
unsigned int flags);
void virCommandPassListenFDs(virCommandPtr cmd);
void virCommandSetPidFile(virCommandPtr cmd,
const char *pidfile) ATTRIBUTE_NONNULL(2);