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