]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tty-ask-password-agent/tty-ask-password-agent.c
tty-ask-password: fix dead code path
[thirdparty/systemd.git] / src / tty-ask-password-agent / tty-ask-password-agent.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ec863ba6 2/***
96b2fb93 3 Copyright © 2015 Werner Fink
ec863ba6
LP
4***/
5
ec863ba6 6#include <errno.h>
3f6fd1ba
LP
7#include <fcntl.h>
8#include <getopt.h>
9#include <poll.h>
6af62124 10#include <signal.h>
3f6fd1ba
LP
11#include <stdbool.h>
12#include <stddef.h>
ec863ba6 13#include <string.h>
6af62124 14#include <sys/prctl.h>
3f6fd1ba 15#include <sys/signalfd.h>
ec863ba6 16#include <sys/socket.h>
ca78ad1d
ZJS
17#include <sys/stat.h>
18#include <sys/types.h>
ec863ba6 19#include <sys/un.h>
ca78ad1d 20#include <sys/wait.h>
ec863ba6 21#include <unistd.h>
ec863ba6 22
b5efdb8a 23#include "alloc-util.h"
3f6fd1ba
LP
24#include "ask-password-api.h"
25#include "conf-parser.h"
26#include "def.h"
a0956174 27#include "dirent-util.h"
6af62124 28#include "exit-status.h"
3ffd4af2 29#include "fd-util.h"
6af62124 30#include "fileio.h"
27c3112d 31#include "fs-util.h"
6af62124 32#include "hashmap.h"
c004493c 33#include "io-util.h"
6af62124 34#include "macro.h"
5e332028 35#include "main-func.h"
0a970718 36#include "memory-util.h"
49e942b2 37#include "mkdir.h"
9eb977db 38#include "path-util.h"
294bf0c3 39#include "pretty-print.h"
3f6fd1ba 40#include "process-util.h"
23d5dd16 41#include "set.h"
3f6fd1ba 42#include "signal-util.h"
e5ebf783 43#include "socket-util.h"
07630cea 44#include "string-util.h"
21bc923a 45#include "strv.h"
288a74cc 46#include "terminal-util.h"
3f6fd1ba 47#include "utmp-wtmp.h"
ec863ba6
LP
48
49static enum {
50 ACTION_LIST,
51 ACTION_QUERY,
52 ACTION_WATCH,
53 ACTION_WALL
54} arg_action = ACTION_QUERY;
55
e5ebf783 56static bool arg_plymouth = false;
0cf84693 57static bool arg_console = false;
6af62124 58static const char *arg_device = NULL;
e5ebf783 59
bbada6d7 60static int send_passwords(const char *socket_name, char **passwords) {
e693a932 61 _cleanup_(erase_and_freep) char *packet = NULL;
bbada6d7 62 _cleanup_close_ int socket_fd = -1;
15a3e96f 63 union sockaddr_union sa = {};
bbada6d7
JAS
64 size_t packet_length = 1;
65 char **p, *d;
5439206b 66 ssize_t n;
e693a932 67 int salen;
bbada6d7
JAS
68
69 assert(socket_name);
70
15a3e96f
LP
71 salen = sockaddr_un_set_path(&sa.un, socket_name);
72 if (salen < 0)
73 return salen;
74
bbada6d7
JAS
75 STRV_FOREACH(p, passwords)
76 packet_length += strlen(*p) + 1;
77
78 packet = new(char, packet_length);
79 if (!packet)
80 return -ENOMEM;
81
82 packet[0] = '+';
83
84 d = packet + 1;
85 STRV_FOREACH(p, passwords)
86 d = stpcpy(d, *p) + 1;
87
88 socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
e693a932
ZJS
89 if (socket_fd < 0)
90 return log_debug_errno(errno, "socket(): %m");
bbada6d7 91
15a3e96f 92 n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
e693a932
ZJS
93 if (n < 0)
94 return log_debug_errno(errno, "sendto(): %m");
5439206b 95
e693a932 96 return (int) n;
bbada6d7
JAS
97}
98
85c221eb
FB
99static bool wall_tty_match(const char *path, void *userdata) {
100 _cleanup_free_ char *p = NULL;
101 _cleanup_close_ int fd = -1;
102 struct stat st;
103
104 if (!path_is_absolute(path))
105 path = strjoina("/dev/", path);
106
107 if (lstat(path, &st) < 0) {
108 log_debug_errno(errno, "Failed to stat %s: %m", path);
109 return true;
110 }
111
112 if (!S_ISCHR(st.st_mode)) {
113 log_debug("%s is not a character device.", path);
114 return true;
115 }
116
117 /* We use named pipes to ensure that wall messages suggesting
118 * password entry are not printed over password prompts
119 * already shown. We use the fact here that opening a pipe in
120 * non-blocking mode for write-only will succeed only if
121 * there's some writer behind it. Using pipes has the
122 * advantage that the block will automatically go away if the
123 * process dies. */
124
125 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
126 log_oom();
127 return true;
128 }
129
130 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
131 if (fd < 0) {
132 log_debug_errno(errno, "Failed to open the wall pipe: %m");
133 return 1;
134 }
135
136 /* What, we managed to open the pipe? Then this tty is filtered. */
137 return 0;
138}
139
87c4444a
FB
140static int agent_ask_password_tty(
141 const char *message,
142 usec_t until,
143 AskPasswordFlags flags,
144 const char *flag_file,
145 char ***ret) {
146
147 int tty_fd = -1;
148 int r;
149
150 if (arg_console) {
151 const char *con = arg_device ?: "/dev/console";
152
153 tty_fd = acquire_terminal(con, ACQUIRE_TERMINAL_WAIT, USEC_INFINITY);
154 if (tty_fd < 0)
155 return log_error_errno(tty_fd, "Failed to acquire %s: %m", con);
156
157 r = reset_terminal_fd(tty_fd, true);
158 if (r < 0)
159 log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
160
161 }
162
163 r = ask_password_tty(tty_fd, message, NULL, until, flags, flag_file, ret);
164
165 if (arg_console) {
166 tty_fd = safe_close(tty_fd);
167 release_terminal();
168 }
169
170 return 0;
171}
172
5461cdeb 173static int process_one_password_file(const char *filename) {
bbada6d7 174 _cleanup_free_ char *socket_name = NULL, *message = NULL;
1602b008 175 bool accept_cached = false, echo = false;
ec863ba6
LP
176 uint64_t not_after = 0;
177 unsigned pid = 0;
ec863ba6 178
f975e971
LP
179 const ConfigTableItem items[] = {
180 { "Ask", "Socket", config_parse_string, 0, &socket_name },
181 { "Ask", "NotAfter", config_parse_uint64, 0, &not_after },
182 { "Ask", "Message", config_parse_string, 0, &message },
183 { "Ask", "PID", config_parse_unsigned, 0, &pid },
184 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
64845bdc 185 { "Ask", "Echo", config_parse_bool, 0, &echo },
1d749d04 186 {}
ec863ba6
LP
187 };
188
ec863ba6 189 int r;
ec863ba6
LP
190
191 assert(filename);
192
36f822c4
ZJS
193 r = config_parse(NULL, filename, NULL,
194 NULL,
195 config_item_table_lookup, items,
bcde742e 196 CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL);
36f822c4
ZJS
197 if (r < 0)
198 return r;
ec863ba6 199
baaa35ad
ZJS
200 if (!socket_name)
201 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
202 "Invalid password file %s", filename);
ec863ba6 203
e46eab86
ZJS
204 if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
205 return 0;
ec863ba6 206
e46eab86
ZJS
207 if (pid > 0 && !pid_is_alive(pid))
208 return 0;
ded80335 209
87c4444a
FB
210 switch (arg_action) {
211 case ACTION_LIST:
ec863ba6 212 printf("'%s' (PID %u)\n", message, pid);
87c4444a 213 return 0;
e46eab86 214
87c4444a
FB
215 case ACTION_WALL: {
216 _cleanup_free_ char *wall = NULL;
ec863ba6 217
87c4444a
FB
218 if (asprintf(&wall,
219 "Password entry required for \'%s\' (PID %u).\r\n"
220 "Please enter password with the systemd-tty-ask-password-agent tool.",
221 message,
222 pid) < 0)
223 return log_oom();
e46eab86 224
87c4444a
FB
225 (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
226 return 0;
227 }
228 case ACTION_QUERY:
229 case ACTION_WATCH: {
bbada6d7 230 _cleanup_strv_free_erase_ char **passwords = NULL;
87c4444a 231 AskPasswordFlags flags = 0;
ec863ba6
LP
232
233 if (access(socket_name, W_OK) < 0) {
ec863ba6
LP
234 if (arg_action == ACTION_QUERY)
235 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
236
e46eab86 237 return 0;
ec863ba6
LP
238 }
239
87c4444a
FB
240 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED, accept_cached);
241 SET_FLAG(flags, ASK_PASSWORD_CONSOLE_COLOR, arg_console);
242 SET_FLAG(flags, ASK_PASSWORD_ECHO, echo);
3d18b167 243
87c4444a
FB
244 if (arg_plymouth)
245 r = ask_password_plymouth(message, not_after, flags, filename, &passwords);
246 else
247 r = agent_ask_password_tty(message, not_after, flags, filename, &passwords);
0cf84693 248
87c4444a
FB
249 if (r < 0) {
250 /* If the query went away, that's OK */
251 if (IN_SET(r, -ETIME, -ENOENT))
252 return 0;
e5ebf783 253
bbada6d7 254 return log_error_errno(r, "Failed to query password: %m");
87c4444a 255 }
ec863ba6 256
d325f244
FB
257 if (strv_isempty(passwords))
258 return -ECANCELED;
259
bbada6d7 260 r = send_passwords(socket_name, passwords);
00843602 261 if (r < 0)
bbada6d7 262 return log_error_errno(r, "Failed to send: %m");
87c4444a
FB
263 break;
264 }
ec863ba6
LP
265 }
266
e46eab86 267 return 0;
ec863ba6
LP
268}
269
0cf84693 270static int wall_tty_block(void) {
1d749d04 271 _cleanup_free_ char *p = NULL;
fc116c6a 272 dev_t devnr;
00843602 273 int fd, r;
7af53310 274
4d6d6518 275 r = get_ctty_devnr(0, &devnr);
e287086b
LP
276 if (r == -ENXIO) /* We have no controlling tty */
277 return -ENOTTY;
4d6d6518 278 if (r < 0)
00843602 279 return log_error_errno(r, "Failed to get controlling TTY: %m");
7af53310 280
2b583ce6 281 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
00843602 282 return log_oom();
7af53310 283
e3e2cf07
LP
284 (void) mkdir_parents_label(p, 0700);
285 (void) mkfifo(p, 0600);
7af53310
LP
286
287 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
7af53310 288 if (fd < 0)
2ee4e222 289 return log_debug_errno(errno, "Failed to open %s: %m", p);
7af53310
LP
290
291 return fd;
292}
293
5461cdeb 294static int process_password_files(void) {
1d749d04 295 _cleanup_closedir_ DIR *d;
ec863ba6
LP
296 struct dirent *de;
297 int r = 0;
298
1d749d04
ZJS
299 d = opendir("/run/systemd/ask-password");
300 if (!d) {
ec863ba6
LP
301 if (errno == ENOENT)
302 return 0;
303
ad71eee5 304 return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
ec863ba6
LP
305 }
306
1503bcb1 307 FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
85c221eb 308 _cleanup_free_ char *p = NULL;
ec863ba6
LP
309 int q;
310
85c221eb 311 /* We only support /run on tmpfs, hence we can rely on
1a6f4df6
LP
312 * d_type to be reliable */
313
ec863ba6
LP
314 if (de->d_type != DT_REG)
315 continue;
316
ec863ba6
LP
317 if (!startswith(de->d_name, "ask."))
318 continue;
319
b910cc72 320 p = path_join("/run/systemd/ask-password", de->d_name);
1d749d04
ZJS
321 if (!p)
322 return log_oom();
ec863ba6 323
5461cdeb 324 q = process_one_password_file(p);
1d749d04 325 if (q < 0 && r == 0)
ec863ba6 326 r = q;
ec863ba6
LP
327 }
328
ec863ba6
LP
329 return r;
330}
331
13227570 332static int process_and_watch_password_files(bool watch) {
b9ba604e 333 enum {
b9ba604e 334 FD_SIGNAL,
13227570 335 FD_INOTIFY,
b9ba604e
LP
336 _FD_MAX
337 };
338
1d749d04 339 _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
7d895205 340 struct pollfd pollfd[_FD_MAX];
b9ba604e 341 sigset_t mask;
ec863ba6
LP
342 int r;
343
0cf84693 344 tty_block_fd = wall_tty_block();
7af53310 345
00843602 346 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
ec863ba6 347
72c0a2c2 348 assert_se(sigemptyset(&mask) >= 0);
998c6da8 349 assert_se(sigset_add_many(&mask, SIGTERM, -1) >= 0);
72c0a2c2 350 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
b9ba604e 351
13227570
FB
352 if (watch) {
353 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
354 if (signal_fd < 0)
355 return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
b9ba604e 356
7d895205 357 pollfd[FD_SIGNAL] = (struct pollfd) { .fd = signal_fd, .events = POLLIN };
13227570
FB
358
359 notify = inotify_init1(IN_CLOEXEC);
360 if (notify < 0)
361 return log_error_errno(errno, "Failed to allocate directory watch: %m");
362
363 r = inotify_add_watch_and_warn(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO);
364 if (r < 0)
365 return r;
366
7d895205 367 pollfd[FD_INOTIFY] = (struct pollfd) { .fd = notify, .events = POLLIN };
13227570 368 }
ec863ba6
LP
369
370 for (;;) {
d325f244
FB
371 int timeout = -1;
372
5461cdeb 373 r = process_password_files();
d325f244
FB
374 if (r < 0) {
375 if (r == -ECANCELED)
376 /* Disable poll() timeout since at least one password has
377 * been skipped and therefore one file remains and is
378 * unlikely to trigger any events. */
379 timeout = 0;
380 else
825721eb
FB
381 /* FIXME: we should do something here since otherwise the service
382 * requesting the password won't notice the error and will wait
383 * indefinitely. */
d325f244
FB
384 log_error_errno(r, "Failed to process password: %m");
385 }
ec863ba6 386
13227570
FB
387 if (!watch)
388 break;
389
7d895205 390 if (poll(pollfd, _FD_MAX, timeout) < 0) {
ec863ba6
LP
391 if (errno == EINTR)
392 continue;
393
1d749d04 394 return -errno;
ec863ba6
LP
395 }
396
b9ba604e 397 if (pollfd[FD_INOTIFY].revents != 0)
00843602 398 (void) flush_fd(notify);
b9ba604e
LP
399
400 if (pollfd[FD_SIGNAL].revents != 0)
401 break;
ec863ba6
LP
402 }
403
1d749d04 404 return 0;
ec863ba6
LP
405}
406
37ec0fdd
LP
407static int help(void) {
408 _cleanup_free_ char *link = NULL;
409 int r;
410
411 r = terminal_urlify_man("systemd-tty-ask-password-agent", "1", &link);
412 if (r < 0)
413 return log_oom();
414
ec863ba6
LP
415 printf("%s [OPTIONS...]\n\n"
416 "Process system password requests.\n\n"
e5ebf783 417 " -h --help Show this help\n"
c52f663b 418 " --version Show package version\n"
e5ebf783
LP
419 " --list Show pending password requests\n"
420 " --query Process pending password requests\n"
35b8ca3a
HH
421 " --watch Continuously process password requests\n"
422 " --wall Continuously forward password requests to wall\n"
0cf84693 423 " --plymouth Ask question with Plymouth instead of on TTY\n"
37ec0fdd
LP
424 " --console Ask question on /dev/console instead of current TTY\n"
425 "\nSee the %s for details.\n"
426 , program_invocation_short_name
427 , link
428 );
429
430 return 0;
ec863ba6
LP
431}
432
433static int parse_argv(int argc, char *argv[]) {
434
435 enum {
436 ARG_LIST = 0x100,
437 ARG_QUERY,
438 ARG_WATCH,
439 ARG_WALL,
0cf84693 440 ARG_PLYMOUTH,
c52f663b
LP
441 ARG_CONSOLE,
442 ARG_VERSION
ec863ba6
LP
443 };
444
445 static const struct option options[] = {
6af62124
WF
446 { "help", no_argument, NULL, 'h' },
447 { "version", no_argument, NULL, ARG_VERSION },
448 { "list", no_argument, NULL, ARG_LIST },
449 { "query", no_argument, NULL, ARG_QUERY },
450 { "watch", no_argument, NULL, ARG_WATCH },
451 { "wall", no_argument, NULL, ARG_WALL },
452 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
453 { "console", optional_argument, NULL, ARG_CONSOLE },
eb9da376 454 {}
ec863ba6
LP
455 };
456
457 int c;
458
459 assert(argc >= 0);
460 assert(argv);
461
601185b4 462 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
ec863ba6
LP
463
464 switch (c) {
465
466 case 'h':
37ec0fdd 467 return help();
ec863ba6 468
c52f663b 469 case ARG_VERSION:
3f6fd1ba 470 return version();
c52f663b 471
ec863ba6
LP
472 case ARG_LIST:
473 arg_action = ACTION_LIST;
474 break;
475
476 case ARG_QUERY:
477 arg_action = ACTION_QUERY;
478 break;
479
480 case ARG_WATCH:
481 arg_action = ACTION_WATCH;
482 break;
483
484 case ARG_WALL:
485 arg_action = ACTION_WALL;
486 break;
487
e5ebf783
LP
488 case ARG_PLYMOUTH:
489 arg_plymouth = true;
490 break;
491
0cf84693
LP
492 case ARG_CONSOLE:
493 arg_console = true;
6af62124
WF
494 if (optarg) {
495
baaa35ad
ZJS
496 if (isempty(optarg))
497 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
498 "Empty console device path is not allowed.");
6af62124
WF
499
500 arg_device = optarg;
501 }
0cf84693
LP
502 break;
503
ec863ba6
LP
504 case '?':
505 return -EINVAL;
506
507 default:
eb9da376 508 assert_not_reached("Unhandled option");
ec863ba6 509 }
ec863ba6 510
baaa35ad
ZJS
511 if (optind != argc)
512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
513 "%s takes no arguments.", program_invocation_short_name);
ec863ba6 514
6af62124
WF
515 if (arg_plymouth || arg_console) {
516
baaa35ad
ZJS
517 if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH))
518 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
519 "Options --query and --watch conflict.");
6af62124 520
baaa35ad
ZJS
521 if (arg_plymouth && arg_console)
522 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
523 "Options --plymouth and --console conflict.");
6af62124
WF
524 }
525
ec863ba6
LP
526 return 1;
527}
528
6af62124 529/*
189b0377
LP
530 * To be able to ask on all terminal devices of /dev/console the devices are collected. If more than one
531 * device is found, then on each of the terminals a inquiring task is forked. Every task has its own session
532 * and its own controlling terminal. If one of the tasks does handle a password, the remaining tasks will be
533 * terminated.
6af62124 534 */
6e79d2b5 535static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments) {
d8502964 536 static const struct sigaction sigchld = {
6af62124
WF
537 .sa_handler = nop_signal_handler,
538 .sa_flags = SA_NOCLDSTOP | SA_RESTART,
539 };
d8502964
LP
540 static const struct sigaction sighup = {
541 .sa_handler = SIG_DFL,
542 .sa_flags = SA_RESTART,
543 };
4c253ed1 544 int r;
6af62124 545
d8502964
LP
546 assert_se(sigaction(SIGCHLD, &sigchld, NULL) >= 0);
547 assert_se(sigaction(SIGHUP, &sighup, NULL) >= 0);
6af62124
WF
548 assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
549
4bec7f09 550 r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, ret_pid);
4c253ed1 551 if (r < 0)
b6e1fff1 552 return r;
4c253ed1 553 if (r == 0) {
ed179fd7 554 char **i;
6af62124
WF
555
556 assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
557
ed179fd7
LP
558 STRV_FOREACH(i, arguments) {
559 char *k;
560
561 if (!streq(*i, "--console"))
562 continue;
563
564 k = strjoin("--console=", tty);
565 if (!k) {
566 log_oom();
567 _exit(EXIT_FAILURE);
6af62124 568 }
6af62124 569
ed179fd7
LP
570 free_and_replace(*i, k);
571 }
6af62124 572
65e5d693 573 execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, arguments);
6af62124
WF
574 _exit(EXIT_FAILURE);
575 }
4c253ed1 576
6af62124
WF
577 return 0;
578}
579
580static void terminate_agents(Set *pids) {
581 struct timespec ts;
582 siginfo_t status = {};
583 sigset_t set;
584 Iterator i;
585 void *p;
586 int r, signum;
587
588 /*
589 * Request termination of the remaining processes as those
590 * are not required anymore.
591 */
592 SET_FOREACH(p, pids, i)
593 (void) kill(PTR_TO_PID(p), SIGTERM);
594
595 /*
596 * Collect the processes which have go away.
597 */
598 assert_se(sigemptyset(&set) >= 0);
599 assert_se(sigaddset(&set, SIGCHLD) >= 0);
600 timespec_store(&ts, 50 * USEC_PER_MSEC);
601
602 while (!set_isempty(pids)) {
603
604 zero(status);
605 r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
606 if (r < 0 && errno == EINTR)
607 continue;
608
609 if (r == 0 && status.si_pid > 0) {
610 set_remove(pids, PID_TO_PTR(status.si_pid));
611 continue;
612 }
613
614 signum = sigtimedwait(&set, NULL, &ts);
615 if (signum < 0) {
616 if (errno != EAGAIN)
617 log_error_errno(errno, "sigtimedwait() failed: %m");
618 break;
619 }
620 assert(signum == SIGCHLD);
621 }
622
623 /*
624 * Kill hanging processes.
625 */
626 SET_FOREACH(p, pids, i) {
627 log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
628 (void) kill(PTR_TO_PID(p), SIGKILL);
629 }
630}
631
ed179fd7 632static int ask_on_consoles(char *argv[]) {
6af62124 633 _cleanup_set_free_ Set *pids = NULL;
6e79d2b5 634 _cleanup_strv_free_ char **consoles = NULL, **arguments = NULL;
6af62124
WF
635 siginfo_t status = {};
636 char **tty;
637 pid_t pid;
638 int r;
639
640 r = get_kernel_consoles(&consoles);
641 if (r < 0)
642 return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
643
644 pids = set_new(NULL);
645 if (!pids)
646 return log_oom();
647
6e79d2b5
YW
648 arguments = strv_copy(argv);
649 if (!arguments)
650 return log_oom();
651
6af62124
WF
652 /* Start an agent on each console. */
653 STRV_FOREACH(tty, consoles) {
6e79d2b5 654 r = ask_on_this_console(*tty, &pid, arguments);
6af62124
WF
655 if (r < 0)
656 return r;
657
658 if (set_put(pids, PID_TO_PTR(pid)) < 0)
659 return log_oom();
660 }
661
662 /* Wait for an agent to exit. */
663 for (;;) {
664 zero(status);
665
666 if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
667 if (errno == EINTR)
668 continue;
669
670 return log_error_errno(errno, "waitid() failed: %m");
671 }
672
673 set_remove(pids, PID_TO_PTR(status.si_pid));
674 break;
675 }
676
1f0958f6 677 if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
6af62124
WF
678 log_error("Password agent failed with: %d", status.si_status);
679
680 terminate_agents(pids);
681 return 0;
682}
683
0420d20d 684static int run(int argc, char *argv[]) {
ec863ba6
LP
685 int r;
686
6bf3c61c 687 log_setup_service();
ec863ba6 688
4c12626c
LP
689 umask(0022);
690
1d749d04
ZJS
691 r = parse_argv(argc, argv);
692 if (r <= 0)
0420d20d 693 return r;
ec863ba6 694
6af62124
WF
695 if (arg_console && !arg_device)
696 /*
0420d20d 697 * Spawn a separate process for each console device.
6af62124 698 */
ed179fd7 699 return ask_on_consoles(argv);
0cf84693 700
0420d20d
ZJS
701 if (arg_device) {
702 /*
703 * Later on, a controlling terminal will be acquired,
704 * therefore the current process has to become a session
705 * leader and should not have a controlling terminal already.
706 */
707 (void) setsid();
708 (void) release_terminal();
6af62124 709 }
96707269 710
13227570 711 return process_and_watch_password_files(arg_action != ACTION_QUERY);
ec863ba6 712}
0420d20d
ZJS
713
714DEFINE_MAIN_FUNCTION(run);