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