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