mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 19:11:16 +00:00
qemu: don't take agent and monitor job for shutdown
We have to assume that the guest agent may be malicious so we don't want to allow any agent queries to block any other libvirt API. By holding a monitor job while we're querying the agent, we open ourselves up to a DoS. So split the function into separate parts: one that does the agent shutdown and one that does the monitor shutdown. Each part holds only a job of the appropriate type. Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
cae955eba2
commit
1cb8bc52c1
@ -1919,6 +1919,77 @@ static int qemuDomainResume(virDomainPtr dom)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qemuDomainShutdownFlagsAgent(virQEMUDriverPtr driver,
|
||||
virDomainObjPtr vm,
|
||||
bool isReboot,
|
||||
bool reportError)
|
||||
{
|
||||
int ret = -1;
|
||||
qemuAgentPtr agent;
|
||||
int agentFlag = isReboot ? QEMU_AGENT_SHUTDOWN_REBOOT :
|
||||
QEMU_AGENT_SHUTDOWN_POWERDOWN;
|
||||
|
||||
if (qemuDomainObjBeginAgentJob(driver, vm,
|
||||
QEMU_AGENT_JOB_MODIFY) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID,
|
||||
"%s", _("domain is not running"));
|
||||
goto endjob;
|
||||
}
|
||||
|
||||
if (!qemuDomainAgentAvailable(vm, reportError))
|
||||
goto endjob;
|
||||
|
||||
qemuDomainSetFakeReboot(driver, vm, false);
|
||||
agent = qemuDomainObjEnterAgent(vm);
|
||||
ret = qemuAgentShutdown(agent, agentFlag);
|
||||
qemuDomainObjExitAgent(vm, agent);
|
||||
|
||||
endjob:
|
||||
qemuDomainObjEndAgentJob(vm);
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qemuDomainShutdownFlagsMonitor(virQEMUDriverPtr driver,
|
||||
virDomainObjPtr vm,
|
||||
bool isReboot)
|
||||
{
|
||||
int ret = -1;
|
||||
qemuDomainObjPrivatePtr priv;
|
||||
|
||||
priv = vm->privateData;
|
||||
|
||||
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID,
|
||||
"%s", _("domain is not running"));
|
||||
goto endjob;
|
||||
}
|
||||
|
||||
qemuDomainSetFakeReboot(driver, vm, isReboot);
|
||||
qemuDomainObjEnterMonitor(driver, vm);
|
||||
ret = qemuMonitorSystemPowerdown(priv->mon);
|
||||
if (qemuDomainObjExitMonitor(driver, vm) < 0)
|
||||
ret = -1;
|
||||
|
||||
endjob:
|
||||
qemuDomainObjEndJob(driver, vm);
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int qemuDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
|
||||
{
|
||||
virQEMUDriverPtr driver = dom->conn->privateData;
|
||||
@ -1928,8 +1999,6 @@ static int qemuDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
|
||||
bool useAgent = false, agentRequested, acpiRequested;
|
||||
bool isReboot = false;
|
||||
bool agentForced;
|
||||
qemuDomainAgentJob agentJob = QEMU_AGENT_JOB_NONE;
|
||||
int agentFlag = QEMU_AGENT_SHUTDOWN_POWERDOWN;
|
||||
|
||||
virCheckFlags(VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN |
|
||||
VIR_DOMAIN_SHUTDOWN_GUEST_AGENT, -1);
|
||||
@ -1940,7 +2009,6 @@ static int qemuDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
|
||||
if (vm->def->onPoweroff == VIR_DOMAIN_LIFECYCLE_ACTION_RESTART ||
|
||||
vm->def->onPoweroff == VIR_DOMAIN_LIFECYCLE_ACTION_RESTART_RENAME) {
|
||||
isReboot = true;
|
||||
agentFlag = QEMU_AGENT_SHUTDOWN_REBOOT;
|
||||
VIR_INFO("Domain on_poweroff setting overridden, attempting reboot");
|
||||
}
|
||||
|
||||
@ -1955,62 +2023,27 @@ static int qemuDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
|
||||
if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (useAgent)
|
||||
agentJob = QEMU_AGENT_JOB_MODIFY;
|
||||
|
||||
if (qemuDomainObjBeginJobWithAgent(driver, vm,
|
||||
QEMU_JOB_MODIFY,
|
||||
agentJob) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID,
|
||||
"%s", _("domain is not running"));
|
||||
goto endjob;
|
||||
}
|
||||
|
||||
agentForced = agentRequested && !acpiRequested;
|
||||
if (!qemuDomainAgentAvailable(vm, agentForced)) {
|
||||
if (agentForced)
|
||||
goto endjob;
|
||||
useAgent = false;
|
||||
}
|
||||
|
||||
|
||||
if (useAgent) {
|
||||
qemuAgentPtr agent;
|
||||
qemuDomainSetFakeReboot(driver, vm, false);
|
||||
agent = qemuDomainObjEnterAgent(vm);
|
||||
ret = qemuAgentShutdown(agent, agentFlag);
|
||||
qemuDomainObjExitAgent(vm, agent);
|
||||
ret = qemuDomainShutdownFlagsAgent(driver, vm, isReboot, agentForced);
|
||||
if (ret < 0 && agentForced)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* If we are not enforced to use just an agent, try ACPI
|
||||
* shutdown as well in case agent did not succeed.
|
||||
*/
|
||||
if (!useAgent ||
|
||||
(ret < 0 && (acpiRequested || !flags))) {
|
||||
|
||||
if (!useAgent || (ret < 0 && (acpiRequested || !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 endjob;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
qemuDomainSetFakeReboot(driver, vm, isReboot);
|
||||
qemuDomainObjEnterMonitor(driver, vm);
|
||||
ret = qemuMonitorSystemPowerdown(priv->mon);
|
||||
if (qemuDomainObjExitMonitor(driver, vm) < 0)
|
||||
ret = -1;
|
||||
ret = qemuDomainShutdownFlagsMonitor(driver, vm, isReboot);
|
||||
}
|
||||
|
||||
endjob:
|
||||
if (agentJob)
|
||||
qemuDomainObjEndJobWithAgent(driver, vm);
|
||||
else
|
||||
qemuDomainObjEndJob(driver, vm);
|
||||
|
||||
cleanup:
|
||||
virDomainObjEndAPI(&vm);
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user