[lxc-devel] [lxc/master] attach: add LXC_ATTACH_ALLOCATE_PTY

brauner on Github lxc-bot at linuxcontainers.org
Sun Dec 24 23:07:41 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171224/ea358ea3/attachment.bin>
-------------- next part --------------
From 397ea7abf6fac5eb7c9887af0a1daf762f6f5a8f Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 23 Dec 2017 11:59:36 +0100
Subject: [PATCH 01/10] console: move pty creation to separate function

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/console.c | 44 ++++++++++++++++++++++++++++++--------------
 src/lxc/console.h |  6 ++++++
 2 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/src/lxc/console.c b/src/lxc/console.c
index ad4a31405..d9e862fe7 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -696,24 +696,13 @@ int lxc_console_create_log_file(struct lxc_console *console)
 	return 0;
 }
 
-int lxc_console_create(struct lxc_conf *conf)
+int lxc_pty_create(struct lxc_console *console)
 {
 	int ret, saved_errno;
-	struct lxc_console *console = &conf->console;
-
-	if (!conf->rootfs.path) {
-		INFO("Container does not have a rootfs. The console will be "
-		     "shared with the host");
-		return 0;
-	}
-
-	if (console->path && !strcmp(console->path, "none")) {
-		INFO("No console was requested");
-		return 0;
-	}
 
 	process_lock();
-	ret = openpty(&console->master, &console->slave, console->name, NULL, NULL);
+	ret = openpty(&console->master, &console->slave, console->name, NULL,
+		      NULL);
 	saved_errno = errno;
 	process_unlock();
 	if (ret < 0) {
@@ -739,6 +728,33 @@ int lxc_console_create(struct lxc_conf *conf)
 		goto err;
 	}
 
+	return 0;
+
+err:
+	lxc_console_delete(console);
+	return -ENODEV;
+}
+
+int lxc_console_create(struct lxc_conf *conf)
+{
+	int ret;
+	struct lxc_console *console = &conf->console;
+
+	if (!conf->rootfs.path) {
+		INFO("Container does not have a rootfs. The console will be "
+		     "shared with the host");
+		return 0;
+	}
+
+	if (console->path && !strcmp(console->path, "none")) {
+		INFO("No console was requested");
+		return 0;
+	}
+
+	ret = lxc_pty_create(console);
+	if (ret < 0)
+		return -1;
+
 	/* create console log file */
 	ret = lxc_console_create_log_file(console);
 	if (ret < 0)
diff --git a/src/lxc/console.h b/src/lxc/console.h
index 60b299631..f88d6054d 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -82,6 +82,12 @@ extern int  lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttynum)
  * automatically chowned to the uid/gid of the unprivileged user. For this
  * ttys_shift_ids() can be called.)
  */
+extern int lxc_pty_create(struct lxc_console *console);
+
+/**
+ * lxc_console_create: Create a new pty.
+ * - In addition to lxc_pty_create() also sets up all pty logs.
+ */
 extern int  lxc_console_create(struct lxc_conf *);
 
 /*

From 48d5db4800145a0deb3d5efeac2945cf324a765a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 23 Dec 2017 12:02:45 +0100
Subject: [PATCH 02/10] conf: move ringbuffer members to anonymous struct

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.h | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index b7ddf1d3f..0246a02b1 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -174,18 +174,19 @@ struct lxc_console {
 	char name[MAXPATHLEN];
 	struct termios *tios;
 	struct lxc_tty_state *tty_state;
+	struct /* lxc_console_ringbuf */ {
+		/* size of the ringbuffer */
+		uint64_t buffer_size;
 
-	/* size of the ringbuffer */
-	uint64_t buffer_size;
+		/* path to the log file for the ringbuffer */
+		char *buffer_log_file;
 
-	/* path to the log file for the ringbuffer */
-	char *buffer_log_file;
+		/* fd to the log file for the ringbuffer */
+		int buffer_log_file_fd;
 
-	/* fd to the log file for the ringbuffer */
-	int buffer_log_file_fd;
-
-	/* the in-memory ringbuffer */
-	struct lxc_ringbuf ringbuf;
+		/* the in-memory ringbuffer */
+		struct lxc_ringbuf ringbuf;
+	};
 };
 
 /*

From 743ecacf042d35911968ace2d0d6d0c230f80e99 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 23 Dec 2017 12:03:32 +0100
Subject: [PATCH 03/10] start: non-functional changes

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/start.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/lxc/start.c b/src/lxc/start.c
index d70764fa1..be04d88db 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -686,11 +686,12 @@ int lxc_init(const char *name, struct lxc_handler *handler)
 	TRACE("set up signal fd");
 
 	/* Do this after setting up signals since it might unblock SIGWINCH. */
-	if (lxc_console_create(conf)) {
-		ERROR("Failed to create console for container \"%s\".", name);
+	ret = lxc_console_create(conf);
+	if (ret < 0) {
+		ERROR("Failed to create console");
 		goto out_restore_sigmask;
 	}
-	TRACE("created console");
+	TRACE("Created console");
 
 	if (lxc_ttys_shift_ids(conf) < 0) {
 		ERROR("Failed to shift tty into container.");

From 2598eaed4a072add7b9cbdc73d407c1b6009ca7d Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 23 Dec 2017 12:19:51 +0100
Subject: [PATCH 04/10] console: add some pty helpers

- int lxc_make_controlling_pty()
- int lxc_login_pty()
- void lxc_pty_conf_free()
- void lxc_pty_info_init()
- void lxc_pty_init()

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c    |  7 ++-----
 src/lxc/console.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/console.h |  6 ++++++
 3 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 38f868afb..1c2fea0c1 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -73,6 +73,7 @@
 #include "cgroup.h"
 #include "conf.h"
 #include "confile_utils.h"
+#include "console.h"
 #include "error.h"
 #include "log.h"
 #include "lxclock.h"
@@ -3648,11 +3649,7 @@ void lxc_conf_free(struct lxc_conf *conf)
 		return;
 	if (current_config == conf)
 		current_config = NULL;
-	free(conf->console.buffer_log_file);
-	free(conf->console.log_path);
-	free(conf->console.path);
-	if (conf->console.buffer_size > 0 && conf->console.ringbuf.addr)
-		lxc_ringbuf_release(&conf->console.ringbuf);
+	lxc_pty_conf_free(&conf->console);
 	free(conf->rootfs.mount);
 	free(conf->rootfs.bdev_type);
 	free(conf->rootfs.options);
diff --git a/src/lxc/console.c b/src/lxc/console.c
index d9e862fe7..37e1fa001 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -972,3 +972,62 @@ int lxc_console(struct lxc_container *c, int ttynum,
 
 	return ret;
 }
+
+int lxc_make_controlling_pty(int fd)
+{
+	int ret;
+
+	setsid();
+
+	ret = ioctl(fd, TIOCSCTTY, (char *)NULL);
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+
+int lxc_login_pty(int fd)
+{
+	int ret;
+
+	ret = lxc_make_controlling_pty(fd);
+	if (ret < 0)
+		return -1;
+
+	ret = lxc_console_set_stdfds(fd);
+	if (ret < 0)
+		return -1;
+
+	if (fd > STDERR_FILENO)
+		close(fd);
+
+	return 0;
+}
+
+void lxc_pty_info_init(struct lxc_pty_info *pty)
+{
+	pty->name[0] = '\0';
+	pty->master = -EBADF;
+	pty->slave = -EBADF;
+	pty->busy = -1;
+}
+
+void lxc_pty_init(struct lxc_console *pty)
+{
+	memset(pty, 0, sizeof(*pty));
+	pty->slave = -EBADF;
+	pty->master = -EBADF;
+	pty->peer = -EBADF;
+	pty->log_fd = -EBADF;
+	pty->buffer_log_file_fd = -EBADF;
+	lxc_pty_info_init(&pty->peerpty);
+}
+
+void lxc_pty_conf_free(struct lxc_console *console)
+{
+	free(console->buffer_log_file);
+	free(console->log_path);
+	free(console->path);
+	if (console->buffer_size > 0 && console->ringbuf.addr)
+		lxc_ringbuf_release(&console->ringbuf);
+}
diff --git a/src/lxc/console.h b/src/lxc/console.h
index f88d6054d..74aeac345 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -230,4 +230,10 @@ extern void lxc_console_signal_fini(struct lxc_tty_state *ts);
 extern int lxc_console_write_ringbuffer(struct lxc_console *console);
 extern int lxc_console_create_log_file(struct lxc_console *console);
 
+extern int lxc_make_controlling_pty(int fd);
+extern int lxc_login_pty(int fd);
+extern void lxc_pty_conf_free(struct lxc_console *console);
+extern void lxc_pty_info_init(struct lxc_pty_info *pty);
+extern void lxc_pty_init(struct lxc_console *pty);
+
 #endif

From ed5c2f7f326f90aeea6440bd8fcaa867b7291024 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 23 Dec 2017 12:39:52 +0100
Subject: [PATCH 05/10] attach: cleanup attach_child_main()

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/attach.c | 161 ++++++++++++++++++++++++++-----------------------------
 1 file changed, 75 insertions(+), 86 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 5ef0802a2..d33c6b506 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -61,6 +61,7 @@
 #include "conf.h"
 #include "config.h"
 #include "confile.h"
+#include "console.h"
 #include "log.h"
 #include "lsm/lsm.h"
 #include "lxclock.h"
@@ -821,13 +822,8 @@ struct attach_clone_payload {
 static int attach_child_main(struct attach_clone_payload *payload)
 {
 	int fd, lsm_fd, ret;
-	long flags;
-#if HAVE_SYS_PERSONALITY_H
-	long new_personality;
-#endif
 	uid_t new_uid;
 	gid_t new_gid;
-	int ipc_socket = payload->ipc_socket;
 	lxc_attach_options_t* options = payload->options;
 	struct lxc_proc_context_info* init_ctx = payload->init_ctx;
 	bool needs_lsm = (options->namespaces & CLONE_NEWNS) &&
@@ -842,36 +838,32 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	if (!(options->namespaces & CLONE_NEWNS) &&
 	    (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) {
 		ret = lxc_attach_remount_sys_proc();
-		if (ret < 0) {
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
+		if (ret < 0)
+			goto on_error;
+		TRACE("Remounted \"/proc\" and \"/sys\"");
 	}
 
-	/* Now perform additional attachments. */
+/* Now perform additional attachments. */
 #if HAVE_SYS_PERSONALITY_H
-	if (options->personality < 0)
-		new_personality = init_ctx->personality;
-	else
-		new_personality = options->personality;
-
 	if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) {
+		long new_personality;
+
+		if (options->personality < 0)
+			new_personality = init_ctx->personality;
+		else
+			new_personality = options->personality;
 		ret = personality(new_personality);
-		if (ret < 0) {
-			SYSERROR("Could not ensure correct architecture");
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
+		if (ret < 0)
+			goto on_error;
+		TRACE("Set new personality");
 	}
 #endif
 
 	if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) {
 		ret = lxc_attach_drop_privs(init_ctx);
-		if (ret < 0) {
-			ERROR("Could not drop privileges");
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
+		if (ret < 0)
+			goto on_error;
+		TRACE("Dropped capabilities");
 	}
 
 	/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
@@ -880,11 +872,9 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	ret = lxc_attach_set_environment(options->env_policy,
 					 options->extra_env_vars,
 					 options->extra_keep_env);
-	if (ret < 0) {
-		ERROR("Failed to set initial environment for attached process");
-		shutdown(ipc_socket, SHUT_RDWR);
-		rexit(-1);
-	}
+	if (ret < 0)
+		goto on_error;
+	TRACE("Set up environment");
 
 	/* This remark only affects fully unprivileged containers:
 	 * Receive fd for LSM security module before we set{g,u}id(). The reason
@@ -898,11 +888,9 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	 * set{g,u}id().
 	 */
 	if (needs_lsm) {
-		ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_fd, 1, NULL, 0);
-		if (ret <= 0) {
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
+		ret = lxc_abstract_unix_recv_fds(payload->ipc_socket, &lsm_fd, 1, NULL, 0);
+		if (ret <= 0)
+			goto on_error;
 		TRACE("Received LSM label file descriptor %d from parent", lsm_fd);
 	}
 
@@ -920,48 +908,36 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	if (options->gid != (gid_t)-1)
 		new_gid = options->gid;
 
-	/* Setup the controlling tty. */
 	if (options->stdin_fd && isatty(options->stdin_fd)) {
-		if (setsid() < 0) {
-			SYSERROR("Unable to setsid.");
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
+		ret = setsid();
+		if (ret < 0)
+			goto on_error;
+		TRACE("Became session leader");
 
-		if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) {
-			SYSERROR("Unable to set TIOCSTTY.");
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
+		ret = ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL);
+		if (ret < 0)
+			goto on_error;
+		TRACE("Set controlling terminal");
 	}
 
 	/* Try to set the {u,g}id combination. */
-	if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) {
-		if (setgid(new_gid) || setgroups(0, NULL)) {
-			SYSERROR("Switching to container gid.");
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
-	}
-	if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) &&
-	    setuid(new_uid)) {
-		SYSERROR("Switching to container uid.");
-		shutdown(ipc_socket, SHUT_RDWR);
-		rexit(-1);
+	if (new_uid != 0 || new_gid != 0 || options->namespaces & CLONE_NEWUSER) {
+		ret = lxc_switch_uid_gid(new_uid, new_gid);
+		if (ret < 0)
+			goto on_error;
+
+		ret = lxc_setgroups(0, NULL);
+		if (ret < 0)
+			goto on_error;
 	}
 
 	if ((init_ctx->container && init_ctx->container->lxc_conf &&
 	     init_ctx->container->lxc_conf->no_new_privs) ||
 	    (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) {
-		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-			SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. "
-				 "Process can use execve() gainable "
-				 "privileges.");
-			shutdown(ipc_socket, SHUT_RDWR);
-			rexit(-1);
-		}
-		INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() "
-		     "gainable privileges.");
+		ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+		if (ret < 0)
+			goto on_error;
+		TRACE("Set PR_SET_NO_NEW_PRIVS");
 	}
 
 	if (needs_lsm) {
@@ -970,26 +946,22 @@ static int attach_child_main(struct attach_clone_payload *payload)
 		/* Change into our new LSM profile. */
 		on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
 		ret = lsm_set_label_at(lsm_fd, on_exec, init_ctx->lsm_label);
-		if (ret < 0) {
-			SYSERROR("Failed to set LSM label.");
-			shutdown(ipc_socket, SHUT_RDWR);
-			close(lsm_fd);
-			rexit(-1);
-		}
 		close(lsm_fd);
+		if (ret < 0)
+			goto on_error;
+		TRACE("Set LSM label");
 	}
 
 	if (init_ctx->container && init_ctx->container->lxc_conf &&
-	    init_ctx->container->lxc_conf->seccomp &&
-	    (lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) {
-		ERROR("Failed to load seccomp policy.");
-		shutdown(ipc_socket, SHUT_RDWR);
-		rexit(-1);
+	    init_ctx->container->lxc_conf->seccomp) {
+		ret = lxc_seccomp_load(init_ctx->container->lxc_conf);
+		if (ret < 0)
+			goto on_error;
+		TRACE("Loaded seccomp profile");
 	}
-
-	shutdown(ipc_socket, SHUT_RDWR);
-	close(ipc_socket);
-	lxc_proc_put_context_info(init_ctx);
+	shutdown(payload->ipc_socket, SHUT_RDWR);
+	close(payload->ipc_socket);
+	payload->ipc_socket = -1;
 
 	/* The following is done after the communication socket is shut down.
 	 * That way, all errors that might (though unlikely) occur up until this
@@ -1018,17 +990,34 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	/* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also
 	 * here, ignore errors.
 	 */
-	for (fd = 0; fd <= 2; fd++) {
+	for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
+		int flags;
+
 		flags = fcntl(fd, F_GETFL);
 		if (flags < 0)
 			continue;
-		if (flags & FD_CLOEXEC)
-			if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0)
-				SYSERROR("Unable to clear FD_CLOEXEC from file descriptor.");
+
+		if ((flags & FD_CLOEXEC) == 0)
+			continue;
+
+		ret = fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC);
+		if (ret < 0) {
+			SYSERROR("Failed to clear FD_CLOEXEC from file descriptor %d", fd);
+			goto on_error;
+		}
 	}
 
 	/* We're done, so we can now do whatever the user intended us to do. */
 	rexit(payload->exec_function(payload->exec_payload));
+
+on_error:
+	if (payload->ipc_socket >= 0) {
+		shutdown(payload->ipc_socket, SHUT_RDWR);
+		close(payload->ipc_socket);
+		payload->ipc_socket = -1;
+	}
+	lxc_proc_put_context_info(init_ctx);
+	rexit(EXIT_FAILURE);
 }
 
 int lxc_attach(const char *name, const char *lxcpath,

From 41fdd71d33d6afa0c748b16a0952734ef253a42b Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 23 Dec 2017 13:25:44 +0100
Subject: [PATCH 06/10] console: adapt lxc_console_mainloop_add()

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/console.c          | 23 +++++++++--------------
 src/lxc/console.h          |  2 +-
 src/lxc/start.c            |  3 ++-
 src/lxc/tools/lxc_attach.c |  2 +-
 4 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/src/lxc/console.c b/src/lxc/console.c
index 37e1fa001..1735234c9 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -277,30 +277,25 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console)
 	}
 }
 
-extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
-				    struct lxc_conf *conf)
+int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
+			     struct lxc_console *console)
 {
-	struct lxc_console *console = &conf->console;
-
-	if (!conf->rootfs.path) {
-		INFO("no rootfs, no console.");
-		return 0;
-	}
+	int ret;
 
 	if (console->master < 0) {
 		INFO("no console");
 		return 0;
 	}
 
-	if (lxc_mainloop_add_handler(descr, console->master,
-				     lxc_console_cb_con, console)) {
-		ERROR("failed to add to mainloop console handler for '%d'",
-		      console->master);
+	ret = lxc_mainloop_add_handler(descr, console->master,
+				       lxc_console_cb_con, console);
+	if (ret < 0) {
+		ERROR("Failed to add handler for %d to mainloop", console->master);
 		return -1;
 	}
 
-	/* we cache the descr so that we can add an fd to it when someone
-	 * does attach to it in lxc_console_allocate()
+	/* We cache the descr so that we can add an fd to it when someone
+	 * does attach to it in lxc_console_allocate().
 	 */
 	console->descr = descr;
 	lxc_console_mainloop_add_peer(console);
diff --git a/src/lxc/console.h b/src/lxc/console.h
index 74aeac345..6220b380a 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -114,7 +114,7 @@ extern void lxc_console_free(struct lxc_conf *conf, int fd);
 /*
  * Register pty event handlers in an open mainloop
  */
-extern int  lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_conf *);
+extern int  lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_console *);
 
 /*
  * Handle SIGWINCH events on the allocated ptys.
diff --git a/src/lxc/start.c b/src/lxc/start.c
index be04d88db..3bce72137 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -481,7 +481,8 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
 		goto out_mainloop_open;
 	}
 
-	if (lxc_console_mainloop_add(&descr, handler->conf)) {
+	if (handler->conf->rootfs.path &&
+	    lxc_console_mainloop_add(&descr, &handler->conf->console)) {
 		ERROR("Failed to add console handler to LXC mainloop.");
 		goto out_mainloop_open;
 	}
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index ccd1787df..b5c89270c 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -356,7 +356,7 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
 		goto err2;
 	}
 
-	if (lxc_console_mainloop_add(&descr, conf) < 0) {
+	if (lxc_console_mainloop_add(&descr, &conf->console) < 0) {
 		fprintf(stderr, "Failed to add handlers to lxc mainloop.\n");
 		goto err3;
 	}

From 8f093e92d3ca3f8c50dfa1ee1fbc063f09f04f84 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 24 Dec 2017 19:13:54 +0100
Subject: [PATCH 07/10] console: add lxc_pty_map_ids()

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c             | 18 ------------------
 src/lxc/conf.h             |  1 -
 src/lxc/console.c          | 22 ++++++++++++++++++++++
 src/lxc/console.h          |  1 +
 src/lxc/start.c            |  3 ++-
 src/lxc/tools/lxc_attach.c |  3 ++-
 6 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 1c2fea0c1..b90cddda7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -3022,24 +3022,6 @@ int chown_mapped_root(const char *path, struct lxc_conf *conf)
 	return ret;
 }
 
-int lxc_ttys_shift_ids(struct lxc_conf *c)
-{
-	if (lxc_list_empty(&c->id_map))
-		return 0;
-
-	if (!strcmp(c->console.name, ""))
-		return 0;
-
-	if (chown_mapped_root(c->console.name, c) < 0) {
-		ERROR("failed to chown console \"%s\"", c->console.name);
-		return -1;
-	}
-
-	TRACE("chowned console \"%s\"", c->console.name);
-
-	return 0;
-}
-
 /* NOTE: Must not be called from inside the container namespace! */
 int lxc_create_tmp_proc_mount(struct lxc_conf *conf)
 {
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 0246a02b1..45ce9cda2 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -442,7 +442,6 @@ extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype);
 extern int mapped_hostid(unsigned id, struct lxc_conf *conf,
 			 enum idtype idtype);
 extern int chown_mapped_root(const char *path, struct lxc_conf *conf);
-extern int lxc_ttys_shift_ids(struct lxc_conf *c);
 extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data,
 			 const char *fn_name);
 extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *),
diff --git a/src/lxc/console.c b/src/lxc/console.c
index 1735234c9..3cac7c286 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -1026,3 +1026,25 @@ void lxc_pty_conf_free(struct lxc_console *console)
 	if (console->buffer_size > 0 && console->ringbuf.addr)
 		lxc_ringbuf_release(&console->ringbuf);
 }
+
+int lxc_pty_map_ids(struct lxc_conf *c, struct lxc_console *pty)
+{
+	int ret;
+
+	if (lxc_list_empty(&c->id_map))
+		return 0;
+
+	ret = strcmp(pty->name, "");
+	if (ret == 0)
+		return 0;
+
+	ret = chown_mapped_root(pty->name, c);
+	if (ret < 0) {
+		ERROR("Failed to chown \"%s\"", pty->name);
+		return -1;
+	}
+
+	TRACE("Chowned \"%s\"", pty->name);
+
+	return 0;
+}
diff --git a/src/lxc/console.h b/src/lxc/console.h
index 6220b380a..d1696133f 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -235,5 +235,6 @@ extern int lxc_login_pty(int fd);
 extern void lxc_pty_conf_free(struct lxc_console *console);
 extern void lxc_pty_info_init(struct lxc_pty_info *pty);
 extern void lxc_pty_init(struct lxc_console *pty);
+extern int lxc_pty_map_ids(struct lxc_conf *c, struct lxc_console *pty);
 
 #endif
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 3bce72137..2a1479cee 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -694,7 +694,8 @@ int lxc_init(const char *name, struct lxc_handler *handler)
 	}
 	TRACE("Created console");
 
-	if (lxc_ttys_shift_ids(conf) < 0) {
+	ret = lxc_pty_map_ids(conf, &conf->console);
+	if (ret < 0) {
 		ERROR("Failed to shift tty into container.");
 		goto out_restore_sigmask;
 	}
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index b5c89270c..0615cc719 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -338,7 +338,8 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
 	conf->console.descr = &descr;
 
 	/* Shift ttys to container. */
-	if (lxc_ttys_shift_ids(conf) < 0) {
+	ret = lxc_pty_map_ids(conf, &conf->console);
+	if (ret < 0) {
 		fprintf(stderr, "Failed to shift tty into container\n");
 		goto err1;
 	}

From 5831e08e5773469dd14687c0b53edb7a2aa1a645 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 24 Dec 2017 19:24:35 +0100
Subject: [PATCH 08/10] attach: minor tweaks

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/attach.c | 48 ++++++++++++++++++++++++++----------------------
 1 file changed, 26 insertions(+), 22 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index d33c6b506..b6f290ad3 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -260,10 +260,16 @@ static inline void lxc_proc_close_ns_fd(struct lxc_proc_context_info *ctx)
 static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx)
 {
 	free(ctx->lsm_label);
-	if (ctx->container)
+	ctx->lsm_label = NULL;
+
+	if (ctx->container) {
 		lxc_container_put(ctx->container);
+		ctx->container = NULL;
+	}
+
 	lxc_proc_close_ns_fd(ctx);
 	free(ctx);
+	ctx = NULL;
 }
 
 /**
@@ -894,6 +900,12 @@ static int attach_child_main(struct attach_clone_payload *payload)
 		TRACE("Received LSM label file descriptor %d from parent", lsm_fd);
 	}
 
+	if (options->stdin_fd > 0 && isatty(options->stdin_fd)) {
+		ret = lxc_make_controlling_pty(options->stdin_fd);
+		if (ret < 0)
+			goto on_error;
+	}
+
 	/* Set {u,g}id. */
 	new_uid = 0;
 	new_gid = 0;
@@ -908,18 +920,6 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	if (options->gid != (gid_t)-1)
 		new_gid = options->gid;
 
-	if (options->stdin_fd && isatty(options->stdin_fd)) {
-		ret = setsid();
-		if (ret < 0)
-			goto on_error;
-		TRACE("Became session leader");
-
-		ret = ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL);
-		if (ret < 0)
-			goto on_error;
-		TRACE("Set controlling terminal");
-	}
-
 	/* Try to set the {u,g}id combination. */
 	if (new_uid != 0 || new_gid != 0 || options->namespaces & CLONE_NEWUSER) {
 		ret = lxc_switch_uid_gid(new_uid, new_gid);
@@ -972,19 +972,23 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	/* Fd handling for stdin, stdout and stderr; ignore errors here, user
 	 * may want to make sure the fds are closed, for example.
 	 */
-	if (options->stdin_fd >= 0 && options->stdin_fd != 0)
-		dup2(options->stdin_fd, 0);
-	if (options->stdout_fd >= 0 && options->stdout_fd != 1)
-		dup2(options->stdout_fd, 1);
-	if (options->stderr_fd >= 0 && options->stderr_fd != 2)
-		dup2(options->stderr_fd, 2);
+	if (options->stdin_fd >= 0 && options->stdin_fd != STDIN_FILENO)
+		dup2(options->stdin_fd, STDIN_FILENO);
+
+	if (options->stdout_fd >= 0 && options->stdout_fd != STDOUT_FILENO)
+		dup2(options->stdout_fd, STDOUT_FILENO);
+
+	if (options->stderr_fd >= 0 && options->stderr_fd != STDERR_FILENO)
+		dup2(options->stderr_fd, STDERR_FILENO);
 
 	/* close the old fds */
-	if (options->stdin_fd > 2)
+	if (options->stdin_fd > STDERR_FILENO)
 		close(options->stdin_fd);
-	if (options->stdout_fd > 2)
+
+	if (options->stdout_fd > STDERR_FILENO)
 		close(options->stdout_fd);
-	if (options->stderr_fd > 2)
+
+	if (options->stderr_fd > STDERR_FILENO)
 		close(options->stderr_fd);
 
 	/* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also

From 1d9cf119371c8237d1de1953c7212329b4112604 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 24 Dec 2017 19:25:34 +0100
Subject: [PATCH 09/10] attach: move pty allocation into api

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/attach.c           | 190 ++++++++++++++++++++++++++++++++++++++-------
 src/lxc/tools/lxc_attach.c | 177 +----------------------------------------
 2 files changed, 165 insertions(+), 202 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index b6f290ad3..396a0fc61 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -66,6 +66,7 @@
 #include "lsm/lsm.h"
 #include "lxclock.h"
 #include "lxcseccomp.h"
+#include "mainloop.h"
 #include "namespace.h"
 #include "utils.h"
 
@@ -819,12 +820,30 @@ static signed long get_personality(const char *name, const char *lxcpath)
 
 struct attach_clone_payload {
 	int ipc_socket;
+	int pty_fd;
 	lxc_attach_options_t *options;
 	struct lxc_proc_context_info *init_ctx;
 	lxc_attach_exec_t exec_function;
 	void *exec_payload;
 };
 
+static void lxc_put_attach_clone_payload(struct attach_clone_payload *p)
+{
+	if (p->ipc_socket >= 0) {
+		shutdown(p->ipc_socket, SHUT_RDWR);
+		close(p->ipc_socket);
+		p->ipc_socket = -EBADF;
+	}
+
+	if (p->pty_fd >= 0) {
+		close(p->pty_fd);
+		p->pty_fd = -EBADF;
+	}
+
+	if (p->init_ctx)
+		lxc_proc_put_context_info(p->init_ctx);
+}
+
 static int attach_child_main(struct attach_clone_payload *payload)
 {
 	int fd, lsm_fd, ret;
@@ -961,7 +980,8 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	}
 	shutdown(payload->ipc_socket, SHUT_RDWR);
 	close(payload->ipc_socket);
-	payload->ipc_socket = -1;
+	payload->ipc_socket = -EBADF;
+	lxc_proc_put_context_info(init_ctx);
 
 	/* The following is done after the communication socket is shut down.
 	 * That way, all errors that might (though unlikely) occur up until this
@@ -1011,19 +1031,96 @@ static int attach_child_main(struct attach_clone_payload *payload)
 		}
 	}
 
+	ret = lxc_login_pty(payload->pty_fd);
+	if (ret < 0) {
+		SYSERROR("Failed to prepare pty file descriptor %d", payload->pty_fd);
+		goto on_error;
+	}
+	TRACE("Prepared pty file descriptor %d", payload->pty_fd);
+
 	/* We're done, so we can now do whatever the user intended us to do. */
 	rexit(payload->exec_function(payload->exec_payload));
 
 on_error:
-	if (payload->ipc_socket >= 0) {
-		shutdown(payload->ipc_socket, SHUT_RDWR);
-		close(payload->ipc_socket);
-		payload->ipc_socket = -1;
-	}
-	lxc_proc_put_context_info(init_ctx);
+	lxc_put_attach_clone_payload(payload);
 	rexit(EXIT_FAILURE);
 }
 
+static int lxc_attach_pty(struct lxc_conf *conf, struct lxc_console *pty)
+{
+	int ret;
+
+	lxc_pty_init(pty);
+
+	ret = lxc_pty_create(pty);
+	if (ret < 0) {
+		SYSERROR("Failed to create pty");
+		return -1;
+	}
+
+	/* Shift ttys to container. */
+	ret = lxc_pty_map_ids(conf, pty);
+	if (ret < 0) {
+		ERROR("Failed to shift pty");
+		goto on_error;
+	}
+
+	return 0;
+
+on_error:
+	lxc_console_delete(pty);
+	lxc_pty_conf_free(pty);
+	return -1;
+}
+
+static int lxc_attach_pty_mainloop_init(struct lxc_console *pty,
+					struct lxc_epoll_descr *descr)
+{
+	int ret;
+
+	ret = lxc_mainloop_open(descr);
+	if (ret < 0) {
+		ERROR("Failed to create mainloop");
+		return -1;
+	}
+
+	ret = lxc_console_mainloop_add(descr, pty);
+	if (ret < 0) {
+		ERROR("Failed to add handlers to mainloop");
+		lxc_mainloop_close(descr);
+		return -1;
+	}
+
+	return 0;
+}
+
+static inline void lxc_attach_pty_close_master(struct lxc_console *pty)
+{
+	if (pty->master < 0)
+		return;
+
+	close(pty->master);
+	pty->master = -EBADF;
+}
+
+static inline void lxc_attach_pty_close_slave(struct lxc_console *pty)
+{
+	if (pty->slave < 0)
+		return;
+
+	close(pty->slave);
+	pty->slave = -EBADF;
+}
+
+static inline void lxc_attach_pty_close_peer(struct lxc_console *pty)
+{
+	if (pty->peer < 0)
+		return;
+
+	close(pty->peer);
+	pty->peer = -EBADF;
+}
+
 int lxc_attach(const char *name, const char *lxcpath,
 	       lxc_attach_exec_t exec_function, void *exec_payload,
 	       lxc_attach_options_t *options, pid_t *attached_process)
@@ -1032,8 +1129,9 @@ int lxc_attach(const char *name, const char *lxcpath,
 	int ipc_sockets[2];
 	char *cwd, *new_cwd;
 	signed long personality;
-	pid_t attached_pid, expected, init_pid, pid;
+	pid_t attached_pid, init_pid, pid;
 	struct lxc_proc_context_info *init_ctx;
+	struct lxc_console pty;
 	struct attach_clone_payload payload = {0};
 
 	ret = access("/proc/self/ns", X_OK);
@@ -1150,6 +1248,14 @@ int lxc_attach(const char *name, const char *lxcpath,
 		return -1;
 	}
 
+	ret = lxc_attach_pty(init_ctx->container->lxc_conf, &pty);
+	if (ret < 0) {
+		ERROR("Failed to allocate pty");
+		free(cwd);
+		lxc_proc_put_context_info(init_ctx);
+		return -1;
+	}
+
 	/* Create a socket pair for IPC communication; set SOCK_CLOEXEC in order
 	 * to make sure we don't irritate other threads that want to fork+exec
 	 * away
@@ -1210,13 +1316,17 @@ int lxc_attach(const char *name, const char *lxcpath,
 	}
 
 	if (pid) {
+		int ret_parent = -1;
 		pid_t to_cleanup_pid = pid;
+		struct lxc_epoll_descr descr = {0};
 
 		/* Initial thread, we close the socket that is for the
 		 * subprocesses.
 		 */
 		close(ipc_sockets[1]);
 		free(cwd);
+		lxc_proc_close_ns_fd(init_ctx);
+		lxc_attach_pty_close_slave(&pty);
 
 		/* Attach to cgroup, if requested. */
 		if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
@@ -1227,21 +1337,28 @@ int lxc_attach(const char *name, const char *lxcpath,
 		}
 
 		/* Setup resource limits */
-		if (!lxc_list_empty(&init_ctx->container->lxc_conf->limits))
-			if (setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid) < 0)
+		if (!lxc_list_empty(&init_ctx->container->lxc_conf->limits)) {
+			ret = setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid);
+			if (ret < 0)
 				goto on_error;
+		}
+
+		ret = lxc_attach_pty_mainloop_init(&pty, &descr);
+		if (ret < 0)
+			goto on_error;
+		TRACE("Initalized pty mainloop");
 
 		/* Let the child process know to go ahead. */
 		status = 0;
 		ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
 		if (ret != sizeof(status))
-			goto on_error;
+			goto close_mainloop;
 		TRACE("Told intermediate process to start initializing");
 
 		/* Get pid of attached process from intermediate process. */
 		ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid));
 		if (ret != sizeof(attached_pid))
-			goto on_error;
+			goto close_mainloop;
 		TRACE("Received pid %d of attached process in parent pid namespace", attached_pid);
 
 		/* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
@@ -1253,7 +1370,7 @@ int lxc_attach(const char *name, const char *lxcpath,
 		/* Reap intermediate process. */
 		ret = wait_for_pid(pid);
 		if (ret < 0)
-			goto on_error;
+			goto close_mainloop;
 		TRACE("Intermediate process %d exited", pid);
 
 		/* We will always have to reap the attached process now. */
@@ -1269,7 +1386,7 @@ int lxc_attach(const char *name, const char *lxcpath,
 			on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
 			labelfd = lsm_open(attached_pid, on_exec);
 			if (labelfd < 0)
-				goto on_error;
+				goto close_mainloop;
 			TRACE("Opened LSM label file descriptor %d", labelfd);
 
 			/* Send child fd of the LSM security module to write to. */
@@ -1277,45 +1394,58 @@ int lxc_attach(const char *name, const char *lxcpath,
 			close(labelfd);
 			if (ret <= 0) {
 				SYSERROR("%d", (int)ret);
-				goto on_error;
+				goto close_mainloop;
 			}
 			TRACE("Sent LSM label file descriptor %d to child", labelfd);
 		}
 
-		/* Now shut down communication with child, we're done. */
-		shutdown(ipc_sockets[0], SHUT_RDWR);
-		close(ipc_sockets[0]);
-		lxc_proc_put_context_info(init_ctx);
-
 		/* We're done, the child process should now execute whatever it
 		 * is that the user requested. The parent can now track it with
 		 * waitpid() or similar.
 		 */
 
 		*attached_process = attached_pid;
-		return 0;
 
-	on_error:
-		/* First shut down the socket, then wait for the pid, otherwise
-		 * the pid we're waiting for may never exit.
-		 */
+		/* Now shut down communication with child, we're done. */
 		shutdown(ipc_sockets[0], SHUT_RDWR);
 		close(ipc_sockets[0]);
-		if (to_cleanup_pid)
+		ipc_sockets[0] = -1;
+
+		ret = lxc_mainloop(&descr, -1);
+		if (ret == 0) {
+			ret_parent = 0;
+			to_cleanup_pid = -1;
+		}
+
+	close_mainloop:
+		lxc_mainloop_close(&descr);
+
+	on_error:
+		if (ipc_sockets[0] >= 0) {
+			shutdown(ipc_sockets[0], SHUT_RDWR);
+			close(ipc_sockets[0]);
+		}
+
+		if (to_cleanup_pid > 0)
 			(void)wait_for_pid(to_cleanup_pid);
+
+		lxc_console_delete(&pty);
+		lxc_pty_conf_free(&pty);
 		lxc_proc_put_context_info(init_ctx);
-		return -1;
+		return ret_parent;
 	}
 
 	/* First subprocess begins here, we close the socket that is for the
 	 * initial thread.
 	 */
 	close(ipc_sockets[0]);
+	ipc_sockets[0] = -EBADF;
+	lxc_attach_pty_close_master(&pty);
+	lxc_attach_pty_close_peer(&pty);
 
 	/* Wait for the parent to have setup cgroups. */
-	expected = 0;
 	ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status));
-	if (ret != sizeof(status) || status != expected) {
+	if (ret != sizeof(status)) {
 		shutdown(ipc_sockets[1], SHUT_RDWR);
 		lxc_proc_put_context_info(init_ctx);
 		rexit(-1);
@@ -1351,6 +1481,7 @@ int lxc_attach(const char *name, const char *lxcpath,
 	payload.ipc_socket = ipc_sockets[1];
 	payload.options = options;
 	payload.init_ctx = init_ctx;
+	payload.pty_fd = pty.slave;
 	payload.exec_function = exec_function;
 	payload.exec_payload = exec_payload;
 
@@ -1368,6 +1499,7 @@ int lxc_attach(const char *name, const char *lxcpath,
 			ERROR("Failed to exec");
 		_exit(EXIT_FAILURE);
 	}
+	lxc_attach_pty_close_slave(&pty);
 
 	/* Tell grandparent the pid of the pid of the newly created child. */
 	ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid));
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index 0615cc719..ce4e25a06 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -241,157 +241,6 @@ Options :\n\
 	.checker  = NULL,
 };
 
-struct wrapargs {
-	lxc_attach_options_t *options;
-	lxc_attach_command_t *command;
-	struct lxc_console *console;
-	int ptyfd;
-};
-
-/* Minimalistic login_tty() implementation. */
-static int login_pty(int fd)
-{
-	setsid();
-	if (ioctl(fd, TIOCSCTTY, NULL) < 0)
-		return -1;
-	if (lxc_console_set_stdfds(fd) < 0)
-		return -1;
-	if (fd > STDERR_FILENO)
-		close(fd);
-	return 0;
-}
-
-static int get_pty_on_host_callback(void *p)
-{
-	struct wrapargs *wrap = p;
-
-	close(wrap->console->master);
-	if (login_pty(wrap->console->slave) < 0)
-		return -1;
-
-	if (wrap->command->program)
-		lxc_attach_run_command(wrap->command);
-	else
-		lxc_attach_run_shell(NULL);
-	return -1;
-}
-
-static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *pid)
-{
-	struct lxc_epoll_descr descr;
-	struct lxc_conf *conf;
-	struct lxc_tty_state *ts;
-	int ret = -1;
-	struct wrapargs *args = wrap;
-
-	if (!isatty(args->ptyfd)) {
-		fprintf(stderr, "Standard file descriptor does not refer to a pty\n");
-		return -1;
-	}
-
-	if (c->lxc_conf) {
-		conf = c->lxc_conf;
-	} else {
-		/* If the container is not defined and the user didn't specify a
-		 * config file to load we will simply init a dummy config here.
-		 */
-		conf = lxc_conf_init();
-		if (!conf) {
-			fprintf(stderr, "Failed to allocate dummy config file for the container\n");
-			return -1;
-		}
-
-		/* We also need a dummy rootfs path otherwise
-		 * lxc_console_create() will not let us create a console. Note,
-		 * I don't want this change to make it into
-		 * lxc_console_create()'s since this function will only be
-		 * responsible for proper /dev/{console,tty<n>} devices.
-		 * lxc-attach is just abusing it to also handle the pty case
-		 * because it is very similar. However, with LXC 3.0 lxc-attach
-		 * will need to move away from using lxc_console_create() since
-		 * this is actually an internal symbol and we only want the
-		 * tools to use the API with LXC 3.0.
-		 */
-		conf->rootfs.path = strdup("dummy");
-		if (!conf->rootfs.path)
-			return -1;
-	}
-	free(conf->console.log_path);
-	if (my_args.console_log)
-		conf->console.log_path = strdup(my_args.console_log);
-	else
-		conf->console.log_path = NULL;
-
-	/* In the case of lxc-attach our peer pty will always be the current
-	 * controlling terminal. We clear whatever was set by the user for
-	 * lxc.console.path here and set it NULL. lxc_console_peer_default()
-	 * will then try to open /dev/tty. If the process doesn't have a
-	 * controlling terminal we should still proceed.
-	 */
-	free(conf->console.path);
-	conf->console.path = NULL;
-
-	/* Create pty on the host. */
-	if (lxc_console_create(conf) < 0)
-		return -1;
-	ts = conf->console.tty_state;
-	conf->console.descr = &descr;
-
-	/* Shift ttys to container. */
-	ret = lxc_pty_map_ids(conf, &conf->console);
-	if (ret < 0) {
-		fprintf(stderr, "Failed to shift tty into container\n");
-		goto err1;
-	}
-
-	/* Send wrapper function on its way. */
-	wrap->console = &conf->console;
-	if (c->attach(c, get_pty_on_host_callback, wrap, wrap->options, pid) < 0)
-		goto err1;
-	close(conf->console.slave); /* Close slave side. */
-	conf->console.slave = -1;
-
-	ret = lxc_mainloop_open(&descr);
-	if (ret) {
-		fprintf(stderr, "failed to create mainloop\n");
-		goto err2;
-	}
-
-	if (lxc_console_mainloop_add(&descr, &conf->console) < 0) {
-		fprintf(stderr, "Failed to add handlers to lxc mainloop.\n");
-		goto err3;
-	}
-
-	ret = lxc_mainloop(&descr, -1);
-	if (ret) {
-		fprintf(stderr, "mainloop returned an error\n");
-		goto err3;
-	}
-	ret = 0;
-
-err3:
-	lxc_mainloop_close(&descr);
-err2:
-	if (ts && ts->sigfd != -1)
-		lxc_console_signal_fini(ts);
-err1:
-	lxc_console_delete(&conf->console);
-
-	return ret;
-}
-
-static int stdfd_is_pty(void)
-{
-	if (isatty(STDIN_FILENO))
-		return STDIN_FILENO;
-	if (isatty(STDOUT_FILENO))
-		return STDOUT_FILENO;
-	if (isatty(STDERR_FILENO))
-		return STDERR_FILENO;
-
-	return -1;
-}
-
 int main(int argc, char *argv[])
 {
 	int ret = -1, r;
@@ -474,28 +323,10 @@ int main(int argc, char *argv[])
 		command.argv = (char**)my_args.argv;
 	}
 
-	struct wrapargs wrap = (struct wrapargs){
-		.command = &command,
-		.options = &attach_options
-	};
-
-	wrap.ptyfd = stdfd_is_pty();
-	if (wrap.ptyfd >= 0) {
-		if ((!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) && my_args.console_log) {
-			fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
-			goto out;
-		}
-		ret = get_pty_on_host(c, &wrap, &pid);
-	} else {
-		if (my_args.console_log) {
-			fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
-			goto out;
-		}
-		if (command.program)
-			ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
-		else
-			ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
-	}
+	if (command.program)
+		ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
+	else
+		ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
 
 	if (ret < 0)
 		goto out;

From 1e178fa2b1c2f18415c7c506e3cc55128747b2cf Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 24 Dec 2017 23:56:41 +0100
Subject: [PATCH 10/10] attach: add LXC_ATTACH_ALLOCATE_PTY

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/attach.c           | 72 +++++++++++++++++++++++++++++-----------------
 src/lxc/attach_options.h   |  1 +
 src/lxc/tools/lxc_attach.c |  1 +
 3 files changed, 47 insertions(+), 27 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 396a0fc61..331314759 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -1031,12 +1031,14 @@ static int attach_child_main(struct attach_clone_payload *payload)
 		}
 	}
 
-	ret = lxc_login_pty(payload->pty_fd);
-	if (ret < 0) {
-		SYSERROR("Failed to prepare pty file descriptor %d", payload->pty_fd);
-		goto on_error;
+	if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+		ret = lxc_login_pty(payload->pty_fd);
+		if (ret < 0) {
+			SYSERROR("Failed to prepare pty file descriptor %d", payload->pty_fd);
+			goto on_error;
+		}
+		TRACE("Prepared pty file descriptor %d", payload->pty_fd);
 	}
-	TRACE("Prepared pty file descriptor %d", payload->pty_fd);
 
 	/* We're done, so we can now do whatever the user intended us to do. */
 	rexit(payload->exec_function(payload->exec_payload));
@@ -1248,12 +1250,14 @@ int lxc_attach(const char *name, const char *lxcpath,
 		return -1;
 	}
 
-	ret = lxc_attach_pty(init_ctx->container->lxc_conf, &pty);
-	if (ret < 0) {
-		ERROR("Failed to allocate pty");
-		free(cwd);
-		lxc_proc_put_context_info(init_ctx);
-		return -1;
+	if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+		ret = lxc_attach_pty(init_ctx->container->lxc_conf, &pty);
+		if (ret < 0) {
+			ERROR("Failed to allocate pty");
+			free(cwd);
+			lxc_proc_put_context_info(init_ctx);
+			return -1;
+		}
 	}
 
 	/* Create a socket pair for IPC communication; set SOCK_CLOEXEC in order
@@ -1325,8 +1329,11 @@ int lxc_attach(const char *name, const char *lxcpath,
 		 */
 		close(ipc_sockets[1]);
 		free(cwd);
-		lxc_proc_close_ns_fd(init_ctx);
-		lxc_attach_pty_close_slave(&pty);
+
+		if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+			lxc_proc_close_ns_fd(init_ctx);
+			lxc_attach_pty_close_slave(&pty);
+		}
 
 		/* Attach to cgroup, if requested. */
 		if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
@@ -1343,10 +1350,12 @@ int lxc_attach(const char *name, const char *lxcpath,
 				goto on_error;
 		}
 
-		ret = lxc_attach_pty_mainloop_init(&pty, &descr);
-		if (ret < 0)
-			goto on_error;
-		TRACE("Initalized pty mainloop");
+		if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+			ret = lxc_attach_pty_mainloop_init(&pty, &descr);
+			if (ret < 0)
+				goto on_error;
+			TRACE("Initalized pty mainloop");
+		}
 
 		/* Let the child process know to go ahead. */
 		status = 0;
@@ -1411,14 +1420,18 @@ int lxc_attach(const char *name, const char *lxcpath,
 		close(ipc_sockets[0]);
 		ipc_sockets[0] = -1;
 
-		ret = lxc_mainloop(&descr, -1);
-		if (ret == 0) {
-			ret_parent = 0;
-			to_cleanup_pid = -1;
+		if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+			ret = lxc_mainloop(&descr, -1);
+			if (ret == 0) {
+				ret_parent = 0;
+				to_cleanup_pid = -1;
+			}
 		}
 
 	close_mainloop:
-		lxc_mainloop_close(&descr);
+		if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+			lxc_mainloop_close(&descr);
+		}
 
 	on_error:
 		if (ipc_sockets[0] >= 0) {
@@ -1429,8 +1442,10 @@ int lxc_attach(const char *name, const char *lxcpath,
 		if (to_cleanup_pid > 0)
 			(void)wait_for_pid(to_cleanup_pid);
 
-		lxc_console_delete(&pty);
-		lxc_pty_conf_free(&pty);
+		if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+			lxc_console_delete(&pty);
+			lxc_pty_conf_free(&pty);
+		}
 		lxc_proc_put_context_info(init_ctx);
 		return ret_parent;
 	}
@@ -1440,8 +1455,10 @@ int lxc_attach(const char *name, const char *lxcpath,
 	 */
 	close(ipc_sockets[0]);
 	ipc_sockets[0] = -EBADF;
-	lxc_attach_pty_close_master(&pty);
-	lxc_attach_pty_close_peer(&pty);
+	if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
+		lxc_attach_pty_close_master(&pty);
+		lxc_attach_pty_close_peer(&pty);
+	}
 
 	/* Wait for the parent to have setup cgroups. */
 	ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status));
@@ -1499,7 +1516,8 @@ int lxc_attach(const char *name, const char *lxcpath,
 			ERROR("Failed to exec");
 		_exit(EXIT_FAILURE);
 	}
-	lxc_attach_pty_close_slave(&pty);
+	if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY)
+		lxc_attach_pty_close_slave(&pty);
 
 	/* Tell grandparent the pid of the pid of the newly created child. */
 	ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid));
diff --git a/src/lxc/attach_options.h b/src/lxc/attach_options.h
index 7055f7a1f..a348bb965 100644
--- a/src/lxc/attach_options.h
+++ b/src/lxc/attach_options.h
@@ -51,6 +51,7 @@ enum {
 	LXC_ATTACH_LSM_NOW               = 0x00020000, /*!< FIXME: unknown */
 	/* Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges. */
 	LXC_ATTACH_NO_NEW_PRIVS		 = 0x00040000, /*!< PR_SET_NO_NEW_PRIVS */
+	LXC_ATTACH_ALLOCATE_PTY          = 0x00080000, /*!< Allocate new pty for attached process. */
 
 	/* We have 16 bits for things that are on by default and 16 bits that
 	 * are off by default, that should be sufficient to keep binary
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index ce4e25a06..01622493a 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -312,6 +312,7 @@ int main(int argc, char *argv[])
 		attach_options.attach_flags |= LXC_ATTACH_REMOUNT_PROC_SYS;
 	if (elevated_privileges)
 		attach_options.attach_flags &= ~(elevated_privileges);
+	attach_options.attach_flags |= LXC_ATTACH_ALLOCATE_PTY;
 	attach_options.namespaces = namespace_flags;
 	attach_options.personality = new_personality;
 	attach_options.env_policy = env_policy;


More information about the lxc-devel mailing list