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(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/util/vircommand.c | 99 ++++++++++++++++++++++++++++++++++++++++++++
src/util/vircommand.h | 4 +-
tests/commanddata/test24.log | 7 ++++
tests/commandtest.c | 56 +++++++++++++++++++++++++
5 files changed, 166 insertions(+), 1 deletion(-)
create mode 100644 tests/commanddata/test24.log
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 138a9fa..3332952 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1165,6 +1165,7 @@ virCommandNewArgList;
virCommandNewArgs;
virCommandNonblockingFDs;
virCommandPassFD;
+virCommandPassListenFDs;
virCommandRawStatus;
virCommandRequireHandshake;
virCommandRun;
diff --git a/src/util/vircommand.c b/src/util/vircommand.c
index e775ba6..3b3e6f5 100644
--- a/src/util/vircommand.c
+++ b/src/util/vircommand.c
@@ -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();
@@ -919,6 +1001,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
* @pidfile: filename to use
diff --git a/src/util/vircommand.h b/src/util/vircommand.h
index 8cdb31c..d3b286d 100644
--- a/src/util/vircommand.h
+++ b/src/util/vircommand.h
@@ -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);
diff --git a/tests/commanddata/test24.log b/tests/commanddata/test24.log
new file mode 100644
index 0000000..8670952
--- /dev/null
+++ b/tests/commanddata/test24.log
@@ -0,0 +1,7 @@
+FD:0
+FD:1
+FD:2
+FD:3
+FD:4
+DAEMON:yes
+CWD:/
diff --git a/tests/commandtest.c b/tests/commandtest.c
index ba823f7..b3287fa 100644
--- a/tests/commandtest.c
+++ b/tests/commandtest.c
@@ -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) {
--
2.0.0