]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
attach: invert child/parent handling
authorChristian Brauner <christian.brauner@ubuntu.com>
Wed, 27 Jan 2021 19:24:57 +0000 (20:24 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Wed, 27 Jan 2021 19:52:34 +0000 (20:52 +0100)
This makes it more consistent with th rest of the shared library.

Cc: stable-4.0
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/attach.c

index cfddf9b36a234bb046b65f5e28099d284ae35b73..f1a59b3306a9444ded4c9aaa82b49ad0e15c7468 100644 (file)
@@ -962,6 +962,9 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
        struct lxc_conf *conf;
        char *name, *lxcpath;
        struct attach_clone_payload payload = {0};
+       int ret_parent = -1;
+       pid_t to_cleanup_pid;
+       struct lxc_epoll_descr descr = {0};
 
        ret = access("/proc/self/ns", X_OK);
        if (ret)
@@ -1155,275 +1158,272 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
                return -1;
        }
 
-       if (pid) {
-               int ret_parent = -1;
-               pid_t to_cleanup_pid = pid;
-               struct lxc_epoll_descr descr = {0};
-
+       if (pid == 0) {
                /* close unneeded file descriptors */
-               close(ipc_sockets[1]);
-               free(cwd);
-               lxc_proc_close_ns_fd(init_ctx);
-               if (options->attach_flags & LXC_ATTACH_TERMINAL)
-                       lxc_attach_terminal_close_pts(&terminal);
-
-               /* Attach to cgroup, if requested. */
-               if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
-                       /*
-                        * If this is the unified hierarchy cgroup_attach() is
-                        * enough.
-                        */
-                       ret = cgroup_attach(conf, name, lxcpath, pid);
-                       if (ret) {
-                               call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
-
-                               cgroup_ops = cgroup_init(conf);
-                               if (!cgroup_ops)
-                                       goto on_error;
+               close_prot_errno_disarm(ipc_sockets[0]);
 
-                               if (!cgroup_ops->attach(cgroup_ops, conf, name, lxcpath, pid))
-                                       goto on_error;
-                       }
-                       TRACE("Moved intermediate process %d into container's cgroups", pid);
+               if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+                       lxc_attach_terminal_close_ptx(&terminal);
+                       lxc_attach_terminal_close_peer(&terminal);
+                       lxc_attach_terminal_close_log(&terminal);
                }
 
-               /* Setup /proc limits */
-               if (!lxc_list_empty(&conf->procs)) {
-                       ret = setup_proc_filesystem(&conf->procs, pid);
-                       if (ret < 0)
-                               goto on_error;
+               /* Wait for the parent to have setup cgroups. */
+               ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status));
+               if (ret != sizeof(status)) {
+                       shutdown(ipc_sockets[1], SHUT_RDWR);
+                       lxc_proc_put_context_info(init_ctx);
+                       _exit(EXIT_FAILURE);
                }
 
-               /* Setup resource limits */
-               if (!lxc_list_empty(&conf->limits)) {
-                       ret = setup_resource_limits(&conf->limits, pid);
-                       if (ret < 0)
-                               goto on_error;
+               TRACE("Intermediate process starting to initialize");
+
+               /* Attach now, create another subprocess later, since pid namespaces
+                * only really affect the children of the current process.
+                */
+               ret = lxc_attach_to_ns(init_pid, init_ctx);
+               if (ret < 0) {
+                       ERROR("Failed to enter namespaces");
+                       shutdown(ipc_sockets[1], SHUT_RDWR);
+                       lxc_proc_put_context_info(init_ctx);
+                       _exit(EXIT_FAILURE);
                }
 
-               if (options->attach_flags & LXC_ATTACH_TERMINAL) {
-                       ret = lxc_attach_terminal_mainloop_init(&terminal, &descr);
-                       if (ret < 0)
-                               goto on_error;
+               /* close namespace file descriptors */
+               lxc_proc_close_ns_fd(init_ctx);
 
-                       TRACE("Initialized terminal mainloop");
+               /* Attach succeeded, try to cwd. */
+               if (options->initial_cwd)
+                       new_cwd = options->initial_cwd;
+               else
+                       new_cwd = cwd;
+               if (new_cwd) {
+                       ret = chdir(new_cwd);
+                       if (ret < 0)
+                               WARN("Could not change directory to \"%s\"", new_cwd);
                }
+               free(cwd);
 
-               /* Let the child process know to go ahead. */
-               status = 0;
-               ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
-               if (ret != sizeof(status))
-                       goto close_mainloop;
-
-               TRACE("Told intermediate process to start initializing");
+               /* Create attached process. */
+               payload.ipc_socket = ipc_sockets[1];
+               payload.options = options;
+               payload.init_ctx = init_ctx;
+               payload.terminal_pts_fd = terminal.pty;
+               payload.exec_function = exec_function;
+               payload.exec_payload = exec_payload;
+
+               pid = lxc_raw_clone(CLONE_PARENT, NULL);
+               if (pid < 0) {
+                       SYSERROR("Failed to clone attached process");
+                       shutdown(ipc_sockets[1], SHUT_RDWR);
+                       lxc_proc_put_context_info(init_ctx);
+                       _exit(EXIT_FAILURE);
+               }
 
-               /* Get pid of attached process from intermediate process. */
-               ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid));
-               if (ret != sizeof(attached_pid))
-                       goto close_mainloop;
+               if (pid == 0) {
+                       if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+                               ret = lxc_terminal_signal_sigmask_safe_blocked(&terminal);
+                               if (ret < 0) {
+                                       SYSERROR("Failed to reset signal mask");
+                                       _exit(EXIT_FAILURE);
+                               }
+                       }
 
-               TRACE("Received pid %d of attached process in parent pid namespace", attached_pid);
+                       ret = attach_child_main(&payload);
+                       if (ret < 0)
+                               ERROR("Failed to exec");
 
-               /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
-               if (options->stdin_fd == 0) {
-                       signal(SIGINT, SIG_IGN);
-                       signal(SIGQUIT, SIG_IGN);
+                       _exit(EXIT_FAILURE);
                }
 
-               /* Reap intermediate process. */
-               ret = wait_for_pid(pid);
-               if (ret < 0)
-                       goto close_mainloop;
+               if (options->attach_flags & LXC_ATTACH_TERMINAL)
+                       lxc_attach_terminal_close_pts(&terminal);
 
-               TRACE("Intermediate process %d exited", pid);
+               /* Tell grandparent the pid of the pid of the newly created child. */
+               ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid));
+               if (ret != sizeof(pid)) {
+                       /* If this really happens here, this is very unfortunate, since
+                        * the parent will not know the pid of the attached process and
+                        * will not be able to wait for it (and we won't either due to
+                        * CLONE_PARENT) so the parent won't be able to reap it and the
+                        * attached process will remain a zombie.
+                        */
+                       shutdown(ipc_sockets[1], SHUT_RDWR);
+                       lxc_proc_put_context_info(init_ctx);
+                       _exit(EXIT_FAILURE);
+               }
 
-               /* We will always have to reap the attached process now. */
-               to_cleanup_pid = attached_pid;
+               TRACE("Sending pid %d of attached process", pid);
 
-               /* Open LSM fd and send it to child. */
-               if ((options->namespaces & CLONE_NEWNS) &&
-                   (options->attach_flags & LXC_ATTACH_LSM) &&
-                   init_ctx->lsm_label) {
-                       int labelfd;
-                       bool on_exec;
+               /* The rest is in the hands of the initial and the attached process. */
+               lxc_proc_put_context_info(init_ctx);
+               _exit(EXIT_SUCCESS);
+       }
 
-                       ret = -1;
-                       on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false;
-                       labelfd = init_ctx->lsm_ops->process_label_fd_get(init_ctx->lsm_ops,
-                                                                         attached_pid, on_exec);
-                       if (labelfd < 0)
-                               goto close_mainloop;
+       to_cleanup_pid = pid;
 
-                       TRACE("Opened LSM label file descriptor %d", labelfd);
+       /* close unneeded file descriptors */
+       close(ipc_sockets[1]);
+       free(cwd);
+       lxc_proc_close_ns_fd(init_ctx);
+       if (options->attach_flags & LXC_ATTACH_TERMINAL)
+               lxc_attach_terminal_close_pts(&terminal);
 
-                       /* Send child fd of the LSM security module to write to. */
-                       ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0);
-                       if (ret <= 0) {
-                               if (ret < 0)
-                                       SYSERROR("Failed to send lsm label fd");
+       /* Attach to cgroup, if requested. */
+       if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
+               /*
+                * If this is the unified hierarchy cgroup_attach() is
+                * enough.
+                */
+               ret = cgroup_attach(conf, name, lxcpath, pid);
+               if (ret) {
+                       call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
 
-                               close(labelfd);
-                               goto close_mainloop;
-                       }
+                       cgroup_ops = cgroup_init(conf);
+                       if (!cgroup_ops)
+                               goto on_error;
 
-                       close(labelfd);
-                       TRACE("Sent LSM label file descriptor %d to child", labelfd);
+                       if (!cgroup_ops->attach(cgroup_ops, conf, name, lxcpath, pid))
+                               goto on_error;
                }
+               TRACE("Moved intermediate process %d into container's cgroups", pid);
+       }
 
-               if (conf->seccomp.seccomp) {
-                       ret = lxc_seccomp_recv_notifier_fd(&conf->seccomp, ipc_sockets[0]);
-                       if (ret < 0)
-                               goto close_mainloop;
+       /* Setup /proc limits */
+       if (!lxc_list_empty(&conf->procs)) {
+               ret = setup_proc_filesystem(&conf->procs, pid);
+               if (ret < 0)
+                       goto on_error;
+       }
 
-                       ret = lxc_seccomp_add_notifier(name, lxcpath, &conf->seccomp);
-                       if (ret < 0)
-                               goto close_mainloop;
-               }
+       /* Setup resource limits */
+       if (!lxc_list_empty(&conf->limits)) {
+               ret = setup_resource_limits(&conf->limits, pid);
+               if (ret < 0)
+                       goto on_error;
+       }
 
-               /* We're done, the child process should now execute whatever it
-                * is that the user requested. The parent can now track it with
-                * waitpid() or similar.
-                */
+       if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+               ret = lxc_attach_terminal_mainloop_init(&terminal, &descr);
+               if (ret < 0)
+                       goto on_error;
 
-               *attached_process = attached_pid;
+               TRACE("Initialized terminal mainloop");
+       }
 
-               /* Now shut down communication with child, we're done. */
-               shutdown(ipc_sockets[0], SHUT_RDWR);
-               close(ipc_sockets[0]);
-               ipc_sockets[0] = -1;
+       /* Let the child process know to go ahead. */
+       status = 0;
+       ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
+       if (ret != sizeof(status))
+               goto close_mainloop;
 
-               ret_parent = 0;
-               to_cleanup_pid = -1;
+       TRACE("Told intermediate process to start initializing");
 
-               if (options->attach_flags & LXC_ATTACH_TERMINAL) {
-                       ret = lxc_mainloop(&descr, -1);
-                       if (ret < 0) {
-                               ret_parent = -1;
-                               to_cleanup_pid = attached_pid;
-                       }
-               }
+       /* Get pid of attached process from intermediate process. */
+       ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid));
+       if (ret != sizeof(attached_pid))
+               goto close_mainloop;
 
-       close_mainloop:
-               if (options->attach_flags & LXC_ATTACH_TERMINAL)
-                       lxc_mainloop_close(&descr);
+       TRACE("Received pid %d of attached process in parent pid namespace", attached_pid);
 
-       on_error:
-               if (ipc_sockets[0] >= 0) {
-                       shutdown(ipc_sockets[0], SHUT_RDWR);
-                       close(ipc_sockets[0]);
-               }
+       /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
+       if (options->stdin_fd == 0) {
+               signal(SIGINT, SIG_IGN);
+               signal(SIGQUIT, SIG_IGN);
+       }
 
-               if (to_cleanup_pid > 0)
-                       (void)wait_for_pid(to_cleanup_pid);
+       /* Reap intermediate process. */
+       ret = wait_for_pid(pid);
+       if (ret < 0)
+               goto close_mainloop;
 
-               if (options->attach_flags & LXC_ATTACH_TERMINAL) {
-                       lxc_terminal_delete(&terminal);
-                       lxc_terminal_conf_free(&terminal);
-               }
+       TRACE("Intermediate process %d exited", pid);
 
-               lxc_proc_put_context_info(init_ctx);
-               return ret_parent;
-       }
+       /* We will always have to reap the attached process now. */
+       to_cleanup_pid = attached_pid;
 
-       /* close unneeded file descriptors */
-       close_prot_errno_disarm(ipc_sockets[0]);
+       /* Open LSM fd and send it to child. */
+       if ((options->namespaces & CLONE_NEWNS) &&
+           (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) {
+               int labelfd;
+               bool on_exec;
 
-       if (options->attach_flags & LXC_ATTACH_TERMINAL) {
-               lxc_attach_terminal_close_ptx(&terminal);
-               lxc_attach_terminal_close_peer(&terminal);
-               lxc_attach_terminal_close_log(&terminal);
-       }
+               ret = -1;
+               on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false;
+               labelfd = init_ctx->lsm_ops->process_label_fd_get(init_ctx->lsm_ops,
+                                                                 attached_pid, on_exec);
+               if (labelfd < 0)
+                       goto close_mainloop;
 
-       /* Wait for the parent to have setup cgroups. */
-       ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status));
-       if (ret != sizeof(status)) {
-               shutdown(ipc_sockets[1], SHUT_RDWR);
-               lxc_proc_put_context_info(init_ctx);
-               _exit(EXIT_FAILURE);
-       }
+               TRACE("Opened LSM label file descriptor %d", labelfd);
 
-       TRACE("Intermediate process starting to initialize");
+               /* Send child fd of the LSM security module to write to. */
+               ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0);
+               if (ret <= 0) {
+                       if (ret < 0)
+                               SYSERROR("Failed to send lsm label fd");
 
-       /* Attach now, create another subprocess later, since pid namespaces
-        * only really affect the children of the current process.
-        */
-       ret = lxc_attach_to_ns(init_pid, init_ctx);
-       if (ret < 0) {
-               ERROR("Failed to enter namespaces");
-               shutdown(ipc_sockets[1], SHUT_RDWR);
-               lxc_proc_put_context_info(init_ctx);
-               _exit(EXIT_FAILURE);
+                       close(labelfd);
+                       goto close_mainloop;
+               }
+
+               close(labelfd);
+               TRACE("Sent LSM label file descriptor %d to child", labelfd);
        }
 
-       /* close namespace file descriptors */
-       lxc_proc_close_ns_fd(init_ctx);
+       if (conf->seccomp.seccomp) {
+               ret = lxc_seccomp_recv_notifier_fd(&conf->seccomp, ipc_sockets[0]);
+               if (ret < 0)
+                       goto close_mainloop;
 
-       /* Attach succeeded, try to cwd. */
-       if (options->initial_cwd)
-               new_cwd = options->initial_cwd;
-       else
-               new_cwd = cwd;
-       if (new_cwd) {
-               ret = chdir(new_cwd);
+               ret = lxc_seccomp_add_notifier(name, lxcpath, &conf->seccomp);
                if (ret < 0)
-                       WARN("Could not change directory to \"%s\"", new_cwd);
+                       goto close_mainloop;
        }
-       free(cwd);
 
-       /* Create attached process. */
-       payload.ipc_socket = ipc_sockets[1];
-       payload.options = options;
-       payload.init_ctx = init_ctx;
-       payload.terminal_pts_fd = terminal.pty;
-       payload.exec_function = exec_function;
-       payload.exec_payload = exec_payload;
+       /* We're done, the child process should now execute whatever it
+        * is that the user requested. The parent can now track it with
+        * waitpid() or similar.
+        */
 
-       pid = lxc_raw_clone(CLONE_PARENT, NULL);
-       if (pid < 0) {
-               SYSERROR("Failed to clone attached process");
-               shutdown(ipc_sockets[1], SHUT_RDWR);
-               lxc_proc_put_context_info(init_ctx);
-               _exit(EXIT_FAILURE);
-       }
+       *attached_process = attached_pid;
 
-       if (pid == 0) {
-               if (options->attach_flags & LXC_ATTACH_TERMINAL) {
-                       ret = lxc_terminal_signal_sigmask_safe_blocked(&terminal);
-                       if (ret < 0) {
-                               SYSERROR("Failed to reset signal mask");
-                               _exit(EXIT_FAILURE);
-                       }
-               }
+       /* Now shut down communication with child, we're done. */
+       shutdown(ipc_sockets[0], SHUT_RDWR);
+       close(ipc_sockets[0]);
+       ipc_sockets[0] = -1;
 
-               ret = attach_child_main(&payload);
-               if (ret < 0)
-                       ERROR("Failed to exec");
+       ret_parent = 0;
+       to_cleanup_pid = -1;
 
-               _exit(EXIT_FAILURE);
+       if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+               ret = lxc_mainloop(&descr, -1);
+               if (ret < 0) {
+                       ret_parent = -1;
+                       to_cleanup_pid = attached_pid;
+               }
        }
 
+close_mainloop:
        if (options->attach_flags & LXC_ATTACH_TERMINAL)
-               lxc_attach_terminal_close_pts(&terminal);
+               lxc_mainloop_close(&descr);
 
-       /* Tell grandparent the pid of the pid of the newly created child. */
-       ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid));
-       if (ret != sizeof(pid)) {
-               /* If this really happens here, this is very unfortunate, since
-                * the parent will not know the pid of the attached process and
-                * will not be able to wait for it (and we won't either due to
-                * CLONE_PARENT) so the parent won't be able to reap it and the
-                * attached process will remain a zombie.
-                */
-               shutdown(ipc_sockets[1], SHUT_RDWR);
-               lxc_proc_put_context_info(init_ctx);
-               _exit(EXIT_FAILURE);
+on_error:
+       if (ipc_sockets[0] >= 0) {
+               shutdown(ipc_sockets[0], SHUT_RDWR);
+               close(ipc_sockets[0]);
        }
 
-       TRACE("Sending pid %d of attached process", pid);
+       if (to_cleanup_pid > 0)
+               (void)wait_for_pid(to_cleanup_pid);
+
+       if (options->attach_flags & LXC_ATTACH_TERMINAL) {
+               lxc_terminal_delete(&terminal);
+               lxc_terminal_conf_free(&terminal);
+       }
 
-       /* The rest is in the hands of the initial and the attached process. */
        lxc_proc_put_context_info(init_ctx);
-       _exit(EXIT_SUCCESS);
+       return ret_parent;
 }
 
 int lxc_attach_run_command(void *payload)