[lxc-devel] [PATCH v2] console API improvements

Dwight Engen dwight.engen at oracle.com
Thu Jun 13 15:58:46 UTC 2013


On Wed, 12 Jun 2013 11:15:10 -0500
Serge Hallyn <serge.hallyn at ubuntu.com> wrote:

> Quoting Dwight Engen (dwight.engen at oracle.com):
> > Add a higher level console API that opens a tty/console and runs the
> > mainloop as well. Rename existing API to console_getfd(). Use these
> > in the python binding.
> > 
> > Allow attaching a console peer after container bootup, including if
> > the container was launched with -d. This is made possible by
> > allocation of a "proxy" pty as the peer when the console is
> > attached to.
> > 
> > Improve handling of SIGWINCH, the pty size will be correctly set at
> > the beginning of a session and future changes when using the
> > lxc_console() API will be propagated to it as well.
> > 
> > Refactor some common code between lxc_console.c and console.c. The
> > variable wait4q (renamed to saw_escape) was static, making the
> > mainloop callback not safe across threads. This wasn't a problem
> > when the callback was in the non-threaded lxc-console, but now that
> > it is internal to console.c, we have to take care of it. This is
> > now contained in a per-tty state structure.
> > 
> > Don't attempt to open /dev/null as the console peer since /dev/null
> > cannot be added to the mainloop (epoll_ctl() fails with EPERM).
> > This isn't needed to get the console setup (and the log to work)
> > since the case of not having a peer at console init time has to be
> > handled to allow for attaching to it later.
> > 
> > Move signalfd libc wrapper/replacement to utils.h.
> > 
> > Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
> > ---
> >  doc/lxc-console.sgml.in                  |  11 +-
> >  src/lxc/commands.c                       |  64 +--
> >  src/lxc/commands.h                       |   2 +
> >  src/lxc/conf.c                           |  11 +-
> >  src/lxc/conf.h                           |   5 +
> >  src/lxc/console.c                        | 742
> > +++++++++++++++++++++++++------
> > src/lxc/console.h                        |  18 +-
> > src/lxc/lxc.h                            |   9 -
> > src/lxc/lxc_console.c                    | 189 +-------
> > src/lxc/lxccontainer.c                   |  12 +-
> > src/lxc/lxccontainer.h                   |  21 +-
> > src/lxc/start.c                          |  83 +---
> > src/lxc/utils.h                          |  65 +++
> > src/python-lxc/examples/pyconsole-vte.py |  58 +++
> > src/python-lxc/examples/pyconsole.py     |  33 ++
> > src/python-lxc/lxc.c                     |  46 ++
> > src/python-lxc/lxc/__init__.py           |  18 +-
> > src/tests/console.c                      |   6 +- 18 files changed,
> > 962 insertions(+), 431 deletions(-) create mode 100755
> > src/python-lxc/examples/pyconsole-vte.py create mode 100755
> > src/python-lxc/examples/pyconsole.py
> > 
> > diff --git a/doc/lxc-console.sgml.in b/doc/lxc-console.sgml.in
> > index 9299778..f4737d1 100644
> > --- a/doc/lxc-console.sgml.in
> > +++ b/doc/lxc-console.sgml.in
> > @@ -78,6 +78,12 @@ Foundation, Inc., 59 Temple Place, Suite 330,
> > Boston, MA 02111-1307 USA </para>
> >  
> >      <para>
> > +      A <replaceable>ttynum</replaceable> of 0 may be given to
> > attach
> > +      to the container's /dev/console instead of its
> > +      dev/tty<<replaceable>ttynum</replaceable>>.
> > +    </para>
> > +
> > +    <para>
> >        A keyboard escape sequence may be used to disconnect from
> > the tty and quit lxc-console. The default escape sequence is
> > <Ctrl+a q>. </para>
> > @@ -107,8 +113,9 @@ Foundation, Inc., 59 Temple Place, Suite 330,
> > Boston, MA 02111-1307 USA </term>
> >  	<listitem>
> >  	  <para>
> > -	    Specify the tty number to connect, if not specified a
> > tty
> > -	    number will be automatically choosen by the container.
> > +	    Specify the tty number to connect to or 0 for the
> > console. If not
> > +	    specified the next available tty number will be
> > automatically
> > +	    choosen by the container.
> >  	  </para>
> >  	</listitem>
> >        </varlistentry>
> > diff --git a/src/lxc/commands.c b/src/lxc/commands.c
> > index b4afc07..b23eb98 100644
> > --- a/src/lxc/commands.c
> > +++ b/src/lxc/commands.c
> > @@ -40,6 +40,7 @@
> >  #include <lxc/utils.h>
> >  
> >  #include "commands.h"
> > +#include "console.h"
> >  #include "confile.h"
> >  #include "mainloop.h"
> >  #include "af_unix.h"
> > @@ -546,6 +547,37 @@ static int lxc_cmd_stop_callback(int fd,
> > struct lxc_cmd_req *req, }
> >  
> >  /*
> > + * lxc_cmd_console_winch: To process as if a SIGWINCH were received
> > + *
> > + * @name      : name of container to connect to
> > + * @lxcpath   : the lxcpath in which the container is running
> > + *
> > + * Returns 0 on success, < 0 on failure
> > + */
> > +int lxc_cmd_console_winch(const char *name, const char *lxcpath)
> > +{
> > +	int ret, stopped = 0;
> > +	struct lxc_cmd_rr cmd = {
> > +		.req = { .cmd = LXC_CMD_CONSOLE_WINCH },
> > +	};
> > +
> > +	ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +static int lxc_cmd_console_winch_callback(int fd, struct
> > lxc_cmd_req *req,
> > +					  struct lxc_handler
> > *handler) +{
> > +	struct lxc_cmd_rsp rsp = { .data = 0 };
> > +
> > +	lxc_console_sigwinch(SIGWINCH);
> > +	return lxc_cmd_rsp_send(fd, &rsp);
> > +}
> > +
> > +/*
> >   * lxc_cmd_console: Open an fd to a tty in the container
> >   *
> >   * @name           : name of container to connect to
> > @@ -599,39 +631,21 @@ static int lxc_cmd_console_callback(int fd,
> > struct lxc_cmd_req *req, struct lxc_handler *handler)
> >  {
> >  	int ttynum = PTR_TO_INT(req->data);
> > -	struct lxc_tty_info *tty_info = &handler->conf->tty_info;
> > +	int masterfd;
> >  	struct lxc_cmd_rsp rsp;
> >  
> > -	if (ttynum > 0) {
> > -		if (ttynum > tty_info->nbtty)
> > -			goto out_close;
> > -
> > -		if (tty_info->pty_info[ttynum - 1].busy)
> > -			goto out_close;
> > -
> > -		/* the requested tty is available */
> > -		goto out_send;
> > -	}
> > -
> > -	/* search for next available tty, fixup index tty1 => [0]
> > */
> > -	for (ttynum = 1;
> > -	     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)
> > +	masterfd = lxc_console_allocate(handler->conf, fd,
> > &ttynum);
> > +	if (masterfd < 0)
> >  		goto out_close;
> >  
> > -out_send:
> >  	memset(&rsp, 0, sizeof(rsp));
> >  	rsp.data = INT_TO_PTR(ttynum);
> > -	if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum -
> > 1].master,
> > -				&rsp, sizeof(rsp)) < 0) {
> > +	if (lxc_af_unix_send_fd(fd, masterfd, &rsp, sizeof(rsp)) <
> > 0) { ERROR("failed to send tty to client");
> > +		lxc_console_free(handler->conf, fd);
> >  		goto out_close;
> >  	}
> >  
> > -	tty_info->pty_info[ttynum - 1].busy = fd;
> >  	return 0;
> >  
> >  out_close:
> > @@ -650,6 +664,7 @@ static int lxc_cmd_process(int fd, struct
> > lxc_cmd_req *req, 
> >  	callback cb[LXC_CMD_MAX] = {
> >  		[LXC_CMD_CONSOLE]         =
> > lxc_cmd_console_callback,
> > +		[LXC_CMD_CONSOLE_WINCH]   =
> > lxc_cmd_console_winch_callback, [LXC_CMD_STOP]            =
> > lxc_cmd_stop_callback, [LXC_CMD_GET_STATE]       =
> > lxc_cmd_get_state_callback, [LXC_CMD_GET_INIT_PID]    =
> > lxc_cmd_get_init_pid_callback, @@ -668,8 +683,7 @@ static int
> > lxc_cmd_process(int fd, struct lxc_cmd_req *req, static void
> > lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, struct
> > lxc_epoll_descr *descr) {
> > -	extern void lxc_console_remove_fd(int, struct lxc_tty_info
> > *);
> > -	lxc_console_remove_fd(fd, &handler->conf->tty_info);
> > +	lxc_console_free(handler->conf, fd);
> >  	lxc_mainloop_del_handler(descr, fd);
> >  	close(fd);
> >  }
> > diff --git a/src/lxc/commands.h b/src/lxc/commands.h
> > index c3738cd..46806cb 100644
> > --- a/src/lxc/commands.h
> > +++ b/src/lxc/commands.h
> > @@ -34,6 +34,7 @@
> >  
> >  typedef enum {
> >  	LXC_CMD_CONSOLE,
> > +	LXC_CMD_CONSOLE_WINCH,
> >  	LXC_CMD_STOP,
> >  	LXC_CMD_GET_STATE,
> >  	LXC_CMD_GET_INIT_PID,
> > @@ -65,6 +66,7 @@ struct lxc_cmd_console_rsp_data {
> >  	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 char *lxc_cmd_get_cgroup_path(const char *name, const char
> > *lxcpath); diff --git a/src/lxc/conf.c b/src/lxc/conf.c
> > index 5700eff..90aea60 100644
> > --- a/src/lxc/conf.c
> > +++ b/src/lxc/conf.c
> > @@ -1293,8 +1293,8 @@ static int setup_dev_console(const struct
> > lxc_rootfs *rootfs, return 0;
> >  	}
> >  
> > -	if (console->peer == -1) {
> > -		INFO("no console output required");
> > +	if (console->master < 0) {
> > +		INFO("no console");
> >  		return 0;
> >  	}
> >  
> > @@ -1359,8 +1359,8 @@ static int setup_ttydir_console(const struct
> > lxc_rootfs *rootfs, if (ret >= 0)
> >  		close(ret);
> >  
> > -	if (console->peer == -1) {
> > -		INFO("no console output required");
> > +	if (console->master < 0) {
> > +		INFO("no console");
> >  		return 0;
> >  	}
> >  
> > @@ -2127,6 +2127,9 @@ struct lxc_conf *lxc_conf_init(void)
> >  	new->console.log_fd = -1;
> >  	new->console.path = NULL;
> >  	new->console.peer = -1;
> > +	new->console.peerpty.busy = -1;
> > +	new->console.peerpty.master = -1;
> > +	new->console.peerpty.slave = -1;
> >  	new->console.master = -1;
> >  	new->console.slave = -1;
> >  	new->console.name[0] = '\0';
> > diff --git a/src/lxc/conf.h b/src/lxc/conf.h
> > index a2efa7c..2fd3ab1 100644
> > --- a/src/lxc/conf.h
> > +++ b/src/lxc/conf.h
> > @@ -188,6 +188,8 @@ struct lxc_tty_info {
> >  	struct lxc_pty_info *pty_info;
> >  };
> >  
> > +struct lxc_tty_state;
> > +
> >  /*
> >   * Defines the structure to store the console information
> >   * @peer   : the file descriptor put/get console traffic
> > @@ -197,11 +199,14 @@ struct lxc_console {
> >  	int slave;
> >  	int master;
> >  	int peer;
> > +	struct lxc_pty_info peerpty;
> > +	struct lxc_epoll_descr *descr;
> >  	char *path;
> >  	char *log_path;
> >  	int log_fd;
> >  	char name[MAXPATHLEN];
> >  	struct termios *tios;
> > +	struct lxc_tty_state *tty_state;
> >  };
> >  
> >  /*
> > diff --git a/src/lxc/console.c b/src/lxc/console.c
> > index 93c16b5..3720c5b 100644
> > --- a/src/lxc/console.c
> > +++ b/src/lxc/console.c
> > @@ -21,6 +21,8 @@
> >   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> > 02111-1307 USA */
> >  
> > +#include <assert.h>
> > +#include <signal.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <unistd.h>
> > @@ -29,6 +31,7 @@
> >  #include <sys/types.h>
> >  #include <termios.h>
> >  
> > +#include "lxccontainer.h"
> >  #include "log.h"
> >  #include "conf.h"
> >  #include "config.h"
> > @@ -37,6 +40,8 @@
> >  #include "commands.h"
> >  #include "mainloop.h"
> >  #include "af_unix.h"
> > +#include "lxclock.h"
> > +#include "utils.h"
> >  
> >  #if HAVE_PTY_H
> >  #include <pty.h>
> > @@ -46,156 +51,490 @@
> >  
> >  lxc_log_define(lxc_console, lxc);
> >  
> > -extern void lxc_console_remove_fd(int fd, struct lxc_tty_info
> > *tty_info) -{
> > -	int i;
> > +static struct lxc_list lxc_ttys;
> >  
> > -	for (i = 0; i < tty_info->nbtty; i++) {
> > -
> > -		if (tty_info->pty_info[i].busy != fd)
> > -			continue;
> > +typedef void (*sighandler_t)(int);
> > +struct lxc_tty_state
> > +{
> > +	struct lxc_list node;
> > +	int stdinfd;
> > +	int stdoutfd;
> > +	int masterfd;
> > +	int escape;
> > +	int saw_escape;
> > +	const char *winch_proxy;
> > +	const char *winch_proxy_lxcpath;
> > +	int sigfd;
> > +	sigset_t oldmask;
> > +};
> > +
> > +__attribute__((constructor))
> > +void lxc_console_init(void)
> > +{
> > +	lxc_list_init(&lxc_ttys);
> > +}
> >  
> > -		tty_info->pty_info[i].busy = 0;
> > +/* lxc_console_winsz: propagte winsz from one terminal to another
> > + *
> > + * @srcfd : terminal to get size from (typically a slave pty)
> > + * @dstfd : terminal to set size on (typically a master pty)
> > + */
> > +static void lxc_console_winsz(int srcfd, int dstfd)
> > +{
> > +	struct winsize wsz;
> > +	if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) {
> > +		DEBUG("set winsz dstfd:%d cols:%d rows:%d", dstfd,
> > +		      wsz.ws_col, wsz.ws_row);
> > +		ioctl(dstfd, TIOCSWINSZ, &wsz);
> >  	}
> > +}
> >  
> > -	return;
> > +static void lxc_console_winch(struct lxc_tty_state *ts)
> > +{
> > +	lxc_console_winsz(ts->stdinfd, ts->masterfd);
> > +	if (ts->winch_proxy) {
> > +		lxc_cmd_console_winch(ts->winch_proxy,
> > +				      ts->winch_proxy_lxcpath);
> > +	}
> >  }
> >  
> > -static int get_default_console(char **console)
> > +void lxc_console_sigwinch(int sig)
> >  {
> > -	int fd;
> > +	if (process_lock() == 0) {
> > +		struct lxc_list *it;
> > +		struct lxc_tty_state *ts;
> >  
> > -	if (!access("/dev/tty", F_OK)) {
> > -		fd = open("/dev/tty", O_RDWR);
> > -		if (fd >= 0) {
> > -			close(fd);
> > -			*console = strdup("/dev/tty");
> > -			goto out;
> > +		lxc_list_for_each(it, &lxc_ttys) {
> > +			ts = it->elem;
> > +			lxc_console_winch(ts);
> >  		}
> > +		process_unlock();
> >  	}
> > +}
> >  
> > -	if (!access("/dev/null", F_OK)) {
> > -		*console = strdup("/dev/null");
> > -		goto out;
> > +static int lxc_console_cb_sigwinch_fd(int fd, void *cbdata,
> > +				      struct lxc_epoll_descr
> > *descr) +{
> > +	struct signalfd_siginfo siginfo;
> > +	struct lxc_tty_state *ts = cbdata;
> > +
> > +	if (read(fd, &siginfo, sizeof(siginfo)) < 0) {
> > +		ERROR("failed to read signal info");
> > +		return -1;
> >  	}
> >  
> > -	ERROR("No suitable default console");
> > +	lxc_console_winch(ts);
> > +	return 0;
> > +}
> > +
> > +/*
> > + * lxc_console_sigwinch_init: install SIGWINCH handler
> > + *
> > + * @srcfd  : src for winsz in SIGWINCH handler
> > + * @dstfd  : dst for winsz in SIGWINCH handler
> > + *
> > + * Returns lxc_tty_state structure on success or NULL on failure.
> > The sigfd
> > + * member of the returned lxc_tty_state can be
> > select()/poll()ed/epoll()ed
> > + * on (ie added to a mainloop) for SIGWINCH.
> > + *
> > + * Must be called with process_lock held to protect the lxc_ttys
> > list, or
> > + * from a non-threaded context.
> 
> Would it be worth putting lxc_ttys list in lxc_container struct,
> passing in the lxc_container and doing a mem_lock?

I'm not sure that makes sense, the lxc_ttys list is per process. It
exists so that when the process is the recipient of an
LXC_CMD_CONSOLE_WINCH it can iterate this list and update the size of
any terminals it has open. I don't think there is any existing list of
all containers the process may have open, but I guess we could do that
instead and then the lxc_ttys list could be hung off the lxc_container
struct, but I'm not sure that is what you were suggesting :)

To clarify, the LXC_CMD_CONSOLE_WINCH mechanism is only used in the
case of /dev/console peer proxy pty, ie. when you start out without
specifying a console (lxc-start -d) and then attach to the console
afterwards (lxc-console -t 0). Its purpose is to forward the winch from
the peer proxy pty to the real (console) pty.
 
> It seems like the cleaner thing to do (if it suffices), but we can do
> it as a follow-on later if we agree.  (No need to rework this patch,
> I'm saying)





More information about the lxc-devel mailing list