To allow for fault injection of the virCommand dry run,
add the ability to register a callback. The callback will
be passed the argv, env and stdin buffer and is expected
to return the exit status and optionally fill stdout and
stderr buffers.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/util/vircommand.c | 47 +++++++++++++++++++++++++++++++-----------
src/util/vircommand.h | 12 ++++++++++-
tests/virkmodtest.c | 8 +++----
tests/virnetdevbandwidthtest.c | 3 ++-
4 files changed, 52 insertions(+), 18 deletions(-)
diff --git a/src/util/vircommand.c b/src/util/vircommand.c
index db4166f..42d0182 100644
--- a/src/util/vircommand.c
+++ b/src/util/vircommand.c
@@ -132,6 +132,9 @@ struct _virCommand {
/* See virCommandSetDryRun for description for this variable */
static virBufferPtr dryRunBuffer;
+static virCommandDryRunCallback dryRunCallback;
+static void *dryRunOpaque;
+static int dryRunStatus;
/*
* virCommandFDIsSet:
@@ -2264,16 +2267,25 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
}
str = virCommandToString(cmd);
- if (dryRunBuffer) {
+ if (dryRunBuffer || dryRunCallback) {
+ dryRunStatus = 0;
if (!str) {
/* error already reported by virCommandToString */
goto cleanup;
}
- VIR_DEBUG("Dry run requested, appending stringified "
- "command to dryRunBuffer=%p", dryRunBuffer);
- virBufferAdd(dryRunBuffer, str, -1);
- virBufferAddChar(dryRunBuffer, '\n');
+ if (dryRunBuffer) {
+ VIR_DEBUG("Dry run requested, appending stringified "
+ "command to dryRunBuffer=%p", dryRunBuffer);
+ virBufferAdd(dryRunBuffer, str, -1);
+ virBufferAddChar(dryRunBuffer, '\n');
+ }
+ if (dryRunCallback) {
+ dryRunCallback((const char *const*)cmd->args,
+ (const char *const*)cmd->env,
+ cmd->inbuf, cmd->outbuf, cmd->errbuf,
+ &dryRunStatus, dryRunOpaque);
+ }
ret = 0;
goto cleanup;
}
@@ -2353,10 +2365,11 @@ virCommandWait(virCommandPtr cmd, int *exitstatus)
return -1;
}
- if (dryRunBuffer) {
- VIR_DEBUG("Dry run requested, claiming success");
+ if (dryRunBuffer || dryRunCallback) {
+ VIR_DEBUG("Dry run requested, returning status %d",
+ dryRunStatus);
if (exitstatus)
- *exitstatus = 0;
+ *exitstatus = dryRunStatus;
return 0;
}
@@ -2701,6 +2714,7 @@ virCommandDoAsyncIO(virCommandPtr cmd)
/**
* virCommandSetDryRun:
* @buf: buffer to store stringified commands
+ * @callback: callback to process input/output/args
*
* Sometimes it's desired to not actually run given command, but
* see its string representation without having to change the
@@ -2709,8 +2723,13 @@ virCommandDoAsyncIO(virCommandPtr cmd)
* virCommandRun* API. The virCommandSetDryRun allows you to
* modify this behavior: once called, every call to
* virCommandRun* results in command string representation being
- * appended to @buf instead of being executed. the strings are
- * escaped for a shell and separated by a newline. For example:
+ * appended to @buf instead of being executed. If @callback is
+ * provided, then it is invoked with the argv, env and stdin
+ * data string for the command. It is expected to fill the stdout
+ * and stderr data strings and exit status variables.
+ *
+ * The strings stored in @buf are escaped for a shell and
+ * separated by a newline. For example:
*
* virBuffer buffer = VIR_BUFFER_INITIALIZER;
* virCommandSetDryRun(&buffer);
@@ -2722,10 +2741,14 @@ virCommandDoAsyncIO(virCommandPtr cmd)
*
* /bin/echo 'Hello world'\n
*
- * To cancel this effect pass NULL.
+ * To cancel this effect pass NULL for @buf and @callback.
*/
void
-virCommandSetDryRun(virBufferPtr buf)
+virCommandSetDryRun(virBufferPtr buf,
+ virCommandDryRunCallback cb,
+ void *opaque)
{
dryRunBuffer = buf;
+ dryRunCallback = cb;
+ dryRunOpaque = opaque;
}
diff --git a/src/util/vircommand.h b/src/util/vircommand.h
index 7485edc..9405f5f 100644
--- a/src/util/vircommand.h
+++ b/src/util/vircommand.h
@@ -187,5 +187,15 @@ void virCommandFree(virCommandPtr cmd);
void virCommandDoAsyncIO(virCommandPtr cmd);
-void virCommandSetDryRun(virBufferPtr buf);
+typedef void (*virCommandDryRunCallback)(const char *const*args,
+ const char *const*env,
+ const char *input,
+ char **output,
+ char **error,
+ int *status,
+ void *opaque);
+
+void virCommandSetDryRun(virBufferPtr buf,
+ virCommandDryRunCallback cb,
+ void *opaque);
#endif /* __VIR_COMMAND_H__ */
diff --git a/tests/virkmodtest.c b/tests/virkmodtest.c
index c6f5a72..f362d03 100644
--- a/tests/virkmodtest.c
+++ b/tests/virkmodtest.c
@@ -95,7 +95,7 @@ testKModLoad(const void *args)
bool useBlacklist = info->useBlacklist;
virBuffer buf = VIR_BUFFER_INITIALIZER;
- virCommandSetDryRun(&buf);
+ virCommandSetDryRun(&buf, NULL, NULL);
errbuf = virKModLoad(module, useBlacklist);
if (errbuf) {
@@ -109,7 +109,7 @@ testKModLoad(const void *args)
ret = 0;
cleanup:
- virCommandSetDryRun(NULL);
+ virCommandSetDryRun(NULL, NULL, NULL);
VIR_FREE(errbuf);
return ret;
}
@@ -124,7 +124,7 @@ testKModUnload(const void *args)
const char *module = info->module;
virBuffer buf = VIR_BUFFER_INITIALIZER;
- virCommandSetDryRun(&buf);
+ virCommandSetDryRun(&buf, NULL, NULL);
errbuf = virKModUnload(module);
if (errbuf) {
@@ -138,7 +138,7 @@ testKModUnload(const void *args)
ret = 0;
cleanup:
- virCommandSetDryRun(NULL);
+ virCommandSetDryRun(NULL, NULL, NULL);
VIR_FREE(errbuf);
return ret;
}
diff --git a/tests/virnetdevbandwidthtest.c b/tests/virnetdevbandwidthtest.c
index 073fdf8..3aebd49 100644
--- a/tests/virnetdevbandwidthtest.c
+++ b/tests/virnetdevbandwidthtest.c
@@ -76,7 +76,7 @@ testVirNetDevBandwidthSet(const void *data)
if (!iface)
iface = "eth0";
- virCommandSetDryRun(&buf);
+ virCommandSetDryRun(&buf, NULL, NULL);
if (virNetDevBandwidthSet(iface, band, info->hierarchical_class) < 0)
goto cleanup;
@@ -100,6 +100,7 @@ testVirNetDevBandwidthSet(const void *data)
ret = 0;
cleanup:
+ virCommandSetDryRun(NULL, NULL, NULL);
virNetDevBandwidthFree(band);
virBufferFreeAndReset(&buf);
VIR_FREE(actual_cmd);
--
1.8.5.3