X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fnspawn%2Fnspawn.c;h=1f3bda5b4aded530b46484e6202cc0b0f3cd50e1;hb=40b5bbb91e1077ff0b7d88d7cbcff0228e282616;hp=59171abff3cdb6c2e14ffbf50d9fb372baed362e;hpb=3c957acf86b9ec482a527528987b2462a32e0d07;p=elogind.git diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 59171abff..1f3bda5b4 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -55,6 +55,7 @@ #include "loopback-setup.h" #include "sd-id128.h" #include "dev-setup.h" +#include "fdset.h" typedef enum LinkJournal { LINK_NO, @@ -816,13 +817,18 @@ static int is_os_tree(const char *path) { return r < 0 ? 0 : 1; } -static int process_pty(int master, sigset_t *mask) { +static int process_pty(int master, pid_t pid, sigset_t *mask) { char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; size_t in_buffer_full = 0, out_buffer_full = 0; struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev; bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false; int ep = -1, signal_fd = -1, r; + bool tried_orderly_shutdown = false; + + assert(master >= 0); + assert(pid > 0); + assert(mask); fd_nonblock(STDIN_FILENO, 1); fd_nonblock(STDOUT_FILENO, 1); @@ -842,9 +848,19 @@ static int process_pty(int master, sigset_t *mask) { goto finish; } - zero(stdin_ev); - stdin_ev.events = EPOLLIN|EPOLLET; - stdin_ev.data.fd = STDIN_FILENO; + /* We read from STDIN only if this is actually a TTY, + * otherwise we assume non-interactivity. */ + if (isatty(STDIN_FILENO)) { + zero(stdin_ev); + stdin_ev.events = EPOLLIN|EPOLLET; + stdin_ev.data.fd = STDIN_FILENO; + + if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0) { + log_error("Failed to register STDIN in epoll: %m"); + r = -errno; + goto finish; + } + } zero(stdout_ev); stdout_ev.events = EPOLLOUT|EPOLLET; @@ -858,11 +874,10 @@ static int process_pty(int master, sigset_t *mask) { signal_ev.events = EPOLLIN; signal_ev.data.fd = signal_fd; - if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 || - epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 || + if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 || epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 || epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) { - log_error("Failed to regiser fds in epoll: %m"); + log_error("Failed to register fds in epoll: %m"); r = -errno; goto finish; } @@ -930,6 +945,14 @@ static int process_pty(int master, sigset_t *mask) { /* The window size changed, let's forward that. */ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) ioctl(master, TIOCSWINSZ, &ws); + } else if (sfsi.ssi_signo == SIGTERM && arg_boot && !tried_orderly_shutdown) { + + log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination."); + + /* This only works for systemd... */ + tried_orderly_shutdown = true; + kill(pid, SIGRTMIN+3); + } else { r = 0; goto finish; @@ -1032,13 +1055,14 @@ int main(int argc, char *argv[]) { int r = EXIT_FAILURE, k; char *oldcg = NULL, *newcg = NULL; char **controller = NULL; - int master = -1; + int master = -1, n_fd_passed; const char *console = NULL; struct termios saved_attr, raw_attr; sigset_t mask; bool saved_attr_valid = false; struct winsize ws; int kmsg_socket_pair[2] = { -1, -1 }; + FDSet *fds = NULL; log_parse_environment(); log_open(); @@ -1083,6 +1107,18 @@ int main(int argc, char *argv[]) { goto finish; } + log_close(); + n_fd_passed = sd_listen_fds(false); + if (n_fd_passed > 0) { + k = fdset_new_listen_fds(&fds, false); + if (k < 0) { + log_error("Failed to collect file descriptors: %s", strerror(-k)); + goto finish; + } + } + fdset_close_others(fds); + log_open(); + k = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &oldcg); if (k < 0) { log_error("Failed to determine current cgroup: %s", strerror(-k)); @@ -1128,16 +1164,13 @@ int main(int argc, char *argv[]) { goto finish; } - if (tcgetattr(STDIN_FILENO, &saved_attr) < 0) { - log_error("Failed to get terminal attributes: %m"); - goto finish; - } - - saved_attr_valid = true; + if (tcgetattr(STDIN_FILENO, &saved_attr) >= 0) { + saved_attr_valid = true; - raw_attr = saved_attr; - cfmakeraw(&raw_attr); - raw_attr.c_lflag &= ~ECHO; + raw_attr = saved_attr; + cfmakeraw(&raw_attr); + raw_attr.c_lflag &= ~ECHO; + } if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) { log_error("Failed to create kmsg socket pair"); @@ -1151,9 +1184,11 @@ int main(int argc, char *argv[]) { for (;;) { siginfo_t status; - if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) { - log_error("Failed to set terminal attributes: %m"); - goto finish; + if (saved_attr_valid) { + if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) { + log_error("Failed to set terminal attributes: %m"); + goto finish; + } } pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL); @@ -1172,6 +1207,7 @@ int main(int argc, char *argv[]) { const char *home = NULL; uid_t uid = (uid_t) -1; gid_t gid = (gid_t) -1; + unsigned n_env = 0; const char *envp[] = { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */ @@ -1180,28 +1216,45 @@ int main(int argc, char *argv[]) { NULL, /* USER */ NULL, /* LOGNAME */ NULL, /* container_uuid */ + NULL, /* LISTEN_FDS */ + NULL, /* LISTEN_PID */ NULL }; envp[2] = strv_find_prefix(environ, "TERM="); + n_env = 3; close_nointr_nofail(master); + master = -1; close_nointr(STDIN_FILENO); close_nointr(STDOUT_FILENO); close_nointr(STDERR_FILENO); - close_all_fds(&kmsg_socket_pair[1], 1); + close_nointr_nofail(kmsg_socket_pair[0]); + kmsg_socket_pair[0] = -1; reset_all_signal_handlers(); assert_se(sigemptyset(&mask) == 0); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); - if (open_terminal(console, O_RDWR) != STDIN_FILENO || - dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO || - dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) + k = open_terminal(console, O_RDWR); + if (k != STDIN_FILENO) { + if (k >= 0) { + close_nointr_nofail(k); + k = -EINVAL; + } + + log_error("Failed to open console: %s", strerror(-k)); + goto child_fail; + } + + if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO || + dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) { + log_error("Failed to duplicate console: %m"); goto child_fail; + } if (setsid() < 0) { log_error("setsid() failed: %m"); @@ -1248,6 +1301,7 @@ int main(int argc, char *argv[]) { goto child_fail; close_nointr_nofail(kmsg_socket_pair[1]); + kmsg_socket_pair[1] = -1; if (setup_boot_id(arg_directory) < 0) goto child_fail; @@ -1346,15 +1400,29 @@ int main(int argc, char *argv[]) { } } - if ((asprintf((char**)(envp + 3), "HOME=%s", home ? home: "/root") < 0) || - (asprintf((char**)(envp + 4), "USER=%s", arg_user ? arg_user : "root") < 0) || - (asprintf((char**)(envp + 5), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) { + if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) || + (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) || + (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) { log_oom(); goto child_fail; } if (arg_uuid) { - if (asprintf((char**)(envp + 6), "container_uuid=%s", arg_uuid) < 0) { + if (asprintf((char**)(envp + n_env++), "container_uuid=%s", arg_uuid) < 0) { + log_oom(); + goto child_fail; + } + } + + if (fdset_size(fds) > 0) { + k = fdset_cloexec(fds, false); + if (k < 0) { + log_error("Failed to unset O_CLOEXEC for file descriptors."); + goto child_fail; + } + + if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", n_fd_passed) < 0) || + (asprintf((char **)(envp + n_env++), "LISTEN_PID=%lu", (unsigned long) getpid()) < 0)) { log_oom(); goto child_fail; } @@ -1393,9 +1461,11 @@ int main(int argc, char *argv[]) { _exit(EXIT_FAILURE); } - if (process_pty(master, &mask) < 0) - goto finish; + fdset_free(fds); + fds = NULL; + if (process_pty(master, pid, &mask) < 0) + goto finish; if (saved_attr_valid) tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr); @@ -1457,5 +1527,7 @@ finish: free(oldcg); free(newcg); + fdset_free(fds); + return r; }