Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP2:GA
systemd-mini.1472
tty-ask-password-agent-on-console.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File tty-ask-password-agent-on-console.patch of Package systemd-mini.1472
--- src/tty-ask-password-agent/tty-ask-password-agent.c | 318 +++++++++++++++++++- 1 file changed, 305 insertions(+), 13 deletions(-) --- systemd-210/src/tty-ask-password-agent/tty-ask-password-agent.c +++ systemd-210/src/tty-ask-password-agent/tty-ask-password-agent.c 2015-10-27 11:35:43.242893783 +0000 @@ -4,6 +4,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering + Copyright 2015 Werner Fink systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -28,8 +29,12 @@ #include <sys/poll.h> #include <sys/inotify.h> #include <unistd.h> +#include <sys/prctl.h> #include <getopt.h> +#include <signal.h> +#include <sys/wait.h> #include <sys/signalfd.h> +#include <sys/mman.h> #include <fcntl.h> #include "util.h" @@ -41,6 +46,11 @@ #include "ask-password-api.h" #include "strv.h" #include "build.h" +#include "fileio.h" +#include "macro.h" +#include "hashmap.h" +#include "strv.h" +#include "exit-status.h" static enum { ACTION_LIST, @@ -49,8 +59,16 @@ static enum { ACTION_WALL } arg_action = ACTION_QUERY; +static volatile sig_atomic_t sigchild; +static void chld_handler(int sig) +{ + (void)sig; + sigchild++; +} + static bool arg_plymouth = false; static bool arg_console = false; +static const char *arg_device; static int ask_password_plymouth( const char *message, @@ -246,12 +264,73 @@ finish: return r; } +static int get_kernel_consoles(char ***consoles) { + _cleanup_strv_free_ char **con = NULL; + _cleanup_free_ char *active = NULL; + char *word, *state; + int count = 0; + size_t len; + int ret; + + assert(consoles); + + ret = read_one_line_file("/sys/class/tty/console/active", &active); + if (ret < 0) + return ret; + + FOREACH_WORD(word, len, active, state) { + _cleanup_free_ char *tty = NULL; + char *path; + + if (len == 4 && strneq(word, "tty0", 4)) { + + ret = read_one_line_file("/sys/class/tty/tty0/active", &tty); + if (ret < 0) + return ret; + } else { + + tty = strndup(word, len); + if (!tty) + return -ENOMEM; + } + + path = strjoin("/dev/", tty, NULL); + if (!path) + return -ENOMEM; + + ret = strv_push(&con, path); + if (ret < 0) { + free(path); + return ret; + } + + count++; + } + + if (count == 0) { + + log_debug("No devices found for system console"); + + ret = strv_extend(&con, "/dev/console"); + if (ret < 0) + return ret; + + count++; + } + + *consoles = con; + con = NULL; + + return count; +} + static int parse_password(const char *filename, char **wall) { char *socket_name = NULL, *message = NULL, *packet = NULL; uint64_t not_after = 0; unsigned pid = 0; int socket_fd = -1; bool accept_cached = false; + size_t packet_length = 0; const ConfigTableItem items[] = { { "Ask", "Socket", config_parse_string, 0, &socket_name }, @@ -323,7 +402,6 @@ static int parse_password(const char *fi struct sockaddr sa; struct sockaddr_un un; } sa = {}; - size_t packet_length = 0; assert(arg_action == ACTION_QUERY || arg_action == ACTION_WATCH); @@ -365,7 +443,7 @@ static int parse_password(const char *fi char *password = NULL; if (arg_console) - if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) { + if ((tty_fd = acquire_terminal(arg_device ? arg_device : "/dev/console", false, false, false, (usec_t) -1)) < 0) { r = tty_fd; goto finish; } @@ -386,6 +464,7 @@ static int parse_password(const char *fi strcpy(packet+1, password); } + memset(password, 0, strlen(password)); free(password); } } @@ -423,6 +502,7 @@ finish: if (socket_fd >= 0) close_nointr_nofail(socket_fd); + memset(packet, 0, packet_length); free(packet); free(socket_name); free(message); @@ -664,7 +744,7 @@ static int parse_argv(int argc, char *ar { "watch", no_argument, NULL, ARG_WATCH }, { "wall", no_argument, NULL, ARG_WALL }, { "plymouth", no_argument, NULL, ARG_PLYMOUTH }, - { "console", no_argument, NULL, ARG_CONSOLE }, + { "console", optional_argument, NULL, ARG_CONSOLE }, {} }; @@ -707,6 +787,8 @@ static int parse_argv(int argc, char *ar case ARG_CONSOLE: arg_console = true; + if (!isempty(optarg)) + arg_device = optarg; break; case '?': @@ -725,6 +807,205 @@ static int parse_argv(int argc, char *ar return 1; } +/* + * To be able to ask on all terminal devices of /dev/console + * the devices are collected. If more than one device are found, + * then on each of the terminals a inquiring task is forked. + * Every task has its own session and its own controlling terminal. + * If one of the tasks does handle a password, the remaining tasks + * will be terminated. + */ +static int ask_on_consoles(int argc, char *argv[]) { + _cleanup_hashmap_free_ Hashmap *pids = NULL; + _cleanup_strv_free_ char **consoles = NULL; + struct sigaction sig = { + .sa_handler = chld_handler, + .sa_flags = SA_NOCLDSTOP | SA_RESTART, + }; + struct timespec timeout; + siginfo_t status = {}; + sigset_t set; + Iterator it; + char *device; + char **tty; + void *ptr; + pid_t pid; + int ret, signum; + + ret = get_kernel_consoles(&consoles); + if (ret < 0) { + errno = -ret; + log_error("Failed to determine devices of /dev/console: %m"); + return ret; + } + + pids = hashmap_new(NULL, NULL); + if (!pids) + return log_oom(); + + assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0); + + assert_se(sigemptyset(&sig.sa_mask) >= 0); + assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0); + + sig.sa_handler = SIG_DFL; + assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); + + STRV_FOREACH(tty, consoles) { + + pid = fork(); + if (pid < 0) { + log_error("Failed to fork process: %m"); + return -errno; + } + + device = *tty; + + if (pid == 0) { + char *conarg; + static const struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_RESTART, + }; + sigset_t ss; + int ac; + + conarg = strjoin("--console=", device, NULL); + if (!conarg) + return log_oom(); + + assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); + + assert_se(sigemptyset(&ss) >= 0); + assert_se(sigprocmask(SIG_SETMASK, &ss, NULL) >= 0); + assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); + + for (ac = 0; ac < argc; ac++) { + if (streq(argv[ac], "--console")) { + argv[ac] = conarg; + break; + } + } + + assert(ac < argc); + + execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv); + _exit(EXIT_FAILURE); + } + + device = strdup(*tty); + if (!device) + return log_oom(); + + ret = hashmap_put(pids, UINT_TO_PTR(pid), device); + if (ret < 0) + return log_oom(); + } + + for (;;) { + + assert_se(!hashmap_isempty(pids)); + + ret = waitid(P_ALL, 0, &status, WEXITED); + + if (!ret) + break; + + if (errno == EINTR) + continue; + + log_error("waitid() failed: %m"); + return -errno; + } + + /* + * Remove the returned process from hashmap. + */ + device = hashmap_remove(pids, UINT_TO_PTR(status.si_pid)); + assert(device); + + if (!is_clean_exit(status.si_code, status.si_status, NULL)) { + if (status.si_code == CLD_EXITED) + log_error("Failed to execute child for %s: %d", device, status.si_status); + else + log_error("Failed to execute child for %s due signal %s", device, signal_to_string(status.si_status)); + } + + free(device); + + if (hashmap_isempty(pids)) + return ret; + + /* + * Request termination of the remaining processes as those + * are not required anymore. + */ + HASHMAP_FOREACH_KEY(device, ptr, pids, it) { + + assert(device); + pid = PTR_TO_UINT(ptr); + + if (kill(pid, SIGTERM) < 0 && errno != ESRCH) + log_warning("kill(%d, SIGTERM) failed: %m", pid); + } + + /* + * Collect the processes which have go away. + */ + assert_se(sigemptyset(&set) >= 0); + assert_se(sigaddset(&set, SIGCHLD) >= 0); + timespec_store(&timeout, 50 * USEC_PER_MSEC); + + while ((ptr = hashmap_first_key(pids))) { + + ret = waitid(P_ALL, 0, &status, WEXITED|WNOHANG); + if (ret < 0 && errno == EINTR) + continue; + + if (!ret && status.si_pid > 0) { + device = hashmap_remove(pids, UINT_TO_PTR(status.si_pid)); + assert(device); + free(device); + continue; + } + + signum = sigtimedwait(&set, NULL, &timeout); + if (signum != SIGCHLD) { + + if (signum < 0 && errno == EAGAIN) + break; + + if (signum < 0) { + log_error("sigtimedwait() failed: %m"); + return -errno; + } + + if (signum >= 0) + log_warning("sigtimedwait() returned unexpected signal."); + } + } + + + /* + * Kill hanging processes. + */ + while ((ptr = hashmap_first_key(pids))) { + + device = hashmap_remove(pids, ptr); + assert(device); + + pid = PTR_TO_UINT(ptr); + + log_debug("Failed to terminate child %d for %s, going to kill it", pid, device); + free(device); + + if (kill(pid, SIGKILL) < 0 && errno != ESRCH) + log_warning("kill(%d, SIGKILL) failed: %m", pid); + } + + return ret; +} + int main(int argc, char *argv[]) { int r; @@ -737,16 +1018,27 @@ int main(int argc, char *argv[]) { if ((r = parse_argv(argc, argv)) <= 0) goto finish; - if (arg_console) { - setsid(); - release_terminal(); - } + if (arg_console && !arg_device) + /* + * Spwan for each console device a own process + */ + r = ask_on_consoles(argc, argv); + else { - if (arg_action == ACTION_WATCH || - arg_action == ACTION_WALL) - r = watch_passwords(); - else - r = show_passwords(); + if (arg_device) { + /* + * Later on a controlling terminal will be will be acquired, + * therefore the current process has to become a session + * leader and should not have a controlling terminal already. + */ + (void) setsid(); + (void) release_terminal(); + } + if (arg_action == ACTION_WATCH || arg_action == ACTION_WALL) + r = watch_passwords(); + else + r = show_passwords(); + } if (r < 0) log_error("Error: %s", strerror(-r));
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor