X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fnspawn%2Fnspawn.c;h=80903a7aef0069b8a2b810edaa2133fcb0b685c0;hp=c0f99482c075b443bcb981f6db5f2d2bd422a1ce;hb=b3451bed41453304735b68726571d49fb676d466;hpb=88212f7bd19e30d73874aa160e789e300a643860;ds=sidebyside diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index c0f99482c..80903a7ae 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -62,6 +62,7 @@ #include "build.h" #include "fileio.h" #include "bus-util.h" +#include "ptyfwd.h" #ifndef TTY_GID #define TTY_GID 5 @@ -223,6 +224,9 @@ static int parse_argv(int argc, char *argv[]) { case 'S': arg_slice = strdup(optarg); + if (!arg_slice) + return log_oom(); + break; case 'M': @@ -314,11 +318,11 @@ static int parse_argv(int argc, char *argv[]) { r = strv_extend(x, a); if (r < 0) - return r; + return log_oom(); r = strv_extend(x, b); if (r < 0) - return r; + return log_oom(); break; } @@ -923,248 +927,6 @@ static int drop_capabilities(void) { return capability_bounding_set_drop(~arg_retain, false); } -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); - fd_nonblock(master, 1); - - signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (signal_fd < 0) { - log_error("signalfd(): %m"); - r = -errno; - goto finish; - } - - ep = epoll_create1(EPOLL_CLOEXEC); - if (ep < 0) { - log_error("Failed to create epoll: %m"); - r = -errno; - goto finish; - } - - /* 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; - stdout_ev.data.fd = STDOUT_FILENO; - - zero(master_ev); - master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET; - master_ev.data.fd = master; - - zero(signal_ev); - signal_ev.events = EPOLLIN; - signal_ev.data.fd = signal_fd; - - if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0) { - if (errno != EPERM) { - log_error("Failed to register stdout in epoll: %m"); - r = -errno; - goto finish; - } - /* stdout without epoll support. Likely redirected to regular file. */ - stdout_writable = true; - } - - if (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 register fds in epoll: %m"); - r = -errno; - goto finish; - } - - for (;;) { - struct epoll_event ev[16]; - ssize_t k; - int i, nfds; - - nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1); - if (nfds < 0) { - - if (errno == EINTR || errno == EAGAIN) - continue; - - log_error("epoll_wait(): %m"); - r = -errno; - goto finish; - } - - assert(nfds >= 1); - - for (i = 0; i < nfds; i++) { - if (ev[i].data.fd == STDIN_FILENO) { - - if (ev[i].events & (EPOLLIN|EPOLLHUP)) - stdin_readable = true; - - } else if (ev[i].data.fd == STDOUT_FILENO) { - - if (ev[i].events & (EPOLLOUT|EPOLLHUP)) - stdout_writable = true; - - } else if (ev[i].data.fd == master) { - - if (ev[i].events & (EPOLLIN|EPOLLHUP)) - master_readable = true; - - if (ev[i].events & (EPOLLOUT|EPOLLHUP)) - master_writable = true; - - } else if (ev[i].data.fd == signal_fd) { - struct signalfd_siginfo sfsi; - ssize_t n; - - n = read(signal_fd, &sfsi, sizeof(sfsi)); - if (n != sizeof(sfsi)) { - - if (n >= 0) { - log_error("Failed to read from signalfd: invalid block size"); - r = -EIO; - goto finish; - } - - if (errno != EINTR && errno != EAGAIN) { - log_error("Failed to read from signalfd: %m"); - r = -errno; - goto finish; - } - } else { - - if (sfsi.ssi_signo == SIGWINCH) { - struct winsize ws; - - /* 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; - } - } - } - } - - while ((stdin_readable && in_buffer_full <= 0) || - (master_writable && in_buffer_full > 0) || - (master_readable && out_buffer_full <= 0) || - (stdout_writable && out_buffer_full > 0)) { - - if (stdin_readable && in_buffer_full < LINE_MAX) { - - k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full); - if (k < 0) { - - if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO) - stdin_readable = false; - else { - log_error("read(): %m"); - r = -errno; - goto finish; - } - } else - in_buffer_full += (size_t) k; - } - - if (master_writable && in_buffer_full > 0) { - - k = write(master, in_buffer, in_buffer_full); - if (k < 0) { - - if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO) - master_writable = false; - else { - log_error("write(): %m"); - r = -errno; - goto finish; - } - - } else { - assert(in_buffer_full >= (size_t) k); - memmove(in_buffer, in_buffer + k, in_buffer_full - k); - in_buffer_full -= k; - } - } - - if (master_readable && out_buffer_full < LINE_MAX) { - - k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full); - if (k < 0) { - - if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO) - master_readable = false; - else { - log_error("read(): %m"); - r = -errno; - goto finish; - } - } else - out_buffer_full += (size_t) k; - } - - if (stdout_writable && out_buffer_full > 0) { - - k = write(STDOUT_FILENO, out_buffer, out_buffer_full); - if (k < 0) { - - if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO) - stdout_writable = false; - else { - log_error("write(): %m"); - r = -errno; - goto finish; - } - - } else { - assert(out_buffer_full >= (size_t) k); - memmove(out_buffer, out_buffer + k, out_buffer_full - k); - out_buffer_full -= k; - } - } - } - } - -finish: - if (ep >= 0) - close_nointr_nofail(ep); - - if (signal_fd >= 0) - close_nointr_nofail(signal_fd); - - return r; -} - static int register_machine(void) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_unref_ sd_bus *bus = NULL; @@ -1217,11 +979,8 @@ int main(int argc, char *argv[]) { _cleanup_close_ int master = -1; int 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 }; + _cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 }; _cleanup_fdset_free_ FDSet *fds = NULL; log_parse_environment(); @@ -1316,24 +1075,13 @@ int main(int argc, char *argv[]) { goto finish; } - log_info("Spawning namespace container on %s (console is %s).", arg_directory, console); - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) - ioctl(master, TIOCSWINSZ, &ws); + log_info("Spawning container %s on %s. Press ^] three times within 1s to abort execution.", arg_machine, arg_directory); if (unlockpt(master) < 0) { log_error("Failed to unlock tty: %m"); goto finish; } - if (tcgetattr(STDIN_FILENO, &saved_attr) >= 0) { - saved_attr_valid = true; - - 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."); goto finish; @@ -1347,18 +1095,6 @@ int main(int argc, char *argv[]) { for (;;) { siginfo_t status; - int pipefd[2], pipefd2[2]; - - if (pipe2(pipefd, O_NONBLOCK|O_CLOEXEC) < 0) { - log_error("pipe2(): %m"); - goto finish; - } - - if (pipe2(pipefd2, O_NONBLOCK|O_CLOEXEC) < 0) { - log_error("pipe2(): %m"); - close_pipe(pipefd); - goto finish; - } pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL); if (pid < 0) { @@ -1393,21 +1129,9 @@ int main(int argc, char *argv[]) { if (envp[n_env]) n_env ++; - /* Wait for the parent process to log our PID */ - close_nointr_nofail(pipefd[1]); - fd_wait_for_event(pipefd[0], POLLHUP, -1); - close_nointr_nofail(pipefd[0]); - close_nointr_nofail(master); master = -1; - if (saved_attr_valid) { - if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) { - log_error("Failed to set terminal attributes: %m"); - goto child_fail; - } - } - close_nointr(STDIN_FILENO); close_nointr(STDOUT_FILENO); close_nointr(STDERR_FILENO); @@ -1447,8 +1171,6 @@ int main(int argc, char *argv[]) { goto child_fail; } - close_pipe(pipefd2); - r = register_machine(); if (r < 0) goto finish; @@ -1657,25 +1379,23 @@ int main(int argc, char *argv[]) { _exit(EXIT_FAILURE); } - log_info("Init process in the container running as PID %lu.", (unsigned long) pid); - close_nointr_nofail(pipefd[0]); - close_nointr_nofail(pipefd[1]); - - /* Wait for the child process to establish cgroup hierarchy */ - close_nointr_nofail(pipefd2[1]); - fd_wait_for_event(pipefd2[0], POLLHUP, -1); - close_nointr_nofail(pipefd2[0]); - fdset_free(fds); fds = NULL; - if (process_pty(master, pid, &mask) < 0) - goto finish; + k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3); + if (k < 0) { + r = EXIT_FAILURE; + break; + } - if (saved_attr_valid) - tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr); + putc('\n', stdout); + + /* Kill if it is not dead yet anyway */ + kill(pid, SIGKILL); k = wait_for_terminate(pid, &status); + pid = 0; + if (k < 0) { r = EXIT_FAILURE; break; @@ -1684,40 +1404,35 @@ int main(int argc, char *argv[]) { if (status.si_code == CLD_EXITED) { r = status.si_status; if (status.si_status != 0) { - log_error("Container failed with error code %i.", status.si_status); + log_error("Container %s failed with error code %i.", arg_machine, status.si_status); break; } - log_debug("Container exited successfully."); + log_debug("Container %s exited successfully.", arg_machine); break; } else if (status.si_code == CLD_KILLED && status.si_status == SIGINT) { - log_info("Container has been shut down."); + log_info("Container %s has been shut down.", arg_machine); r = 0; break; } else if (status.si_code == CLD_KILLED && status.si_status == SIGHUP) { - log_info("Container is being rebooted."); + log_info("Container %s is being rebooted.", arg_machine); continue; } else if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) { - log_error("Container terminated by signal %s.", signal_to_string(status.si_status)); + log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status)); r = EXIT_FAILURE; break; } else { - log_error("Container failed due to unknown reason."); + log_error("Container %s failed due to unknown reason.", arg_machine); r = EXIT_FAILURE; break; } } finish: - if (saved_attr_valid) - tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr); - - close_pipe(kmsg_socket_pair); - if (pid > 0) kill(pid, SIGKILL);