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