diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0c163437de47b2d97153222b598d379468499839..76552478461c87545ee6a94f8b53ce43a01ed9ae 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1096,6 +1096,7 @@ virCommandRequireHandshake; virCommandRun; virCommandRunAsync; virCommandSetAppArmorProfile; +virCommandSetDryRun; virCommandSetErrorBuffer; virCommandSetErrorFD; virCommandSetGID; diff --git a/src/util/vircommand.c b/src/util/vircommand.c index a52a1ab96b2a147f8e1188cbc0b09011f3ee352c..b137436ea6747f3c796590a19412c34315ed4922 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -129,6 +129,9 @@ struct _virCommand { #endif }; +/* See virCommandSetDryRun for description for this variable */ +static virBufferPtr dryRunBuffer; + /* * virCommandFDIsSet: * @fd: FD to test @@ -2199,7 +2202,7 @@ int virCommandRunAsync(virCommandPtr cmd, pid_t *pid) { int ret = -1; - char *str; + char *str = NULL; size_t i; bool synchronous = false; int infd[2] = {-1, -1}; @@ -2262,9 +2265,21 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) } str = virCommandToString(cmd); - VIR_DEBUG("About to run %s", str ? str : cmd->args[0]); - VIR_FREE(str); + if (dryRunBuffer) { + 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'); + ret = 0; + goto cleanup; + } + VIR_DEBUG("About to run %s", str ? str : cmd->args[0]); ret = virExec(cmd); VIR_DEBUG("Command result %d, with PID %d", ret, (int)cmd->pid); @@ -2303,6 +2318,7 @@ cleanup: VIR_FORCE_CLOSE(cmd->infd); VIR_FORCE_CLOSE(cmd->inpipe); } + VIR_FREE(str); return ret; } @@ -2334,6 +2350,13 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) return -1; } + if (dryRunBuffer) { + VIR_DEBUG("Dry run requested, claiming success"); + if (exitstatus) + *exitstatus = 0; + return 0; + } + if (cmd->pid == -1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("command is not yet running")); @@ -2669,3 +2692,35 @@ virCommandDoAsyncIO(virCommandPtr cmd) cmd->flags |= VIR_EXEC_ASYNC_IO | VIR_EXEC_NONBLOCK; } + +/** + * virCommandSetDryRun: + * @buf: buffer to store stringified commands + * + * Sometimes it's desired to not actually run given command, but + * see its string representation without having to change the + * callee. Unit testing serves as a great example. In such cases, + * the callee constructs the command and calls it via + * 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: + * + * virBuffer buffer = VIR_BUFFER_INITIALIZER; + * virCommandSetDryRun(&buffer); + * + * virCommandPtr echocmd = virCommandNewArgList("/bin/echo", "Hello world", NULL); + * virCommandRun(echocmd, NULL); + * + * After this, the @buffer should contain: + * + * /bin/echo 'Hello world'\n + * + * To cancel this effect pass NULL. + */ +void +virCommandSetDryRun(virBufferPtr buf) +{ + dryRunBuffer = buf; +} diff --git a/src/util/vircommand.h b/src/util/vircommand.h index e977f93ed73b167e1eafc2315cc27a59479334ea..a7432004f1f1de5541a85b7dd8ee7f0bfb31585b 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -184,4 +184,6 @@ void virCommandAbort(virCommandPtr cmd); void virCommandFree(virCommandPtr cmd); void virCommandDoAsyncIO(virCommandPtr cmd); + +void virCommandSetDryRun(virBufferPtr buf); #endif /* __VIR_COMMAND_H__ */