The actual behaviour of the console is messy as: * it relies on a heuristic (tty or not, rootfs or not, etc ...) * the container init stole the tty and we lose the control
The following patch: * allocates a tty * maps this tty to the container console * proxy the io from the console to the file specified in the configuration lxc.console=<file> That allows to specify a file, a fifo, a $(tty), and can be extended with an uri like file://mypath, net://1.2.3.4:1234, etc ... That solves the problem with the heuristic and the container does no longer stole our current tty. Note by default, the console output will go to a blackhole if no configuration is specified making the container showing nothing. In order to access the console from the tty, use lxc-start -n foo -s lxc.console=$(tty) I propose the make the container to daemonize by default now. I tried the following: in a shell: tail --retry -f /var/lib/lxc/foo/console in another shell: lxc-start -n foo -s lxc.console=$(tty) Signed-off-by: Daniel Lezcano <dlezc...@fr.ibm.com> --- src/lxc/conf.c | 56 +++++++++++++++++++----------------- src/lxc/conf.h | 30 ++++++++++++++++--- src/lxc/confile.c | 20 +++++++++++++ src/lxc/console.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/lxc/console.h | 26 ++++++++++++++++ src/lxc/lxc_start.c | 45 ----------------------------- src/lxc/start.c | 78 ++++++++++++-------------------------------------- 7 files changed, 200 insertions(+), 135 deletions(-) Index: lxc/src/lxc/conf.c =================================================================== --- lxc.orig/src/lxc/conf.c +++ lxc/src/lxc/conf.c @@ -641,41 +641,42 @@ out: return 0; } -static int setup_console(const char *rootfs, const char *tty) +static int setup_console(const char *rootfs, const struct lxc_console *console) { - char console[MAXPATHLEN]; + char path[MAXPATHLEN]; + struct stat s; - snprintf(console, sizeof(console), "%s/dev/console", - rootfs ? rootfs : ""); - - /* we have the rootfs with /dev/console but no tty - * to be used as console, let's remap /dev/console - * to /dev/null to avoid to log to the system console - */ - if (rootfs && !tty[0]) { + /* We don't have a rootfs, /dev/console will be shared */ + if (!rootfs) + return 0; - if (!access(console, F_OK)) { + snprintf(path, sizeof(path), "%s/dev/console", rootfs); - if (mount("/dev/null", console, "none", MS_BIND, 0)) { - SYSERROR("failed to mount '/dev/null'->'%s'", - console); - return -1; - } - } + if (access(path, F_OK)) { + WARN("rootfs specified but no console found"); + return 0; } - if (!tty[0]) - return 0; + if (console->peer == -1) + INFO("no console output required"); - if (access(console, R_OK|W_OK)) - return 0; + if (stat(path, &s)) { + SYSERROR("failed to stat '%s'", path); + return -1; + } + + if (chmod(console->name, s.st_mode)) { + SYSERROR("failed to set mode '0%o' to '%s'", + s.st_mode, console->name); + return -1; + } - if (mount(tty, console, "none", MS_BIND, 0)) { - ERROR("failed to mount the console"); + if (mount(console->name, path, "none", MS_BIND, 0)) { + ERROR("failed to mount '%s' on '%s'", console->name, path); return -1; } - INFO("console '%s' mounted to '%s'", tty, console); + INFO("console has been setup"); return 0; } @@ -1073,7 +1074,10 @@ struct lxc_conf *lxc_conf_init(void) new->utsname = NULL; new->tty = 0; new->pts = 0; - new->console[0] = '\0'; + new->console.peer = -1; + new->console.master = -1; + new->console.slave = -1; + new->console.name[0] = '\0'; lxc_list_init(&new->cgroup); lxc_list_init(&new->network); lxc_list_init(&new->mount_list); @@ -1361,7 +1365,7 @@ int lxc_setup(const char *name, struct l return -1; } - if (setup_console(lxc_conf->rootfs, lxc_conf->console)) { + if (setup_console(lxc_conf->rootfs, &lxc_conf->console)) { ERROR("failed to setup the console for '%s'", name); return -1; } Index: lxc/src/lxc/conf.h =================================================================== --- lxc.orig/src/lxc/conf.h +++ lxc/src/lxc/conf.h @@ -149,11 +149,31 @@ struct lxc_tty_info { }; /* + * Defines the structure to store the console information + * @peer : the file descriptor put/get console traffic + * @name : the file name of the slave pty + */ +struct lxc_console { + int slave; + int master; + int peer; + char name[MAXPATHLEN]; +}; + +/* * Defines the global container configuration - * @rootfs : the root directory to run the container - * @mount : the list of mount points - * @network : the network configuration - * @utsname : the container utsname + * @rootfs : root directory to run the container + * @pivotdir : pivotdir path, if not set default will be used + * @mount : list of mount points + * @tty : numbers of tty + * @pts : new pts instance + * @mount_list : list of mount point (alternative to fstab file) + * @network : network configuration + * @utsname : container utsname + * @fstab : path to a fstab file format + * @caps : list of the capabilities + * @tty_info : tty data + * @console : console data */ struct lxc_conf { char *rootfs; @@ -167,7 +187,7 @@ struct lxc_conf { struct lxc_list mount_list; struct lxc_list caps; struct lxc_tty_info tty_info; - char console[MAXPATHLEN]; + struct lxc_console console; }; /* Index: lxc/src/lxc/confile.c =================================================================== --- lxc.orig/src/lxc/confile.c +++ lxc/src/lxc/confile.c @@ -25,6 +25,9 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <fcntl.h> +#include <pty.h> +#include <sys/stat.h> #include <sys/types.h> #include <sys/param.h> #include <sys/utsname.h> @@ -59,6 +62,7 @@ static int config_network_mtu(const char static int config_network_ipv4(const char *, char *, struct lxc_conf *); static int config_network_ipv6(const char *, char *, struct lxc_conf *); static int config_cap_drop(const char *, char *, struct lxc_conf *); +static int config_console(const char *, char *, struct lxc_conf *); typedef int (*config_cb)(const char *, char *, struct lxc_conf *); @@ -88,6 +92,7 @@ static struct config config[] = { { "lxc.network.ipv4", config_network_ipv4 }, { "lxc.network.ipv6", config_network_ipv6 }, { "lxc.cap.drop", config_cap_drop }, + { "lxc.console", config_console }, }; static const size_t config_size = sizeof(config)/sizeof(struct config); @@ -615,6 +620,21 @@ static int config_cap_drop(const char *k return ret; } +static int config_console(const char *key, char *value, struct lxc_conf *lxc_conf) +{ + int fd; + + fd = open(value, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600); + if (fd < 0) { + SYSERROR("failed to open '%s'", value); + return -1; + } + + lxc_conf->console.peer = fd; + + return 0; +} + static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf) { if (strlen(value) >= MAXPATHLEN) { Index: lxc/src/lxc/console.c =================================================================== --- lxc.orig/src/lxc/console.c +++ lxc/src/lxc/console.c @@ -23,7 +23,9 @@ #include <stdio.h> #include <unistd.h> +#include <fcntl.h> #include <errno.h> +#include <pty.h> #include <sys/types.h> #include <sys/un.h> @@ -32,6 +34,7 @@ #include <lxc/start.h> /* for struct lxc_handler */ #include "commands.h" +#include "mainloop.h" #include "af_unix.h" lxc_log_define(lxc_console, lxc); @@ -95,7 +98,7 @@ extern void lxc_console_remove_fd(int fd } extern int lxc_console_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) + struct lxc_handler *handler) { int ttynum = request->data; struct lxc_tty_info *tty_info = &handler->conf->tty_info; @@ -135,3 +138,78 @@ out_close: return 1; } +int lxc_create_console(struct lxc_console *console) +{ + 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; + } + + return 0; +err: + close(console->master); + close(console->slave); + return -1; +} + +void lxc_delete_console(const struct lxc_console *console) +{ + close(console->master); + close(console->slave); +} + +static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr) +{ + struct lxc_console *console = (struct lxc_console *)data; + char buf[1024]; + int r; + + r = read(fd, buf, sizeof(buf)); + if (r < 0) { + SYSERROR("failed to read"); + return 1; + } + + /* no output for the console, do nothing */ + if (console->peer == -1) + return 0; + + if (console->peer == fd) + write(console->master, buf, r); + else + write(console->peer, buf, r); + + return 0; +} + +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 (lxc_mainloop_add_handler(descr, console->master, + console_handler, console)) { + ERROR("failed to add to mainloop console handler for '%d'", + console->master); + return -1; + } + + if (console->peer != -1 && + lxc_mainloop_add_handler(descr, console->peer, + console_handler, console)) + WARN("console input disabled"); + + return 0; +} Index: lxc/src/lxc/console.h =================================================================== --- /dev/null +++ lxc/src/lxc/console.h @@ -0,0 +1,26 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2010 + * + * Authors: + * Daniel Lezcano <dlezcano at fr.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern int lxc_create_console(struct lxc_console *); +extern void lxc_delete_console(struct lxc_console *); +extern int lxc_console_mainloop_add(struct lxc_epoll_descr *, struct lxc_handler *); Index: lxc/src/lxc/lxc_start.c =================================================================== --- lxc.orig/src/lxc/lxc_start.c +++ lxc/src/lxc/lxc_start.c @@ -43,6 +43,7 @@ #include "log.h" #include "lxc.h" #include "conf.h" +#include "cgroup.h" #include "utils.h" #include "config.h" #include "confile.h" @@ -87,50 +88,10 @@ Options :\n\ .daemonize = 0, }; -static int save_tty(struct termios *tios) -{ - if (!isatty(0)) - return 0; - - if (tcgetattr(0, tios)) - WARN("failed to get current terminal settings : %s", - strerror(errno)); - - return 0; -} - -static int restore_tty(struct termios *tios) -{ - struct termios current_tios; - void (*oldhandler)(int); - int ret; - - if (!isatty(0)) - return 0; - - if (tcgetattr(0, ¤t_tios)) { - ERROR("failed to get current terminal settings : %s", - strerror(errno)); - return -1; - } - - if (!memcmp(tios, ¤t_tios, sizeof(*tios))) - return 0; - - oldhandler = signal(SIGTTOU, SIG_IGN); - ret = tcsetattr(0, TCSADRAIN, tios); - if (ret) - ERROR("failed to restore terminal attributes"); - signal(SIGTTOU, oldhandler); - - return ret; -} - int main(int argc, char *argv[]) { char *const *args; int err = -1; - struct termios tios; char *const default_args[] = { "/sbin/init", @@ -213,12 +174,8 @@ int main(int argc, char *argv[]) } } - save_tty(&tios); - err = lxc_start(my_args.name, args, conf); - restore_tty(&tios); - return err; } Index: lxc/src/lxc/start.c =================================================================== --- lxc.orig/src/lxc/start.c +++ lxc/src/lxc/start.c @@ -88,19 +88,16 @@ int signalfd(int fd, const sigset_t *mas #define PR_CAPBSET_DROP 24 #endif -#include <lxc/log.h> -#include <lxc/conf.h> -#include <lxc/confile.h> -#include <lxc/start.h> -#include <lxc/utils.h> -#include <lxc/cgroup.h> -#include <lxc/monitor.h> - +#include "start.h" +#include "conf.h" +#include "log.h" #include "error.h" #include "af_unix.h" #include "mainloop.h" +#include "utils.h" +#include "monitor.h" #include "commands.h" - +#include "console.h" lxc_log_define(lxc_start, lxc); @@ -170,6 +167,11 @@ int lxc_poll(const char *name, struct lx goto out_mainloop_open; } + if (lxc_console_mainloop_add(&descr, handler)) { + ERROR("failed to add console handler to mainloop"); + goto out_mainloop_open; + } + if (lxc_command_mainloop_add(name, &descr, handler)) goto out_mainloop_open; @@ -182,50 +184,6 @@ out_sigfd: return -1; } -static int fdname(int fd, char *name, size_t size) -{ - char path[MAXPATHLEN]; - ssize_t len; - - snprintf(path, MAXPATHLEN, "/proc/self/fd/%d", fd); - - len = readlink(path, name, size); - if (len > 0) - path[len] = '\0'; - - return (len <= 0) ? -1 : 0; -} - -static int console_init(char *console, size_t size) -{ - struct stat stat; - int i; - - for (i = 0; i < 3; i++) { - if (!isatty(i)) - continue; - - if (ttyname_r(i, console, size)) { - SYSERROR("failed to retrieve tty name"); - return -1; - } - - return 0; - } - - if (!fstat(0, &stat)) { - if (S_ISREG(stat.st_mode) || S_ISCHR(stat.st_mode) || - S_ISFIFO(stat.st_mode) || S_ISLNK(stat.st_mode)) - return fdname(0, console, size); - } - - console[0] = '\0'; - - DEBUG("console initialized"); - - return 0; -} - struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf) { struct lxc_handler *handler; @@ -244,23 +202,23 @@ struct lxc_handler *lxc_init(const char goto out_free; } - if (console_init(conf->console, sizeof(conf->console))) { - ERROR("failed to initialize the console"); - goto out_aborting; - } - if (lxc_create_tty(name, conf)) { ERROR("failed to create the ttys"); goto out_aborting; } + if (lxc_create_console(&conf->console)) { + 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_sigchld_fd(&handler->oldmask); if (handler->sigfd < 0) { ERROR("failed to set sigchild fd handler"); - goto out_delete_tty; + goto out_delete_console; } /* Avoid signals from terminal */ @@ -270,6 +228,8 @@ struct lxc_handler *lxc_init(const char INFO("'%s' is initialized", name); return handler; +out_delete_console: + lxc_delete_console(&conf->console); out_delete_tty: lxc_delete_tty(&conf->tty_info); out_aborting: ------------------------------------------------------------------------------ The Planet: dedicated and managed hosting, cloud storage, colocation Stay online with enterprise data centers and the best network in the business Choose flexible plans and management services without long-term contracts Personal 24x7 support from experience hosting pros just a phone call away. http://p.sf.net/sfu/theplanet-com _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel