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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list