Commit b5159817 by Dwight Engen Committed by Serge Hallyn

console API improvements

Add a higher level console API that opens a tty/console and runs the mainloop as well. Rename existing API to console_getfd(). Use these in the python binding. Allow attaching a console peer after container bootup, including if the container was launched with -d. This is made possible by allocation of a "proxy" pty as the peer when the console is attached to. Improve handling of SIGWINCH, the pty size will be correctly set at the beginning of a session and future changes when using the lxc_console() API will be propagated to it as well. Refactor some common code between lxc_console.c and console.c. The variable wait4q (renamed to saw_escape) was static, making the mainloop callback not safe across threads. This wasn't a problem when the callback was in the non-threaded lxc-console, but now that it is internal to console.c, we have to take care of it. This is now contained in a per-tty state structure. Don't attempt to open /dev/null as the console peer since /dev/null cannot be added to the mainloop (epoll_ctl() fails with EPERM). This isn't needed to get the console setup (and the log to work) since the case of not having a peer at console init time has to be handled to allow for attaching to it later. Move signalfd libc wrapper/replacement to utils.h. Signed-off-by: 's avatarDwight Engen <dwight.engen@oracle.com> Signed-off-by: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com>
parent 5d4d3ebb
......@@ -78,6 +78,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
</para>
<para>
A <replaceable>ttynum</replaceable> of 0 may be given to attach
to the container's /dev/console instead of its
dev/tty&lt;<replaceable>ttynum</replaceable>&gt;.
</para>
<para>
A keyboard escape sequence may be used to disconnect from the tty
and quit lxc-console. The default escape sequence is &lt;Ctrl+a q&gt;.
</para>
......@@ -107,8 +113,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
</term>
<listitem>
<para>
Specify the tty number to connect, if not specified a tty
number will be automatically choosen by the container.
Specify the tty number to connect to or 0 for the console. If not
specified the next available tty number will be automatically
choosen by the container.
</para>
</listitem>
</varlistentry>
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -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';
......
......@@ -188,6 +188,8 @@ struct lxc_tty_info {
struct lxc_pty_info *pty_info;
};
struct lxc_tty_state;
/*
* Defines the structure to store the console information
* @peer : the file descriptor put/get console traffic
......@@ -197,11 +199,14 @@ struct lxc_console {
int slave;
int master;
int peer;
struct lxc_pty_info peerpty;
struct lxc_epoll_descr *descr;
char *path;
char *log_path;
int log_fd;
char name[MAXPATHLEN];
struct termios *tios;
struct lxc_tty_state *tty_state;
};
/*
......
......@@ -21,6 +21,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
......@@ -29,6 +31,7 @@
#include <sys/types.h>
#include <termios.h>
#include "lxccontainer.h"
#include "log.h"
#include "conf.h"
#include "config.h"
......@@ -37,6 +40,8 @@
#include "commands.h"
#include "mainloop.h"
#include "af_unix.h"
#include "lxclock.h"
#include "utils.h"
#if HAVE_PTY_H
#include <pty.h>
......@@ -46,156 +51,490 @@
lxc_log_define(lxc_console, lxc);
extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info)
{
int i;
static struct lxc_list lxc_ttys;
for (i = 0; i < tty_info->nbtty; i++) {
if (tty_info->pty_info[i].busy != fd)
continue;
typedef void (*sighandler_t)(int);
struct lxc_tty_state
{
struct lxc_list node;
int stdinfd;
int stdoutfd;
int masterfd;
int escape;
int saw_escape;
const char *winch_proxy;
const char *winch_proxy_lxcpath;
int sigfd;
sigset_t oldmask;
};
__attribute__((constructor))
void lxc_console_init(void)
{
lxc_list_init(&lxc_ttys);
}
tty_info->pty_info[i].busy = 0;
/* lxc_console_winsz: propagte winsz from one terminal to another
*
* @srcfd : terminal to get size from (typically a slave pty)
* @dstfd : terminal to set size on (typically a master pty)
*/
static void lxc_console_winsz(int srcfd, int dstfd)
{
struct winsize wsz;
if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) {
DEBUG("set winsz dstfd:%d cols:%d rows:%d", dstfd,
wsz.ws_col, wsz.ws_row);
ioctl(dstfd, TIOCSWINSZ, &wsz);
}
}
return;
static void lxc_console_winch(struct lxc_tty_state *ts)
{
lxc_console_winsz(ts->stdinfd, ts->masterfd);
if (ts->winch_proxy) {
lxc_cmd_console_winch(ts->winch_proxy,
ts->winch_proxy_lxcpath);
}
}
static int get_default_console(char **console)
void lxc_console_sigwinch(int sig)
{
int fd;
if (process_lock() == 0) {
struct lxc_list *it;
struct lxc_tty_state *ts;
if (!access("/dev/tty", F_OK)) {
fd = open("/dev/tty", O_RDWR);
if (fd >= 0) {
close(fd);
*console = strdup("/dev/tty");
goto out;
lxc_list_for_each(it, &lxc_ttys) {
ts = it->elem;
lxc_console_winch(ts);
}
process_unlock();
}
}
if (!access("/dev/null", F_OK)) {
*console = strdup("/dev/null");
goto out;
static int lxc_console_cb_sigwinch_fd(int fd, void *cbdata,
struct lxc_epoll_descr *descr)
{
struct signalfd_siginfo siginfo;
struct lxc_tty_state *ts = cbdata;
if (read(fd, &siginfo, sizeof(siginfo)) < 0) {
ERROR("failed to read signal info");
return -1;
}
ERROR("No suitable default console");
lxc_console_winch(ts);
return 0;
}
/*
* lxc_console_sigwinch_init: install SIGWINCH handler
*
* @srcfd : src for winsz in SIGWINCH handler
* @dstfd : dst for winsz in SIGWINCH handler
*
* Returns lxc_tty_state structure on success or NULL on failure. The sigfd
* member of the returned lxc_tty_state can be select()/poll()ed/epoll()ed
* on (ie added to a mainloop) for SIGWINCH.
*
* Must be called with process_lock held to protect the lxc_ttys list, or
* from a non-threaded context.
*
* Note that SIGWINCH isn't installed as a classic asychronous handler,
* rather signalfd(2) is used so that we can handle the signal when we're
* ready for it. This avoids deadlocks since a signal handler
* (ie lxc_console_sigwinch()) would need to take the thread mutex to
* prevent lxc_ttys list corruption, but using the fd we can provide the
* tty_state needed to the callback (lxc_console_cb_sigwinch_fd()).
*/
static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
{
sigset_t mask;
struct lxc_tty_state *ts;
ts = malloc(sizeof(*ts));
if (!ts)
return NULL;
memset(ts, 0, sizeof(*ts));
ts->stdinfd = srcfd;
ts->masterfd = dstfd;
ts->sigfd = -1;
/* add tty to list to be scanned at SIGWINCH time */
lxc_list_add_elem(&ts->node, ts);
lxc_list_add_tail(&lxc_ttys, &ts->node);
sigemptyset(&mask);
sigaddset(&mask, SIGWINCH);
if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) {
SYSERROR("failed to block SIGWINCH");
goto err1;
}
ts->sigfd = signalfd(-1, &mask, 0);
if (ts->sigfd < 0) {
SYSERROR("failed to get signalfd");
goto err2;
}
DEBUG("%d got SIGWINCH fd %d", getpid(), ts->sigfd);
goto out;
err2:
sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
err1:
lxc_list_del(&ts->node);
free(ts);
ts = NULL;
out:
return *console ? 0 : -1;
return ts;
}
int lxc_create_console(struct lxc_conf *conf)
/*
* lxc_console_sigwinch_fini: uninstall SIGWINCH handler
*
* @ts : the lxc_tty_state returned by lxc_console_sigwinch_init
*
* Restore the saved signal handler that was in effect at the time
* lxc_console_sigwinch_init() was called.
*
* Must be called with process_lock held to protect the lxc_ttys list, or
* from a non-threaded context.
*/
static void lxc_console_sigwinch_fini(struct lxc_tty_state *ts)
{
struct termios tios;
struct lxc_console *console = &conf->console;
int fd;
if (ts->sigfd >= 0)
close(ts->sigfd);
lxc_list_del(&ts->node);
sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
free(ts);
}
if (!conf->rootfs.path)
return 0;
static int lxc_console_cb_con(int fd, void *data,
struct lxc_epoll_descr *descr)
{
struct lxc_console *console = (struct lxc_console *)data;
char buf[1024];
int r,w;
if (!console->path && get_default_console(&console->path)) {
ERROR("failed to get default console");
return -1;
w = r = read(fd, buf, sizeof(buf));
if (r < 0) {
SYSERROR("failed to read");
return 1;
}
if (!strcmp(console->path, "none"))
if (!r) {
INFO("console client on fd %d has exited", fd);
lxc_mainloop_del_handler(descr, fd);
close(fd);
return 0;
if (openpty(&console->master, &console->slave,
console->name, NULL, NULL)) {
SYSERROR("failed to allocate a pty");
return -1;
}
if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
SYSERROR("failed to set console master to close-on-exec");
goto err;
if (fd == console->peer)
w = write(console->master, buf, r);
if (fd == console->master) {
if (console->log_fd >= 0)
w = write(console->log_fd, buf, r);
if (console->peer >= 0)
w = write(console->peer, buf, r);
}
if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
SYSERROR("failed to set console slave to close-on-exec");
goto err;
if (w != r)
WARN("console short write r:%d w:%d", r, w);
return 0;
}
static void lxc_console_mainloop_add_peer(struct lxc_console *console)
{
if (console->peer >= 0) {
if (lxc_mainloop_add_handler(console->descr, console->peer,
lxc_console_cb_con, console))
WARN("console peer not added to mainloop");
}
if (console->log_path) {
fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600));
if (fd < 0) {
SYSERROR("failed to open '%s'", console->log_path);
goto err;
if (console->tty_state) {
if (lxc_mainloop_add_handler(console->descr,
console->tty_state->sigfd,
lxc_console_cb_sigwinch_fd,
console->tty_state)) {
WARN("failed to add to mainloop SIGWINCH handler for '%d'",
console->tty_state->sigfd);
}
DEBUG("using '%s' as console log", console->log_path);
console->log_fd = fd;
}
}
int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
struct lxc_handler *handler)
{
struct lxc_conf *conf = handler->conf;
struct lxc_console *console = &conf->console;
if (!conf->rootfs.path) {
INFO("no rootfs, no console.");
return 0;
}
if (console->master < 0) {
INFO("no console");
return 0;
}
fd = lxc_unpriv(open(console->path, O_CLOEXEC | O_RDWR | O_CREAT |
O_APPEND, 0600));
if (fd < 0) {
SYSERROR("failed to open '%s'", console->path);
goto err_close_console_log;
if (lxc_mainloop_add_handler(descr, console->master,
lxc_console_cb_con, console)) {
ERROR("failed to add to mainloop console handler for '%d'",
console->master);
return -1;
}
DEBUG("using '%s' as console", console->path);
/* we cache the descr so that we can add an fd to it when someone
* does attach to it in lxc_console_allocate()
*/
console->descr = descr;
lxc_console_mainloop_add_peer(console);
console->peer = fd;
return 0;
}
if (!isatty(console->peer))
return 0;
static int setup_tios(int fd, struct termios *oldtios)
{
struct termios newtios;
console->tios = malloc(sizeof(tios));
if (!console->tios) {
SYSERROR("failed to allocate memory");
goto err_close_console;
if (!isatty(fd)) {
ERROR("'%d' is not a tty", fd);
return -1;
}
/* Get termios */
if (tcgetattr(console->peer, console->tios)) {
/* Get current termios */
if (tcgetattr(fd, oldtios)) {
SYSERROR("failed to get current terminal settings");
goto err_free;
return -1;
}
tios = *console->tios;
newtios = *oldtios;
/* Remove the echo characters and signal reception, the echo
* will be done below with master proxying */
tios.c_iflag &= ~IGNBRK;
tios.c_iflag &= BRKINT;
tios.c_lflag &= ~(ECHO|ICANON|ISIG);
tios.c_cc[VMIN] = 1;
tios.c_cc[VTIME] = 0;
* will be done with master proxying */
newtios.c_iflag &= ~IGNBRK;
newtios.c_iflag &= BRKINT;
newtios.c_lflag &= ~(ECHO|ICANON|ISIG);
newtios.c_cc[VMIN] = 1;
newtios.c_cc[VTIME] = 0;
/* Set new attributes */
if (tcsetattr(console->peer, TCSAFLUSH, &tios)) {
if (tcsetattr(fd, TCSAFLUSH, &newtios)) {
ERROR("failed to set new terminal settings");
goto err_free;
return -1;
}
return 0;
}
err_free:
free(console->tios);
err_close_console:
close(console->peer);
static void lxc_console_peer_proxy_free(struct lxc_console *console)
{
if (console->tty_state) {
lxc_console_sigwinch_fini(console->tty_state);
console->tty_state = NULL;
}
close(console->peerpty.master);
close(console->peerpty.slave);
console->peerpty.master = -1;
console->peerpty.slave = -1;
console->peerpty.busy = -1;
console->peerpty.name[0] = '\0';
console->peer = -1;
}
err_close_console_log:
if (console->log_fd >= 0) {
close(console->log_fd);
console->log_fd = -1;
static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd)
{
struct termios oldtermio;
struct lxc_tty_state *ts;
if (console->master < 0) {
ERROR("console not set up");
return -1;
}
if (console->peerpty.busy != -1 || console->peer != -1) {
NOTICE("console already in use");
return -1;
}
if (console->tty_state) {
ERROR("console already has tty_state");
return -1;
}
err:
close(console->master);
console->master = -1;
/* this is the proxy pty that will be given to the client, and that
* the real pty master will send to / recv from
*/
if (openpty(&console->peerpty.master, &console->peerpty.slave,
console->peerpty.name, NULL, NULL)) {
SYSERROR("failed to create proxy pty");
return -1;
}
close(console->slave);
console->slave = -1;
if (setup_tios(console->peerpty.slave, &oldtermio) < 0)
goto err1;
ts = lxc_console_sigwinch_init(console->peerpty.master, console->master);
if (!ts)
goto err1;
console->tty_state = ts;
console->peer = console->peerpty.slave;
console->peerpty.busy = sockfd;
lxc_console_mainloop_add_peer(console);
DEBUG("%d %s peermaster:%d sockfd:%d", getpid(), __FUNCTION__, console->peerpty.master, sockfd);
return 0;
err1:
lxc_console_peer_proxy_free(console);
return -1;
}
void lxc_delete_console(struct lxc_console *console)
/* lxc_console_allocate: allocate the console or a tty
*
* @conf : the configuration of the container to allocate from
* @sockfd : the socket fd whose remote side when closed, will be an
* indication that the console or tty is no longer in use
* @ttyreq : the tty requested to be opened, -1 for any, 0 for the console
*/
int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
{
int masterfd = -1, ttynum;
struct lxc_tty_info *tty_info = &conf->tty_info;
struct lxc_console *console = &conf->console;
process_lock();
if (*ttyreq == 0) {
if (lxc_console_peer_proxy_alloc(console, sockfd) < 0)
goto out;
masterfd = console->peerpty.master;
goto out;
}
if (*ttyreq > 0) {
if (*ttyreq > tty_info->nbtty)
goto out;
if (tty_info->pty_info[*ttyreq - 1].busy)
goto out;
/* the requested tty is available */
ttynum = *ttyreq;
goto out_tty;
}
/* search for next available tty, fixup index tty1 => [0] */
for (ttynum = 1;
ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy;
ttynum++);
/* we didn't find any available slot for tty */
if (ttynum > tty_info->nbtty)
goto out;
*ttyreq = ttynum;
out_tty:
tty_info->pty_info[ttynum - 1].busy = sockfd;
masterfd = tty_info->pty_info[ttynum - 1].master;
out:
process_unlock();
return masterfd;
}
/* lxc_console_free: mark the console or a tty as unallocated, free any
* resources allocated by lxc_console_allocate().
*
* @conf : the configuration of the container whose tty was closed
* @fd : the socket fd whose remote side was closed, which indicated
* the console or tty is no longer in use. this is used to match
* which console/tty is being freed.
*/
void lxc_console_free(struct lxc_conf *conf, int fd)
{
if (console->tios &&
int i;
struct lxc_tty_info *tty_info = &conf->tty_info;
struct lxc_console *console = &conf->console;
process_lock();
for (i = 0; i < tty_info->nbtty; i++) {
if (tty_info->pty_info[i].busy == fd)
tty_info->pty_info[i].busy = 0;
}
if (console->peerpty.busy == fd) {
lxc_mainloop_del_handler(console->descr, console->peerpty.slave);
lxc_console_peer_proxy_free(console);
}
process_unlock();
}
static void lxc_console_peer_default(struct lxc_console *console)
{
struct lxc_tty_state *ts;
const char *path = console->path;
/* if no console was given, try current controlling terminal, there
* won't be one if we were started as a daemon (-d)
*/
if (!path && !access("/dev/tty", F_OK)) {
int fd;
fd = open("/dev/tty", O_RDWR);
if (fd >= 0) {
close(fd);
path = "/dev/tty";
}
}
if (!path)
goto out;
DEBUG("opening %s for console peer", path);
console->peer = lxc_unpriv(open(path, O_CLOEXEC | O_RDWR | O_CREAT |
O_APPEND, 0600));
if (console->peer < 0)
goto out;
DEBUG("using '%s' as console", path);
if (!isatty(console->peer))
return;
ts = lxc_console_sigwinch_init(console->peer, console->master);
if (!ts)
WARN("Unable to install SIGWINCH");
console->tty_state = ts;
lxc_console_winsz(console->peer, console->master);
console->tios = malloc(sizeof(*console->tios));
if (!console->tios) {
SYSERROR("failed to allocate memory");
goto err1;
}
if (setup_tios(console->peer, console->tios) < 0)
goto err2;
return;
err2:
free(console->tios);
console->tios = NULL;
err1:
close(console->peer);
console->peer = -1;
out:
DEBUG("no console peer");
}
void lxc_console_delete(struct lxc_console *console)
{
if (console->tios && console->peer >= 0 &&
tcsetattr(console->peer, TCSAFLUSH, console->tios))
WARN("failed to set old terminal settings");
free(console->tios);
......@@ -216,73 +555,208 @@ void lxc_delete_console(struct lxc_console *console)
console->slave = -1;
}
static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr)
int lxc_console_create(struct lxc_conf *conf)
{
struct lxc_console *console = (struct lxc_console *)data;
char buf[1024];
int r,w;
struct lxc_console *console = &conf->console;
r = read(fd, buf, sizeof(buf));
if (r < 0) {
if (!conf->rootfs.path)
return 0;
if (console->path && !strcmp(console->path, "none"))
return 0;
if (openpty(&console->master, &console->slave,
console->name, NULL, NULL)) {
SYSERROR("failed to allocate a pty");
return -1;
}
if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
SYSERROR("failed to set console master to close-on-exec");
goto err;
}
if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
SYSERROR("failed to set console slave to close-on-exec");
goto err;
}
lxc_console_peer_default(console);
if (console->log_path) {
console->log_fd = lxc_unpriv(open(console->log_path,
O_CLOEXEC | O_RDWR |
O_CREAT | O_APPEND, 0600));
if (console->log_fd < 0) {
SYSERROR("failed to open '%s'", console->log_path);
goto err;
}
DEBUG("using '%s' as console log", console->log_path);
}
return 0;
err:
lxc_console_delete(console);
return -1;
}
static int lxc_console_cb_tty_stdin(int fd, void *cbdata,
struct lxc_epoll_descr *descr)
{
struct lxc_tty_state *ts = cbdata;
char c;
assert(fd == ts->stdinfd);
if (read(ts->stdinfd, &c, 1) < 0) {
SYSERROR("failed to read");
return 1;
}
if (!r) {
INFO("console client has exited");
lxc_mainloop_del_handler(descr, fd);
close(fd);
/* we want to exit the console with Ctrl+a q */
if (c == ts->escape && !ts->saw_escape) {
ts->saw_escape = 1;
return 0;
}
/* no output for the console, do nothing */
if (console->peer == -1)
return 0;
if (c == 'q' && ts->saw_escape)
return 1;
if (console->peer == fd)
w = write(console->master, buf, r);
else {
w = write(console->peer, buf, r);
if (console->log_fd > 0)
w = write(console->log_fd, buf, r);
ts->saw_escape = 0;
if (write(ts->masterfd, &c, 1) < 0) {
SYSERROR("failed to write");
return 1;
}
if (w != r)
WARN("console short write");
return 0;
}
int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
struct lxc_handler *handler)
static int lxc_console_cb_tty_master(int fd, void *cbdata,
struct lxc_epoll_descr *descr)
{
struct lxc_conf *conf = handler->conf;
struct lxc_console *console = &conf->console;
struct lxc_tty_state *ts = cbdata;
char buf[1024];
int r,w;
if (!conf->rootfs.path) {
INFO("no rootfs, no console.");
return 0;
assert(fd == ts->masterfd);
r = read(fd, buf, sizeof(buf));
if (r < 0) {
SYSERROR("failed to read");
return 1;
}
if (!console->path) {
INFO("no console specified");
return 0;
w = write(ts->stdoutfd, buf, r);
if (w < 0 || w != r) {
SYSERROR("failed to write");
return 1;
}
if (console->peer == -1) {
INFO("no console will be used");
return 0;
return 0;
}
int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd)
{
return lxc_cmd_console(c->name, ttynum, masterfd, c->config_path);
}
int lxc_console(struct lxc_container *c, int ttynum,
int stdinfd, int stdoutfd, int stderrfd,
int escape)
{
int ret, ttyfd, masterfd;
struct lxc_epoll_descr descr;
struct termios oldtios;
struct lxc_tty_state *ts;
if (!isatty(stdinfd)) {
ERROR("stdin is not a tty");
return -1;
}
if (lxc_mainloop_add_handler(descr, console->master,
console_handler, console)) {
ERROR("failed to add to mainloop console handler for '%d'",
console->master);
ret = setup_tios(stdinfd, &oldtios);
if (ret) {
ERROR("failed to setup tios");
return -1;
}
if (console->peer != -1 &&
lxc_mainloop_add_handler(descr, console->peer,
console_handler, console))
WARN("console input disabled");
process_lock();
ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path);
if (ttyfd < 0) {
ret = ttyfd;
goto err1;
}
return 0;
fprintf(stderr, "\n"
"Connected to tty %1$d\n"
"Type <Ctrl+%2$c q> to exit the console, "
"<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n",
ttynum, 'a' + escape - 1);
ret = setsid();
if (ret)
INFO("already group leader");
ts = lxc_console_sigwinch_init(stdinfd, masterfd);
if (!ts) {
ret = -1;
goto err2;
}
ts->escape = escape;
ts->winch_proxy = c->name;
ts->winch_proxy_lxcpath = c->config_path;
lxc_console_winsz(stdinfd, masterfd);
lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
ret = lxc_mainloop_open(&descr);
if (ret) {
ERROR("failed to create mainloop");
goto err3;
}
ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
lxc_console_cb_sigwinch_fd, ts);
if (ret) {
ERROR("failed to add handler for SIGWINCH fd");
goto err4;
}
ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
lxc_console_cb_tty_stdin, ts);
if (ret) {
ERROR("failed to add handler for stdinfd");
goto err4;
}
ret = lxc_mainloop_add_handler(&descr, ts->masterfd,
lxc_console_cb_tty_master, ts);
if (ret) {
ERROR("failed to add handler for masterfd");
goto err4;
}
process_unlock();
ret = lxc_mainloop(&descr, -1);
process_lock();
if (ret) {
ERROR("mainloop returned an error");
goto err4;
}
ret = 0;
err4:
lxc_mainloop_close(&descr);
err3:
lxc_console_sigwinch_fini(ts);
err2:
close(masterfd);
close(ttyfd);
err1:
tcsetattr(stdinfd, TCSAFLUSH, &oldtios);
process_unlock();
return ret;
}
......@@ -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);
......@@ -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
......
......@@ -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;
}
......@@ -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;
......
......@@ -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);
......
......@@ -50,69 +50,6 @@
#include <sys/capability.h>
#endif
#ifdef HAVE_SYS_SIGNALFD_H
# include <sys/signalfd.h>
#else
/* assume kernel headers are too old */
#include <stdint.h>
struct signalfd_siginfo
{
uint32_t ssi_signo;
int32_t ssi_errno;
int32_t ssi_code;
uint32_t ssi_pid;
uint32_t ssi_uid;
int32_t ssi_fd;
uint32_t ssi_tid;
uint32_t ssi_band;
uint32_t ssi_overrun;
uint32_t ssi_trapno;
int32_t ssi_status;
int32_t ssi_int;
uint64_t ssi_ptr;
uint64_t ssi_utime;
uint64_t ssi_stime;
uint64_t ssi_addr;
uint8_t __pad[48];
};
# ifndef __NR_signalfd4
/* assume kernel headers are too old */
# if __i386__
# define __NR_signalfd4 327
# elif __x86_64__
# define __NR_signalfd4 289
# elif __powerpc__
# define __NR_signalfd4 313
# elif __s390x__
# define __NR_signalfd4 322
# endif
#endif
# ifndef __NR_signalfd
/* assume kernel headers are too old */
# if __i386__
# define __NR_signalfd 321
# elif __x86_64__
# define __NR_signalfd 282
# elif __powerpc__
# define __NR_signalfd 305
# elif __s390x__
# define __NR_signalfd 316
# endif
#endif
int signalfd(int fd, const sigset_t *mask, int flags)
{
int retval;
retval = syscall (__NR_signalfd4, fd, mask, _NSIG / 8, flags);
if (errno == ENOSYS && flags == 0)
retval = syscall (__NR_signalfd, fd, mask, _NSIG / 8);
return retval;
}
#endif
#if !HAVE_DECL_PR_CAPBSET_DROP
#define PR_CAPBSET_DROP 24
#endif
......@@ -198,6 +135,7 @@ static int setup_signal_fd(sigset_t *oldmask)
sigdelset(&mask, SIGILL) ||
sigdelset(&mask, SIGSEGV) ||
sigdelset(&mask, SIGBUS) ||
sigdelset(&mask, SIGWINCH) ||
sigprocmask(SIG_BLOCK, &mask, oldmask)) {
SYSERROR("failed to set signal mask");
return -1;
......@@ -387,25 +325,26 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
goto out_aborting;
}
if (lxc_create_console(conf)) {
ERROR("failed to create console");
goto out_delete_tty;
}
/* the signal fd has to be created before forking otherwise
* if the child process exits before we setup the signal fd,
* the event will be lost and the command will be stuck */
handler->sigfd = setup_signal_fd(&handler->oldmask);
if (handler->sigfd < 0) {
ERROR("failed to set sigchild fd handler");
goto out_delete_console;
goto out_delete_tty;
}
/* do this after setting up signals since it might unblock SIGWINCH */
if (lxc_console_create(conf)) {
ERROR("failed to create console");
goto out_restore_sigmask;
}
INFO("'%s' is initialized", name);
return handler;
out_delete_console:
lxc_delete_console(&conf->console);
out_restore_sigmask:
sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
out_delete_tty:
lxc_delete_tty(&conf->tty_info);
out_aborting:
......@@ -436,7 +375,7 @@ static void lxc_fini(const char *name, struct lxc_handler *handler)
if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL))
WARN("failed to restore sigprocmask");
lxc_delete_console(&handler->conf->console);
lxc_console_delete(&handler->conf->console);
lxc_delete_tty(&handler->conf->tty_info);
close(handler->conf->maincmd_fd);
handler->conf->maincmd_fd = -1;
......
......@@ -75,6 +75,71 @@ static inline int unshare(int flags)
int unshare(int);
#endif
/* Define signalfd() if missing from the C library */
#ifdef HAVE_SYS_SIGNALFD_H
# include <sys/signalfd.h>
#else
/* assume kernel headers are too old */
#include <stdint.h>
struct signalfd_siginfo
{
uint32_t ssi_signo;
int32_t ssi_errno;
int32_t ssi_code;
uint32_t ssi_pid;
uint32_t ssi_uid;
int32_t ssi_fd;
uint32_t ssi_tid;
uint32_t ssi_band;
uint32_t ssi_overrun;
uint32_t ssi_trapno;
int32_t ssi_status;
int32_t ssi_int;
uint64_t ssi_ptr;
uint64_t ssi_utime;
uint64_t ssi_stime;
uint64_t ssi_addr;
uint8_t __pad[48];
};
# ifndef __NR_signalfd4
/* assume kernel headers are too old */
# if __i386__
# define __NR_signalfd4 327
# elif __x86_64__
# define __NR_signalfd4 289
# elif __powerpc__
# define __NR_signalfd4 313
# elif __s390x__
# define __NR_signalfd4 322
# endif
#endif
# ifndef __NR_signalfd
/* assume kernel headers are too old */
# if __i386__
# define __NR_signalfd 321
# elif __x86_64__
# define __NR_signalfd 282
# elif __powerpc__
# define __NR_signalfd 305
# elif __s390x__
# define __NR_signalfd 316
# endif
#endif
static inline int signalfd(int fd, const sigset_t *mask, int flags)
{
int retval;
retval = syscall (__NR_signalfd4, fd, mask, _NSIG / 8, flags);
if (errno == ENOSYS && flags == 0)
retval = syscall (__NR_signalfd, fd, mask, _NSIG / 8);
return retval;
}
#endif
/**
* BUILD_BUG_ON - break compile if a condition is true.
* @condition: the condition which the compiler should know is false.
......
#!/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)
#!/usr/bin/python3
#
# Example program showing use of console functions in the lxc python binding
#
import warnings
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
import lxc
import sys
import time
if __name__ == '__main__':
ttynum = -1
escape = 1
if len(sys.argv) < 2:
sys.exit("Usage: %s container-name [ttynum [escape]]" % sys.argv[0])
if len(sys.argv) > 2:
ttynum = int(sys.argv[2])
if len(sys.argv) > 3:
escape = ord(sys.argv[3]) - ord('a') + 1
ct = lxc.Container(sys.argv[1])
print("Container:%s tty:%d Ctrl-%c q to quit" % (ct.name, ttynum, ord('a') + escape-1))
time.sleep(1)
if not ct.defined:
sys.exit("Container %s not defined" % ct.name)
if not ct.running:
sys.exit("Container %s not running" % ct.name)
ct.console(ttynum, 0, 1, 2, escape)
print("Console done")
......@@ -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}
};
......
......@@ -291,19 +291,25 @@ class Container(_lxc.Container):
self.load_config()
return True
def console(self, tty="1"):
def console(self, ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, escape = 1):
"""
Access the console of a container.
Attach to console of running container.
"""
if not self.running:
return False
if subprocess.call(["lxc-console", "-n", self.name, "-t", "%s" % tty,
"-P", self.get_config_path()],
universal_newlines=True) != 0:
return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd, stderrfd, escape)
def console_getfd(self, ttynum = -1):
"""
Attach to console of running container.
"""
if not self.running:
return False
return True
return _lxc.Container.console_getfd(self, ttynum)
def get_cgroup_item(self, key):
"""
......
......@@ -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;
......
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