]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
CVE-2016-10124: backport new console backend
authorChristian Brauner <christian.brauner@ubuntu.com>
Mon, 16 Jan 2017 14:03:59 +0000 (15:03 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Tue, 4 Apr 2017 14:52:30 +0000 (16:52 +0200)
- Make escape sequence to exit tty optional since we want to reuse
  lxc_console_cb_tty_stdin() in lxc_attach.c.
- Export the following functions since they can be reused in other modules:
  - lxc_console_cb_tty_stdin()
  - lxc_console_cb_tty_master()
  - lxc_setup_tios(int fd, struct termios *oldtios);
  - lxc_console_winsz(int srcfd, int dstfd);
  - lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr);
  - lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd);
  - lxc_console_sigwinch_fini(struct lxc_tty_state *ts);
- rewrite lxc_console_set_stdfds()
  - Make lxc_console_set_stdfds useable by other callers that do not have
    access to lxc_handler.
- Use ssh settings for ptys.
- Remove all asserts from console.{c,h}.
- Adapt start.c to changes.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/console.c
src/lxc/console.h
src/lxc/start.c

index d48cb81e7f96618f25c283ad3976623200ccc596..908ead0af134342db4be2f0632f6baebb3bc79bb 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <termios.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
+#include <sys/epoll.h>
 #include <sys/types.h>
-#include <termios.h>
 
 #include <lxc/lxccontainer.h>
 
-#include "log.h"
-#include "conf.h"
-#include "config.h"
-#include "start.h"     /* for struct lxc_handler */
+#include "af_unix.h"
 #include "caps.h"
 #include "commands.h"
-#include "mainloop.h"
-#include "af_unix.h"
+#include "conf.h"
+#include "config.h"
+#include "console.h"
+#include "log.h"
 #include "lxclock.h"
+#include "mainloop.h"
+#include "start.h"     /* for struct lxc_handler */
 #include "utils.h"
 
 #if HAVE_PTY_H
@@ -55,19 +56,6 @@ lxc_log_define(console, lxc);
 static struct lxc_list lxc_ttys;
 
 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)
@@ -75,12 +63,7 @@ void lxc_console_init(void)
        lxc_list_init(&lxc_ttys);
 }
 
-/* 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)
+void lxc_console_winsz(int srcfd, int dstfd)
 {
        struct winsize wsz;
        if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) {
@@ -93,10 +76,8 @@ static void lxc_console_winsz(int srcfd, int dstfd)
 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);
-       }
+       if (ts->winch_proxy)
+               lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
 }
 
 void lxc_console_sigwinch(int sig)
@@ -110,13 +91,14 @@ void lxc_console_sigwinch(int sig)
        }
 }
 
-static int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
-                                     struct lxc_epoll_descr *descr)
+int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
+               struct lxc_epoll_descr *descr)
 {
        struct signalfd_siginfo siginfo;
        struct lxc_tty_state *ts = cbdata;
 
-       if (read(fd, &siginfo, sizeof(siginfo)) < sizeof(siginfo)) {
+       ssize_t ret = read(fd, &siginfo, sizeof(siginfo));
+       if (ret < 0 || (size_t)ret < sizeof(siginfo)) {
                ERROR("failed to read signal info");
                return -1;
        }
@@ -125,27 +107,7 @@ static int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
        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)
+struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
 {
        sigset_t mask;
        struct lxc_tty_state *ts;
@@ -155,9 +117,9 @@ static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
                return NULL;
 
        memset(ts, 0, sizeof(*ts));
-       ts->stdinfd  = srcfd;
+       ts->stdinfd = srcfd;
        ts->masterfd = dstfd;
-       ts->sigfd    = -1;
+       ts->sigfd = -1;
 
        /* add tty to list to be scanned at SIGWINCH time */
        lxc_list_add_elem(&ts->node, ts);
@@ -166,45 +128,28 @@ static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
        sigemptyset(&mask);
        sigaddset(&mask, SIGWINCH);
        if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) {
-               SYSERROR("failed to block SIGWINCH");
-               goto err1;
+               SYSERROR("failed to block SIGWINCH.");
+               ts->sigfd = -1;
+               return ts;
        }
 
        ts->sigfd = signalfd(-1, &mask, 0);
        if (ts->sigfd < 0) {
-               SYSERROR("failed to get signalfd");
-               goto err2;
+               SYSERROR("failed to get signalfd.");
+               sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
+               ts->sigfd = -1;
+               return ts;
        }
 
        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 ts;
 }
 
-/*
- * 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)
+void lxc_console_sigwinch_fini(struct lxc_tty_state *ts)
 {
-       if (ts->sigfd >= 0) {
+       if (ts->sigfd >= 0)
                close(ts->sigfd);
-       }
+
        lxc_list_del(&ts->node);
        sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
        free(ts);
@@ -215,34 +160,30 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data,
 {
        struct lxc_console *console = (struct lxc_console *)data;
        char buf[1024];
-       int r,w;
-
-       w = r = read(fd, buf, sizeof(buf));
-       if (r < 0) {
-               SYSERROR("failed to read");
-               return 1;
-       }
+       int r, w;
 
-       if (!r) {
+       w = r = lxc_read_nointr(fd, buf, sizeof(buf));
+       if (r <= 0) {
                INFO("console client on fd %d has exited", fd);
                lxc_mainloop_del_handler(descr, fd);
                close(fd);
-               return 0;
+               return 1;
        }
 
        if (fd == console->peer)
-               w = write(console->master, buf, r);
+               w = lxc_write_nointr(console->master, buf, r);
 
        if (fd == console->master) {
                if (console->log_fd >= 0)
-                       w = write(console->log_fd, buf, r);
+                       w = lxc_write_nointr(console->log_fd, buf, r);
 
                if (console->peer >= 0)
-                       w = write(console->peer, buf, r);
+                       w = lxc_write_nointr(console->peer, buf, r);
        }
 
        if (w != r)
                WARN("console short write r:%d w:%d", r, w);
+
        return 0;
 }
 
@@ -254,7 +195,7 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console)
                        WARN("console peer not added to mainloop");
        }
 
-       if (console->tty_state) {
+       if (console->tty_state && console->tty_state->sigfd != -1) {
                if (lxc_mainloop_add_handler(console->descr,
                                             console->tty_state->sigfd,
                                             lxc_console_cb_sigwinch_fd,
@@ -265,10 +206,9 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console)
        }
 }
 
-int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
-                            struct lxc_handler *handler)
+extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
+                                   struct lxc_conf *conf)
 {
-       struct lxc_conf *conf = handler->conf;
        struct lxc_console *console = &conf->console;
 
        if (conf->is_execute) {
@@ -302,7 +242,7 @@ int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
        return 0;
 }
 
-static int setup_tios(int fd, struct termios *oldtios)
+int lxc_setup_tios(int fd, struct termios *oldtios)
 {
        struct termios newtios;
 
@@ -319,15 +259,21 @@ static int setup_tios(int fd, struct termios *oldtios)
 
        newtios = *oldtios;
 
-       /* Remove the echo characters and signal reception, the echo
-        * will be done with master proxying */
-       newtios.c_iflag &= ~IGNBRK;
-       newtios.c_iflag &= BRKINT;
-       newtios.c_lflag &= ~(ECHO|ICANON|ISIG);
+       /* We use the same settings that ssh does. */
+       newtios.c_iflag |= IGNPAR;
+       newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
+#ifdef IUCLC
+       newtios.c_iflag &= ~IUCLC;
+#endif
+       newtios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+#ifdef IEXTEN
+       newtios.c_lflag &= ~IEXTEN;
+#endif
+       newtios.c_oflag &= ~OPOST;
        newtios.c_cc[VMIN] = 1;
        newtios.c_cc[VTIME] = 0;
 
-       /* Set new attributes */
+       /* Set new attributes. */
        if (tcsetattr(fd, TCSAFLUSH, &newtios)) {
                ERROR("failed to set new terminal settings");
                return -1;
@@ -338,7 +284,7 @@ static int setup_tios(int fd, struct termios *oldtios)
 
 static void lxc_console_peer_proxy_free(struct lxc_console *console)
 {
-       if (console->tty_state) {
+       if (console->tty_state && console->tty_state->sigfd != -1) {
                lxc_console_sigwinch_fini(console->tty_state);
                console->tty_state = NULL;
        }
@@ -382,7 +328,7 @@ static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd)
                return -1;
        }
 
-       if (setup_tios(console->peerpty.slave, &oldtermio) < 0)
+       if (lxc_setup_tios(console->peerpty.slave, &oldtermio) < 0)
                goto err1;
 
        ts = lxc_console_sigwinch_init(console->peerpty.master, console->master);
@@ -402,13 +348,6 @@ err1:
        return -1;
 }
 
-/* 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;
@@ -435,9 +374,8 @@ 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++);
+       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)
@@ -452,14 +390,6 @@ out:
        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)
 {
        int i;
@@ -509,9 +439,11 @@ static void lxc_console_peer_default(struct lxc_console *console)
                goto err1;
 
        ts = lxc_console_sigwinch_init(console->peer, console->master);
-       if (!ts)
-               WARN("Unable to install SIGWINCH");
        console->tty_state = ts;
+       if (!ts) {
+               WARN("Unable to install SIGWINCH");
+               goto err1;
+       }
 
        lxc_console_winsz(console->peer, console->master);
 
@@ -521,7 +453,7 @@ static void lxc_console_peer_default(struct lxc_console *console)
                goto err1;
        }
 
-       if (setup_tios(console->peer, console->tios) < 0)
+       if (lxc_setup_tios(console->peer, console->tios) < 0)
                goto err2;
 
        return;
@@ -534,6 +466,7 @@ err1:
        console->peer = -1;
 out:
        DEBUG("no console peer");
+       return;
 }
 
 void lxc_console_delete(struct lxc_console *console)
@@ -611,70 +544,81 @@ err:
        return -1;
 }
 
-int lxc_console_set_stdfds(struct lxc_handler *handler)
+int lxc_console_set_stdfds(int fd)
 {
-       struct lxc_conf *conf = handler->conf;
-       struct lxc_console *console = &conf->console;
-
-       if (console->slave < 0)
+       if (fd < 0)
                return 0;
 
-       if (dup2(console->slave, 0) < 0 ||
-           dup2(console->slave, 1) < 0 ||
-           dup2(console->slave, 2) < 0)
-       {
-               SYSERROR("failed to dup console");
-               return -1;
-       }
+       if (isatty(STDIN_FILENO))
+               if (dup2(fd, STDIN_FILENO) < 0) {
+                       SYSERROR("failed to duplicate stdin.");
+                       return -1;
+               }
+
+       if (isatty(STDOUT_FILENO))
+               if (dup2(fd, STDOUT_FILENO) < 0) {
+                       SYSERROR("failed to duplicate stdout.");
+                       return -1;
+               }
+
+       if (isatty(STDERR_FILENO))
+               if (dup2(fd, STDERR_FILENO) < 0) {
+                       SYSERROR("failed to duplicate stderr.");
+                       return -1;
+               }
+
        return 0;
 }
 
-static int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
-                                   struct lxc_epoll_descr *descr)
+int lxc_console_cb_tty_stdin(int fd, uint32_t events, 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");
+       if (fd != ts->stdinfd)
                return 1;
-       }
-
-       /* we want to exit the console with Ctrl+a q */
-       if (c == ts->escape && !ts->saw_escape) {
-               ts->saw_escape = 1;
-               return 0;
-       }
 
-       if (c == 'q' && ts->saw_escape)
+       if (lxc_read_nointr(ts->stdinfd, &c, 1) <= 0)
                return 1;
 
-       ts->saw_escape = 0;
-       if (write(ts->masterfd, &c, 1) < 0) {
-               SYSERROR("failed to write");
-               return 1;
+       if (ts->escape != -1) {
+               /* we want to exit the console with Ctrl+a q */
+               if (c == ts->escape && !ts->saw_escape) {
+                       ts->saw_escape = 1;
+                       return 0;
+               }
+
+               if (c == 'q' && ts->saw_escape)
+                       return 1;
+
+               ts->saw_escape = 0;
        }
 
+       if (lxc_write_nointr(ts->masterfd, &c, 1) <= 0)
+               return 1;
+
        return 0;
 }
 
-static int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
-                                    struct lxc_epoll_descr *descr)
+int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
+               struct lxc_epoll_descr *descr)
 {
        struct lxc_tty_state *ts = cbdata;
        char buf[1024];
-       int r,w;
+       int r, w;
 
-       assert(fd == ts->masterfd);
-       r = read(fd, buf, sizeof(buf));
-       if (r < 0) {
-               SYSERROR("failed to read");
+       if (fd != ts->masterfd)
+               return 1;
+
+       r = lxc_read_nointr(fd, buf, sizeof(buf));
+       if (r <= 0)
                return 1;
-       }
 
-       w = write(ts->stdoutfd, buf, r);
-       if (w < 0 || w != r) {
+       w = lxc_write_nointr(ts->stdoutfd, buf, r);
+       if (w <= 0) {
+               return 1;
+       } else if (w != r) {
                SYSERROR("failed to write");
                return 1;
        }
@@ -701,7 +645,7 @@ int lxc_console(struct lxc_container *c, int ttynum,
                return -1;
        }
 
-       ret = setup_tios(stdinfd, &oldtios);
+       ret = lxc_setup_tios(stdinfd, &oldtios);
        if (ret) {
                ERROR("failed to setup tios");
                return -1;
@@ -741,11 +685,13 @@ int lxc_console(struct lxc_container *c, int ttynum,
                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;
+       if (ts->sigfd != -1) {
+               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,
@@ -773,7 +719,8 @@ int lxc_console(struct lxc_container *c, int ttynum,
 err4:
        lxc_mainloop_close(&descr);
 err3:
-       lxc_console_sigwinch_fini(ts);
+       if (ts->sigfd != -1)
+               lxc_console_sigwinch_fini(ts);
 err2:
        close(masterfd);
        close(ttyfd);
@@ -782,3 +729,4 @@ err1:
 
        return ret;
 }
+
index 41d53e62641ec5a5585491bc1b2ae919fd19f693..fefdb19e2c962a436ab26550b00f02b7bb704b9c 100644 (file)
 #ifndef __LXC_CONSOLE_H
 #define __LXC_CONSOLE_H
 
-struct lxc_epoll_descr;
-struct lxc_container;
+#include "conf.h"
+#include "list.h"
 
+struct lxc_epoll_descr; /* defined in mainloop.h */
+struct lxc_container; /* defined in lxccontainer.h */
+struct lxc_tty_state
+{
+       struct lxc_list node;
+       int stdinfd;
+       int stdoutfd;
+       int masterfd;
+       /* Escape sequence to use for exiting the pty. A single char can be
+        * specified. The pty can then exited by doing: Ctrl + specified_char + q.
+        * This field is checked by lxc_console_cb_tty_stdin(). Set to -1 to
+        * disable exiting the pty via a escape sequence. */
+       int escape;
+       /* Used internally by lxc_console_cb_tty_stdin() to check whether an
+        * escape sequence has been received. */
+       int saw_escape;
+       /* Name of the container to forward the SIGWINCH event to. */
+       const char *winch_proxy;
+       /* Path of the container to forward the SIGWINCH event to. */
+       const char *winch_proxy_lxcpath;
+       /* File descriptor that accepts SIGWINCH signals. If set to -1 no
+        * SIGWINCH handler could be installed. This also means that
+        * the sigset_t oldmask member is meaningless. */
+       int sigfd;
+       sigset_t oldmask;
+};
+
+/*
+ * 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
+ */
 extern int  lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttynum);
+
+/*
+ * Create a new pty:
+ * - calls openpty() to allocate a master/slave pty pair
+ * - sets the FD_CLOEXEC flag on the master/slave fds
+ * - allocates either the current controlling pty (default) or a user specified
+ *   pty as peer pty for the newly created master/slave pair
+ * - sets up SIGWINCH handler, winsz, and new terminal settings
+ *   (Handlers for SIGWINCH and I/O are not registered in a mainloop.)
+ * (For an unprivileged container the created pty on the host is not
+ * 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 *);
+
+/*
+ * Delete a pty created via lxc_console_create():
+ * - set old terminal settings
+ * - memory allocated via lxc_console_create() is free()ed.
+ * - close master/slave pty pair and allocated fd for the peer (usually
+ *   /dev/tty)
+ * Registered handlers in a mainloop are not automatically deleted.
+ */
 extern void lxc_console_delete(struct lxc_console *);
+
+/*
+ * 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.
+ */
 extern void lxc_console_free(struct lxc_conf *conf, int fd);
 
-extern int  lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_handler *);
+/*
+ * Register pty event handlers in an open mainloop
+ */
+extern int  lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_conf *);
+
+/*
+ * Handle SIGWINCH events on the allocated ptys.
+ */
 extern void lxc_console_sigwinch(int sig);
+
+/*
+ * Connect to one of the ptys given to the container via lxc.tty.
+ * - allocates either the current controlling pty (default) or a user specified
+ *   pty as peer pty for the containers tty
+ * - sets up SIGWINCH handler, winsz, and new terminal settings
+ * - opens mainloop
+ * - 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);
+
+/*
+ * Allocate one of the ptys given to the container via lxc.tty. Returns an open
+ * fd to the allocated pty.
+ * 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_set_stdfds(struct lxc_handler *);
+
+/*
+ * Make fd a duplicate of the standard file descriptors:
+ * fd is made a duplicate of a specific standard file descriptor iff the
+ * standard file descriptor refers to a pty.
+ */
+extern int lxc_console_set_stdfds(int fd);
+
+/*
+ * Handler for events on the stdin fd of the pty. To be registered via the
+ * corresponding functions declared and defined in mainloop.{c,h} or
+ * lxc_console_mainloop_add().
+ * 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);
+
+/*
+ * Handler for events on the master fd of the pty. To be registered via the
+ * corresponding functions declared and defined in mainloop.{c,h} or
+ * lxc_console_mainloop_add().
+ * 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);
+
+/*
+ * Setup new terminal properties. The old terminal settings are stored in
+ * oldtios.
+ */
+extern int lxc_setup_tios(int fd, struct termios *oldtios);
+
+
+/*
+ * 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)
+ */
+extern void lxc_console_winsz(int srcfd, int dstfd);
+
+/*
+ * 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()).
+ *
+ * This function allocates memory. It is up to the caller to free it.
+ */
+extern struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd);
+
+/*
+ * Handler for SIGWINCH events. To be registered via the corresponding functions
+ * 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);
+
+/*
+ * 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.
+ */
+extern void lxc_console_sigwinch_fini(struct lxc_tty_state *ts);
 
 #endif
index b46e85920ad03389169dd728b09f3a99185d5408..d9b3c40270c3995e914bc92e6c6eaab6af10bc14 100644 (file)
@@ -339,7 +339,7 @@ static int lxc_poll(const char *name, struct lxc_handler *handler)
                goto out_mainloop_open;
        }
 
-       if (lxc_console_mainloop_add(&descr, handler)) {
+       if (lxc_console_mainloop_add(&descr, handler->conf)) {
                ERROR("failed to add console handler to mainloop");
                goto out_mainloop_open;
        }
@@ -751,7 +751,7 @@ static int do_start(void *data)
         * setup on its console ie. the pty allocated in lxc_console_create()
         * so make sure that that pty is stdin,stdout,stderr.
         */
-       if (lxc_console_set_stdfds(handler) < 0)
+       if (lxc_console_set_stdfds(handler->conf->console.slave) < 0)
                goto out_warn_father;
 
        /* If we mounted a temporary proc, then unmount it now */
@@ -800,7 +800,7 @@ static int save_phys_nics(struct lxc_conf *conf)
 
        if (!am_root)
                return 0;
-               
+
        lxc_list_for_each(iterator, &conf->network) {
                struct lxc_netdev *netdev = iterator->elem;