From: Daniel Lezcano Date: Wed, 24 Feb 2010 09:57:43 +0000 (+0100) Subject: allocate a console to be proxied X-Git-Tag: lxc-0.7.0~132 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=63376d7db32acf2f8582627e5ff01d8d3f0d46d1;p=thirdparty%2Flxc.git allocate a console to be proxied 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= 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: touch /var/lib/lxc/foo/console tail --retry -f /var/lib/lxc/foo/console in another shell: lxc-start -n foo -s lxc.console=/var/lib/lxc/foo/console Signed-off-by: Daniel Lezcano --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 286eb7685..d84e22f9d 100644 --- a/src/lxc/conf.c +++ b/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]; - - snprintf(console, sizeof(console), "%s/dev/console", - rootfs ? rootfs : ""); + char path[MAXPATHLEN]; + struct stat s; - /* 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); @@ -1365,7 +1369,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) 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; } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index b79dfe815..39cb0ecf6 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -148,12 +148,32 @@ struct lxc_tty_info { struct lxc_pty_info *pty_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; }; /* diff --git a/src/lxc/confile.c b/src/lxc/confile.c index f56495623..e24cc01ec 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -59,6 +62,7 @@ static int config_network_mtu(const char *, char *, struct lxc_conf *); 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 *key, char *value, 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) { diff --git a/src/lxc/console.c b/src/lxc/console.c index 96a6edd85..8e66665e6 100644 --- a/src/lxc/console.c +++ b/src/lxc/console.c @@ -23,7 +23,9 @@ #include #include +#include #include +#include #include #include @@ -32,6 +34,7 @@ #include /* 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, struct lxc_tty_info *tty_info) } 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; +} diff --git a/src/lxc/console.h b/src/lxc/console.h new file mode 100644 index 000000000..b465cc72e --- /dev/null +++ b/src/lxc/console.h @@ -0,0 +1,26 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2010 + * + * Authors: + * Daniel Lezcano + * + * 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 *); diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index 7a9c13c98..d1f42fe36 100644 --- a/src/lxc/lxc_start.c +++ b/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; } diff --git a/src/lxc/start.c b/src/lxc/start.c index 032fdf6c7..f4293f73b 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -88,19 +88,16 @@ int signalfd(int fd, const sigset_t *mask, int flags) #define PR_CAPBSET_DROP 24 #endif -#include -#include -#include -#include -#include -#include -#include - +#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 lxc_handler *handler) 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 *name, struct lxc_conf *conf) 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 *name, struct lxc_conf *conf) 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: @@ -288,6 +248,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler) lxc_set_state(name, handler, STOPPED); lxc_unlink_nsgroup(name); + lxc_delete_console(&handler->conf->console); lxc_delete_tty(&handler->conf->tty_info); free(handler);