diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index eeeb657623..678cd4ed4d 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -764,21 +764,7 @@ int make_console_stdio(void) { return 0; } -bool tty_is_vc(const char *tty) { - assert(tty); - - return vtnr_from_tty(tty) >= 0; -} - -bool tty_is_console(const char *tty) { - assert(tty); - - return streq(skip_dev_prefix(tty), "console"); -} - -int vtnr_from_tty(const char *tty) { - int r; - +static int vtnr_from_tty_raw(const char *tty, unsigned *ret) { assert(tty); tty = skip_dev_prefix(tty); @@ -787,8 +773,16 @@ int vtnr_from_tty(const char *tty) { if (!e) return -EINVAL; + return safe_atou(e, ret); +} + +int vtnr_from_tty(const char *tty) { unsigned u; - r = safe_atou(e, &u); + int r; + + assert(tty); + + r = vtnr_from_tty_raw(tty, &u); if (r < 0) return r; if (!vtnr_is_valid(u)) @@ -797,6 +791,23 @@ int vtnr_from_tty(const char *tty) { return (int) u; } +bool tty_is_vc(const char *tty) { + assert(tty); + + /* NB: for >= 0 values no range check is conducted here, on the assumption that the caller will + * either extract vtnr through vtnr_from_tty() later where ERANGE would be reported, or doesn't care + * about whether it's strictly valid, but only asking "does this fall into the vt catogory?", for which + * "yes" seems to be a better answer. */ + + return vtnr_from_tty_raw(tty, /* ret = */ NULL) >= 0; +} + +bool tty_is_console(const char *tty) { + assert(tty); + + return streq(skip_dev_prefix(tty), "console"); +} + int resolve_dev_console(char **ret) { int r; @@ -855,13 +866,12 @@ int resolve_dev_console(char **ret) { int get_kernel_consoles(char ***ret) { _cleanup_strv_free_ char **l = NULL; _cleanup_free_ char *line = NULL; - const char *p; int r; assert(ret); - /* If /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 /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; @@ -869,8 +879,7 @@ int get_kernel_consoles(char ***ret) { if (r < 0) return r; - p = line; - for (;;) { + for (const char *p = line;;) { _cleanup_free_ char *tty = NULL, *path = NULL; r = extract_first_word(&p, &tty, NULL, 0); @@ -906,8 +915,7 @@ int get_kernel_consoles(char ***ret) { } *ret = TAKE_PTR(l); - - return 0; + return strv_length(*ret); fallback: r = strv_extend(&l, "/dev/console"); @@ -915,7 +923,6 @@ fallback: return r; *ret = TAKE_PTR(l); - return 0; } diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index 9ad5597b05..e71b5ec101 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -25,72 +25,53 @@ static const char *arg_dest = NULL; static bool arg_enabled = true; -static int add_symlink(const char *fservice, const char *tservice) { - const char *from, *to; - - assert(fservice); - assert(tservice); - - from = strjoina(SYSTEM_DATA_UNIT_DIR "/", fservice); - to = strjoina(arg_dest, "/getty.target.wants/", tservice); - - (void) mkdir_parents_label(to, 0755); - - if (symlink(from, to) < 0) { - /* In case console=hvc0 is passed this will very likely result in EEXIST */ - if (errno == EEXIST) - return 0; - - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - } - - return 0; -} - static int add_serial_getty(const char *tty) { - _cleanup_free_ char *n = NULL; + _cleanup_free_ char *instance = NULL; int r; assert(tty); + tty = skip_dev_prefix(tty); + log_debug("Automatically adding serial getty for /dev/%s.", tty); - r = unit_name_from_path_instance("serial-getty", tty, ".service", &n); + r = unit_name_path_escape(tty, &instance); if (r < 0) - return log_error_errno(r, "Failed to generate service name: %m"); + return log_error_errno(r, "Failed to escape tty path: %m"); - return add_symlink("serial-getty@.service", n); + return generator_add_symlink_full(arg_dest, + "getty.target", "wants", + SYSTEM_DATA_UNIT_DIR "/serial-getty@.service", instance); } static int add_container_getty(const char *tty) { - _cleanup_free_ char *n = NULL; + _cleanup_free_ char *instance = NULL; int r; assert(tty); + assert(!path_startswith(tty, "/dev/")); log_debug("Automatically adding container getty for /dev/pts/%s.", tty); - r = unit_name_from_path_instance("container-getty", tty, ".service", &n); + r = unit_name_path_escape(tty, &instance); if (r < 0) - return log_error_errno(r, "Failed to generate service name: %m"); + return log_error_errno(r, "Failed to escape tty path: %m"); - return add_symlink("container-getty@.service", n); + return generator_add_symlink_full(arg_dest, + "getty.target", "wants", + SYSTEM_DATA_UNIT_DIR "/container-getty@.service", instance); } -static int verify_tty(const char *name) { +static int verify_tty(const char *path) { _cleanup_close_ int fd = -EBADF; - const char *p; - /* Some TTYs are weird and have been enumerated but don't work - * when you try to use them, such as classic ttyS0 and - * friends. Let's check that and open the device and run - * isatty() on it. */ + assert(path); - p = strjoina("/dev/", name); + /* Some TTYs are weird and have been enumerated but don't work when you try to use them, such as + * classic ttyS0 and friends. Let's check that and open the device and run isatty() on it. */ - /* O_NONBLOCK is essential here, to make sure we don't wait - * for DCD */ - fd = open(p, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW); + /* O_NONBLOCK is essential here, to make sure we don't wait for DCD */ + fd = open(path, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW); if (fd < 0) return -errno; @@ -101,18 +82,17 @@ static int verify_tty(const char *name) { } static int run_container(void) { - _cleanup_free_ char *container_ttys = NULL; int r; log_debug("Automatically adding console shell."); - r = add_symlink("console-getty.service", "console-getty.service"); + r = generator_add_symlink(arg_dest, "getty.target", "wants", SYSTEM_DATA_UNIT_DIR "/console-getty.service"); if (r < 0) return r; /* When $container_ttys is set for PID 1, spawn gettys on all ptys named therein. * Note that despite the variable name we only support ptys here. */ - + _cleanup_free_ char *container_ttys = NULL; (void) getenv_for_pid(1, "container_ttys", &container_ttys); for (const char *p = container_ttys;;) { @@ -124,10 +104,8 @@ static int run_container(void) { if (r == 0) return 0; - const char *tty = word; - /* First strip off /dev/ if it is specified */ - tty = path_startswith(tty, "/dev/") ?: tty; + const char *tty = skip_dev_prefix(word); /* Then, make sure it's actually a pty */ tty = path_startswith(tty, "pts/"); @@ -245,30 +223,24 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) return run_container(); /* Automatically add in a serial getty on all active kernel consoles */ - _cleanup_free_ char *active = NULL; - (void) read_one_line_file("/sys/class/tty/console/active", &active); - for (const char *p = active;;) { - _cleanup_free_ char *tty = NULL; + _cleanup_strv_free_ char **consoles = NULL; + r = get_kernel_consoles(&consoles); + if (r < 0) + log_warning_errno(r, "Failed to get active kernel consoles, ignoring: %m"); + else if (r > 0) + STRV_FOREACH(i, consoles) { + /* We assume that gettys on virtual terminals are started via manual configuration + * and do this magic only for non-VC terminals. */ + if (tty_is_vc(*i)) + continue; - r = extract_first_word(&p, &tty, NULL, 0); - if (r < 0) - return log_error_errno(r, "Failed to parse /sys/class/tty/console/active: %m"); - if (r == 0) - break; + if (verify_tty(*i) < 0) + continue; - /* We assume that gettys on virtual terminals are started via manual configuration and do - * this magic only for non-VC terminals. */ - - if (isempty(tty) || tty_is_vc(tty)) - continue; - - if (verify_tty(tty) < 0) - continue; - - r = add_serial_getty(tty); - if (r < 0) - return r; - } + r = add_serial_getty(*i); + if (r < 0) + return r; + } /* Automatically add in a serial getty on the first virtualizer console */ FOREACH_STRING(j, diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index e97485e9c9..ffca6045b6 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -348,8 +348,7 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ v = vtnr_from_tty(tty); if (v < 0) return v; - else if (v == 0) - return -ENOENT; + assert(v > 0); if (seat) *seat = "seat0"; diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 5ff892c25d..1c1b95bef3 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -40,6 +40,7 @@ #include "set.h" #include "signal-util.h" #include "socket-util.h" +#include "static-destruct.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" @@ -54,7 +55,9 @@ static enum { static bool arg_plymouth = false; static bool arg_console = false; -static const char *arg_device = NULL; +static char *arg_device = NULL; + +STATIC_DESTRUCTOR_REGISTER(arg_device, freep); static int send_passwords(const char *socket_name, char **passwords) { int r; @@ -530,12 +533,13 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CONSOLE: arg_console = true; if (optarg) { - if (isempty(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty console device path is not allowed."); - arg_device = optarg; + arg_device = strdup(optarg); + if (!arg_device) + return log_oom(); } break; @@ -667,6 +671,11 @@ static int ask_on_consoles(char *argv[]) { r = get_kernel_consoles(&consoles); if (r < 0) return log_error_errno(r, "Failed to determine devices of /dev/console: %m"); + if (r <= 1) { + /* No need to spawn subprocesses, there's only one console or using /dev/console as fallback */ + arg_device = TAKE_PTR(consoles[0]); + return 0; + } pids = set_new(NULL); if (!pids) @@ -714,7 +723,7 @@ static int ask_on_consoles(char *argv[]) { } terminate_agents(pids); - return 0; + return 1; } static int run(int argc, char *argv[]) { @@ -728,11 +737,14 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; - if (arg_console && !arg_device) - /* - * Spawn a separate process for each console device. - */ - return ask_on_consoles(argv); + /* Spawn a separate process for each console device if there're multiple. */ + if (arg_console && !arg_device) { + r = ask_on_consoles(argv); + if (r != 0) + return r; + + assert(arg_device); + } if (arg_device) /* Later on, a controlling terminal will be acquired, therefore the current process has to