[PATCH] lxc: handle SIGCHLD from exiting container

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch allows the lxc driver to handle SIGCHLD signals from exiting
containers.  The handling will perform some cleanup such as waiting for
the container process and killing/waiting the tty process.  This is also
required as a first step towards providing some kind of client container exit
notification.  Additional support is needed for that but this SIGCHLD handling
is what would trigger the notification.

libvirtd was already catching SIGCHLD although it was just ignoring it.  I
implemented a mechanism to distribute the signal to any other drivers in the
daemon that registered a function to handle them.  This required some changes to
the way libvirtd was catching signals (to get the pid of the sending process) as
well as an addition to the state driver structure.  The intent was to provide
future drivers access to signals as well.

-- 
Best Regards,
Dave Leskovec
IBM Linux Technology Center
Open Virtualization

---
 qemud/qemud.c           |   26 ++++++----
 src/driver.h            |    6 ++
 src/internal.h          |    2 
 src/libvirt.c           |   11 ++++
 src/libvirt_sym.version |    1 
 src/lxc_driver.c        |  114 +++++++++++++++++++++++++++++++++++-------------
 src/qemu_driver.c       |    1 
 src/remote_internal.c   |    1 
 8 files changed, 121 insertions(+), 41 deletions(-)

Index: b/qemud/qemud.c
===================================================================
--- a/qemud/qemud.c	2008-04-28 02:09:52.000000000 -0700
+++ b/qemud/qemud.c	2008-04-30 15:34:29.000000000 -0700
@@ -109,16 +109,16 @@
 static sig_atomic_t sig_errors = 0;
 static int sig_lasterrno = 0;
 
-static void sig_handler(int sig) {
-    unsigned char sigc = sig;
+static void sig_handler(int sig, siginfo_t * siginfo,
+                        void* context ATTRIBUTE_UNUSED) {
     int origerrno;
     int r;
 
-    if (sig == SIGCHLD) /* We explicitly waitpid the child later */
-        return;
+    /* set the sig num in the struct */
+    siginfo->si_signo = sig;
 
     origerrno = errno;
-    r = safewrite(sigwrite, &sigc, 1);
+    r = safewrite(sigwrite, siginfo, sizeof(*siginfo));
     if (r == -1) {
         sig_errors++;
         sig_lasterrno = errno;
@@ -232,10 +232,10 @@
                                      int events ATTRIBUTE_UNUSED,
                                      void *opaque) {
     struct qemud_server *server = (struct qemud_server *)opaque;
-    unsigned char sigc;
+    siginfo_t siginfo;
     int ret;
 
-    if (read(server->sigread, &sigc, 1) != 1) {
+    if (read(server->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) {
         qemudLog(QEMUD_ERR, _("Failed to read from signal pipe: %s"),
                  strerror(errno));
         return;
@@ -243,7 +243,7 @@
 
     ret = 0;
 
-    switch (sigc) {
+    switch (siginfo.si_signo) {
     case SIGHUP:
         qemudLog(QEMUD_INFO, "%s", _("Reloading configuration on SIGHUP"));
         if (virStateReload() < 0)
@@ -253,11 +253,15 @@
     case SIGINT:
     case SIGQUIT:
     case SIGTERM:
-        qemudLog(QEMUD_WARN, _("Shutting down on signal %d"), sigc);
+        qemudLog(QEMUD_WARN, _("Shutting down on signal %d"),
+                 siginfo.si_signo);
         server->shutdown = 1;
         break;
 
     default:
+        qemudLog(QEMUD_INFO, _("Received signal %d, dispatching to drivers"),
+                 siginfo.si_signo);
+        virStateSigDispatcher(&siginfo);
         break;
     }
 
@@ -2186,8 +2190,8 @@
             goto error1;
     }
 
-    sig_action.sa_handler = sig_handler;
-    sig_action.sa_flags = 0;
+    sig_action.sa_sigaction = sig_handler;
+    sig_action.sa_flags = SA_SIGINFO;
     sigemptyset(&sig_action.sa_mask);
 
     sigaction(SIGHUP, &sig_action, NULL);
Index: b/src/driver.h
===================================================================
--- a/src/driver.h	2008-04-10 09:54:54.000000000 -0700
+++ b/src/driver.h	2008-04-30 15:36:47.000000000 -0700
@@ -11,6 +11,10 @@
 
 #include <libxml/uri.h>
 
+#ifndef	_SIGNAL_H
+#include <signal.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -565,6 +569,7 @@
 typedef int (*virDrvStateCleanup) (void);
 typedef int (*virDrvStateReload) (void);
 typedef int (*virDrvStateActive) (void);
+typedef int (*virDrvSigHandler) (siginfo_t *siginfo);
 
 typedef struct _virStateDriver virStateDriver;
 typedef virStateDriver *virStateDriverPtr;
@@ -574,6 +579,7 @@
     virDrvStateCleanup     cleanup;
     virDrvStateReload      reload;
     virDrvStateActive      active;
+    virDrvSigHandler       sigHandler;
 };
 
 /*
Index: b/src/internal.h
===================================================================
--- a/src/internal.h	2008-04-28 14:44:54.000000000 -0700
+++ b/src/internal.h	2008-04-30 15:01:24.000000000 -0700
@@ -344,10 +344,12 @@
 int __virStateCleanup(void);
 int __virStateReload(void);
 int __virStateActive(void);
+int __virStateSigDispatcher(siginfo_t *siginfo);
 #define virStateInitialize() __virStateInitialize()
 #define virStateCleanup() __virStateCleanup()
 #define virStateReload() __virStateReload()
 #define virStateActive() __virStateActive()
+#define virStateSigDispatcher(s) __virStateSigDispatcher(s)
 
 int __virDrvSupportsFeature (virConnectPtr conn, int feature);
 
Index: b/src/libvirt.c
===================================================================
--- a/src/libvirt.c	2008-04-10 09:54:54.000000000 -0700
+++ b/src/libvirt.c	2008-04-30 15:01:24.000000000 -0700
@@ -607,6 +607,17 @@
     return ret;
 }
 
+int __virStateSigDispatcher(siginfo_t *siginfo) {
+    int i, ret = 0;
+
+    for (i = 0 ; i < virStateDriverTabCount ; i++) {
+        if (virStateDriverTab[i]->sigHandler &&
+            virStateDriverTab[i]->sigHandler(siginfo))
+            ret = 1;
+    }
+    return ret;
+}
+
 
 
 /**
Index: b/src/libvirt_sym.version
===================================================================
--- a/src/libvirt_sym.version	2008-04-28 08:14:59.000000000 -0700
+++ b/src/libvirt_sym.version	2008-04-30 15:01:24.000000000 -0700
@@ -170,6 +170,7 @@
 	__virStateCleanup;
 	__virStateReload;
 	__virStateActive;
+	__virStateSigDispatcher;
 
 	__virDrvSupportsFeature;
 
Index: b/src/lxc_driver.c
===================================================================
--- a/src/lxc_driver.c	2008-04-10 09:53:29.000000000 -0700
+++ b/src/lxc_driver.c	2008-04-30 17:37:09.000000000 -0700
@@ -841,55 +841,42 @@
 }
 
 /**
- * lxcDomainDestroy:
- * @dom: Ptr to domain to destroy
+ * lxcVmCleanup:
+ * @vm: Ptr to VM to clean up
  *
- * Sends SIGKILL to container root process to terminate the container
+ * waitpid() on the container process.  kill and wait the tty process
+ * This is called by boh lxcDomainDestroy and lxcSigHandler when a
+ * container exits.
  *
  * Returns 0 on success or -1 in case of error
  */
-static int lxcDomainDestroy(virDomainPtr dom)
+static int lxcVMCleanup(lxc_driver_t *driver, lxc_vm_t * vm)
 {
     int rc = -1;
     int waitRc;
-    lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData;
-    lxc_vm_t *vm = lxcFindVMByID(driver, dom->id);
-    int childStatus;
-
-    if (!vm) {
-        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
-                 _("no domain with id %d"), dom->id);
-        goto error_out;
-    }
-
-    if (0 > (kill(vm->def->id, SIGKILL))) {
-        if (ESRCH != errno) {
-            lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
-                     _("sending SIGKILL failed: %s"), strerror(errno));
-
-            goto error_out;
-        }
-    }
-
-    vm->state = VIR_DOMAIN_SHUTDOWN;
+    int childStatus = -1;
 
     while (((waitRc = waitpid(vm->def->id, &childStatus, 0)) == -1) &&
            errno == EINTR);
 
     if (waitRc != vm->def->id) {
-        lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                  _("waitpid failed to wait for container %d: %d %s"),
                  vm->def->id, waitRc, strerror(errno));
         goto kill_tty;
     }
 
-    rc = WEXITSTATUS(childStatus);
-    DEBUG("container exited with rc: %d", rc);
+    if (WIFEXITED(childStatus)) {
+        rc = WEXITSTATUS(childStatus);
+        DEBUG("container exited with rc: %d", rc);
+    }
+
+    rc = 0;
 
 kill_tty:
     if (0 > (kill(vm->pid, SIGKILL))) {
         if (ESRCH != errno) {
-            lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
+            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                      _("sending SIGKILL to tty process failed: %s"),
                      strerror(errno));
 
@@ -901,7 +888,7 @@
            errno == EINTR);
 
     if (waitRc != vm->pid) {
-        lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                  _("waitpid failed to wait for tty %d: %d %s"),
                  vm->pid, waitRc, strerror(errno));
     }
@@ -912,8 +899,43 @@
     vm->def->id = -1;
     driver->nactivevms--;
     driver->ninactivevms++;
+    lxcSaveConfig(NULL, driver, vm, vm->def);
 
-    rc = 0;
+    return rc;
+ }
+
+/**
+ * lxcDomainDestroy:
+ * @dom: Ptr to domain to destroy
+ *
+ * Sends SIGKILL to container root process to terminate the container
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int lxcDomainDestroy(virDomainPtr dom)
+{
+    int rc = -1;
+    lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByID(driver, dom->id);
+
+    if (!vm) {
+        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+                 _("no domain with id %d"), dom->id);
+        goto error_out;
+    }
+
+    if (0 > (kill(vm->def->id, SIGKILL))) {
+        if (ESRCH != errno) {
+            lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
+                     _("sending SIGKILL failed: %s"), strerror(errno));
+
+            goto error_out;
+        }
+    }
+
+    vm->state = VIR_DOMAIN_SHUTDOWN;
+
+    rc = lxcVMCleanup(driver, vm);
 
 error_out:
     return rc;
@@ -993,6 +1015,37 @@
     return 0;
 }
 
+/**
+ * lxcSigHandler:
+ * @siginfo: Pointer to siginfo_t structure
+ *
+ * Handles signals received by libvirtd.  Currently this is used to
+ * catch SIGCHLD from an exiting container.
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int lxcSigHandler(siginfo_t *siginfo)
+{
+    int rc = -1;
+    lxc_vm_t *vm;
+
+    if (siginfo->si_signo == SIGCHLD) {
+        vm = lxcFindVMByID(lxc_driver, siginfo->si_pid);
+
+        if (NULL == vm) {
+            DEBUG("Ignoring SIGCHLD from non-container process %d\n",
+                  siginfo->si_pid);
+            goto cleanup;
+        }
+
+        rc = lxcVMCleanup(lxc_driver, vm);
+
+    }
+
+cleanup:
+    return rc;
+}
+
 
 /* Function Tables */
 static virDriver lxcDriver = {
@@ -1061,6 +1114,7 @@
     lxcShutdown,
     NULL, /* reload */
     lxcActive,
+    lxcSigHandler
 };
 
 int lxcRegister(void)
Index: b/src/qemu_driver.c
===================================================================
--- a/src/qemu_driver.c	2008-04-25 13:46:13.000000000 -0700
+++ b/src/qemu_driver.c	2008-04-30 15:01:24.000000000 -0700
@@ -3133,6 +3133,7 @@
     qemudShutdown,
     qemudReload,
     qemudActive,
+    NULL
 };
 
 int qemudRegister(void) {
Index: b/src/remote_internal.c
===================================================================
--- a/src/remote_internal.c	2008-04-29 12:43:57.000000000 -0700
+++ b/src/remote_internal.c	2008-04-30 17:37:14.000000000 -0700
@@ -4794,6 +4794,7 @@
     NULL,
     NULL,
     NULL,
+    NULL
 };
 
 
--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]