]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tty-ask-password-agent/tty-ask-password-agent.c
conf-parser: turn three bool function params into a flags fields
[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 (IN_SET(errno, EINTR, 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 (IN_SET(buffer[0], 2, 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 explicit_bzero(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 explicit_bzero(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 CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, 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(IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH));
350
351 if (access(socket_name, W_OK) < 0) {
352 if (arg_action == ACTION_QUERY)
353 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
354
355 return 0;
356 }
357
358 if (arg_plymouth)
359 r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
360 else {
361 char *password = NULL;
362 int tty_fd = -1;
363
364 if (arg_console) {
365 const char *con = arg_device ? arg_device : "/dev/console";
366
367 tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY);
368 if (tty_fd < 0)
369 return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
370
371 r = reset_terminal_fd(tty_fd, true);
372 if (r < 0)
373 log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
374 }
375
376 r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
377
378 if (arg_console) {
379 tty_fd = safe_close(tty_fd);
380 release_terminal();
381 }
382
383 if (r >= 0)
384 r = strv_push(&passwords, password);
385
386 if (r < 0)
387 string_free_erase(password);
388 }
389
390 /* If the query went away, that's OK */
391 if (IN_SET(r, -ETIME, -ENOENT))
392 return 0;
393
394 if (r < 0)
395 return log_error_errno(r, "Failed to query password: %m");
396
397 r = send_passwords(socket_name, passwords);
398 if (r < 0)
399 return log_error_errno(r, "Failed to send: %m");
400 }
401
402 return 0;
403 }
404
405 static int wall_tty_block(void) {
406 _cleanup_free_ char *p = NULL;
407 dev_t devnr;
408 int fd, r;
409
410 r = get_ctty_devnr(0, &devnr);
411 if (r == -ENXIO) /* We have no controlling tty */
412 return -ENOTTY;
413 if (r < 0)
414 return log_error_errno(r, "Failed to get controlling TTY: %m");
415
416 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
417 return log_oom();
418
419 mkdir_parents_label(p, 0700);
420 mkfifo(p, 0600);
421
422 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
423 if (fd < 0)
424 return log_debug_errno(errno, "Failed to open %s: %m", p);
425
426 return fd;
427 }
428
429 static bool wall_tty_match(const char *path, void *userdata) {
430 _cleanup_free_ char *p = NULL;
431 _cleanup_close_ int fd = -1;
432 struct stat st;
433
434 if (!path_is_absolute(path))
435 path = strjoina("/dev/", path);
436
437 if (lstat(path, &st) < 0) {
438 log_debug_errno(errno, "Failed to stat %s: %m", path);
439 return true;
440 }
441
442 if (!S_ISCHR(st.st_mode)) {
443 log_debug("%s is not a character device.", path);
444 return true;
445 }
446
447 /* We use named pipes to ensure that wall messages suggesting
448 * password entry are not printed over password prompts
449 * already shown. We use the fact here that opening a pipe in
450 * non-blocking mode for write-only will succeed only if
451 * there's some writer behind it. Using pipes has the
452 * advantage that the block will automatically go away if the
453 * process dies. */
454
455 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
456 log_oom();
457 return true;
458 }
459
460 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
461 if (fd < 0) {
462 log_debug_errno(errno, "Failed top open the wall pipe: %m");
463 return 1;
464 }
465
466 /* What, we managed to open the pipe? Then this tty is filtered. */
467 return 0;
468 }
469
470 static int show_passwords(void) {
471 _cleanup_closedir_ DIR *d;
472 struct dirent *de;
473 int r = 0;
474
475 d = opendir("/run/systemd/ask-password");
476 if (!d) {
477 if (errno == ENOENT)
478 return 0;
479
480 return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
481 }
482
483 FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
484 _cleanup_free_ char *p = NULL, *wall = NULL;
485 int q;
486
487 /* We only support /dev on tmpfs, hence we can rely on
488 * d_type to be reliable */
489
490 if (de->d_type != DT_REG)
491 continue;
492
493 if (hidden_or_backup_file(de->d_name))
494 continue;
495
496 if (!startswith(de->d_name, "ask."))
497 continue;
498
499 p = strappend("/run/systemd/ask-password/", de->d_name);
500 if (!p)
501 return log_oom();
502
503 q = parse_password(p, &wall);
504 if (q < 0 && r == 0)
505 r = q;
506
507 if (wall)
508 (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
509 }
510
511 return r;
512 }
513
514 static int watch_passwords(void) {
515 enum {
516 FD_INOTIFY,
517 FD_SIGNAL,
518 _FD_MAX
519 };
520
521 _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
522 struct pollfd pollfd[_FD_MAX] = {};
523 sigset_t mask;
524 int r;
525
526 tty_block_fd = wall_tty_block();
527
528 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
529
530 notify = inotify_init1(IN_CLOEXEC);
531 if (notify < 0)
532 return log_error_errno(errno, "Failed to allocate directory watch: %m");
533
534 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
535 return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
536
537 assert_se(sigemptyset(&mask) >= 0);
538 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
539 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
540
541 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
542 if (signal_fd < 0)
543 return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
544
545 pollfd[FD_INOTIFY].fd = notify;
546 pollfd[FD_INOTIFY].events = POLLIN;
547 pollfd[FD_SIGNAL].fd = signal_fd;
548 pollfd[FD_SIGNAL].events = POLLIN;
549
550 for (;;) {
551 r = show_passwords();
552 if (r < 0)
553 log_error_errno(r, "Failed to show password: %m");
554
555 if (poll(pollfd, _FD_MAX, -1) < 0) {
556 if (errno == EINTR)
557 continue;
558
559 return -errno;
560 }
561
562 if (pollfd[FD_INOTIFY].revents != 0)
563 (void) flush_fd(notify);
564
565 if (pollfd[FD_SIGNAL].revents != 0)
566 break;
567 }
568
569 return 0;
570 }
571
572 static void help(void) {
573 printf("%s [OPTIONS...]\n\n"
574 "Process system password requests.\n\n"
575 " -h --help Show this help\n"
576 " --version Show package version\n"
577 " --list Show pending password requests\n"
578 " --query Process pending password requests\n"
579 " --watch Continuously process password requests\n"
580 " --wall Continuously forward password requests to wall\n"
581 " --plymouth Ask question with Plymouth instead of on TTY\n"
582 " --console Ask question on /dev/console instead of current TTY\n",
583 program_invocation_short_name);
584 }
585
586 static int parse_argv(int argc, char *argv[]) {
587
588 enum {
589 ARG_LIST = 0x100,
590 ARG_QUERY,
591 ARG_WATCH,
592 ARG_WALL,
593 ARG_PLYMOUTH,
594 ARG_CONSOLE,
595 ARG_VERSION
596 };
597
598 static const struct option options[] = {
599 { "help", no_argument, NULL, 'h' },
600 { "version", no_argument, NULL, ARG_VERSION },
601 { "list", no_argument, NULL, ARG_LIST },
602 { "query", no_argument, NULL, ARG_QUERY },
603 { "watch", no_argument, NULL, ARG_WATCH },
604 { "wall", no_argument, NULL, ARG_WALL },
605 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
606 { "console", optional_argument, NULL, ARG_CONSOLE },
607 {}
608 };
609
610 int c;
611
612 assert(argc >= 0);
613 assert(argv);
614
615 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
616
617 switch (c) {
618
619 case 'h':
620 help();
621 return 0;
622
623 case ARG_VERSION:
624 return version();
625
626 case ARG_LIST:
627 arg_action = ACTION_LIST;
628 break;
629
630 case ARG_QUERY:
631 arg_action = ACTION_QUERY;
632 break;
633
634 case ARG_WATCH:
635 arg_action = ACTION_WATCH;
636 break;
637
638 case ARG_WALL:
639 arg_action = ACTION_WALL;
640 break;
641
642 case ARG_PLYMOUTH:
643 arg_plymouth = true;
644 break;
645
646 case ARG_CONSOLE:
647 arg_console = true;
648 if (optarg) {
649
650 if (isempty(optarg)) {
651 log_error("Empty console device path is not allowed.");
652 return -EINVAL;
653 }
654
655 arg_device = optarg;
656 }
657 break;
658
659 case '?':
660 return -EINVAL;
661
662 default:
663 assert_not_reached("Unhandled option");
664 }
665
666 if (optind != argc) {
667 log_error("%s takes no arguments.", program_invocation_short_name);
668 return -EINVAL;
669 }
670
671 if (arg_plymouth || arg_console) {
672
673 if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) {
674 log_error("Options --query and --watch conflict.");
675 return -EINVAL;
676 }
677
678 if (arg_plymouth && arg_console) {
679 log_error("Options --plymouth and --console conflict.");
680 return -EINVAL;
681 }
682 }
683
684 return 1;
685 }
686
687 /*
688 * To be able to ask on all terminal devices of /dev/console
689 * the devices are collected. If more than one device is found,
690 * then on each of the terminals a inquiring task is forked.
691 * Every task has its own session and its own controlling terminal.
692 * If one of the tasks does handle a password, the remaining tasks
693 * will be terminated.
694 */
695 static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) {
696 struct sigaction sig = {
697 .sa_handler = nop_signal_handler,
698 .sa_flags = SA_NOCLDSTOP | SA_RESTART,
699 };
700
701 assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
702
703 assert_se(sigemptyset(&sig.sa_mask) >= 0);
704 assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0);
705
706 sig.sa_handler = SIG_DFL;
707 assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
708
709 *pid = fork();
710 if (*pid < 0)
711 return log_error_errno(errno, "Failed to fork process: %m");
712
713 if (*pid == 0) {
714 int ac;
715
716 assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
717
718 reset_signal_mask();
719 reset_all_signal_handlers();
720
721 for (ac = 0; ac < argc; ac++) {
722 if (streq(argv[ac], "--console")) {
723 argv[ac] = strjoina("--console=", tty, NULL);
724 break;
725 }
726 }
727
728 assert(ac < argc);
729
730 execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv);
731 _exit(EXIT_FAILURE);
732 }
733 return 0;
734 }
735
736 static void terminate_agents(Set *pids) {
737 struct timespec ts;
738 siginfo_t status = {};
739 sigset_t set;
740 Iterator i;
741 void *p;
742 int r, signum;
743
744 /*
745 * Request termination of the remaining processes as those
746 * are not required anymore.
747 */
748 SET_FOREACH(p, pids, i)
749 (void) kill(PTR_TO_PID(p), SIGTERM);
750
751 /*
752 * Collect the processes which have go away.
753 */
754 assert_se(sigemptyset(&set) >= 0);
755 assert_se(sigaddset(&set, SIGCHLD) >= 0);
756 timespec_store(&ts, 50 * USEC_PER_MSEC);
757
758 while (!set_isempty(pids)) {
759
760 zero(status);
761 r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
762 if (r < 0 && errno == EINTR)
763 continue;
764
765 if (r == 0 && status.si_pid > 0) {
766 set_remove(pids, PID_TO_PTR(status.si_pid));
767 continue;
768 }
769
770 signum = sigtimedwait(&set, NULL, &ts);
771 if (signum < 0) {
772 if (errno != EAGAIN)
773 log_error_errno(errno, "sigtimedwait() failed: %m");
774 break;
775 }
776 assert(signum == SIGCHLD);
777 }
778
779 /*
780 * Kill hanging processes.
781 */
782 SET_FOREACH(p, pids, i) {
783 log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
784 (void) kill(PTR_TO_PID(p), SIGKILL);
785 }
786 }
787
788 static int ask_on_consoles(int argc, char *argv[]) {
789 _cleanup_set_free_ Set *pids = NULL;
790 _cleanup_strv_free_ char **consoles = NULL;
791 siginfo_t status = {};
792 char **tty;
793 pid_t pid;
794 int r;
795
796 r = get_kernel_consoles(&consoles);
797 if (r < 0)
798 return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
799
800 pids = set_new(NULL);
801 if (!pids)
802 return log_oom();
803
804 /* Start an agent on each console. */
805 STRV_FOREACH(tty, consoles) {
806 r = ask_on_this_console(*tty, &pid, argc, argv);
807 if (r < 0)
808 return r;
809
810 if (set_put(pids, PID_TO_PTR(pid)) < 0)
811 return log_oom();
812 }
813
814 /* Wait for an agent to exit. */
815 for (;;) {
816 zero(status);
817
818 if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
819 if (errno == EINTR)
820 continue;
821
822 return log_error_errno(errno, "waitid() failed: %m");
823 }
824
825 set_remove(pids, PID_TO_PTR(status.si_pid));
826 break;
827 }
828
829 if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
830 log_error("Password agent failed with: %d", status.si_status);
831
832 terminate_agents(pids);
833 return 0;
834 }
835
836 int main(int argc, char *argv[]) {
837 int r;
838
839 log_set_target(LOG_TARGET_AUTO);
840 log_parse_environment();
841 log_open();
842
843 umask(0022);
844
845 r = parse_argv(argc, argv);
846 if (r <= 0)
847 goto finish;
848
849 if (arg_console && !arg_device)
850 /*
851 * Spawn for each console device a separate process.
852 */
853 r = ask_on_consoles(argc, argv);
854 else {
855
856 if (arg_device) {
857 /*
858 * Later on, a controlling terminal will be acquired,
859 * therefore the current process has to become a session
860 * leader and should not have a controlling terminal already.
861 */
862 (void) setsid();
863 (void) release_terminal();
864 }
865
866 if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
867 r = watch_passwords();
868 else
869 r = show_passwords();
870 }
871
872 finish:
873 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
874 }