diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index e388302a8b..56ed7d3195 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -661,6 +661,7 @@ static int lxcControllerClearCapabilities(void) } static bool quit = false; +static bool wantReboot = false; static virMutex lock; @@ -670,11 +671,15 @@ static void virLXCControllerSignalChildIO(virNetServerPtr server ATTRIBUTE_UNUSE { virLXCControllerPtr ctrl = opaque; int ret; + int status; - ret = waitpid(-1, NULL, WNOHANG); + ret = waitpid(-1, &status, WNOHANG); if (ret == ctrl->initpid) { virMutexLock(&lock); quit = true; + if (WIFSIGNALED(status) && + WTERMSIG(status) == SIGHUP) + wantReboot = true; virMutexUnlock(&lock); } } @@ -998,7 +1003,7 @@ static int virLXCControllerMain(virLXCControllerPtr ctrl) err = virGetLastError(); if (!err || err->code == VIR_ERR_OK) - rc = 0; + rc = wantReboot ? 1 : 0; cleanup: virMutexDestroy(&lock); @@ -1319,6 +1324,9 @@ virLXCControllerEventSendExit(virLXCControllerPtr ctrl, case 0: msg.status = VIR_LXC_PROTOCOL_EXIT_STATUS_SHUTDOWN; break; + case 1: + msg.status = VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT; + break; default: msg.status = VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR; break; diff --git a/src/lxc/lxc_domain.h b/src/lxc/lxc_domain.h index f4a8384126..06da1418c4 100644 --- a/src/lxc/lxc_domain.h +++ b/src/lxc/lxc_domain.h @@ -33,6 +33,7 @@ struct _virLXCDomainObjPrivate { virLXCMonitorPtr monitor; bool doneStopEvent; int stopReason; + bool wantReboot; }; void virLXCDomainSetPrivateDataHooks(virCapsPtr caps); diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 5ffdff5c65..b42f4a04de 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -148,6 +148,58 @@ int virLXCProcessAutoDestroyRemove(virLXCDriverPtr driver, return 0; } +static virConnectPtr +virLXCProcessAutoDestroyGetConn(virLXCDriverPtr driver, + virDomainObjPtr vm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s uuid=%s", vm->def->name, uuidstr); + return virHashLookup(driver->autodestroy, uuidstr); +} + + +static int +virLXCProcessReboot(virLXCDriverPtr driver, + virDomainObjPtr vm) +{ + virConnectPtr conn = virLXCProcessAutoDestroyGetConn(driver, vm); + int reason = vm->state.reason; + bool autodestroy = false; + int ret = -1; + virDomainDefPtr savedDef; + + if (conn) { + virConnectRef(conn); + autodestroy = true; + } else { + conn = virConnectOpen("lxc:///"); + /* Ignoring NULL conn which is mostly harmless here */ + } + + /* In a reboot scenario, we need to make sure we continue + * to use the current 'def', and not switch to 'newDef'. + * So temporarily hide the newDef and then reinstate it + */ + savedDef = vm->newDef; + vm->newDef = NULL; + virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN); + vm->newDef = savedDef; + if (virLXCProcessStart(conn, driver, vm, autodestroy, reason) < 0) { + VIR_WARN("Unable to handle reboot of vm %s", + vm->def->name); + goto cleanup; + } + + if (conn) + virConnectClose(conn); + + ret = 0; + +cleanup: + return ret; +} + /** * virLXCProcessCleanup: @@ -509,17 +561,35 @@ static void virLXCProcessMonitorEOFNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED, priv = vm->privateData; virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN); - if (!priv->doneStopEvent) { - event = virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_STOPPED, - priv->stopReason); - virDomainAuditStop(vm, "shutdown"); + if (!priv->wantReboot) { + virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN); + if (!priv->doneStopEvent) { + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + priv->stopReason); + virDomainAuditStop(vm, "shutdown"); + } else { + VIR_DEBUG("Stop event has already been sent"); + } + if (!vm->persistent) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + } } else { - VIR_DEBUG("Stop event has already been sent"); - } - if (!vm->persistent) { - virDomainRemoveInactive(&driver->domains, vm); - vm = NULL; + int ret = virLXCProcessReboot(driver, vm); + virDomainAuditStop(vm, "reboot"); + virDomainAuditStart(vm, "reboot", ret == 0); + if (ret == 0) { + event = virDomainEventRebootNewFromObj(vm); + } else { + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + priv->stopReason); + if (!vm->persistent) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + } + } } if (vm) @@ -544,6 +614,10 @@ static void virLXCProcessMonitorExitNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED case VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR: priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED; break; + case VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT: + priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; + priv->wantReboot = true; + break; default: priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED; break; @@ -1033,6 +1107,7 @@ int virLXCProcessStart(virConnectPtr conn, } priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED; + priv->wantReboot = false; vm->def->id = vm->pid; virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason); priv->doneStopEvent = false; diff --git a/src/lxc/lxc_protocol.x b/src/lxc/lxc_protocol.x index 4fdbe34e7b..e4372178eb 100644 --- a/src/lxc/lxc_protocol.x +++ b/src/lxc/lxc_protocol.x @@ -6,7 +6,8 @@ enum virLXCProtocolExitStatus { VIR_LXC_PROTOCOL_EXIT_STATUS_ERROR, - VIR_LXC_PROTOCOL_EXIT_STATUS_SHUTDOWN + VIR_LXC_PROTOCOL_EXIT_STATUS_SHUTDOWN, + VIR_LXC_PROTOCOL_EXIT_STATUS_REBOOT }; struct virLXCProtocolExitEventMsg {