[libvirt] [PATCH 1/2] Fix occasional container creation failure due to misuse of grantpt

Serge E. Hallyn serge.hallyn at canonical.com
Thu Oct 13 02:31:28 UTC 2011


glibc's grantpt and ptsname cannot be used on a fd for a pty not in
/dev/pts.  The lxc controller tries to do just that.  So if you try to
start a container on a system where /dev/pts/0 is not available, it
will fail.  You can make this happen by opening a terminal on
/dev/pts/0, and doing 'sleep 2h & disown; exit'.  To fix this, I call
the virFileOpenTtyAt() from a forked task in a new mount ns, and first
mount the container's /dev/pts onto /dev/pts.  (Then the opened fd must
be passed back to the lxc driver).  Another solution would be to just
do it all by hand without grantpt and ptsname.

Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libvirt/+bug/863629

Signed-off-by: Serge Hallyn <serge.hallyn at canonical.com>
---
 src/lxc/lxc_controller.c |  117 ++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 112 insertions(+), 5 deletions(-)

diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 51488e7..1a56e0c 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -780,6 +780,113 @@ static int lxcSetPersonality(virDomainDefPtr def)
 # define MS_SLAVE              (1<<19)
 #endif
 
+static int send_pty(int sock, int *pty)
+{
+    struct iovec vector;
+    struct msghdr msg;
+    struct cmsghdr * cmsg;
+    int ret;
+
+    vector.iov_base = "PTY";
+    vector.iov_len = 3;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &vector;
+    msg.msg_iovlen = 1;
+
+    cmsg = alloca(sizeof(struct cmsghdr) + sizeof(*pty));
+    cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(*pty);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+  
+    memcpy(CMSG_DATA(cmsg), pty, sizeof(*pty));
+
+    msg.msg_control = cmsg;
+    msg.msg_controllen = cmsg->cmsg_len;
+
+    ret = sendmsg(sock, &msg, 0);
+    if (ret < 0)
+        return -1;
+    return 0;
+}
+
+static int recv_pty(int sock, int *pty, char **path, char *devpts)
+{
+    char buf[50];
+    struct iovec vector;
+    struct msghdr msg;
+    struct cmsghdr * cmsg;
+    int ret;
+
+    vector.iov_base = buf;
+    vector.iov_len = 50;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &vector;
+    msg.msg_iovlen = 1;
+
+    cmsg = alloca(sizeof(struct cmsghdr) + sizeof(*pty));
+    cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(*pty);
+    msg.msg_control = cmsg;
+    msg.msg_controllen = cmsg->cmsg_len;
+
+    ret = recvmsg(sock, &msg, 0);
+    if (ret < 0)
+        return ret;
+
+    memcpy(pty, CMSG_DATA(cmsg), sizeof(*pty));
+
+    if (VIR_ALLOC_N(*path, PATH_MAX) < 0) {
+        virReportSystemError(errno, "%s",
+                _("Failed to allocate space for ptyname"));
+        return -ENOMEM;
+    }
+    //snprintf(*path, PATH_MAX, "%s/0", devpts);
+    snprintf(*path, PATH_MAX, "/dev/pts/0");
+    return 0;
+}
+
+static int private_open_tty_at(char *devpts, char *devptmx,
+        int *containerPty,
+        char **containerPtyPath, int rawmode)
+{
+    int pid;
+    int ret;
+    int status;
+    int s[2];
+
+    ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, s);
+    if (ret < 0)
+		return ret;
+
+    pid = fork();
+    if (pid < 0)
+        exit(pid);
+    if (pid == 0) {
+        close(s[1]);
+        ret = unshare(CLONE_NEWNS);
+        if (ret < 0)
+            exit(ret);
+        ret = mount(devpts, "/dev/pts", "none", MS_BIND, NULL);
+        if (ret < 0)
+            exit(ret);
+        ret = virFileOpenTtyAt(devptmx, containerPty, containerPtyPath, rawmode);
+        if (ret < 0)
+            exit(ret);
+        send_pty(s[0], containerPty);
+        exit(ret);
+    }
+    close(s[0]);
+    ret = recv_pty(s[1], containerPty, containerPtyPath, devpts);
+    close(s[1]);
+    if (ret)
+        return ret;
+    waitpid(pid, &status, 0);
+    return WEXITSTATUS(status);
+}
+
 static int
 lxcControllerRun(virDomainDefPtr def,
                  unsigned int nveths,
@@ -894,12 +1001,12 @@ lxcControllerRun(virDomainDefPtr def,
 
     if (devptmx) {
         VIR_DEBUG("Opening tty on private %s", devptmx);
-        if (virFileOpenTtyAt(devptmx,
-                             &containerPty,
-                             &containerPtyPath,
-                             0) < 0) {
+        if (private_open_tty_at(devpts, devptmx,
+                    &containerPty,
+                    &containerPtyPath,
+                    0) < 0) {
             virReportSystemError(errno, "%s",
-                                 _("Failed to allocate tty"));
+                    _("Failed to allocate tty"));
             goto cleanup;
         }
     } else {
-- 
1.7.5.4




More information about the libvir-list mailing list