From: Christian Brauner Date: Wed, 27 Jan 2021 19:24:57 +0000 (+0100) Subject: attach: invert child/parent handling X-Git-Tag: lxc-5.0.0~314^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f25e72fb01a60dbe2d3be195582bab1fbd9707c;p=thirdparty%2Flxc.git attach: invert child/parent handling This makes it more consistent with th rest of the shared library. Cc: stable-4.0 Signed-off-by: Christian Brauner --- diff --git a/src/lxc/attach.c b/src/lxc/attach.c index cfddf9b36..f1a59b330 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -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)