diff --git a/tools/virsh-host.c b/tools/virsh-host.c index 3c44969405..da6089518c 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -647,6 +647,93 @@ cleanup: return ret; } +/* + * "qemu-agent-command" command + */ +static const vshCmdInfo info_qemu_agent_command[] = { + {"help", N_("QEMU Guest Agent Command")}, + {"desc", N_("Run an arbitrary qemu guest agent command; use at your own risk")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_agent_command[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, + {"async", VSH_OT_BOOL, 0, N_("execute command without waiting for timeout")}, + {"block", VSH_OT_BOOL, 0, N_("execute command without timeout")}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + char *guest_agent_cmd = NULL; + char *result = NULL; + int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT; + int judge = 0; + unsigned int flags = 0; + const vshCmdOpt *opt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool pad = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (pad) + virBufferAddChar(&buf, ' '); + pad = true; + virBufferAdd(&buf, opt->data, -1); + } + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to collect command")); + goto cleanup; + } + guest_agent_cmd = virBufferContentAndReset(&buf); + + judge = vshCommandOptInt(cmd, "timeout", &timeout); + if (judge < 0) { + vshError(ctl, "%s", _("timeout number has to be a number")); + } else if (judge > 0) { + judge = 1; + } + if (judge && timeout < 1) { + vshError(ctl, "%s", _("timeout must be positive")); + } + + if (vshCommandOptBool(cmd, "async")) { + timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT; + judge++; + } + if (vshCommandOptBool(cmd, "block")) { + timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK; + judge++; + } + + if (judge > 1) { + vshError(ctl, "%s", _("timeout, async and block options are exclusive")); + } + result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags); + + if (result) printf("%s\n", result); + + ret = true; + +cleanup: + VIR_FREE(result); + VIR_FREE(guest_agent_cmd); + if (dom) + virDomainFree(dom); + + return ret; +} /* * "sysinfo" command */ @@ -847,6 +934,8 @@ const vshCmdDef hostAndHypervisorCmds[] = { {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0}, {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, info_qemu_monitor_command, 0}, + {"qemu-agent-command", cmdQemuAgentCommand, opts_qemu_agent_command, + info_qemu_agent_command, 0}, {"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0}, {"uri", cmdURI, NULL, info_uri, 0}, {"version", cmdVersion, opts_version, info_version, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 424f1d989e..cad5b1c2ea 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -164,6 +164,7 @@ group as an option. For example: hostname print the hypervisor hostname qemu-attach Attach to existing QEMU process qemu-monitor-command QEMU Monitor Command + qemu-agent-command QEMU Guest Agent Command sysinfo print the hypervisor sysinfo uri print the hypervisor canonical URI @@ -2797,6 +2798,18 @@ before passing the single command to the monitor. =back +=item B I [I<--timeout> I | I<--async> | I<--block>] I... + +Send an arbitrary guest agent command I to domain I through +qemu agent. +I<--timeout>, I<--async> and I<--block> options are exclusive. +I<--timeout> requires timeout seconds I and it must be positive. +When I<--aysnc> is given, the command waits for timeout whether success or +failed. And when I<--block> is given, the command waits forever with blocking +timeout. + +=back + =head1 ENVIRONMENT The following environment variables can be set to alter the behaviour