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 ...@@ -78,6 +78,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
</para> </para>
<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 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;. and quit lxc-console. The default escape sequence is &lt;Ctrl+a q&gt;.
</para> </para>
...@@ -107,8 +113,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ...@@ -107,8 +113,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
</term> </term>
<listitem> <listitem>
<para> <para>
Specify the tty number to connect, if not specified a tty Specify the tty number to connect to or 0 for the console. If not
number will be automatically choosen by the container. specified the next available tty number will be automatically
choosen by the container.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <lxc/utils.h> #include <lxc/utils.h>
#include "commands.h" #include "commands.h"
#include "console.h"
#include "confile.h" #include "confile.h"
#include "mainloop.h" #include "mainloop.h"
#include "af_unix.h" #include "af_unix.h"
...@@ -546,6 +547,37 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, ...@@ -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 * lxc_cmd_console: Open an fd to a tty in the container
* *
* @name : name of container to connect to * @name : name of container to connect to
...@@ -599,39 +631,21 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, ...@@ -599,39 +631,21 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler) struct lxc_handler *handler)
{ {
int ttynum = PTR_TO_INT(req->data); int ttynum = PTR_TO_INT(req->data);
struct lxc_tty_info *tty_info = &handler->conf->tty_info; int masterfd;
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
if (ttynum > 0) { masterfd = lxc_console_allocate(handler->conf, fd, &ttynum);
if (ttynum > tty_info->nbtty) if (masterfd < 0)
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)
goto out_close; goto out_close;
out_send:
memset(&rsp, 0, sizeof(rsp)); memset(&rsp, 0, sizeof(rsp));
rsp.data = INT_TO_PTR(ttynum); rsp.data = INT_TO_PTR(ttynum);
if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master, if (lxc_af_unix_send_fd(fd, masterfd, &rsp, sizeof(rsp)) < 0) {
&rsp, sizeof(rsp)) < 0) {
ERROR("failed to send tty to client"); ERROR("failed to send tty to client");
lxc_console_free(handler->conf, fd);
goto out_close; goto out_close;
} }
tty_info->pty_info[ttynum - 1].busy = fd;
return 0; return 0;
out_close: out_close:
...@@ -650,6 +664,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, ...@@ -650,6 +664,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
callback cb[LXC_CMD_MAX] = { callback cb[LXC_CMD_MAX] = {
[LXC_CMD_CONSOLE] = lxc_cmd_console_callback, [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_STOP] = lxc_cmd_stop_callback,
[LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback,
[LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_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, ...@@ -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, static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
extern void lxc_console_remove_fd(int, struct lxc_tty_info *); lxc_console_free(handler->conf, fd);
lxc_console_remove_fd(fd, &handler->conf->tty_info);
lxc_mainloop_del_handler(descr, fd); lxc_mainloop_del_handler(descr, fd);
close(fd); close(fd);
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
typedef enum { typedef enum {
LXC_CMD_CONSOLE, LXC_CMD_CONSOLE,
LXC_CMD_CONSOLE_WINCH,
LXC_CMD_STOP, LXC_CMD_STOP,
LXC_CMD_GET_STATE, LXC_CMD_GET_STATE,
LXC_CMD_GET_INIT_PID, LXC_CMD_GET_INIT_PID,
...@@ -65,6 +66,7 @@ struct lxc_cmd_console_rsp_data { ...@@ -65,6 +66,7 @@ struct lxc_cmd_console_rsp_data {
int ttynum; 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, extern int lxc_cmd_console(const char *name, int *ttynum, int *fd,
const char *lxcpath); const char *lxcpath);
extern char *lxc_cmd_get_cgroup_path(const char *name, 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, ...@@ -1293,8 +1293,8 @@ static int setup_dev_console(const struct lxc_rootfs *rootfs,
return 0; return 0;
} }
if (console->peer == -1) { if (console->master < 0) {
INFO("no console output required"); INFO("no console");
return 0; return 0;
} }
...@@ -1359,8 +1359,8 @@ static int setup_ttydir_console(const struct lxc_rootfs *rootfs, ...@@ -1359,8 +1359,8 @@ static int setup_ttydir_console(const struct lxc_rootfs *rootfs,
if (ret >= 0) if (ret >= 0)
close(ret); close(ret);
if (console->peer == -1) { if (console->master < 0) {
INFO("no console output required"); INFO("no console");
return 0; return 0;
} }
...@@ -2127,6 +2127,9 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -2127,6 +2127,9 @@ struct lxc_conf *lxc_conf_init(void)
new->console.log_fd = -1; new->console.log_fd = -1;
new->console.path = NULL; new->console.path = NULL;
new->console.peer = -1; 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.master = -1;
new->console.slave = -1; new->console.slave = -1;
new->console.name[0] = '\0'; new->console.name[0] = '\0';
......
...@@ -188,6 +188,8 @@ struct lxc_tty_info { ...@@ -188,6 +188,8 @@ struct lxc_tty_info {
struct lxc_pty_info *pty_info; struct lxc_pty_info *pty_info;
}; };
struct lxc_tty_state;
/* /*
* Defines the structure to store the console information * Defines the structure to store the console information
* @peer : the file descriptor put/get console traffic * @peer : the file descriptor put/get console traffic
...@@ -197,11 +199,14 @@ struct lxc_console { ...@@ -197,11 +199,14 @@ struct lxc_console {
int slave; int slave;
int master; int master;
int peer; int peer;
struct lxc_pty_info peerpty;
struct lxc_epoll_descr *descr;
char *path; char *path;
char *log_path; char *log_path;
int log_fd; int log_fd;
char name[MAXPATHLEN]; char name[MAXPATHLEN];
struct termios *tios; struct termios *tios;
struct lxc_tty_state *tty_state;
}; };
/* /*
......
...@@ -21,6 +21,18 @@ ...@@ -21,6 +21,18 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
extern int lxc_create_console(struct lxc_conf *); struct lxc_epoll_descr;
extern void lxc_delete_console(struct lxc_console *); struct lxc_container;
extern int lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_handler *);
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 ...@@ -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); 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> * Freeze all the tasks running inside the container <name>
* @name : the container name * @name : the container name
* Returns 0 on success, < 0 otherwise * Returns 0 on success, < 0 otherwise
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "../lxc/lxccontainer.h"
#include "error.h" #include "error.h"
#include "lxc.h" #include "lxc.h"
#include "log.h" #include "log.h"
...@@ -87,184 +88,34 @@ Options :\n\ ...@@ -87,184 +88,34 @@ Options :\n\
.escape = 1, .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 main(int argc, char *argv[])
{ {
int err, ttyfd, std_in = 1; int ret;
struct lxc_epoll_descr descr; struct lxc_container *c;
struct termios newtios, oldtios;
err = lxc_arguments_parse(&my_args, argc, argv); ret = lxc_arguments_parse(&my_args, argc, argv);
if (err) if (ret)
return -1; 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]); my_args.progname, my_args.quiet, my_args.lxcpath[0]);
if (err) if (ret)
return -1; return EXIT_FAILURE;
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;
}
err = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &master); c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
if (err) { if (!c) {
ERROR("failed to add handler for the stdin"); fprintf(stderr, "System error loading container\n");
goto out_mainloop_open; exit(EXIT_FAILURE);
} }
err = lxc_mainloop_add_handler(&descr, master, master_handler, &std_in); if (!c->is_running(c)) {
if (err) { fprintf(stderr, "%s is not running\n", my_args.name);
ERROR("failed to add handler for the master"); exit(EXIT_FAILURE);
goto out_mainloop_open;
} }
err = lxc_mainloop(&descr, -1); ret = c->console(c, my_args.ttynum, 0, 1, 2, my_args.escape);
if (err) { if (ret < 0) {
ERROR("mainloop returned an error"); exit(EXIT_FAILURE);
goto out_mainloop_open;
} }
return EXIT_SUCCESS;
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;
} }
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "lxccontainer.h" #include "lxccontainer.h"
#include "conf.h" #include "conf.h"
#include "confile.h" #include "confile.h"
#include "console.h"
#include "cgroup.h" #include "cgroup.h"
#include "commands.h" #include "commands.h"
#include "version.h" #include "version.h"
...@@ -350,16 +351,22 @@ static bool lxcapi_unfreeze(struct lxc_container *c) ...@@ -350,16 +351,22 @@ static bool lxcapi_unfreeze(struct lxc_container *c)
return true; 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; int ttyfd;
if (!c) if (!c)
return -1; return -1;
ttyfd = lxc_cmd_console(c->name, ttynum, masterfd, c->config_path); ttyfd = lxc_console_getfd(c, ttynum, masterfd);
return ttyfd; 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) static pid_t lxcapi_init_pid(struct lxc_container *c)
{ {
if (!c) if (!c)
...@@ -2018,6 +2025,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath ...@@ -2018,6 +2025,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
c->freeze = lxcapi_freeze; c->freeze = lxcapi_freeze;
c->unfreeze = lxcapi_unfreeze; c->unfreeze = lxcapi_unfreeze;
c->console = lxcapi_console; c->console = lxcapi_console;
c->console_getfd = lxcapi_console_getfd;
c->init_pid = lxcapi_init_pid; c->init_pid = lxcapi_init_pid;
c->load_config = lxcapi_load_config; c->load_config = lxcapi_load_config;
c->want_daemonize = lxcapi_want_daemonize; c->want_daemonize = lxcapi_want_daemonize;
......
...@@ -115,7 +115,7 @@ struct lxc_container { ...@@ -115,7 +115,7 @@ struct lxc_container {
const char *lxcpath, int flags, const char *bdevtype, const char *lxcpath, int flags, const char *bdevtype,
const char *bdevdata, unsigned long newsize, char **hookargs); 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 * @c : the running container
* @ttynum : in : tty number to attempt to allocate or -1 to * @ttynum : in : tty number to attempt to allocate or -1 to
...@@ -128,7 +128,24 @@ struct lxc_container { ...@@ -128,7 +128,24 @@ struct lxc_container {
* indicate that it is done with the allocated console so that it can * indicate that it is done with the allocated console so that it can
* be allocated by another caller. * 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 #if 0
bool (*commit_cgroups)(struct lxc_container *c); bool (*commit_cgroups)(struct lxc_container *c);
......
...@@ -50,69 +50,6 @@ ...@@ -50,69 +50,6 @@
#include <sys/capability.h> #include <sys/capability.h>
#endif #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 #if !HAVE_DECL_PR_CAPBSET_DROP
#define PR_CAPBSET_DROP 24 #define PR_CAPBSET_DROP 24
#endif #endif
...@@ -198,6 +135,7 @@ static int setup_signal_fd(sigset_t *oldmask) ...@@ -198,6 +135,7 @@ static int setup_signal_fd(sigset_t *oldmask)
sigdelset(&mask, SIGILL) || sigdelset(&mask, SIGILL) ||
sigdelset(&mask, SIGSEGV) || sigdelset(&mask, SIGSEGV) ||
sigdelset(&mask, SIGBUS) || sigdelset(&mask, SIGBUS) ||
sigdelset(&mask, SIGWINCH) ||
sigprocmask(SIG_BLOCK, &mask, oldmask)) { sigprocmask(SIG_BLOCK, &mask, oldmask)) {
SYSERROR("failed to set signal mask"); SYSERROR("failed to set signal mask");
return -1; return -1;
...@@ -387,25 +325,26 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char ...@@ -387,25 +325,26 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
goto out_aborting; 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 /* the signal fd has to be created before forking otherwise
* if the child process exits before we setup the signal fd, * if the child process exits before we setup the signal fd,
* the event will be lost and the command will be stuck */ * the event will be lost and the command will be stuck */
handler->sigfd = setup_signal_fd(&handler->oldmask); handler->sigfd = setup_signal_fd(&handler->oldmask);
if (handler->sigfd < 0) { if (handler->sigfd < 0) {
ERROR("failed to set sigchild fd handler"); 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); INFO("'%s' is initialized", name);
return handler; return handler;
out_delete_console: out_restore_sigmask:
lxc_delete_console(&conf->console); sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
out_delete_tty: out_delete_tty:
lxc_delete_tty(&conf->tty_info); lxc_delete_tty(&conf->tty_info);
out_aborting: out_aborting:
...@@ -436,7 +375,7 @@ static void lxc_fini(const char *name, struct lxc_handler *handler) ...@@ -436,7 +375,7 @@ static void lxc_fini(const char *name, struct lxc_handler *handler)
if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL)) if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL))
WARN("failed to restore sigprocmask"); WARN("failed to restore sigprocmask");
lxc_delete_console(&handler->conf->console); lxc_console_delete(&handler->conf->console);
lxc_delete_tty(&handler->conf->tty_info); lxc_delete_tty(&handler->conf->tty_info);
close(handler->conf->maincmd_fd); close(handler->conf->maincmd_fd);
handler->conf->maincmd_fd = -1; handler->conf->maincmd_fd = -1;
......
...@@ -75,6 +75,71 @@ static inline int unshare(int flags) ...@@ -75,6 +75,71 @@ static inline int unshare(int flags)
int unshare(int); int unshare(int);
#endif #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. * BUILD_BUG_ON - break compile if a condition is true.
* @condition: the condition which the compiler should know is false. * @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) ...@@ -638,6 +638,40 @@ Container_unfreeze(Container *self, PyObject *args, PyObject *kwds)
} }
static PyObject * 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) Container_wait(Container *self, PyObject *args, PyObject *kwds)
{ {
static char *kwlist[] = {"state", "timeout", NULL}; static char *kwlist[] = {"state", "timeout", NULL};
...@@ -804,6 +838,18 @@ static PyMethodDef Container_methods[] = { ...@@ -804,6 +838,18 @@ static PyMethodDef Container_methods[] = {
"\n" "\n"
"Wait for the container to reach a given state or timeout." "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} {NULL, NULL, 0, NULL}
}; };
......
...@@ -291,19 +291,25 @@ class Container(_lxc.Container): ...@@ -291,19 +291,25 @@ class Container(_lxc.Container):
self.load_config() self.load_config()
return True 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: if not self.running:
return False return False
if subprocess.call(["lxc-console", "-n", self.name, "-t", "%s" % tty, return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd, stderrfd, escape)
"-P", self.get_config_path()],
universal_newlines=True) != 0: def console_getfd(self, ttynum = -1):
"""
Attach to console of running container.
"""
if not self.running:
return False return False
return True
return _lxc.Container.console_getfd(self, ttynum)
def get_cgroup_item(self, key): def get_cgroup_item(self, key):
""" """
......
...@@ -61,7 +61,7 @@ static int test_console_running_container(struct lxc_container *c) ...@@ -61,7 +61,7 @@ static int test_console_running_container(struct lxc_container *c)
ttynum[i] = ttyfd[i] = masterfd[i] = -1; ttynum[i] = ttyfd[i] = masterfd[i] = -1;
ttynum[0] = 1; ttynum[0] = 1;
ret = c->console(c, &ttynum[0], &masterfd[0]); ret = c->console_getfd(c, &ttynum[0], &masterfd[0]);
if (ret < 0) { if (ret < 0) {
TSTERR("console allocate failed"); TSTERR("console allocate failed");
goto err1; goto err1;
...@@ -73,7 +73,7 @@ static int test_console_running_container(struct lxc_container *c) ...@@ -73,7 +73,7 @@ static int test_console_running_container(struct lxc_container *c)
} }
/* attempt to alloc same ttynum */ /* 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) { if (ret != -1) {
TSTERR("console allocate should fail for allocated ttynum %d", ttynum[0]); TSTERR("console allocate should fail for allocated ttynum %d", ttynum[0]);
goto err2; goto err2;
...@@ -86,7 +86,7 @@ static int test_console_running_container(struct lxc_container *c) ...@@ -86,7 +86,7 @@ static int test_console_running_container(struct lxc_container *c)
*/ */
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
for (nrconsoles = 0; nrconsoles < MAXCONSOLES; nrconsoles++) { 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) if (ret < 0)
break; break;
ttyfd[nrconsoles] = ret; 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