]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
Make LXC I/O controller process a parent of the container process
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 13 Aug 2008 10:52:15 +0000 (10:52 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 13 Aug 2008 10:52:15 +0000 (10:52 +0000)
ChangeLog
configure.in
src/lxc_conf.c
src/lxc_conf.h
src/lxc_container.c
src/lxc_container.h
src/lxc_controller.c
src/lxc_controller.h
src/lxc_driver.c
src/util.c
src/util.h

index 88f206bf8afd1ffcabfb5e841d4fe83cf0974b1b..4b2883daf74393cb7d94f442bdb22043eb1b5396 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+Wed Aug 13 11:48:36 BST 2008 Daniel Berrange <berrange@redhat.com>
+
+       * configure.in: Add check for termios.h
+       * src/util.h, src/util.c: Add virFileOpenTty and helpers
+       for creating/deleting/reading PID files
+       * src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.c,
+       src/lxc_container.h, src/lxc_controller.c,
+       src/lxc_controller.h, src/lxc_driver.c: Re-arrange
+       container launch process so that the I/O helper is
+       a direct parent of the container process. Daemonize
+       container so it survives restarts of libvirtd.
+
+Wed Aug 13 11:23:36 BST 2008 Daniel Berrange <berrange@redhat.com>
+
+       Re-arrange code between LXC driver files
+       * src/lxc_container.c, src/lxc_container.h,
+       src/lxc_controller.h, src/lxc_container.c,
+       src/lxc_driver.c: Move code for I/O handling into
+       a seprate lxc_controller module, and move code for
+       creating containers into lcx_container module.
+
 Wed Aug 13 10:55:36 BST 2008 Daniel Berrange <berrange@redhat.com>
 
        * src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.h,
index 14dd776562617e3b67bc6a681061910013f74bbf..0513a72afa36ebe58a56ef0855d1f0c5cb72ce6d 100644 (file)
@@ -68,7 +68,7 @@ dnl Availability of various common functions (non-fatal if missing).
 AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity])
 
 dnl Availability of various common headers (non-fatal if missing).
-AC_CHECK_HEADERS([pwd.h paths.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h])
+AC_CHECK_HEADERS([pwd.h paths.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h])
 
 dnl Where are the XDR functions?
 dnl If portablexdr is installed, prefer that.
index 226df1889fb04dd314deea971e31b7f5d7b4fdf4..875651eaedb12a60d6d839136b9b044a9d429c29 100644 (file)
@@ -833,25 +833,24 @@ static lxc_vm_t * lxcLoadConfig(lxc_driver_t *driver,
     strncpy(vm->configFileBase, file, PATH_MAX);
     vm->configFile[PATH_MAX-1] = '\0';
 
-    if (lxcLoadTtyPid(driver, vm) < 0) {
-        DEBUG0("failed to load tty pid");
-    }
-
     return vm;
 }
 
 int lxcLoadDriverConfig(lxc_driver_t *driver)
 {
     /* Set the container configuration directory */
-    driver->configDir = strdup(SYSCONF_DIR "/libvirt/lxc");
-    if (NULL == driver->configDir) {
-        lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, "configDir");
-        return -1;
-    }
-
-    driver->stateDir = strdup(LOCAL_STATE_DIR "/run/libvirt/lxc");
+    if ((driver->configDir = strdup(SYSCONF_DIR "/libvirt/lxc")) == NULL)
+        goto no_memory;
+    if ((driver->stateDir = strdup(LOCAL_STATE_DIR "/run/libvirt/lxc")) == NULL)
+        goto no_memory;
+    if ((driver->logDir = strdup(LOCAL_STATE_DIR "/log/libvirt/lxc")) == NULL)
+        goto no_memory;
 
     return 0;
+
+no_memory:
+    lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, "configDir");
+    return -1;
 }
 
 int lxcLoadContainerConfigFile(lxc_driver_t *driver,
@@ -1012,9 +1011,7 @@ void lxcFreeVMDef(lxc_vm_def_t *vmdef)
     curNet = vmdef->nets;
     while (curNet) {
         nextNet = curNet->next;
-        printf("Freeing %s:%s\n", curNet->parentVeth, curNet->containerVeth);
         VIR_FREE(curNet->parentVeth);
-        VIR_FREE(curNet->containerVeth);
         VIR_FREE(curNet->txName);
         VIR_FREE(curNet);
         curNet = nextNet;
@@ -1106,176 +1103,4 @@ int lxcDeleteConfig(virConnectPtr conn,
     return 0;
 }
 
-/**
- * lxcStoreTtyPid:
- * @driver: pointer to driver
- * @vm: Ptr to VM
- *
- * Stores the pid of the tty forward process contained in vm->pid
- * LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid
- *
- * Returns 0 on success or -1 in case of error
- */
-int lxcStoreTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm)
-{
-    int rc = -1;
-    int fd;
-    FILE *file = NULL;
-
-    if (vm->ttyPidFile[0] == 0x00) {
-        if ((rc = virFileMakePath(driver->stateDir))) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("cannot create lxc state directory %s: %s"),
-                     driver->stateDir, strerror(rc));
-            goto error_out;
-        }
-
-        if (virFileBuildPath(driver->stateDir, vm->def->name, ".pid",
-                             vm->ttyPidFile, PATH_MAX) < 0) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("cannot construct tty pid file path"));
-            goto error_out;
-        }
-    }
-
-    if ((fd = open(vm->ttyPidFile,
-                   O_WRONLY | O_CREAT | O_TRUNC,
-                   S_IRUSR | S_IWUSR)) < 0) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("cannot create tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-        goto error_out;
-    }
-
-    if (!(file = fdopen(fd, "w"))) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("cannot fdopen tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-
-        if (close(fd) < 0) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("failed to close tty pid file %s: %s"),
-                     vm->ttyPidFile, strerror(errno));
-        }
-
-        goto error_out;
-    }
-
-    if (fprintf(file, "%d", vm->pid) < 0) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("cannot write tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-
-        goto fclose_error_out;
-    }
-
-    rc = 0;
-
-fclose_error_out:
-    if (fclose(file) < 0) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("failed to close tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-    }
-
-error_out:
-    return rc;
-}
-
-/**
- * lxcLoadTtyPid:
- * @driver: pointer to driver
- * @vm: Ptr to VM
- *
- * Loads the pid of the tty forward process from the pid file.
- * LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid
- *
- * Returns
- * > 0 - pid of tty process
- *   0 - no tty pid file
- *  -1 - error
- */
-int lxcLoadTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm)
-{
-    int rc = -1;
-    FILE *file;
-
-    if (vm->ttyPidFile[0] == 0x00) {
-        if ((rc = virFileMakePath(driver->stateDir))) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("cannot create lxc state directory %s: %s"),
-                     driver->stateDir, strerror(rc));
-            goto cleanup;
-        }
-
-        if (virFileBuildPath(driver->stateDir, vm->def->name, ".pid",
-                             vm->ttyPidFile, PATH_MAX) < 0) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("cannot construct tty pid file path"));
-            goto cleanup;
-        }
-    }
-
-    if (!(file = fopen(vm->ttyPidFile, "r"))) {
-        if (ENOENT == errno) {
-            rc = 0;
-            goto cleanup;
-        }
-
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("cannot open tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-        goto cleanup;
-    }
-
-    if (fscanf(file, "%d", &(vm->pid)) < 0) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("cannot read tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-        goto cleanup;
-    }
-
-    if (fclose(file) < 0) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("failed to close tty pid file %s: %s"),
-                 vm->ttyPidFile, strerror(errno));
-        goto cleanup;
-    }
-
-    rc = vm->pid;
-
- cleanup:
-    return rc;
-}
-
-/**
- * lxcDeleteTtyPid:
- * @vm: Ptr to VM
- *
- * Unlinks the tty pid file for the vm
- * LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid
- *
- * Returns on 0 success or -1 in case of error
- */
-int lxcDeleteTtyPidFile(const lxc_vm_t *vm)
-{
-    if (vm->ttyPidFile[0] == 0x00) {
-        goto no_file;
-    }
-
-    if (unlink(vm->ttyPidFile) < 0) {
-        if (errno == ENOENT) {
-            goto no_file;
-        }
-
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("cannot remove ttyPidFile %s: %s"), vm->ttyPidFile,
-                 strerror(errno));
-        return -1;
-    }
-
-no_file:
-    return 0;
-}
-
 #endif /* WITH_LXC */
index be21560ed830852bc3b7c86b29c88b21ba7d32a0..b3fe86b36425e22b1c91fcf74cd46cbdcc779990 100644 (file)
@@ -46,7 +46,6 @@ typedef struct __lxc_net_def lxc_net_def_t;
 struct __lxc_net_def {
     int type;
     char *parentVeth;       /* veth device in parent namespace */
-    char *containerVeth;    /* veth device in container namespace */
     char *txName;           /* bridge or network name */
 
     lxc_net_def_t *next;
@@ -87,12 +86,11 @@ typedef struct __lxc_vm lxc_vm_t;
 struct __lxc_vm {
     int pid;
     int state;
+    int monitor;
 
     char configFile[PATH_MAX];
     char configFileBase[PATH_MAX];
 
-    char ttyPidFile[PATH_MAX];
-
     lxc_vm_def_t *def;
 
     lxc_vm_t *next;
@@ -103,8 +101,9 @@ struct __lxc_driver {
     lxc_vm_t *vms;
     int nactivevms;
     int ninactivevms;
-    char* configDir;
-    char* stateDir;
+    char *configDir;
+    char *stateDir;
+    char *logDir;
     int have_netns;
 };
 
@@ -154,9 +153,6 @@ int lxcDeleteConfig(virConnectPtr conn,
                     lxc_driver_t *driver,
                     const char *configFile,
                     const char *name);
-int lxcStoreTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm);
-int lxcLoadTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm);
-int lxcDeleteTtyPidFile(const lxc_vm_t *vm);
 
 void lxcError(virConnectPtr conn,
               virDomainPtr dom,
index 510a11305460aa6c6c775260bb2724981f95ea67..36ab654928999696afaa1c2766766a4a1d78aa61 100644 (file)
@@ -69,6 +69,8 @@ typedef char lxc_message_t;
 typedef struct __lxc_child_argv lxc_child_argv_t;
 struct __lxc_child_argv {
     lxc_vm_def_t *config;
+    unsigned int nveths;
+    char **veths;
     int monitor;
     char *ttyPath;
 };
@@ -171,8 +173,7 @@ error_out:
  *
  * Returns 0 on success or -1 in case of error
  */
-int lxcContainerSendContinue(virConnectPtr conn,
-                             int control)
+int lxcContainerSendContinue(int control)
 {
     int rc = -1;
     lxc_message_t msg = LXC_CONTINUE_MSG;
@@ -180,7 +181,7 @@ int lxcContainerSendContinue(virConnectPtr conn,
 
     writeCount = safewrite(control, &msg, sizeof(msg));
     if (writeCount != sizeof(msg)) {
-        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                  _("unable to send container continue message: %s"),
                  strerror(errno));
         goto error_out;
@@ -230,21 +231,22 @@ static int lxcContainerWaitForContinue(int control)
  *
  * Returns 0 on success or nonzero in case of error
  */
-static int lxcContainerEnableInterfaces(const lxc_vm_def_t *def)
+static int lxcContainerEnableInterfaces(unsigned int nveths,
+                                        char **veths)
 {
     int rc = 0;
-    const lxc_net_def_t *net;
+    unsigned int i;
 
-    for (net = def->nets; net; net = net->next) {
-        DEBUG("Enabling %s", net->containerVeth);
-        rc =  vethInterfaceUpOrDown(net->containerVeth, 1);
+    for (i = 0 ; i < nveths ; i++) {
+        DEBUG("Enabling %s", veths[i]);
+        rc =  vethInterfaceUpOrDown(veths[i], 1);
         if (0 != rc) {
             goto error_out;
         }
     }
 
     /* enable lo device only if there were other net devices */
-    if (def->nets)
+    if (veths)
         rc = vethInterfaceUpOrDown("lo", 1);
 
 error_out:
@@ -311,7 +313,7 @@ static int lxcContainerChild( void *data )
         return -1;
 
     /* enable interfaces */
-    if (lxcContainerEnableInterfaces(vmDef) < 0)
+    if (lxcContainerEnableInterfaces(argv->nveths, argv->veths) < 0)
         return -1;
 
     /* this function will only return if an error occured */
@@ -320,7 +322,6 @@ static int lxcContainerChild( void *data )
 
 /**
  * lxcContainerStart:
- * @conn: pointer to connection
  * @driver: pointer to driver structure
  * @vm: pointer to virtual machine structure
  *
@@ -328,8 +329,9 @@ static int lxcContainerChild( void *data )
  *
  * Returns PID of container on success or -1 in case of error
  */
-int lxcContainerStart(virConnectPtr conn,
-                      lxc_vm_def_t *def,
+int lxcContainerStart(lxc_vm_def_t *def,
+                      unsigned int nveths,
+                      char **veths,
                       int control,
                       char *ttyPath)
 {
@@ -337,12 +339,11 @@ int lxcContainerStart(virConnectPtr conn,
     int flags;
     int stacksize = getpagesize() * 4;
     char *stack, *stacktop;
-    lxc_child_argv_t args = { def, control, ttyPath };
+    lxc_child_argv_t args = { def, nveths, veths, control, ttyPath };
 
     /* allocate a stack for the container */
     if (VIR_ALLOC_N(stack, stacksize) < 0) {
-        lxcError(conn, NULL, VIR_ERR_NO_MEMORY,
-                 _("unable to allocate container stack"));
+        lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
         return -1;
     }
     stacktop = stack + stacksize;
@@ -357,7 +358,7 @@ int lxcContainerStart(virConnectPtr conn,
     DEBUG("clone() returned, %d", pid);
 
     if (pid < 0) {
-        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                  _("clone() failed, %s"), strerror(errno));
         return -1;
     }
index 715753ec38eed344dd4da973ce069b433c6c2b57..1b492a69ede1f0736e5b6aeb5df81e2b6d97d611 100644 (file)
@@ -32,11 +32,11 @@ enum {
     LXC_CONTAINER_FEATURE_NET = (1 << 0),
 };
 
-int lxcContainerSendContinue(virConnectPtr conn,
-                             int control);
+int lxcContainerSendContinue(int control);
 
-int lxcContainerStart(virConnectPtr conn,
-                      lxc_vm_def_t *def,
+int lxcContainerStart(lxc_vm_def_t *def,
+                      unsigned int nveths,
+                      char **veths,
                       int control,
                       char *ttyPath);
 
index 09af60579142163d13c1027d21667b826113b123..b2c07182a8eb9161ca804e7c39e37e8707f9696b 100644 (file)
 #ifdef WITH_LXC
 
 #include <sys/epoll.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
 #include <unistd.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <signal.h>
 
 #include "internal.h"
 #include "util.h"
 
 #include "lxc_conf.h"
+#include "lxc_container.h"
 #include "lxc_controller.h"
+#include "veth.h"
+#include "memory.h"
+#include "util.h"
 
 
 #define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
 
 /**
  * lxcFdForward:
@@ -91,7 +102,10 @@ typedef struct _lxcTtyForwardFd_t {
  *
  * Returns 0 on success or -1 in case of error
  */
-int lxcControllerMain(int appPty, int contPty)
+static int lxcControllerMain(int monitor,
+                             int client,
+                             int appPty,
+                             int contPty)
 {
     int rc = -1;
     int epollFd;
@@ -120,41 +134,77 @@ int lxcControllerMain(int appPty, int contPty)
     memset(&epollEvent, 0x00, sizeof(epollEvent));
     epollEvent.events = EPOLLIN|EPOLLET;    /* edge triggered */
     epollEvent.data.fd = appPty;
-    epollEvent.data.u32 = 0;                /* fdArray position */
     if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, appPty, &epollEvent)) {
         lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                  _("epoll_ctl(appPty) failed: %s"), strerror(errno));
         goto cleanup;
     }
     epollEvent.data.fd = contPty;
-    epollEvent.data.u32 = 1;                /* fdArray position */
     if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, contPty, &epollEvent)) {
         lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                  _("epoll_ctl(contPty) failed: %s"), strerror(errno));
         goto cleanup;
     }
 
+    epollEvent.events = EPOLLIN;
+    epollEvent.data.fd = monitor;
+    if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, monitor, &epollEvent)) {
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("epoll_ctl(contPty) failed: %s"), strerror(errno));
+        goto cleanup;
+    }
+
+    epollEvent.events = EPOLLHUP;
+    epollEvent.data.fd = client;
+    if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) {
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("epoll_ctl(contPty) failed: %s"), strerror(errno));
+        goto cleanup;
+    }
+
     while (1) {
         /* if active fd's, return if no events, else wait forever */
         timeout = (numActive > 0) ? 0 : -1;
         numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout);
-        if (0 < numEvents) {
-            if (epollEvent.events & EPOLLIN) {
-                curFdOff = epollEvent.data.u32;
-                if (!fdArray[curFdOff].active) {
-                    fdArray[curFdOff].active = 1;
-                    ++numActive;
+        if (numEvents > 0) {
+            if (epollEvent.data.fd == monitor) {
+                int fd = accept(monitor, NULL, 0);
+                if (client != -1) { /* Already connected, so kick new one out */
+                    close(fd);
+                    continue;
                 }
-
-            } else if (epollEvent.events & EPOLLHUP) {
-                DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
-                continue;
+                client = fd;
+                epollEvent.events = EPOLLHUP;
+                epollEvent.data.fd = client;
+                if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) {
+                    lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("epoll_ctl(contPty) failed: %s"), strerror(errno));
+                    goto cleanup;
+                }
+            } else if (client != -1 && epollEvent.data.fd == client) {
+                if (0 > epoll_ctl(epollFd, EPOLL_CTL_DEL, client, &epollEvent)) {
+                    lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("epoll_ctl(contPty) failed: %s"), strerror(errno));
+                    goto cleanup;
+                }
+                close(client);
+                client = -1;
             } else {
-                lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                         _("error event %d"), epollEvent.events);
-                goto cleanup;
+                if (epollEvent.events & EPOLLIN) {
+                    curFdOff = epollEvent.data.fd == appPty ? 0 : 1;
+                    if (!fdArray[curFdOff].active) {
+                        fdArray[curFdOff].active = 1;
+                        ++numActive;
+                    }
+                } else if (epollEvent.events & EPOLLHUP) {
+                    DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
+                    continue;
+                } else {
+                    lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("error event %d"), epollEvent.events);
+                    goto cleanup;
+                }
             }
-
         } else if (0 == numEvents) {
             if (2 == numActive) {
                 /* both fds active, toggle between the two */
@@ -202,4 +252,255 @@ cleanup:
     return rc;
 }
 
+
+
+/**
+ * lxcControllerMoveInterfaces
+ * @nveths: number of interfaces
+ * @veths: interface names
+ * @container: pid of container
+ *
+ * Moves network interfaces into a container's namespace
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int lxcControllerMoveInterfaces(unsigned int nveths,
+                                       char **veths,
+                                       pid_t container)
+{
+    unsigned int i;
+    for (i = 0 ; i < nveths ; i++)
+        if (moveInterfaceToNetNs(veths[i], container) < 0) {
+            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("failed to move interface %s to ns %d"),
+                     veths[i], container);
+            return -1;
+        }
+
+    return 0;
+}
+
+
+/**
+ * lxcCleanupInterfaces:
+ * @conn: pointer to connection
+ * @vm: pointer to virtual machine structure
+ *
+ * Cleans up the container interfaces by deleting the veth device pairs.
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int lxcControllerCleanupInterfaces(unsigned int nveths,
+                                          char **veths)
+{
+    unsigned int i;
+    for (i = 0 ; i < nveths ; i++)
+        if (vethDelete(veths[i]) < 0)
+            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("failed to delete veth: %s"), veths[i]);
+            /* will continue to try to cleanup any other interfaces */
+
+    return 0;
+}
+
+
+static int
+lxcControllerRun(const char *stateDir,
+                 lxc_vm_def_t *def,
+                 unsigned int nveths,
+                 char **veths,
+                 int monitor,
+                 int client,
+                 int appPty)
+{
+    int rc = -1;
+    int control[2] = { -1, -1};
+    int containerPty;
+    char *containerPtyPath;
+    pid_t container = -1;
+
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) {
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("sockpair failed: %s"), strerror(errno));
+        goto cleanup;
+    }
+
+    if (virFileOpenTty(&containerPty,
+                       &containerPtyPath,
+                       0) < 0) {
+        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("failed to allocate tty: %s"), strerror(errno));
+        goto cleanup;
+    }
+
+    if ((container = lxcContainerStart(def,
+                                       nveths,
+                                       veths,
+                                       control[1],
+                                       containerPtyPath)) < 0)
+        goto cleanup;
+    close(control[1]);
+    control[1] = -1;
+
+    if (lxcControllerMoveInterfaces(nveths, veths, container) < 0)
+        goto cleanup;
+
+    if (lxcContainerSendContinue(control[0]) < 0)
+        goto cleanup;
+
+    rc = lxcControllerMain(monitor, client, appPty, containerPty);
+
+cleanup:
+    if (control[0] != -1)
+        close(control[0]);
+    if (control[1] != -1)
+        close(control[1]);
+    VIR_FREE(containerPtyPath);
+    if (containerPty != -1)
+        close(containerPty);
+
+    kill(container, SIGTERM);
+    waitpid(container, NULL, 0);
+    lxcControllerCleanupInterfaces(nveths, veths);
+    virFileDeletePid(stateDir, def->name);
+    return rc;
+}
+
+
+int lxcControllerStart(const char *stateDir,
+                       lxc_vm_def_t *def,
+                       unsigned int nveths,
+                       char **veths,
+                       int monitor,
+                       int appPty,
+                       int logfd)
+{
+    pid_t pid;
+    int rc;
+    int status, null;
+    int open_max, i;
+    int client;
+    struct sigaction sig_action;
+
+    if ((pid = fork()) < 0)
+        return -1;
+
+    if (pid > 0) {
+        /* Original caller waits for first child to exit */
+        while (1) {
+            rc = waitpid(pid, &status, 0);
+            if (rc < 0) {
+                if (errno == EINTR)
+                    continue;
+                return -1;
+            }
+            if (rc != pid) {
+                fprintf(stderr,
+                        _("Unexpected pid %d != %d from waitpid\n"),
+                        rc, pid);
+                return -1;
+            }
+            if (WIFEXITED(status) &&
+                WEXITSTATUS(status) == 0)
+                return 0;
+            else {
+                fprintf(stderr,
+                        _("Unexpected status %d from pid %d\n"),
+                        status, pid);
+                return -1;
+            }
+        }
+    }
+
+    /* First child is running here */
+
+    /* Clobber all libvirtd's signal handlers so they
+     * don't affect us
+     */
+    sig_action.sa_handler = SIG_DFL;
+    sig_action.sa_flags = 0;
+    sigemptyset(&sig_action.sa_mask);
+
+    sigaction(SIGHUP, &sig_action, NULL);
+    sigaction(SIGINT, &sig_action, NULL);
+    sigaction(SIGQUIT, &sig_action, NULL);
+    sigaction(SIGTERM, &sig_action, NULL);
+    sigaction(SIGCHLD, &sig_action, NULL);
+
+    sig_action.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &sig_action, NULL);
+
+
+    /* Don't hold onto any cwd we inherit from libvirtd either */
+    if (chdir("/") < 0) {
+        fprintf(stderr, _("Unable to change to root dir: %s\n"),
+                strerror(errno));
+        _exit(-1);
+    }
+
+    if (setsid() < 0) {
+        fprintf(stderr, _("Unable to become session leader: %s\n"),
+                strerror(errno));
+        _exit(-1);
+    }
+
+    if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) {
+        fprintf(stderr, _("Unable to open %s: %s\n"),
+                _PATH_DEVNULL, strerror(errno));
+        _exit(-1);
+    }
+
+    open_max = sysconf (_SC_OPEN_MAX);
+    for (i = 0; i < open_max; i++)
+        if (i != appPty &&
+            i != monitor &&
+            i != logfd &&
+            i != null)
+            close(i);
+
+    if (dup2(null, STDIN_FILENO) < 0 ||
+        dup2(logfd, STDOUT_FILENO) < 0 ||
+        dup2(logfd, STDERR_FILENO) < 0) {
+        fprintf(stderr, _("Unable to redirect stdio: %s\n"),
+                strerror(errno));
+        _exit(-1);
+    }
+
+    close(null);
+    close(logfd);
+
+    /* Now fork the real controller process */
+    if ((pid = fork()) < 0) {
+        fprintf(stderr, _("Unable to fork controller: %s\n"),
+                strerror(errno));
+        _exit(-1);
+    }
+
+    if (pid > 0) {
+        if ((rc = virFileWritePid(stateDir, def->name, pid)) != 0) {
+            fprintf(stderr, _("Unable to write pid file: %s\n"),
+                    strerror(rc));
+            _exit(-1);
+        }
+        /* First child now exits, allowing originall caller to
+         * complete their waitpid & continue */
+        _exit(0);
+    }
+
+    /* This is real controller running finally... */
+
+    /* Accept initial client which is the libvirtd daemon */
+    if ((client = accept(monitor, NULL, 0))) {
+        fprintf(stderr, _("Failed connection from LXC driver: %s\n"),
+                strerror(errno));
+        _exit(-1);
+    }
+
+    /* Controlling libvirtd LXC driver now knows
+       what our PID is, and is able to cleanup after
+       us from now on */
+    _exit(lxcControllerRun(stateDir, def, nveths, veths, monitor, client, appPty));
+}
+
+
 #endif
index edaf53ada745ecc025ac69826a028dbfa92a6a41..d390dc27613dfe97946a17ca885ce7d2424358c7 100644 (file)
 
 #ifdef WITH_LXC
 
-int lxcControllerMain(int appPty, int contPty);
+#include "lxc_conf.h"
+
+int lxcControllerStart(const char *stateDir,
+                       lxc_vm_def_t *def,
+                       unsigned int nveths,
+                       char **veths,
+                       int monitor,
+                       int appPty,
+                       int logfd);
 
 #endif /* WITH_LXC */
 
index 4eb9aba1c77403d796378a9d1c3e906540d3056a..515bfd76d67ba1a079d59a9c2d2655afad04ecfb 100644 (file)
 #include <stdbool.h>
 #include <string.h>
 #include <sys/types.h>
-#include <termios.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
 #include <unistd.h>
 #include <wait.h>
 
+#include "internal.h"
 #include "lxc_conf.h"
 #include "lxc_container.h"
 #include "lxc_driver.h"
 #include "lxc_controller.h"
-#include "driver.h"
-#include "internal.h"
 #include "memory.h"
 #include "util.h"
-#include "memory.h"
 #include "bridge.h"
-#include "qemu_conf.h"
 #include "veth.h"
+#include "event.h"
+
 
 /* debug macros */
 #define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
@@ -284,8 +285,6 @@ static int lxcDomainUndefine(virDomainPtr dom)
 
     vm->configFile[0] = '\0';
 
-    lxcDeleteTtyPidFile(vm);
-
     lxcRemoveInactiveVM(driver, vm);
 
     return 0;
@@ -339,10 +338,60 @@ static char *lxcDomainDumpXML(virDomainPtr dom,
     return lxcGenerateXML(dom->conn, driver, vm, vm->def);
 }
 
+
+/**
+ * lxcVmCleanup:
+ * @vm: Ptr to VM to clean up
+ *
+ * waitpid() on the container process.  kill and wait the tty process
+ * This is called by both lxcDomainDestroy and lxcSigHandler when a
+ * container exits.
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int lxcVMCleanup(virConnectPtr conn,
+                        lxc_driver_t *driver,
+                        lxc_vm_t * vm)
+{
+    int rc = -1;
+    int waitRc;
+    int childStatus = -1;
+
+    while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) &&
+           errno == EINTR)
+        ; /* empty */
+
+    if ((waitRc != vm->pid) && (errno != ECHILD)) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("waitpid failed to wait for container %d: %d %s"),
+                 vm->pid, waitRc, strerror(errno));
+    }
+
+    rc = 0;
+
+    if (WIFEXITED(childStatus)) {
+        rc = WEXITSTATUS(childStatus);
+        DEBUG("container exited with rc: %d", rc);
+    }
+
+    virEventRemoveHandle(vm->monitor);
+    close(vm->monitor);
+
+    virFileDeletePid(driver->stateDir, vm->def->name);
+
+    vm->state = VIR_DOMAIN_SHUTOFF;
+    vm->pid = -1;
+    vm->def->id = -1;
+    vm->monitor = -1;
+    driver->nactivevms--;
+    driver->ninactivevms++;
+
+    return rc;
+}
+
 /**
  * lxcSetupInterfaces:
- * @conn: pointer to connection
- * @vm: pointer to virtual machine structure
+ * @def: pointer to virtual machine structure
  *
  * Sets up the container interfaces by creating the veth device pairs and
  * attaching the parent end to the appropriate bridge.  The container end
@@ -351,24 +400,21 @@ static char *lxcDomainDumpXML(virDomainPtr dom,
  * Returns 0 on success or -1 in case of error
  */
 static int lxcSetupInterfaces(virConnectPtr conn,
-                              lxc_vm_t *vm)
+                              lxc_vm_def_t *def,
+                              unsigned int *nveths,
+                              char ***veths)
 {
     int rc = -1;
-    lxc_driver_t *driver = conn->privateData;
-    struct qemud_driver *networkDriver =
-        (struct qemud_driver *)(conn->networkPrivateData);
-    lxc_net_def_t *net = vm->def->nets;
-    char* bridge;
+    lxc_net_def_t *net;
+    char *bridge = NULL;
     char parentVeth[PATH_MAX] = "";
     char containerVeth[PATH_MAX] = "";
+    brControl *brctl = NULL;
 
-    if ((vm->def->nets != NULL) && (driver->have_netns == 0)) {
-        lxcError(conn, NULL, VIR_ERR_NO_SUPPORT,
-                 _("System lacks NETNS support"));
+    if (brInit(&brctl) != 0)
         return -1;
-    }
 
-    for (net = vm->def->nets; net; net = net->next) {
+    for (net = def->nets; net; net = net->next) {
         if (LXC_NET_NETWORK == net->type) {
             virNetworkPtr network = virNetworkLookupByName(conn, net->txName);
             if (!network) {
@@ -378,7 +424,6 @@ static int lxcSetupInterfaces(virConnectPtr conn,
             bridge = virNetworkGetBridgeName(network);
 
             virNetworkFree(network);
-
         } else {
             bridge = net->txName;
         }
@@ -394,9 +439,6 @@ static int lxcSetupInterfaces(virConnectPtr conn,
         if (NULL != net->parentVeth) {
             strcpy(parentVeth, net->parentVeth);
         }
-        if (NULL != net->containerVeth) {
-            strcpy(containerVeth, net->containerVeth);
-        }
         DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
         if (0 != (rc = vethCreate(parentVeth, PATH_MAX, containerVeth, PATH_MAX))) {
             lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
@@ -406,24 +448,18 @@ static int lxcSetupInterfaces(virConnectPtr conn,
         if (NULL == net->parentVeth) {
             net->parentVeth = strdup(parentVeth);
         }
-        if (NULL == net->containerVeth) {
-            net->containerVeth = strdup(containerVeth);
-        }
-
-        if ((NULL == net->parentVeth) || (NULL == net->containerVeth)) {
-            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("failed to allocate veth names"));
+        if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0)
+            goto error_exit;
+        if (((*veths)[(*nveths)++] = strdup(containerVeth)) == NULL)
             goto error_exit;
-        }
 
-        if (!(networkDriver->brctl) && (rc = brInit(&(networkDriver->brctl)))) {
-            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("cannot initialize bridge support: %s"),
-                     strerror(rc));
+        if (NULL == net->parentVeth) {
+            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("failed to allocate veth names"));
             goto error_exit;
         }
 
-        if (0 != (rc = brAddInterface(networkDriver->brctl, bridge, parentVeth))) {
+        if (0 != (rc = brAddInterface(brctl, bridge, parentVeth))) {
             lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                      _("failed to add %s device to %s: %s"),
                      parentVeth,
@@ -433,7 +469,7 @@ static int lxcSetupInterfaces(virConnectPtr conn,
         }
 
         if (0 != (rc = vethInterfaceUpOrDown(parentVeth, 1))) {
-            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                      _("failed to enable parent ns veth device: %d"), rc);
             goto error_exit;
         }
@@ -443,136 +479,144 @@ static int lxcSetupInterfaces(virConnectPtr conn,
     rc = 0;
 
 error_exit:
+    brShutdown(brctl);
     return rc;
 }
 
-/**
- * lxcMoveInterfacesToNetNs:
- * @conn: pointer to connection
- * @vm: pointer to virtual machine structure
- *
- * Starts a container process by calling clone() with the namespace flags
- *
- * Returns 0 on success or -1 in case of error
- */
-static int lxcMoveInterfacesToNetNs(virConnectPtr conn,
-                                    const lxc_vm_t *vm)
+static int lxcMonitorServer(virConnectPtr conn,
+                            lxc_driver_t * driver,
+                            lxc_vm_t *vm)
 {
-    int rc = -1;
-    lxc_net_def_t *net;
+    char *sockpath = NULL;
+    int fd;
+    struct sockaddr_un addr;
 
-    for (net = vm->def->nets; net; net = net->next) {
-        if (0 != moveInterfaceToNetNs(net->containerVeth, vm->def->id)) {
-            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("failed to move interface %s to ns %d"),
-                     net->containerVeth, vm->def->id);
-            goto error_exit;
-        }
+    if (asprintf(&sockpath, "%s/%s.sock",
+                 driver->stateDir, vm->def->name) < 0) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
     }
 
-    rc = 0;
-
-error_exit:
-    return rc;
-}
+    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("failed to create server socket: %s"),
+                 strerror(errno));
+        goto error;
+    }
 
-/**
- * lxcCleanupInterfaces:
- * @conn: pointer to connection
- * @vm: pointer to virtual machine structure
- *
- * Cleans up the container interfaces by deleting the veth device pairs.
- *
- * Returns 0 on success or -1 in case of error
- */
-static int lxcCleanupInterfaces(const lxc_vm_t *vm)
-{
-    int rc = -1;
-    lxc_net_def_t *net;
+    unlink(sockpath);
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
 
-    for (net = vm->def->nets; net; net = net->next) {
-        if (0 != (rc = vethDelete(net->parentVeth))) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("failed to delete veth: %s"), net->parentVeth);
-            /* will continue to try to cleanup any other interfaces */
-        }
+    if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("failed to bind server socket: %s"),
+                 strerror(errno));
+        goto error;
+    }
+    if (listen(fd, 30 /* backlog */ ) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("failed to listen server socket: %s"),
+                 strerror(errno));
+        goto error;
+        return (-1);
     }
 
-    return 0;
-}
+    VIR_FREE(sockpath);
+    return fd;
 
+error:
+    VIR_FREE(sockpath);
+    if (fd != -1)
+        close(fd);
+    return -1;
+}
 
-/**
- * lxcOpenTty:
- * @conn: pointer to connection
- * @ttymaster: pointer to int.  On success, set to fd for master end
- * @ttyName: On success, will point to string slave end of tty.  Caller
- * must free when done (such as in lxcFreeVM).
- *
- * Opens and configures container tty.
- *
- * Returns 0 on success or -1 in case of error
- */
-static int lxcOpenTty(virConnectPtr conn,
-                      int *ttymaster,
-                      char **ttyName,
-                      int rawmode)
+static int lxcMonitorClient(virConnectPtr conn,
+                            lxc_driver_t * driver,
+                            lxc_vm_t *vm)
 {
-    int rc = -1;
+    char *sockpath = NULL;
+    int fd;
+    struct sockaddr_un addr;
 
-    *ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK);
-    if (*ttymaster < 0) {
+    if (asprintf(&sockpath, "%s/%s.sock",
+                 driver->stateDir, vm->def->name) < 0) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
+
+    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
         lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("posix_openpt failed: %s"), strerror(errno));
-        goto cleanup;
+                 _("failed to create client socket: %s"),
+                 strerror(errno));
+        goto error;
     }
 
-    if (unlockpt(*ttymaster) < 0) {
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
+
+    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
         lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("unlockpt failed: %s"), strerror(errno));
-        goto cleanup;
+                 _("failed to connect to client socket: %s"),
+                 strerror(errno));
+        goto error;
     }
 
-    if (rawmode) {
-        struct termios ttyAttr;
-        if (tcgetattr(*ttymaster, &ttyAttr) < 0) {
-            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                     "tcgetattr() failed: %s", strerror(errno));
-            goto cleanup;
-        }
+    VIR_FREE(sockpath);
+    return fd;
 
-        cfmakeraw(&ttyAttr);
+error:
+    VIR_FREE(sockpath);
+    if (fd != -1)
+        close(fd);
+    return -1;
+}
+
+
+static int lxcVmTerminate(virConnectPtr conn,
+                          lxc_driver_t *driver,
+                          lxc_vm_t *vm,
+                          int signum)
+{
+    if (signum == 0)
+        signum = SIGINT;
 
-        if (tcsetattr(*ttymaster, TCSADRAIN, &ttyAttr) < 0) {
+    if (kill(vm->pid, signum) < 0) {
+        if (errno != ESRCH) {
             lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                     "tcsetattr failed: %s", strerror(errno));
-            goto cleanup;
+                     _("failed to kill pid %d: %s"),
+                     vm->pid, strerror(errno));
+            return -1;
         }
     }
 
-    if (ttyName) {
-        char tempTtyName[PATH_MAX];
-        if (0 != ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName))) {
-            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("ptsname_r failed: %s"), strerror(errno));
-            goto cleanup;
-        }
+    vm->state = VIR_DOMAIN_SHUTDOWN;
 
-        if ((*ttyName = strdup(tempTtyName)) == NULL) {
-            lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
-            goto cleanup;
-        }
-    }
+    return lxcVMCleanup(conn, driver, vm);
+}
 
-    rc = 0;
+static void lxcMonitorEvent(int fd,
+                            int events ATTRIBUTE_UNUSED,
+                            void *data)
+{
+    lxc_driver_t *driver = data;
+    lxc_vm_t *vm = driver->vms;
 
-cleanup:
-    if (rc != 0 &&
-        *ttymaster != -1) {
-        close(*ttymaster);
+    while (vm) {
+        if (vm->monitor == fd)
+            break;
+        vm = vm->next;
+    }
+    if (!vm) {
+        virEventRemoveHandle(fd);
+        return;
     }
 
-    return rc;
+    if (lxcVmTerminate(NULL, driver, vm, SIGINT) < 0)
+        virEventRemoveHandle(fd);
 }
 
 
@@ -591,80 +635,106 @@ static int lxcVmStart(virConnectPtr conn,
                       lxc_vm_t * vm)
 {
     int rc = -1;
-    int sockpair[2] = { -1, -1 };
-    int containerTty, parentTty;
-    char *containerTtyPath = NULL;
+    unsigned int i;
+    int monitor;
+    int parentTty;
+    char *logfile = NULL;
+    int logfd = -1;
+    unsigned int nveths = 0;
+    char **veths = NULL;
+
+    if (virFileMakePath(driver->logDir) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("cannot create log directory %s: %s"),
+                 driver->logDir, strerror(rc));
+        return -1;
+    }
 
-    /* open parent tty */
-    VIR_FREE(vm->def->tty);
-    if (lxcOpenTty(conn, &parentTty, &vm->def->tty, 1) < 0) {
-        goto cleanup;
+    if (asprintf(&logfile, "%s/%s.log",
+                 driver->logDir, vm->def->name) < 0) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
     }
 
-    /* open container tty */
-    if (lxcOpenTty(conn, &containerTty, &containerTtyPath, 0) < 0) {
+    if ((monitor = lxcMonitorServer(conn, driver, vm)) < 0)
         goto cleanup;
-    }
 
-    /* fork process to handle the tty io forwarding */
-    if ((vm->pid = fork()) < 0) {
+    /* open parent tty */
+    VIR_FREE(vm->def->tty);
+    if (virFileOpenTty(&parentTty, &vm->def->tty, 1) < 0) {
         lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("unable to fork tty forwarding process: %s"),
+                 _("failed to allocate tty: %s"),
                  strerror(errno));
         goto cleanup;
     }
 
-    if (vm->pid  == 0) {
-        /* child process calls forward routine */
-        lxcControllerMain(parentTty, containerTty);
-    }
-
-    if (lxcStoreTtyPid(driver, vm)) {
-        DEBUG0("unable to store tty pid");
-    }
-
-    close(parentTty);
-    close(containerTty);
-
-    if (0 != (rc = lxcSetupInterfaces(conn, vm))) {
+    if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0)
         goto cleanup;
-    }
 
-    /* create a socket pair to send continue message to the container once */
-    /* we've completed the post clone configuration */
-    if (0 != socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair)) {
+    if ((logfd = open(logfile, O_WRONLY | O_TRUNC | O_CREAT,
+             S_IRUSR|S_IWUSR)) < 0) {
         lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("sockpair failed: %s"), strerror(errno));
+                 _("failed to open %s: %s"), logfile,
+                 strerror(errno));
         goto cleanup;
     }
 
-    /* check this rc */
-
-    vm->def->id = lxcContainerStart(conn,
-                                    vm->def,
-                                    sockpair[1],
-                                    containerTtyPath);
-    if (vm->def->id == -1)
+    if (lxcControllerStart(driver->stateDir,
+                           vm->def, nveths, veths,
+                           monitor, parentTty, logfd) < 0)
         goto cleanup;
-    lxcSaveConfig(conn, driver, vm, vm->def);
-
-    rc = lxcMoveInterfacesToNetNs(conn, vm);
-    if (rc != 0)
+    /* Close the server side of the monitor, now owned
+     * by the controller process */
+    close(monitor);
+    monitor = -1;
+
+    /* Connect to the controller as a client *first* because
+     * this will block until the child has written their
+     * pid file out to disk */
+    if ((vm->monitor = lxcMonitorClient(conn, driver, vm)) < 0)
         goto cleanup;
 
-    rc = lxcContainerSendContinue(conn, sockpair[0]);
-    if (rc != 0)
+    /* And get its pid */
+    if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) != 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("Failed to read pid file %s/%s.pid: %s"),
+                 driver->stateDir, vm->def->name, strerror(rc));
+        rc = -1;
         goto cleanup;
+    }
 
+    vm->def->id = vm->pid;
     vm->state = VIR_DOMAIN_RUNNING;
     driver->ninactivevms--;
     driver->nactivevms++;
 
-cleanup:
-    if (sockpair[0] != -1) close(sockpair[0]);
-    if (sockpair[1] != -1) close(sockpair[1]);
-    VIR_FREE(containerTtyPath);
+    if (virEventAddHandle(vm->monitor,
+                          POLLERR | POLLHUP,
+                          lxcMonitorEvent,
+                          driver) < 0) {
+        lxcVmTerminate(conn, driver, vm, 0);
+        goto cleanup;
+    }
 
+    rc = 0;
+
+cleanup:
+    for (i = 0 ; i < nveths ; i++) {
+        if (rc != 0)
+            vethDelete(veths[i]);
+        VIR_FREE(veths[i]);
+    }
+    if (monitor != -1)
+        close(monitor);
+    if (rc != 0 && vm->monitor != -1) {
+        close(vm->monitor);
+        vm->monitor = -1;
+    }
+    if (parentTty != -1)
+        close(parentTty);
+    if (logfd != -1)
+        close(logfd);
+    VIR_FREE(logfile);
     return rc;
 }
 
@@ -752,105 +822,18 @@ return_point:
  */
 static int lxcDomainShutdown(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, SIGINT))) {
-        if (ESRCH != errno) {
-            lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
-                     _("sending SIGTERM failed: %s"), strerror(errno));
-
-            goto error_out;
-        }
+        return -1;
     }
 
-    vm->state = VIR_DOMAIN_SHUTDOWN;
-
-    rc = 0;
-
-error_out:
-    return rc;
+    return lxcVmTerminate(dom->conn, driver, vm, 0);
 }
 
-/**
- * lxcVmCleanup:
- * @vm: Ptr to VM to clean up
- *
- * 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 lxcVMCleanup(lxc_driver_t *driver, lxc_vm_t * vm)
-{
-    int rc = -1;
-    int waitRc;
-    int childStatus = -1;
-
-    /* if this fails, we'll continue.  it will report any errors */
-    lxcCleanupInterfaces(vm);
-
-    while (((waitRc = waitpid(vm->def->id, &childStatus, 0)) == -1) &&
-           errno == EINTR);
-
-    if ((waitRc != vm->def->id) && (errno != ECHILD)) {
-        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 = 0;
-
-    if (WIFEXITED(childStatus)) {
-        rc = WEXITSTATUS(childStatus);
-        DEBUG("container exited with rc: %d", rc);
-    }
-
-kill_tty:
-    if (2 > vm->pid) {
-        DEBUG("not killing tty process with pid %d", vm->pid);
-        goto tty_error_out;
-    }
-
-    if (0 > (kill(vm->pid, SIGKILL))) {
-        if (ESRCH != errno) {
-            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                     _("sending SIGKILL to tty process failed: %s"),
-                     strerror(errno));
-
-            goto tty_error_out;
-        }
-    }
-
-    while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) &&
-           errno == EINTR);
-
-    if ((waitRc != vm->pid) && (errno != ECHILD)) {
-        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                 _("waitpid failed to wait for tty %d: %d %s"),
-                 vm->pid, waitRc, strerror(errno));
-    }
-
-tty_error_out:
-    vm->state = VIR_DOMAIN_SHUTOFF;
-    vm->pid = -1;
-    lxcDeleteTtyPidFile(vm);
-    vm->def->id = -1;
-    driver->nactivevms--;
-    driver->ninactivevms++;
-    lxcSaveConfig(NULL, driver, vm, vm->def);
-
-    return rc;
- }
 
 /**
  * lxcDomainDestroy:
@@ -862,31 +845,16 @@ tty_error_out:
  */
 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;
-        }
+        return -1;
     }
 
-    vm->state = VIR_DOMAIN_SHUTDOWN;
-
-    rc = lxcVMCleanup(driver, vm);
-
-error_out:
-    return rc;
+    return lxcVmTerminate(dom->conn, driver, vm, SIGKILL);
 }
 
 static int lxcCheckNetNsSupport(void)
@@ -907,6 +875,7 @@ static int lxcCheckNetNsSupport(void)
 static int lxcStartup(void)
 {
     uid_t uid = getuid();
+    lxc_vm_t *vm;
 
     /* Check that the user is root */
     if (0 != uid) {
@@ -935,6 +904,36 @@ static int lxcStartup(void)
         return -1;
     }
 
+    vm = lxc_driver->vms;
+    while (vm) {
+        int rc;
+        if ((vm->monitor = lxcMonitorClient(NULL, lxc_driver, vm)) < 0) {
+            vm = vm->next;
+            continue;
+        }
+
+        /* Read pid from controller */
+        if ((rc = virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) {
+            close(vm->monitor);
+            vm->monitor = -1;
+            vm = vm->next;
+            continue;
+        }
+
+        if (vm->pid != 0) {
+            vm->def->id = vm->pid;
+            vm->state = VIR_DOMAIN_RUNNING;
+            lxc_driver->ninactivevms--;
+            lxc_driver->nactivevms++;
+        } else {
+            vm->def->id = -1;
+            close(vm->monitor);
+            vm->monitor = -1;
+        }
+
+        vm = vm->next;
+    }
+
     return 0;
 }
 
@@ -942,6 +941,7 @@ static void lxcFreeDriver(lxc_driver_t *driver)
 {
     VIR_FREE(driver->configDir);
     VIR_FREE(driver->stateDir);
+    VIR_FREE(driver->logDir);
     VIR_FREE(driver);
 }
 
@@ -978,37 +978,6 @@ lxcActive(void) {
     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 = {
@@ -1079,7 +1048,7 @@ static virStateDriver lxcStateDriver = {
     lxcShutdown,
     NULL, /* reload */
     lxcActive,
-    lxcSigHandler
+    NULL,
 };
 
 int lxcRegister(void)
index fe701cf382d2a75941f4bba385dec791bdeb7f41..a4ae78a4e0b8aea426c4c2c932c1353bf4a237c6 100644 (file)
@@ -37,6 +37,9 @@
 #include <sys/wait.h>
 #endif
 #include <string.h>
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
 #include "c-ctype.h"
 
 #ifdef HAVE_PATHS_H
@@ -472,6 +475,169 @@ int virFileBuildPath(const char *dir,
     return 0;
 }
 
+
+#ifdef __linux__
+int virFileOpenTty(int *ttymaster,
+                   char **ttyName,
+                   int rawmode)
+{
+    int rc = -1;
+
+    if ((*ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0)
+        goto cleanup;
+
+    if (unlockpt(*ttymaster) < 0)
+        goto cleanup;
+
+    if (grantpt(*ttymaster) < 0)
+        goto cleanup;
+
+    if (rawmode) {
+        struct termios ttyAttr;
+        if (tcgetattr(*ttymaster, &ttyAttr) < 0)
+            goto cleanup;
+
+        cfmakeraw(&ttyAttr);
+
+        if (tcsetattr(*ttymaster, TCSADRAIN, &ttyAttr) < 0)
+            goto cleanup;
+    }
+
+    if (ttyName) {
+        char tempTtyName[PATH_MAX];
+        if (ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName)) < 0)
+            goto cleanup;
+
+        if ((*ttyName = strdup(tempTtyName)) == NULL) {
+            errno = ENOMEM;
+            goto cleanup;
+        }
+    }
+
+    rc = 0;
+
+cleanup:
+    if (rc != 0 &&
+        *ttymaster != -1) {
+        close(*ttymaster);
+    }
+
+    return rc;
+
+}
+#else
+int virFileOpenTty(int *ttymaster ATTRIBUTE_UNUSED,
+                   char **ttyName ATTRIBUTE_UNUSED,
+                   int rawmode ATTRIBUTE_UNUSED)
+{
+    return -1;
+}
+#endif
+
+
+int virFileWritePid(const char *dir,
+                    const char *name,
+                    pid_t pid)
+{
+    int rc;
+    int fd;
+    FILE *file = NULL;
+    char *pidfile = NULL;
+
+    if ((rc = virFileMakePath(dir)))
+        goto cleanup;
+
+    if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) {
+        rc = ENOMEM;
+        goto cleanup;
+    }
+
+    if ((fd = open(pidfile,
+                   O_WRONLY | O_CREAT | O_TRUNC,
+                   S_IRUSR | S_IWUSR)) < 0) {
+        rc = errno;
+        goto cleanup;
+    }
+
+    if (!(file = fdopen(fd, "w"))) {
+        rc = errno;
+        close(fd);
+        goto cleanup;
+    }
+
+    if (fprintf(file, "%d", pid) < 0) {
+        rc = errno;
+        goto cleanup;
+    }
+
+    rc = 0;
+
+cleanup:
+    if (file &&
+        fclose(file) < 0) {
+        rc = errno;
+    }
+
+    VIR_FREE(pidfile);
+    return rc;
+}
+
+int virFileReadPid(const char *dir,
+                   const char *name,
+                   pid_t *pid)
+{
+    int rc;
+    FILE *file;
+    char *pidfile = NULL;
+    *pid = 0;
+    if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) {
+        rc = ENOMEM;
+        goto cleanup;
+    }
+
+    if (!(file = fopen(pidfile, "r"))) {
+        rc = errno;
+        goto cleanup;
+    }
+
+    if (fscanf(file, "%d", pid) != 1) {
+        rc = EINVAL;
+        goto cleanup;
+    }
+
+    if (fclose(file) < 0) {
+        rc = errno;
+        goto cleanup;
+    }
+
+    rc = 0;
+
+ cleanup:
+    VIR_FREE(pidfile);
+    return rc;
+}
+
+int virFileDeletePid(const char *dir,
+                     const char *name)
+{
+    int rc = 0;
+    char *pidfile = NULL;
+
+    if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) {
+        rc = errno;
+        goto cleanup;
+    }
+
+    if (unlink(pidfile) < 0 && errno != ENOENT)
+        rc = errno;
+
+cleanup:
+    VIR_FREE(pidfile);
+    return rc;
+}
+
+
+
 /* Like strtol, but produce an "int" result, and check more carefully.
    Return 0 upon success;  return -1 to indicate failure.
    When END_PTR is NULL, the byte after the final valid digit must be NUL.
index 16f79c26e4323cd63907fca1a1c4df177d7b75ea..4452a297d2853bdcb21298057cd7f89a40293533 100644 (file)
@@ -58,6 +58,18 @@ int virFileBuildPath(const char *dir,
                      char *buf,
                      unsigned int buflen);
 
+int virFileOpenTty(int *ttymaster,
+                   char **ttyName,
+                   int rawmode);
+
+int virFileWritePid(const char *dir,
+                    const char *name,
+                    pid_t pid);
+int virFileReadPid(const char *dir,
+                   const char *name,
+                   pid_t *pid);
+int virFileDeletePid(const char *dir,
+                     const char *name);
 
 int __virStrToLong_i(char const *s,
                      char **end_ptr,