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