Allow the parent process to perform a bi-directional handshake with the child process during fork/exec. The child process will fork and do its initial setup. Immediately prior to the exec(), it will stop & wait for a handshake from the parent process. The parent process will spawn the child and wait until the child reaches the handshake point. It will do whatever extra setup work is required, before signalling the child to continue. The implementation of this is done using two pairs of blocking pipes. The first pair is used to block the parent, until the child writes a single byte. Then the second pair pair is used to block the child, until the parent confirms with another single byte. * src/util/command.c, src/util/command.h, src/libvirt_private.syms: Add APIs to perform a handshake --- src/libvirt_private.syms | 3 + src/util/command.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-- src/util/command.h | 5 ++ 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fdd23f9..1b4de1d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -92,8 +92,11 @@ virCommandAddEnvPassCommon; virCommandClearCaps; virCommandDaemonize; virCommandFree; +virCommandHandshakeNotify; +virCommandHandshakeWait; virCommandNew; virCommandPreserveFD; +virCommandRequireHandshake; virCommandRun; virCommandSetErrorBuffer; virCommandSetErrorFD; diff --git a/src/util/command.c b/src/util/command.c index 3f6c6f5..c711691 100644 --- a/src/util/command.c +++ b/src/util/command.c @@ -26,8 +26,10 @@ #include "virterror_internal.h" #include "util.h" #include "logging.h" +#include "files.h" #include <stdlib.h> +#include <stdbool.h> #include <poll.h> #include <sys/wait.h> @@ -60,6 +62,10 @@ struct _virCommand { int *outfdptr; int *errfdptr; + bool handshake; + int handshakeWait[2]; + int handshakeNotify[2]; + virExecHook hook; void *opaque; @@ -626,8 +632,8 @@ int virCommandRun(virCommandPtr cmd, ret = -1; VIR_DEBUG("Result stdout: '%s' stderr: '%s'", - NULLSTR(*cmd->outbuf), - NULLSTR(*cmd->errbuf)); + cmd->outbuf ? NULLSTR(*cmd->outbuf) : "(null)", + cmd->errbuf ? NULLSTR(*cmd->errbuf) : "(null)"); /* Reset any capturing, in case caller runs * this identical command again */ @@ -654,6 +660,38 @@ int virCommandRun(virCommandPtr cmd, } +static int virCommandHookImpl(void *data) +{ + virCommandPtr cmd = data; + + if (cmd->hook && + cmd->hook(cmd->opaque) < 0) + return -1; + + if (cmd->handshake) { + char c = 'w'; + VIR_WARN0("Notifying parent for handshake start"); + if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to notify parent process")); + return -1; + } + VIR_WARN0("Waiting on parent for handshake complete "); + if (saferead(cmd->handshakeNotify[0], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to wait on parent process")); + return -1; + } + if (c != 'n') { + virReportSystemError(EINVAL, _("Unexpected data '%d' from parent process"), (int)c); + return -1; + } + VIR_WARN0("Handshake is done, child is running"); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[0]); + } + + return 0; +} + /* * Run the command asynchronously * Returns -1 on any error executing the @@ -689,8 +727,8 @@ int virCommandRunAsync(virCommandPtr cmd, cmd->outfdptr, cmd->errfdptr, cmd->flags, - cmd->hook, - cmd->opaque, + virCommandHookImpl, + cmd, cmd->pidfile); VIR_DEBUG("Command result %d, with PID is %d", @@ -808,6 +846,45 @@ char *virCommandToString(virCommandPtr cmd) } +void virCommandRequireHandshake(virCommandPtr cmd) +{ + if (pipe(cmd->handshakeWait) < 0) + cmd->has_error = errno; + if (pipe(cmd->handshakeNotify) < 0) { + VIR_FORCE_CLOSE(cmd->handshakeWait[0]); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + } + + virCommandPreserveFD(cmd, cmd->handshakeWait[1]); + virCommandPreserveFD(cmd, cmd->handshakeNotify[0]); + cmd->handshake = true; +} + +int virCommandHandshakeWait(virCommandPtr cmd) +{ + char c; + if (saferead(cmd->handshakeWait[0], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to wait for child process")); + return -1; + } + if (c != 'w') { + virReportSystemError(EINVAL, _("Unexpected data '%d' from child process"), (int)c); + return -1; + } + return 0; +} + +int virCommandHandshakeNotify(virCommandPtr cmd) +{ + char c = 'n'; + if (safewrite(cmd->handshakeNotify[1], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to notify child process")); + return -1; + } + return 0; +} + + /* * Release all resources * @@ -832,6 +909,13 @@ void virCommandFree(virCommandPtr cmd) VIR_FREE(cmd->env[i]); VIR_FREE(cmd->env); + if (cmd->handshake) { + VIR_FORCE_CLOSE(cmd->handshakeWait[0]); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[0]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[1]); + } + VIR_FREE(cmd->pidfile); VIR_FREE(cmd); diff --git a/src/util/command.h b/src/util/command.h index 8b99361..f1a45b1 100644 --- a/src/util/command.h +++ b/src/util/command.h @@ -193,6 +193,11 @@ int virCommandWait(virCommandPtr cmd, void virCommandWriteArgLog(virCommandPtr cmd, int fd); +void virCommandRequireHandshake(virCommandPtr cmd); + +int virCommandHandshakeWait(virCommandPtr cmd); +int virCommandHandshakeNotify(virCommandPtr cmd); + /* * Release all resources */ -- 1.7.2.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list