CVE-2016-10124: backport new console backend

- 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: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent b2ed6cc2
...@@ -21,27 +21,28 @@ ...@@ -21,27 +21,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * 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 <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <sys/epoll.h>
#include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <termios.h>
#include <lxc/lxccontainer.h> #include <lxc/lxccontainer.h>
#include "log.h" #include "af_unix.h"
#include "conf.h"
#include "config.h"
#include "start.h" /* for struct lxc_handler */
#include "caps.h" #include "caps.h"
#include "commands.h" #include "commands.h"
#include "mainloop.h" #include "conf.h"
#include "af_unix.h" #include "config.h"
#include "console.h"
#include "log.h"
#include "lxclock.h" #include "lxclock.h"
#include "mainloop.h"
#include "start.h" /* for struct lxc_handler */
#include "utils.h" #include "utils.h"
#if HAVE_PTY_H #if HAVE_PTY_H
...@@ -55,19 +56,6 @@ lxc_log_define(console, lxc); ...@@ -55,19 +56,6 @@ lxc_log_define(console, lxc);
static struct lxc_list lxc_ttys; static struct lxc_list lxc_ttys;
typedef void (*sighandler_t)(int); 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)) __attribute__((constructor))
void lxc_console_init(void) void lxc_console_init(void)
...@@ -75,12 +63,7 @@ void lxc_console_init(void) ...@@ -75,12 +63,7 @@ void lxc_console_init(void)
lxc_list_init(&lxc_ttys); lxc_list_init(&lxc_ttys);
} }
/* lxc_console_winsz: propagte winsz from one terminal to another void lxc_console_winsz(int srcfd, int dstfd)
*
* @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; struct winsize wsz;
if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) { if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) {
...@@ -93,10 +76,8 @@ static void lxc_console_winsz(int srcfd, int dstfd) ...@@ -93,10 +76,8 @@ static void lxc_console_winsz(int srcfd, int dstfd)
static void lxc_console_winch(struct lxc_tty_state *ts) static void lxc_console_winch(struct lxc_tty_state *ts)
{ {
lxc_console_winsz(ts->stdinfd, ts->masterfd); lxc_console_winsz(ts->stdinfd, ts->masterfd);
if (ts->winch_proxy) { if (ts->winch_proxy)
lxc_cmd_console_winch(ts->winch_proxy, lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
ts->winch_proxy_lxcpath);
}
} }
void lxc_console_sigwinch(int sig) void lxc_console_sigwinch(int sig)
...@@ -110,13 +91,14 @@ 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, int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
struct signalfd_siginfo siginfo; struct signalfd_siginfo siginfo;
struct lxc_tty_state *ts = cbdata; 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"); ERROR("failed to read signal info");
return -1; return -1;
} }
...@@ -125,27 +107,7 @@ static int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata, ...@@ -125,27 +107,7 @@ static int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
return 0; return 0;
} }
/* struct lxc_tty_state *lxc_console_sigwinch_init(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()).
*/
static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
{ {
sigset_t mask; sigset_t mask;
struct lxc_tty_state *ts; struct lxc_tty_state *ts;
...@@ -155,9 +117,9 @@ static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd) ...@@ -155,9 +117,9 @@ static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
return NULL; return NULL;
memset(ts, 0, sizeof(*ts)); memset(ts, 0, sizeof(*ts));
ts->stdinfd = srcfd; ts->stdinfd = srcfd;
ts->masterfd = dstfd; ts->masterfd = dstfd;
ts->sigfd = -1; ts->sigfd = -1;
/* add tty to list to be scanned at SIGWINCH time */ /* add tty to list to be scanned at SIGWINCH time */
lxc_list_add_elem(&ts->node, ts); lxc_list_add_elem(&ts->node, ts);
...@@ -166,45 +128,28 @@ static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd) ...@@ -166,45 +128,28 @@ static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
sigemptyset(&mask); sigemptyset(&mask);
sigaddset(&mask, SIGWINCH); sigaddset(&mask, SIGWINCH);
if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) { if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) {
SYSERROR("failed to block SIGWINCH"); SYSERROR("failed to block SIGWINCH.");
goto err1; ts->sigfd = -1;
return ts;
} }
ts->sigfd = signalfd(-1, &mask, 0); ts->sigfd = signalfd(-1, &mask, 0);
if (ts->sigfd < 0) { if (ts->sigfd < 0) {
SYSERROR("failed to get signalfd"); SYSERROR("failed to get signalfd.");
goto err2; sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
ts->sigfd = -1;
return ts;
} }
DEBUG("%d got SIGWINCH fd %d", getpid(), ts->sigfd); 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; return ts;
} }
/* void lxc_console_sigwinch_fini(struct lxc_tty_state *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)
{ {
if (ts->sigfd >= 0) { if (ts->sigfd >= 0)
close(ts->sigfd); close(ts->sigfd);
}
lxc_list_del(&ts->node); lxc_list_del(&ts->node);
sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
free(ts); free(ts);
...@@ -215,34 +160,30 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data, ...@@ -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; struct lxc_console *console = (struct lxc_console *)data;
char buf[1024]; char buf[1024];
int r,w; int r, w;
w = r = read(fd, buf, sizeof(buf));
if (r < 0) {
SYSERROR("failed to read");
return 1;
}
if (!r) { w = r = lxc_read_nointr(fd, buf, sizeof(buf));
if (r <= 0) {
INFO("console client on fd %d has exited", fd); INFO("console client on fd %d has exited", fd);
lxc_mainloop_del_handler(descr, fd); lxc_mainloop_del_handler(descr, fd);
close(fd); close(fd);
return 0; return 1;
} }
if (fd == console->peer) if (fd == console->peer)
w = write(console->master, buf, r); w = lxc_write_nointr(console->master, buf, r);
if (fd == console->master) { if (fd == console->master) {
if (console->log_fd >= 0) 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) if (console->peer >= 0)
w = write(console->peer, buf, r); w = lxc_write_nointr(console->peer, buf, r);
} }
if (w != r) if (w != r)
WARN("console short write r:%d w:%d", r, w); WARN("console short write r:%d w:%d", r, w);
return 0; return 0;
} }
...@@ -254,7 +195,7 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console) ...@@ -254,7 +195,7 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console)
WARN("console peer not added to mainloop"); 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, if (lxc_mainloop_add_handler(console->descr,
console->tty_state->sigfd, console->tty_state->sigfd,
lxc_console_cb_sigwinch_fd, lxc_console_cb_sigwinch_fd,
...@@ -265,10 +206,9 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console) ...@@ -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, extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
struct lxc_handler *handler) struct lxc_conf *conf)
{ {
struct lxc_conf *conf = handler->conf;
struct lxc_console *console = &conf->console; struct lxc_console *console = &conf->console;
if (conf->is_execute) { if (conf->is_execute) {
...@@ -302,7 +242,7 @@ int lxc_console_mainloop_add(struct lxc_epoll_descr *descr, ...@@ -302,7 +242,7 @@ int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
return 0; return 0;
} }
static int setup_tios(int fd, struct termios *oldtios) int lxc_setup_tios(int fd, struct termios *oldtios)
{ {
struct termios newtios; struct termios newtios;
...@@ -319,15 +259,21 @@ static int setup_tios(int fd, struct termios *oldtios) ...@@ -319,15 +259,21 @@ static int setup_tios(int fd, struct termios *oldtios)
newtios = *oldtios; newtios = *oldtios;
/* Remove the echo characters and signal reception, the echo /* We use the same settings that ssh does. */
* will be done with master proxying */ newtios.c_iflag |= IGNPAR;
newtios.c_iflag &= ~IGNBRK; newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
newtios.c_iflag &= BRKINT; #ifdef IUCLC
newtios.c_lflag &= ~(ECHO|ICANON|ISIG); 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[VMIN] = 1;
newtios.c_cc[VTIME] = 0; newtios.c_cc[VTIME] = 0;
/* Set new attributes */ /* Set new attributes. */
if (tcsetattr(fd, TCSAFLUSH, &newtios)) { if (tcsetattr(fd, TCSAFLUSH, &newtios)) {
ERROR("failed to set new terminal settings"); ERROR("failed to set new terminal settings");
return -1; return -1;
...@@ -338,7 +284,7 @@ static int setup_tios(int fd, struct termios *oldtios) ...@@ -338,7 +284,7 @@ static int setup_tios(int fd, struct termios *oldtios)
static void lxc_console_peer_proxy_free(struct lxc_console *console) 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); lxc_console_sigwinch_fini(console->tty_state);
console->tty_state = NULL; console->tty_state = NULL;
} }
...@@ -382,7 +328,7 @@ static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd) ...@@ -382,7 +328,7 @@ static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd)
return -1; return -1;
} }
if (setup_tios(console->peerpty.slave, &oldtermio) < 0) if (lxc_setup_tios(console->peerpty.slave, &oldtermio) < 0)
goto err1; goto err1;
ts = lxc_console_sigwinch_init(console->peerpty.master, console->master); ts = lxc_console_sigwinch_init(console->peerpty.master, console->master);
...@@ -402,13 +348,6 @@ err1: ...@@ -402,13 +348,6 @@ err1:
return -1; 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 lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
{ {
int masterfd = -1, ttynum; int masterfd = -1, ttynum;
...@@ -435,9 +374,8 @@ int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) ...@@ -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] */ /* search for next available tty, fixup index tty1 => [0] */
for (ttynum = 1; for (ttynum = 1; ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; ttynum++)
ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; ;
ttynum++);
/* we didn't find any available slot for tty */ /* we didn't find any available slot for tty */
if (ttynum > tty_info->nbtty) if (ttynum > tty_info->nbtty)
...@@ -452,14 +390,6 @@ out: ...@@ -452,14 +390,6 @@ out:
return masterfd; 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) void lxc_console_free(struct lxc_conf *conf, int fd)
{ {
int i; int i;
...@@ -509,9 +439,11 @@ static void lxc_console_peer_default(struct lxc_console *console) ...@@ -509,9 +439,11 @@ static void lxc_console_peer_default(struct lxc_console *console)
goto err1; goto err1;
ts = lxc_console_sigwinch_init(console->peer, console->master); ts = lxc_console_sigwinch_init(console->peer, console->master);
if (!ts)
WARN("Unable to install SIGWINCH");
console->tty_state = ts; console->tty_state = ts;
if (!ts) {
WARN("Unable to install SIGWINCH");
goto err1;
}
lxc_console_winsz(console->peer, console->master); lxc_console_winsz(console->peer, console->master);
...@@ -521,7 +453,7 @@ static void lxc_console_peer_default(struct lxc_console *console) ...@@ -521,7 +453,7 @@ static void lxc_console_peer_default(struct lxc_console *console)
goto err1; goto err1;
} }
if (setup_tios(console->peer, console->tios) < 0) if (lxc_setup_tios(console->peer, console->tios) < 0)
goto err2; goto err2;
return; return;
...@@ -534,6 +466,7 @@ err1: ...@@ -534,6 +466,7 @@ err1:
console->peer = -1; console->peer = -1;
out: out:
DEBUG("no console peer"); DEBUG("no console peer");
return;
} }
void lxc_console_delete(struct lxc_console *console) void lxc_console_delete(struct lxc_console *console)
...@@ -611,70 +544,81 @@ err: ...@@ -611,70 +544,81 @@ err:
return -1; return -1;
} }
int lxc_console_set_stdfds(struct lxc_handler *handler) int lxc_console_set_stdfds(int fd)
{ {
struct lxc_conf *conf = handler->conf; if (fd < 0)
struct lxc_console *console = &conf->console;
if (console->slave < 0)
return 0; return 0;
if (dup2(console->slave, 0) < 0 || if (isatty(STDIN_FILENO))
dup2(console->slave, 1) < 0 || if (dup2(fd, STDIN_FILENO) < 0) {
dup2(console->slave, 2) < 0) SYSERROR("failed to duplicate stdin.");
{ return -1;
SYSERROR("failed to dup console"); }
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; return 0;
} }
static int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata, int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
struct lxc_tty_state *ts = cbdata; struct lxc_tty_state *ts = cbdata;
char c; char c;
assert(fd == ts->stdinfd); if (fd != ts->stdinfd)
if (read(ts->stdinfd, &c, 1) < 0) {
SYSERROR("failed to read");
return 1; 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; return 1;
ts->saw_escape = 0; if (ts->escape != -1) {
if (write(ts->masterfd, &c, 1) < 0) { /* we want to exit the console with Ctrl+a q */
SYSERROR("failed to write"); if (c == ts->escape && !ts->saw_escape) {
return 1; 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; return 0;
} }
static int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata, int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
struct lxc_tty_state *ts = cbdata; struct lxc_tty_state *ts = cbdata;
char buf[1024]; char buf[1024];
int r,w; int r, w;
assert(fd == ts->masterfd); if (fd != ts->masterfd)
r = read(fd, buf, sizeof(buf)); return 1;
if (r < 0) {
SYSERROR("failed to read"); r = lxc_read_nointr(fd, buf, sizeof(buf));
if (r <= 0)
return 1; return 1;
}
w = write(ts->stdoutfd, buf, r); w = lxc_write_nointr(ts->stdoutfd, buf, r);
if (w < 0 || w != r) { if (w <= 0) {
return 1;
} else if (w != r) {
SYSERROR("failed to write"); SYSERROR("failed to write");
return 1; return 1;
} }
...@@ -701,7 +645,7 @@ int lxc_console(struct lxc_container *c, int ttynum, ...@@ -701,7 +645,7 @@ int lxc_console(struct lxc_container *c, int ttynum,
return -1; return -1;
} }
ret = setup_tios(stdinfd, &oldtios); ret = lxc_setup_tios(stdinfd, &oldtios);
if (ret) { if (ret) {
ERROR("failed to setup tios"); ERROR("failed to setup tios");
return -1; return -1;
...@@ -741,11 +685,13 @@ int lxc_console(struct lxc_container *c, int ttynum, ...@@ -741,11 +685,13 @@ int lxc_console(struct lxc_container *c, int ttynum,
goto err3; goto err3;
} }
ret = lxc_mainloop_add_handler(&descr, ts->sigfd, if (ts->sigfd != -1) {
lxc_console_cb_sigwinch_fd, ts); ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
if (ret) { lxc_console_cb_sigwinch_fd, ts);
ERROR("failed to add handler for SIGWINCH fd"); if (ret) {
goto err4; ERROR("failed to add handler for SIGWINCH fd");
goto err4;
}
} }
ret = lxc_mainloop_add_handler(&descr, ts->stdinfd, ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
...@@ -773,7 +719,8 @@ int lxc_console(struct lxc_container *c, int ttynum, ...@@ -773,7 +719,8 @@ int lxc_console(struct lxc_container *c, int ttynum,
err4: err4:
lxc_mainloop_close(&descr); lxc_mainloop_close(&descr);
err3: err3:
lxc_console_sigwinch_fini(ts); if (ts->sigfd != -1)
lxc_console_sigwinch_fini(ts);
err2: err2:
close(masterfd); close(masterfd);
close(ttyfd); close(ttyfd);
...@@ -782,3 +729,4 @@ err1: ...@@ -782,3 +729,4 @@ err1:
return ret; return ret;
} }
...@@ -24,21 +24,195 @@ ...@@ -24,21 +24,195 @@
#ifndef __LXC_CONSOLE_H #ifndef __LXC_CONSOLE_H
#define __LXC_CONSOLE_H #define __LXC_CONSOLE_H
struct lxc_epoll_descr; #include "conf.h"
struct lxc_container; #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); 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 *); 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 *); 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 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); 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, extern int lxc_console(struct lxc_container *c, int ttynum,
int stdinfd, int stdoutfd, int stderrfd, int stdinfd, int stdoutfd, int stderrfd,
int escape); 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, extern int lxc_console_getfd(struct lxc_container *c, int *ttynum,
int *masterfd); 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 #endif
...@@ -339,7 +339,7 @@ static int lxc_poll(const char *name, struct lxc_handler *handler) ...@@ -339,7 +339,7 @@ static int lxc_poll(const char *name, struct lxc_handler *handler)
goto out_mainloop_open; 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"); ERROR("failed to add console handler to mainloop");
goto out_mainloop_open; goto out_mainloop_open;
} }
...@@ -751,7 +751,7 @@ static int do_start(void *data) ...@@ -751,7 +751,7 @@ static int do_start(void *data)
* setup on its console ie. the pty allocated in lxc_console_create() * setup on its console ie. the pty allocated in lxc_console_create()
* so make sure that that pty is stdin,stdout,stderr. * 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; goto out_warn_father;
/* If we mounted a temporary proc, then unmount it now */ /* If we mounted a temporary proc, then unmount it now */
...@@ -800,7 +800,7 @@ static int save_phys_nics(struct lxc_conf *conf) ...@@ -800,7 +800,7 @@ static int save_phys_nics(struct lxc_conf *conf)
if (!am_root) if (!am_root)
return 0; return 0;
lxc_list_for_each(iterator, &conf->network) { lxc_list_for_each(iterator, &conf->network) {
struct lxc_netdev *netdev = iterator->elem; struct lxc_netdev *netdev = iterator->elem;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment