Implement shutdown using a guest agent. Implementation and behaviour is very similar to the one found in the qemu driver. The bhyveDomainShutdownFlags() function now supports the VIR_DOMAIN_SHUTDOWN_SIGNAL and VIR_DOMAIN_SHUTDOWN_GUEST_AGENT. If flags were not specified, try the agent first, and if it does not work, proceed with the signal. As we do not expect a response from the agent shutdown command, we do not check qemuAgentShutdown()'s return value. Assume that the command failed if the agent did not receive EOF, thus "priv->agent" is not NULL. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 69 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index 35abf36877..4b28c698e7 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -1053,14 +1053,51 @@ bhyveDomainShutdownSignal(virDomainObj *vm, return virBhyveProcessShutdown(vm); } +static int +bhyveDomainShutdownFlagsAgent(virDomainObj *vm, + bool isReboot, + bool reportError) +{ + int ret = -1; + qemuAgent *agent; + int agentFlag = isReboot ? QEMU_AGENT_SHUTDOWN_REBOOT : + QEMU_AGENT_SHUTDOWN_POWERDOWN; + + if (virDomainObjBeginAgentJob(vm, VIR_AGENT_JOB_MODIFY) < 0) + return -1; + + if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + if (bhyveDomainEnsureAgent(vm, reportError) < 0) + goto endjob; + + agent = bhyveDomainObjEnterAgent(vm); + qemuAgentShutdown(agent, agentFlag); + bhyveDomainObjExitAgent(vm, agent); + ret = 0; + + endjob: + virDomainObjEndAgentJob(vm); + return ret; +} + static int bhyveDomainShutdownFlags(virDomainPtr dom, unsigned int flags) { virDomainObj *vm; + bhyveDomainObjPrivate *priv; int ret = -1; bool isReboot = false; + bool useAgent = false; + bool agentRequested, signalRequested; + bool agentForced; - virCheckFlags(0, -1); + virCheckFlags(VIR_DOMAIN_SHUTDOWN_SIGNAL | + VIR_DOMAIN_SHUTDOWN_GUEST_AGENT, -1); if (!(vm = bhyveDomObjFromDomain(dom))) goto cleanup; @@ -1071,13 +1108,41 @@ bhyveDomainShutdownFlags(virDomainPtr dom, unsigned int flags) VIR_INFO("Domain on_poweroff setting overridden, attempting reboot"); } + priv = vm->privateData; + agentRequested = flags & VIR_DOMAIN_SHUTDOWN_GUEST_AGENT; + signalRequested = flags & VIR_DOMAIN_SHUTDOWN_SIGNAL; + + /* Prefer agent unless we were requested to not to. */ + if (agentRequested || !flags) + useAgent = true; + if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0) goto cleanup; if (virDomainObjCheckActive(vm) < 0) goto cleanup; - ret = bhyveDomainShutdownSignal(vm, isReboot); + agentForced = agentRequested && !signalRequested; + if (useAgent) { + ret = bhyveDomainShutdownFlagsAgent(vm, isReboot, agentForced); + if (((ret < 0) || (priv->agent != NULL)) && agentForced) + goto cleanup; + } + + /* If we are not enforced to use just an agent, try signal + * shutdown as well in case agent did not succeed. + */ + if (!useAgent || (((ret < 0) || + (priv->agent != NULL)) && (signalRequested || !flags))) { + /* Even if agent failed, we have to check if guest went away + * by itself while our locks were down. */ + if (useAgent && !virDomainObjIsActive(vm)) { + ret = 0; + goto cleanup; + } + + ret = bhyveDomainShutdownSignal(vm, isReboot); + } cleanup: virDomainObjEndAPI(&vm); -- 2.52.0