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. 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 | 2 + src/lxc/console.c | 677 +++++++++++++++++++++++++------ 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 | 19 +- 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 +- 17 files changed, 827 insertions(+), 369 deletions(-) create mode 100755 src/python-lxc/examples/pyconsole-vte.py create mode 100755 src/python-lxc/examples/pyconsole.py diff --git a/doc/lxc-console.sgml.in b/doc/lxc-console.sgml.in index 9299778..f4737d1 100644 --- a/doc/lxc-console.sgml.in +++ b/doc/lxc-console.sgml.in @@ -78,6 +78,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA </para> <para> + A <replaceable>ttynum</replaceable> of 0 may be given to attach + to the container's /dev/console instead of its + dev/tty<<replaceable>ttynum</replaceable>>. + </para> + + <para> A keyboard escape sequence may be used to disconnect from the tty and quit lxc-console. The default escape sequence is <Ctrl+a q>. </para> @@ -107,8 +113,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA </term> <listitem> <para> - Specify the tty number to connect, if not specified a tty - number will be automatically choosen by the container. + Specify the tty number to connect to or 0 for the console. If not + specified the next available tty number will be automatically + choosen by the container. </para> </listitem> </varlistentry> diff --git a/src/lxc/commands.c b/src/lxc/commands.c index b4afc07..b23eb98 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -40,6 +40,7 @@ #include <lxc/utils.h> #include "commands.h" +#include "console.h" #include "confile.h" #include "mainloop.h" #include "af_unix.h" @@ -546,6 +547,37 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, } /* + * lxc_cmd_console_winch: To process as if a SIGWINCH were received + * + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running + * + * Returns 0 on success, < 0 on failure + */ +int lxc_cmd_console_winch(const char *name, const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_CONSOLE_WINCH }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) + return ret; + + return 0; +} + +static int lxc_cmd_console_winch_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + struct lxc_cmd_rsp rsp = { .data = 0 }; + + lxc_console_sigwinch(SIGWINCH); + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* * lxc_cmd_console: Open an fd to a tty in the container * * @name : name of container to connect to @@ -599,39 +631,21 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { int ttynum = PTR_TO_INT(req->data); - struct lxc_tty_info *tty_info = &handler->conf->tty_info; + int masterfd; struct lxc_cmd_rsp rsp; - if (ttynum > 0) { - if (ttynum > tty_info->nbtty) - goto out_close; - - if (tty_info->pty_info[ttynum - 1].busy) - goto out_close; - - /* the requested tty is available */ - goto out_send; - } - - /* search for next available tty, fixup index tty1 => [0] */ - for (ttynum = 1; - ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; - ttynum++); - - /* we didn't find any available slot for tty */ - if (ttynum > tty_info->nbtty) + masterfd = lxc_console_allocate(handler->conf, fd, &ttynum); + if (masterfd < 0) goto out_close; -out_send: memset(&rsp, 0, sizeof(rsp)); rsp.data = INT_TO_PTR(ttynum); - if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master, - &rsp, sizeof(rsp)) < 0) { + if (lxc_af_unix_send_fd(fd, masterfd, &rsp, sizeof(rsp)) < 0) { ERROR("failed to send tty to client"); + lxc_console_free(handler->conf, fd); goto out_close; } - tty_info->pty_info[ttynum - 1].busy = fd; return 0; out_close: @@ -650,6 +664,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, callback cb[LXC_CMD_MAX] = { [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, + [LXC_CMD_CONSOLE_WINCH] = lxc_cmd_console_winch_callback, [LXC_CMD_STOP] = lxc_cmd_stop_callback, [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, @@ -668,8 +683,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, struct lxc_epoll_descr *descr) { - extern void lxc_console_remove_fd(int, struct lxc_tty_info *); - lxc_console_remove_fd(fd, &handler->conf->tty_info); + lxc_console_free(handler->conf, fd); lxc_mainloop_del_handler(descr, fd); close(fd); } diff --git a/src/lxc/commands.h b/src/lxc/commands.h index c3738cd..46806cb 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -34,6 +34,7 @@ typedef enum { LXC_CMD_CONSOLE, + LXC_CMD_CONSOLE_WINCH, LXC_CMD_STOP, LXC_CMD_GET_STATE, LXC_CMD_GET_INIT_PID, @@ -65,6 +66,7 @@ struct lxc_cmd_console_rsp_data { int ttynum; }; +extern int lxc_cmd_console_winch(const char *name, const char *lxcpath); extern int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath); extern char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath); diff --git a/src/lxc/conf.c b/src/lxc/conf.c index a1aee14..9010623 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..434bab8 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -197,6 +197,8 @@ 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; diff --git a/src/lxc/console.c b/src/lxc/console.c index 93c16b5..4f3e32b 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,7 @@ #include "commands.h" #include "mainloop.h" #include "af_unix.h" +#include "lxclock.h" #if HAVE_PTY_H #include <pty.h> @@ -46,156 +50,431 @@ 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; + sighandler_t handler; +}; + +__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; +void lxc_console_sigwinch(int sig) +{ + if (process_lock() == 0) { + struct lxc_list *it; + struct lxc_tty_state *ts; + + lxc_list_for_each(it, &lxc_ttys) { + ts = it->elem; + lxc_console_winsz(ts->stdinfd, ts->masterfd); + if (ts->winch_proxy) { + lxc_cmd_console_winch(ts->winch_proxy, + ts->winch_proxy_lxcpath); + } + } + process_unlock(); + } } -static int get_default_console(char **console) +/* + * 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 + * + * Must be called with process_lock held to protect the lxc_ttys list, or + * from a non-threaded context. + */ +static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd) { - int fd; + sigset_t mask; + 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; - } + ts = malloc(sizeof(*ts)); + if (!ts) + return NULL; + + memset(ts, 0, sizeof(*ts)); + ts->stdinfd = srcfd; + ts->masterfd = dstfd; + + /* 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); + + ts->handler = signal(SIGWINCH, lxc_console_sigwinch); + if (ts->handler == SIG_ERR) { + SYSERROR("failed to set SIGWINCH handler"); + goto err1; } - if (!access("/dev/null", F_OK)) { - *console = strdup("/dev/null"); - goto out; + sigemptyset(&mask); + sigaddset(&mask, SIGWINCH); + if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { + SYSERROR("failed to unblock SIGWINCH"); + goto err2; } - ERROR("No suitable default console"); + DEBUG("%d installed SIGWINCH handler", getpid()); + goto out; + +err2: + signal(SIGWINCH, ts->handler); +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; + signal(SIGWINCH, ts->handler); + lxc_list_del(&ts->node); + 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 (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->master, F_SETFD, FD_CLOEXEC)) { - SYSERROR("failed to set console master 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 (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) { - SYSERROR("failed to set console slave to close-on-exec"); - goto err; +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->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; - } - DEBUG("using '%s' as console log", console->log_path); - console->log_fd = fd; + 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) +{ + 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; + + 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; } -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; + + console->peer = console->peerpty.slave; + console->peerpty.busy = sockfd; + lxc_console_mainloop_add_peer(console); + + lxc_console_sigwinch_init(console->peerpty.master, console->master); + 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) { - if (console->tios && + 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) +{ + int i; + struct lxc_tty_info *tty_info = &conf->tty_info; + struct lxc_console *console = &conf->console; + + 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); + } +} + +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"); + + 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 +495,201 @@ 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->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..a660ec5 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -387,25 +387,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 +437,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/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..4dbcf77 --- /dev/null +++ b/src/python-lxc/examples/pyconsole.py @@ -0,0 +1,33 @@ +#!/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 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..fd227d3 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 = 0): """ - 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 ------------------------------------------------------------------------------ How ServiceNow helps IT people transform IT departments: 1. A cloud service to automate IT design, transition and operations 2. Dashboards that offer high-level views of enterprise services 3. A single system of record for all IT processes http://p.sf.net/sfu/servicenow-d2d-j _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel