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;
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
/**
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();
}
}
+/**
+ * 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
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;
DO_TEST(test21);
DO_TEST(test22);
DO_TEST(test23);
+ DO_TEST(test24);
virMutexLock(&test->lock);
if (test->running) {