]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
util: add virCommandPassListenFDs() function
authorMartin Kletzander <mkletzan@redhat.com>
Tue, 15 Jul 2014 15:07:02 +0000 (17:07 +0200)
committerMartin Kletzander <mkletzan@redhat.com>
Fri, 22 Aug 2014 07:12:14 +0000 (09:12 +0200)
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>
src/libvirt_private.syms
src/util/vircommand.c
src/util/vircommand.h
tests/commanddata/test24.log [new file with mode: 0644]
tests/commandtest.c

index 7516ed3aa014970f2b5862ae7b6ecf66ee695bb8..e09ddd57cc3d55c80e0c2598b2850cb0d1d19133 100644 (file)
@@ -1146,6 +1146,7 @@ virCommandNewArgList;
 virCommandNewArgs;
 virCommandNonblockingFDs;
 virCommandPassFD;
+virCommandPassListenFDs;
 virCommandRawStatus;
 virCommandRequireHandshake;
 virCommandRun;
index e775ba65d855e415d7ea59bf4efb6a72315d995d..3b3e6f523f00b9f86999391dff68cc440d2b9ee0 100644 (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
index 8cdb31cf5fdebb323c5906be4f6e458406101e38..d3b286de835d300789e4f492a4c77d412267146a 100644 (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);
 
diff --git a/tests/commanddata/test24.log b/tests/commanddata/test24.log
new file mode 100644 (file)
index 0000000..8670952
--- /dev/null
@@ -0,0 +1,7 @@
+FD:0
+FD:1
+FD:2
+FD:3
+FD:4
+DAEMON:yes
+CWD:/
index ba823f793fb8fb2f617be6e1328d69ee6e6a5aaf..b3287faa6fd452d924b587679f96d2483e96ea93 100644 (file)
@@ -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) {