Trivial merge conflict resolved locally.
m4
meson
pam-devel
+ pcre2-devel
pkgconfig
python3-devel
python3-lxml
* 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.
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");
if (!f) {
if (errno == ENOENT)
return -ESRCH;
+
return -errno;
}
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
l = strlen(field);
- r = 0;
do {
char line[LINE_MAX];
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) {
#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;
}
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
c++;
}
- r = isatty(fd);
- if (r == 0) {
+ if (isatty(fd) <= 0) {
safe_close(fd);
return -ENOTTY;
}
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 (;;) {
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;
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));
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;
}
_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)
* 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);
/* 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");
if (r < 0)
return log_error_errno(r, "Failed to duplicate terminal fd: %m");
+ reset_terminal_feature_caches();
+
return 0;
}
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)
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);
const char *e;
int c;
- if (_likely_(cached_columns > 0))
+ if (cached_columns > 0)
return cached_columns;
c = 0;
const char *e;
int l;
- if (_likely_(cached_lines > 0))
+ if (cached_lines > 0)
return cached_lines;
l = 0;
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;
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;
}
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) {
}
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) {
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);
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_;
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) { \
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;
+}
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);
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;
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;
}
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;
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) {
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");
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");
}
}
+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,
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);
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);
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;
goto finish;
}
- sleep_for = (int) ((until - y) / USEC_PER_MSEC);
+ sleep_for = (int) DIV_ROUND_UP(until - y, USEC_PER_MSEC);
}
if (flag_file)
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;
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;
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;
#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);
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);
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);
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);
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);