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