From: "Daniel P. Berrange" <berrange(a)redhat.com>
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(a)redhat.com>
---
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