[lxc-devel] [PATCH] create lxc.tty ptys from container process

Serge Hallyn serge.hallyn at ubuntu.com
Thu Jan 29 10:13:36 UTC 2015


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 at ubuntu.com>
---
 config/apparmor/abstractions/start-container      |   1 +
 config/apparmor/profiles/lxc-default-with-nesting |   2 +-
 src/lxc/conf.c                                    | 100 +++++++++++++++-------
 src/lxc/start.c                                   |  61 +++++++++++--
 src/lxc/start.h                                   |   1 +
 5 files changed, 127 insertions(+), 38 deletions(-)

diff --git a/config/apparmor/abstractions/start-container b/config/apparmor/abstractions/start-container
index e361968..0d02379 100644
--- a/config/apparmor/abstractions/start-container
+++ b/config/apparmor/abstractions/start-container
@@ -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>/
diff --git a/config/apparmor/profiles/lxc-default-with-nesting b/config/apparmor/profiles/lxc-default-with-nesting
index 03325aa..91ad6de 100644
--- a/config/apparmor/profiles/lxc-default-with-nesting
+++ b/config/apparmor/profiles/lxc-default-with-nesting
@@ -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),
 }
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index e7def3e..aeabb6c 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -65,6 +65,7 @@
 
 #include "network.h"
 #include "error.h"
+#include "af_unix.h"
 #include "parse.h"
 #include "utils.h"
 #include "conf.h"
@@ -968,29 +969,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;
@@ -1024,8 +1022,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)) {
@@ -1038,14 +1034,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;
 		}
@@ -3506,20 +3499,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;
@@ -3737,6 +3719,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;
@@ -3827,14 +3851,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;
@@ -3856,6 +3872,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;
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 1949886..983f7a3 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -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
diff --git a/src/lxc/start.h b/src/lxc/start.h
index d39b3b4..92f5b7d 100644
--- a/src/lxc/start.h
+++ b/src/lxc/start.h
@@ -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
 };
 
 
-- 
2.1.0



More information about the lxc-devel mailing list