Commit 63376d7d by Daniel Lezcano

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=<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: 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: 's avatarDaniel Lezcano <dlezcano@fr.ibm.com>
parent 24654103
...@@ -641,41 +641,42 @@ out: ...@@ -641,41 +641,42 @@ out:
return 0; 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 /* We don't have a rootfs, /dev/console will be shared */
* to be used as console, let's remap /dev/console if (!rootfs)
* to /dev/null to avoid to log to the system console return 0;
*/
if (rootfs && !tty[0]) {
if (!access(console, F_OK)) { snprintf(path, sizeof(path), "%s/dev/console", rootfs);
if (mount("/dev/null", console, "none", MS_BIND, 0)) { if (access(path, F_OK)) {
SYSERROR("failed to mount '/dev/null'->'%s'", WARN("rootfs specified but no console found");
console); return 0;
return -1;
}
}
} }
if (!tty[0]) if (console->peer == -1)
return 0; INFO("no console output required");
if (access(console, R_OK|W_OK)) if (stat(path, &s)) {
return 0; 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)) { if (mount(console->name, path, "none", MS_BIND, 0)) {
ERROR("failed to mount the console"); ERROR("failed to mount '%s' on '%s'", console->name, path);
return -1; return -1;
} }
INFO("console '%s' mounted to '%s'", tty, console); INFO("console has been setup");
return 0; return 0;
} }
...@@ -1073,7 +1074,10 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -1073,7 +1074,10 @@ struct lxc_conf *lxc_conf_init(void)
new->utsname = NULL; new->utsname = NULL;
new->tty = 0; new->tty = 0;
new->pts = 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->cgroup);
lxc_list_init(&new->network); lxc_list_init(&new->network);
lxc_list_init(&new->mount_list); lxc_list_init(&new->mount_list);
...@@ -1365,7 +1369,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) ...@@ -1365,7 +1369,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
return -1; 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); ERROR("failed to setup the console for '%s'", name);
return -1; return -1;
} }
......
...@@ -149,11 +149,31 @@ struct lxc_tty_info { ...@@ -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 * Defines the global container configuration
* @rootfs : the root directory to run the container * @rootfs : root directory to run the container
* @mount : the list of mount points * @pivotdir : pivotdir path, if not set default will be used
* @network : the network configuration * @mount : list of mount points
* @utsname : the container utsname * @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 { struct lxc_conf {
char *rootfs; char *rootfs;
...@@ -167,7 +187,7 @@ struct lxc_conf { ...@@ -167,7 +187,7 @@ struct lxc_conf {
struct lxc_list mount_list; struct lxc_list mount_list;
struct lxc_list caps; struct lxc_list caps;
struct lxc_tty_info tty_info; struct lxc_tty_info tty_info;
char console[MAXPATHLEN]; struct lxc_console console;
}; };
/* /*
......
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <pty.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/utsname.h> #include <sys/utsname.h>
...@@ -59,6 +62,7 @@ static int config_network_mtu(const char *, char *, struct lxc_conf *); ...@@ -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_ipv4(const char *, char *, struct lxc_conf *);
static int config_network_ipv6(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_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 *); typedef int (*config_cb)(const char *, char *, struct lxc_conf *);
...@@ -88,6 +92,7 @@ static struct config config[] = { ...@@ -88,6 +92,7 @@ static struct config config[] = {
{ "lxc.network.ipv4", config_network_ipv4 }, { "lxc.network.ipv4", config_network_ipv4 },
{ "lxc.network.ipv6", config_network_ipv6 }, { "lxc.network.ipv6", config_network_ipv6 },
{ "lxc.cap.drop", config_cap_drop }, { "lxc.cap.drop", config_cap_drop },
{ "lxc.console", config_console },
}; };
static const size_t config_size = sizeof(config)/sizeof(struct config); 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, ...@@ -615,6 +620,21 @@ static int config_cap_drop(const char *key, char *value,
return ret; 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) static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf)
{ {
if (strlen(value) >= MAXPATHLEN) { if (strlen(value) >= MAXPATHLEN) {
......
...@@ -23,7 +23,9 @@ ...@@ -23,7 +23,9 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <pty.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
...@@ -32,6 +34,7 @@ ...@@ -32,6 +34,7 @@
#include <lxc/start.h> /* for struct lxc_handler */ #include <lxc/start.h> /* for struct lxc_handler */
#include "commands.h" #include "commands.h"
#include "mainloop.h"
#include "af_unix.h" #include "af_unix.h"
lxc_log_define(lxc_console, lxc); lxc_log_define(lxc_console, lxc);
...@@ -95,7 +98,7 @@ extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info) ...@@ -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, extern int lxc_console_callback(int fd, struct lxc_request *request,
struct lxc_handler *handler) struct lxc_handler *handler)
{ {
int ttynum = request->data; int ttynum = request->data;
struct lxc_tty_info *tty_info = &handler->conf->tty_info; struct lxc_tty_info *tty_info = &handler->conf->tty_info;
...@@ -135,3 +138,78 @@ out_close: ...@@ -135,3 +138,78 @@ out_close:
return 1; 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;
}
/*
* 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 *);
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "log.h" #include "log.h"
#include "lxc.h" #include "lxc.h"
#include "conf.h" #include "conf.h"
#include "cgroup.h"
#include "utils.h" #include "utils.h"
#include "config.h" #include "config.h"
#include "confile.h" #include "confile.h"
...@@ -87,50 +88,10 @@ Options :\n\ ...@@ -87,50 +88,10 @@ Options :\n\
.daemonize = 0, .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, &current_tios)) {
ERROR("failed to get current terminal settings : %s",
strerror(errno));
return -1;
}
if (!memcmp(tios, &current_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[]) int main(int argc, char *argv[])
{ {
char *const *args; char *const *args;
int err = -1; int err = -1;
struct termios tios;
char *const default_args[] = { char *const default_args[] = {
"/sbin/init", "/sbin/init",
...@@ -213,12 +174,8 @@ int main(int argc, char *argv[]) ...@@ -213,12 +174,8 @@ int main(int argc, char *argv[])
} }
} }
save_tty(&tios);
err = lxc_start(my_args.name, args, conf); err = lxc_start(my_args.name, args, conf);
restore_tty(&tios);
return err; return err;
} }
...@@ -88,19 +88,16 @@ int signalfd(int fd, const sigset_t *mask, int flags) ...@@ -88,19 +88,16 @@ int signalfd(int fd, const sigset_t *mask, int flags)
#define PR_CAPBSET_DROP 24 #define PR_CAPBSET_DROP 24
#endif #endif
#include <lxc/log.h> #include "start.h"
#include <lxc/conf.h> #include "conf.h"
#include <lxc/confile.h> #include "log.h"
#include <lxc/start.h>
#include <lxc/utils.h>
#include <lxc/cgroup.h>
#include <lxc/monitor.h>
#include "error.h" #include "error.h"
#include "af_unix.h" #include "af_unix.h"
#include "mainloop.h" #include "mainloop.h"
#include "utils.h"
#include "monitor.h"
#include "commands.h" #include "commands.h"
#include "console.h"
lxc_log_define(lxc_start, lxc); lxc_log_define(lxc_start, lxc);
...@@ -170,6 +167,11 @@ int lxc_poll(const char *name, struct lxc_handler *handler) ...@@ -170,6 +167,11 @@ 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)) {
ERROR("failed to add console handler to mainloop");
goto out_mainloop_open;
}
if (lxc_command_mainloop_add(name, &descr, handler)) if (lxc_command_mainloop_add(name, &descr, handler))
goto out_mainloop_open; goto out_mainloop_open;
...@@ -182,50 +184,6 @@ out_sigfd: ...@@ -182,50 +184,6 @@ out_sigfd:
return -1; 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 *lxc_init(const char *name, struct lxc_conf *conf)
{ {
struct lxc_handler *handler; struct lxc_handler *handler;
...@@ -244,23 +202,23 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf) ...@@ -244,23 +202,23 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
goto out_free; 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)) { if (lxc_create_tty(name, conf)) {
ERROR("failed to create the ttys"); ERROR("failed to create the ttys");
goto out_aborting; 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 /* 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_sigchld_fd(&handler->oldmask); handler->sigfd = setup_sigchld_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_tty; goto out_delete_console;
} }
/* Avoid signals from terminal */ /* Avoid signals from terminal */
...@@ -270,6 +228,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf) ...@@ -270,6 +228,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
INFO("'%s' is initialized", name); INFO("'%s' is initialized", name);
return handler; return handler;
out_delete_console:
lxc_delete_console(&conf->console);
out_delete_tty: out_delete_tty:
lxc_delete_tty(&conf->tty_info); lxc_delete_tty(&conf->tty_info);
out_aborting: out_aborting:
...@@ -288,6 +248,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler) ...@@ -288,6 +248,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
lxc_set_state(name, handler, STOPPED); lxc_set_state(name, handler, STOPPED);
lxc_unlink_nsgroup(name); lxc_unlink_nsgroup(name);
lxc_delete_console(&handler->conf->console);
lxc_delete_tty(&handler->conf->tty_info); lxc_delete_tty(&handler->conf->tty_info);
free(handler); free(handler);
......
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