Unverified Commit 0fa84a8c by Stéphane Graber Committed by GitHub

Merge pull request #3637 from brauner/2021-01-27/fixes

attach: fixes
parents 665fcdf9 b4959848
...@@ -109,8 +109,7 @@ static inline void lxc_proc_close_ns_fd(struct lxc_proc_context_info *ctx) ...@@ -109,8 +109,7 @@ static inline void lxc_proc_close_ns_fd(struct lxc_proc_context_info *ctx)
static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx) static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx)
{ {
free(ctx->lsm_label); free_disarm(ctx->lsm_label);
ctx->lsm_label = NULL;
if (ctx->container) { if (ctx->container) {
lxc_container_put(ctx->container); lxc_container_put(ctx->container);
...@@ -645,7 +644,7 @@ static void lxc_put_attach_clone_payload(struct attach_clone_payload *p) ...@@ -645,7 +644,7 @@ static void lxc_put_attach_clone_payload(struct attach_clone_payload *p)
} }
} }
static int attach_child_main(struct attach_clone_payload *payload) __noreturn static void do_attach(struct attach_clone_payload *payload)
{ {
int lsm_fd, ret; int lsm_fd, ret;
uid_t new_uid; uid_t new_uid;
...@@ -818,8 +817,7 @@ static int attach_child_main(struct attach_clone_payload *payload) ...@@ -818,8 +817,7 @@ static int attach_child_main(struct attach_clone_payload *payload)
goto on_error; goto on_error;
} }
close(payload->ipc_socket); close_prot_errno_disarm(payload->ipc_socket);
payload->ipc_socket = -EBADF;
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
payload->init_ctx = NULL; payload->init_ctx = NULL;
...@@ -833,13 +831,16 @@ static int attach_child_main(struct attach_clone_payload *payload) ...@@ -833,13 +831,16 @@ static int attach_child_main(struct attach_clone_payload *payload)
* may want to make sure the fds are closed, for example. * may want to make sure the fds are closed, for example.
*/ */
if (options->stdin_fd >= 0 && options->stdin_fd != STDIN_FILENO) if (options->stdin_fd >= 0 && options->stdin_fd != STDIN_FILENO)
(void)dup2(options->stdin_fd, STDIN_FILENO); if (dup2(options->stdin_fd, STDIN_FILENO))
DEBUG("Failed to replace stdin with %d", options->stdin_fd);
if (options->stdout_fd >= 0 && options->stdout_fd != STDOUT_FILENO) if (options->stdout_fd >= 0 && options->stdout_fd != STDOUT_FILENO)
(void)dup2(options->stdout_fd, STDOUT_FILENO); if (dup2(options->stdout_fd, STDOUT_FILENO))
DEBUG("Failed to replace stdout with %d", options->stdin_fd);
if (options->stderr_fd >= 0 && options->stderr_fd != STDERR_FILENO) if (options->stderr_fd >= 0 && options->stderr_fd != STDERR_FILENO)
(void)dup2(options->stderr_fd, STDERR_FILENO); if (dup2(options->stderr_fd, STDERR_FILENO))
DEBUG("Failed to replace stderr with %d", options->stdin_fd);
/* close the old fds */ /* close the old fds */
if (options->stdin_fd > STDERR_FILENO) if (options->stdin_fd > STDERR_FILENO)
...@@ -893,6 +894,7 @@ static int attach_child_main(struct attach_clone_payload *payload) ...@@ -893,6 +894,7 @@ static int attach_child_main(struct attach_clone_payload *payload)
on_error: on_error:
lxc_put_attach_clone_payload(payload); lxc_put_attach_clone_payload(payload);
ERROR("Failed to attach to container");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
...@@ -952,9 +954,10 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -952,9 +954,10 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
void *exec_payload, lxc_attach_options_t *options, void *exec_payload, lxc_attach_options_t *options,
pid_t *attached_process) pid_t *attached_process)
{ {
__do_free char *cwd = NULL;
int i, ret, status; int i, ret, status;
int ipc_sockets[2]; int ipc_sockets[2];
char *cwd, *new_cwd; char *new_cwd;
signed long personality; signed long personality;
pid_t attached_pid, init_pid, pid; pid_t attached_pid, init_pid, pid;
struct lxc_proc_context_info *init_ctx; struct lxc_proc_context_info *init_ctx;
...@@ -962,6 +965,9 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -962,6 +965,9 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
struct lxc_conf *conf; struct lxc_conf *conf;
char *name, *lxcpath; char *name, *lxcpath;
struct attach_clone_payload payload = {0}; 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); ret = access("/proc/self/ns", X_OK);
if (ret) if (ret)
...@@ -1030,7 +1036,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1030,7 +1036,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
if (options->namespaces == -1) { if (options->namespaces == -1) {
ERROR("Failed to automatically determine the " ERROR("Failed to automatically determine the "
"namespaces which the container uses"); "namespaces which the container uses");
free(cwd);
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
...@@ -1080,7 +1085,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1080,7 +1085,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
close(init_ctx->ns_fd[j]); close(init_ctx->ns_fd[j]);
free(cwd);
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
...@@ -1089,7 +1093,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1089,7 +1093,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
ret = lxc_attach_terminal(name, lxcpath, conf, &terminal); ret = lxc_attach_terminal(name, lxcpath, conf, &terminal);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to setup new terminal"); ERROR("Failed to setup new terminal");
free(cwd);
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
...@@ -1135,7 +1138,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1135,7 +1138,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
if (ret < 0) { if (ret < 0) {
SYSERROR("Could not set up required IPC mechanism for attaching"); SYSERROR("Could not set up required IPC mechanism for attaching");
free(cwd);
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
...@@ -1150,280 +1152,272 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1150,280 +1152,272 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
SYSERROR("Failed to create first subprocess"); SYSERROR("Failed to create first subprocess");
free(cwd);
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
if (pid) { if (pid == 0) {
int ret_parent = -1;
pid_t to_cleanup_pid = pid;
struct lxc_epoll_descr descr = {0};
/* close unneeded file descriptors */ /* close unneeded file descriptors */
close(ipc_sockets[1]); close_prot_errno_disarm(ipc_sockets[0]);
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_TERMINAL) {
if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { lxc_attach_terminal_close_ptx(&terminal);
/* lxc_attach_terminal_close_peer(&terminal);
* If this is the unified hierarchy cgroup_attach() is lxc_attach_terminal_close_log(&terminal);
* 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;
if (!cgroup_ops->attach(cgroup_ops, conf, name, lxcpath, pid))
goto on_error;
}
TRACE("Moved intermediate process %d into container's cgroups", pid);
} }
/* Setup /proc limits */ /* Wait for the parent to have setup cgroups. */
if (!lxc_list_empty(&conf->procs)) { ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status));
ret = setup_proc_filesystem(&conf->procs, pid); if (ret != sizeof(status)) {
if (ret < 0) shutdown(ipc_sockets[1], SHUT_RDWR);
goto on_error; lxc_proc_put_context_info(init_ctx);
_exit(EXIT_FAILURE);
} }
/* Setup resource limits */ TRACE("Intermediate process starting to initialize");
if (!lxc_list_empty(&conf->limits)) {
ret = setup_resource_limits(&conf->limits, pid); /* Attach now, create another subprocess later, since pid namespaces
if (ret < 0) * only really affect the children of the current process.
goto on_error; */
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) { /* close namespace file descriptors */
ret = lxc_attach_terminal_mainloop_init(&terminal, &descr); lxc_proc_close_ns_fd(init_ctx);
if (ret < 0)
goto on_error;
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);
} }
/* Let the child process know to go ahead. */ /* Create attached process. */
status = 0; payload.ipc_socket = ipc_sockets[1];
ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); payload.options = options;
if (ret != sizeof(status)) payload.init_ctx = init_ctx;
goto close_mainloop; 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);
}
TRACE("Told intermediate process to start initializing"); 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);
}
}
/* Get pid of attached process from intermediate process. */ do_attach(&payload);
ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid)); }
if (ret != sizeof(attached_pid))
goto close_mainloop;
TRACE("Received pid %d of attached process in parent pid namespace", attached_pid); if (options->attach_flags & LXC_ATTACH_TERMINAL)
lxc_attach_terminal_close_pts(&terminal);
/* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */ /* Tell grandparent the pid of the pid of the newly created child. */
if (options->stdin_fd == 0) { ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid));
signal(SIGINT, SIG_IGN); if (ret != sizeof(pid)) {
signal(SIGQUIT, SIG_IGN); /* 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);
} }
/* Reap intermediate process. */ TRACE("Sending pid %d of attached process", pid);
ret = wait_for_pid(pid);
if (ret < 0)
goto close_mainloop;
TRACE("Intermediate process %d exited", pid); /* The rest is in the hands of the initial and the attached process. */
lxc_proc_put_context_info(init_ctx);
_exit(EXIT_SUCCESS);
}
/* We will always have to reap the attached process now. */ to_cleanup_pid = pid;
to_cleanup_pid = attached_pid;
/* Open LSM fd and send it to child. */ /* close unneeded file descriptors */
if ((options->namespaces & CLONE_NEWNS) && close(ipc_sockets[1]);
(options->attach_flags & LXC_ATTACH_LSM) && free_disarm(cwd);
init_ctx->lsm_label) { lxc_proc_close_ns_fd(init_ctx);
int labelfd; if (options->attach_flags & LXC_ATTACH_TERMINAL)
bool on_exec; lxc_attach_terminal_close_pts(&terminal);
ret = -1; /* Attach to cgroup, if requested. */
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
labelfd = init_ctx->lsm_ops->process_label_fd_get(init_ctx->lsm_ops, /*
attached_pid, on_exec); * If this is the unified hierarchy cgroup_attach() is
if (labelfd < 0) * enough.
goto close_mainloop; */
ret = cgroup_attach(conf, name, lxcpath, pid);
if (ret) {
call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
TRACE("Opened LSM label file descriptor %d", labelfd); cgroup_ops = cgroup_init(conf);
if (!cgroup_ops)
goto on_error;
/* Send child fd of the LSM security module to write to. */ if (!cgroup_ops->attach(cgroup_ops, conf, name, lxcpath, pid))
ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0); goto on_error;
if (ret <= 0) { }
if (ret < 0) TRACE("Moved intermediate process %d into container's cgroups", pid);
SYSERROR("Failed to send lsm label fd"); }
close(labelfd); /* Setup /proc limits */
goto close_mainloop; if (!lxc_list_empty(&conf->procs)) {
} ret = setup_proc_filesystem(&conf->procs, pid);
if (ret < 0)
goto on_error;
close(labelfd); TRACE("Setup /proc/%d settings", pid);
TRACE("Sent LSM label file descriptor %d to child", labelfd); }
}
if (conf->seccomp.seccomp) { /* Setup resource limits */
ret = lxc_seccomp_recv_notifier_fd(&conf->seccomp, ipc_sockets[0]); if (!lxc_list_empty(&conf->limits)) {
if (ret < 0) ret = setup_resource_limits(&conf->limits, pid);
goto close_mainloop; if (ret < 0)
goto on_error;
ret = lxc_seccomp_add_notifier(name, lxcpath, &conf->seccomp); TRACE("Setup resource limits");
if (ret < 0) }
goto close_mainloop;
}
/* We're done, the child process should now execute whatever it if (options->attach_flags & LXC_ATTACH_TERMINAL) {
* is that the user requested. The parent can now track it with ret = lxc_attach_terminal_mainloop_init(&terminal, &descr);
* waitpid() or similar. if (ret < 0)
*/ goto on_error;
*attached_process = attached_pid; TRACE("Initialized terminal mainloop");
}
/* Now shut down communication with child, we're done. */ /* Let the child process know to go ahead. */
shutdown(ipc_sockets[0], SHUT_RDWR); status = 0;
close(ipc_sockets[0]); ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
ipc_sockets[0] = -1; if (ret != sizeof(status))
goto close_mainloop;
ret_parent = 0; TRACE("Told intermediate process to start initializing");
to_cleanup_pid = -1;
if (options->attach_flags & LXC_ATTACH_TERMINAL) { /* Get pid of attached process from intermediate process. */
ret = lxc_mainloop(&descr, -1); ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid));
if (ret < 0) { if (ret != sizeof(attached_pid))
ret_parent = -1; goto close_mainloop;
to_cleanup_pid = attached_pid;
}
}
close_mainloop: TRACE("Received pid %d of attached process in parent pid namespace", attached_pid);
if (options->attach_flags & LXC_ATTACH_TERMINAL)
lxc_mainloop_close(&descr);
on_error: /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
if (ipc_sockets[0] >= 0) { if (options->stdin_fd == 0) {
shutdown(ipc_sockets[0], SHUT_RDWR); signal(SIGINT, SIG_IGN);
close(ipc_sockets[0]); signal(SIGQUIT, SIG_IGN);
} }
if (to_cleanup_pid > 0) /* Reap intermediate process. */
(void)wait_for_pid(to_cleanup_pid); ret = wait_for_pid(pid);
if (ret < 0)
goto close_mainloop;
if (options->attach_flags & LXC_ATTACH_TERMINAL) { TRACE("Intermediate process %d exited", pid);
lxc_terminal_delete(&terminal);
lxc_terminal_conf_free(&terminal);
}
lxc_proc_put_context_info(init_ctx); /* We will always have to reap the attached process now. */
return ret_parent; to_cleanup_pid = attached_pid;
}
/* close unneeded file descriptors */ /* Open LSM fd and send it to child. */
close_prot_errno_disarm(ipc_sockets[0]); if ((options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) {
__do_close int labelfd = -EBADF;
bool on_exec;
if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = -1;
lxc_attach_terminal_close_ptx(&terminal); on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false;
lxc_attach_terminal_close_peer(&terminal); labelfd = init_ctx->lsm_ops->process_label_fd_get(init_ctx->lsm_ops,
lxc_attach_terminal_close_log(&terminal); attached_pid, on_exec);
} if (labelfd < 0)
goto close_mainloop;
/* Wait for the parent to have setup cgroups. */ TRACE("Opened LSM label file descriptor %d", labelfd);
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("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");
goto close_mainloop;
}
/* Attach now, create another subprocess later, since pid namespaces TRACE("Sent LSM label file descriptor %d to child", labelfd);
* 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 namespace file descriptors */ if (conf->seccomp.seccomp) {
lxc_proc_close_ns_fd(init_ctx); ret = lxc_seccomp_recv_notifier_fd(&conf->seccomp, ipc_sockets[0]);
if (ret < 0)
goto close_mainloop;
/* Attach succeeded, try to cwd. */ ret = lxc_seccomp_add_notifier(name, lxcpath, &conf->seccomp);
if (options->initial_cwd)
new_cwd = options->initial_cwd;
else
new_cwd = cwd;
if (new_cwd) {
ret = chdir(new_cwd);
if (ret < 0) if (ret < 0)
WARN("Could not change directory to \"%s\"", new_cwd); goto close_mainloop;
} }
free(cwd);
/* Create attached process. */ /* We're done, the child process should now execute whatever it
payload.ipc_socket = ipc_sockets[1]; * is that the user requested. The parent can now track it with
payload.options = options; * waitpid() or similar.
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); *attached_process = attached_pid;
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);
}
if (pid == 0) { /* Now shut down communication with child, we're done. */
if (options->attach_flags & LXC_ATTACH_TERMINAL) { shutdown(ipc_sockets[0], SHUT_RDWR);
ret = lxc_terminal_signal_sigmask_safe_blocked(&terminal); close(ipc_sockets[0]);
if (ret < 0) { ipc_sockets[0] = -1;
SYSERROR("Failed to reset signal mask");
_exit(EXIT_FAILURE);
}
}
ret = attach_child_main(&payload); ret_parent = 0;
if (ret < 0) to_cleanup_pid = -1;
ERROR("Failed to exec");
_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) 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. */ on_error:
ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid)); if (ipc_sockets[0] >= 0) {
if (ret != sizeof(pid)) { shutdown(ipc_sockets[0], SHUT_RDWR);
/* If this really happens here, this is very unfortunate, since close(ipc_sockets[0]);
* 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);
} }
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); lxc_proc_put_context_info(init_ctx);
_exit(EXIT_SUCCESS); return ret_parent;
} }
int lxc_attach_run_command(void *payload) int lxc_attach_run_command(void *payload)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment