]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
create lxc.tty ptys from container process
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Thu, 29 Jan 2015 10:13:36 +0000 (10:13 +0000)
committerStéphane Graber <stgraber@ubuntu.com>
Thu, 29 Jan 2015 10:46:02 +0000 (11:46 +0100)
Lxc has always created the ptys for use by console and ttys early
on from the monitor process.  This has some advantages, but also
has disadvantages, namely (1) container ptys counting against the
max ptys for the host, and (2) not having a /dev/pts/N in the
container to pass to getty.  (2) was not a problem for us historically
because we bind-mounted the host's /dev/pts/N onto a /dev/ttyN in
the container.  However, systemd hardocdes a check for container_ttys
that the path have 'pts/' in it.  If it were only for (2) I'd have
opted for a systemd patch to check the device major number, but (1)
made it worth moving the openpty to the container namespace.

So this patch moves the tty creation into the task which becomes
the container init.  It then passes the fds for the opened ptys
back to the monitor over a unix socketpair (for use by lxc-console).
The /dev/console is still created in the monitor process, so that
it can for instance be used by lxc.logfd.

So now if you have a foreground container with lxc.tty = 4, you
should end up with one host /dev/pts entry per container rather than 5.

And lxc-console now works with systemd containers.

Note that if the container init mounts its own devpts over the
one mounted by lxc, the tty /dev/pts/n will be hidden.  This is ok
since it's only systemd that needs it, and systemd won't do that.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Acked-by: Stéphane Graber <stgraber@ubuntu.com>
config/apparmor/abstractions/start-container
config/apparmor/profiles/lxc-default-with-nesting
src/lxc/conf.c
src/lxc/start.c
src/lxc/start.h

index e36196884a485edb16d7f4001fd8ce31ec93ad55..0d02379dcd0b30e3c259ddb1b16c23183f981e84 100644 (file)
@@ -13,6 +13,7 @@
   mount -> /usr/lib/lxc/{**,},
   mount fstype=devpts -> /dev/pts/,
   mount options=bind /dev/pts/ptmx/ -> /dev/ptmx/,
+  mount options=bind /dev/pts/** -> /dev/tty*/,
   mount options=(rw, make-slave) -> **,
   mount fstype=debugfs,
   # allow pre-mount hooks to stage mounts under /var/lib/lxc/<container>/
index 03325aae11abcaf4c0fa5ac07d0bb5a3f20692d9..91ad6de115ed19a336d5cd768c342c0a9eb202ce 100644 (file)
@@ -10,5 +10,5 @@ profile lxc-container-default-with-nesting flags=(attach_disconnected,mediate_de
 
   mount fstype=proc -> /var/cache/lxc/**,
   mount fstype=sysfs -> /var/cache/lxc/**,
-  mount options=(rw,bind) /var/cache/lxc/**/dev/shm/ -> /var/cache/lxc/**/run/shm/,
+  mount options=(rw,bind),
 }
index d2cfbe80772a83c11e1594aaae362113bb2e6f98..fbe82b5a4d42e0aea0e36296b5f2fe66674224e8 100644 (file)
@@ -65,6 +65,7 @@
 
 #include "network.h"
 #include "error.h"
+#include "af_unix.h"
 #include "parse.h"
 #include "utils.h"
 #include "conf.h"
@@ -975,29 +976,26 @@ static bool append_ptyname(char **pp, char *name)
 
 static int setup_tty(struct lxc_conf *conf)
 {
-       const struct lxc_rootfs *rootfs = &conf->rootfs;
        const struct lxc_tty_info *tty_info = &conf->tty_info;
        char *ttydir = conf->ttydir;
        char path[MAXPATHLEN], lxcpath[MAXPATHLEN];
        int i, ret;
 
-       if (!rootfs->path)
+       if (!conf->rootfs.path)
                return 0;
 
        for (i = 0; i < tty_info->nbtty; i++) {
 
                struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
 
-               ret = snprintf(path, sizeof(path), "%s/dev/tty%d",
-                        rootfs->mount, i + 1);
+               ret = snprintf(path, sizeof(path), "/dev/tty%d", i + 1);
                if (ret >= sizeof(path)) {
                        ERROR("pathname too long for ttys");
                        return -1;
                }
                if (ttydir) {
                        /* create dev/lxc/tty%d" */
-                       ret = snprintf(lxcpath, sizeof(lxcpath), "%s/dev/%s/tty%d",
-                                rootfs->mount, ttydir, i + 1);
+                       ret = snprintf(lxcpath, sizeof(lxcpath), "/dev/%s/tty%d", ttydir, i + 1);
                        if (ret >= sizeof(lxcpath)) {
                                ERROR("pathname too long for ttys");
                                return -1;
@@ -1031,8 +1029,6 @@ static int setup_tty(struct lxc_conf *conf)
                                SYSERROR("failed to create symlink for tty %d", i+1);
                                return -1;
                        }
-                       /* Now save the relative path in @path for append_ptyname */
-                       sprintf(path, "%s/tty%d", ttydir, i + 1);
                } else {
                        /* If we populated /dev, then we need to create /dev/ttyN */
                        if (access(path, F_OK)) {
@@ -1045,14 +1041,11 @@ static int setup_tty(struct lxc_conf *conf)
                                }
                        }
                        if (mount(pty_info->name, path, "none", MS_BIND, 0)) {
-                               WARN("failed to mount '%s'->'%s'",
-                                               pty_info->name, path);
+                               SYSERROR("failed to mount '%s'->'%s'", pty_info->name, path);
                                continue;
                        }
-                       /* Now save the relative path in @path for append_ptyname */
-                       sprintf(path, "tty%d", i + 1);
                }
-               if (!append_ptyname(&conf->pty_names, path)) {
+               if (!append_ptyname(&conf->pty_names, pty_info->name)) {
                        ERROR("Error setting up container_ttys string");
                        return -1;
                }
@@ -3513,20 +3506,9 @@ int chown_mapped_root(char *path, struct lxc_conf *conf)
 
 int ttys_shift_ids(struct lxc_conf *c)
 {
-       int i;
-
        if (lxc_list_empty(&c->id_map))
                return 0;
 
-       for (i = 0; i < c->tty_info.nbtty; i++) {
-               struct lxc_pty_info *pty_info = &c->tty_info.pty_info[i];
-
-               if (chown_mapped_root(pty_info->name, c) < 0) {
-                       ERROR("Failed to chown %s", pty_info->name);
-                       return -1;
-               }
-       }
-
        if (strcmp(c->console.name, "") !=0 && chown_mapped_root(c->console.name, c) < 0) {
                ERROR("Failed to chown %s", c->console.name);
                return -1;
@@ -3744,6 +3726,48 @@ static bool verify_start_hooks(struct lxc_conf *conf)
        return true;
 }
 
+static int send_fd(int sock, int fd)
+{
+       int ret = lxc_abstract_unix_send_fd(sock, fd, NULL, 0);
+
+
+       if (ret < 0) {
+               SYSERROR("Error sending tty fd to parent");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int send_ttys_to_parent(struct lxc_handler *handler)
+{
+       struct lxc_conf *conf = handler->conf;
+       const struct lxc_tty_info *tty_info = &conf->tty_info;
+       int i;
+       int sock = handler->ttysock[0];
+
+       for (i = 0; i < tty_info->nbtty; i++) {
+               struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+               if (send_fd(sock, pty_info->slave) < 0)
+                       goto bad;
+               close(pty_info->slave);
+               pty_info->slave = -1;
+               if (send_fd(sock, pty_info->master) < 0)
+                       goto bad;
+               close(pty_info->master);
+               pty_info->master = -1;
+       }
+
+       close(handler->ttysock[0]);
+       close(handler->ttysock[1]);
+
+       return 0;
+
+bad:
+       ERROR("Error writing tty fd to parent");
+       return -1;
+}
+
 int lxc_setup(struct lxc_handler *handler)
 {
        const char *name = handler->name;
@@ -3834,14 +3858,6 @@ int lxc_setup(struct lxc_handler *handler)
                        ERROR("failed to setup kmsg for '%s'", name);
        }
 
-       if (!lxc_conf->is_execute && setup_tty(lxc_conf)) {
-               ERROR("failed to setup the ttys for '%s'", name);
-               return -1;
-       }
-
-       if (lxc_conf->pty_names && setenv("container_ttys", lxc_conf->pty_names, 1))
-               SYSERROR("failed to set environment variable for container ptys");
-
        if (!lxc_conf->is_execute && setup_dev_symlinks(&lxc_conf->rootfs)) {
                ERROR("failed to setup /dev symlinks for '%s'", name);
                return -1;
@@ -3863,6 +3879,26 @@ int lxc_setup(struct lxc_handler *handler)
                return -1;
        }
 
+       if (lxc_create_tty(name, lxc_conf)) {
+               ERROR("failed to create the ttys");
+               return -1;
+       }
+
+       if (send_ttys_to_parent(handler) < 0) {
+               ERROR("failure sending console info to parent");
+               return -1;
+       }
+
+
+       if (!lxc_conf->is_execute && setup_tty(lxc_conf)) {
+               ERROR("failed to setup the ttys for '%s'", name);
+               return -1;
+       }
+
+       if (lxc_conf->pty_names && setenv("container_ttys", lxc_conf->pty_names, 1))
+               SYSERROR("failed to set environment variable for container ptys");
+
+
        if (setup_personality(lxc_conf->personality)) {
                ERROR("failed to setup personality");
                return -1;
index 1949886a795be11c114e5d1b7fd43c721c748457..70e46937424a831b1525d8e2ba115097dfc2db3d 100644 (file)
@@ -375,6 +375,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
 
        memset(handler, 0, sizeof(*handler));
 
+       handler->ttysock[0] = handler->ttysock[1] = -1;
        handler->conf = conf;
        handler->lxcpath = lxcpath;
        handler->pinfd = -1;
@@ -427,11 +428,6 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
                goto out_aborting;
        }
 
-       if (lxc_create_tty(name, conf)) {
-               ERROR("failed to create the ttys");
-               goto out_aborting;
-       }
-
        /* the signal fd has to be created before forking otherwise
         * if the child process exits before we setup the signal fd,
         * the event will be lost and the command will be stuck */
@@ -492,6 +488,10 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
        close(handler->conf->maincmd_fd);
        handler->conf->maincmd_fd = -1;
        free(handler->name);
+       if (handler->ttysock[0] != -1) {
+               close(handler->ttysock[0]);
+               close(handler->ttysock[1]);
+       }
        cgroup_destroy(handler);
        free(handler);
 }
@@ -800,6 +800,46 @@ static int save_phys_nics(struct lxc_conf *conf)
        return 0;
 }
 
+static int recv_fd(int sock, int *fd)
+{
+       if (lxc_abstract_unix_recv_fd(sock, fd, NULL, 0) < 0) {
+               SYSERROR("Error receiving tty fd from child");
+               return -1;
+       }
+       if (*fd == -1)
+               return -1;
+       return 0;
+}
+
+static int recv_ttys_from_child(struct lxc_handler *handler)
+{
+       struct lxc_conf *conf = handler->conf;
+       int i, sock = handler->ttysock[1];
+       struct lxc_tty_info *tty_info = &conf->tty_info;
+
+       if (!conf->tty)
+               return 0;
+
+       tty_info->pty_info = malloc(sizeof(*tty_info->pty_info)*conf->tty);
+       if (!tty_info->pty_info) {
+               SYSERROR("failed to allocate pty_info");
+               return -1;
+       }
+
+       for (i = 0; i < conf->tty; i++) {
+               struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+               pty_info->busy = 0;
+               if (recv_fd(sock, &pty_info->slave) < 0 ||
+                               recv_fd(sock, &pty_info->master) < 0) {
+                       ERROR("Error receiving tty info from child");
+                       return -1;
+               }
+       }
+       tty_info->nbtty = conf->tty;
+
+       return 0;
+}
+
 static int lxc_spawn(struct lxc_handler *handler)
 {
        int failed_before_rename = 0;
@@ -824,6 +864,11 @@ static int lxc_spawn(struct lxc_handler *handler)
                handler->clone_flags |= CLONE_NEWUSER;
        }
 
+       if (socketpair(AF_UNIX, SOCK_DGRAM, 0, handler->ttysock) < 0) {
+               lxc_sync_fini(handler);
+               return -1;
+       }
+
        if (handler->conf->inherit_ns_fd[LXC_NS_NET] == -1) {
                if (!lxc_requests_empty_network(handler))
                        handler->clone_flags |= CLONE_NEWNET;
@@ -991,6 +1036,12 @@ static int lxc_spawn(struct lxc_handler *handler)
        cgroup_disconnect();
        cgroups_connected = false;
 
+       /* read tty fds allocated by child */
+       if (recv_ttys_from_child(handler) < 0) {
+               ERROR("failed to receive tty info from child");
+               goto out_delete_net;
+       }
+
        /* Tell the child to complete its initialization and wait for
         * it to exec or return an error.  (the child will never
         * return LXC_SYNC_POST_CGROUP+1.  It will either close the
index d39b3b46a38cb6841ac1a7573fcf5c5d5156743d..2c6fc0d818a47f02ef651a1f01254d66097b2a1c 100644 (file)
@@ -73,6 +73,7 @@ struct lxc_handler {
        int pinfd;
        const char *lxcpath;
        void *cgroup_data;
+       int ttysock[2]; // socketpair for child->parent tty fd passing
 };