chiark / gitweb /
add Belarussian mapping: simple, 'by' and 'by'
[elogind.git] / src / nspawn / nspawn.c
index 59171abff3cdb6c2e14ffbf50d9fb372baed362e..1f3bda5b4aded530b46484e6202cc0b0f3cd50e1 100644 (file)
@@ -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;
 }