提交 62f263a7 编写于 作者: M Martin Kletzander

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: NMartin Kletzander <mkletzan@redhat.com>
上级 241ac071
......@@ -1146,6 +1146,7 @@ virCommandNewArgList;
virCommandNewArgs;
virCommandNonblockingFDs;
virCommandPassFD;
virCommandPassListenFDs;
virCommandRawStatus;
virCommandRequireHandshake;
virCommandRun;
......
......@@ -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
......
/*
* 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);
......
FD:0
FD:1
FD:2
FD:3
FD:4
DAEMON:yes
CWD:/
......@@ -1032,6 +1032,61 @@ test23(const void *unused ATTRIBUTE_UNUSED)
return ret;
}
static int test24(const void *unused ATTRIBUTE_UNUSED)
{
char *pidfile = virPidFileBuildPath(abs_builddir, "commandhelper");
char *prefix = NULL;
int newfd1 = dup(STDERR_FILENO);
int newfd2 = dup(STDERR_FILENO);
int newfd3 = dup(STDERR_FILENO);
int ret = -1;
pid_t pid;
virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper");
if (!pidfile)
goto cleanup;
if (VIR_CLOSE(newfd1) < 0)
printf("Cannot close fd %d\n", newfd1);
virCommandSetPidFile(cmd, pidfile);
virCommandDaemonize(cmd);
virCommandPassFD(cmd, newfd2, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
virCommandPassFD(cmd, newfd3, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
virCommandPassListenFDs(cmd);
if (virCommandRun(cmd, NULL) < 0) {
virErrorPtr err = virGetLastError();
printf("Cannot run child %s\n", err->message);
goto cleanup;
}
if (virPidFileRead(abs_builddir, "commandhelper", &pid) < 0) {
printf("cannot read pidfile\n");
goto cleanup;
}
if (virAsprintf(&prefix,
"ENV:LISTEN_FDS=2\nENV:LISTEN_PID=%u\n",
pid) < 0)
goto cleanup;
while (kill(pid, 0) != -1)
usleep(100*1000);
ret = checkoutput("test24", prefix);
cleanup:
if (pidfile)
unlink(pidfile);
VIR_FREE(pidfile);
virCommandFree(cmd);
/* coverity[double_close] */
VIR_FORCE_CLOSE(newfd2);
VIR_FORCE_CLOSE(newfd3);
return ret;
}
static void virCommandThreadWorker(void *opaque)
{
virCommandTestDataPtr test = opaque;
......@@ -1181,6 +1236,7 @@ mymain(void)
DO_TEST(test21);
DO_TEST(test22);
DO_TEST(test23);
DO_TEST(test24);
virMutexLock(&test->lock);
if (test->running) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册