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