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