chiark / gitweb /
core: make systemd.confirm_spawn=1 actually work
authorLennart Poettering <lennart@poettering.net>
Tue, 26 Jun 2012 10:16:18 +0000 (12:16 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 26 Jun 2012 10:21:43 +0000 (12:21 +0200)
This adds a timeout if the TTY cannot be acquired and makes sure we
always output the question to the console, never to the TTY of the
respective service.

TODO
src/core/execute.c
src/core/main.c
src/core/manager.c
src/shared/def.h
src/shared/util.c
src/shared/util.h
src/tty-ask-password-agent/tty-ask-password-agent.c

diff --git a/TODO b/TODO
index 25b9be4b4b380a28b0c54e897033b7823f4c1123..747a9f3357e3ec13b6b81f4ae3d5ee1e5a74fc67 100644 (file)
--- a/TODO
+++ b/TODO
@@ -25,6 +25,8 @@ Bugfixes:
 
 Features:
 
+* new dependency type to "group" services in a target
+
 * add switch to journalctl to only show data from current boot
 
 * change REquires=basic.target to RequisiteOverride=basic.target
index 01dceb0788f9a78b755e75728b95ec7f44be8865..c0e8f9e01302677ea1fd89016ea0885ce530b90d 100644 (file)
@@ -293,7 +293,8 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
                                      tty_path(context),
                                      i == EXEC_INPUT_TTY_FAIL,
                                      i == EXEC_INPUT_TTY_FORCE,
-                                     false)) < 0)
+                                     false,
+                                     (usec_t) -1)) < 0)
                         return fd;
 
                 if (fd != STDIN_FILENO) {
@@ -444,47 +445,45 @@ static int chown_terminal(int fd, uid_t uid) {
         return 0;
 }
 
-static int setup_confirm_stdio(const ExecContext *context,
-                               int *_saved_stdin,
+static int setup_confirm_stdio(int *_saved_stdin,
                                int *_saved_stdout) {
         int fd = -1, saved_stdin, saved_stdout = -1, r;
 
-        assert(context);
         assert(_saved_stdin);
         assert(_saved_stdout);
 
-        /* This returns positive EXIT_xxx return values instead of
-         * negative errno style values! */
-
-        if ((saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3)) < 0)
-                return EXIT_STDIN;
+        saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
+        if (saved_stdin < 0)
+                return -errno;
 
-        if ((saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3)) < 0) {
-                r = EXIT_STDOUT;
+        saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
+        if (saved_stdout < 0) {
+                r = errno;
                 goto fail;
         }
 
-        if ((fd = acquire_terminal(
-                             tty_path(context),
-                             context->std_input == EXEC_INPUT_TTY_FAIL,
-                             context->std_input == EXEC_INPUT_TTY_FORCE,
-                             false)) < 0) {
-                r = EXIT_STDIN;
+        fd = acquire_terminal(
+                        "/dev/console",
+                        false,
+                        false,
+                        false,
+                        DEFAULT_CONFIRM_USEC);
+        if (fd < 0) {
+                r = fd;
                 goto fail;
         }
 
-        if (chown_terminal(fd, getuid()) < 0) {
-                r = EXIT_STDIN;
+        r = chown_terminal(fd, getuid());
+        if (r < 0)
                 goto fail;
-        }
 
         if (dup2(fd, STDIN_FILENO) < 0) {
-                r = EXIT_STDIN;
+                r = -errno;
                 goto fail;
         }
 
         if (dup2(fd, STDOUT_FILENO) < 0) {
-                r = EXIT_STDOUT;
+                r = -errno;
                 goto fail;
         }
 
@@ -509,48 +508,70 @@ fail:
         return r;
 }
 
-static int restore_confirm_stdio(const ExecContext *context,
-                                 int *saved_stdin,
-                                 int *saved_stdout,
-                                 bool *keep_stdin,
-                                 bool *keep_stdout) {
+static int write_confirm_message(const char *format, ...) {
+        int fd;
+        va_list ap;
 
-        assert(context);
-        assert(saved_stdin);
-        assert(*saved_stdin >= 0);
-        assert(saved_stdout);
-        assert(*saved_stdout >= 0);
+        assert(format);
 
-        /* This returns positive EXIT_xxx return values instead of
-         * negative errno style values! */
+        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
 
-        if (is_terminal_input(context->std_input)) {
+        va_start(ap, format);
+        vdprintf(fd, format, ap);
+        va_end(ap);
 
-                /* The service wants terminal input. */
+        close_nointr_nofail(fd);
 
-                *keep_stdin = true;
-                *keep_stdout =
-                        context->std_output == EXEC_OUTPUT_INHERIT ||
-                        context->std_output == EXEC_OUTPUT_TTY;
+        return 0;
+}
 
-        } else {
-                /* If the service doesn't want a controlling terminal,
-                 * then we need to get rid entirely of what we have
-                 * already. */
+static int restore_confirm_stdio(int *saved_stdin,
+                                 int *saved_stdout) {
 
-                if (release_terminal() < 0)
-                        return EXIT_STDIN;
+        int r = 0;
 
+        assert(saved_stdin);
+        assert(saved_stdout);
+
+        release_terminal();
+
+        if (*saved_stdin >= 0)
                 if (dup2(*saved_stdin, STDIN_FILENO) < 0)
-                        return EXIT_STDIN;
+                        r = -errno;
 
+        if (*saved_stdout >= 0)
                 if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
-                        return EXIT_STDOUT;
+                        r = -errno;
 
-                *keep_stdout = *keep_stdin = false;
-        }
+        if (*saved_stdin >= 0)
+                close_nointr_nofail(*saved_stdin);
 
-        return 0;
+        if (*saved_stdout >= 0)
+                close_nointr_nofail(*saved_stdout);
+
+        return r;
+}
+
+static int ask_for_confirmation(char *response, char **argv) {
+        int saved_stdout = -1, saved_stdin = -1, r;
+        char *line;
+
+        r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
+        if (r < 0)
+                return r;
+
+        line = exec_command_line(argv);
+        if (!line)
+                return -ENOMEM;
+
+        r = ask(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
+        free(line);
+
+        restore_confirm_stdio(&saved_stdin, &saved_stdout);
+
+        return r;
 }
 
 static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
@@ -952,7 +973,8 @@ int exec_spawn(ExecCommand *command,
         if (!argv)
                 argv = command->argv;
 
-        if (!(line = exec_command_line(argv))) {
+        line = exec_command_line(argv);
+        if (!line) {
                 r = -ENOMEM;
                 goto fail_parent;
         }
@@ -979,8 +1001,7 @@ int exec_spawn(ExecCommand *command,
                 gid_t gid = (gid_t) -1;
                 char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
                 unsigned n_env = 0;
-                int saved_stdout = -1, saved_stdin = -1;
-                bool keep_stdout = false, keep_stdin = false, set_access = false;
+                bool set_access = false;
 
                 /* child */
 
@@ -1050,44 +1071,24 @@ int exec_spawn(ExecCommand *command,
 
                 exec_context_tty_reset(context);
 
-                /* We skip the confirmation step if we shall not apply the TTY */
-                if (confirm_spawn &&
-                    (!is_terminal_input(context->std_input) || apply_tty_stdin)) {
+                if (confirm_spawn) {
                         char response;
 
-                        /* Set up terminal for the question */
-                        if ((r = setup_confirm_stdio(context,
-                                                     &saved_stdin, &saved_stdout))) {
-                                err = -errno;
-                                goto fail_child;
-                        }
-
-                        /* Now ask the question. */
-                        if (!(line = exec_command_line(argv))) {
-                                err = -ENOMEM;
-                                r = EXIT_MEMORY;
-                                goto fail_child;
-                        }
-
-                        r = ask(&response, "yns", "Execute %s? [Yes, No, Skip] ", line);
-                        free(line);
-
-                        if (r < 0 || response == 'n') {
+                        err = ask_for_confirmation(&response, argv);
+                        if (err == -ETIMEDOUT)
+                                write_confirm_message("Confirmation question timed out, assuming positive response.\n");
+                        else if (err < 0)
+                                write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-err));
+                        else if (response == 's') {
+                                write_confirm_message("Skipping execution.\n");
                                 err = -ECANCELED;
                                 r = EXIT_CONFIRM;
                                 goto fail_child;
-                        } else if (response == 's') {
+                        } else if (response == 'n') {
+                                write_confirm_message("Failing execution.\n");
                                 err = r = 0;
                                 goto fail_child;
                         }
-
-                        /* Release terminal for the question */
-                        if ((r = restore_confirm_stdio(context,
-                                                       &saved_stdin, &saved_stdout,
-                                                       &keep_stdin, &keep_stdout))) {
-                                err = -errno;
-                                goto fail_child;
-                        }
                 }
 
                 /* If a socket is connected to STDIN/STDOUT/STDERR, we
@@ -1095,20 +1096,16 @@ int exec_spawn(ExecCommand *command,
                 if (socket_fd >= 0)
                         fd_nonblock(socket_fd, false);
 
-                if (!keep_stdin) {
-                        err = setup_input(context, socket_fd, apply_tty_stdin);
-                        if (err < 0) {
-                                r = EXIT_STDIN;
-                                goto fail_child;
-                        }
+                err = setup_input(context, socket_fd, apply_tty_stdin);
+                if (err < 0) {
+                        r = EXIT_STDIN;
+                        goto fail_child;
                 }
 
-                if (!keep_stdout) {
-                        err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
-                        if (err < 0) {
-                                r = EXIT_STDOUT;
-                                goto fail_child;
-                        }
+                err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+                if (err < 0) {
+                        r = EXIT_STDOUT;
+                        goto fail_child;
                 }
 
                 err = setup_error(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
@@ -1439,12 +1436,6 @@ int exec_spawn(ExecCommand *command,
                 strv_free(files_env);
                 strv_free(final_argv);
 
-                if (saved_stdin >= 0)
-                        close_nointr_nofail(saved_stdin);
-
-                if (saved_stdout >= 0)
-                        close_nointr_nofail(saved_stdout);
-
                 _exit(r);
         }
 
index 8f27a714cc01d35bc00cda5801d9f7fb52b2f221..7c66665e843957f931149bc12e166467f40ffae4 100644 (file)
@@ -169,7 +169,7 @@ _noreturn_ static void crash(int sig) {
                 else if (pid == 0) {
                         int fd, r;
 
-                        if ((fd = acquire_terminal("/dev/console", false, true, true)) < 0)
+                        if ((fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1)) < 0)
                                 log_error("Failed to acquire terminal: %s", strerror(-fd));
                         else if ((r = make_stdio(fd)) < 0)
                                 log_error("Failed to duplicate terminal fd: %s", strerror(-r));
index dedcb74be6bbb9e8677489a2e7ace6d2362d9dc7..9dfcb98de930f674181345d41a3c0bbd63e6c5a5 100644 (file)
@@ -2005,6 +2005,9 @@ void manager_check_finished(Manager *m) {
         /* Notify Type=idle units that we are done now */
         close_pipe(m->idle_pipe);
 
+        /* Turn off confirm spawn now */
+        m->confirm_spawn = false;
+
         if (dual_timestamp_is_set(&m->finish_timestamp))
                 return;
 
index be969fca2ec16e7cfa3229f27a12308c0efbf457..d021e660215009eb1974ead11e5b60bb6a028231 100644 (file)
@@ -26,6 +26,7 @@
 
 #define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC)
 #define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
+#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC)
 
 #define DEFAULT_EXIT_USEC (5*USEC_PER_MINUTE)
 
index 67ec5ae747777d7121bb7a9626ea85fd471bf8d9..041a63bb4baae8f070313daf806e22b007413afa 100644 (file)
@@ -2305,12 +2305,14 @@ int open_terminal(const char *name, int mode) {
          */
 
         for (;;) {
-                if ((fd = open(name, mode)) >= 0)
+                fd = open(name, mode);
+                if (fd >= 0)
                         break;
 
                 if (errno != EIO)
                         return -errno;
 
+                /* Max 1s in total */
                 if (c >= 20)
                         return -errno;
 
@@ -2321,7 +2323,8 @@ int open_terminal(const char *name, int mode) {
         if (fd < 0)
                 return -errno;
 
-        if ((r = isatty(fd)) < 0) {
+        r = isatty(fd);
+        if (r < 0) {
                 close_nointr_nofail(fd);
                 return -errno;
         }
@@ -2373,8 +2376,15 @@ int flush_fd(int fd) {
         }
 }
 
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
+int acquire_terminal(
+                const char *name,
+                bool fail,
+                bool force,
+                bool ignore_tiocstty_eperm,
+                usec_t timeout) {
+
         int fd = -1, notify = -1, r, wd = -1;
+        usec_t ts = 0;
 
         assert(name);
 
@@ -2391,27 +2401,35 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst
          * on the same tty as an untrusted user this should not be a
          * problem. (Which he probably should not do anyway.) */
 
+        if (timeout != (usec_t) -1)
+                ts = now(CLOCK_MONOTONIC);
+
         if (!fail && !force) {
-                if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+                notify = inotify_init1(IN_CLOEXEC | (timeout != (usec_t) -1 ? IN_NONBLOCK : 0));
+                if (notify < 0) {
                         r = -errno;
                         goto fail;
                 }
 
-                if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
+                wd = inotify_add_watch(notify, name, IN_CLOSE);
+                if (wd < 0) {
                         r = -errno;
                         goto fail;
                 }
         }
 
         for (;;) {
-                if (notify >= 0)
-                        if ((r = flush_fd(notify)) < 0)
+                if (notify >= 0) {
+                        r = flush_fd(notify);
+                        if (r < 0)
                                 goto fail;
+                }
 
                 /* We pass here O_NOCTTY only so that we can check the return
                  * value TIOCSCTTY and have a reliable way to figure out if we
                  * successfully became the controlling process of the tty */
-                if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+                fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+                if (fd < 0)
                         return fd;
 
                 /* First, try to get the tty */
@@ -2440,9 +2458,29 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst
                         ssize_t l;
                         struct inotify_event *e;
 
-                        if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
+                        if (timeout != (usec_t) -1) {
+                                usec_t n;
+
+                                n = now(CLOCK_MONOTONIC);
+                                if (ts + timeout < n) {
+                                        r = -ETIMEDOUT;
+                                        goto fail;
+                                }
+
+                                r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+                                if (r < 0)
+                                        goto fail;
+
+                                if (r == 0) {
+                                        r = -ETIMEDOUT;
+                                        goto fail;
+                                }
+                        }
+
+                        l = read(notify, inotify_buffer, sizeof(inotify_buffer));
+                        if (l < 0) {
 
-                                if (errno == EINTR)
+                                if (errno == EINTR || errno == EAGAIN)
                                         continue;
 
                                 r = -errno;
index 908593564ebd27459a736014240df5a9d1e6ed9a..47497b578c36001b5314b6c07d25947a0587b528 100644 (file)
@@ -321,7 +321,7 @@ int reset_terminal_fd(int fd, bool switch_to_text);
 int reset_terminal(const char *name);
 
 int open_terminal(const char *name, int mode);
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
 int release_terminal(void);
 
 int flush_fd(int fd);
index 7f537c27408a5692573a756c0a6e24c850859be6..0dec0629c0012daa327549ad4b7192bfc707b9bd 100644 (file)
@@ -368,7 +368,7 @@ static int parse_password(const char *filename, char **wall) {
                         char *password;
 
                         if (arg_console)
-                                if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
+                                if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) {
                                         r = tty_fd;
                                         goto finish;
                                 }