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