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

Stéphane Graber stgraber at ubuntu.com
Thu Jan 29 10:39:51 UTC 2015


On Thu, Jan 29, 2015 at 10:13:36AM +0000, Serge Hallyn wrote:
> 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>

Acked-by: Stéphane Graber <stgraber 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
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20150129/9a32c0be/attachment-0001.sig>


More information about the lxc-devel mailing list