]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tty-ask-password-agent/tty-ask-password-agent.c
ask-password: ask for passphrases not only on the first console of /dev/console
[thirdparty/systemd.git] / src / tty-ask-password-agent / tty-ask-password-agent.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5 Copyright 2015 Werner Fink
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <poll.h>
25 #include <signal.h>
26 #include <stdbool.h>
27 #include <stddef.h>
28 #include <string.h>
29 #include <sys/inotify.h>
30 #include <sys/prctl.h>
31 #include <sys/signalfd.h>
32 #include <sys/socket.h>
33 #include <sys/wait.h>
34 #include <sys/un.h>
35 #include <unistd.h>
36
37 #include "alloc-util.h"
38 #include "ask-password-api.h"
39 #include "conf-parser.h"
40 #include "def.h"
41 #include "dirent-util.h"
42 #include "exit-status.h"
43 #include "fd-util.h"
44 #include "fileio.h"
45 #include "hashmap.h"
46 #include "io-util.h"
47 #include "macro.h"
48 #include "mkdir.h"
49 #include "path-util.h"
50 #include "process-util.h"
51 #include "signal-util.h"
52 #include "socket-util.h"
53 #include "string-util.h"
54 #include "strv.h"
55 #include "terminal-util.h"
56 #include "util.h"
57 #include "utmp-wtmp.h"
58
59 static enum {
60 ACTION_LIST,
61 ACTION_QUERY,
62 ACTION_WATCH,
63 ACTION_WALL
64 } arg_action = ACTION_QUERY;
65
66 static bool arg_plymouth = false;
67 static bool arg_console = false;
68 static const char *arg_device = NULL;
69
70 static int ask_password_plymouth(
71 const char *message,
72 usec_t until,
73 AskPasswordFlags flags,
74 const char *flag_file,
75 char ***ret) {
76
77 static const union sockaddr_union sa = PLYMOUTH_SOCKET;
78 _cleanup_close_ int fd = -1, notify = -1;
79 _cleanup_free_ char *packet = NULL;
80 ssize_t k;
81 int r, n;
82 struct pollfd pollfd[2] = {};
83 char buffer[LINE_MAX];
84 size_t p = 0;
85 enum {
86 POLL_SOCKET,
87 POLL_INOTIFY
88 };
89
90 assert(ret);
91
92 if (flag_file) {
93 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
94 if (notify < 0)
95 return -errno;
96
97 r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
98 if (r < 0)
99 return -errno;
100 }
101
102 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
103 if (fd < 0)
104 return -errno;
105
106 r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
107 if (r < 0)
108 return -errno;
109
110 if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
111 packet = strdup("c");
112 n = 1;
113 } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
114 packet = NULL;
115 if (!packet)
116 return -ENOMEM;
117
118 r = loop_write(fd, packet, n + 1, true);
119 if (r < 0)
120 return r;
121
122 pollfd[POLL_SOCKET].fd = fd;
123 pollfd[POLL_SOCKET].events = POLLIN;
124 pollfd[POLL_INOTIFY].fd = notify;
125 pollfd[POLL_INOTIFY].events = POLLIN;
126
127 for (;;) {
128 int sleep_for = -1, j;
129
130 if (until > 0) {
131 usec_t y;
132
133 y = now(CLOCK_MONOTONIC);
134
135 if (y > until) {
136 r = -ETIME;
137 goto finish;
138 }
139
140 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
141 }
142
143 if (flag_file && access(flag_file, F_OK) < 0) {
144 r = -errno;
145 goto finish;
146 }
147
148 j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
149 if (j < 0) {
150 if (errno == EINTR)
151 continue;
152
153 r = -errno;
154 goto finish;
155 } else if (j == 0) {
156 r = -ETIME;
157 goto finish;
158 }
159
160 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
161 flush_fd(notify);
162
163 if (pollfd[POLL_SOCKET].revents == 0)
164 continue;
165
166 k = read(fd, buffer + p, sizeof(buffer) - p);
167 if (k < 0) {
168 if (errno == EINTR || errno == EAGAIN)
169 continue;
170
171 r = -errno;
172 goto finish;
173 } else if (k == 0) {
174 r = -EIO;
175 goto finish;
176 }
177
178 p += k;
179
180 if (p < 1)
181 continue;
182
183 if (buffer[0] == 5) {
184
185 if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
186 /* Hmm, first try with cached
187 * passwords failed, so let's retry
188 * with a normal password request */
189 packet = mfree(packet);
190
191 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
192 r = -ENOMEM;
193 goto finish;
194 }
195
196 r = loop_write(fd, packet, n+1, true);
197 if (r < 0)
198 goto finish;
199
200 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
201 p = 0;
202 continue;
203 }
204
205 /* No password, because UI not shown */
206 r = -ENOENT;
207 goto finish;
208
209 } else if (buffer[0] == 2 || buffer[0] == 9) {
210 uint32_t size;
211 char **l;
212
213 /* One or more answers */
214 if (p < 5)
215 continue;
216
217 memcpy(&size, buffer+1, sizeof(size));
218 size = le32toh(size);
219 if (size + 5 > sizeof(buffer)) {
220 r = -EIO;
221 goto finish;
222 }
223
224 if (p-5 < size)
225 continue;
226
227 l = strv_parse_nulstr(buffer + 5, size);
228 if (!l) {
229 r = -ENOMEM;
230 goto finish;
231 }
232
233 *ret = l;
234 break;
235
236 } else {
237 /* Unknown packet */
238 r = -EIO;
239 goto finish;
240 }
241 }
242
243 r = 0;
244
245 finish:
246 memory_erase(buffer, sizeof(buffer));
247 return r;
248 }
249
250 static int send_passwords(const char *socket_name, char **passwords) {
251 _cleanup_free_ char *packet = NULL;
252 _cleanup_close_ int socket_fd = -1;
253 union sockaddr_union sa = { .un.sun_family = AF_UNIX };
254 size_t packet_length = 1;
255 char **p, *d;
256 int r;
257
258 assert(socket_name);
259
260 STRV_FOREACH(p, passwords)
261 packet_length += strlen(*p) + 1;
262
263 packet = new(char, packet_length);
264 if (!packet)
265 return -ENOMEM;
266
267 packet[0] = '+';
268
269 d = packet + 1;
270 STRV_FOREACH(p, passwords)
271 d = stpcpy(d, *p) + 1;
272
273 socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
274 if (socket_fd < 0) {
275 r = log_debug_errno(errno, "socket(): %m");
276 goto finish;
277 }
278
279 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
280
281 r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
282 if (r < 0)
283 r = log_debug_errno(errno, "sendto(): %m");
284
285 finish:
286 memory_erase(packet, packet_length);
287 return r;
288 }
289
290 static int parse_password(const char *filename, char **wall) {
291 _cleanup_free_ char *socket_name = NULL, *message = NULL;
292 bool accept_cached = false, echo = false;
293 uint64_t not_after = 0;
294 unsigned pid = 0;
295
296 const ConfigTableItem items[] = {
297 { "Ask", "Socket", config_parse_string, 0, &socket_name },
298 { "Ask", "NotAfter", config_parse_uint64, 0, &not_after },
299 { "Ask", "Message", config_parse_string, 0, &message },
300 { "Ask", "PID", config_parse_unsigned, 0, &pid },
301 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
302 { "Ask", "Echo", config_parse_bool, 0, &echo },
303 {}
304 };
305
306 int r;
307
308 assert(filename);
309
310 r = config_parse(NULL, filename, NULL,
311 NULL,
312 config_item_table_lookup, items,
313 true, false, true, NULL);
314 if (r < 0)
315 return r;
316
317 if (!socket_name) {
318 log_error("Invalid password file %s", filename);
319 return -EBADMSG;
320 }
321
322 if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
323 return 0;
324
325 if (pid > 0 && !pid_is_alive(pid))
326 return 0;
327
328 if (arg_action == ACTION_LIST)
329 printf("'%s' (PID %u)\n", message, pid);
330
331 else if (arg_action == ACTION_WALL) {
332 char *_wall;
333
334 if (asprintf(&_wall,
335 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
336 "Please enter password with the systemd-tty-ask-password-agent tool!",
337 strempty(*wall),
338 *wall ? "\r\n\r\n" : "",
339 message,
340 pid) < 0)
341 return log_oom();
342
343 free(*wall);
344 *wall = _wall;
345
346 } else {
347 _cleanup_strv_free_erase_ char **passwords = NULL;
348
349 assert(arg_action == ACTION_QUERY ||
350 arg_action == ACTION_WATCH);
351
352 if (access(socket_name, W_OK) < 0) {
353 if (arg_action == ACTION_QUERY)
354 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
355
356 return 0;
357 }
358
359 if (arg_plymouth)
360 r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
361 else {
362 char *password = NULL;
363 int tty_fd = -1;
364
365 if (arg_console) {
366 const char *con = arg_device ? arg_device : "/dev/console";
367
368 tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY);
369 if (tty_fd < 0)
370 return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
371
372 r = reset_terminal_fd(tty_fd, true);
373 if (r < 0)
374 log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
375 }
376
377 r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
378
379 if (arg_console) {
380 tty_fd = safe_close(tty_fd);
381 release_terminal();
382 }
383
384 if (r >= 0)
385 r = strv_push(&passwords, password);
386
387 if (r < 0)
388 string_free_erase(password);
389 }
390
391 /* If the query went away, that's OK */
392 if (IN_SET(r, -ETIME, -ENOENT))
393 return 0;
394
395 if (r < 0)
396 return log_error_errno(r, "Failed to query password: %m");
397
398 r = send_passwords(socket_name, passwords);
399 if (r < 0)
400 return log_error_errno(r, "Failed to send: %m");
401 }
402
403 return 0;
404 }
405
406 static int wall_tty_block(void) {
407 _cleanup_free_ char *p = NULL;
408 dev_t devnr;
409 int fd, r;
410
411 r = get_ctty_devnr(0, &devnr);
412 if (r == -ENXIO) /* We have no controlling tty */
413 return -ENOTTY;
414 if (r < 0)
415 return log_error_errno(r, "Failed to get controlling TTY: %m");
416
417 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
418 return log_oom();
419
420 mkdir_parents_label(p, 0700);
421 mkfifo(p, 0600);
422
423 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
424 if (fd < 0)
425 return log_debug_errno(errno, "Failed to open %s: %m", p);
426
427 return fd;
428 }
429
430 static bool wall_tty_match(const char *path, void *userdata) {
431 _cleanup_free_ char *p = NULL;
432 _cleanup_close_ int fd = -1;
433 struct stat st;
434
435 if (!path_is_absolute(path))
436 path = strjoina("/dev/", path);
437
438 if (lstat(path, &st) < 0) {
439 log_debug_errno(errno, "Failed to stat %s: %m", path);
440 return true;
441 }
442
443 if (!S_ISCHR(st.st_mode)) {
444 log_debug("%s is not a character device.", path);
445 return true;
446 }
447
448 /* We use named pipes to ensure that wall messages suggesting
449 * password entry are not printed over password prompts
450 * already shown. We use the fact here that opening a pipe in
451 * non-blocking mode for write-only will succeed only if
452 * there's some writer behind it. Using pipes has the
453 * advantage that the block will automatically go away if the
454 * process dies. */
455
456 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
457 log_oom();
458 return true;
459 }
460
461 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
462 if (fd < 0) {
463 log_debug_errno(errno, "Failed top open the wall pipe: %m");
464 return 1;
465 }
466
467 /* What, we managed to open the pipe? Then this tty is filtered. */
468 return 0;
469 }
470
471 static int show_passwords(void) {
472 _cleanup_closedir_ DIR *d;
473 struct dirent *de;
474 int r = 0;
475
476 d = opendir("/run/systemd/ask-password");
477 if (!d) {
478 if (errno == ENOENT)
479 return 0;
480
481 return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
482 }
483
484 FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
485 _cleanup_free_ char *p = NULL, *wall = NULL;
486 int q;
487
488 /* We only support /dev on tmpfs, hence we can rely on
489 * d_type to be reliable */
490
491 if (de->d_type != DT_REG)
492 continue;
493
494 if (hidden_or_backup_file(de->d_name))
495 continue;
496
497 if (!startswith(de->d_name, "ask."))
498 continue;
499
500 p = strappend("/run/systemd/ask-password/", de->d_name);
501 if (!p)
502 return log_oom();
503
504 q = parse_password(p, &wall);
505 if (q < 0 && r == 0)
506 r = q;
507
508 if (wall)
509 (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
510 }
511
512 return r;
513 }
514
515 static int watch_passwords(void) {
516 enum {
517 FD_INOTIFY,
518 FD_SIGNAL,
519 _FD_MAX
520 };
521
522 _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
523 struct pollfd pollfd[_FD_MAX] = {};
524 sigset_t mask;
525 int r;
526
527 tty_block_fd = wall_tty_block();
528
529 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
530
531 notify = inotify_init1(IN_CLOEXEC);
532 if (notify < 0)
533 return log_error_errno(errno, "Failed to allocate directory watch: %m");
534
535 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
536 return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
537
538 assert_se(sigemptyset(&mask) >= 0);
539 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
540 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
541
542 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
543 if (signal_fd < 0)
544 return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
545
546 pollfd[FD_INOTIFY].fd = notify;
547 pollfd[FD_INOTIFY].events = POLLIN;
548 pollfd[FD_SIGNAL].fd = signal_fd;
549 pollfd[FD_SIGNAL].events = POLLIN;
550
551 for (;;) {
552 r = show_passwords();
553 if (r < 0)
554 log_error_errno(r, "Failed to show password: %m");
555
556 if (poll(pollfd, _FD_MAX, -1) < 0) {
557 if (errno == EINTR)
558 continue;
559
560 return -errno;
561 }
562
563 if (pollfd[FD_INOTIFY].revents != 0)
564 (void) flush_fd(notify);
565
566 if (pollfd[FD_SIGNAL].revents != 0)
567 break;
568 }
569
570 return 0;
571 }
572
573 static void help(void) {
574 printf("%s [OPTIONS...]\n\n"
575 "Process system password requests.\n\n"
576 " -h --help Show this help\n"
577 " --version Show package version\n"
578 " --list Show pending password requests\n"
579 " --query Process pending password requests\n"
580 " --watch Continuously process password requests\n"
581 " --wall Continuously forward password requests to wall\n"
582 " --plymouth Ask question with Plymouth instead of on TTY\n"
583 " --console Ask question on /dev/console instead of current TTY\n",
584 program_invocation_short_name);
585 }
586
587 static int parse_argv(int argc, char *argv[]) {
588
589 enum {
590 ARG_LIST = 0x100,
591 ARG_QUERY,
592 ARG_WATCH,
593 ARG_WALL,
594 ARG_PLYMOUTH,
595 ARG_CONSOLE,
596 ARG_VERSION
597 };
598
599 static const struct option options[] = {
600 { "help", no_argument, NULL, 'h' },
601 { "version", no_argument, NULL, ARG_VERSION },
602 { "list", no_argument, NULL, ARG_LIST },
603 { "query", no_argument, NULL, ARG_QUERY },
604 { "watch", no_argument, NULL, ARG_WATCH },
605 { "wall", no_argument, NULL, ARG_WALL },
606 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
607 { "console", optional_argument, NULL, ARG_CONSOLE },
608 {}
609 };
610
611 int c;
612
613 assert(argc >= 0);
614 assert(argv);
615
616 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
617
618 switch (c) {
619
620 case 'h':
621 help();
622 return 0;
623
624 case ARG_VERSION:
625 return version();
626
627 case ARG_LIST:
628 arg_action = ACTION_LIST;
629 break;
630
631 case ARG_QUERY:
632 arg_action = ACTION_QUERY;
633 break;
634
635 case ARG_WATCH:
636 arg_action = ACTION_WATCH;
637 break;
638
639 case ARG_WALL:
640 arg_action = ACTION_WALL;
641 break;
642
643 case ARG_PLYMOUTH:
644 arg_plymouth = true;
645 break;
646
647 case ARG_CONSOLE:
648 arg_console = true;
649 if (optarg) {
650
651 if (isempty(optarg)) {
652 log_error("Empty console device path is not allowed.");
653 return -EINVAL;
654 }
655
656 arg_device = optarg;
657 }
658 break;
659
660 case '?':
661 return -EINVAL;
662
663 default:
664 assert_not_reached("Unhandled option");
665 }
666
667 if (optind != argc) {
668 log_error("%s takes no arguments.", program_invocation_short_name);
669 return -EINVAL;
670 }
671
672 if (arg_plymouth || arg_console) {
673
674 if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) {
675 log_error("Options --query and --watch conflict.");
676 return -EINVAL;
677 }
678
679 if (arg_plymouth && arg_console) {
680 log_error("Options --plymouth and --console conflict.");
681 return -EINVAL;
682 }
683 }
684
685 return 1;
686 }
687
688 /*
689 * To be able to ask on all terminal devices of /dev/console
690 * the devices are collected. If more than one device is found,
691 * then on each of the terminals a inquiring task is forked.
692 * Every task has its own session and its own controlling terminal.
693 * If one of the tasks does handle a password, the remaining tasks
694 * will be terminated.
695 */
696 static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) {
697 struct sigaction sig = {
698 .sa_handler = nop_signal_handler,
699 .sa_flags = SA_NOCLDSTOP | SA_RESTART,
700 };
701
702 assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
703
704 assert_se(sigemptyset(&sig.sa_mask) >= 0);
705 assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0);
706
707 sig.sa_handler = SIG_DFL;
708 assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
709
710 *pid = fork();
711 if (*pid < 0)
712 return log_error_errno(errno, "Failed to fork process: %m");
713
714 if (*pid == 0) {
715 int ac;
716
717 assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
718
719 reset_signal_mask();
720 reset_all_signal_handlers();
721
722 for (ac = 0; ac < argc; ac++) {
723 if (streq(argv[ac], "--console")) {
724 argv[ac] = strjoina("--console=", tty, NULL);
725 break;
726 }
727 }
728
729 assert(ac < argc);
730
731 execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv);
732 _exit(EXIT_FAILURE);
733 }
734 return 0;
735 }
736
737 static void terminate_agents(Set *pids) {
738 struct timespec ts;
739 siginfo_t status = {};
740 sigset_t set;
741 Iterator i;
742 void *p;
743 int r, signum;
744
745 /*
746 * Request termination of the remaining processes as those
747 * are not required anymore.
748 */
749 SET_FOREACH(p, pids, i)
750 (void) kill(PTR_TO_PID(p), SIGTERM);
751
752 /*
753 * Collect the processes which have go away.
754 */
755 assert_se(sigemptyset(&set) >= 0);
756 assert_se(sigaddset(&set, SIGCHLD) >= 0);
757 timespec_store(&ts, 50 * USEC_PER_MSEC);
758
759 while (!set_isempty(pids)) {
760
761 zero(status);
762 r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
763 if (r < 0 && errno == EINTR)
764 continue;
765
766 if (r == 0 && status.si_pid > 0) {
767 set_remove(pids, PID_TO_PTR(status.si_pid));
768 continue;
769 }
770
771 signum = sigtimedwait(&set, NULL, &ts);
772 if (signum < 0) {
773 if (errno != EAGAIN)
774 log_error_errno(errno, "sigtimedwait() failed: %m");
775 break;
776 }
777 assert(signum == SIGCHLD);
778 }
779
780 /*
781 * Kill hanging processes.
782 */
783 SET_FOREACH(p, pids, i) {
784 log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
785 (void) kill(PTR_TO_PID(p), SIGKILL);
786 }
787 }
788
789 static int ask_on_consoles(int argc, char *argv[]) {
790 _cleanup_set_free_ Set *pids = NULL;
791 _cleanup_strv_free_ char **consoles = NULL;
792 siginfo_t status = {};
793 char **tty;
794 pid_t pid;
795 int r;
796
797 r = get_kernel_consoles(&consoles);
798 if (r < 0)
799 return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
800
801 pids = set_new(NULL);
802 if (!pids)
803 return log_oom();
804
805 /* Start an agent on each console. */
806 STRV_FOREACH(tty, consoles) {
807 r = ask_on_this_console(*tty, &pid, argc, argv);
808 if (r < 0)
809 return r;
810
811 if (set_put(pids, PID_TO_PTR(pid)) < 0)
812 return log_oom();
813 }
814
815 /* Wait for an agent to exit. */
816 for (;;) {
817 zero(status);
818
819 if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
820 if (errno == EINTR)
821 continue;
822
823 return log_error_errno(errno, "waitid() failed: %m");
824 }
825
826 set_remove(pids, PID_TO_PTR(status.si_pid));
827 break;
828 }
829
830 if (!is_clean_exit(status.si_code, status.si_status, NULL))
831 log_error("Password agent failed with: %d", status.si_status);
832
833 terminate_agents(pids);
834 return 0;
835 }
836
837 int main(int argc, char *argv[]) {
838 int r;
839
840 log_set_target(LOG_TARGET_AUTO);
841 log_parse_environment();
842 log_open();
843
844 umask(0022);
845
846 r = parse_argv(argc, argv);
847 if (r <= 0)
848 goto finish;
849
850 if (arg_console && !arg_device)
851 /*
852 * Spawn for each console device a separate process.
853 */
854 r = ask_on_consoles(argc, argv);
855 else {
856
857 if (arg_device) {
858 /*
859 * Later on, a controlling terminal will be acquired,
860 * therefore the current process has to become a session
861 * leader and should not have a controlling terminal already.
862 */
863 (void) setsid();
864 (void) release_terminal();
865 }
866
867 if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
868 r = watch_passwords();
869 else
870 r = show_passwords();
871 }
872
873 finish:
874 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
875 }