Merge pull request #8184 from poettering/color-ask-pw

Trivial merge conflict resolved locally.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-02-15 17:14:59 +01:00
commit 730f40eb57
13 changed files with 425 additions and 236 deletions

View File

@ -67,6 +67,7 @@ BuildPackages=
m4
meson
pam-devel
pcre2-devel
pkgconfig
python3-devel
python3-lxml

12
TODO
View File

@ -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.

View 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) {

View 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"
@ -56,14 +58,20 @@
#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.) */
* 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) {
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;
if (r < 0 && (force || fail || r != -EPERM))
goto fail;
/* Success? Exit the loop now! */
if (r >= 0)
break;
assert(!fail);
assert(!force);
/* Any failure besides -EPERM? Fail, regardless of the mode. */
if (r != -EPERM)
return r;
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;
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) {

View 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) { \

View 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;
}

View 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);

View 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) {

View 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");

View 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,19 +231,20 @@ int ask_password_tty(
const char *flag_file,
char **ret) {
struct termios old_termios, new_termios;
char passphrase[LINE_MAX + 1] = {}, *x;
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
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;
assert(ret);
if (flags & ASK_PASSWORD_NO_TTY)
@ -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 (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_HIGHLIGHT, STRLEN(ANSI_HIGHLIGHT), false);
(void) loop_write(ttyfd, message, strlen(message), false);
(void) loop_write(ttyfd, " ", 1, 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)
}
/* We treat EOF, newline and NUL byte all as valid end markers */
if (n == 0 || c == '\n' || c == 0)
break;
if (c == '\n')
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;

View File

@ -25,15 +25,16 @@
#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);

View 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);

View 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);