From cbb106f807b32f1f6af22d1e92fe0ff9ba6d73b3 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 28 Nov 2012 13:26:52 +0000 Subject: [PATCH] Add support for shutdown / reboot APIs in LXC driver Add support for doing controlled shutdown / reboot in the LXC driver. The default behaviour is to try talking to /dev/initctl inside the container's virtual root (/proc/$INITPID/root). This works with sysvinit or systemd. If that file does not exist then send SIGTERM (for shutdown) or SIGHUP (for reboot). These signals are not any kind of particular standard for shutdown or reboot, just something apps can choose to handle. The new virDomainSendProcessSignal allows for sending custom signals. We might allow the choice of SIGTERM/HUP to be configured for LXC containers via the XML in the future. Signed-off-by: Daniel P. Berrange --- src/lxc/lxc_driver.c | 177 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 4ca4de8a00..75c4f52543 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -57,6 +57,7 @@ #include "domain_audit.h" #include "domain_nwfilter.h" #include "network/bridge_driver.h" +#include "virinitctl.h" #include "virnetdev.h" #include "virnetdevtap.h" #include "virnodesuspend.h" @@ -2762,6 +2763,179 @@ lxcListAllDomains(virConnectPtr conn, return ret; } +static int +lxcDomainShutdownFlags(virDomainPtr dom, + unsigned int flags) +{ + virLXCDriverPtr driver = dom->conn->privateData; + virLXCDomainObjPrivatePtr priv; + virDomainObjPtr vm; + char *vroot = NULL; + int ret = -1; + int rc; + + virCheckFlags(VIR_DOMAIN_SHUTDOWN_INITCTL | + VIR_DOMAIN_SHUTDOWN_SIGNAL, -1); + + lxcDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + lxcDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + + if (priv->initpid == 0) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Init process ID is not yet known")); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (flags == 0 || + (flags & VIR_DOMAIN_SHUTDOWN_INITCTL)) { + if ((rc = virInitctlSetRunLevel(VIR_INITCTL_RUNLEVEL_POWEROFF, + vroot)) < 0) { + goto cleanup; + } + if (rc == 0 && flags != 0 && + ((flags & ~VIR_DOMAIN_SHUTDOWN_INITCTL) == 0)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Container does not provide an initctl pipe")); + goto cleanup; + } + } else { + rc = 0; + } + + if (rc == 0 && + (flags == 0 || + (flags & VIR_DOMAIN_SHUTDOWN_SIGNAL))) { + if (kill(priv->initpid, SIGTERM) < 0 && + errno != ESRCH) { + virReportSystemError(errno, + _("Unable to send SIGTERM to init pid %llu"), + (unsigned long long)priv->initpid); + goto cleanup; + } + } + + ret = 0; + +cleanup: + VIR_FREE(vroot); + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +lxcDomainShutdown(virDomainPtr dom) +{ + return lxcDomainShutdownFlags(dom, 0); +} + +static int +lxcDomainReboot(virDomainPtr dom, + unsigned int flags) +{ + virLXCDriverPtr driver = dom->conn->privateData; + virLXCDomainObjPrivatePtr priv; + virDomainObjPtr vm; + char *vroot = NULL; + int ret = -1; + int rc; + + virCheckFlags(VIR_DOMAIN_REBOOT_INITCTL | + VIR_DOMAIN_REBOOT_SIGNAL, -1); + + lxcDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + lxcDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + + if (priv->initpid == 0) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Init process ID is not yet known")); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (flags == 0 || + (flags & VIR_DOMAIN_REBOOT_INITCTL)) { + if ((rc = virInitctlSetRunLevel(VIR_INITCTL_RUNLEVEL_REBOOT, + vroot)) < 0) { + goto cleanup; + } + if (rc == 0 && flags != 0 && + ((flags & ~VIR_DOMAIN_SHUTDOWN_INITCTL) == 0)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Container does not provide an initctl pipe")); + goto cleanup; + } + } else { + rc = 0; + } + + if (rc == 0 && + (flags == 0 || + (flags & VIR_DOMAIN_REBOOT_SIGNAL))) { + if (kill(priv->initpid, SIGHUP) < 0 && + errno != ESRCH) { + virReportSystemError(errno, + _("Unable to send SIGTERM to init pid %llu"), + (unsigned long long)priv->initpid); + goto cleanup; + } + } + + ret = 0; + +cleanup: + VIR_FREE(vroot); + if (vm) + virDomainObjUnlock(vm); + return ret; +} + + /* Function Tables */ static virDriver lxcDriver = { .no = VIR_DRV_LXC, @@ -2831,6 +3005,9 @@ static virDriver lxcDriver = { .nodeGetMemoryParameters = nodeGetMemoryParameters, /* 0.10.2 */ .nodeSetMemoryParameters = nodeSetMemoryParameters, /* 0.10.2 */ .domainSendProcessSignal = lxcDomainSendProcessSignal, /* 1.0.1 */ + .domainShutdown = lxcDomainShutdown, /* 1.0.1 */ + .domainShutdownFlags = lxcDomainShutdownFlags, /* 1.0.1 */ + .domainReboot = lxcDomainReboot, /* 1.0.1 */ }; static virStateDriver lxcStateDriver = { -- 2.47.2