mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-07-06 09:55:46 +00:00
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:
parent
241ac07124
commit
62f263a73e
@ -1146,6 +1146,7 @@ virCommandNewArgList;
|
|||||||
virCommandNewArgs;
|
virCommandNewArgs;
|
||||||
virCommandNonblockingFDs;
|
virCommandNonblockingFDs;
|
||||||
virCommandPassFD;
|
virCommandPassFD;
|
||||||
|
virCommandPassListenFDs;
|
||||||
virCommandRawStatus;
|
virCommandRawStatus;
|
||||||
virCommandRequireHandshake;
|
virCommandRequireHandshake;
|
||||||
virCommandRun;
|
virCommandRun;
|
||||||
|
@ -66,6 +66,7 @@ enum {
|
|||||||
VIR_EXEC_CLEAR_CAPS = (1 << 2),
|
VIR_EXEC_CLEAR_CAPS = (1 << 2),
|
||||||
VIR_EXEC_RUN_SYNC = (1 << 3),
|
VIR_EXEC_RUN_SYNC = (1 << 3),
|
||||||
VIR_EXEC_ASYNC_IO = (1 << 4),
|
VIR_EXEC_ASYNC_IO = (1 << 4),
|
||||||
|
VIR_EXEC_LISTEN_FDS = (1 << 5),
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _virCommandFD virCommandFD;
|
typedef struct _virCommandFD virCommandFD;
|
||||||
@ -200,6 +201,78 @@ virCommandFDSet(virCommandPtr cmd,
|
|||||||
return 0;
|
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
|
#ifndef WIN32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -678,6 +751,15 @@ virExec(virCommandPtr cmd)
|
|||||||
goto fork_error;
|
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 */
|
/* Close logging again to ensure no FDs leak to child */
|
||||||
virLogReset();
|
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:
|
* virCommandSetPidFile:
|
||||||
* @cmd: the command to modify
|
* @cmd: the command to modify
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* vircommand.h: Child command execution
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -60,6 +60,8 @@ void virCommandPassFD(virCommandPtr cmd,
|
|||||||
int fd,
|
int fd,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
|
|
||||||
|
void virCommandPassListenFDs(virCommandPtr cmd);
|
||||||
|
|
||||||
void virCommandSetPidFile(virCommandPtr cmd,
|
void virCommandSetPidFile(virCommandPtr cmd,
|
||||||
const char *pidfile) ATTRIBUTE_NONNULL(2);
|
const char *pidfile) ATTRIBUTE_NONNULL(2);
|
||||||
|
|
||||||
|
7
tests/commanddata/test24.log
Normal file
7
tests/commanddata/test24.log
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
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;
|
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)
|
static void virCommandThreadWorker(void *opaque)
|
||||||
{
|
{
|
||||||
virCommandTestDataPtr test = opaque;
|
virCommandTestDataPtr test = opaque;
|
||||||
@ -1181,6 +1236,7 @@ mymain(void)
|
|||||||
DO_TEST(test21);
|
DO_TEST(test21);
|
||||||
DO_TEST(test22);
|
DO_TEST(test22);
|
||||||
DO_TEST(test23);
|
DO_TEST(test23);
|
||||||
|
DO_TEST(test24);
|
||||||
|
|
||||||
virMutexLock(&test->lock);
|
virMutexLock(&test->lock);
|
||||||
if (test->running) {
|
if (test->running) {
|
||||||
|
Loading…
Reference in New Issue
Block a user