]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #8184 from poettering/color-ask-pw
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 15 Feb 2018 16:14:59 +0000 (17:14 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 15 Feb 2018 16:14:59 +0000 (17:14 +0100)
Trivial merge conflict resolved locally.

13 files changed:
.mkosi/mkosi.fedora
TODO
src/basic/process-util.c
src/basic/terminal-util.c
src/basic/terminal-util.h
src/basic/utf8.c
src/basic/utf8.h
src/core/execute.c
src/firstboot/firstboot.c
src/shared/ask-password-api.c
src/shared/ask-password-api.h
src/test/test-ask-password-api.c
src/tty-ask-password-agent/tty-ask-password-agent.c

index c7505d19f6a59afc664a0a02c7aed5817c4b01bf..e642c0b064b10834433bc763af05ae2988eb23ac 100644 (file)
@@ -67,6 +67,7 @@ BuildPackages=
         m4
         meson
         pam-devel
+        pcre2-devel
         pkgconfig
         python3-devel
         python3-lxml
diff --git a/TODO b/TODO
index 17022d912bfc99088ab6c39487b26ab2213d8477..048270bf513e1134df949cc30e6369bc41356ce3 100644 (file)
--- a/TODO
+++ b/TODO
@@ -32,6 +32,18 @@ Features:
 * teach tmpfiles.d q/Q logic something sensible in the context of XFS/ext4
   project quota
 
+* introduce DefaultSlice= or so in system.conf that allows changing where we
+  place our units by default, i.e. change system.slice to something
+  else. Similar, ManagerSlice= should exist so that PID1's own scope unit could
+  be moved somewhere else too. Finally machined and logind should get similar
+  options so that it is possible to move user session scopes and machines to a
+  different slice too by default. Usecase: people who want to put resources on
+  the entire system, with the exception of one specific service. See:
+  https://lists.freedesktop.org/archives/systemd-devel/2018-February/040369.html
+
+* check what setting the login shell to /bin/false vs. /sbin/nologin means and
+  do the right thing in get_user_creds_clean() with it.
+
 * maybe rework get_user_creds() to query the user database if $SHELL is used
   for root, but only then.
 
index 855ac7534a388ee83069450578cf3f9980728d48..aa41b3b686e5e9ea84d082f8dae62d8d6a62dafc 100644 (file)
@@ -850,17 +850,33 @@ int kill_and_sigcont(pid_t pid, int sig) {
         return r;
 }
 
-int getenv_for_pid(pid_t pid, const char *field, char **_value) {
+int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         _cleanup_fclose_ FILE *f = NULL;
         char *value = NULL;
-        int r;
         bool done = false;
-        size_t l;
         const char *path;
+        size_t l;
 
         assert(pid >= 0);
         assert(field);
-        assert(_value);
+        assert(ret);
+
+        if (pid == 0 || pid == getpid_cached()) {
+                const char *e;
+
+                e = getenv(field);
+                if (!e) {
+                        *ret = NULL;
+                        return 0;
+                }
+
+                value = strdup(e);
+                if (!value)
+                        return -ENOMEM;
+
+                *ret = value;
+                return 1;
+        }
 
         path = procfs_file_alloca(pid, "environ");
 
@@ -868,13 +884,13 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
         if (!f) {
                 if (errno == ENOENT)
                         return -ESRCH;
+
                 return -errno;
         }
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
         l = strlen(field);
-        r = 0;
 
         do {
                 char line[LINE_MAX];
@@ -899,14 +915,14 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
                         if (!value)
                                 return -ENOMEM;
 
-                        r = 1;
-                        break;
+                        *ret = value;
+                        return 1;
                 }
 
         } while (!done);
 
-        *_value = value;
-        return r;
+        *ret = NULL;
+        return 0;
 }
 
 bool pid_is_unwaited(pid_t pid) {
index 42336e8fdfd241fd89b52b10a4b7d5be74c73909..a897a6367d4015ec0ca6a9148a87ccb5934323f2 100644 (file)
@@ -48,6 +48,8 @@
 #include "log.h"
 #include "macro.h"
 #include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
 #include "process-util.h"
 #include "socket-util.h"
 #include "stat-util.h"
 #include "terminal-util.h"
 #include "time-util.h"
 #include "util.h"
-#include "path-util.h"
 
 static volatile unsigned cached_columns = 0;
 static volatile unsigned cached_lines = 0;
 
+static volatile int cached_on_tty = -1;
+static volatile int cached_colors_enabled = -1;
+static volatile int cached_underline_enabled = -1;
+
 int chvt(int vt) {
         _cleanup_close_ int fd;
 
+        /* Switch to the specified vt number. If the VT is specified <= 0 switch to the VT the kernel log messages go,
+         * if that's configured. */
+
         fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
         if (fd < 0)
                 return -errno;
@@ -323,8 +331,8 @@ int reset_terminal(const char *name) {
 }
 
 int open_terminal(const char *name, int mode) {
-        int fd, r;
         unsigned c = 0;
+        int fd;
 
         /*
          * If a TTY is in the process of being closed opening it might
@@ -354,8 +362,7 @@ int open_terminal(const char *name, int mode) {
                 c++;
         }
 
-        r = isatty(fd);
-        if (r == 0) {
+        if (isatty(fd) <= 0) {
                 safe_close(fd);
                 return -ENOTTY;
         }
@@ -365,44 +372,36 @@ int open_terminal(const char *name, int mode) {
 
 int acquire_terminal(
                 const char *name,
-                bool fail,
-                bool force,
-                bool ignore_tiocstty_eperm,
+                AcquireTerminalFlags flags,
                 usec_t timeout) {
 
-        int fd = -1, notify = -1, r = 0, wd = -1;
-        usec_t ts = 0;
+        _cleanup_close_ int notify = -1, fd = -1;
+        usec_t ts = USEC_INFINITY;
+        int r, wd = -1;
 
         assert(name);
+        assert(IN_SET(flags & ~ACQUIRE_TERMINAL_PERMISSIVE, ACQUIRE_TERMINAL_TRY, ACQUIRE_TERMINAL_FORCE, ACQUIRE_TERMINAL_WAIT));
 
-        /* We use inotify to be notified when the tty is closed. We
-         * create the watch before checking if we can actually acquire
-         * it, so that we don't lose any event.
+        /* We use inotify to be notified when the tty is closed. We create the watch before checking if we can actually
+         * acquire it, so that we don't lose any event.
          *
-         * Note: strictly speaking this actually watches for the
-         * device being closed, it does *not* really watch whether a
-         * tty loses its controlling process. However, unless some
-         * rogue process uses TIOCNOTTY on /dev/tty *after* closing
-         * its tty otherwise this will not become a problem. As long
-         * as the administrator makes sure not configure any service
-         * on the same tty as an untrusted user this should not be a
-         * problem. (Which he probably should not do anyway.) */
-
-        if (timeout != USEC_INFINITY)
-                ts = now(CLOCK_MONOTONIC);
-
-        if (!fail && !force) {
+         * Note: strictly speaking this actually watches for the device being closed, it does *not* really watch
+         * whether a tty loses its controlling process. However, unless some rogue process uses TIOCNOTTY on /dev/tty
+         * *after* closing its tty otherwise this will not become a problem. As long as the administrator makes sure
+         * not configure any service on the same tty as an untrusted user this should not be a problem. (Which he
+         * probably should not do anyway.) */
+
+        if ((flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_WAIT) {
                 notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
-                if (notify < 0) {
-                        r = -errno;
-                        goto fail;
-                }
+                if (notify < 0)
+                        return -errno;
 
                 wd = inotify_add_watch(notify, name, IN_CLOSE);
-                if (wd < 0) {
-                        r = -errno;
-                        goto fail;
-                }
+                if (wd < 0)
+                        return -errno;
+
+                if (timeout != USEC_INFINITY)
+                        ts = now(CLOCK_MONOTONIC);
         }
 
         for (;;) {
@@ -414,41 +413,43 @@ int acquire_terminal(
                 if (notify >= 0) {
                         r = flush_fd(notify);
                         if (r < 0)
-                                goto fail;
+                                return r;
                 }
 
-                /* 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 */
+                /* 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 */
                 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
                 if (fd < 0)
                         return fd;
 
-                /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
-                 * if we already own the tty. */
+                /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed if we already own the tty. */
                 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
 
                 /* First, try to get the tty */
-                if (ioctl(fd, TIOCSCTTY, force) < 0)
-                        r = -errno;
+                r = ioctl(fd, TIOCSCTTY,
+                          (flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_FORCE) < 0 ? -errno : 0;
 
+                /* Reset signal handler to old value */
                 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
 
-                /* Sometimes, it makes sense to ignore TIOCSCTTY
-                 * returning EPERM, i.e. when very likely we already
-                 * are have this controlling terminal. */
-                if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
-                        r = 0;
+                /* Success? Exit the loop now! */
+                if (r >= 0)
+                        break;
 
-                if (r < 0 && (force || fail || r != -EPERM))
-                        goto fail;
+                /* Any failure besides -EPERM? Fail, regardless of the mode. */
+                if (r != -EPERM)
+                        return r;
 
-                if (r >= 0)
+                if (flags & ACQUIRE_TERMINAL_PERMISSIVE) /* If we are in permissive mode, then EPERM is fine, turn this
+                                                          * into a success. Note that EPERM is also returned if we
+                                                          * already are the owner of the TTY. */
                         break;
 
-                assert(!fail);
-                assert(!force);
+                if (flags != ACQUIRE_TERMINAL_WAIT) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
+                        return r;
+
                 assert(notify >= 0);
+                assert(wd >= 0);
 
                 for (;;) {
                         union inotify_event_buffer buffer;
@@ -458,20 +459,17 @@ int acquire_terminal(
                         if (timeout != USEC_INFINITY) {
                                 usec_t n;
 
+                                assert(ts != USEC_INFINITY);
+
                                 n = now(CLOCK_MONOTONIC);
-                                if (ts + timeout < n) {
-                                        r = -ETIMEDOUT;
-                                        goto fail;
-                                }
+                                if (ts + timeout < n)
+                                        return -ETIMEDOUT;
 
                                 r = fd_wait_for_event(notify, POLLIN, ts + timeout - n);
                                 if (r < 0)
-                                        goto fail;
-
-                                if (r == 0) {
-                                        r = -ETIMEDOUT;
-                                        goto fail;
-                                }
+                                        return r;
+                                if (r == 0)
+                                        return -ETIMEDOUT;
                         }
 
                         l = read(notify, &buffer, sizeof(buffer));
@@ -479,34 +477,27 @@ int acquire_terminal(
                                 if (IN_SET(errno, EINTR, EAGAIN))
                                         continue;
 
-                                r = -errno;
-                                goto fail;
+                                return -errno;
                         }
 
                         FOREACH_INOTIFY_EVENT(e, buffer, l) {
-                                if (e->wd != wd || !(e->mask & IN_CLOSE)) {
-                                        r = -EIO;
-                                        goto fail;
-                                }
+                                if (e->mask & IN_Q_OVERFLOW) /* If we hit an inotify queue overflow, simply check if the terminal is up for grabs now. */
+                                        break;
+
+                                if (e->wd != wd || !(e->mask & IN_CLOSE)) /* Safety checks */
+                                        return -EIO;
                         }
 
                         break;
                 }
 
-                /* We close the tty fd here since if the old session
-                 * ended our handle will be dead. It's important that
-                 * we do this after sleeping, so that we don't enter
-                 * an endless loop. */
+                /* We close the tty fd here since if the old session ended our handle will be dead. It's important that
+                 * we do this after sleeping, so that we don't enter an endless loop. */
                 fd = safe_close(fd);
         }
 
-        safe_close(notify);
-
-        return fd;
-
-fail:
-        safe_close(fd);
-        safe_close(notify);
+        r = fd;
+        fd = -1;
 
         return r;
 }
@@ -519,7 +510,7 @@ int release_terminal(void) {
 
         _cleanup_close_ int fd = -1;
         struct sigaction sa_old;
-        int r = 0;
+        int r;
 
         fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
         if (fd < 0)
@@ -529,8 +520,7 @@ int release_terminal(void) {
          * by our own TIOCNOTTY */
         assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
 
-        if (ioctl(fd, TIOCNOTTY) < 0)
-                r = -errno;
+        r = ioctl(fd, TIOCNOTTY) < 0 ? -errno : 0;
 
         assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
 
@@ -630,7 +620,7 @@ int make_console_stdio(void) {
 
         /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
 
-        fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
+        fd = acquire_terminal("/dev/console", ACQUIRE_TERMINAL_FORCE|ACQUIRE_TERMINAL_PERMISSIVE, USEC_INFINITY);
         if (fd < 0)
                 return log_error_errno(fd, "Failed to acquire terminal: %m");
 
@@ -642,6 +632,8 @@ int make_console_stdio(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to duplicate terminal fd: %m");
 
+        reset_terminal_feature_caches();
+
         return 0;
 }
 
@@ -680,57 +672,80 @@ int vtnr_from_tty(const char *tty) {
         return i;
 }
 
-char *resolve_dev_console(char **active) {
+ int resolve_dev_console(char **ret) {
+        _cleanup_free_ char *active = NULL;
         char *tty;
+        int r;
 
-        /* Resolve where /dev/console is pointing to, if /sys is actually ours
-         * (i.e. not read-only-mounted which is a sign for container setups) */
+        assert(ret);
+
+        /* Resolve where /dev/console is pointing to, if /sys is actually ours (i.e. not read-only-mounted which is a
+         * sign for container setups) */
 
         if (path_is_read_only_fs("/sys") > 0)
-                return NULL;
+                return -ENOMEDIUM;
 
-        if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
-                return NULL;
+        r = read_one_line_file("/sys/class/tty/console/active", &active);
+        if (r < 0)
+                return r;
 
-        /* If multiple log outputs are configured the last one is what
-         * /dev/console points to */
-        tty = strrchr(*active, ' ');
+        /* If multiple log outputs are configured the last one is what /dev/console points to */
+        tty = strrchr(active, ' ');
         if (tty)
                 tty++;
         else
-                tty = *active;
+                tty = active;
 
         if (streq(tty, "tty0")) {
-                char *tmp;
+                active = mfree(active);
 
                 /* Get the active VC (e.g. tty1) */
-                if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
-                        free(*active);
-                        tty = *active = tmp;
-                }
+                r = read_one_line_file("/sys/class/tty/tty0/active", &active);
+                if (r < 0)
+                        return r;
+
+                tty = active;
         }
 
-        return tty;
+        if (tty == active) {
+                *ret = active;
+                active = NULL;
+        } else {
+                char *tmp;
+
+                tmp = strdup(tty);
+                if (!tmp)
+                        return -ENOMEM;
+
+                *ret = tmp;
+        }
+
+        return 0;
 }
 
-int get_kernel_consoles(char ***consoles) {
-        _cleanup_strv_free_ char **con = NULL;
+int get_kernel_consoles(char ***ret) {
+        _cleanup_strv_free_ char **l = NULL;
         _cleanup_free_ char *line = NULL;
-        const char *active;
+        const char *p;
         int r;
 
-        assert(consoles);
+        assert(ret);
+
+        /* If we /sys is mounted read-only this means we are running in some kind of container environment. In that
+         * case /sys would reflect the host system, not us, hence ignore the data we can read from it. */
+        if (path_is_read_only_fs("/sys") > 0)
+                goto fallback;
 
         r = read_one_line_file("/sys/class/tty/console/active", &line);
         if (r < 0)
                 return r;
 
-        active = line;
+        p = line;
         for (;;) {
                 _cleanup_free_ char *tty = NULL;
                 char *path;
 
-                r = extract_first_word(&active, &tty, NULL, 0);
+                r = extract_first_word(&p, &tty, NULL, 0);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -753,35 +768,44 @@ int get_kernel_consoles(char ***consoles) {
                         continue;
                 }
 
-                r = strv_consume(&con, path);
+                r = strv_consume(&l, path);
                 if (r < 0)
                         return r;
         }
 
-        if (strv_isempty(con)) {
+        if (strv_isempty(l)) {
                 log_debug("No devices found for system console");
-
-                r = strv_extend(&con, "/dev/console");
-                if (r < 0)
-                        return r;
+                goto fallback;
         }
 
-        *consoles = con;
-        con = NULL;
+        *ret = l;
+        l = NULL;
+
+        return 0;
+
+fallback:
+        r = strv_extend(&l, "/dev/console");
+        if (r < 0)
+                return r;
+
+        *ret = l;
+        l = NULL;
+
         return 0;
 }
 
 bool tty_is_vc_resolve(const char *tty) {
-        _cleanup_free_ char *active = NULL;
+        _cleanup_free_ char *resolved = NULL;
 
         assert(tty);
 
         tty = skip_dev_prefix(tty);
 
         if (streq(tty, "console")) {
-                tty = resolve_dev_console(&active);
-                if (!tty)
+                if (resolve_dev_console(&resolved) < 0)
                         return false;
+
+                tty = resolved;
         }
 
         return tty_is_vc(tty);
@@ -807,7 +831,7 @@ unsigned columns(void) {
         const char *e;
         int c;
 
-        if (_likely_(cached_columns > 0))
+        if (cached_columns > 0)
                 return cached_columns;
 
         c = 0;
@@ -841,7 +865,7 @@ unsigned lines(void) {
         const char *e;
         int l;
 
-        if (_likely_(cached_lines > 0))
+        if (cached_lines > 0)
                 return cached_lines;
 
         l = 0;
@@ -865,10 +889,17 @@ void columns_lines_cache_reset(int signum) {
         cached_lines = 0;
 }
 
-bool on_tty(void) {
-        static int cached_on_tty = -1;
+void reset_terminal_feature_caches(void) {
+        cached_columns = 0;
+        cached_lines = 0;
 
-        if (_unlikely_(cached_on_tty < 0))
+        cached_colors_enabled = -1;
+        cached_underline_enabled = -1;
+        cached_on_tty = -1;
+}
+
+bool on_tty(void) {
+        if (cached_on_tty < 0)
                 cached_on_tty = isatty(STDOUT_FILENO) > 0;
 
         return cached_on_tty;
@@ -879,7 +910,7 @@ int make_stdio(int fd) {
 
         assert(fd >= 0);
 
-        if (dup2(fd, STDIN_FILENO) < 0 && r >= 0)
+        if (dup2(fd, STDIN_FILENO) < 0)
                 r = -errno;
         if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0)
                 r = -errno;
@@ -896,13 +927,17 @@ int make_stdio(int fd) {
 }
 
 int make_null_stdio(void) {
-        int null_fd;
+        int null_fd, r;
 
         null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC);
         if (null_fd < 0)
                 return -errno;
 
-        return make_stdio(null_fd);
+        r = make_stdio(null_fd);
+
+        reset_terminal_feature_caches();
+
+        return r;
 }
 
 int getttyname_malloc(int fd, char **ret) {
@@ -1205,38 +1240,63 @@ bool terminal_is_dumb(void) {
 }
 
 bool colors_enabled(void) {
-        static int enabled = -1;
 
-        if (_unlikely_(enabled < 0)) {
+        /* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
+         * (which is the explicit way to turn off/on colors). If that didn't work we turn off colors unless we are on a
+         * TTY. And if we are on a TTY we turn it off if $TERM is set to "dumb". There's one special tweak though: if
+         * we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open
+         * continously due to fear of SAK, and hence things are a bit weird. */
+
+        if (cached_colors_enabled < 0) {
                 int val;
 
                 val = getenv_bool("SYSTEMD_COLORS");
                 if (val >= 0)
-                        enabled = val;
+                        cached_colors_enabled = val;
                 else if (getpid_cached() == 1)
                         /* PID1 outputs to the console without holding it open all the time */
-                        enabled = !getenv_terminal_is_dumb();
+                        cached_colors_enabled = !getenv_terminal_is_dumb();
                 else
-                        enabled = !terminal_is_dumb();
+                        cached_colors_enabled = !terminal_is_dumb();
         }
 
-        return enabled;
+        return cached_colors_enabled;
+}
+
+bool dev_console_colors_enabled(void) {
+        _cleanup_free_ char *s = NULL;
+        int b;
+
+        /* Returns true if we assume that color is supported on /dev/console.
+         *
+         * For that we first check if we explicitly got told to use colors or not, by checking $SYSTEMD_COLORS. If that
+         * didn't tell us anything we check whether PID 1 has $TERM set, and if not whether $TERM is set on the kernel
+         * command line. If we find $TERM set we assume color if it's not set to "dumb", similar to regular
+         * colors_enabled() operates. */
+
+        b = getenv_bool("SYSTEMD_COLORS");
+        if (b >= 0)
+                return b;
+
+        if (getenv_for_pid(1, "TERM", &s) <= 0)
+                (void) proc_cmdline_get_key("TERM", 0, &s);
+
+        return !streq_ptr(s, "dumb");
 }
 
 bool underline_enabled(void) {
-        static int enabled = -1;
 
-        if (enabled < 0) {
+        if (cached_underline_enabled < 0) {
 
                 /* The Linux console doesn't support underlining, turn it off, but only there. */
 
-                if (!colors_enabled())
-                        enabled = false;
+                if (colors_enabled())
+                        cached_underline_enabled = !streq_ptr(getenv("TERM"), "linux");
                 else
-                        enabled = !streq_ptr(getenv("TERM"), "linux");
+                        cached_underline_enabled = false;
         }
 
-        return enabled;
+        return cached_underline_enabled;
 }
 
 int vt_default_utf8(void) {
index e82719b11bdf8107d4ca938193036ec2ae093182..f6e6020b66e078b4896b4198acb0fb758001a969 100644 (file)
@@ -52,7 +52,23 @@ 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, usec_t timeout);
+
+/* Flags for tweaking the way we become the controlling process of a terminal. */
+typedef enum AcquireTerminalFlags {
+        /* Try to become the controlling process of the TTY. If we can't return -EPERM. */
+        ACQUIRE_TERMINAL_TRY = 0,
+
+        /* Tell the kernel to forcibly make us the controlling process of the TTY. Returns -EPERM if the kernel doesn't allow that. */
+        ACQUIRE_TERMINAL_FORCE = 1,
+
+        /* If we can't become the controlling process of the TTY right-away, then wait until we can. */
+        ACQUIRE_TERMINAL_WAIT = 2,
+
+        /* Pick one of the above, and then OR this flag in, in order to request permissive behaviour, if we can't become controlling process then don't mind */
+        ACQUIRE_TERMINAL_PERMISSIVE = 4,
+} AcquireTerminalFlags;
+
+int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
 int release_terminal(void);
 
 int terminal_vhangup_fd(int fd);
@@ -66,8 +82,8 @@ int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
 
 int vt_disallocate(const char *name);
 
-char *resolve_dev_console(char **active);
-int get_kernel_consoles(char ***consoles);
+int resolve_dev_console(char **ret);
+int get_kernel_consoles(char ***ret);
 bool tty_is_vc(const char *tty);
 bool tty_is_vc_resolve(const char *tty);
 bool tty_is_console(const char *tty) _pure_;
@@ -82,12 +98,15 @@ int fd_columns(int fd);
 unsigned columns(void);
 int fd_lines(int fd);
 unsigned lines(void);
+
 void columns_lines_cache_reset(int _unused_ signum);
+void reset_terminal_feature_caches(void);
 
 bool on_tty(void);
 bool terminal_is_dumb(void);
 bool colors_enabled(void);
 bool underline_enabled(void);
+bool dev_console_colors_enabled(void);
 
 #define DEFINE_ANSI_FUNC(name, NAME)                            \
         static inline const char *ansi_##name(void) {           \
index 4da9a405cb85914c13457f5a6a4c0b6358499353..b17f420264089df0291908d92e8d99b979efdc45 100644 (file)
@@ -408,3 +408,22 @@ int utf8_encoded_valid_unichar(const char *str) {
 
         return len;
 }
+
+size_t utf8_n_codepoints(const char *str) {
+        size_t n = 0;
+
+        /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
+
+        while (*str != 0) {
+                int k;
+
+                k = utf8_encoded_valid_unichar(str);
+                if (k < 0)
+                        return (size_t) -1;
+
+                str += k;
+                n++;
+        }
+
+        return n;
+}
index b0a7485aedc5f61026a12bc75d954c0cdabc85e9..7128615181020d1d77d12bb2a88f1eaa5547df01 100644 (file)
@@ -59,3 +59,5 @@ static inline bool utf16_is_trailing_surrogate(char16_t c) {
 static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) {
                 return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
 }
+
+size_t utf8_n_codepoints(const char *str);
index be9ef207250c8d1fb7eaa62fc7f9a8caba508ab0..bebd4eca805eaa030b9f422922330be729032f83 100644 (file)
@@ -509,9 +509,9 @@ static int setup_input(
                 int fd;
 
                 fd = acquire_terminal(exec_context_tty_path(context),
-                                      i == EXEC_INPUT_TTY_FAIL,
-                                      i == EXEC_INPUT_TTY_FORCE,
-                                      false,
+                                      i == EXEC_INPUT_TTY_FAIL  ? ACQUIRE_TERMINAL_TRY :
+                                      i == EXEC_INPUT_TTY_FORCE ? ACQUIRE_TERMINAL_FORCE :
+                                                                  ACQUIRE_TERMINAL_WAIT,
                                       USEC_INFINITY);
                 if (fd < 0)
                         return fd;
@@ -753,7 +753,7 @@ static int setup_confirm_stdio(const char *vc, int *_saved_stdin, int *_saved_st
         if (saved_stdout < 0)
                 return -errno;
 
-        fd = acquire_terminal(vc, false, false, false, DEFAULT_CONFIRM_USEC);
+        fd = acquire_terminal(vc, ACQUIRE_TERMINAL_WAIT, DEFAULT_CONFIRM_USEC);
         if (fd < 0)
                 return fd;
 
@@ -3871,8 +3871,7 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
 }
 
 static bool tty_may_match_dev_console(const char *tty) {
-        _cleanup_free_ char *active = NULL;
-        char *console;
+        _cleanup_free_ char *resolved = NULL;
 
         if (!tty)
                 return true;
@@ -3883,13 +3882,11 @@ static bool tty_may_match_dev_console(const char *tty) {
         if (streq(tty, "console"))
                 return true;
 
-        console = resolve_dev_console(&active);
-        /* if we could not resolve, assume it may */
-        if (!console)
-                return true;
+        if (resolve_dev_console(&resolved) < 0)
+                return true; /* if we could not resolve, assume it may */
 
         /* "tty0" means the active VC, so it may be the same sometimes */
-        return streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
+        return streq(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
 }
 
 bool exec_context_may_touch_console(const ExecContext *ec) {
index 262e520d5637acac47e0d7f3689ae03e33650085..effa092ec9df3b2ebb2bd2c5524b38971008d157 100644 (file)
@@ -558,7 +558,7 @@ static int prompt_root_password(void) {
         for (;;) {
                 _cleanup_string_free_erase_ char *a = NULL, *b = NULL;
 
-                r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a);
+                r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a);
                 if (r < 0)
                         return log_error_errno(r, "Failed to query root password: %m");
 
@@ -567,7 +567,7 @@ static int prompt_root_password(void) {
                         break;
                 }
 
-                r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b);
+                r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b);
                 if (r < 0)
                         return log_error_errno(r, "Failed to query root password: %m");
 
index 99d6a9b143a2ea7a1d84aa8cca8e7d2bf5dcac5d..a0ee05e2d8a871de05a42f46c29d0b0438b0c5e1 100644 (file)
@@ -201,7 +201,29 @@ static void backspace_chars(int ttyfd, size_t p) {
         }
 }
 
+static void backspace_string(int ttyfd, const char *str) {
+        size_t m;
+
+        assert(str);
+
+        if (ttyfd < 0)
+                return;
+
+        /* Backspaces back for enough characters to entirely undo printing of the specified string. */
+
+        m = utf8_n_codepoints(str);
+        if (m == (size_t) -1)
+                m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes output. Most
+                                  * likely this happened because we are not in an UTF-8 locale, and in that case that
+                                  * is the correct thing to do. And even if it's not, terminals tend to stop
+                                  * backspacing at the leftmost column, hence backspacing too much should be mostly
+                                  * OK. */
+
+        backspace_chars(ttyfd, m);
+}
+
 int ask_password_tty(
+                int ttyfd,
                 const char *message,
                 const char *keyname,
                 usec_t until,
@@ -209,18 +231,19 @@ int ask_password_tty(
                 const char *flag_file,
                 char **ret) {
 
+        enum {
+                POLL_TTY,
+                POLL_INOTIFY,
+                _POLL_MAX,
+        };
+
+        bool reset_tty = false, dirty = false, use_color = false;
+        _cleanup_close_ int cttyfd = -1, notify = -1;
         struct termios old_termios, new_termios;
         char passphrase[LINE_MAX + 1] = {}, *x;
+        struct pollfd pollfd[_POLL_MAX];
         size_t p = 0, codepoint = 0;
         int r;
-        _cleanup_close_ int ttyfd = -1, notify = -1;
-        struct pollfd pollfd[2];
-        bool reset_tty = false;
-        bool dirty = false;
-        enum {
-                POLL_TTY,
-                POLL_INOTIFY
-        };
 
         assert(ret);
 
@@ -232,33 +255,34 @@ int ask_password_tty(
 
         if (flag_file) {
                 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
-                if (notify < 0) {
-                        r = -errno;
-                        goto finish;
-                }
+                if (notify < 0)
+                        return -errno;
 
-                if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
-                        r = -errno;
-                        goto finish;
-                }
+                if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0)
+                        return -errno;
         }
 
-        ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
+        if (ttyfd < 0)
+                ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
+
         if (ttyfd >= 0) {
+                if (tcgetattr(ttyfd, &old_termios) < 0)
+                        return -errno;
 
-                if (tcgetattr(ttyfd, &old_termios) < 0) {
-                        r = -errno;
-                        goto finish;
-                }
+                if (flags & ASK_PASSWORD_CONSOLE_COLOR)
+                        use_color = dev_console_colors_enabled();
+                else
+                        use_color = colors_enabled();
+
+                if (use_color)
+                        (void) loop_write(ttyfd, ANSI_HIGHLIGHT, STRLEN(ANSI_HIGHLIGHT), false);
+
+                (void) loop_write(ttyfd, message, strlen(message), false);
+                (void) loop_write(ttyfd, " ", 1, false);
 
-                if (colors_enabled())
-                        loop_write(ttyfd, ANSI_HIGHLIGHT,
-                                   STRLEN(ANSI_HIGHLIGHT), false);
-                loop_write(ttyfd, message, strlen(message), false);
-                loop_write(ttyfd, " ", 1, false);
-                if (colors_enabled())
-                        loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL),
-                                   false);
+                if (use_color)
+                        (void) loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), false);
 
                 new_termios = old_termios;
                 new_termios.c_lflag &= ~(ICANON|ECHO);
@@ -273,16 +297,19 @@ int ask_password_tty(
                 reset_tty = true;
         }
 
-        zero(pollfd);
-        pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
-        pollfd[POLL_TTY].events = POLLIN;
-        pollfd[POLL_INOTIFY].fd = notify;
-        pollfd[POLL_INOTIFY].events = POLLIN;
+        pollfd[POLL_TTY] = (struct pollfd) {
+                .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
+                .events = POLLIN,
+        };
+        pollfd[POLL_INOTIFY] = (struct pollfd) {
+                .fd = notify,
+                .events = POLLIN,
+        };
 
         for (;;) {
-                char c;
                 int sleep_for = -1, k;
                 ssize_t n;
+                char c;
 
                 if (until > 0) {
                         usec_t y;
@@ -294,7 +321,7 @@ int ask_password_tty(
                                 goto finish;
                         }
 
-                        sleep_for = (int) ((until - y) / USEC_PER_MSEC);
+                        sleep_for = (int) DIV_ROUND_UP(until - y, USEC_PER_MSEC);
                 }
 
                 if (flag_file)
@@ -329,72 +356,99 @@ int ask_password_tty(
                         r = -errno;
                         goto finish;
 
-                } else if (n == 0)
-                        break;
+                }
 
-                if (c == '\n')
+                /* We treat EOF, newline and NUL byte all as valid end markers */
+                if (n == 0 || c == '\n' || c == 0)
                         break;
-                else if (c == 21) { /* C-u */
+
+                if (c == 21) { /* C-u */
 
                         if (!(flags & ASK_PASSWORD_SILENT))
-                                backspace_chars(ttyfd, p);
-                        p = 0;
+                                backspace_string(ttyfd, passphrase);
+
+                        explicit_bzero(passphrase, sizeof(passphrase));
+                        p = codepoint = 0;
 
                 } else if (IN_SET(c, '\b', 127)) {
 
                         if (p > 0) {
+                                size_t q;
 
                                 if (!(flags & ASK_PASSWORD_SILENT))
                                         backspace_chars(ttyfd, 1);
 
-                                p--;
+                                /* Remove a full UTF-8 codepoint from the end. For that, figure out where the last one
+                                 * begins */
+                                q = 0;
+                                for (;;) {
+                                        size_t z;
+
+                                        z = utf8_encoded_valid_unichar(passphrase + q);
+                                        if (z == 0) {
+                                                q = (size_t) -1; /* Invalid UTF8! */
+                                                break;
+                                        }
+
+                                        if (q + z >= p) /* This one brings us over the edge */
+                                                break;
+
+                                        q += z;
+                                }
+
+                                p = codepoint = q == (size_t) -1 ? p - 1 : q;
+                                explicit_bzero(passphrase + p, sizeof(passphrase) - p);
+
                         } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
 
                                 flags |= ASK_PASSWORD_SILENT;
 
-                                /* There are two ways to enter silent
-                                 * mode. Either by pressing backspace
-                                 * as first key (and only as first
-                                 * key), or ... */
+                                /* There are two ways to enter silent mode. Either by pressing backspace as first key
+                                 * (and only as first key), or ... */
+
                                 if (ttyfd >= 0)
-                                        loop_write(ttyfd, "(no echo) ", 10, false);
+                                        (void) loop_write(ttyfd, "(no echo) ", 10, false);
 
                         } else if (ttyfd >= 0)
-                                loop_write(ttyfd, "\a", 1, false);
+                                (void) loop_write(ttyfd, "\a", 1, false);
 
                 } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) {
 
-                        backspace_chars(ttyfd, p);
+                        backspace_string(ttyfd, passphrase);
                         flags |= ASK_PASSWORD_SILENT;
 
                         /* ... or by pressing TAB at any time. */
 
                         if (ttyfd >= 0)
-                                loop_write(ttyfd, "(no echo) ", 10, false);
-                } else {
-                        if (p >= sizeof(passphrase)-1) {
-                                loop_write(ttyfd, "\a", 1, false);
-                                continue;
-                        }
+                                (void) loop_write(ttyfd, "(no echo) ", 10, false);
+
+                } else if (p >= sizeof(passphrase)-1) {
 
+                        /* Reached the size limit */
+                        if (ttyfd >= 0)
+                                (void) loop_write(ttyfd, "\a", 1, false);
+
+                } else {
                         passphrase[p++] = c;
 
                         if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) {
+                                /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
                                 n = utf8_encoded_valid_unichar(passphrase + codepoint);
                                 if (n >= 0) {
                                         codepoint = p;
-                                        loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
+                                        (void) loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
                                 }
                         }
 
                         dirty = true;
                 }
 
+                /* Let's forget this char, just to not keep needlessly copies of key material around */
                 c = 'x';
         }
 
         x = strndup(passphrase, p);
-        explicit_bzero(passphrase, p);
+        explicit_bzero(passphrase, sizeof(passphrase));
         if (!x) {
                 r = -ENOMEM;
                 goto finish;
@@ -408,8 +462,8 @@ int ask_password_tty(
 
 finish:
         if (ttyfd >= 0 && reset_tty) {
-                loop_write(ttyfd, "\n", 1, false);
-                tcsetattr(ttyfd, TCSADRAIN, &old_termios);
+                (void) loop_write(ttyfd, "\n", 1, false);
+                (void) tcsetattr(ttyfd, TCSADRAIN, &old_termios);
         }
 
         return r;
@@ -715,7 +769,7 @@ int ask_password_auto(
         if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) {
                 char *s = NULL, **l = NULL;
 
-                r = ask_password_tty(message, keyname, until, flags, NULL, &s);
+                r = ask_password_tty(-1, message, keyname, until, flags, NULL, &s);
                 if (r < 0)
                         return r;
 
index f3ca6743a97e385e3d0e6c5ba9f7bf7569ecd89f..4b2eb3fe9291c072bd2fd797148fe1392705bbe4 100644 (file)
 #include "time-util.h"
 
 typedef enum AskPasswordFlags {
-        ASK_PASSWORD_ACCEPT_CACHED = 1,
-        ASK_PASSWORD_PUSH_CACHE = 2,
-        ASK_PASSWORD_ECHO = 4,                /* show the password literally while reading, instead of "*" */
-        ASK_PASSWORD_SILENT = 8,              /* do no show any password at all while reading */
-        ASK_PASSWORD_NO_TTY = 16,
-        ASK_PASSWORD_NO_AGENT = 32,
+        ASK_PASSWORD_ACCEPT_CACHED = 1U << 0,
+        ASK_PASSWORD_PUSH_CACHE    = 1U << 1,
+        ASK_PASSWORD_ECHO          = 1U << 2, /* show the password literally while reading, instead of "*" */
+        ASK_PASSWORD_SILENT        = 1U << 3, /* do no show any password at all while reading */
+        ASK_PASSWORD_NO_TTY        = 1U << 4,
+        ASK_PASSWORD_NO_AGENT      = 1U << 5,
+        ASK_PASSWORD_CONSOLE_COLOR = 1U << 6, /* Use color if /dev/console points to a console that supports color */
 } AskPasswordFlags;
 
-int ask_password_tty(const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret);
+int ask_password_tty(int tty_fd, const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret);
 int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
 int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret);
 int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
index da44465821fde0477af01336267c3e81fbeaf82b..562a774531fc6e2eb682f4d56d7160f36f6fdb40 100644 (file)
@@ -26,7 +26,7 @@ static void ask_password(void) {
         int r;
         _cleanup_free_ char *ret;
 
-        r = ask_password_tty("hello?", "da key", 0, 0, NULL, &ret);
+        r = ask_password_tty(-1, "hello?", "da key", 0, 0, NULL, &ret);
         assert(r >= 0);
 
         log_info("Got %s", ret);
index 9dfb0d80de97046babdd9bb3b7508c2baabc1103..c52a19dc374efe31603d14149b28ff5c80afb3f4 100644 (file)
@@ -254,6 +254,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
         union sockaddr_union sa = { .un.sun_family = AF_UNIX };
         size_t packet_length = 1;
         char **p, *d;
+        ssize_t n;
         int r;
 
         assert(socket_name);
@@ -279,9 +280,13 @@ static int send_passwords(const char *socket_name, char **passwords) {
 
         strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
 
-        r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
-        if (r < 0)
+        n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+        if (n < 0) {
                 r = log_debug_errno(errno, "sendto(): %m");
+                goto finish;
+        }
+
+        r = (int) n;
 
 finish:
         explicit_bzero(packet, packet_length);
@@ -363,18 +368,21 @@ static int parse_password(const char *filename, char **wall) {
                         int tty_fd = -1;
 
                         if (arg_console) {
-                                const char *con = arg_device ? arg_device : "/dev/console";
+                                const char *con = arg_device ?: "/dev/console";
 
-                                tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY);
+                                tty_fd = acquire_terminal(con, ACQUIRE_TERMINAL_WAIT, USEC_INFINITY);
                                 if (tty_fd < 0)
-                                        return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
+                                        return log_error_errno(tty_fd, "Failed to acquire %s: %m", con);
 
                                 r = reset_terminal_fd(tty_fd, true);
                                 if (r < 0)
                                         log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
                         }
 
-                        r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
+                        r = ask_password_tty(tty_fd, message, NULL, not_after,
+                                             (echo ? ASK_PASSWORD_ECHO : 0) |
+                                             (arg_console ? ASK_PASSWORD_CONSOLE_COLOR : 0),
+                                             filename, &password);
 
                         if (arg_console) {
                                 tty_fd = safe_close(tty_fd);