]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tty-ask-password-agent/tty-ask-password-agent.c
ask-password: add support for caching passwords in the kernel keyring
[thirdparty/systemd.git] / src / tty-ask-password-agent / tty-ask-password-agent.c
CommitLineData
ec863ba6
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
ec863ba6
LP
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
5430f7f2 16 Lesser General Public License for more details.
ec863ba6 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
ec863ba6
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
ec863ba6 22#include <errno.h>
3f6fd1ba
LP
23#include <fcntl.h>
24#include <getopt.h>
25#include <poll.h>
26#include <stdbool.h>
27#include <stddef.h>
ec863ba6 28#include <string.h>
3f6fd1ba
LP
29#include <sys/inotify.h>
30#include <sys/signalfd.h>
ec863ba6
LP
31#include <sys/socket.h>
32#include <sys/un.h>
ec863ba6 33#include <unistd.h>
ec863ba6 34
3f6fd1ba
LP
35#include "ask-password-api.h"
36#include "conf-parser.h"
37#include "def.h"
49e942b2 38#include "mkdir.h"
9eb977db 39#include "path-util.h"
3f6fd1ba
LP
40#include "process-util.h"
41#include "signal-util.h"
e5ebf783 42#include "socket-util.h"
21bc923a 43#include "strv.h"
288a74cc 44#include "terminal-util.h"
3f6fd1ba
LP
45#include "util.h"
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;
e5ebf783 57
21bc923a
LP
58static int ask_password_plymouth(
59 const char *message,
60 usec_t until,
e287086b 61 AskPasswordFlags flags,
21bc923a 62 const char *flag_file,
e287086b 63 char ***ret) {
21bc923a 64
1d749d04
ZJS
65 _cleanup_close_ int fd = -1, notify = -1;
66 union sockaddr_union sa = PLYMOUTH_SOCKET;
67 _cleanup_free_ char *packet = NULL;
e5ebf783
LP
68 ssize_t k;
69 int r, n;
b92bea5d 70 struct pollfd pollfd[2] = {};
e5ebf783
LP
71 char buffer[LINE_MAX];
72 size_t p = 0;
73 enum {
74 POLL_SOCKET,
75 POLL_INOTIFY
76 };
77
e287086b 78 assert(ret);
21bc923a 79
e5ebf783 80 if (flag_file) {
1d749d04
ZJS
81 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
82 if (notify < 0)
83 return -errno;
e5ebf783 84
1d749d04
ZJS
85 r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
86 if (r < 0)
87 return -errno;
e5ebf783
LP
88 }
89
1d749d04
ZJS
90 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
91 if (fd < 0)
92 return -errno;
e5ebf783 93
1d749d04 94 r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
4a62c710 95 if (r < 0)
00843602 96 return -errno;
e5ebf783 97
e287086b 98 if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
21bc923a
LP
99 packet = strdup("c");
100 n = 1;
00843602 101 } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
7de80bfe 102 packet = NULL;
1d749d04 103 if (!packet)
00843602 104 return -ENOMEM;
e5ebf783 105
553acb7b
ZJS
106 r = loop_write(fd, packet, n + 1, true);
107 if (r < 0)
108 return r;
e5ebf783 109
e5ebf783
LP
110 pollfd[POLL_SOCKET].fd = fd;
111 pollfd[POLL_SOCKET].events = POLLIN;
112 pollfd[POLL_INOTIFY].fd = notify;
113 pollfd[POLL_INOTIFY].events = POLLIN;
114
115 for (;;) {
116 int sleep_for = -1, j;
117
118 if (until > 0) {
119 usec_t y;
120
121 y = now(CLOCK_MONOTONIC);
122
1d749d04
ZJS
123 if (y > until)
124 return -ETIME;
e5ebf783
LP
125
126 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
127 }
128
1d749d04
ZJS
129 if (flag_file && access(flag_file, F_OK) < 0)
130 return -errno;
e5ebf783 131
e287086b 132 j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
1d749d04 133 if (j < 0) {
e5ebf783
LP
134 if (errno == EINTR)
135 continue;
136
1d749d04
ZJS
137 return -errno;
138 } else if (j == 0)
139 return -ETIME;
e5ebf783 140
e287086b 141 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
e5ebf783
LP
142 flush_fd(notify);
143
144 if (pollfd[POLL_SOCKET].revents == 0)
145 continue;
146
1d749d04 147 k = read(fd, buffer + p, sizeof(buffer) - p);
e287086b
LP
148 if (k < 0) {
149 if (errno == EINTR || errno == EAGAIN)
150 continue;
151
152 return -errno;
153 } else if (k == 0)
154 return -EIO;
e5ebf783
LP
155
156 p += k;
157
158 if (p < 1)
159 continue;
160
161 if (buffer[0] == 5) {
21bc923a 162
e287086b 163 if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
21bc923a
LP
164 /* Hmm, first try with cached
165 * passwords failed, so let's retry
166 * with a normal password request */
97b11eed 167 packet = mfree(packet);
21bc923a 168
1d749d04
ZJS
169 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
170 return -ENOMEM;
21bc923a 171
553acb7b
ZJS
172 r = loop_write(fd, packet, n+1, true);
173 if (r < 0)
174 return r;
21bc923a 175
e287086b 176 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
21bc923a
LP
177 p = 0;
178 continue;
179 }
180
e5ebf783 181 /* No password, because UI not shown */
1d749d04 182 return -ENOENT;
e5ebf783 183
21bc923a 184 } else if (buffer[0] == 2 || buffer[0] == 9) {
e5ebf783 185 uint32_t size;
21bc923a 186 char **l;
e5ebf783 187
4cf07da2 188 /* One or more answers */
e5ebf783
LP
189 if (p < 5)
190 continue;
191
192 memcpy(&size, buffer+1, sizeof(size));
bb53abeb 193 size = le32toh(size);
1d749d04
ZJS
194 if (size + 5 > sizeof(buffer))
195 return -EIO;
e5ebf783
LP
196
197 if (p-5 < size)
198 continue;
199
1d749d04
ZJS
200 l = strv_parse_nulstr(buffer + 5, size);
201 if (!l)
202 return -ENOMEM;
e5ebf783 203
e287086b 204 *ret = l;
e5ebf783 205 break;
21bc923a 206
1d749d04 207 } else
e5ebf783 208 /* Unknown packet */
1d749d04 209 return -EIO;
e5ebf783
LP
210 }
211
1d749d04 212 return 0;
e5ebf783
LP
213}
214
0ddf1d3a 215static int parse_password(const char *filename, char **wall) {
e46eab86 216 _cleanup_free_ char *socket_name = NULL, *message = NULL, *packet = NULL;
ec863ba6
LP
217 uint64_t not_after = 0;
218 unsigned pid = 0;
64845bdc 219 bool accept_cached = false, echo = false;
ec863ba6 220
f975e971
LP
221 const ConfigTableItem items[] = {
222 { "Ask", "Socket", config_parse_string, 0, &socket_name },
223 { "Ask", "NotAfter", config_parse_uint64, 0, &not_after },
224 { "Ask", "Message", config_parse_string, 0, &message },
225 { "Ask", "PID", config_parse_unsigned, 0, &pid },
226 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
64845bdc 227 { "Ask", "Echo", config_parse_bool, 0, &echo },
1d749d04 228 {}
ec863ba6
LP
229 };
230
ec863ba6 231 int r;
ec863ba6
LP
232
233 assert(filename);
234
36f822c4
ZJS
235 r = config_parse(NULL, filename, NULL,
236 NULL,
237 config_item_table_lookup, items,
238 true, false, true, NULL);
239 if (r < 0)
240 return r;
ec863ba6 241
7dcda352 242 if (!socket_name) {
ec863ba6 243 log_error("Invalid password file %s", filename);
e46eab86 244 return -EBADMSG;
ec863ba6
LP
245 }
246
e46eab86
ZJS
247 if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
248 return 0;
ec863ba6 249
e46eab86
ZJS
250 if (pid > 0 && !pid_is_alive(pid))
251 return 0;
ded80335 252
ec863ba6
LP
253 if (arg_action == ACTION_LIST)
254 printf("'%s' (PID %u)\n", message, pid);
e46eab86 255
ec863ba6 256 else if (arg_action == ACTION_WALL) {
0ddf1d3a 257 char *_wall;
ec863ba6 258
0ddf1d3a
LP
259 if (asprintf(&_wall,
260 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
9d3e691e 261 "Please enter password with the systemd-tty-ask-password-agent tool!",
5cfee414 262 strempty(*wall),
0ddf1d3a 263 *wall ? "\r\n\r\n" : "",
ec863ba6 264 message,
e46eab86
ZJS
265 pid) < 0)
266 return log_oom();
ec863ba6 267
0ddf1d3a
LP
268 free(*wall);
269 *wall = _wall;
e46eab86 270
ec863ba6 271 } else {
e46eab86 272 union sockaddr_union sa = {};
3414abee 273 size_t packet_length = 0;
e46eab86 274 _cleanup_close_ int socket_fd = -1;
ec863ba6
LP
275
276 assert(arg_action == ACTION_QUERY ||
277 arg_action == ACTION_WATCH);
278
279 if (access(socket_name, W_OK) < 0) {
ec863ba6
LP
280 if (arg_action == ACTION_QUERY)
281 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
282
e46eab86 283 return 0;
ec863ba6
LP
284 }
285
21bc923a 286 if (arg_plymouth) {
cb90460e 287 _cleanup_strv_free_ char **passwords = NULL;
21bc923a 288
e287086b 289 r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
e46eab86 290 if (r >= 0) {
21bc923a
LP
291 char **p;
292
293 packet_length = 1;
294 STRV_FOREACH(p, passwords)
295 packet_length += strlen(*p) + 1;
296
e46eab86
ZJS
297 packet = new(char, packet_length);
298 if (!packet)
21bc923a
LP
299 r = -ENOMEM;
300 else {
1d749d04 301 char *d = packet + 1;
21bc923a
LP
302
303 STRV_FOREACH(p, passwords)
304 d = stpcpy(d, *p) + 1;
1d749d04
ZJS
305
306 packet[0] = '+';
21bc923a
LP
307 }
308 }
309
310 } else {
e46eab86 311 _cleanup_free_ char *password = NULL;
00843602 312 int tty_fd = -1;
0cf84693 313
e46eab86 314 if (arg_console) {
3a43da28 315 tty_fd = acquire_terminal("/dev/console", false, false, false, USEC_INFINITY);
e46eab86 316 if (tty_fd < 0)
00843602 317 return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
e46eab86 318 }
0cf84693 319
e287086b 320 r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
e5ebf783 321
0cf84693 322 if (arg_console) {
00843602 323 tty_fd = safe_close(tty_fd);
0cf84693
LP
324 release_terminal();
325 }
21bc923a 326
9726f9ff 327 if (r >= 0) {
e46eab86
ZJS
328 packet_length = 1 + strlen(password) + 1;
329 packet = new(char, packet_length);
330 if (!packet)
9726f9ff
LP
331 r = -ENOMEM;
332 else {
333 packet[0] = '+';
e46eab86 334 strcpy(packet + 1, password);
9726f9ff 335 }
5ba7b871 336 }
0cf84693
LP
337 }
338
1d749d04 339 if (IN_SET(r, -ETIME, -ENOENT))
446f0046 340 /* If the query went away, that's OK */
e46eab86 341 return 0;
446f0046 342
f647962d
MS
343 if (r < 0)
344 return log_error_errno(r, "Failed to query password: %m");
ec863ba6 345
e46eab86 346 socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
4a62c710
MS
347 if (socket_fd < 0)
348 return log_error_errno(errno, "socket(): %m");
ec863ba6 349
ec863ba6
LP
350 sa.un.sun_family = AF_UNIX;
351 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
352
00843602
LP
353 r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name));
354 if (r < 0)
355 return log_error_errno(errno, "Failed to send: %m");
ec863ba6
LP
356 }
357
e46eab86 358 return 0;
ec863ba6
LP
359}
360
0cf84693 361static int wall_tty_block(void) {
1d749d04 362 _cleanup_free_ char *p = NULL;
fc116c6a 363 dev_t devnr;
00843602 364 int fd, r;
7af53310 365
4d6d6518 366 r = get_ctty_devnr(0, &devnr);
e287086b
LP
367 if (r == -ENXIO) /* We have no controlling tty */
368 return -ENOTTY;
4d6d6518 369 if (r < 0)
00843602 370 return log_error_errno(r, "Failed to get controlling TTY: %m");
7af53310 371
2b583ce6 372 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
00843602 373 return log_oom();
7af53310 374
d2e54fae 375 mkdir_parents_label(p, 0700);
7af53310
LP
376 mkfifo(p, 0600);
377
378 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
7af53310 379 if (fd < 0)
00843602 380 return log_error_errno(errno, "Failed to open %s: %m", p);
7af53310
LP
381
382 return fd;
383}
384
99f710dd 385static bool wall_tty_match(const char *path, void *userdata) {
1d749d04 386 _cleanup_free_ char *p = NULL;
00843602
LP
387 _cleanup_close_ int fd = -1;
388 struct stat st;
fc116c6a 389
1d749d04 390 if (!path_is_absolute(path))
63c372cb 391 path = strjoina("/dev/", path);
fc116c6a 392
00843602
LP
393 if (lstat(path, &st) < 0) {
394 log_debug_errno(errno, "Failed to stat %s: %m", path);
fc116c6a 395 return true;
00843602 396 }
fc116c6a 397
00843602
LP
398 if (!S_ISCHR(st.st_mode)) {
399 log_debug("%s is not a character device.", path);
fc116c6a 400 return true;
00843602 401 }
7af53310
LP
402
403 /* We use named pipes to ensure that wall messages suggesting
404 * password entry are not printed over password prompts
405 * already shown. We use the fact here that opening a pipe in
406 * non-blocking mode for write-only will succeed only if
407 * there's some writer behind it. Using pipes has the
408 * advantage that the block will automatically go away if the
409 * process dies. */
410
00843602
LP
411 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
412 log_oom();
7af53310 413 return true;
00843602 414 }
7af53310
LP
415
416 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
00843602
LP
417 if (fd < 0) {
418 log_debug_errno(errno, "Failed top open the wall pipe: %m");
419 return 1;
420 }
7af53310
LP
421
422 /* What, we managed to open the pipe? Then this tty is filtered. */
00843602 423 return 0;
7af53310
LP
424}
425
ec863ba6 426static int show_passwords(void) {
1d749d04 427 _cleanup_closedir_ DIR *d;
ec863ba6
LP
428 struct dirent *de;
429 int r = 0;
430
1d749d04
ZJS
431 d = opendir("/run/systemd/ask-password");
432 if (!d) {
ec863ba6
LP
433 if (errno == ENOENT)
434 return 0;
435
00843602 436 return log_error_errno(errno, "Failed top open /run/systemd/ask-password: %m");
ec863ba6
LP
437 }
438
00843602 439 FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
1d749d04 440 _cleanup_free_ char *p = NULL, *wall = NULL;
ec863ba6
LP
441 int q;
442
1a6f4df6
LP
443 /* We only support /dev on tmpfs, hence we can rely on
444 * d_type to be reliable */
445
ec863ba6
LP
446 if (de->d_type != DT_REG)
447 continue;
448
a34bf9db 449 if (hidden_file(de->d_name))
ec863ba6
LP
450 continue;
451
452 if (!startswith(de->d_name, "ask."))
453 continue;
454
1d749d04
ZJS
455 p = strappend("/run/systemd/ask-password/", de->d_name);
456 if (!p)
457 return log_oom();
ec863ba6 458
1d749d04
ZJS
459 q = parse_password(p, &wall);
460 if (q < 0 && r == 0)
ec863ba6
LP
461 r = q;
462
1d749d04 463 if (wall)
00843602 464 (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
ec863ba6
LP
465 }
466
ec863ba6
LP
467 return r;
468}
469
470static int watch_passwords(void) {
b9ba604e
LP
471 enum {
472 FD_INOTIFY,
473 FD_SIGNAL,
474 _FD_MAX
475 };
476
1d749d04 477 _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
b92bea5d 478 struct pollfd pollfd[_FD_MAX] = {};
b9ba604e 479 sigset_t mask;
ec863ba6
LP
480 int r;
481
0cf84693 482 tty_block_fd = wall_tty_block();
7af53310 483
00843602 484 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
ec863ba6 485
1d749d04
ZJS
486 notify = inotify_init1(IN_CLOEXEC);
487 if (notify < 0)
00843602 488 return log_error_errno(errno, "Failed to allocate directory watch: %m");
ec863ba6 489
1d749d04 490 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
00843602 491 return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
ec863ba6 492
72c0a2c2
LP
493 assert_se(sigemptyset(&mask) >= 0);
494 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
495 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
b9ba604e 496
1d749d04
ZJS
497 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
498 if (signal_fd < 0)
00843602 499 return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
b9ba604e 500
b9ba604e
LP
501 pollfd[FD_INOTIFY].fd = notify;
502 pollfd[FD_INOTIFY].events = POLLIN;
503 pollfd[FD_SIGNAL].fd = signal_fd;
504 pollfd[FD_SIGNAL].events = POLLIN;
ec863ba6
LP
505
506 for (;;) {
1d749d04
ZJS
507 r = show_passwords();
508 if (r < 0)
da927ba9 509 log_error_errno(r, "Failed to show password: %m");
ec863ba6 510
b9ba604e 511 if (poll(pollfd, _FD_MAX, -1) < 0) {
ec863ba6
LP
512 if (errno == EINTR)
513 continue;
514
1d749d04 515 return -errno;
ec863ba6
LP
516 }
517
b9ba604e 518 if (pollfd[FD_INOTIFY].revents != 0)
00843602 519 (void) flush_fd(notify);
b9ba604e
LP
520
521 if (pollfd[FD_SIGNAL].revents != 0)
522 break;
ec863ba6
LP
523 }
524
1d749d04 525 return 0;
ec863ba6
LP
526}
527
601185b4 528static void help(void) {
ec863ba6
LP
529 printf("%s [OPTIONS...]\n\n"
530 "Process system password requests.\n\n"
e5ebf783 531 " -h --help Show this help\n"
c52f663b 532 " --version Show package version\n"
e5ebf783
LP
533 " --list Show pending password requests\n"
534 " --query Process pending password requests\n"
35b8ca3a
HH
535 " --watch Continuously process password requests\n"
536 " --wall Continuously forward password requests to wall\n"
0cf84693
LP
537 " --plymouth Ask question with Plymouth instead of on TTY\n"
538 " --console Ask question on /dev/console instead of current TTY\n",
ec863ba6 539 program_invocation_short_name);
ec863ba6
LP
540}
541
542static int parse_argv(int argc, char *argv[]) {
543
544 enum {
545 ARG_LIST = 0x100,
546 ARG_QUERY,
547 ARG_WATCH,
548 ARG_WALL,
0cf84693 549 ARG_PLYMOUTH,
c52f663b
LP
550 ARG_CONSOLE,
551 ARG_VERSION
ec863ba6
LP
552 };
553
554 static const struct option options[] = {
e5ebf783 555 { "help", no_argument, NULL, 'h' },
c52f663b 556 { "version", no_argument, NULL, ARG_VERSION },
e5ebf783
LP
557 { "list", no_argument, NULL, ARG_LIST },
558 { "query", no_argument, NULL, ARG_QUERY },
559 { "watch", no_argument, NULL, ARG_WATCH },
560 { "wall", no_argument, NULL, ARG_WALL },
561 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
0cf84693 562 { "console", no_argument, NULL, ARG_CONSOLE },
eb9da376 563 {}
ec863ba6
LP
564 };
565
566 int c;
567
568 assert(argc >= 0);
569 assert(argv);
570
601185b4 571 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
ec863ba6
LP
572
573 switch (c) {
574
575 case 'h':
601185b4
ZJS
576 help();
577 return 0;
ec863ba6 578
c52f663b 579 case ARG_VERSION:
3f6fd1ba 580 return version();
c52f663b 581
ec863ba6
LP
582 case ARG_LIST:
583 arg_action = ACTION_LIST;
584 break;
585
586 case ARG_QUERY:
587 arg_action = ACTION_QUERY;
588 break;
589
590 case ARG_WATCH:
591 arg_action = ACTION_WATCH;
592 break;
593
594 case ARG_WALL:
595 arg_action = ACTION_WALL;
596 break;
597
e5ebf783
LP
598 case ARG_PLYMOUTH:
599 arg_plymouth = true;
600 break;
601
0cf84693
LP
602 case ARG_CONSOLE:
603 arg_console = true;
604 break;
605
ec863ba6
LP
606 case '?':
607 return -EINVAL;
608
609 default:
eb9da376 610 assert_not_reached("Unhandled option");
ec863ba6 611 }
ec863ba6
LP
612
613 if (optind != argc) {
601185b4 614 log_error("%s takes no arguments.", program_invocation_short_name);
ec863ba6
LP
615 return -EINVAL;
616 }
617
618 return 1;
619}
620
621int main(int argc, char *argv[]) {
622 int r;
623
4b261568 624 log_set_target(LOG_TARGET_AUTO);
ec863ba6
LP
625 log_parse_environment();
626 log_open();
627
4c12626c
LP
628 umask(0022);
629
1d749d04
ZJS
630 r = parse_argv(argc, argv);
631 if (r <= 0)
ec863ba6
LP
632 goto finish;
633
0cf84693 634 if (arg_console) {
16f98462
LP
635 (void) setsid();
636 (void) release_terminal();
0cf84693
LP
637 }
638
1d749d04 639 if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
ec863ba6
LP
640 r = watch_passwords();
641 else
642 r = show_passwords();
96707269 643
ec863ba6
LP
644finish:
645 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
646}