--- src/tty-ask-password-agent/tty-ask-password-agent.c | 190 +++++++++++++++++++- 1 file changed, 185 insertions(+), 5 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 2014-07-30 10:48:43.602052750 +0000 @@ -28,8 +28,12 @@ #include #include #include +#include #include +#include +#include #include +#include #include #include "util.h" @@ -41,6 +45,9 @@ #include "ask-password-api.h" #include "strv.h" #include "build.h" +#include "fileio.h" +#include "macro.h" +#include "list.h" static enum { ACTION_LIST, @@ -49,6 +56,21 @@ static enum { ACTION_WALL } arg_action = ACTION_QUERY; +struct console { + LIST_FIELDS(struct console, handle); + char *tty; + pid_t pid; + int id; +}; + +static volatile uint32_t *usemask; +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; @@ -246,12 +268,77 @@ finish: return r; } +static const char *current_dev = "/dev/console"; +static LIST_HEAD(struct console, consoles); +static int collect_consoles(void) { + _cleanup_free_ char *active = NULL; + char *w, *state; + struct console *c; + size_t l; + int id; + int r; + + r = read_one_line_file("/sys/class/tty/console/active", &active); + if (r < 0) + return r; + + id = 0; + FOREACH_WORD(w, l, active, state) { + _cleanup_free_ char *tty = NULL; + + if (strneq(w, "tty0", l)) { + if (read_one_line_file("/sys/class/tty/tty0/active", &tty) >= 0) { + w = tty; + l = strlen(tty); + } + } + + c = malloc0(sizeof(struct console)+5+l+1); + if (!c) { + log_oom(); + continue; + } + + c->tty = ((char*)c)+sizeof(struct console); + stpncpy(stpcpy(c->tty, "/dev/"),w,l); + c->id = id++; + + LIST_PREPEND(handle, consoles, c); + } + + if (!consoles) { + + c = malloc0(sizeof(struct console)); + if (!c) { + log_oom(); + return -ENOMEM; + } + + c->tty = (char *)current_dev; + c->id = id++; + + LIST_PREPEND(handle, consoles, c); + } + + return 0; +} + +static void free_consoles(void) { + struct console *c; + LIST_FOREACH(handle, c, consoles) { + LIST_REMOVE(handle, consoles, c); + free(c); + } + LIST_HEAD_INIT(consoles); +} + 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 +410,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 +451,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(current_dev, false, false, true, (usec_t) -1)) < 0) { r = tty_fd; goto finish; } @@ -386,6 +472,7 @@ static int parse_password(const char *fi strcpy(packet+1, password); } + memset(password, 0, strlen(password)); free(password); } } @@ -423,6 +510,7 @@ finish: if (socket_fd >= 0) close_nointr_nofail(socket_fd); + memset(packet, 0, packet_length); free(packet); free(socket_name); free(message); @@ -726,8 +814,10 @@ static int parse_argv(int argc, char *ar } int main(int argc, char *argv[]) { + int id = 0; int r; + LIST_HEAD_INIT(consoles); log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); @@ -737,11 +827,99 @@ int main(int argc, char *argv[]) { if ((r = parse_argv(argc, argv)) <= 0) goto finish; + usemask = (uint32_t*) mmap(NULL, sizeof(uint32_t), PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_SHARED, -1, 0); + if (arg_console) { - setsid(); - release_terminal(); - } + if (!arg_plymouth && arg_action != ACTION_WALL && + arg_action != ACTION_LIST) { + struct console *c; + struct sigaction sig = { + .sa_handler = chld_handler, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; + struct sigaction oldsig; + sigset_t set, oldset; + int status = 0; + pid_t job; + + collect_consoles(); + + if (!consoles->handle_next) { + consoles->pid = 0; + c = consoles; + goto nofork; + } + 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); + LIST_FOREACH(handle, c, consoles) { + + switch ((c->pid = fork())) { + case 0: + if (prctl(PR_SET_PDEATHSIG, SIGHUP) < 0) + _exit(EXIT_FAILURE); + zero(sig); + assert_se(sigprocmask(SIG_UNBLOCK, &oldset, NULL) == 0); + assert_se(sigaction(SIGCHLD, &oldsig, NULL) == 0); + /* fall through */ + nofork: + setsid(); + release_terminal(); + id = c->id; + *usemask |= (1<tty; + goto forked; /* child */ + case -1: + log_error("Failed to query password: %s", strerror(errno)); + return EXIT_FAILURE; + default: + break; + } + } + + r = 0; + while ((job = wait(&status))) { + if (job < 0) { + if (errno != EINTR) + break; + continue; + } + LIST_FOREACH(handle, c, consoles) { + if (c->pid == job) { + *usemask &= ~(1<id); + continue; + } + if (kill(c->pid, 0) < 0) { + *usemask &= ~(1<id); + continue; + } + if (*usemask & (1<id)) + continue; + kill(c->pid, SIGHUP); + usleep(50000); + kill(c->pid, SIGKILL); + } + + if (WIFEXITED(status) && !r) + r = WEXITSTATUS(status); + } + free_consoles(); + return r != 0 ? EXIT_FAILURE : EXIT_SUCCESS; /* parent */ + + } else { + setsid(); + release_terminal(); + } + } +forked: if (arg_action == ACTION_WATCH || arg_action == ACTION_WALL) r = watch_passwords(); @@ -751,6 +929,8 @@ int main(int argc, char *argv[]) { if (r < 0) log_error("Error: %s", strerror(-r)); + free_consoles(); + *usemask &= ~(1<