[libvirt] [PATCH 4/7] lxc: Improve guest startup error reporting

Cole Robinson crobinso at redhat.com
Thu Jun 2 19:40:23 UTC 2011


Add a simple handshake with the lxc_controller process so we can detect
process startup failures. We do this by adding a new --handshake cli arg
to lxc_controller for passing a file descriptor. If the process fails to
launch, we scrape all output from the logfile and report it to the user.

Signed-off-by: Cole Robinson <crobinso at redhat.com>
---
 src/lxc/lxc_container.c  |    2 +-
 src/lxc/lxc_container.h  |    1 +
 src/lxc/lxc_controller.c |   33 +++++++++++++-
 src/lxc/lxc_driver.c     |  107 +++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 137 insertions(+), 6 deletions(-)

diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index 00add94..ff90842 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -213,7 +213,7 @@ error_out:
  *
  * Returns 0 on success or -1 in case of error
  */
-static int lxcContainerWaitForContinue(int control)
+int lxcContainerWaitForContinue(int control)
 {
     lxc_message_t msg;
     int readLen;
diff --git a/src/lxc/lxc_container.h b/src/lxc/lxc_container.h
index 5e08d45..a3e457e 100644
--- a/src/lxc/lxc_container.h
+++ b/src/lxc/lxc_container.h
@@ -46,6 +46,7 @@ enum {
 # define LXC_DEV_MAJ_PTY     136
 
 int lxcContainerSendContinue(int control);
+int lxcContainerWaitForContinue(int control);
 
 int lxcContainerStart(virDomainDefPtr def,
                       unsigned int nveths,
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index cef4b58..5bf8ee3 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -612,7 +612,8 @@ lxcControllerRun(virDomainDefPtr def,
                  char **veths,
                  int monitor,
                  int client,
-                 int appPty)
+                 int appPty,
+                 int handshakefd)
 {
     int rc = -1;
     int control[2] = { -1, -1};
@@ -742,6 +743,13 @@ lxcControllerRun(virDomainDefPtr def,
     if (lxcControllerClearCapabilities() < 0)
         goto cleanup;
 
+    if (lxcContainerSendContinue(handshakefd) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("error sending continue signal to parent"));
+        goto cleanup;
+    }
+    VIR_FORCE_CLOSE(handshakefd);
+
     rc = lxcControllerMain(monitor, client, appPty, containerPty, container);
 
 cleanup:
@@ -751,6 +759,7 @@ cleanup:
     VIR_FORCE_CLOSE(control[1]);
     VIR_FREE(containerPtyPath);
     VIR_FORCE_CLOSE(containerPty);
+    VIR_FORCE_CLOSE(handshakefd);
 
     if (container > 1) {
         int status;
@@ -774,6 +783,7 @@ int main(int argc, char *argv[])
     char **veths = NULL;
     int monitor = -1;
     int appPty = -1;
+    int handshakefd = -1;
     int bg = 0;
     virCapsPtr caps = NULL;
     virDomainDefPtr def = NULL;
@@ -784,6 +794,7 @@ int main(int argc, char *argv[])
         { "name",   1, NULL, 'n' },
         { "veth",   1, NULL, 'v' },
         { "console", 1, NULL, 'c' },
+        { "handshakefd", 1, NULL, 's' },
         { "help", 0, NULL, 'h' },
         { 0, 0, 0, 0 },
     };
@@ -798,7 +809,7 @@ int main(int argc, char *argv[])
     while (1) {
         int c;
 
-        c = getopt_long(argc, argv, "dn:v:m:c:h",
+        c = getopt_long(argc, argv, "dn:v:m:c:s:h",
                        options, NULL);
 
         if (c == -1)
@@ -834,6 +845,14 @@ int main(int argc, char *argv[])
             }
             break;
 
+        case 's':
+            if (virStrToLong_i(optarg, NULL, 10, &handshakefd) < 0) {
+                fprintf(stderr, "malformed --handshakefd argument '%s'",
+                        optarg);
+                goto cleanup;
+            }
+            break;
+
         case 'h':
         case '?':
             fprintf(stderr, "\n");
@@ -845,6 +864,7 @@ int main(int argc, char *argv[])
             fprintf(stderr, "  -n NAME, --name NAME\n");
             fprintf(stderr, "  -c FD, --console FD\n");
             fprintf(stderr, "  -v VETH, --veth VETH\n");
+            fprintf(stderr, "  -s FD, --handshakefd FD\n");
             fprintf(stderr, "  -h, --help\n");
             fprintf(stderr, "\n");
             goto cleanup;
@@ -862,6 +882,12 @@ int main(int argc, char *argv[])
         goto cleanup;
     }
 
+    if (handshakefd < 0) {
+        fprintf(stderr, "%s: missing --handshake argument for container PTY\n",
+                argv[0]);
+        goto cleanup;
+    }
+
     if (getuid() != 0) {
         fprintf(stderr, "%s: must be run as the 'root' user\n", argv[0]);
         goto cleanup;
@@ -932,7 +958,8 @@ int main(int argc, char *argv[])
         goto cleanup;
     }
 
-    rc = lxcControllerRun(def, nveths, veths, monitor, client, appPty);
+    rc = lxcControllerRun(def, nveths, veths, monitor, client, appPty,
+                          handshakefd);
 
 
 cleanup:
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index 99f94ff..755de70 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -1287,7 +1287,8 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
                       int nveths,
                       char **veths,
                       int appPty,
-                      int logfile)
+                      int logfile,
+                      int handshakefd)
 {
     int i;
     char *filterstr;
@@ -1332,6 +1333,8 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
 
     virCommandAddArgList(cmd, "--name", vm->def->name, "--console", NULL);
     virCommandAddArgFormat(cmd, "%d", appPty);
+    virCommandAddArg(cmd, "--handshake");
+    virCommandAddArgFormat(cmd, "%d", handshakefd);
     virCommandAddArg(cmd, "--background");
 
     for (i = 0 ; i < nveths ; i++) {
@@ -1355,6 +1358,7 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
     }
 
     virCommandPreserveFD(cmd, appPty);
+    virCommandPreserveFD(cmd, handshakefd);
     virCommandSetOutputFD(cmd, &logfile);
     virCommandSetErrorFD(cmd, &logfile);
 
@@ -1364,6 +1368,78 @@ cleanup:
     return NULL;
 }
 
+static int
+lxcReadLogOutput(virDomainObjPtr vm,
+                 char *logfile,
+                 off_t pos,
+                 char *buf,
+                 size_t buflen)
+{
+    int fd;
+    off_t off;
+    int whence;
+    int got = 0, ret = -1;
+    int retries = 10;
+
+    if ((fd = open(logfile, O_RDONLY)) < 0) {
+        virReportSystemError(errno, _("failed to open logfile %s"),
+                             logfile);
+        goto cleanup;
+    }
+
+    if (pos < 0) {
+        off = 0;
+        whence = SEEK_END;
+    } else {
+        off = pos;
+        whence = SEEK_SET;
+    }
+
+    if (lseek(fd, off, whence) < 0) {
+        if (whence == SEEK_END)
+            virReportSystemError(errno,
+                                 _("unable to seek to end of log for %s"),
+                                 logfile);
+        else
+            virReportSystemError(errno,
+                                 _("unable to seek to %lld from start for %s"),
+                                 (long long)off, logfile);
+        goto cleanup;
+    }
+
+    while (retries) {
+        ssize_t bytes;
+        int isdead = 0;
+
+        if (kill(vm->pid, 0) == -1 && errno == ESRCH)
+            isdead = 1;
+
+        /* Any failures should be detected before we read the log, so we
+         * always have something useful to report on failure. */
+        bytes = saferead(fd, buf+got, buflen-got-1);
+        if (bytes < 0) {
+            virReportSystemError(errno, "%s",
+                                 _("Failure while reading guest log output"));
+            goto cleanup;
+        }
+
+        got += bytes;
+        buf[got] = '\0';
+
+        if ((got == buflen-1) || isdead) {
+            break;
+        }
+
+        usleep(100*1000);
+        retries--;
+    }
+
+
+    ret = got;
+cleanup:
+    VIR_FORCE_CLOSE(fd);
+    return ret;
+}
 
 /**
  * lxcVmStart:
@@ -1389,6 +1465,7 @@ static int lxcVmStart(virConnectPtr conn,
     int logfd = -1;
     unsigned int nveths = 0;
     char **veths = NULL;
+    int handshakefds[2] = { -1, -1 };
     off_t pos = -1;
     char ebuf[1024];
     char *timestamp;
@@ -1462,10 +1539,16 @@ static int lxcVmStart(virConnectPtr conn,
         goto cleanup;
     }
 
+    if (pipe(handshakefds) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to create pipe"));
+        goto cleanup;
+    }
+
     if (!(cmd = lxcBuildControllerCmd(driver,
                                       vm,
                                       nveths, veths,
-                                      parentTty, logfd)))
+                                      parentTty, logfd, handshakefds[1])))
         goto cleanup;
 
     /* Log timestamp */
@@ -1489,6 +1572,12 @@ static int lxcVmStart(virConnectPtr conn,
     if (virCommandRun(cmd, NULL) < 0)
         goto cleanup;
 
+    if (VIR_CLOSE(handshakefds[1]) < 0) {
+        virReportSystemError(errno, "%s", _("could not close handshake fd"));
+        goto cleanup;
+    }
+    handshakefds[1] = -1;
+
     /* Connect to the controller as a client *first* because
      * this will block until the child has written their
      * pid file out to disk */
@@ -1506,6 +1595,18 @@ static int lxcVmStart(virConnectPtr conn,
     vm->def->id = vm->pid;
     virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
 
+    if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
+        char out[1024];
+
+        if (!(lxcReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
+            lxcError(VIR_ERR_INTERNAL_ERROR,
+                     _("guest failed to start: %s"), out);
+        }
+
+        lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
+        goto cleanup;
+    }
+
     if ((priv->monitorWatch = virEventAddHandle(
              priv->monitor,
              VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
@@ -1545,6 +1646,8 @@ cleanup:
     if (rc != 0)
         VIR_FORCE_CLOSE(priv->monitor);
     VIR_FORCE_CLOSE(parentTty);
+    VIR_FORCE_CLOSE(handshakefds[0]);
+    VIR_FORCE_CLOSE(handshakefds[1]);
     VIR_FREE(logfile);
     return rc;
 }
-- 
1.7.4.4




More information about the libvir-list mailing list