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.en...@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&lt;<replaceable>ttynum</replaceable>&gt;.
+    </para>
+
+    <para>
       A keyboard escape sequence may be used to disconnect from the tty
       and quit lxc-console. The default escape sequence is &lt;Ctrl+a q&gt;.
     </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.
+ *
+ * Note that SIGWINCH isn't installed as a classic asychronous handler,
+ * rather signalfd(2) is used so that we can handle the signal when we're
+ * ready for it. This avoids deadlocks since a signal handler
+ * (ie lxc_console_sigwinch()) would need to take the thread mutex to
+ * prevent lxc_ttys list corruption, but using the fd we can provide the
+ * tty_state needed to the callback (lxc_console_cb_sigwinch_fd()).
+ */
+static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
+{
+       sigset_t mask;
+       struct lxc_tty_state *ts;
+
+       ts = malloc(sizeof(*ts));
+       if (!ts)
+               return NULL;
+
+       memset(ts, 0, sizeof(*ts));
+       ts->stdinfd  = srcfd;
+       ts->masterfd = dstfd;
+       ts->sigfd    = -1;
+
+       /* add tty to list to be scanned at SIGWINCH time */
+       lxc_list_add_elem(&ts->node, ts);
+       lxc_list_add_tail(&lxc_ttys, &ts->node);
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGWINCH);
+       if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) {
+               SYSERROR("failed to block SIGWINCH");
+               goto err1;
+       }
+
+       ts->sigfd = signalfd(-1, &mask, 0);
+       if (ts->sigfd < 0) {
+               SYSERROR("failed to get signalfd");
+               goto err2;
+       }
+
+       DEBUG("%d got SIGWINCH fd %d", getpid(), ts->sigfd);
+       goto out;
+
+err2:
+       sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
+err1:
+       lxc_list_del(&ts->node);
+       free(ts);
+       ts = NULL;
 out:
-       return *console ? 0 : -1;
+       return ts;
 }
 
-int lxc_create_console(struct lxc_conf *conf)
+/*
+ * lxc_console_sigwinch_fini: uninstall SIGWINCH handler
+ *
+ * @ts  : the lxc_tty_state returned by lxc_console_sigwinch_init
+ *
+ * Restore the saved signal handler that was in effect at the time
+ * lxc_console_sigwinch_init() was called.
+ *
+ * Must be called with process_lock held to protect the lxc_ttys list, or
+ * from a non-threaded context.
+ */
+static void lxc_console_sigwinch_fini(struct lxc_tty_state *ts)
 {
-       struct termios tios;
-       struct lxc_console *console = &conf->console;
-       int fd;
+       if (ts->sigfd >= 0)
+               close(ts->sigfd);
+       lxc_list_del(&ts->node);
+       sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
+       free(ts);
+}
 
-       if (!conf->rootfs.path)
-               return 0;
+static int lxc_console_cb_con(int fd, void *data,
+                             struct lxc_epoll_descr *descr)
+{
+       struct lxc_console *console = (struct lxc_console *)data;
+       char buf[1024];
+       int r,w;
 
-       if (!console->path && get_default_console(&console->path)) {
-               ERROR("failed to get default console");
-               return -1;
+       w = r = read(fd, buf, sizeof(buf));
+       if (r < 0) {
+               SYSERROR("failed to read");
+               return 1;
        }
 
-       if (!strcmp(console->path, "none"))
+       if (!r) {
+               INFO("console client on fd %d has exited", fd);
+               lxc_mainloop_del_handler(descr, fd);
+               close(fd);
                return 0;
-
-       if (openpty(&console->master, &console->slave,
-                   console->name, NULL, NULL)) {
-               SYSERROR("failed to allocate a pty");
-               return -1;
        }
 
-       if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
-               SYSERROR("failed to set console master to close-on-exec");
-               goto err;
+       if (fd == console->peer)
+               w = write(console->master, buf, r);
+
+       if (fd == console->master) {
+               if (console->log_fd >= 0)
+                       w = write(console->log_fd, buf, r);
+
+               if (console->peer >= 0)
+                       w = write(console->peer, buf, r);
        }
 
-       if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
-               SYSERROR("failed to set console slave to close-on-exec");
-               goto err;
+       if (w != r)
+               WARN("console short write r:%d w:%d", r, w);
+       return 0;
+}
+
+static void lxc_console_mainloop_add_peer(struct lxc_console *console)
+{
+       if (console->peer >= 0) {
+               if (lxc_mainloop_add_handler(console->descr, console->peer,
+                                            lxc_console_cb_con, console))
+                       WARN("console peer not added to mainloop");
        }
 
-       if (console->log_path) {
-               fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | 
O_CREAT | O_APPEND, 0600));
-               if (fd < 0) {
-                       SYSERROR("failed to open '%s'", console->log_path);
-                       goto err;
+       if (console->tty_state) {
+               if (lxc_mainloop_add_handler(console->descr,
+                                            console->tty_state->sigfd,
+                                            lxc_console_cb_sigwinch_fd,
+                                            console->tty_state)) {
+                       WARN("failed to add to mainloop SIGWINCH handler for 
'%d'",
+                            console->tty_state->sigfd);
                }
-               DEBUG("using '%s' as console log", console->log_path);
-               console->log_fd = fd;
+       }
+}
+
+int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
+                            struct lxc_handler *handler)
+{
+       struct lxc_conf *conf = handler->conf;
+       struct lxc_console *console = &conf->console;
+
+       if (!conf->rootfs.path) {
+               INFO("no rootfs, no console.");
+               return 0;
+       }
+
+       if (console->master < 0) {
+               INFO("no console");
+               return 0;
        }
 
-       fd = lxc_unpriv(open(console->path, O_CLOEXEC | O_RDWR | O_CREAT |
-                            O_APPEND, 0600));
-       if (fd < 0) {
-               SYSERROR("failed to open '%s'", console->path);
-               goto err_close_console_log;
+       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);
+               return -1;
        }
 
-       DEBUG("using '%s' as console", console->path);
+       /* 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);
 
-       console->peer = fd;
+       return 0;
+}
 
-       if (!isatty(console->peer))
-               return 0;
+static int setup_tios(int fd, struct termios *oldtios)
+{
+       struct termios newtios;
 
-       console->tios = malloc(sizeof(tios));
-       if (!console->tios) {
-               SYSERROR("failed to allocate memory");
-               goto err_close_console;
+       if (!isatty(fd)) {
+               ERROR("'%d' is not a tty", fd);
+               return -1;
        }
 
-       /* Get termios */
-       if (tcgetattr(console->peer, console->tios)) {
+       /* Get current termios */
+       if (tcgetattr(fd, oldtios)) {
                SYSERROR("failed to get current terminal settings");
-               goto err_free;
+               return -1;
        }
 
-       tios = *console->tios;
+       newtios = *oldtios;
 
        /* Remove the echo characters and signal reception, the echo
-        * will be done below with master proxying */
-       tios.c_iflag &= ~IGNBRK;
-       tios.c_iflag &= BRKINT;
-       tios.c_lflag &= ~(ECHO|ICANON|ISIG);
-       tios.c_cc[VMIN] = 1;
-       tios.c_cc[VTIME] = 0;
+        * will be done with master proxying */
+       newtios.c_iflag &= ~IGNBRK;
+       newtios.c_iflag &= BRKINT;
+       newtios.c_lflag &= ~(ECHO|ICANON|ISIG);
+       newtios.c_cc[VMIN] = 1;
+       newtios.c_cc[VTIME] = 0;
 
        /* Set new attributes */
-       if (tcsetattr(console->peer, TCSAFLUSH, &tios)) {
+       if (tcsetattr(fd, TCSAFLUSH, &newtios)) {
                ERROR("failed to set new terminal settings");
-               goto err_free;
+               return -1;
        }
 
        return 0;
+}
 
-err_free:
-       free(console->tios);
-
-err_close_console:
-       close(console->peer);
+static void lxc_console_peer_proxy_free(struct lxc_console *console)
+{
+       if (console->tty_state) {
+               lxc_console_sigwinch_fini(console->tty_state);
+               console->tty_state = NULL;
+       }
+       close(console->peerpty.master);
+       close(console->peerpty.slave);
+       console->peerpty.master = -1;
+       console->peerpty.slave = -1;
+       console->peerpty.busy = -1;
+       console->peerpty.name[0] = '\0';
        console->peer = -1;
+}
 
-err_close_console_log:
-       if (console->log_fd >= 0) {
-               close(console->log_fd);
-               console->log_fd = -1;
+static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int 
sockfd)
+{
+       struct termios oldtermio;
+       struct lxc_tty_state *ts;
+
+       if (console->master < 0) {
+               ERROR("console not set up");
+               return -1;
+       }
+       if (console->peerpty.busy != -1 || console->peer != -1) {
+               NOTICE("console already in use");
+               return -1;
+       }
+       if (console->tty_state) {
+               ERROR("console already has tty_state");
+               return -1;
        }
 
-err:
-       close(console->master);
-       console->master = -1;
+       /* this is the proxy pty that will be given to the client, and that
+        * the real pty master will send to / recv from
+        */
+       if (openpty(&console->peerpty.master, &console->peerpty.slave,
+                   console->peerpty.name, NULL, NULL)) {
+               SYSERROR("failed to create proxy pty");
+               return -1;
+       }
 
-       close(console->slave);
-       console->slave = -1;
+       if (setup_tios(console->peerpty.slave, &oldtermio) < 0)
+               goto err1;
+
+       ts = lxc_console_sigwinch_init(console->peerpty.master, 
console->master);
+       if (!ts)
+               goto err1;
+
+       console->tty_state = ts;
+       console->peer = console->peerpty.slave;
+       console->peerpty.busy = sockfd;
+       lxc_console_mainloop_add_peer(console);
+
+       DEBUG("%d %s peermaster:%d sockfd:%d", getpid(), __FUNCTION__, 
console->peerpty.master, sockfd);
+       return 0;
+
+err1:
+       lxc_console_peer_proxy_free(console);
        return -1;
 }
 
-void lxc_delete_console(struct lxc_console *console)
+/* lxc_console_allocate: allocate the console or a tty
+ *
+ * @conf    : the configuration of the container to allocate from
+ * @sockfd  : the socket fd whose remote side when closed, will be an
+ *            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
+ */
+int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
+{
+       int masterfd = -1, ttynum;
+       struct lxc_tty_info *tty_info = &conf->tty_info;
+       struct lxc_console *console = &conf->console;
+
+       process_lock();
+       if (*ttyreq == 0) {
+               if (lxc_console_peer_proxy_alloc(console, sockfd) < 0)
+                       goto out;
+               masterfd = console->peerpty.master;
+               goto out;
+       }
+
+       if (*ttyreq > 0) {
+               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;
+       }
+
+       /* 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)
+               goto out;
+
+       *ttyreq = ttynum;
+
+out_tty:
+       tty_info->pty_info[ttynum - 1].busy = sockfd;
+       masterfd = tty_info->pty_info[ttynum - 1].master;
+out:
+       process_unlock();
+       return masterfd;
+}
+
+/* lxc_console_free: mark the console or a tty as unallocated, free any
+ * resources allocated by lxc_console_allocate().
+ *
+ * @conf : the configuration of the container whose tty was closed
+ * @fd   : the socket fd whose remote side was closed, which indicated
+ *         the console or tty is no longer in use. this is used to match
+ *         which console/tty is being freed.
+ */
+void lxc_console_free(struct lxc_conf *conf, int fd)
 {
-       if (console->tios &&
+       int i;
+       struct lxc_tty_info *tty_info = &conf->tty_info;
+       struct lxc_console *console = &conf->console;
+
+       process_lock();
+       for (i = 0; i < tty_info->nbtty; i++) {
+               if (tty_info->pty_info[i].busy == fd)
+                       tty_info->pty_info[i].busy = 0;
+       }
+
+       if (console->peerpty.busy == fd) {
+               lxc_mainloop_del_handler(console->descr, 
console->peerpty.slave);
+               lxc_console_peer_proxy_free(console);
+       }
+       process_unlock();
+}
+
+static void lxc_console_peer_default(struct lxc_console *console)
+{
+       struct lxc_tty_state *ts;
+       const char *path = console->path;
+
+       /* if no console was given, try current controlling terminal, there
+        * won't be one if we were started as a daemon (-d)
+        */
+       if (!path && !access("/dev/tty", F_OK)) {
+               int fd;
+               fd = open("/dev/tty", O_RDWR);
+               if (fd >= 0) {
+                       close(fd);
+                       path = "/dev/tty";
+               }
+       }
+
+       if (!path)
+               goto out;
+
+       DEBUG("opening %s for console peer", path);
+       console->peer = lxc_unpriv(open(path, O_CLOEXEC | O_RDWR | O_CREAT |
+                                       O_APPEND, 0600));
+       if (console->peer < 0)
+               goto out;
+
+       DEBUG("using '%s' as console", path);
+
+       if (!isatty(console->peer))
+               return;
+
+       ts = lxc_console_sigwinch_init(console->peer, console->master);
+       if (!ts)
+               WARN("Unable to install SIGWINCH");
+       console->tty_state = ts;
+
+       lxc_console_winsz(console->peer, console->master);
+
+       console->tios = malloc(sizeof(*console->tios));
+       if (!console->tios) {
+               SYSERROR("failed to allocate memory");
+               goto err1;
+       }
+
+       if (setup_tios(console->peer, console->tios) < 0)
+               goto err2;
+
+       return;
+
+err2:
+       free(console->tios);
+       console->tios = NULL;
+err1:
+       close(console->peer);
+       console->peer = -1;
+out:
+       DEBUG("no console peer");
+}
+
+void lxc_console_delete(struct lxc_console *console)
+{
+       if (console->tios && console->peer >= 0 &&
            tcsetattr(console->peer, TCSAFLUSH, console->tios))
                WARN("failed to set old terminal settings");
        free(console->tios);
@@ -216,73 +555,208 @@ void lxc_delete_console(struct lxc_console *console)
        console->slave = -1;
 }
 
-static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr)
+int lxc_console_create(struct lxc_conf *conf)
 {
-       struct lxc_console *console = (struct lxc_console *)data;
-       char buf[1024];
-       int r,w;
+       struct lxc_console *console = &conf->console;
 
-       r = read(fd, buf, sizeof(buf));
-       if (r < 0) {
+       if (!conf->rootfs.path)
+               return 0;
+
+       if (console->path && !strcmp(console->path, "none"))
+               return 0;
+
+       if (openpty(&console->master, &console->slave,
+                   console->name, NULL, NULL)) {
+               SYSERROR("failed to allocate a pty");
+               return -1;
+       }
+
+       if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
+               SYSERROR("failed to set console master to close-on-exec");
+               goto err;
+       }
+
+       if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
+               SYSERROR("failed to set console slave to close-on-exec");
+               goto err;
+       }
+
+       lxc_console_peer_default(console);
+
+       if (console->log_path) {
+               console->log_fd = lxc_unpriv(open(console->log_path,
+                                                 O_CLOEXEC | O_RDWR |
+                                                 O_CREAT | O_APPEND, 0600));
+               if (console->log_fd < 0) {
+                       SYSERROR("failed to open '%s'", console->log_path);
+                       goto err;
+               }
+               DEBUG("using '%s' as console log", console->log_path);
+       }
+
+       return 0;
+
+err:
+       lxc_console_delete(console);
+       return -1;
+}
+
+
+
+static int lxc_console_cb_tty_stdin(int fd, void *cbdata,
+                                   struct lxc_epoll_descr *descr)
+{
+       struct lxc_tty_state *ts = cbdata;
+       char c;
+
+       assert(fd == ts->stdinfd);
+       if (read(ts->stdinfd, &c, 1) < 0) {
                SYSERROR("failed to read");
                return 1;
        }
 
-       if (!r) {
-               INFO("console client has exited");
-               lxc_mainloop_del_handler(descr, fd);
-               close(fd);
+       /* we want to exit the console with Ctrl+a q */
+       if (c == ts->escape && !ts->saw_escape) {
+               ts->saw_escape = 1;
                return 0;
        }
 
-       /* no output for the console, do nothing */
-       if (console->peer == -1)
-               return 0;
+       if (c == 'q' && ts->saw_escape)
+               return 1;
 
-       if (console->peer == fd)
-               w = write(console->master, buf, r);
-       else {
-               w = write(console->peer, buf, r);
-               if (console->log_fd > 0)
-                       w = write(console->log_fd, buf, r);
+       ts->saw_escape = 0;
+       if (write(ts->masterfd, &c, 1) < 0) {
+               SYSERROR("failed to write");
+               return 1;
        }
-       if (w != r)
-               WARN("console short write");
+
        return 0;
 }
 
-int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
-                            struct lxc_handler *handler)
+static int lxc_console_cb_tty_master(int fd, void *cbdata,
+                                    struct lxc_epoll_descr *descr)
 {
-       struct lxc_conf *conf = handler->conf;
-       struct lxc_console *console = &conf->console;
+       struct lxc_tty_state *ts = cbdata;
+       char buf[1024];
+       int r,w;
 
-       if (!conf->rootfs.path) {
-               INFO("no rootfs, no console.");
-               return 0;
+       assert(fd == ts->masterfd);
+       r = read(fd, buf, sizeof(buf));
+       if (r < 0) {
+               SYSERROR("failed to read");
+               return 1;
        }
 
-       if (!console->path) {
-               INFO("no console specified");
-               return 0;
+       w = write(ts->stdoutfd, buf, r);
+       if (w < 0 || w != r) {
+               SYSERROR("failed to write");
+               return 1;
        }
 
-       if (console->peer == -1) {
-               INFO("no console will be used");
-               return 0;
+       return 0;
+}
+
+int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd)
+{
+       return lxc_cmd_console(c->name, ttynum, masterfd, c->config_path);
+}
+
+int lxc_console(struct lxc_container *c, int ttynum,
+               int stdinfd, int stdoutfd, int stderrfd,
+               int escape)
+{
+       int ret, ttyfd, masterfd;
+       struct lxc_epoll_descr descr;
+       struct termios oldtios;
+       struct lxc_tty_state *ts;
+
+       if (!isatty(stdinfd)) {
+               ERROR("stdin is not a tty");
+               return -1;
        }
 
-       if (lxc_mainloop_add_handler(descr, console->master,
-                                    console_handler, console)) {
-               ERROR("failed to add to mainloop console handler for '%d'",
-                     console->master);
+       ret = setup_tios(stdinfd, &oldtios);
+       if (ret) {
+               ERROR("failed to setup tios");
                return -1;
        }
 
-       if (console->peer != -1 &&
-           lxc_mainloop_add_handler(descr, console->peer,
-                                    console_handler, console))
-               WARN("console input disabled");
+       process_lock();
+       ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path);
+       if (ttyfd < 0) {
+               ret = ttyfd;
+               goto err1;
+       }
 
-       return 0;
+       fprintf(stderr, "\n"
+                       "Connected to tty %1$d\n"
+                       "Type <Ctrl+%2$c q> to exit the console, "
+                       "<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n",
+                       ttynum, 'a' + escape - 1);
+
+       ret = setsid();
+       if (ret)
+               INFO("already group leader");
+
+       ts = lxc_console_sigwinch_init(stdinfd, masterfd);
+       if (!ts) {
+               ret = -1;
+               goto err2;
+       }
+       ts->escape = escape;
+       ts->winch_proxy = c->name;
+       ts->winch_proxy_lxcpath = c->config_path;
+
+       lxc_console_winsz(stdinfd, masterfd);
+       lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
+
+       ret = lxc_mainloop_open(&descr);
+       if (ret) {
+               ERROR("failed to create mainloop");
+               goto err3;
+       }
+
+       ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
+                                      lxc_console_cb_sigwinch_fd, ts);
+       if (ret) {
+               ERROR("failed to add handler for SIGWINCH fd");
+               goto err4;
+       }
+
+       ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
+                                      lxc_console_cb_tty_stdin, ts);
+       if (ret) {
+               ERROR("failed to add handler for stdinfd");
+               goto err4;
+       }
+
+       ret = lxc_mainloop_add_handler(&descr, ts->masterfd,
+                                      lxc_console_cb_tty_master, ts);
+       if (ret) {
+               ERROR("failed to add handler for masterfd");
+               goto err4;
+       }
+
+       process_unlock();
+       ret = lxc_mainloop(&descr, -1);
+       process_lock();
+       if (ret) {
+               ERROR("mainloop returned an error");
+               goto err4;
+       }
+
+       ret = 0;
+
+err4:
+       lxc_mainloop_close(&descr);
+err3:
+       lxc_console_sigwinch_fini(ts);
+err2:
+       close(masterfd);
+       close(ttyfd);
+err1:
+       tcsetattr(stdinfd, TCSAFLUSH, &oldtios);
+       process_unlock();
+
+       return ret;
 }
diff --git a/src/lxc/console.h b/src/lxc/console.h
index c9bf937..1089493 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -21,6 +21,18 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
-extern int lxc_create_console(struct lxc_conf *);
-extern void lxc_delete_console(struct lxc_console *);
-extern int lxc_console_mainloop_add(struct lxc_epoll_descr *, struct 
lxc_handler *);
+struct lxc_epoll_descr;
+struct lxc_container;
+
+extern int  lxc_console_allocate(struct lxc_conf *conf, int sockfd, int 
*ttynum);
+extern int  lxc_console_create(struct lxc_conf *);
+extern void lxc_console_delete(struct lxc_console *);
+extern void lxc_console_free(struct lxc_conf *conf, int fd);
+
+extern int  lxc_console_mainloop_add(struct lxc_epoll_descr *, struct 
lxc_handler *);
+extern void lxc_console_sigwinch(int sig);
+extern int  lxc_console(struct lxc_container *c, int ttynum,
+                       int stdinfd, int stdoutfd, int stderrfd,
+                       int escape);
+extern int  lxc_console_getfd(struct lxc_container *c, int *ttynum,
+                             int *masterfd);
diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h
index 8491ff3..67e0d9e 100644
--- a/src/lxc/lxc.h
+++ b/src/lxc/lxc.h
@@ -108,15 +108,6 @@ extern int lxc_monitor_read_fdset(fd_set *rfds, int nfds, 
struct lxc_msg *msg, i
 extern int lxc_monitor_close(int fd);
 
 /*
- * Show the console of the container.
- * @name : the name of container
- * @tty  : the tty number
- * @fd   : a pointer to a tty file descriptor
- * Returns 0 on sucess, < 0 otherwise
- */
-extern int lxc_console(const char *name, int ttynum, int *fd, const char 
*lxcpath);
-
-/*
  * Freeze all the tasks running inside the container <name>
  * @name : the container name
  * Returns 0 on success, < 0 otherwise
diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c
index 52e6988..77afdc1 100644
--- a/src/lxc/lxc_console.c
+++ b/src/lxc/lxc_console.c
@@ -38,6 +38,7 @@
 #include <sys/poll.h>
 #include <sys/ioctl.h>
 
+#include "../lxc/lxccontainer.h"
 #include "error.h"
 #include "lxc.h"
 #include "log.h"
@@ -87,184 +88,34 @@ Options :\n\
        .escape = 1,
 };
 
-static int master = -1;
-
-static void winsz(void)
-{
-       struct winsize wsz;
-       if (ioctl(0, TIOCGWINSZ, &wsz) == 0)
-               ioctl(master, TIOCSWINSZ, &wsz);
-}
-
-static void sigwinch(int sig)
-{
-       winsz();
-}
-
-static int setup_tios(int fd, struct termios *newtios, struct termios *oldtios)
-{
-       if (!isatty(fd)) {
-               ERROR("'%d' is not a tty", fd);
-               return -1;
-       }
-
-       /* Get current termios */
-       if (tcgetattr(fd, oldtios)) {
-               SYSERROR("failed to get current terminal settings");
-               return -1;
-       }
-
-       *newtios = *oldtios;
-
-       /* Remove the echo characters and signal reception, the echo
-        * will be done below with master proxying */
-       newtios->c_iflag &= ~IGNBRK;
-       newtios->c_iflag &= BRKINT;
-       newtios->c_lflag &= ~(ECHO|ICANON|ISIG);
-       newtios->c_cc[VMIN] = 1;
-       newtios->c_cc[VTIME] = 0;
-
-       /* Set new attributes */
-       if (tcsetattr(fd, TCSAFLUSH, newtios)) {
-               ERROR("failed to set new terminal settings");
-               return -1;
-       }
-
-       return 0;
-}
-
-static int stdin_handler(int fd, void *data, struct lxc_epoll_descr *descr)
-{
-       static int wait4q = 0;
-       int *peer = (int *)data;
-       char c;
-
-       if (read(0, &c, 1) < 0) {
-               SYSERROR("failed to read");
-               return 1;
-       }
-
-       /* we want to exit the console with Ctrl+a q */
-       if (c == my_args.escape && !wait4q) {
-               wait4q = !wait4q;
-               return 0;
-       }
-
-       if (c == 'q' && wait4q)
-               return 1;
-
-       wait4q = 0;
-       if (write(*peer, &c, 1) < 0) {
-               SYSERROR("failed to write");
-               return 1;
-       }
-
-       return 0;
-}
-
-static int master_handler(int fd, void *data, struct lxc_epoll_descr *descr)
-{
-       char buf[1024];
-       int *peer = (int *)data;
-       int r,w;
-
-       r = read(fd, buf, sizeof(buf));
-       if (r < 0) {
-               SYSERROR("failed to read");
-               return 1;
-       }
-       w = write(*peer, buf, r);
-       if (w < 0 || w != r) {
-               SYSERROR("failed to write");
-               return 1;
-       }
-
-       return 0;
-}
-
 int main(int argc, char *argv[])
 {
-       int err, ttyfd, std_in = 1;
-       struct lxc_epoll_descr descr;
-       struct termios newtios, oldtios;
+       int ret;
+       struct lxc_container *c;
 
-       err = lxc_arguments_parse(&my_args, argc, argv);
-       if (err)
-               return -1;
+       ret = lxc_arguments_parse(&my_args, argc, argv);
+       if (ret)
+               return EXIT_FAILURE;
 
-       err = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+       ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
                           my_args.progname, my_args.quiet, my_args.lxcpath[0]);
-       if (err)
-               return -1;
-
-       err = setup_tios(0, &newtios, &oldtios);
-       if (err) {
-               ERROR("failed to setup tios");
-               return -1;
-       }
-
-       ttyfd = lxc_cmd_console(my_args.name, &my_args.ttynum, &master, 
my_args.lxcpath[0]);
-       if (ttyfd < 0) {
-               err = ttyfd;
-               goto out;
-       }
-
-       fprintf(stderr, "\n\
-Connected to tty %1$d\n\
-Type <Ctrl+%2$c q> to exit the console, \
-<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n", my_args.ttynum,
-                'a' + my_args.escape - 1);
-
-       err = setsid();
-       if (err)
-               INFO("already group leader");
-
-       if (signal(SIGWINCH, sigwinch) == SIG_ERR) {
-               SYSERROR("failed to set SIGWINCH handler");
-               err = -1;
-               goto out;
-       }
-
-       winsz();
-
-       err = lxc_mainloop_open(&descr);
-       if (err) {
-               ERROR("failed to create mainloop");
-               goto out;
-       }
+       if (ret)
+               return EXIT_FAILURE;
 
-       err = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &master);
-       if (err) {
-               ERROR("failed to add handler for the stdin");
-               goto out_mainloop_open;
+       c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+       if (!c) {
+               fprintf(stderr, "System error loading container\n");
+               exit(EXIT_FAILURE);
        }
 
-       err = lxc_mainloop_add_handler(&descr, master, master_handler, &std_in);
-       if (err) {
-               ERROR("failed to add handler for the master");
-               goto out_mainloop_open;
+       if (!c->is_running(c)) {
+               fprintf(stderr, "%s is not running\n", my_args.name);
+               exit(EXIT_FAILURE);
        }
 
-       err = lxc_mainloop(&descr, -1);
-       if (err) {
-               ERROR("mainloop returned an error");
-               goto out_mainloop_open;
+       ret = c->console(c, my_args.ttynum, 0, 1, 2, my_args.escape);
+       if (ret < 0) {
+               exit(EXIT_FAILURE);
        }
-
-       close(ttyfd);
-       err =  0;
-
-out_mainloop_open:
-       lxc_mainloop_close(&descr);
-
-out:
-       /* Restore previous terminal parameter */
-       tcsetattr(0, TCSAFLUSH, &oldtios);
-
-       /* Return to line it is */
-       printf("\n");
-
-       close(master);
-
-       return err;
+       return EXIT_SUCCESS;
 }
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index c8fc16f..b1a05b4 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -31,6 +31,7 @@
 #include "lxccontainer.h"
 #include "conf.h"
 #include "confile.h"
+#include "console.h"
 #include "cgroup.h"
 #include "commands.h"
 #include "version.h"
@@ -350,16 +351,22 @@ static bool lxcapi_unfreeze(struct lxc_container *c)
        return true;
 }
 
-static int lxcapi_console(struct lxc_container *c, int *ttynum, int *masterfd)
+static int lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int 
*masterfd)
 {
        int ttyfd;
        if (!c)
                return -1;
 
-       ttyfd = lxc_cmd_console(c->name, ttynum, masterfd, c->config_path);
+       ttyfd = lxc_console_getfd(c, ttynum, masterfd);
        return ttyfd;
 }
 
+static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd,
+                         int stdoutfd, int stderrfd, int escape)
+{
+       return lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape);
+}
+
 static pid_t lxcapi_init_pid(struct lxc_container *c)
 {
        if (!c)
@@ -2018,6 +2025,7 @@ struct lxc_container *lxc_container_new(const char *name, 
const char *configpath
        c->freeze = lxcapi_freeze;
        c->unfreeze = lxcapi_unfreeze;
        c->console = lxcapi_console;
+       c->console_getfd = lxcapi_console_getfd;
        c->init_pid = lxcapi_init_pid;
        c->load_config = lxcapi_load_config;
        c->want_daemonize = lxcapi_want_daemonize;
diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
index 5078f03..5449a46 100644
--- a/src/lxc/lxccontainer.h
+++ b/src/lxc/lxccontainer.h
@@ -115,7 +115,7 @@ struct lxc_container {
                const char *lxcpath, int flags, const char *bdevtype,
                const char *bdevdata, unsigned long newsize, char **hookargs);
 
-       /* lxcapi_console: allocate a console tty from container @c
+       /* lxcapi_console_getfd: allocate a console tty from container @c
         *
         * @c        : the running container
         * @ttynum   : in : tty number to attempt to allocate or -1 to
@@ -128,7 +128,24 @@ struct lxc_container {
         * indicate that it is done with the allocated console so that it can
         * be allocated by another caller.
         */
-       int (*console)(struct lxc_container *c, int *ttynum, int *masterfd);
+       int (*console_getfd)(struct lxc_container *c, int *ttynum, int 
*masterfd);
+
+       /* lxcapi_console: allocate and run a console tty from container @c
+        *
+        * @c        : the running container
+        * @ttynum   : tty number to attempt to allocate, -1 to
+        *             allocate the first available tty, or 0 to allocate
+        *             the console
+        * @stdinfd  : fd to read input from
+        * @stdoutfd : fd to write output to
+        * @stderrfd : fd to write error output to
+        * @escape   : the escape character (1 == 'a', 2 == 'b', ...)
+        *
+        * Returns 0 on success, -1 on failure. This function will not return
+        * until the console has been exited by the user.
+        */
+       int (*console)(struct lxc_container *c, int ttynum,
+                      int stdinfd, int stdoutfd, int stderrfd, int escape);
 
 #if 0
        bool (*commit_cgroups)(struct lxc_container *c);
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 5092b51..8c8af9c 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -50,69 +50,6 @@
 #include <sys/capability.h>
 #endif
 
-#ifdef HAVE_SYS_SIGNALFD_H
-#  include <sys/signalfd.h>
-#else
-/* assume kernel headers are too old */
-#include <stdint.h>
-struct signalfd_siginfo
-{
-       uint32_t ssi_signo;
-       int32_t ssi_errno;
-       int32_t ssi_code;
-       uint32_t ssi_pid;
-       uint32_t ssi_uid;
-       int32_t ssi_fd;
-       uint32_t ssi_tid;
-       uint32_t ssi_band;
-       uint32_t ssi_overrun;
-       uint32_t ssi_trapno;
-       int32_t ssi_status;
-       int32_t ssi_int;
-       uint64_t ssi_ptr;
-       uint64_t ssi_utime;
-       uint64_t ssi_stime;
-       uint64_t ssi_addr;
-       uint8_t __pad[48];
-};
-
-#  ifndef __NR_signalfd4
-/* assume kernel headers are too old */
-#    if __i386__
-#      define __NR_signalfd4 327
-#    elif __x86_64__
-#      define __NR_signalfd4 289
-#    elif __powerpc__
-#      define __NR_signalfd4 313
-#    elif __s390x__
-#      define __NR_signalfd4 322
-#    endif
-#endif
-
-#  ifndef __NR_signalfd
-/* assume kernel headers are too old */
-#    if __i386__
-#      define __NR_signalfd 321
-#    elif __x86_64__
-#      define __NR_signalfd 282
-#    elif __powerpc__
-#      define __NR_signalfd 305
-#    elif __s390x__
-#      define __NR_signalfd 316
-#    endif
-#endif
-
-int signalfd(int fd, const sigset_t *mask, int flags)
-{
-       int retval;
-
-       retval = syscall (__NR_signalfd4, fd, mask, _NSIG / 8, flags);
-       if (errno == ENOSYS && flags == 0)
-               retval = syscall (__NR_signalfd, fd, mask, _NSIG / 8);
-       return retval;
-}
-#endif
-
 #if !HAVE_DECL_PR_CAPBSET_DROP
 #define PR_CAPBSET_DROP 24
 #endif
@@ -198,6 +135,7 @@ static int setup_signal_fd(sigset_t *oldmask)
            sigdelset(&mask, SIGILL) ||
            sigdelset(&mask, SIGSEGV) ||
            sigdelset(&mask, SIGBUS) ||
+           sigdelset(&mask, SIGWINCH) ||
            sigprocmask(SIG_BLOCK, &mask, oldmask)) {
                SYSERROR("failed to set signal mask");
                return -1;
@@ -387,25 +325,26 @@ struct lxc_handler *lxc_init(const char *name, struct 
lxc_conf *conf, const char
                goto out_aborting;
        }
 
-       if (lxc_create_console(conf)) {
-               ERROR("failed to create console");
-               goto out_delete_tty;
-       }
-
        /* the signal fd has to be created before forking otherwise
         * if the child process exits before we setup the signal fd,
         * the event will be lost and the command will be stuck */
        handler->sigfd = setup_signal_fd(&handler->oldmask);
        if (handler->sigfd < 0) {
                ERROR("failed to set sigchild fd handler");
-               goto out_delete_console;
+               goto out_delete_tty;
+       }
+
+       /* do this after setting up signals since it might unblock SIGWINCH */
+       if (lxc_console_create(conf)) {
+               ERROR("failed to create console");
+               goto out_restore_sigmask;
        }
 
        INFO("'%s' is initialized", name);
        return handler;
 
-out_delete_console:
-       lxc_delete_console(&conf->console);
+out_restore_sigmask:
+       sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
 out_delete_tty:
        lxc_delete_tty(&conf->tty_info);
 out_aborting:
@@ -436,7 +375,7 @@ static void lxc_fini(const char *name, struct lxc_handler 
*handler)
        if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL))
                WARN("failed to restore sigprocmask");
 
-       lxc_delete_console(&handler->conf->console);
+       lxc_console_delete(&handler->conf->console);
        lxc_delete_tty(&handler->conf->tty_info);
        close(handler->conf->maincmd_fd);
        handler->conf->maincmd_fd = -1;
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 7d2bfa8..063f76c 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -75,6 +75,71 @@ static inline int unshare(int flags)
 int unshare(int);
 #endif
 
+/* Define signalfd() if missing from the C library */
+#ifdef HAVE_SYS_SIGNALFD_H
+#  include <sys/signalfd.h>
+#else
+/* assume kernel headers are too old */
+#include <stdint.h>
+struct signalfd_siginfo
+{
+       uint32_t ssi_signo;
+       int32_t ssi_errno;
+       int32_t ssi_code;
+       uint32_t ssi_pid;
+       uint32_t ssi_uid;
+       int32_t ssi_fd;
+       uint32_t ssi_tid;
+       uint32_t ssi_band;
+       uint32_t ssi_overrun;
+       uint32_t ssi_trapno;
+       int32_t ssi_status;
+       int32_t ssi_int;
+       uint64_t ssi_ptr;
+       uint64_t ssi_utime;
+       uint64_t ssi_stime;
+       uint64_t ssi_addr;
+       uint8_t __pad[48];
+};
+
+#  ifndef __NR_signalfd4
+/* assume kernel headers are too old */
+#    if __i386__
+#      define __NR_signalfd4 327
+#    elif __x86_64__
+#      define __NR_signalfd4 289
+#    elif __powerpc__
+#      define __NR_signalfd4 313
+#    elif __s390x__
+#      define __NR_signalfd4 322
+#    endif
+#endif
+
+#  ifndef __NR_signalfd
+/* assume kernel headers are too old */
+#    if __i386__
+#      define __NR_signalfd 321
+#    elif __x86_64__
+#      define __NR_signalfd 282
+#    elif __powerpc__
+#      define __NR_signalfd 305
+#    elif __s390x__
+#      define __NR_signalfd 316
+#    endif
+#endif
+
+static inline int signalfd(int fd, const sigset_t *mask, int flags)
+{
+       int retval;
+
+       retval = syscall (__NR_signalfd4, fd, mask, _NSIG / 8, flags);
+       if (errno == ENOSYS && flags == 0)
+               retval = syscall (__NR_signalfd, fd, mask, _NSIG / 8);
+       return retval;
+}
+#endif
+
+
 /**
  * BUILD_BUG_ON - break compile if a condition is true.
  * @condition: the condition which the compiler should know is false.
diff --git a/src/python-lxc/examples/pyconsole-vte.py 
b/src/python-lxc/examples/pyconsole-vte.py
new file mode 100755
index 0000000..7926758
--- /dev/null
+++ b/src/python-lxc/examples/pyconsole-vte.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+#
+# Example program showing use of console functions in the lxc python binding
+#
+
+import warnings
+warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
+
+import gtk
+import vte
+import lxc
+import sys
+
+def gtk_exit_cb(terminal):
+    gtk.main_quit()
+
+def vte_con(ct, ttynum):
+    print("Doing console in a VTE widget...")
+    masterfd = ct.console_getfd(ttynum)
+    term = vte.Terminal()
+    term.set_cursor_blinks(True)
+    term.set_scrollback_lines(1000)
+    term.connect('eof', gtk_exit_cb)
+
+    term.set_pty(masterfd)
+    term.feed_child('\n')
+    #term.feed_child('ps aux\n')
+
+    vscrollbar = gtk.VScrollbar()
+    vscrollbar.set_adjustment(term.get_adjustment())
+
+    hbox = gtk.HBox()
+    hbox.pack_start(term)
+    hbox.pack_start(vscrollbar)
+
+    window = gtk.Window()
+    window.add(hbox)
+    window.connect('delete-event', lambda window, event: gtk.main_quit())
+    window.show_all()
+    gtk.main()
+    print("Console done")
+
+if __name__ == '__main__':
+    ttynum = -1
+    if len(sys.argv) < 2:
+        sys.exit("Usage: %s container-name [ttynum]" % sys.argv[0])
+    if len(sys.argv) > 2:
+        ttynum = int(sys.argv[2])
+
+    ct = lxc.Container(sys.argv[1])
+
+    print("Container:%s tty:%d" % (ct.name, ttynum))
+    if not ct.defined:
+        sys.exit("Container %s not defined" % ct.name)
+    if not ct.running:
+        sys.exit("Container %s not running" % ct.name)
+
+    vte_con(ct, ttynum)
diff --git a/src/python-lxc/examples/pyconsole.py 
b/src/python-lxc/examples/pyconsole.py
new file mode 100755
index 0000000..7661ef4
--- /dev/null
+++ b/src/python-lxc/examples/pyconsole.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python3
+#
+# Example program showing use of console functions in the lxc python binding
+#
+
+import warnings
+warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
+
+import lxc
+import sys
+import time
+
+if __name__ == '__main__':
+    ttynum = -1
+    escape = 1
+    if len(sys.argv) < 2:
+        sys.exit("Usage: %s container-name [ttynum [escape]]" % sys.argv[0])
+    if len(sys.argv) > 2:
+        ttynum = int(sys.argv[2])
+    if len(sys.argv) > 3:
+        escape = ord(sys.argv[3]) - ord('a') + 1
+
+    ct = lxc.Container(sys.argv[1])
+
+    print("Container:%s tty:%d Ctrl-%c q to quit" % (ct.name, ttynum, ord('a') 
+ escape-1))
+    time.sleep(1)
+    if not ct.defined:
+        sys.exit("Container %s not defined" % ct.name)
+    if not ct.running:
+        sys.exit("Container %s not running" % ct.name)
+
+    ct.console(ttynum, 0, 1, 2, escape)
+    print("Console done")
diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c
index 0b31a37..02428d4 100644
--- a/src/python-lxc/lxc.c
+++ b/src/python-lxc/lxc.c
@@ -638,6 +638,40 @@ Container_unfreeze(Container *self, PyObject *args, 
PyObject *kwds)
 }
 
 static PyObject *
+Container_console(Container *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"ttynum", "stdinfd", "stdoutfd", "stderrfd", 
"escape", NULL};
+    int ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, escape = 1;
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iiiii", kwlist,
+                                      &ttynum, &stdinfd, &stdoutfd, &stderrfd,
+                                      &escape))
+        return NULL;
+
+    if (self->container->console(self->container, ttynum,
+                                stdinfd, stdoutfd, stderrfd, escape) == 0) {
+        Py_RETURN_TRUE;
+    }
+    Py_RETURN_FALSE;
+}
+
+static PyObject *
+Container_console_getfd(Container *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"ttynum", NULL};
+    int ttynum = -1, masterfd;
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &ttynum))
+        return NULL;
+
+    if (self->container->console_getfd(self->container, &ttynum, &masterfd) < 
0) {
+        PyErr_SetString(PyExc_ValueError, "Unable to allocate tty");
+        return NULL;
+    }
+    return PyLong_FromLong(masterfd);
+}
+
+static PyObject *
 Container_wait(Container *self, PyObject *args, PyObject *kwds)
 {
     static char *kwlist[] = {"state", "timeout", NULL};
@@ -804,6 +838,18 @@ static PyMethodDef Container_methods[] = {
      "\n"
      "Wait for the container to reach a given state or timeout."
     },
+    {"console", (PyCFunction)Container_console,
+     METH_VARARGS|METH_KEYWORDS,
+     "console(ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, escape = 
0) -> boolean\n"
+     "\n"
+     "Attach to container's console."
+    },
+    {"console_getfd", (PyCFunction)Container_console_getfd,
+     METH_VARARGS|METH_KEYWORDS,
+     "console(ttynum = -1) -> boolean\n"
+     "\n"
+     "Attach to container's console."
+    },
     {NULL, NULL, 0, NULL}
 };
 
diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py
index 2fc907c..8f108f9 100644
--- a/src/python-lxc/lxc/__init__.py
+++ b/src/python-lxc/lxc/__init__.py
@@ -291,19 +291,25 @@ class Container(_lxc.Container):
         self.load_config()
         return True
 
-    def console(self, tty="1"):
+    def console(self, ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, 
escape = 1):
         """
-            Access the console of a container.
+            Attach to console of running container.
         """
 
         if not self.running:
             return False
 
-        if subprocess.call(["lxc-console", "-n", self.name, "-t", "%s" % tty,
-                            "-P", self.get_config_path()],
-                           universal_newlines=True) != 0:
+        return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd, 
stderrfd, escape)
+
+    def console_getfd(self, ttynum = -1):
+        """
+            Attach to console of running container.
+        """
+
+        if not self.running:
             return False
-        return True
+
+        return _lxc.Container.console_getfd(self, ttynum)
 
     def get_cgroup_item(self, key):
         """
diff --git a/src/tests/console.c b/src/tests/console.c
index a3e7bca..434f7f2 100644
--- a/src/tests/console.c
+++ b/src/tests/console.c
@@ -61,7 +61,7 @@ static int test_console_running_container(struct 
lxc_container *c)
                ttynum[i] = ttyfd[i] = masterfd[i] = -1;
 
        ttynum[0] = 1;
-       ret = c->console(c, &ttynum[0], &masterfd[0]);
+       ret = c->console_getfd(c, &ttynum[0], &masterfd[0]);
        if (ret < 0) {
                TSTERR("console allocate failed");
                goto err1;
@@ -73,7 +73,7 @@ static int test_console_running_container(struct 
lxc_container *c)
        }
 
        /* attempt to alloc same ttynum */
-       ret = c->console(c, &ttynum[0], &masterfd[1]);
+       ret = c->console_getfd(c, &ttynum[0], &masterfd[1]);
        if (ret != -1) {
                TSTERR("console allocate should fail for allocated ttynum %d", 
ttynum[0]);
                goto err2;
@@ -86,7 +86,7 @@ static int test_console_running_container(struct 
lxc_container *c)
         */
        for (i = 0; i < 10; i++) {
                for (nrconsoles = 0; nrconsoles < MAXCONSOLES; nrconsoles++) {
-                       ret = c->console(c, &ttynum[nrconsoles], 
&masterfd[nrconsoles]);
+                       ret = c->console_getfd(c, &ttynum[nrconsoles], 
&masterfd[nrconsoles]);
                        if (ret < 0)
                                break;
                        ttyfd[nrconsoles] = ret;
-- 
1.8.1.4


------------------------------------------------------------------------------
This SF.net email is sponsored by Windows:

Build for Windows Store.

http://p.sf.net/sfu/windows-dev2dev
_______________________________________________
Lxc-devel mailing list
Lxc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lxc-devel

Reply via email to