[libvirt] [PATCH v4 7/9] util: add virCommandPassListenFDs() function

Martin Kletzander mkletzan at redhat.com
Thu Aug 14 12:08:58 UTC 2014


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 at 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 7516ed3..e09ddd5 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1146,6 +1146,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.4




More information about the libvir-list mailing list