[lxc-devel] [lxc/master] [RFC]: retrieve pre-allocated pts devices from container

brauner on Github lxc-bot at linuxcontainers.org
Tue May 23 02:06:35 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1345 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170523/54ed281b/attachment.bin>
-------------- next part --------------
From 3cf51af7a31a1a5c386660a2d8594e8fff58fd22 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 21 May 2017 03:55:29 +0200
Subject: [PATCH 1/7] console: non-functional changes

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/console.c |  7 +++----
 src/lxc/console.h | 24 ++++++++++++------------
 2 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/src/lxc/console.c b/src/lxc/console.c
index 3c593609b..e628e836b 100755
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -593,7 +593,7 @@ int lxc_console_set_stdfds(int fd)
 }
 
 int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
-		struct lxc_epoll_descr *descr)
+			     struct lxc_epoll_descr *descr)
 {
 	struct lxc_tty_state *ts = cbdata;
 	char c;
@@ -624,11 +624,11 @@ int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
 }
 
 int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
-		struct lxc_epoll_descr *descr)
+			      struct lxc_epoll_descr *descr)
 {
 	struct lxc_tty_state *ts = cbdata;
 	char buf[1024];
-	int r, w;
+	ssize_t r, w;
 
 	if (fd != ts->masterfd)
 		return 1;
@@ -752,4 +752,3 @@ int lxc_console(struct lxc_container *c, int ttynum,
 
 	return ret;
 }
-
diff --git a/src/lxc/console.h b/src/lxc/console.h
index fefdb19e2..f2618e9c3 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -62,7 +62,7 @@ struct lxc_tty_state
  *            indication that the console or tty is no longer in use
  * @ttyreq  : the tty requested to be opened, -1 for any, 0 for the console
  */
-extern int  lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttynum);
+extern int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttynum);
 
 /*
  * Create a new pty:
@@ -76,7 +76,7 @@ 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_console_create(struct lxc_conf *);
+extern int lxc_console_create(struct lxc_conf *);
 
 /*
  * Delete a pty created via lxc_console_create():
@@ -102,7 +102,8 @@ 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_conf *);
 
 /*
  * Handle SIGWINCH events on the allocated ptys.
@@ -118,9 +119,8 @@ extern void lxc_console_sigwinch(int sig);
  * - registers SIGWINCH, I/O handlers in the mainloop
  * - performs all necessary cleanup operations
  */
-extern int  lxc_console(struct lxc_container *c, int ttynum,
-		        int stdinfd, int stdoutfd, int stderrfd,
-		        int escape);
+extern int lxc_console(struct lxc_container *c, int ttynum, int stdinfd,
+		       int stdoutfd, int stderrfd, int escape);
 
 /*
  * Allocate one of the ptys given to the container via lxc.tty. Returns an open
@@ -128,8 +128,8 @@ extern int  lxc_console(struct lxc_container *c, int ttynum,
  * Set ttynum to -1 to allocate the first available pty, or to a value within
  * the range specified by lxc.tty to allocate a specific pty.
  */
-extern int  lxc_console_getfd(struct lxc_container *c, int *ttynum,
-			      int *masterfd);
+extern int lxc_console_getfd(struct lxc_container *c, int *ttynum,
+			     int *masterfd);
 
 /*
  * Make fd a duplicate of the standard file descriptors:
@@ -145,7 +145,8 @@ extern int lxc_console_set_stdfds(int fd);
  * This function exits the loop cleanly when an EPOLLHUP event is received.
  */
 extern int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
-		struct lxc_epoll_descr *descr);
+				    struct lxc_epoll_descr *descr);
+
 
 /*
  * Handler for events on the master fd of the pty. To be registered via the
@@ -154,7 +155,7 @@ extern int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
  * This function exits the loop cleanly when an EPOLLHUP event is received.
  */
 extern int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
-		struct lxc_epoll_descr *descr);
+				     struct lxc_epoll_descr *descr);
 
 /*
  * Setup new terminal properties. The old terminal settings are stored in
@@ -162,7 +163,6 @@ extern int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
  */
 extern int lxc_setup_tios(int fd, struct termios *oldtios);
 
-
 /*
  * lxc_console_winsz: propagte winsz from one terminal to another
  *
@@ -200,7 +200,7 @@ extern struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd);
  * declared and defined in mainloop.{c,h} or lxc_console_mainloop_add().
  */
 extern int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
-		struct lxc_epoll_descr *descr);
+				      struct lxc_epoll_descr *descr);
 
 /*
  * lxc_console_sigwinch_fini: uninstall SIGWINCH handler

From ce456e845141ab63d0035617ed737fb32f66e1e3 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 23 May 2017 02:57:46 +0200
Subject: [PATCH 2/7] conf,console: bugfixes

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c    |  3 +++
 src/lxc/console.c | 12 ++++++++++--
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 85805f975..9bfdd1bf7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2713,6 +2713,9 @@ struct lxc_conf *lxc_conf_init(void)
 	new->loglevel = LXC_LOG_PRIORITY_NOTSET;
 	new->personality = -1;
 	new->autodev = 1;
+	new->console.descr = NULL;
+	new->console.tios = NULL;
+	new->console.tty_state = NULL;
 	new->console.log_path = NULL;
 	new->console.log_fd = -1;
 	new->console.path = NULL;
diff --git a/src/lxc/console.c b/src/lxc/console.c
index e628e836b..4cf2e9e46 100755
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -147,12 +147,20 @@ struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
 
 void lxc_console_sigwinch_fini(struct lxc_tty_state *ts)
 {
-	if (ts->sigfd >= 0)
+	if (!ts) {
+		SYSERROR("no sigwinch handler");
+		return;
+	}
+
+	if (ts->sigfd >= 0) {
 		close(ts->sigfd);
+		ts->sigfd = -1;
+	}
 
 	lxc_list_del(&ts->node);
 	sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
 	free(ts);
+	ts = NULL;
 }
 
 static int lxc_console_cb_con(int fd, uint32_t events, void *data,
@@ -340,10 +348,10 @@ static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd)
 		goto err1;
 
 	ts = lxc_console_sigwinch_init(console->peerpty.master, console->master);
+	console->tty_state = ts;
 	if (!ts)
 		goto err1;
 
-	console->tty_state = ts;
 	console->peer = console->peerpty.slave;
 	console->peerpty.busy = sockfd;
 	lxc_console_mainloop_add_peer(console);

From 2574b533d835cfe6c98ad495102d16c973229705 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 21 May 2017 03:59:15 +0200
Subject: [PATCH 3/7] conf{,ile}: add lxc.pts.allocate

If lxc.pts.allocate is set to a non-zero value lxc will allocated the requested
number of pts devices and cache the corresponding file descriptors.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c    |  2 ++
 src/lxc/conf.h    |  4 ++++
 src/lxc/confile.c | 14 +++++++++++++-
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 9bfdd1bf7..05b0a1076 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -5034,6 +5034,8 @@ int lxc_clear_simple_config_item(struct lxc_conf *c, const char *key)
 		c->monitor_unshare = 0;
 	} else if (strcmp(key, "lxc.pts") == 0) {
 		c->pts = 0;
+	} else if (strcmp(key, "lxc.pts.allocate") == 0) {
+		c->pts_allocate = 0;
 	} else {
 		return -1;
 	}
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index a0bb05b0a..ace5033d1 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -408,6 +408,10 @@ struct lxc_conf {
 
 	/* RLIMIT_* limits */
 	struct lxc_list limits;
+
+	/* Number of pts devices to allocate before the container's init starts.
+	 */
+	unsigned int pts_allocate;
 };
 
 #ifdef HAVE_TLS
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 4114e9fff..25da2ed8e 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -124,10 +124,11 @@ static int config_init_gid(const char *, const char *, struct lxc_conf *);
 static int config_ephemeral(const char *, const char *, struct lxc_conf *);
 static int config_no_new_privs(const char *, const char *, struct lxc_conf *);
 static int config_limit(const char *, const char *, struct lxc_conf *);
+static int config_pts_allocate(const char *, const char *, struct lxc_conf *);
 
 static struct lxc_config_t config[] = {
-
 	{ "lxc.arch",                 config_personality          },
+	{ "lxc.pts.allocate",	      config_pts_allocate	  },
 	{ "lxc.pts",                  config_pts                  },
 	{ "lxc.tty",                  config_tty                  },
 	{ "lxc.devttydir",            config_ttydir               },
@@ -2839,6 +2840,8 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
 		return lxc_get_limit_entry(c, retv, inlen, "all");
 	else if (strncmp(key, "lxc.limit.", 10) == 0) // specific limit
 		return lxc_get_limit_entry(c, retv, inlen, key + 10);
+	else if (strcmp(key, "lxc.pts.allocate") == 0)
+		return lxc_get_conf_int(c, retv, inlen, c->pts_allocate);
 	else return -1;
 
 	if (!v)
@@ -3262,3 +3265,12 @@ static int config_no_new_privs(const char *key, const char *value,
 
 	return 0;
 }
+
+static int config_pts_allocate(const char *key, const char *value,
+			       struct lxc_conf *lxc_conf)
+{
+	if (lxc_safe_uint(value, &lxc_conf->pts_allocate) < 0)
+		return -1;
+
+	return 0;
+}

From 8a287a3f14fff287c78f227a201d614defd551ad Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 21 May 2017 04:12:38 +0200
Subject: [PATCH 4/7] {conf,start}: allocate pty fds

Allocate pts devices for /dev/tty<N> and for attach().

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c  | 40 +++++++++++++++++++++++++---------------
 src/lxc/start.c |  9 +++++----
 2 files changed, 30 insertions(+), 19 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 05b0a1076..f5cfcb1df 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -974,7 +974,7 @@ static int lxc_setup_tty(struct lxc_conf *conf)
 	if (!conf->rootfs.path)
 		return 0;
 
-	for (i = 0; i < tty_info->nbtty; i++) {
+	for (i = 0; i < conf->tty; i++) {
 		struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
 
 		ret = snprintf(path, sizeof(path), "/dev/tty%d", i + 1);
@@ -1059,7 +1059,7 @@ static int lxc_setup_tty(struct lxc_conf *conf)
 		}
 	}
 
-	INFO("finished setting up %d /dev/tty<N> device(s)", tty_info->nbtty);
+	INFO("finished setting up %d /dev/tty<N> device(s)", conf->tty);
 	return 0;
 }
 
@@ -3660,22 +3660,28 @@ int lxc_find_gateway_addresses(struct lxc_handler *handler)
 	return 0;
 }
 
-int lxc_create_tty(const char *name, struct lxc_conf *conf)
+/*
+ * lxc_allocate_pty_devices() allocates the requested number of pty master and
+ * slave file descriptors in the containers mount namespace. This presupposes
+ * that a fresh devpts instance has been mounted!
+ */
+int lxc_allocate_pty_devices(const char *name, struct lxc_conf *conf)
 {
 	struct lxc_tty_info *tty_info = &conf->tty_info;
 	int i, ret;
+	int npty = conf->tty + conf->pts_allocate;
 
 	/* no tty in the configuration */
-	if (!conf->tty)
+	if (!npty)
 		return 0;
 
-	tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty);
+	tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * npty);
 	if (!tty_info->pty_info) {
 		SYSERROR("failed to allocate struct *pty_info");
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < conf->tty; i++) {
+	for (i = 0; i < npty; i++) {
 		struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
 
 		process_lock();
@@ -3708,9 +3714,8 @@ int lxc_create_tty(const char *name, struct lxc_conf *conf)
 		pty_info->busy = 0;
 	}
 
-	tty_info->nbtty = conf->tty;
-
-	INFO("finished allocating %d pts devices", conf->tty);
+	tty_info->nbtty = npty;
+	INFO("finished allocating %d pts devices", npty);
 	return 0;
 }
 
@@ -3721,13 +3726,18 @@ void lxc_delete_tty(struct lxc_tty_info *tty_info)
 	for (i = 0; i < tty_info->nbtty; i++) {
 		struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
 
-		close(pty_info->master);
-		close(pty_info->slave);
+		if (pty_info->master >= 0)
+			close(pty_info->master);
+
+		if (pty_info->slave >= 0)
+			close(pty_info->slave);
 	}
 
-	free(tty_info->pty_info);
-	tty_info->pty_info = NULL;
-	tty_info->nbtty = 0;
+	if (tty_info->pty_info) {
+		free(tty_info->pty_info);
+		tty_info->pty_info = NULL;
+		tty_info->nbtty = 0;
+	}
 }
 
 /*
@@ -4206,7 +4216,7 @@ int lxc_setup(struct lxc_handler *handler)
 		return -1;
 	}
 
-	if (lxc_create_tty(name, lxc_conf)) {
+	if (lxc_allocate_pty_devices(name, lxc_conf)) {
 		ERROR("failed to create the ttys");
 		return -1;
 	}
diff --git a/src/lxc/start.c b/src/lxc/start.c
index f1b3f8e11..7637952dd 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -1024,15 +1024,16 @@ 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;
+	int npty = conf->tty + conf->pts_allocate;
 
-	if (!conf->tty)
+	if (!npty)
 		return 0;
 
-	tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty);
+	tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * npty);
 	if (!tty_info->pty_info)
 		return -1;
 
-	for (i = 0; i < conf->tty; i++) {
+	for (i = 0; i < npty; i++) {
 		struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
 		pty_info->busy = 0;
 		if (recv_fd(sock, &pty_info->slave) < 0 ||
@@ -1041,7 +1042,7 @@ static int recv_ttys_from_child(struct lxc_handler *handler)
 			return -1;
 		}
 	}
-	tty_info->nbtty = conf->tty;
+	tty_info->nbtty = npty;
 
 	return 0;
 }

From e3928530abc420ed006a156e81e2bb5f68b954fa Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 23 May 2017 00:38:11 +0200
Subject: [PATCH 5/7] console: tweak /dev/tty<N> allocation

Only look at conf->tty entries.

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

diff --git a/src/lxc/console.c b/src/lxc/console.c
index 4cf2e9e46..0eea6e4e8 100755
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -378,7 +378,7 @@ int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
 	}
 
 	if (*ttyreq > 0) {
-		if (*ttyreq > tty_info->nbtty)
+		if (*ttyreq > conf->tty)
 			goto out;
 
 		if (tty_info->pty_info[*ttyreq - 1].busy)
@@ -390,11 +390,12 @@ int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
 	}
 
 	/* search for next available tty, fixup index tty1 => [0] */
-	for (ttynum = 1; ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; ttynum++)
-		;
+	ttynum = 1;
+	while (ttynum <= conf->tty && tty_info->pty_info[ttynum - 1].busy)
+		ttynum++;
 
 	/* we didn't find any available slot for tty */
-	if (ttynum > tty_info->nbtty)
+	if (ttynum > conf->tty)
 		goto out;
 
 	*ttyreq = ttynum;

From 8db6a41f47095168e5ee22f39233dbcece0be5bf Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 23 May 2017 00:39:17 +0200
Subject: [PATCH 6/7] console: add lxc_pty_allocate()

lxc_pty_allocate() allows to retrieve a simple pty {master,slave} file
descriptor pair from the container which we have setup before the container's
init started.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/console.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/console.h | 11 +++++++++++
 2 files changed, 66 insertions(+)

diff --git a/src/lxc/console.c b/src/lxc/console.c
index 0eea6e4e8..2205be587 100755
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -761,3 +761,58 @@ int lxc_console(struct lxc_container *c, int ttynum,
 
 	return ret;
 }
+
+int lxc_pty_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq,
+		     int *masterfd, int *slavefd, void *data)
+{
+	int ttynum;
+	struct lxc_tty_info *tty_info = &conf->tty_info;
+
+	if (*ttyreq == LXC_GET_TTY_FD) {
+		ERROR("cannot allocate /dev/tty<N> devices as simple pty devices");
+		goto out;
+	}
+
+	if (*ttyreq == 0) {
+		ERROR("cannot allocate /dev/console as simple pty device");
+		return 0;
+	}
+
+	if (*ttyreq > 0) {
+		if (*ttyreq <= conf->tty) {
+			ERROR("cannot allocate /dev/tty<N> as simple pty device");
+			goto out;
+		}
+
+		if (*ttyreq > tty_info->nbtty)
+			goto out;
+
+		if (tty_info->pty_info[*ttyreq - 1].busy)
+			goto out;
+
+		/* the requested tty is available */
+		ttynum = *ttyreq;
+		goto out_tty;
+	}
+
+	DEBUG("trying to find an available pty master slave fd pair");
+	/* search for next available pty devices starting at conf->tty + 1 after
+	 * the last /dev/tty<N> device the user requested
+	 */
+	ttynum = conf->tty + 1;
+	while (ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy)
+		ttynum++;
+
+	/* we didn't find any available slot for tty */
+	if (ttynum > tty_info->nbtty)
+		return -1;
+
+	*ttyreq = ttynum;
+
+out_tty:
+	tty_info->pty_info[ttynum - 1].busy = sockfd;
+	*masterfd = tty_info->pty_info[ttynum - 1].master;
+	*slavefd = tty_info->pty_info[ttynum - 1].slave;
+out:
+	return 0;
+}
diff --git a/src/lxc/console.h b/src/lxc/console.h
index f2618e9c3..bca8cecfe 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -27,6 +27,9 @@
 #include "conf.h"
 #include "list.h"
 
+#define LXC_GET_TTY_FD -1
+#define LXC_GET_PTY_FDS -2
+
 struct lxc_epoll_descr; /* defined in mainloop.h */
 struct lxc_container; /* defined in lxccontainer.h */
 struct lxc_tty_state
@@ -215,4 +218,12 @@ extern int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
  */
 extern void lxc_console_sigwinch_fini(struct lxc_tty_state *ts);
 
+/*
+ * lxc_pty_allocate: allocate pty master slave file descriptor pair
+ * @data : can be used to extend the function to send additional data about the
+ *	   pty device, e.g. it's /dev/pts/<n> name.
+ */
+extern int lxc_pty_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq,
+			    int *masterfd, int *slavefd, void *data);
+
 #endif

From bc3902eef8e3f9ef72cad4b8aa61b0d392dbfbd4 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 23 May 2017 00:42:26 +0200
Subject: [PATCH 7/7] commands: add lxc_cmd_get_pty()

lxc_cmd_get_pty() allows to retrieve a pty {master,slave} pair via the commands
api from the container. Note that the pty device needs to be setup before the
container has started so that we can guarantee it hasn't been tampered with.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/lxc/commands.h |  13 ++++
 2 files changed, 176 insertions(+), 7 deletions(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 27c8c084f..9abf25c4c 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -142,6 +142,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
 		[LXC_CMD_GET_CONFIG_ITEM] = "get_config_item",
 		[LXC_CMD_GET_NAME]        = "get_name",
 		[LXC_CMD_GET_LXCPATH]     = "get_lxcpath",
+		[LXC_CMD_GET_PTY]	  = "get_pty_fd",
 	};
 
 	if (cmd >= LXC_CMD_MAX)
@@ -168,14 +169,65 @@ 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, rspfd = 0;
 	struct lxc_cmd_rsp *rsp = &cmd->rsp;
 
-	ret = lxc_abstract_unix_recv_fd(sock, &rspfd, rsp, sizeof(*rsp));
-	if (ret < 0) {
-		WARN("Command %s failed to receive response: %s.",
-		     lxc_cmd_str(cmd->req.cmd), strerror(errno));
-		return -1;
+	if (cmd->req.cmd == LXC_CMD_GET_PTY) {
+		struct lxc_cmd_pty_rsp_data *ptydata;
+
+		struct msghdr msg = {0};
+		struct iovec iov = {0};
+		struct cmsghdr *cmsg = NULL;
+		int ptyfds[CMD_PTY_FD_PAIR] = {-1, -1};
+		int *ptyfdptr = NULL;
+		char cmsgbuf[CMSG_SPACE(sizeof(ptyfds))] = {0};
+
+		msg.msg_name = NULL;
+		msg.msg_namelen = 0;
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+
+		iov.iov_base = rsp;
+		iov.iov_len = sizeof(*rsp);
+		msg.msg_iov = &iov;
+		msg.msg_iovlen = 1;
+
+		ret = recvmsg(sock, &msg, 0);
+		if (ret <= 0)
+			return 0;
+
+		cmsg = CMSG_FIRSTHDR(&msg);
+
+		if (cmsg &&
+		    cmsg->cmsg_len == CMSG_LEN(CMD_PTY_FD_PAIR * sizeof(int)) &&
+		    cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_RIGHTS) {
+			ptyfdptr = (int *)(CMSG_DATA(cmsg));
+			memcpy(ptyfds, ptyfdptr, CMD_PTY_FD_PAIR * sizeof(int));
+		}
+
+		ptydata = malloc(sizeof(*ptydata));
+		if (!ptydata) {
+			ERROR("Command %s couldn't allocate response buffer.",
+			      lxc_cmd_str(cmd->req.cmd));
+			return -1;
+		}
+
+		ptydata->masterfd = ptyfds[CMD_PTY_MASTER_FD];
+		ptydata->slavefd = ptyfds[CMD_PTY_SLAVE_FD];
+		ptydata->ttynum = PTR_TO_INT(rsp->data);
+		rsp->data = ptydata;
+		DEBUG("received master fd %d for pty %d",
+		      ptyfds[CMD_PTY_MASTER_FD], ptydata->ttynum);
+		DEBUG("received slave fd %d for pty %d",
+		      ptyfds[CMD_PTY_SLAVE_FD], ptydata->ttynum);
+	} else {
+		ret = lxc_abstract_unix_recv_fd(sock, &rspfd, rsp, sizeof(*rsp));
+		if (ret < 0) {
+			WARN("Command %s failed to receive response: %s.",
+					lxc_cmd_str(cmd->req.cmd), strerror(errno));
+			return -1;
+		}
 	}
 
 	if (cmd->req.cmd == LXC_CMD_CONSOLE) {
@@ -283,7 +335,10 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
 	char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
 	char *offset = &path[1];
 	size_t len;
-	int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE;
+	bool stay_connected = false;
+
+	if (cmd->req.cmd == LXC_CMD_CONSOLE || cmd->req.cmd == LXC_CMD_GET_PTY)
+		stay_connected = true;
 
 	*stopped = 0;
 
@@ -843,6 +898,106 @@ static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req,
 	return lxc_cmd_rsp_send(fd, &rsp);
 }
 
+int lxc_cmd_get_pty(const char *name, int *ttynum, int *masterfd, int *slavefd,
+		    const char *lxcpath)
+{
+	int ret, stopped;
+	struct lxc_cmd_pty_rsp_data *rspdata;
+	struct lxc_cmd_rr cmd = {
+	    .req = {.cmd = LXC_CMD_GET_PTY, .data = INT_TO_PTR(*ttynum)},
+	};
+
+	ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
+	if (ret < 0)
+		return ret;
+
+	if (cmd.rsp.ret < 0) {
+		ERROR("Console access denied: %s.", strerror(-cmd.rsp.ret));
+		ret = -1;
+		goto out;
+	}
+
+	if (ret == 0) {
+		DEBUG("Console %d invalid, busy or all consoles busy.", *ttynum);
+		ret = -1;
+		goto out;
+	}
+
+	rspdata = cmd.rsp.data;
+	if (rspdata->masterfd < 0) {
+		ERROR("Unable to allocate master fd for tty %d.", *ttynum);
+		goto out;
+	}
+
+	if (rspdata->slavefd < 0) {
+		ERROR("Unable to allocate slave fd for tty %d.", *ttynum);
+		goto out;
+	}
+
+	ret = cmd.rsp.ret; /* sock fd */
+	*masterfd = rspdata->masterfd;
+	*slavefd = rspdata->slavefd;
+	*ttynum = rspdata->ttynum;
+	INFO("tty %d allocated fd %d sock %d.", *ttynum, *masterfd, ret);
+out:
+	free(cmd.rsp.data);
+	return ret;
+}
+
+static int lxc_cmd_get_pty_callback(int fd, struct lxc_cmd_req *req,
+				    struct lxc_handler *handler)
+{
+	int ret;
+	int ttynum;
+	struct lxc_cmd_rsp data;
+
+	struct msghdr msg = {0};
+	struct iovec iov = {0};
+	struct cmsghdr *cmsg = NULL;
+	int ptyfds[CMD_PTY_FD_PAIR] = {-1, -1};
+	int *ptyfdptr = NULL;
+	char cmsgbuf[CMSG_SPACE(sizeof(ptyfds))] = {0};
+
+	ttynum = PTR_TO_INT(req->data);
+	ret = lxc_pty_allocate(handler->conf, fd, &ttynum,
+			       &ptyfds[CMD_PTY_MASTER_FD],
+			       &ptyfds[CMD_PTY_SLAVE_FD], NULL);
+	if (ret < 0)
+		return 1;
+
+	DEBUG("sending master fd %d for pty %d", ptyfds[CMD_PTY_MASTER_FD], ttynum);
+	DEBUG("sending slave fd %d for pty %d", ptyfds[CMD_PTY_SLAVE_FD], ttynum);
+
+	memset(&data, 0, sizeof(data));
+	data.data = INT_TO_PTR(ttynum);
+
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_RIGHTS;
+	cmsg->cmsg_len = CMSG_LEN(CMD_PTY_FD_PAIR * sizeof(int));
+
+	ptyfdptr = (int *)(CMSG_DATA(cmsg));
+	memcpy(ptyfdptr, ptyfds, CMD_PTY_FD_PAIR * sizeof(int));
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_controllen = cmsg->cmsg_len;
+
+	iov.iov_base = &data;
+	iov.iov_len = sizeof(data);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+	if (ret < 0)
+		return 1;
+
+	return 0;
+}
+
 static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
 			   struct lxc_handler *handler)
 {
@@ -859,6 +1014,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
 		[LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback,
 		[LXC_CMD_GET_NAME]        = lxc_cmd_get_name_callback,
 		[LXC_CMD_GET_LXCPATH]     = lxc_cmd_get_lxcpath_callback,
+		[LXC_CMD_GET_PTY]	  = lxc_cmd_get_pty_callback,
 	};
 
 	if (req->cmd >= LXC_CMD_MAX) {
diff --git a/src/lxc/commands.h b/src/lxc/commands.h
index 184eefa09..8605298ad 100644
--- a/src/lxc/commands.h
+++ b/src/lxc/commands.h
@@ -43,6 +43,7 @@ typedef enum {
 	LXC_CMD_GET_CONFIG_ITEM,
 	LXC_CMD_GET_NAME,
 	LXC_CMD_GET_LXCPATH,
+	LXC_CMD_GET_PTY,
 	LXC_CMD_MAX,
 } lxc_cmd_t;
 
@@ -68,9 +69,21 @@ struct lxc_cmd_console_rsp_data {
 	int ttynum;
 };
 
+#define CMD_PTY_FD_PAIR 2
+#define CMD_PTY_MASTER_FD 0
+#define CMD_PTY_SLAVE_FD 1
+
+struct lxc_cmd_pty_rsp_data {
+	int masterfd;
+	int slavefd;
+	int ttynum;
+};
+
 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);
+extern int lxc_cmd_get_pty(const char *name, int *ttynum, int *masterfd,
+			   int *slavefd, const char *lxcpath);
 /*
  * Get the 'real' cgroup path (as seen in /proc/self/cgroup) for a container
  * for a particular subsystem


More information about the lxc-devel mailing list