Commit d3e3b188 by Christian Brauner Committed by Stéphane Graber

simplify pty allocation and implement pty logging

lxc-attach allocated a pty in a manner that relied on ts->stdinfd and ts->stdoutfd to be set. We had to resort to a trick to get it working when output is redirected. The new implementation gets rid of the black magic and also simplifies the code. This commit also implements pty logging for lxc-attach. Users will now be able to log commands and corresponding output to a file given that lxc-attach allocates a pty. Signed-off-by: 's avatarChristian Brauner <christian.brauner@mailbox.org>
parent e73bcbbe
...@@ -27,10 +27,10 @@ ...@@ -27,10 +27,10 @@
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <lxc/lxccontainer.h> #include <lxc/lxccontainer.h>
...@@ -163,6 +163,9 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data, ...@@ -163,6 +163,9 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data,
char buf[1024]; char buf[1024];
int r,w; int r,w;
if (events & EPOLLHUP)
return 1;
w = r = read(fd, buf, sizeof(buf)); w = r = read(fd, buf, sizeof(buf));
if (r < 0) { if (r < 0) {
SYSERROR("failed to read"); SYSERROR("failed to read");
...@@ -212,10 +215,9 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console) ...@@ -212,10 +215,9 @@ static void lxc_console_mainloop_add_peer(struct lxc_console *console)
} }
} }
int lxc_console_mainloop_add(struct lxc_epoll_descr *descr, extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
struct lxc_handler *handler) struct lxc_conf *conf)
{ {
struct lxc_conf *conf = handler->conf;
struct lxc_console *console = &conf->console; struct lxc_console *console = &conf->console;
if (conf->is_execute) { if (conf->is_execute) {
......
...@@ -102,7 +102,7 @@ extern void lxc_console_free(struct lxc_conf *conf, int fd); ...@@ -102,7 +102,7 @@ extern void lxc_console_free(struct lxc_conf *conf, int fd);
/* /*
* Register pty event handlers in an open mainloop * Register pty event handlers in an open mainloop
*/ */
extern int lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_handler *); extern int lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_conf *);
/* /*
* Handle SIGWINCH events on the allocated ptys. * Handle SIGWINCH events on the allocated ptys.
......
...@@ -64,6 +64,7 @@ static const struct option my_longopts[] = { ...@@ -64,6 +64,7 @@ static const struct option my_longopts[] = {
{"keep-env", no_argument, 0, 501}, {"keep-env", no_argument, 0, 501},
{"keep-var", required_argument, 0, 502}, {"keep-var", required_argument, 0, 502},
{"set-var", required_argument, 0, 'v'}, {"set-var", required_argument, 0, 'v'},
{"pty-log", required_argument, 0, 'L'},
LXC_COMMON_OPTIONS LXC_COMMON_OPTIONS
}; };
...@@ -149,6 +150,9 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg) ...@@ -149,6 +150,9 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
return -1; return -1;
} }
break; break;
case 'L':
args->console_log = arg;
break;
} }
return 0; return 0;
...@@ -191,6 +195,8 @@ Options :\n\ ...@@ -191,6 +195,8 @@ Options :\n\
--keep-env Keep all current environment variables. This\n\ --keep-env Keep all current environment variables. This\n\
is the current default behaviour, but is likely to\n\ is the current default behaviour, but is likely to\n\
change in the future.\n\ change in the future.\n\
-L, --pty-log=FILE\n\
Log pty output to FILE\n\
-v, --set-var Set an additional variable that is seen by the\n\ -v, --set-var Set an additional variable that is seen by the\n\
attached program in the container. May be specified\n\ attached program in the container. May be specified\n\
multiple times.\n\ multiple times.\n\
...@@ -254,7 +260,10 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int * ...@@ -254,7 +260,10 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
conf = c->lxc_conf; conf = c->lxc_conf;
free(conf->console.log_path); free(conf->console.log_path);
conf->console.log_path = NULL; if (my_args.console_log)
conf->console.log_path = strdup(my_args.console_log);
else
conf->console.log_path = NULL;
/* In the case of lxc-attach our peer pty will always be the current /* In the case of lxc-attach our peer pty will always be the current
* controlling terminal. We clear whatever was set by the user for * controlling terminal. We clear whatever was set by the user for
...@@ -272,39 +281,6 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int * ...@@ -272,39 +281,6 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
if (lxc_console_create(conf) < 0) if (lxc_console_create(conf) < 0)
return -1; return -1;
ts = conf->console.tty_state; ts = conf->console.tty_state;
/*
* We need to make sure that the ouput that is produced inside the
* container is received on the host. Suppose we want to run
*
* lxc-attach -n a -- /bin/sh -c 'hostname >&2' > /dev/null
*
* This command produces output on stderr inside the container. On the
* host we close stdout by redirecting it to /dev/null. But stderr is,
* as expected, still connected to a tty. We receive the output produced
* on stderr in the container on stderr on the host.
*
* For the command
*
* lxc-attach -n a -- /bin/sh -c 'hostname >&2' 2> /dev/null
*
* the logic is analogous but because we now have closed stderr on the
* host no output will be received.
*
* Finally, imagine a more complicated case
*
* lxc-attach -n a -- /bin/sh -c 'echo OUT; echo ERR >&2' > /tmp/out 2> /tmp/err
*
* Here, we produce output in the container on stdout and stderr. On the
* host we redirect stdout and stderr to files. Because of that stdout
* and stderr are not dup2()ed. Thus, they are not connected to a pty
* and output on stdout and stderr is redirected to the corresponding
* files as expected.
*/
if (!isatty(STDOUT_FILENO) && isatty(STDERR_FILENO))
ts->stdoutfd = STDERR_FILENO;
else
ts->stdoutfd = STDOUT_FILENO;
conf->console.descr = &descr; conf->console.descr = &descr;
/* Shift ttys to container. */ /* Shift ttys to container. */
...@@ -325,33 +301,8 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int * ...@@ -325,33 +301,8 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
goto err2; goto err2;
} }
/* Register sigwinch handler in mainloop. When ts->sigfd == -1 it means if (lxc_console_mainloop_add(&descr, conf) < 0)
* we weren't able to install a sigwinch handler in
* lxc_console_create(). We don't consider this fatal and just move on.
*/
if (ts->sigfd != -1) {
ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
lxc_console_cb_sigwinch_fd, ts);
if (ret) {
ERROR("failed to add handler for SIGWINCH fd");
goto err3;
}
}
/* Register i/o callbacks in mainloop. */
ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
lxc_console_cb_tty_stdin, ts);
if (ret) {
ERROR("failed to add handler for stdinfd");
goto err3;
}
ret = lxc_mainloop_add_handler(&descr, ts->masterfd,
lxc_console_cb_tty_master, ts);
if (ret) {
ERROR("failed to add handler for masterfd");
goto err3; goto err3;
}
ret = lxc_mainloop(&descr, -1); ret = lxc_mainloop(&descr, -1);
if (ret) { if (ret) {
......
...@@ -365,7 +365,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler) ...@@ -365,7 +365,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
goto out_mainloop_open; goto out_mainloop_open;
} }
if (lxc_console_mainloop_add(&descr, handler)) { if (lxc_console_mainloop_add(&descr, handler->conf)) {
ERROR("failed to add console handler to mainloop"); ERROR("failed to add console handler to mainloop");
goto out_mainloop_open; goto out_mainloop_open;
} }
......
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