From 5174fe3a8c3ebee598ec2b5b2426d70d4aaf5bb3 Mon Sep 17 00:00:00 2001 Message-Id: <5174fe3a8c3ebee598ec2b5b2426d70d4aaf5bb3.1391615407.git.jdenemar@redhat.com> From: Michal Privoznik Date: Tue, 4 Feb 2014 13:34:53 -0500 Subject: [PATCH] virCommand: Introduce virCommandSetDryRun https://bugzilla.redhat.com/show_bug.cgi?id=1045124 There are some units within libvirt that utilize virCommand API to run some commands and deserve own unit testing. These units are, however, not desired to be rewritten to dig virCommand API usage out. As a great example virNetDevBandwidth could be used. The problem with the bandwidth unit is: it uses virCommand API heavily. Therefore we need a mechanism to not really run a command, but rather see its string representation after which we can decide if the unit construct the correct sequence of commands or not. Signed-off-by: Michal Privoznik (cherry picked from commit 550a2ceffb46f2a7dbc6ce0197da8b1b6e8c5abd) Signed-off-by: John Ferlan Signed-off-by: Jiri Denemark --- src/libvirt_private.syms | 1 + src/util/vircommand.c | 61 +++++++++++++++++++++++++++++++++++++++++++++--- src/util/vircommand.h | 2 ++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a1b3e4a..5e65c35 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1267,6 +1267,7 @@ virCommandRequireHandshake; virCommandRun; virCommandRunAsync; virCommandSetAppArmorProfile; +virCommandSetDryRun; virCommandSetErrorBuffer; virCommandSetErrorFD; virCommandSetGID; diff --git a/src/util/vircommand.c b/src/util/vircommand.c index fca0e09..3302ce6 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 e977f93..a743200 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__ */ -- 1.8.5.3