From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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 <berrange@xxxxxxxxxx> --- 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 991b593..210bc2d 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" @@ -2683,6 +2684,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, @@ -2751,6 +2925,9 @@ static virDriver lxcDriver = { .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .nodeGetMemoryParameters = nodeGetMemoryParameters, /* 0.10.2 */ .nodeSetMemoryParameters = nodeSetMemoryParameters, /* 0.10.2 */ + .domainShutdown = lxcDomainShutdown, /* 1.0.1 */ + .domainShutdownFlags = lxcDomainShutdownFlags, /* 1.0.1 */ + .domainReboot = lxcDomainReboot, /* 1.0.1 */ }; static virStateDriver lxcStateDriver = { -- 1.7.11.7 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list