diff --git a/systemd-mini.changes b/systemd-mini.changes index 6cbc4663..67fd672a 100644 --- a/systemd-mini.changes +++ b/systemd-mini.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Tue Oct 6 15:13:04 UTC 2015 - werner@suse.de + +- Modify patch tty-ask-password-agent-on-console.patch to reflect + the changes done for pull request 1432 + ------------------------------------------------------------------- Thu Oct 1 15:58:32 UTC 2015 - jengelh@inai.de diff --git a/systemd.changes b/systemd.changes index 6cbc4663..67fd672a 100644 --- a/systemd.changes +++ b/systemd.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Tue Oct 6 15:13:04 UTC 2015 - werner@suse.de + +- Modify patch tty-ask-password-agent-on-console.patch to reflect + the changes done for pull request 1432 + ------------------------------------------------------------------- Thu Oct 1 15:58:32 UTC 2015 - jengelh@inai.de diff --git a/tty-ask-password-agent-on-console.patch b/tty-ask-password-agent-on-console.patch index ae962403..9490adc2 100644 --- a/tty-ask-password-agent-on-console.patch +++ b/tty-ask-password-agent-on-console.patch @@ -1,6 +1,6 @@ -From 633a5904c1c4e363a7147f47e2d9fdb1925f7b9f Mon Sep 17 00:00:00 2001 +From 907bc2aa36f58c6050cd4b7b290e0992a4373e49 Mon Sep 17 00:00:00 2001 From: Werner Fink -Date: Fri, 25 Sep 2015 14:28:58 +0200 +Date: Wed, 30 Sep 2015 15:00:41 +0200 Subject: [PATCH] Ask for passphrases not only on the first console of /dev/console @@ -9,22 +9,45 @@ where often a serial console together with other consoles are used. Even rack based servers attachted to both a serial console as well as having a virtual console do sometimes miss a connected monitor. + +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 for passphrase is forked and do not return +to the caller. + +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. + +Also let contradictory options on the command of +systemd-tty-ask-password-agent fail. + +Spwan for each device of the system console /dev/console a own process. + +Replace the system call wait() with with system call waitid(). --- - src/tty-ask-password-agent/tty-ask-password-agent.c | 191 ++++++++++++++++++++- - 1 file changed, 186 insertions(+), 5 deletions(-) + src/tty-ask-password-agent.c | 264 ++++++++++++++++++++- + 1 file changed, 255 insertions(+), 9 deletions(-) diff --git src/tty-ask-password-agent/tty-ask-password-agent.c src/tty-ask-password-agent/tty-ask-password-agent.c -index 82cbf95..928a5e8 100644 +index 4630eb9..df4bada 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c -@@ -31,6 +31,10 @@ +@@ -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 +@@ -31,6 +32,9 @@ #include #include #include +#include +#include +#include -+#include #include "util.h" #include "mkdir.h" @@ -37,7 +60,7 @@ index 82cbf95..928a5e8 100644 static enum { ACTION_LIST, -@@ -53,6 +59,19 @@ static enum { +@@ -53,8 +59,21 @@ static enum { ACTION_WALL } arg_action = ACTION_QUERY; @@ -46,88 +69,101 @@ index 82cbf95..928a5e8 100644 + char *tty; +}; + -+static volatile unsigned long *usemask; +static volatile sig_atomic_t sigchild; -+static void chld_handler(int sig) -+{ -+ (void)sig; ++ ++static void chld_handler(int sig) { + ++sigchild; +} + static bool arg_plymouth = false; static bool arg_console = false; ++static bool arg_device = false; ++static const char *current_dev = "/dev/console"; -@@ -210,6 +229,69 @@ static int ask_password_plymouth( + static int ask_password_plymouth( + const char *message, +@@ -211,6 +230,80 @@ static int ask_password_plymouth( return 0; } -+static void free_consoles(struct console *con, const unsigned int num) { ++static void free_consoles(struct console *con, unsigned int num) { + unsigned int n; -+ if (!con || !num) ++ ++ if (!con || num == 0) + return; ++ + for (n = 0; n < num; n++) + free(con[n].tty); ++ + free(con); +} + -+static const char *current_dev = "/dev/console"; -+static struct console* collect_consoles(unsigned int * num) { ++static int collect_consoles(struct console **consoles, unsigned int *num) { + _cleanup_free_ char *active = NULL; + const char *word, *state; + struct console *con = NULL; + size_t con_len = 0, len; ++ unsigned int count = 0; + int ret; + + assert(num); -+ assert(*num == 0); ++ assert(consoles); + + ret = read_one_line_file("/sys/class/tty/console/active", &active); + if (ret < 0) -+ return con; ++ return log_error_errno(ret, "Failed to read /sys/class/tty/console/active: %m"); ++ + FOREACH_WORD(word, len, active, state) { + _cleanup_free_ char *tty = NULL; + -+ if (strneq(word, "tty0", len) && -+ read_one_line_file("/sys/class/tty/tty0/active", &tty) >= 0) { ++ if (len == 4 && strneq(word, "tty0", 4)) { ++ ++ ret = read_one_line_file("/sys/class/tty/tty0/active", &tty); ++ if (ret < 0) ++ return log_error_errno(ret, "Failed to read /sys/class/tty/tty0/active: %m"); ++ + word = tty; + len = strlen(tty); + } -+ con = greedy_realloc((void**)&con, &con_len, 1+(*num), sizeof(struct console)); -+ if (con == NULL) { -+ log_oom(); -+ return NULL; ++ ++ con = GREEDY_REALLOC(con, con_len, 1+count); ++ if (!con) ++ return log_oom(); ++ ++ if (asprintf(&con[count].tty, "/dev/%.*s", (int)len, word) < 0) { ++ free_consoles(con, count); ++ return log_oom(); + } -+ if (asprintf(&con[*num].tty, "/dev/%.*s", (int)len, word) < 0) { -+ free_consoles(con, *num); -+ log_oom(); -+ *num = 0; -+ return NULL; -+ } -+ con[*num].pid = 0; -+ (*num)++; ++ ++ con[count].pid = 0; ++ count++; + } -+ if (con == NULL) { -+ con = greedy_realloc((void**)&con, &con_len, 1, sizeof(struct console)); -+ if (con == NULL) { -+ log_oom(); -+ return NULL; -+ } ++ ++ if (!con) { ++ con = GREEDY_REALLOC(con, con_len, 1); ++ if (!con) ++ return log_oom(); ++ + con[0].tty = strdup(current_dev); -+ if (con[0].tty == NULL) { ++ if (!con[0].tty) { + free_consoles(con, 1); -+ log_oom(); -+ return NULL; ++ return log_oom(); + } ++ + con[0].pid = 0; -+ (*num)++; ++ count++; + } -+ return con; ++ ++ *num = count; ++ *consoles = con; ++ ++ return 0; +} + static int parse_password(const char *filename, char **wall) { _cleanup_free_ char *socket_name = NULL, *message = NULL, *packet = NULL; uint64_t not_after = 0; -@@ -310,7 +392,7 @@ static int parse_password(const char *filename, char **wall) { +@@ -311,7 +404,7 @@ static int parse_password(const char *fi _cleanup_free_ char *password = NULL; if (arg_console) { @@ -136,137 +172,208 @@ index 82cbf95..928a5e8 100644 if (tty_fd < 0) return tty_fd; } -@@ -614,8 +696,90 @@ static int parse_argv(int argc, char *argv[]) { +@@ -554,7 +647,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 }, + {} + }; + +@@ -598,6 +691,10 @@ static int parse_argv(int argc, char *ar + + case ARG_CONSOLE: + arg_console = true; ++ if (optarg && *optarg) { ++ current_dev = optarg; ++ arg_device = true; ++ } + break; + + case '?': +@@ -612,9 +709,143 @@ static int parse_argv(int argc, char *ar + return -EINVAL; + } + ++ if (arg_plymouth || arg_console) { ++ ++ if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) { ++ log_error("%s conflicting options --query and --watch.", program_invocation_short_name); ++ return -EINVAL; ++ } ++ ++ if (arg_plymouth && arg_console) { ++ log_error("%s conflicting options --plymouth and --console.", program_invocation_short_name); ++ return -EINVAL; ++ } ++ } ++ return 1; } -+static unsigned int wfa_child(const struct console * con, const unsigned int id) -+{ -+ setsid(); -+ release_terminal(); -+ *usemask |= 1 << id; /* shared memory area */ -+ current_dev = con[id].tty; -+ return id; -+} -+ -+static unsigned int wait_for_answer(void) -+{ -+ struct console *consoles; ++/* ++ * 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[]) { ++ struct console *consoles = NULL; + struct sigaction sig = { + .sa_handler = chld_handler, + .sa_flags = SA_NOCLDSTOP | SA_RESTART, + }; + struct sigaction oldsig; -+ sigset_t set, oldset; ++ sigset_t oldset; + unsigned int num = 0, id; -+ int status = 0, ret; -+ pid_t job; ++ siginfo_t status = {}; ++ int ret; + -+ consoles = collect_consoles(&num); -+ if (!consoles) { -+ log_error("Failed to query password: %m"); -+ exit(EXIT_FAILURE); -+ } -+ if (num < 2) -+ return wfa_child(consoles, 0); ++ ret = collect_consoles(&consoles, &num); ++ if (ret < 0) ++ return log_error_errno(ret, "Failed to query password: %m"); ++ ++ assert_se(sigprocmask_many(SIG_UNBLOCK, &oldset, SIGHUP, SIGCHLD, -1) >= 0); ++ ++ assert_se(sigemptyset(&sig.sa_mask) >= 0); ++ assert_se(sigaction(SIGCHLD, &sig, &oldsig) >= 0); + -+ assert_se(sigemptyset(&set) == 0); -+ assert_se(sigaddset(&set, SIGHUP) == 0); -+ assert_se(sigaddset(&set, SIGCHLD) == 0); -+ assert_se(sigemptyset(&sig.sa_mask) == 0); -+ assert_se(sigprocmask(SIG_UNBLOCK, &set, &oldset) == 0); -+ assert_se(sigaction(SIGCHLD, &sig, &oldsig) == 0); + sig.sa_handler = SIG_DFL; -+ assert_se(sigaction(SIGHUP, &sig, NULL) == 0); ++ assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); + + for (id = 0; id < num; id++) { + consoles[id].pid = fork(); + -+ if (consoles[id].pid < 0) { -+ log_error("Failed to query password: %m"); -+ exit(EXIT_FAILURE); -+ } ++ if (consoles[id].pid < 0) ++ return log_error_errno(errno, "Failed to query password: %m"); + + if (consoles[id].pid == 0) { -+ if (prctl(PR_SET_PDEATHSIG, SIGHUP) < 0) -+ _exit(EXIT_FAILURE); ++ char *conarg; ++ int ac; ++ ++ conarg = strjoina("--console=", consoles[id].tty); ++ if (!conarg) ++ return log_oom(); ++ ++ free_consoles(consoles, num); /* not used anymore */ ++ ++ assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); ++ + zero(sig); -+ assert_se(sigprocmask(SIG_UNBLOCK, &oldset, NULL) == 0); -+ assert_se(sigaction(SIGCHLD, &oldsig, NULL) == 0); -+ return wfa_child(consoles, id); ++ assert_se(sigprocmask(SIG_UNBLOCK, &oldset, NULL) >= 0); ++ assert_se(sigaction(SIGCHLD, &oldsig, NULL) >= 0); ++ ++ for (ac = 0; ac < argc; ac++) { ++ if (streq(argv[ac], "--console")) { ++ argv[ac] = conarg; ++ break; ++ } ++ } ++ ++ execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv); ++ ++ return log_error_errno(errno, "Failed to execute %s: %m", program_invocation_name); + } + } + + ret = 0; -+ while ((job = wait(&status)) != 0) { -+ if (job < 0) { -+ if (errno != EINTR) ++ while (true) { ++ ++ if ((ret = waitid(P_ALL, 0, &status, WEXITED)) < 0) { ++ ++ if (errno != EINTR) { ++ ret = -errno; ++ if (errno == ECHILD) ++ ret = EXIT_SUCCESS; + break; ++ } + continue; + } ++ + for (id = 0; id < num; id++) { -+ if (consoles[id].pid == job || kill(consoles[id].pid, 0) < 0) { -+ *usemask &= ~(1 << id); /* shared memory area */ -+ continue; -+ } -+ if (*usemask & (1 << id)) /* shared memory area */ ++ struct timespec timeout; ++ sigset_t set; ++ int signum; ++ ++ if (consoles[id].pid == status.si_pid || kill(consoles[id].pid, 0) < 0) ++ consoles[id].pid = -1; ++ ++ if (consoles[id].pid < 0) + continue; ++ + kill(consoles[id].pid, SIGHUP); -+ usleep(50000); ++ ++ assert_se(sigemptyset(&set) >= 0); ++ assert_se(sigaddset(&set, SIGCHLD) >= 0); ++ ++ timespec_store(&timeout, 50 * USEC_PER_MSEC); ++ signum = sigtimedwait(&set, NULL, &timeout); ++ ++ if (signum != SIGCHLD) { ++ ++ if (signum < 0 && errno != EAGAIN) ++ return log_error_errno(errno, "sigtimedwait() failed: %m"); ++ ++ if (signum >= 0) ++ log_warning("sigtimedwait() returned unexpected signal."); ++ } ++ + kill(consoles[id].pid, SIGKILL); + } -+ if (WIFEXITED(status) && ret == 0) -+ ret = WEXITSTATUS(status); ++ ++ if (WIFEXITED(status.si_status) && ret == 0) ++ ret = WEXITSTATUS(status.si_status); + } ++ + free_consoles(consoles, num); -+ exit(ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS); /* parent */ ++ ++ return ret; +} + int main(int argc, char *argv[]) { -- int r; -+ int r, id = 0; + int r; - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); -@@ -627,11 +791,27 @@ int main(int argc, char *argv[]) { +@@ -628,15 +859,28 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; -+ /* -+ * Use this shared memory area to be able to synchronize the -+ * workers asking for password with the main process. -+ * This allows to continue if one of the consoles had been -+ * used as afterwards the remaining asking processes will -+ * be terminated. The wait_for_terminate() does not help -+ * for this use case. -+ */ -+ usemask = mmap(NULL, sizeof(*usemask), PROT_READ | PROT_WRITE, -+ MAP_ANONYMOUS | MAP_SHARED, -1, 0); -+ assert_se(usemask != NULL); -+ - if (arg_console) { +- if (arg_console) { - setsid(); - release_terminal(); -+ if (!arg_plymouth && -+ !IN_SET(arg_action, ACTION_WALL, ACTION_LIST)) { -+ id = wait_for_answer(); -+ } else { -+ 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_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 (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL)) ++ r = watch_passwords(); ++ else ++ r = show_passwords(); } - - if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL)) - r = watch_passwords(); - else -@@ -640,6 +820,7 @@ int main(int argc, char *argv[]) { +- if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL)) +- r = watch_passwords(); +- else +- r = show_passwords(); + if (r < 0) log_error_errno(r, "Error: %m"); -+ *usemask &= ~(1 << id); /* shared memory area */ - finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - } -- 2.2.0