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 <dlezcano@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, &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[])
 {
 	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:



