return 0;
}
-static inline bool wants_console(const struct lxc_terminal *terminal)
-{
- return !terminal->path || !strequal(terminal->path, "none");
-}
-
-static int bind_mount_console(struct lxc_rootfs *rootfs,
+static int bind_mount_console(int fd_devpts, struct lxc_rootfs *rootfs,
struct lxc_terminal *console, int fd_to)
{
__do_close int fd_pty = -EBADF;
* created a detached mount based on the newly opened O_PATH file
* descriptor and then safely mount.
*/
- fd_pty = open_at_same(console->pty, rootfs->dfd_host, deabs(console->name),
+ fd_pty = open_at_same(console->pty, fd_devpts, fdstr(console->pty_nr),
PROTECT_OPATH_FILE, PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
if (fd_pty < 0)
return syserror("Failed to open \"%s\"", console->name);
return mount_fd(fd_pty, fd_to, "none", MS_BIND, 0);
}
-static int lxc_setup_dev_console(struct lxc_rootfs *rootfs,
+static int lxc_setup_dev_console(int fd_devpts, struct lxc_rootfs *rootfs,
struct lxc_terminal *console)
{
__do_close int fd_console = -EBADF;
if (ret < 0)
return syserror("Failed to change console mode");
- ret = bind_mount_console(rootfs, console, fd_console);
+ ret = bind_mount_console(fd_devpts, rootfs, console, fd_console);
if (ret < 0)
return syserror("Failed to mount \"%d(%s)\" on \"%d\"",
console->pty, console->name, fd_console);
return 0;
}
-static int lxc_setup_ttydir_console(struct lxc_rootfs *rootfs,
+static int lxc_setup_ttydir_console(int fd_devpts, struct lxc_rootfs *rootfs,
struct lxc_terminal *console,
char *ttydir)
{
return syserror("Failed to change console mode");
/* bind mount console to '/dev/<ttydir>/console' */
- ret = bind_mount_console(rootfs, console, fd_reg_ttydir_console);
+ ret = bind_mount_console(fd_devpts, rootfs, console, fd_reg_ttydir_console);
if (ret < 0)
return syserror("Failed to mount \"%d(%s)\" on \"%d\"",
console->pty, console->name, fd_reg_ttydir_console);
struct lxc_rootfs *rootfs,
struct lxc_terminal *console, char *ttydir)
{
- __do_close int fd_pty = -EBADF;
- int ret;
+ __do_close int fd_devpts_host = -EBADF;
+ int fd_devpts = handler->conf->devpts_fd;
+ int ret = -1;
if (!wants_console(console))
return log_trace(0, "Skipping console setup");
+ if (console->pty < 0) {
+ /*
+ * Allocate a console from the container's devpts instance. We
+ * have checked on the host that we have enough pty devices
+ * available.
+ */
+ ret = lxc_devpts_terminal(handler->conf->devpts_fd, &console->ptx,
+ &console->pty, &console->pty_nr);
+ if (ret < 0)
+ return syserror("Failed to allocate console from container's devpts instance");
+
+ ret = strnprintf(console->name, sizeof(console->name),
+ "/dev/pts/%d", console->pty_nr);
+ if (ret < 0)
+ return syserror("Failed to create console path");
+ } else {
+ /*
+ * We're using a console from the host's devpts instance. Open
+ * it again so we can later verify that the console we're
+ * supposed to use is still the same as the one we opened on
+ * the host.
+ */
+ fd_devpts_host = open_at(rootfs->dfd_host,
+ "dev/pts",
+ PROTECT_OPATH_DIRECTORY,
+ PROTECT_LOOKUP_BENEATH_XDEV,
+ 0);
+ if (fd_devpts_host < 0)
+ return syserror("Failed to open host devpts");
+
+ fd_devpts = fd_devpts_host;
+ }
+
if (ttydir)
- ret = lxc_setup_ttydir_console(rootfs, console, ttydir);
+ ret = lxc_setup_ttydir_console(fd_devpts, rootfs, console, ttydir);
else
- ret = lxc_setup_dev_console(rootfs, console);
+ ret = lxc_setup_dev_console(fd_devpts, rootfs, console);
if (ret < 0)
return syserror("Failed to setup console");
- fd_pty = move_fd(console->pty);
-
/*
* Some init's such as busybox will set sane tty settings on stdin,
* stdout, stderr which it thinks is the console. We already set them
* setup on its console ie. the pty allocated in lxc_terminal_setup() so
* make sure that that pty is stdin,stdout,stderr.
*/
- if (fd_pty >= 0) {
+ if (console->pty >= 0) {
if (handler->daemonize || !handler->conf->is_execute)
- ret = set_stdfds(fd_pty);
+ ret = set_stdfds(console->pty);
else
- ret = lxc_terminal_set_stdfds(fd_pty);
+ ret = lxc_terminal_set_stdfds(console->pty);
if (ret < 0)
- return syserror("Failed to redirect std{in,out,err} to pty file descriptor %d", fd_pty);
+ return syserror("Failed to redirect std{in,out,err} to pty file descriptor %d", console->pty);
+
+ /*
+ * If the console has been allocated from the host's devpts
+ * we're done and we don't need to send fds to the parent.
+ */
+ if (fd_devpts_host >= 0)
+ lxc_terminal_delete(console);
}
return ret;
return ret;
}
+static int lxc_send_console_to_parent(struct lxc_handler *handler)
+{
+ struct lxc_terminal *console = &handler->conf->console;
+ int ret;
+
+ if (!wants_console(console))
+ return 0;
+
+ /* We've already allocated a console from the host's devpts instance. */
+ if (console->pty < 0)
+ return 0;
+
+ ret = __lxc_abstract_unix_send_two_fds(handler->data_sock[0],
+ console->ptx, console->pty,
+ console,
+ sizeof(struct lxc_terminal));
+ if (ret < 0)
+ return syserror("Fail to send console to parent");
+
+ TRACE("Sent console to parent");
+ return 0;
+}
+
+static int lxc_recv_console_from_child(struct lxc_handler *handler)
+{
+ __do_close int fd_ptx = -EBADF, fd_pty = -EBADF;
+ struct lxc_terminal *console = &handler->conf->console;
+ int ret;
+
+ if (!wants_console(console))
+ return 0;
+
+ /* We've already allocated a console from the host's devpts instance. */
+ if (console->pty >= 0)
+ return 0;
+
+ ret = __lxc_abstract_unix_recv_two_fds(handler->data_sock[1],
+ &fd_ptx, &fd_pty,
+ console,
+ sizeof(struct lxc_terminal));
+ if (ret < 0)
+ return syserror("Fail to receive console from child");
+
+ console->ptx = move_fd(fd_ptx);
+ console->pty = move_fd(fd_pty);
+
+ TRACE("Received console from child");
+ return 0;
+}
+
int lxc_sync_fds_parent(struct lxc_handler *handler)
{
int ret;
return syserror_ret(ret, "Failed to receive names and ifindices for network devices from child");
}
+ ret = lxc_recv_console_from_child(handler);
+ if (ret < 0)
+ return syserror_ret(ret, "Failed to receive console from child");
+
TRACE("Finished syncing file descriptors with child");
return 0;
}
return syserror_ret(ret, "Failed to send network device names and ifindices to parent");
}
+ ret = lxc_send_console_to_parent(handler);
+ if (ret < 0)
+ return syserror_ret(ret, "Failed to send console to parent");
+
TRACE("Finished syncing file descriptors with parent");
return 0;
}
return log_error(-1, "Failed to run lxc.hook.pre-start for container \"%s\"", name);
TRACE("Ran pre-start hooks");
+ ret = lxc_terminal_parent(conf);
+ if (ret < 0)
+ return log_error(-1, "Failed to allocate terminal");
+
/* 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.
return log_error(-1, "Failed to setup SIGCHLD fd handler.");
TRACE("Set up signal fd");
- /* Do this after setting up signals since it might unblock SIGWINCH. */
- ret = lxc_terminal_setup(conf);
- if (ret < 0) {
- ERROR("Failed to create console");
- goto out_restore_sigmask;
- }
- TRACE("Created console");
-
handler->cgroup_ops = cgroup_init(handler->conf);
if (!handler->cgroup_ops) {
ERROR("Failed to initialize cgroup driver");
- goto out_delete_terminal;
+ goto out_restore_sigmask;
}
TRACE("Initialized cgroup driver");
ret = lxc_read_seccomp_config(conf);
- if (ret < 0)
- return log_error(-1, "Failed loading seccomp policy");
+ if (ret < 0) {
+ ERROR("Failed to read seccomp policy");
+ goto out_restore_sigmask;
+ }
TRACE("Read seccomp policy");
ret = handler->lsm_ops->prepare(handler->lsm_ops, conf, handler->lxcpath);
if (ret < 0) {
ERROR("Failed to initialize LSM");
- goto out_delete_terminal;
+ goto out_restore_sigmask;
}
TRACE("Initialized LSM");
handler->monitor_status_fd = move_fd(status_fd);
return 0;
-out_delete_terminal:
- lxc_terminal_delete(&handler->conf->console);
-
out_restore_sigmask:
(void)pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
goto out_delete_net;
}
+ ret = lxc_terminal_setup(conf);
+ if (ret < 0) {
+ SYSERROR("Failed to create console");
+ goto out_delete_net;
+ }
+
/*
* Tell the child to complete its initialization and wait for it to
* exec or return an error. (The child will never return
int pty_nr = -1;
int ret;
+ if (devpts_fd < 0)
+ return ret_errno(EBADF);
+
fd_ptx = open_beneath(devpts_fd, "ptmx", O_RDWR | O_NOCTTY | O_CLOEXEC);
if (fd_ptx < 0) {
if (errno == ENOSPC)
return 0;
}
+int lxc_terminal_parent(struct lxc_conf *conf)
+{
+ __do_close int fd_devpts = -EBADF;
+ struct lxc_terminal *console = &conf->console;
+ int ret;
+
+ if (!wants_console(&conf->console))
+ return 0;
+
+ /* Allocate console from the container's devpts. */
+ if (conf->pty_max > 1)
+ return 0;
+
+ /* Allocate console for the container from the host's devpts. */
+ fd_devpts = open_at(-EBADF,
+ "/dev/pts",
+ PROTECT_OPATH_DIRECTORY,
+ PROTECT_LOOKUP_ABSOLUTE_XDEV,
+ 0);
+ if (fd_devpts < 0)
+ return syserror("Failed to open devpts instance");
+
+ ret = lxc_devpts_terminal(fd_devpts, &console->ptx,
+ &console->pty, &console->pty_nr);
+ if (ret < 0)
+ return syserror("Failed to allocate console");
+
+ ret = strnprintf(console->name, sizeof(console->name),
+ "/dev/pts/%d", console->pty_nr);
+ if (ret < 0)
+ return syserror("Failed to create console path");
+
+ return lxc_terminal_map_ids(conf, &conf->console);
+}
+
static int lxc_terminal_create_native(const char *name, const char *lxcpath,
struct lxc_terminal *terminal)
{
if (terminal->path && strequal(terminal->path, "none"))
return log_info(0, "No terminal requested");
- ret = lxc_terminal_create_foreign(conf, terminal);
+ ret = lxc_terminal_peer_default(terminal);
if (ret < 0)
- return -1;
+ goto err;
ret = lxc_terminal_create_log_file(terminal);
if (ret < 0)