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