]> git.proxmox.com Git - mirror_lxc.git/commitdiff
start: allow containers to use a native console
authorChristian Brauner <christian.brauner@ubuntu.com>
Fri, 30 Jul 2021 12:28:17 +0000 (14:28 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Fri, 30 Jul 2021 13:24:22 +0000 (15:24 +0200)
After all of the previous rework we can make it possible for a container
to use a console allocated from the container's devpts instance.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/conf.c
src/lxc/start.c
src/lxc/terminal.c
src/lxc/terminal.h

index dbd7f012d13c17645077e852f0c4c8023c899ae1..12b227959b65d0678c360c3cdc79e708965442a3 100644 (file)
@@ -1829,12 +1829,7 @@ static int setup_personality(personality_t persona)
        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;
@@ -1856,7 +1851,7 @@ static int bind_mount_console(struct lxc_rootfs *rootfs,
         * 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);
@@ -1871,7 +1866,7 @@ static int bind_mount_console(struct lxc_rootfs *rootfs,
        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;
@@ -1911,7 +1906,7 @@ static int lxc_setup_dev_console(struct lxc_rootfs *rootfs,
        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);
@@ -1920,7 +1915,7 @@ static int lxc_setup_dev_console(struct lxc_rootfs *rootfs,
        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)
 {
@@ -1982,7 +1977,7 @@ static int lxc_setup_ttydir_console(struct lxc_rootfs *rootfs,
                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);
@@ -2022,21 +2017,53 @@ static int lxc_setup_console(const struct lxc_handler *handler,
                             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
@@ -2044,13 +2071,20 @@ static int lxc_setup_console(const struct lxc_handler *handler,
         * 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;
@@ -4057,6 +4091,56 @@ static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
        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;
@@ -4080,6 +4164,10 @@ int lxc_sync_fds_parent(struct lxc_handler *handler)
                        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;
 }
@@ -4106,6 +4194,10 @@ int lxc_sync_fds_child(struct lxc_handler *handler)
                        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;
 }
index 2ab3f087d6a0e2ea8c0cfdfdce7db17cbca79d9a..7930974bb0de43dc8f60cfe02a32273a3310b863 100644 (file)
@@ -850,6 +850,10 @@ int lxc_init(const char *name, struct lxc_handler *handler)
                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.
@@ -859,30 +863,24 @@ int lxc_init(const char *name, struct lxc_handler *handler)
                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");
 
@@ -890,9 +888,6 @@ int lxc_init(const char *name, struct lxc_handler *handler)
        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);
 
@@ -1930,6 +1925,12 @@ static int lxc_spawn(struct lxc_handler *handler)
                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
index 26470515b85874aa7d64c6f9262e646ae19e35d6..1e1e2111bf08d2aaab2e5d06fb1ae794e4d1e1d8 100644 (file)
@@ -917,6 +917,9 @@ int lxc_devpts_terminal(int devpts_fd, int *ret_ptx, int *ret_pty, int *ret_pty_
        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)
@@ -964,6 +967,41 @@ int lxc_devpts_terminal(int devpts_fd, int *ret_ptx, int *ret_pty, int *ret_pty_
        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)
 {
@@ -1010,9 +1048,9 @@ int lxc_terminal_setup(struct lxc_conf *conf)
        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)
index 9d3b61d324f7b8c7f3e89620aab3e09c9bdd8a53..05a01e5508670625087910ed17603624694cc7f5 100644 (file)
@@ -255,5 +255,11 @@ __hidden extern void lxc_terminal_init(struct lxc_terminal *terminal);
 __hidden extern int lxc_terminal_signal_sigmask_safe_blocked(struct lxc_terminal *terminal);
 __hidden extern int lxc_devpts_terminal(int devpts_fd, int *ret_ptx,
                                        int *ret_pty, int *ret_pty_nr);
+__hidden extern int lxc_terminal_parent(struct lxc_conf *conf);
+
+static inline bool wants_console(const struct lxc_terminal *terminal)
+{
+       return !terminal->path || !strequal(terminal->path, "none");
+}
 
 #endif /* __LXC_TERMINAL_H */