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