]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tty-ask-password-agent/tty-ask-password-agent.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / tty-ask-password-agent / tty-ask-password-agent.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6 Copyright 2015 Werner Fink
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <poll.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <stddef.h>
29 #include <string.h>
30 #include <sys/inotify.h>
31 #include <sys/prctl.h>
32 #include <sys/signalfd.h>
33 #include <sys/socket.h>
34 #include <sys/wait.h>
35 #include <sys/un.h>
36 #include <unistd.h>
37
38 #include "alloc-util.h"
39 #include "ask-password-api.h"
40 #include "conf-parser.h"
41 #include "def.h"
42 #include "dirent-util.h"
43 #include "exit-status.h"
44 #include "fd-util.h"
45 #include "fileio.h"
46 #include "hashmap.h"
47 #include "io-util.h"
48 #include "macro.h"
49 #include "mkdir.h"
50 #include "path-util.h"
51 #include "process-util.h"
52 #include "signal-util.h"
53 #include "socket-util.h"
54 #include "string-util.h"
55 #include "strv.h"
56 #include "terminal-util.h"
57 #include "util.h"
58 #include "utmp-wtmp.h"
59
60 static enum {
61 ACTION_LIST,
62 ACTION_QUERY,
63 ACTION_WATCH,
64 ACTION_WALL
65 } arg_action = ACTION_QUERY;
66
67 static bool arg_plymouth = false;
68 static bool arg_console = false;
69 static const char *arg_device = NULL;
70
71 static int ask_password_plymouth(
72 const char *message,
73 usec_t until,
74 AskPasswordFlags flags,
75 const char *flag_file,
76 char ***ret) {
77
78 static const union sockaddr_union sa = PLYMOUTH_SOCKET;
79 _cleanup_close_ int fd = -1, notify = -1;
80 _cleanup_free_ char *packet = NULL;
81 ssize_t k;
82 int r, n;
83 struct pollfd pollfd[2] = {};
84 char buffer[LINE_MAX];
85 size_t p = 0;
86 enum {
87 POLL_SOCKET,
88 POLL_INOTIFY
89 };
90
91 assert(ret);
92
93 if (flag_file) {
94 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
95 if (notify < 0)
96 return -errno;
97
98 r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
99 if (r < 0)
100 return -errno;
101 }
102
103 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
104 if (fd < 0)
105 return -errno;
106
107 r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
108 if (r < 0)
109 return -errno;
110
111 if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
112 packet = strdup("c");
113 n = 1;
114 } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
115 packet = NULL;
116 if (!packet)
117 return -ENOMEM;
118
119 r = loop_write(fd, packet, n + 1, true);
120 if (r < 0)
121 return r;
122
123 pollfd[POLL_SOCKET].fd = fd;
124 pollfd[POLL_SOCKET].events = POLLIN;
125 pollfd[POLL_INOTIFY].fd = notify;
126 pollfd[POLL_INOTIFY].events = POLLIN;
127
128 for (;;) {
129 int sleep_for = -1, j;
130
131 if (until > 0) {
132 usec_t y;
133
134 y = now(CLOCK_MONOTONIC);
135
136 if (y > until) {
137 r = -ETIME;
138 goto finish;
139 }
140
141 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
142 }
143
144 if (flag_file && access(flag_file, F_OK) < 0) {
145 r = -errno;
146 goto finish;
147 }
148
149 j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
150 if (j < 0) {
151 if (errno == EINTR)
152 continue;
153
154 r = -errno;
155 goto finish;
156 } else if (j == 0) {
157 r = -ETIME;
158 goto finish;
159 }
160
161 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
162 flush_fd(notify);
163
164 if (pollfd[POLL_SOCKET].revents == 0)
165 continue;
166
167 k = read(fd, buffer + p, sizeof(buffer) - p);
168 if (k < 0) {
169 if (IN_SET(errno, EINTR, EAGAIN))
170 continue;
171
172 r = -errno;
173 goto finish;
174 } else if (k == 0) {
175 r = -EIO;
176 goto finish;
177 }
178
179 p += k;
180
181 if (p < 1)
182 continue;
183
184 if (buffer[0] == 5) {
185
186 if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
187 /* Hmm, first try with cached
188 * passwords failed, so let's retry
189 * with a normal password request */
190 packet = mfree(packet);
191
192 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
193 r = -ENOMEM;
194 goto finish;
195 }
196
197 r = loop_write(fd, packet, n+1, true);
198 if (r < 0)
199 goto finish;
200
201 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
202 p = 0;
203 continue;
204 }
205
206 /* No password, because UI not shown */
207 r = -ENOENT;
208 goto finish;
209
210 } else if (IN_SET(buffer[0], 2, 9)) {
211 uint32_t size;
212 char **l;
213
214 /* One or more answers */
215 if (p < 5)
216 continue;
217
218 memcpy(&size, buffer+1, sizeof(size));
219 size = le32toh(size);
220 if (size + 5 > sizeof(buffer)) {
221 r = -EIO;
222 goto finish;
223 }
224
225 if (p-5 < size)
226 continue;
227
228 l = strv_parse_nulstr(buffer + 5, size);
229 if (!l) {
230 r = -ENOMEM;
231 goto finish;
232 }
233
234 *ret = l;
235 break;
236
237 } else {
238 /* Unknown packet */
239 r = -EIO;
240 goto finish;
241 }
242 }
243
244 r = 0;
245
246 finish:
247 explicit_bzero(buffer, sizeof(buffer));
248 return r;
249 }
250
251 static int send_passwords(const char *socket_name, char **passwords) {
252 _cleanup_free_ char *packet = NULL;
253 _cleanup_close_ int socket_fd = -1;
254 union sockaddr_union sa = { .un.sun_family = AF_UNIX };
255 size_t packet_length = 1;
256 char **p, *d;
257 int r;
258
259 assert(socket_name);
260
261 STRV_FOREACH(p, passwords)
262 packet_length += strlen(*p) + 1;
263
264 packet = new(char, packet_length);
265 if (!packet)
266 return -ENOMEM;
267
268 packet[0] = '+';
269
270 d = packet + 1;
271 STRV_FOREACH(p, passwords)
272 d = stpcpy(d, *p) + 1;
273
274 socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
275 if (socket_fd < 0) {
276 r = log_debug_errno(errno, "socket(): %m");
277 goto finish;
278 }
279
280 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
281
282 r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
283 if (r < 0)
284 r = log_debug_errno(errno, "sendto(): %m");
285
286 finish:
287 explicit_bzero(packet, packet_length);
288 return r;
289 }
290
291 static int parse_password(const char *filename, char **wall) {
292 _cleanup_free_ char *socket_name = NULL, *message = NULL;
293 bool accept_cached = false, echo = false;
294 uint64_t not_after = 0;
295 unsigned pid = 0;
296
297 const ConfigTableItem items[] = {
298 { "Ask", "Socket", config_parse_string, 0, &socket_name },
299 { "Ask", "NotAfter", config_parse_uint64, 0, &not_after },
300 { "Ask", "Message", config_parse_string, 0, &message },
301 { "Ask", "PID", config_parse_unsigned, 0, &pid },
302 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
303 { "Ask", "Echo", config_parse_bool, 0, &echo },
304 {}
305 };
306
307 int r;
308
309 assert(filename);
310
311 r = config_parse(NULL, filename, NULL,
312 NULL,
313 config_item_table_lookup, items,
314 CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL);
315 if (r < 0)
316 return r;
317
318 if (!socket_name) {
319 log_error("Invalid password file %s", filename);
320 return -EBADMSG;
321 }
322
323 if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
324 return 0;
325
326 if (pid > 0 && !pid_is_alive(pid))
327 return 0;
328
329 if (arg_action == ACTION_LIST)
330 printf("'%s' (PID %u)\n", message, pid);
331
332 else if (arg_action == ACTION_WALL) {
333 char *_wall;
334
335 if (asprintf(&_wall,
336 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
337 "Please enter password with the systemd-tty-ask-password-agent tool!",
338 strempty(*wall),
339 *wall ? "\r\n\r\n" : "",
340 message,
341 pid) < 0)
342 return log_oom();
343
344 free(*wall);
345 *wall = _wall;
346
347 } else {
348 _cleanup_strv_free_erase_ char **passwords = NULL;
349
350 assert(IN_SET(arg_action, ACTION_QUERY, 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, EXIT_CLEAN_DAEMON, 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 }