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 | 52 ++++++++++++++++++++++++++++++++---------- src/util/vircommand.h | 1 + src/util/vircommandpriv.h | 13 ++++++++++- tests/virkmodtest.c | 8 +++---- tests/virnetdevbandwidthtest.c | 3 ++- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/util/vircommand.c b/src/util/vircommand.c index ff135bf..79bb20c 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -135,6 +135,9 @@ struct _virCommand { /* See virCommandSetDryRun for description for this variable */ static virBufferPtr dryRunBuffer; +static virCommandDryRunCallback dryRunCallback; +static void *dryRunOpaque; +static int dryRunStatus; /* * virCommandFDIsSet: @@ -1860,6 +1863,11 @@ virCommandProcessIO(virCommandPtr cmd) size_t inoff = 0; int ret = 0; + if (dryRunBuffer || dryRunCallback) { + VIR_DEBUG("Dry run requested, skipping I/O processing"); + return 0; + } + /* With an input buffer, feed data to child * via pipe */ if (cmd->inbuf) @@ -2267,16 +2275,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; } @@ -2356,10 +2373,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; } @@ -2704,6 +2722,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 @@ -2712,8 +2731,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); @@ -2725,10 +2749,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 10b4fa2..929375b 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -186,4 +186,5 @@ void virCommandAbort(virCommandPtr cmd); void virCommandFree(virCommandPtr cmd); void virCommandDoAsyncIO(virCommandPtr cmd); + #endif /* __VIR_COMMAND_H__ */ diff --git a/src/util/vircommandpriv.h b/src/util/vircommandpriv.h index 80247b2..8930299 100644 --- a/src/util/vircommandpriv.h +++ b/src/util/vircommandpriv.h @@ -28,5 +28,16 @@ # include "vircommand.h" -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_PRIV_H__ */ diff --git a/tests/virkmodtest.c b/tests/virkmodtest.c index 8899689..9473352 100644 --- a/tests/virkmodtest.c +++ b/tests/virkmodtest.c @@ -96,7 +96,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) { @@ -110,7 +110,7 @@ testKModLoad(const void *args) ret = 0; cleanup: - virCommandSetDryRun(NULL); + virCommandSetDryRun(NULL, NULL, NULL); VIR_FREE(errbuf); return ret; } @@ -125,7 +125,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) { @@ -139,7 +139,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 38dfbc1..f5d252c 100644 --- a/tests/virnetdevbandwidthtest.c +++ b/tests/virnetdevbandwidthtest.c @@ -77,7 +77,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; @@ -101,6 +101,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