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