Skip to content

Commit

Permalink
simplify pty allocation and implement pty logging
Browse files Browse the repository at this point in the history
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: Christian Brauner <christian.brauner@mailbox.org>
  • Loading branch information
Christian Brauner authored and stgraber committed Apr 18, 2016
1 parent e73bcbb commit d3e3b18
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 67 deletions.
12 changes: 7 additions & 5 deletions src/lxc/console.c
Expand Up @@ -27,10 +27,10 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>

#include <lxc/lxccontainer.h>

Expand Down Expand Up @@ -163,6 +163,9 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data,
char buf[1024];
int r,w;

if (events & EPOLLHUP)
return 1;

w = r = read(fd, buf, sizeof(buf));
if (r < 0) {
SYSERROR("failed to read");
Expand Down Expand Up @@ -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,
struct lxc_handler *handler)
extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
struct lxc_conf *conf)
{
struct lxc_conf *conf = handler->conf;
struct lxc_console *console = &conf->console;

if (conf->is_execute) {
Expand Down
2 changes: 1 addition & 1 deletion src/lxc/console.h
Expand Up @@ -102,7 +102,7 @@ extern void lxc_console_free(struct lxc_conf *conf, int fd);
/*
* 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.
Expand Down
71 changes: 11 additions & 60 deletions src/lxc/lxc_attach.c
Expand Up @@ -64,6 +64,7 @@ static const struct option my_longopts[] = {
{"keep-env", no_argument, 0, 501},
{"keep-var", required_argument, 0, 502},
{"set-var", required_argument, 0, 'v'},
{"pty-log", required_argument, 0, 'L'},
LXC_COMMON_OPTIONS
};

Expand Down Expand Up @@ -149,6 +150,9 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
return -1;
}
break;
case 'L':
args->console_log = arg;
break;
}

return 0;
Expand Down Expand Up @@ -191,6 +195,8 @@ Options :\n\
--keep-env Keep all current environment variables. This\n\
is the current default behaviour, but is likely to\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\
attached program in the container. May be specified\n\
multiple times.\n\
Expand Down Expand Up @@ -254,7 +260,10 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *

conf = c->lxc_conf;
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
* controlling terminal. We clear whatever was set by the user for
Expand All @@ -272,39 +281,6 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
if (lxc_console_create(conf) < 0)
return -1;
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;

/* Shift ttys to container. */
Expand All @@ -325,33 +301,8 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *
goto err2;
}

/* Register sigwinch handler in mainloop. When ts->sigfd == -1 it means
* 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");
if (lxc_console_mainloop_add(&descr, conf) < 0)
goto err3;
}

ret = lxc_mainloop(&descr, -1);
if (ret) {
Expand Down
2 changes: 1 addition & 1 deletion src/lxc/start.c
Expand Up @@ -365,7 +365,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
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");
goto out_mainloop_open;
}
Expand Down

0 comments on commit d3e3b18

Please sign in to comment.