[lxc-devel] [lxc/master] [RFC] commands: add LXC_CMD_OPENPTY

brauner on Github lxc-bot at linuxcontainers.org
Thu Aug 3 20:41:04 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 381 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170803/2b64ce25/attachment.bin>
-------------- next part --------------
From 154750341d37deeca3a5e65eb60e118e55d03383 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 2 Aug 2017 22:21:56 +0200
Subject: [PATCH 1/5] conf: lxc-setup()

non-functional changes

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c | 156 +++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 97 insertions(+), 59 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 9508f6946..651ba7844 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -4180,51 +4180,62 @@ static int lxc_send_ttys_to_parent(struct lxc_handler *handler)
 
 int lxc_setup(struct lxc_handler *handler)
 {
+	int ret;
 	const char *name = handler->name;
-	struct lxc_conf *lxc_conf = handler->conf;
 	const char *lxcpath = handler->lxcpath;
+	struct lxc_conf *lxc_conf = handler->conf;
 
-	if (do_rootfs_setup(lxc_conf, name, lxcpath) < 0) {
-		ERROR("Error setting up rootfs mount after spawn");
+	ret = do_rootfs_setup(lxc_conf, name, lxcpath);
+	if (ret < 0) {
+		ERROR("Failed to set up rootfs");
 		return -1;
 	}
 
 	if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) {
-		if (setup_utsname(lxc_conf->utsname)) {
-			ERROR("failed to setup the utsname for '%s'", name);
+		ret = setup_utsname(lxc_conf->utsname);
+		if (ret < 0) {
+			ERROR("Failed to set up uts name");
 			return -1;
 		}
 	}
 
-	if (lxc_setup_networks_in_child_namespaces(lxc_conf,
-						   &lxc_conf->network)) {
-		ERROR("failed to setup the network for '%s'", name);
+	ret = lxc_setup_networks_in_child_namespaces(lxc_conf, &lxc_conf->network);
+	if (ret < 0) {
+		ERROR("Failed to set up networking");
 		return -1;
 	}
 
 	if (lxc_conf->autodev > 0) {
-		if (mount_autodev(name, &lxc_conf->rootfs, lxcpath)) {
-			ERROR("failed to mount /dev in the container");
+		ret = mount_autodev(name, &lxc_conf->rootfs, lxcpath);
+		if (ret < 0) {
+			ERROR("Failed to set up \"/dev\"");
 			return -1;
 		}
 	}
 
-	/* do automatic mounts (mainly /proc and /sys), but exclude
-	 * those that need to wait until other stuff has finished
+	/* Do automatic mounts (mainly /proc and /sys), but exclude those that
+	 * need to wait until other stuff has finished.
 	 */
-	if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler) < 0) {
-		ERROR("failed to setup the automatic mounts for '%s'", name);
+	ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler);
+	if (ret < 0) {
+		ERROR("Failed to set up initial automatic mounts");
 		return -1;
 	}
 
-	if (setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath)) {
-		ERROR("failed to setup the mounts for '%s'", name);
+	ret = setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name,
+			  lxcpath);
+	if (ret < 0) {
+		ERROR("Failed to set up fstab mounts");
 		return -1;
 	}
 
-	if (!lxc_list_empty(&lxc_conf->mount_list) && setup_mount_entries(lxc_conf, &lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath)) {
-		ERROR("failed to setup the mount entries for '%s'", name);
-		return -1;
+	if (!lxc_list_empty(&lxc_conf->mount_list)) {
+		ret = setup_mount_entries(lxc_conf, &lxc_conf->rootfs,
+					  &lxc_conf->mount_list, name, lxcpath);
+		if (ret < 0) {
+			ERROR("Failed to set up mount entries");
+			return -1;
+		}
 	}
 
 	/* Make sure any start hooks are in the container */
@@ -4238,94 +4249,121 @@ int lxc_setup(struct lxc_handler *handler)
 	 * before, /sys could not have been mounted
 	 * (is either mounted automatically or via fstab entries)
 	 */
-	if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler) < 0) {
-		ERROR("failed to setup the automatic mounts for '%s'", name);
+	ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler);
+	if (ret < 0) {
+		ERROR("Failed to set up remaining automatic mounts");
 		return -1;
 	}
 
-	if (run_lxc_hooks(name, "mount", lxc_conf, lxcpath, NULL)) {
-		ERROR("failed to run mount hooks for container '%s'.", name);
+	ret = run_lxc_hooks(name, "mount", lxc_conf, lxcpath, NULL);
+	if (ret < 0) {
+		ERROR("Failed to run mount hooks");
 		return -1;
 	}
 
 	if (lxc_conf->autodev > 0) {
-		if (run_lxc_hooks(name, "autodev", lxc_conf, lxcpath, NULL)) {
-			ERROR("failed to run autodev hooks for container '%s'.", name);
+		ret = run_lxc_hooks(name, "autodev", lxc_conf, lxcpath, NULL);
+		if (ret < 0) {
+			ERROR("Failed to run autodev hooks");
 			return -1;
 		}
 
-		if (lxc_fill_autodev(&lxc_conf->rootfs)) {
-			ERROR("failed to populate /dev in the container");
+		ret = lxc_fill_autodev(&lxc_conf->rootfs);
+		if (ret < 0) {
+			ERROR("Failed to populate \"/dev\"");
 			return -1;
 		}
 	}
 
-	if (!lxc_conf->is_execute && lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console, lxc_conf->ttydir)) {
-		ERROR("failed to setup the console for '%s'", name);
-		return -1;
-	}
+	if (!lxc_conf->is_execute) {
+		ret = lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console,
+					lxc_conf->ttydir);
+		if (ret < 0) {
+			ERROR("Failed to set up console");
+			return -1;
+		}
 
-	if (!lxc_conf->is_execute && setup_dev_symlinks(&lxc_conf->rootfs)) {
-		ERROR("failed to setup /dev symlinks for '%s'", name);
-		return -1;
+		ret = setup_dev_symlinks(&lxc_conf->rootfs);
+		if (ret < 0) {
+			ERROR("Failed to setup \"/dev\" symlinks");
+			return -1;
+		}
 	}
 
 	/* mount /proc if it's not already there */
-	if (lxc_create_tmp_proc_mount(lxc_conf) < 0) {
-		ERROR("failed to LSM mount proc for '%s'", name);
+	ret = lxc_create_tmp_proc_mount(lxc_conf);
+	if (ret < 0) {
+		ERROR("Failed to create temporary proc mount for LSM");
 		return -1;
 	}
 
-	if (setup_pivot_root(&lxc_conf->rootfs)) {
-		ERROR("failed to set rootfs for '%s'", name);
+	ret = setup_pivot_root(&lxc_conf->rootfs);
+	if (ret < 0) {
+		ERROR("Failed to set up rootfs");
 		return -1;
 	}
 
-	if (lxc_setup_devpts(lxc_conf->pts)) {
-		ERROR("failed to setup the new pts instance");
+	ret = lxc_setup_devpts(lxc_conf->pts);
+	if (ret < 0) {
+		ERROR("Failed to set up new devpts instance");
 		return -1;
 	}
 
-	if (lxc_create_tty(name, lxc_conf)) {
-		ERROR("failed to create the ttys");
+	ret = lxc_create_tty(name, lxc_conf);
+	if (ret < 0) {
+		ERROR("Failed to allocate ttys");
 		return -1;
 	}
 
-	if (lxc_send_ttys_to_parent(handler) < 0) {
-		ERROR("failure sending console info to parent");
+	ret = lxc_send_ttys_to_parent(handler);
+	if (ret < 0) {
+		ERROR("Failed to send ttys to parent");
 		return -1;
 	}
 
-	if (!lxc_conf->is_execute && lxc_setup_tty(lxc_conf)) {
-		ERROR("failed to setup the ttys for '%s'", name);
-		return -1;
+	if (!lxc_conf->is_execute) {
+		ret = lxc_setup_tty(lxc_conf);
+		if (ret < 0) {
+			ERROR("Failed to set up ttys");
+			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->pty_names) {
+		ret = setenv("container_ttys", lxc_conf->pty_names, 1);
+		if (ret < 0)
+			SYSERROR("Failed to set environment variable for ptys");
+	}
 
 
-	if (setup_personality(lxc_conf->personality)) {
-		ERROR("failed to setup personality");
+	ret = setup_personality(lxc_conf->personality);
+	if (ret < 0) {
+		ERROR("Failed to setup personality");
 		return -1;
 	}
 
 	if (!lxc_list_empty(&lxc_conf->keepcaps)) {
 		if (!lxc_list_empty(&lxc_conf->caps)) {
-			ERROR("Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both.");
+			ERROR("Container requests lxc.cap.drop and "
+			      "lxc.cap.keep: either use lxc.cap.drop or "
+			      "lxc.cap.keep, not both");
 			return -1;
 		}
-		if (dropcaps_except(&lxc_conf->keepcaps)) {
-			ERROR("failed to keep requested caps");
+
+		ret = dropcaps_except(&lxc_conf->keepcaps);
+		if (ret < 0) {
+			ERROR("Failed to keep requested capabilities");
+			return -1;
+		}
+	} else {
+		ret = setup_caps(&lxc_conf->caps);
+		if (ret < 0) {
+			ERROR("Failed to drop requested capabilities");
 			return -1;
 		}
-	} else if (setup_caps(&lxc_conf->caps)) {
-		ERROR("failed to drop capabilities");
-		return -1;
 	}
 
-	NOTICE("Container \"%s\" is set up", name);
-
+	NOTICE("Finished setting up container");
 	return 0;
 }
 

From 534efb2ce57c426aa5ac583afba8eff0893cdf9a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 3 Aug 2017 17:42:00 +0200
Subject: [PATCH 2/5] conf: non-functional changes

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

diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 4d3f6d78f..c1c9543ff 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -329,8 +329,10 @@ struct lxc_conf {
 	struct lxc_list caps;
 	struct lxc_list keepcaps;
 	struct lxc_tty_info tty_info;
-	/* Comma-separated list of lxc.tty.max pty names. */
+
+	/* comma-separated list of lxc.tty.max pty names */
 	char *pty_names;
+
 	struct lxc_console console;
 	struct lxc_rootfs rootfs;
 	char *ttydir;
@@ -341,24 +343,35 @@ struct lxc_conf {
 	unsigned int lsm_aa_allow_incomplete;
 	char *lsm_se_context;
 	int tmp_umount_proc;
-	char *seccomp;  // filename with the seccomp rules
+	char *seccomp;
 #if HAVE_SCMP_FILTER_CTX
 	scmp_filter_ctx seccomp_ctx;
 #endif
 	int maincmd_fd;
-	unsigned int autodev;  // if 1, mount and fill a /dev at start
-	int haltsignal; // signal used to halt container
-	int rebootsignal; // signal used to reboot container
-	int stopsignal; // signal used to hard stop container
-	char *rcfile;	// Copy of the top level rcfile we read
-
-	// Logfile and logleve can be set in a container config file.
-	// Those function as defaults.  The defaults can be overriden
-	// by command line.  However we don't want the command line
-	// specified values to be saved on c->save_config().  So we
-	// store the config file specified values here.
-	char *logfile;  // the logfile as specifed in config
-	int loglevel;   // loglevel as specifed in config (if any)
+
+	/* if 1, mount and fill a /dev at start */
+	unsigned int autodev;
+
+	/* signal used to halt container */
+	int haltsignal;
+
+	/* signal used to reboot container */
+	int rebootsignal;
+
+	/* signal used to hard stop container */
+	int stopsignal;
+
+	/* Copy of the top level rcfile we read */
+	char *rcfile;
+
+	/* Logfile and loglevel can be set in a container config file. Those
+	 * function as defaults. The defaults can be overriden by command line.
+	 * However we don't want the command line specified values to be saved
+	 * on c->save_config(). So we store the config file specified values
+	 * here.
+	 */
+	char *logfile;
+	int loglevel;
 	int logfd;
 
 	int inherit_ns_fd[LXC_NS_MAX];
@@ -377,11 +390,13 @@ struct lxc_conf {
 
 	/* list of included files */
 	struct lxc_list includes;
+
 	/* config entries which are not "lxc.*" are aliens */
 	struct lxc_list aliens;
 
-	/* list of environment variables we'll add to the container when
-	 * started */
+	/* List of environment variables we'll add to the container when
+	 * started.
+	 */
 	struct lxc_list environment;
 
 	/* text representation of the config file */
@@ -391,8 +406,9 @@ struct lxc_conf {
 	/* init command */
 	char *init_cmd;
 
-	/* if running in a new user namespace, the UID/GID that init and COMMAND
-	 * should run under when using lxc-execute */
+	/* If running in a new user namespace, the UID/GID that init and COMMAND
+	 * should run under when using lxc-execute.
+	 */
 	uid_t init_uid;
 	gid_t init_gid;
 
@@ -400,7 +416,8 @@ struct lxc_conf {
 	unsigned int ephemeral;
 
 	/* The facility to pass to syslog. Let's users establish as what type of
-	 * program liblxc is supposed to write to the syslog. */
+	 * program liblxc is supposed to write to the syslog.
+	 */
 	char *syslog;
 
 	/* Whether PR_SET_NO_NEW_PRIVS will be set for the container. */

From 1d732b29e33730f5bcf320ebf69b8d07a1212c33 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 3 Aug 2017 17:47:54 +0200
Subject: [PATCH 3/5] conf: allocate O_PATH /dev/pts/ptmx file descriptor

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c  | 30 ++++++++++++++++++++++++++++++
 src/lxc/conf.h  |  7 +++++++
 src/lxc/start.c |  8 ++++++++
 3 files changed, 45 insertions(+)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 651ba7844..72569edea 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1427,6 +1427,18 @@ static int lxc_setup_devpts(int num_pts)
 	return 0;
 }
 
+static int lxc_allocate_dev_ptmx_opath_fd(struct lxc_conf *conf)
+{
+	int fd;
+
+	fd = open("/dev/pts/ptmx", O_PATH);
+	if (fd < 0)
+		return -1;
+
+	conf->dev_ptmx.opath_fd = fd;
+	return 0;
+}
+
 static int setup_personality(int persona)
 {
 	#if HAVE_SYS_PERSONALITY_H
@@ -2769,6 +2781,7 @@ struct lxc_conf *lxc_conf_init(void)
 	 * default to running as UID/GID 0 when using lxc-execute */
 	new->init_uid = 0;
 	new->init_gid = 0;
+	new->dev_ptmx.opath_fd = -1;
 
 	return new;
 }
@@ -4309,6 +4322,21 @@ int lxc_setup(struct lxc_handler *handler)
 		return -1;
 	}
 
+	/* Allocate "/dev/pts/ptmx" O_PATH file descriptor. */
+	ret = lxc_allocate_dev_ptmx_opath_fd(lxc_conf);
+	if (ret < 0) {
+		ERROR("Failed to allocate \"/dev/pts/ptmx\" file descriptor");
+		return -1;
+	}
+
+	/* Send "/dev/pts/ptmx" O_PATH file descriptor to parent. */
+	ret = lxc_abstract_unix_send_fds(handler->ttysock[0],
+					 &lxc_conf->dev_ptmx.opath_fd, 1, NULL, 0);
+	if (ret < 0) {
+		ERROR("Failed to send \"/dev/pts/ptmx\" file descriptor");
+		return -1;
+	}
+
 	ret = lxc_create_tty(name, lxc_conf);
 	if (ret < 0) {
 		ERROR("Failed to allocate ttys");
@@ -4644,6 +4672,8 @@ void lxc_conf_free(struct lxc_conf *conf)
 	lxc_clear_aliens(conf);
 	lxc_clear_environment(conf);
 	lxc_clear_limits(conf, "lxc.prlimit");
+	if (conf->dev_ptmx.opath_fd >= 0)
+		close(conf->dev_ptmx.opath_fd);
 	free(conf);
 }
 
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index c1c9543ff..73b3a0d5a 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -311,6 +311,11 @@ struct saved_nic {
 	char *orig_name;
 };
 
+/* O_PATH File descriptor */
+struct lxc_dev_ptmx {
+	int opath_fd;
+};
+
 struct lxc_conf {
 	int is_execute;
 	char *fstab;
@@ -425,6 +430,8 @@ struct lxc_conf {
 
 	/* RLIMIT_* limits */
 	struct lxc_list limits;
+
+	struct lxc_dev_ptmx dev_ptmx;
 };
 
 #ifdef HAVE_TLS
diff --git a/src/lxc/start.c b/src/lxc/start.c
index d2a054bfc..7e8156662 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -1419,6 +1419,14 @@ static int lxc_spawn(struct lxc_handler *handler)
 	if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CGROUP))
 		return -1;
 
+	/* Receive "/dev/pts/ptmx" O_PATH file descriptor from child. */
+	if (lxc_abstract_unix_recv_fds(handler->ttysock[1],
+				       &handler->conf->dev_ptmx.opath_fd, 1, NULL,
+				       0) < 0) {
+		ERROR("Failed to send \"/dev/pts/ptmx\" file descriptor");
+		return -1;
+	}
+
 	/* Read tty fds allocated by child. */
 	if (lxc_recv_ttys_from_child(handler) < 0) {
 		ERROR("Failed to receive tty info from child process.");

From 078d81f3418cabc5372e9bf6ec05f5c101e982f5 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 3 Aug 2017 19:55:32 +0200
Subject: [PATCH 4/5] commands: non-functional changes

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index c6ece2cc7..416388ad8 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -21,33 +21,35 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <stdio.h>
+#include "config.h"
+
 #include <errno.h>
-#include <unistd.h>
-#include <signal.h>
 #include <fcntl.h>
+#include <malloc.h>
 #include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <sys/param.h>
-#include <malloc.h>
-#include <stdlib.h>
 
-#include "log.h"
-#include "lxc.h"
-#include "conf.h"
-#include "start.h"	/* for struct lxc_handler */
-#include "utils.h"
+#include "af_unix.h"
 #include "cgroup.h"
 #include "commands.h"
 #include "commands_utils.h"
-#include "console.h"
+#include "conf.h"
+#include "config.h"
 #include "confile.h"
+#include "console.h"
+#include "log.h"
+#include "lxc.h"
 #include "lxclock.h"
 #include "mainloop.h"
 #include "monitor.h"
-#include "af_unix.h"
-#include "config.h"
+#include "start.h" /* for struct lxc_handler */
+#include "utils.h"
 
 /*
  * This file provides the different functions for clients to

From 6abc60506a94d94a0f56f6730102610cc0b4a009 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 3 Aug 2017 19:55:47 +0200
Subject: [PATCH 5/5] commands: add LXC_CMD_OPENPTY

Closes #1620.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/lxc/commands.h |   9 +++++
 src/lxc/conf.c     |   4 +-
 src/lxc/utils.h    |   9 +++++
 4 files changed, 126 insertions(+), 3 deletions(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 416388ad8..07113b6e5 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -92,6 +92,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
 		[LXC_CMD_GET_NAME]         = "get_name",
 		[LXC_CMD_GET_LXCPATH]      = "get_lxcpath",
 		[LXC_CMD_ADD_STATE_CLIENT] = "add_state_client",
+		[LXC_CMD_OPENPTY]          = "openpty",
 	};
 
 	if (cmd >= LXC_CMD_MAX)
@@ -118,10 +119,15 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
  */
 static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 {
-	int ret, rspfd;
+	int ret;
+	int rspfd = -1;
+	int pty_fds[2] = {-1, -1};
 	struct lxc_cmd_rsp *rsp = &cmd->rsp;
 
-	ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp));
+	if (cmd->req.cmd == LXC_CMD_OPENPTY)
+		ret = lxc_abstract_unix_recv_fds(sock, pty_fds, 2, rsp, sizeof(*rsp));
+	else
+		ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp));
 	if (ret < 0) {
 		WARN("Command %s failed to receive response: %s.",
 		     lxc_cmd_str(cmd->req.cmd), strerror(errno));
@@ -147,6 +153,18 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 		rspdata->masterfd = rspfd;
 		rspdata->ttynum = PTR_TO_INT(rsp->data);
 		rsp->data = rspdata;
+	} else if (cmd->req.cmd == LXC_CMD_OPENPTY) {
+		struct lxc_cmd_openpty_rsp_data *openpty_rsp = NULL;
+
+		openpty_rsp = malloc(sizeof(*openpty_rsp));
+		if (!openpty_rsp) {
+			ERROR("Command %s couldn't allocate response buffer.",
+					lxc_cmd_str(cmd->req.cmd));
+			return -1;
+		}
+		openpty_rsp->master = pty_fds[0];
+		openpty_rsp->slave = pty_fds[1];
+		rsp->data = openpty_rsp;
 	}
 
 	if (rsp->datalen == 0) {
@@ -934,6 +952,90 @@ static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
 	return lxc_cmd_rsp_send(fd, &rsp);
 }
 
+int lxc_cmd_openpty(const char *name, const char *lxcpath, int *amaster,
+		    int *aslave)
+{
+	int ret, stopped;
+	struct lxc_cmd_rr cmd = {
+	    .req = {
+		    .cmd = LXC_CMD_OPENPTY
+	    },
+	};
+	struct lxc_cmd_openpty_rsp_data *openpty_rsp;
+
+	ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
+	if (ret < 0)
+		return -1;
+
+	if (cmd.rsp.ret < 0)
+		return -1;
+
+	openpty_rsp = cmd.rsp.data;
+
+	*amaster = openpty_rsp->master;
+	*aslave = openpty_rsp->slave;
+
+	return 0;
+}
+
+static int lxc_cmd_openpty_callback(int fd, struct lxc_cmd_req *req,
+				    struct lxc_handler *handler)
+{
+	int ret;
+	struct lxc_cmd_rsp rsp;
+	char proc_self_path[LXC_PROC_SELF_LEN];
+	int pty_fds[2] = {-1, -1};
+
+	memset(&rsp, 0, sizeof(rsp));
+
+	ret = snprintf(proc_self_path, LXC_PROC_SELF_LEN, "/proc/self/fd/%d",
+		       handler->conf->dev_ptmx.opath_fd);
+	if (ret < 0 || (size_t)ret >= LXC_PROC_SELF_LEN) {
+		SYSERROR("Failed to create string: %d != %d", ret, LXC_PROC_SELF_LEN);
+		return -1;
+	}
+
+	/* open the pseudoterminal master */
+	pty_fds[0] = open(proc_self_path, O_RDWR | O_NOCTTY);
+	if (pty_fds[0] < 0) {
+		ERROR("%s - Failed to open \"%s\"", proc_self_path,
+		      strerror(errno));
+		return -1;
+	}
+
+	ret = grantpt(pty_fds[0]);
+	if (ret < 0) {
+		ERROR("%s - Failed to grant access to slave pseudoterminal",
+		      strerror(errno));
+		close(pty_fds[0]);
+		return -1;
+	}
+
+	ret = unlockpt(pty_fds[0]);
+	if (ret < 0) {
+		ERROR("%s - Failed to unlock slave pseudoterminal",
+		      strerror(errno));
+		close(pty_fds[0]);
+		return -1;
+	}
+
+	pty_fds[1] = ioctl(pty_fds[0], TIOCGPTPEER, O_RDWR | O_NOCTTY);
+	if (pty_fds[1] < 0) {
+		ERROR("%s - Failed to unlock slave pseudoterminal file "
+		      "descriptor", strerror(errno));
+		close(pty_fds[0]);
+		return -1;
+	}
+
+	ret = lxc_abstract_unix_send_fds(fd, pty_fds, 2, NULL, 0);
+	if (ret < 0) {
+		ERROR("Failed to send pseudoterminal file descriptors");
+		return -1;
+	}
+
+	return 0;
+}
+
 static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
 			   struct lxc_handler *handler)
 {
@@ -951,6 +1053,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
 		[LXC_CMD_GET_NAME]         = lxc_cmd_get_name_callback,
 		[LXC_CMD_GET_LXCPATH]      = lxc_cmd_get_lxcpath_callback,
 		[LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback,
+		[LXC_CMD_OPENPTY]          = lxc_cmd_openpty_callback,
 	};
 
 	if (req->cmd >= LXC_CMD_MAX) {
diff --git a/src/lxc/commands.h b/src/lxc/commands.h
index 28428c774..4b20f967c 100644
--- a/src/lxc/commands.h
+++ b/src/lxc/commands.h
@@ -48,6 +48,7 @@ typedef enum {
 	LXC_CMD_GET_NAME,
 	LXC_CMD_GET_LXCPATH,
 	LXC_CMD_ADD_STATE_CLIENT,
+	LXC_CMD_OPENPTY,
 	LXC_CMD_MAX,
 } lxc_cmd_t;
 
@@ -73,6 +74,11 @@ struct lxc_cmd_console_rsp_data {
 	int ttynum;
 };
 
+struct lxc_cmd_openpty_rsp_data {
+	int master;
+	int slave;
+};
+
 extern int lxc_cmd_console_winch(const char *name, const char *lxcpath);
 extern int lxc_cmd_console(const char *name, int *ttynum, int *fd,
 			   const char *lxcpath);
@@ -116,4 +122,7 @@ extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
 				    struct lxc_handler *handler);
 extern int lxc_try_cmd(const char *name, const char *lxcpath);
 
+extern int lxc_cmd_openpty(const char *name, const char *lxcpath, int *amaster,
+			   int *aslave);
+
 #endif /* __commands_h */
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 72569edea..334cecca7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1431,7 +1431,7 @@ static int lxc_allocate_dev_ptmx_opath_fd(struct lxc_conf *conf)
 {
 	int fd;
 
-	fd = open("/dev/pts/ptmx", O_PATH);
+	fd = open("/dev/ptmx", O_PATH | O_CLOEXEC);
 	if (fd < 0)
 		return -1;
 
@@ -4332,6 +4332,8 @@ int lxc_setup(struct lxc_handler *handler)
 	/* Send "/dev/pts/ptmx" O_PATH file descriptor to parent. */
 	ret = lxc_abstract_unix_send_fds(handler->ttysock[0],
 					 &lxc_conf->dev_ptmx.opath_fd, 1, NULL, 0);
+	close(lxc_conf->dev_ptmx.opath_fd);
+	lxc_conf->dev_ptmx.opath_fd = -1;
 	if (ret < 0) {
 		ERROR("Failed to send \"/dev/pts/ptmx\" file descriptor");
 		return -1;
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 4408c6d69..72efc7d79 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -51,6 +51,15 @@
 #define LXC_NUMSTRLEN64 21
 #define LXC_LINELEN 4096
 #define LXC_IDMAPLEN 4096
+/* strlen("/proc/self/") = 11
+ * +
+ * uint64_t_as_string = 21
+ * +
+ * strlen("fd/") = 3;
+ * +
+ * \0
+ */
+#define LXC_PROC_SELF_LEN 36
 
 /* returns 1 on success, 0 if there were any failures */
 extern int lxc_rmdir_onedev(char *path, const char *exclude);


More information about the lxc-devel mailing list