• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.03
/src/shared/ask-password-api.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <poll.h>
5
#include <stdio.h>
6
#include <sys/inotify.h>
7
#include <sys/signalfd.h>
8
#include <sys/stat.h>
9
#include <termios.h>
10
#include <unistd.h>
11

12
#include "alloc-util.h"
13
#include "ansi-color.h"
14
#include "ask-password-api.h"
15
#include "creds-util.h"
16
#include "fd-util.h"
17
#include "fileio.h"
18
#include "format-util.h"
19
#include "fs-util.h"
20
#include "glyph-util.h"
21
#include "inotify-util.h"
22
#include "io-util.h"
23
#include "iovec-util.h"
24
#include "keyring-util.h"
25
#include "log.h"
26
#include "missing_syscall.h"
27
#include "nulstr-util.h"
28
#include "parse-util.h"
29
#include "path-lookup.h"
30
#include "plymouth-util.h"
31
#include "process-util.h"
32
#include "random-util.h"
33
#include "signal-util.h"
34
#include "socket-util.h"
35
#include "string-table.h"
36
#include "string-util.h"
37
#include "strv.h"
38
#include "terminal-util.h"
39
#include "time-util.h"
40
#include "tmpfile-util.h"
41
#include "umask-util.h"
42
#include "utf8.h"
43

44
#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
45

46
static const char* keyring_table[] = {
47
        [-KEY_SPEC_THREAD_KEYRING]       = "thread",
48
        [-KEY_SPEC_PROCESS_KEYRING]      = "process",
49
        [-KEY_SPEC_SESSION_KEYRING]      = "session",
50
        [-KEY_SPEC_USER_KEYRING]         = "user",
51
        [-KEY_SPEC_USER_SESSION_KEYRING] = "user-session",
52
        [-KEY_SPEC_GROUP_KEYRING]        = "group",
53
};
54

55
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(keyring, int);
×
56

57
static int lookup_key(const char *keyname, key_serial_t *ret) {
4✔
58
        key_serial_t serial;
4✔
59

60
        assert(keyname);
4✔
61
        assert(ret);
4✔
62

63
        serial = request_key("user", keyname, /* callout_info= */ NULL, /* dest_keyring= */ 0);
4✔
64
        if (serial == -1)
4✔
65
                return negative_errno();
4✔
66

67
        *ret = serial;
×
68
        return 0;
×
69
}
70

71
static int retrieve_key(key_serial_t serial, char ***ret) {
×
72
        _cleanup_(erase_and_freep) void *p = NULL;
×
73
        char **l;
×
74
        size_t n;
×
75
        int r;
×
76

77
        assert(ret);
×
78

79
        r = keyring_read(serial, &p, &n);
×
80
        if (r < 0)
×
81
                return r;
82

83
        l = strv_parse_nulstr(p, n);
×
84
        if (!l)
×
85
                return -ENOMEM;
86

87
        *ret = l;
×
88
        return 0;
×
89
}
90

91
static int get_ask_password_directory_for_flags(AskPasswordFlags flags, char **ret) {
1✔
92
        if (FLAGS_SET(flags, ASK_PASSWORD_USER))
1✔
93
                return acquire_user_ask_password_directory(ret);
×
94

95
        return strdup_to_full(ret, "/run/systemd/ask-password/"); /* Returns 1, indicating there's a suitable directory */
1✔
96
}
97

98
static int touch_ask_password_directory(AskPasswordFlags flags) {
×
99
        int r;
×
100

101
        _cleanup_free_ char *p = NULL;
×
102
        r = get_ask_password_directory_for_flags(flags, &p);
×
103
        if (r <= 0)
×
104
                return r;
105

106
        _cleanup_close_ int fd = open_mkdir(p, O_CLOEXEC, 0755);
×
107
        if (fd < 0)
×
108
                return fd;
109

110
        r = touch_fd(fd, USEC_INFINITY);
×
111
        if (r < 0)
×
112
                return r;
×
113

114
        return 1; /* did something */
115
}
116

117
static usec_t keyring_cache_timeout(void) {
×
118
        static usec_t saved_timeout = KEYRING_TIMEOUT_USEC;
×
119
        static bool saved_timeout_set = false;
×
120
        int r;
×
121

122
        if (saved_timeout_set)
×
123
                return saved_timeout;
×
124

125
        const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC");
×
126
        if (e) {
×
127
                r = parse_sec(e, &saved_timeout);
×
128
                if (r < 0)
×
129
                        log_debug_errno(r, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC, ignoring: %s", e);
×
130
        }
131

132
        saved_timeout_set = true;
×
133

134
        return saved_timeout;
×
135
}
136

137
static key_serial_t keyring_cache_type(void) {
×
138
        static key_serial_t saved_keyring = KEY_SPEC_USER_KEYRING;
×
139
        static bool saved_keyring_set = false;
×
140
        int r;
×
141

142
        if (saved_keyring_set)
×
143
                return saved_keyring;
×
144

145
        const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TYPE");
×
146
        if (e) {
×
147
                key_serial_t keyring;
×
148

149
                r = safe_atoi32(e, &keyring);
×
150
                if (r >= 0)
×
151
                        if (keyring < 0)
×
152
                                log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
×
153
                        else
154
                                saved_keyring = keyring;
×
155
                else {
156
                        keyring = keyring_from_string(e);
×
157
                        if (keyring < 0)
×
158
                                log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
×
159
                        else
160
                                saved_keyring = -keyring;
×
161
                }
162
        }
163

164
        saved_keyring_set = true;
×
165

166
        return saved_keyring;
×
167
}
168

169
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
×
170
        _cleanup_strv_free_erase_ char **l = NULL;
×
171
        _cleanup_(erase_and_freep) char *p = NULL;
×
172
        key_serial_t serial;
×
173
        size_t n;
×
174
        int r;
×
175

176
        assert(keyname);
×
177

178
        if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE) || keyring_cache_timeout() == 0)
×
179
                return 0;
×
180
        if (strv_isempty(passwords))
×
181
                return 0;
182

183
        r = lookup_key(keyname, &serial);
×
184
        if (r >= 0) {
×
185
                r = retrieve_key(serial, &l);
×
186
                if (r < 0)
×
187
                        return r;
188
        } else if (r != -ENOKEY)
×
189
                return r;
190

191
        r = strv_extend_strv(&l, passwords, /* filter_duplicates= */ true);
×
192
        if (r <= 0)
×
193
                return r;
194

195
        r = strv_make_nulstr(l, &p, &n);
×
196
        if (r < 0)
×
197
                return r;
198

199
        /* chop off the final NUL byte. We do this because we want to use the separator NUL bytes only if we
200
         * have multiple passwords. */
201
        n = LESS_BY(n, (size_t) 1);
×
202

203
        serial = add_key("user", keyname, p, n, keyring_cache_type());
×
204
        if (serial == -1)
×
205
                return -errno;
×
206

207
        if (keyring_cache_timeout() != USEC_INFINITY &&
×
208
                keyctl(KEYCTL_SET_TIMEOUT,
×
209
                       (unsigned long) serial,
210
                       (unsigned long) DIV_ROUND_UP(keyring_cache_timeout(), USEC_PER_SEC), 0, 0) < 0)
×
211
                log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
×
212

213
        /* Tell everyone to check the keyring */
214
        (void) touch_ask_password_directory(flags);
×
215

216
        log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
×
217

218
        return 1;
219
}
220

221
static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
×
222
        int r;
×
223

224
        assert(keyname);
×
225

226
        r = add_to_keyring(keyname, flags, passwords);
×
227
        if (r < 0)
×
228
                return log_debug_errno(r, "Failed to add password to kernel keyring: %m");
×
229

230
        return 0;
231
}
232

233
static int ask_password_keyring(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
4✔
234
        key_serial_t serial;
4✔
235
        int r;
4✔
236

237
        assert(req);
4✔
238
        assert(ret);
4✔
239

240
        if (!FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED))
4✔
241
                return -EUNATCH;
4✔
242

243
        r = lookup_key(req->keyring, &serial);
4✔
244
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EPERM)
8✔
245
                /* When retrieving, the distinction between "kernel or container manager don't support or
246
                 * allow this" and "no matching key known" doesn't matter. Note that we propagate EACCESS
247
                 * here (even if EPERM not) since that is used if the keyring is available, but we lack
248
                 * access to the key. */
249
                return -ENOKEY;
250
        if (r < 0)
4✔
251
                return r;
252

253
        _cleanup_strv_free_erase_ char **l = NULL;
×
254
        r = retrieve_key(serial, &l);
×
255
        if (r < 0)
×
256
                return r;
257

258
        if (strv_isempty(l))
×
259
                return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password from keyring.");
×
260

261
        *ret = TAKE_PTR(l);
×
262
        return 0;
×
263
}
264

265
static int backspace_chars(int ttyfd, size_t p) {
×
266
        if (ttyfd < 0)
×
267
                return 0;
×
268

269
        _cleanup_free_ char *buf = malloc_multiply(3, p);
×
270
        if (!buf)
×
271
                return log_oom();
×
272

273
        for (size_t i = 0; i < p; i++)
×
274
                memcpy(buf + 3 * i, "\b \b", 3);
×
275

276
        return loop_write(ttyfd, buf, 3 * p);
×
277
}
278

279
static int backspace_string(int ttyfd, const char *str) {
×
280
        assert(str);
×
281

282
        /* Backspaces through enough characters to entirely undo printing of the specified string. */
283

284
        if (ttyfd < 0)
×
285
                return 0;
286

287
        size_t m = utf8_n_codepoints(str);
×
288
        if (m == SIZE_MAX)
×
289
                m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes
×
290
                                  * output. Most likely this happened because we are not in a UTF-8 locale,
291
                                  * and in that case that is the correct thing to do. And even if it's not,
292
                                  * terminals tend to stop backspacing at the leftmost column, hence
293
                                  * backspacing too much should be mostly OK. */
294

295
        return backspace_chars(ttyfd, m);
×
296
}
297

298
int ask_password_plymouth(
×
299
                const AskPasswordRequest *req,
300
                AskPasswordFlags flags,
301
                char ***ret) {
302

303
        _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF;
×
304
        _cleanup_free_ char *packet = NULL;
×
305
        ssize_t k;
×
306
        int r, n;
×
307
        char buffer[LINE_MAX];
×
308
        size_t p = 0;
×
309

310
        assert(req);
×
311
        assert(ret);
×
312

313
        if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
×
314
                return -ENOEXEC;
315

316
        const char *message = req->message ?: "Password:";
×
317

318
        if (req->flag_file) {
×
319
                inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
×
320
                if (inotify_fd < 0)
×
321
                        return -errno;
×
322

323
                if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB) < 0) /* for the link count */
×
324
                        return -errno;
×
325
        }
326

327
        fd = plymouth_connect(SOCK_NONBLOCK);
×
328
        if (fd < 0)
×
329
                return fd;
330

331
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
×
332
                packet = strdup("c");
×
333
                n = 1;
×
334
        } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
×
335
                packet = NULL;
×
336
        if (!packet)
×
337
                return -ENOMEM;
338

339
        r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
×
340
        if (r < 0)
×
341
                return r;
342

343
        CLEANUP_ERASE(buffer);
×
344

345
        enum {
×
346
                POLL_SOCKET,
347
                POLL_TWO,
348
                POLL_THREE,
349
                _POLL_MAX,
350
        };
351

352
        struct pollfd pollfd[_POLL_MAX] = {
×
353
                [POLL_SOCKET] = {
354
                        .fd = fd,
355
                        .events = POLLIN,
356
                },
357
        };
358
        size_t n_pollfd = POLL_SOCKET + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
×
359
        if (inotify_fd >= 0)
×
360
                pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
×
361
                        .fd = inotify_fd,
362
                        .events = POLLIN,
363
                };
364
        if (req->hup_fd >= 0)
×
365
                pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
×
366
                        .fd = req->hup_fd,
367
                        .events = POLLHUP,
368
                };
369

370
        assert(n_pollfd <= _POLL_MAX);
×
371

372
        for (;;) {
×
373
                usec_t timeout;
×
374

375
                if (req->until > 0)
×
376
                        timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
×
377
                else
378
                        timeout = USEC_INFINITY;
379

380
                if (req->flag_file && access(req->flag_file, F_OK) < 0)
×
381
                        return -errno;
×
382

383
                r = ppoll_usec(pollfd, n_pollfd, timeout);
×
384
                if (r == -EINTR)
×
385
                        continue;
×
386
                if (r < 0)
×
387
                        return r;
388
                if (r == 0)
×
389
                        return -ETIME;
390

391
                if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
×
392
                        return -ECONNRESET;
393

394
                if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0)
×
395
                        (void) flush_fd(inotify_fd);
×
396

397
                if (pollfd[POLL_SOCKET].revents == 0)
×
398
                        continue;
×
399

400
                k = read(fd, buffer + p, sizeof(buffer) - p);
×
401
                if (k < 0) {
×
402
                        if (ERRNO_IS_TRANSIENT(errno))
×
403
                                continue;
×
404

405
                        return -errno;
×
406
                }
407
                if (k == 0)
×
408
                        return -EIO;
409

410
                p += k;
×
411

412
                if (buffer[0] == 5) {
×
413

414
                        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
×
415
                                /* Hmm, first try with cached
416
                                 * passwords failed, so let's retry
417
                                 * with a normal password request */
418
                                packet = mfree(packet);
×
419

420
                                if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
×
421
                                        return -ENOMEM;
422

423
                                r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
×
424
                                if (r < 0)
×
425
                                        return r;
426

427
                                flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
×
428
                                p = 0;
×
429
                                continue;
×
430
                        }
431

432
                        /* No password, because UI not shown */
433
                        return -ENOENT;
434

435
                } else if (IN_SET(buffer[0], 2, 9)) {
×
436
                        _cleanup_strv_free_erase_ char **l = NULL;
×
437
                        uint32_t size;
×
438

439
                        /* One or more answers */
440
                        if (p < 5)
×
441
                                continue;
×
442

443
                        memcpy(&size, buffer+1, sizeof(size));
×
444
                        size = le32toh(size);
×
445
                        if (size + 5 > sizeof(buffer))
×
446
                                return -EIO;
447

448
                        if (p-5 < size)
×
449
                                continue;
×
450

451
                        l = strv_parse_nulstr(buffer + 5, size);
×
452
                        if (!l)
×
453
                                return -ENOMEM;
454

455
                        if (strv_isempty(l))
×
456
                                return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Received an empty password.");
×
457

458
                        *ret = TAKE_PTR(l);
×
459
                        return 0;
×
460

461
                } else
462
                        /* Unknown packet */
463
                        return -EIO;
464
        }
465
}
466

467
#define NO_ECHO "(no echo) "
468
#define PRESS_TAB "(press TAB for no echo) "
469
#define SKIPPED "(skipped)"
470

471
int ask_password_tty(
1✔
472
                const AskPasswordRequest *req,
473
                AskPasswordFlags flags,
474
                char ***ret) {
475

476
        bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
1✔
477
        _cleanup_close_ int cttyfd = -EBADF, inotify_fd = -EBADF;
1✔
478
        struct termios old_termios, new_termios;
1✔
479
        char passphrase[LINE_MAX + 1] = {}, *x;
1✔
480
        _cleanup_strv_free_erase_ char **l = NULL;
1✔
481
        size_t p = 0, codepoint = 0;
1✔
482
        int r;
1✔
483

484
        assert(req);
1✔
485
        assert(ret);
1✔
486

487
        if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
1✔
488
                return -ENOEXEC;
489

490
        if (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY))
1✔
491
                return -EUNATCH;
492

493
        const char *message = req->message ?: "Password:";
1✔
494
        const char *keyring = req->keyring;
1✔
495

496
        if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled())
1✔
497
                message = strjoina(glyph(GLYPH_LOCK_AND_KEY), " ", message);
×
498

499
        if (req->flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) {
1✔
500
                inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
×
501
                if (inotify_fd < 0)
×
502
                        return -errno;
×
503
        }
504
        if (req->flag_file)
1✔
505
                if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB /* for the link count */) < 0)
×
506
                        return -errno;
×
507
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring) {
1✔
508
                r = ask_password_keyring(req, flags, ret);
×
509
                if (r >= 0)
×
510
                        return 0;
×
511
                if (r != -ENOKEY)
×
512
                        return r;
513

514
                /* Let's watch the askpw directory for mtime changes, which we issue above whenever the
515
                 * keyring changes */
516
                _cleanup_free_ char *watch_path = NULL;
×
517
                r = get_ask_password_directory_for_flags(flags, &watch_path);
×
518
                if (r < 0)
×
519
                        return r;
520
                if (r > 0) {
×
521
                        _cleanup_close_ int watch_fd = open_mkdir(watch_path, O_CLOEXEC|O_RDONLY, 0755);
×
522
                        if (watch_fd < 0)
×
523
                                return watch_fd;
524

525
                        r = inotify_add_watch_fd(inotify_fd, watch_fd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
×
526
                        if (r < 0)
×
527
                                return r;
528
                }
529
        }
530

531
        CLEANUP_ERASE(passphrase);
1✔
532

533
        /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
534
        int ttyfd;
1✔
535
        if (req->tty_fd < 0)
1✔
536
                ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
1✔
537
        else
538
                ttyfd = req->tty_fd;
539

540
        if (ttyfd >= 0) {
1✔
541
                if (tcgetattr(ttyfd, &old_termios) < 0)
×
542
                        return -errno;
×
543

544
                if (FLAGS_SET(flags, ASK_PASSWORD_CONSOLE_COLOR))
×
545
                        use_color = dev_console_colors_enabled();
×
546
                else
547
                        use_color = colors_enabled();
×
548

549
                if (use_color)
×
550
                        (void) loop_write(ttyfd, ANSI_HIGHLIGHT, SIZE_MAX);
×
551

552
                (void) loop_write(ttyfd, message, SIZE_MAX);
×
553
                (void) loop_write(ttyfd, " ", 1);
×
554

555
                if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && !FLAGS_SET(flags, ASK_PASSWORD_ECHO)) {
×
556
                        if (use_color)
×
557
                                (void) loop_write(ttyfd, ansi_grey(), SIZE_MAX);
×
558

559
                        (void) loop_write(ttyfd, PRESS_TAB, SIZE_MAX);
×
560
                        press_tab_visible = true;
561
                }
562

563
                if (use_color)
×
564
                        (void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
×
565

566
                new_termios = old_termios;
×
567
                termios_disable_echo(&new_termios);
×
568

569
                r = RET_NERRNO(tcsetattr(ttyfd, TCSADRAIN, &new_termios));
×
570
                if (r < 0)
×
571
                        goto finish;
×
572

573
                reset_tty = true;
574
        }
575

576
        enum {
1✔
577
                POLL_TTY,
578
                POLL_TWO,
579
                POLL_THREE,
580
                _POLL_MAX,
581
        };
582

583
        struct pollfd pollfd[_POLL_MAX] = {
1✔
584
                [POLL_TTY]     = {
585
                        .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
1✔
586
                        .events = POLLIN,
587
                },
588
        };
589
        size_t n_pollfd = POLL_TTY + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
1✔
590

591
        if (inotify_fd >= 0)
1✔
592
                pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
×
593
                        .fd = inotify_fd,
594
                        .events = POLLIN,
595
                };
596
        if (req->hup_fd >= 0)
1✔
597
                pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
×
598
                        .fd = req->hup_fd,
599
                        .events = POLLHUP,
600
                };
601

602
        assert(n_pollfd <= _POLL_MAX);
1✔
603

604
        for (;;) {
1✔
605
                _cleanup_(erase_char) char c;
×
606
                usec_t timeout;
1✔
607
                ssize_t n;
1✔
608

609
                if (req->until > 0)
1✔
610
                        timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
1✔
611
                else
612
                        timeout = USEC_INFINITY;
613

614
                if (req->flag_file) {
1✔
615
                        r = RET_NERRNO(access(req->flag_file, F_OK));
×
616
                        if (r < 0)
×
617
                                goto finish;
×
618
                }
619

620
                r = ppoll_usec(pollfd, n_pollfd, timeout);
1✔
621
                if (r == -EINTR)
1✔
622
                        continue;
×
623
                if (r < 0)
1✔
624
                        goto finish;
×
625
                if (r == 0) {
1✔
626
                        r = -ETIME;
×
627
                        goto finish;
×
628
                }
629

630
                if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP) {
1✔
631
                        r = -ECONNRESET;
×
632
                        goto finish;
×
633
                }
634

635
                if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0 && keyring) {
1✔
636
                        (void) flush_fd(inotify_fd);
×
637

638
                        r = ask_password_keyring(req, flags, ret);
×
639
                        if (r >= 0) {
×
640
                                r = 0;
×
641
                                goto finish;
×
642
                        } else if (r != -ENOKEY)
×
643
                                goto finish;
×
644
                }
645

646
                if (pollfd[POLL_TTY].revents == 0)
1✔
647
                        continue;
×
648

649
                n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
1✔
650
                if (n < 0) {
1✔
651
                        if (ERRNO_IS_TRANSIENT(errno))
×
652
                                continue;
×
653

654
                        r = -errno;
×
655
                        goto finish;
×
656

657
                }
658

659
                if (press_tab_visible) {
1✔
660
                        assert(ttyfd >= 0);
×
661
                        backspace_chars(ttyfd, strlen(PRESS_TAB));
×
662
                        press_tab_visible = false;
663
                }
664

665
                /* We treat EOF, newline and NUL byte all as valid end markers */
666
                if (n == 0 || c == '\n' || c == 0)
1✔
667
                        break;
668

669
                if (c == 4) { /* C-d also known as EOT */
×
670
                        if (ttyfd >= 0)
×
671
                                (void) loop_write(ttyfd, SKIPPED, SIZE_MAX);
×
672

673
                        goto skipped;
×
674
                }
675

676
                if (c == 21) { /* C-u */
×
677

678
                        if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
×
679
                                (void) backspace_string(ttyfd, passphrase);
×
680

681
                        explicit_bzero_safe(passphrase, sizeof(passphrase));
×
682
                        p = codepoint = 0;
×
683

684
                } else if (IN_SET(c, '\b', 127)) {
×
685

686
                        if (p > 0) {
×
687
                                size_t q;
×
688

689
                                if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
×
690
                                        (void) backspace_chars(ttyfd, 1);
×
691

692
                                /* Remove a full UTF-8 codepoint from the end. For that, figure out where the
693
                                 * last one begins */
694
                                q = 0;
695
                                for (;;) {
×
696
                                        int z;
×
697

698
                                        z = utf8_encoded_valid_unichar(passphrase + q, SIZE_MAX);
×
699
                                        if (z <= 0) {
×
700
                                                q = SIZE_MAX; /* Invalid UTF8! */
701
                                                break;
702
                                        }
703

704
                                        if (q + z >= p) /* This one brings us over the edge */
×
705
                                                break;
706

707
                                        q += z;
708
                                }
709

710
                                p = codepoint = q == SIZE_MAX ? p - 1 : q;
×
711
                                explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
×
712

713
                        } else if (!dirty && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
×
714

715
                                flags |= ASK_PASSWORD_SILENT;
×
716

717
                                /* There are two ways to enter silent mode. Either by pressing backspace as
718
                                 * first key (and only as first key), or ... */
719

720
                                if (ttyfd >= 0)
×
721
                                        (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
×
722

723
                        } else if (ttyfd >= 0)
×
724
                                (void) loop_write(ttyfd, "\a", 1);
×
725

726
                } else if (c == '\t' && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
×
727

728
                        (void) backspace_string(ttyfd, passphrase);
×
729
                        flags |= ASK_PASSWORD_SILENT;
×
730

731
                        /* ... or by pressing TAB at any time. */
732

733
                        if (ttyfd >= 0)
×
734
                                (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
×
735

736
                } else if (char_is_cc(c) || p >= sizeof(passphrase)-1) {
×
737
                        /* Don't accept control chars or overly long passphrases */
738
                        if (ttyfd >= 0)
×
739
                                (void) loop_write(ttyfd, "\a", 1);
×
740

741
                } else {
742
                        passphrase[p++] = c;
×
743

744
                        if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && ttyfd >= 0) {
×
745
                                /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
746
                                n = utf8_encoded_valid_unichar(passphrase + codepoint, SIZE_MAX);
×
747
                                if (n >= 0) {
×
748
                                        if (FLAGS_SET(flags, ASK_PASSWORD_ECHO))
×
749
                                                (void) loop_write(ttyfd, passphrase + codepoint, n);
×
750
                                        else
751
                                                (void) loop_write(ttyfd,
×
752
                                                                  glyph(GLYPH_BULLET),
×
753
                                                                  SIZE_MAX);
754
                                        codepoint = p;
755
                                }
756
                        }
757

758
                        dirty = true;
759
                }
760
        }
761

762
        x = strndup(passphrase, p);
1✔
763
        if (!x) {
1✔
764
                r = -ENOMEM;
×
765
                goto finish;
×
766
        }
767

768
        r = strv_consume(&l, x);
1✔
769
        if (r < 0)
1✔
770
                goto finish;
×
771

772
skipped:
1✔
773
        if (strv_isempty(l))
1✔
774
                r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
×
775
        else {
776
                if (keyring)
1✔
777
                        (void) add_to_keyring_and_log(keyring, flags, l);
×
778

779
                *ret = TAKE_PTR(l);
1✔
780
                r = 0;
1✔
781
        }
782

783
finish:
1✔
784
        if (ttyfd >= 0 && reset_tty) {
1✔
785
                (void) loop_write(ttyfd, "\n", 1);
×
786
                (void) tcsetattr(ttyfd, TCSADRAIN, &old_termios);
×
787
        }
788

789
        return r;
790
}
791

792
static int create_socket(const char *askpwdir, char **ret) {
1✔
793
        _cleanup_free_ char *path = NULL;
1✔
794
        union sockaddr_union sa;
1✔
795
        socklen_t sa_len;
1✔
796
        _cleanup_close_ int fd = -EBADF;
1✔
797
        int r;
1✔
798

799
        assert(askpwdir);
1✔
800
        assert(ret);
1✔
801

802
        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
1✔
803
        if (fd < 0)
1✔
804
                return -errno;
×
805

806
        if (asprintf(&path, "%s/sck.%" PRIx64, askpwdir, random_u64()) < 0)
1✔
807
                return -ENOMEM;
808

809
        r = sockaddr_un_set_path(&sa.un, path);
1✔
810
        if (r < 0)
1✔
811
                return r;
812
        sa_len = r;
1✔
813

814
        WITH_UMASK(0177)
2✔
815
                if (bind(fd, &sa.sa, sa_len) < 0)
1✔
816
                        return -errno;
×
817

818
        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
1✔
819
        if (r < 0)
1✔
820
                return r;
821

822
        *ret = TAKE_PTR(path);
1✔
823
        return TAKE_FD(fd);
1✔
824
}
825

826
int ask_password_agent(
1✔
827
                const AskPasswordRequest *req,
828
                AskPasswordFlags flags,
829
                char ***ret) {
830

831
        _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, dfd = -EBADF;
3✔
UNCOV
832
        _cleanup_(unlink_and_freep) char *socket_name = NULL;
×
833
        _cleanup_free_ char *temp = NULL, *final = NULL;
1✔
834
        _cleanup_strv_free_erase_ char **l = NULL;
×
835
        _cleanup_fclose_ FILE *f = NULL;
1✔
836
        sigset_t mask, oldmask;
1✔
837
        int r;
1✔
838

839
        assert(req);
1✔
840
        assert(ret);
1✔
841

842
        if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
1✔
843
                return -ENOEXEC;
844

845
        if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
1✔
846
                return -EUNATCH;
847

848
        /* We don't support the flag file concept for now when querying via the agent logic */
849
        if (req->flag_file)
1✔
850
                return -EOPNOTSUPP;
851

852
        assert_se(sigemptyset(&mask) >= 0);
1✔
853
        assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0);
1✔
854
        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
1✔
855

856
        _cleanup_free_ char *askpwdir = NULL;
1✔
857
        r = get_ask_password_directory_for_flags(flags, &askpwdir);
1✔
858
        if (r < 0)
1✔
859
                goto finish;
×
860
        if (r == 0) {
1✔
861
                r = -ENXIO;
×
862
                goto finish;
×
863
        }
864

865
        dfd = open_mkdir(askpwdir, O_RDONLY|O_CLOEXEC, 0755);
1✔
866
        if (dfd < 0) {
1✔
867
                r = log_debug_errno(dfd, "Failed to open directory '%s': %m", askpwdir);
×
868
                goto finish;
×
869
        }
870

871
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req->keyring) {
1✔
872
                r = ask_password_keyring(req, flags, ret);
×
873
                if (r >= 0) {
×
874
                        r = 0;
×
875
                        goto finish;
×
876
                } else if (r != -ENOKEY)
×
877
                        goto finish;
×
878

879
                inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
×
880
                if (inotify_fd < 0) {
×
881
                        r = -errno;
×
882
                        goto finish;
×
883
                }
884

885
                r = inotify_add_watch_fd(inotify_fd, dfd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
×
886
                if (r < 0)
×
887
                        goto finish;
×
888
        }
889

890
        if (asprintf(&final, "ask.%" PRIu64, random_u64()) < 0) {
1✔
891
                r = -ENOMEM;
×
892
                goto finish;
×
893
        }
894

895
        r = fopen_temporary_at(dfd, final, &f, &temp);
1✔
896
        if (r < 0)
1✔
897
                goto finish;
×
898

899
        signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
1✔
900
        if (signal_fd < 0) {
1✔
901
                r = -errno;
×
902
                goto finish;
×
903
        }
904

905
        socket_fd = create_socket(askpwdir, &socket_name);
1✔
906
        if (socket_fd < 0) {
1✔
907
                r = socket_fd;
×
908
                goto finish;
×
909
        }
910

911
        fprintf(f,
1✔
912
                "[Ask]\n"
913
                "PID="PID_FMT"\n"
914
                "Socket=%s\n"
915
                "AcceptCached=%i\n"
916
                "Echo=%i\n"
917
                "NotAfter="USEC_FMT"\n"
918
                "Silent=%i\n",
919
                getpid_cached(),
920
                socket_name,
921
                FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED),
922
                FLAGS_SET(flags, ASK_PASSWORD_ECHO),
1✔
923
                req->until,
1✔
924
                FLAGS_SET(flags, ASK_PASSWORD_SILENT));
1✔
925

926
        if (req->message)
1✔
927
                fprintf(f, "Message=%s\n", req->message);
×
928

929
        if (req->icon)
1✔
930
                fprintf(f, "Icon=%s\n", req->icon);
×
931

932
        if (req->id)
1✔
933
                fprintf(f, "Id=%s\n", req->id);
×
934

935
        if (fchmod(fileno(f), 0644) < 0) {
1✔
936
                r = -errno;
×
937
                goto finish;
×
938
        }
939

940
        r = fflush_and_check(f);
1✔
941
        if (r < 0)
1✔
942
                goto finish;
×
943

944
        if (renameat(dfd, temp, dfd, final) < 0) {
1✔
945
                r = -errno;
×
946
                goto finish;
×
947
        }
948

949
        temp = mfree(temp);
1✔
950

951
        enum {
1✔
952
                POLL_SOCKET,
953
                POLL_SIGNAL,
954
                POLL_THREE,
955
                POLL_FOUR,
956
                _POLL_MAX
957
        };
958

959
        struct pollfd pollfd[_POLL_MAX] = {
1✔
960
                [POLL_SOCKET]  = { .fd = socket_fd,  .events = POLLIN },
961
                [POLL_SIGNAL]  = { .fd = signal_fd,  .events = POLLIN },
962
        };
963
        size_t n_pollfd = POLL_SIGNAL + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
1✔
964

965
        if (inotify_fd >= 0)
1✔
966
                pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
×
967
                        .fd = inotify_fd,
968
                        .events = POLLIN,
969
                };
970
        if (req->hup_fd >= 0)
1✔
971
                pollfd[hup_fd_idx = n_pollfd ++] = (struct pollfd) {
×
972
                        .fd = req->hup_fd,
973
                        .events = POLLHUP,
974
                };
975

976
        assert(n_pollfd <= _POLL_MAX);
1✔
977

978
        for (;;) {
1✔
979
                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
1✔
980
                char passphrase[LINE_MAX+1];
1✔
981
                struct iovec iovec;
1✔
982
                struct ucred *ucred;
1✔
983
                usec_t timeout;
1✔
984
                ssize_t n;
1✔
985

986
                if (req->until > 0)
1✔
987
                        timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
1✔
988
                else
989
                        timeout = USEC_INFINITY;
990

991
                r = ppoll_usec(pollfd, n_pollfd, timeout);
1✔
992
                if (r == -EINTR)
1✔
993
                        continue;
×
994
                if (r < 0)
1✔
995
                        goto finish;
×
996
                if (r == 0) {
1✔
997
                        r = -ETIME;
×
998
                        goto finish;
×
999
                }
1000

1001
                if (pollfd[POLL_SIGNAL].revents & POLLIN) {
1✔
1002
                        r = -EINTR;
×
1003
                        goto finish;
×
1004
                }
1005

1006
                if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
1✔
1007
                        return -ECONNRESET;
×
1008

1009
                if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0) {
1✔
1010
                        (void) flush_fd(inotify_fd);
×
1011

1012
                        if (req->keyring) {
×
1013
                                r = ask_password_keyring(req, flags, ret);
×
1014
                                if (r >= 0) {
×
1015
                                        r = 0;
×
1016
                                        goto finish;
×
1017
                                } else if (r != -ENOKEY)
×
1018
                                        goto finish;
×
1019
                        }
1020
                }
1021

1022
                if (pollfd[POLL_SOCKET].revents == 0)
1✔
1023
                        continue;
×
1024

1025
                if (pollfd[POLL_SOCKET].revents != POLLIN) {
1✔
1026
                        r = -EIO;
×
1027
                        goto finish;
×
1028
                }
1029

1030
                iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
1✔
1031

1032
                struct msghdr msghdr = {
1✔
1033
                        .msg_iov = &iovec,
1034
                        .msg_iovlen = 1,
1035
                        .msg_control = &control,
1036
                        .msg_controllen = sizeof(control),
1037
                };
1038

1039
                n = recvmsg_safe(socket_fd, &msghdr, 0);
1✔
1040
                if (ERRNO_IS_NEG_TRANSIENT(n))
1✔
1041
                        continue;
×
1042
                if (n == -ECHRNG) {
1✔
1043
                        log_debug_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
×
1044
                        continue;
×
1045
                }
1046
                if (n == -EXFULL) {
1✔
1047
                        log_debug_errno(n, "Got message with truncated payload data, ignoring.");
×
1048
                        continue;
×
1049
                }
1050
                if (n < 0) {
1✔
1051
                        r = (int) n;
×
1052
                        goto finish;
×
1053
                }
1054

1055
                CLEANUP_ERASE(passphrase);
×
1056

1057
                cmsg_close_all(&msghdr);
1✔
1058

1059
                if (n == 0) {
1✔
1060
                        log_debug("Message too short");
×
1061
                        continue;
×
1062
                }
1063

1064
                ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
1✔
1065
                if (!ucred) {
1✔
1066
                        log_debug("Received message without credentials. Ignoring.");
×
1067
                        continue;
×
1068
                }
1069

1070
                if (ucred->uid != getuid() && ucred->uid != 0) {
1✔
1071
                        log_debug("Got response from bad user. Ignoring.");
×
1072
                        continue;
×
1073
                }
1074

1075
                if (passphrase[0] == '+') {
1✔
1076
                        /* An empty message refers to the empty password */
1077
                        if (n == 1)
1✔
1078
                                l = strv_new("");
×
1079
                        else
1080
                                l = strv_parse_nulstr(passphrase+1, n-1);
1✔
1081
                        if (!l) {
1✔
1082
                                r = -ENOMEM;
×
1083
                                goto finish;
×
1084
                        }
1085

1086
                        if (strv_isempty(l)) {
1✔
1087
                                l = strv_free(l);
×
1088
                                log_debug("Invalid packet");
×
1089
                                continue;
×
1090
                        }
1091

1092
                        break;
1✔
1093
                }
1094

1095
                if (passphrase[0] == '-') {
×
1096
                        r = -ECANCELED;
×
1097
                        goto finish;
×
1098
                }
1099

1100
                log_debug("Invalid packet");
×
1101
        }
1102

1103
        if (req->keyring)
1✔
1104
                (void) add_to_keyring_and_log(req->keyring, flags, l);
×
1105

1106
        *ret = TAKE_PTR(l);
1✔
1107
        r = 0;
1✔
1108

1109
finish:
1✔
1110
        if (temp) {
1✔
1111
                assert(dfd >= 0);
×
1112
                (void) unlinkat(dfd, temp, 0);
×
1113
        } else if (final) {
1✔
1114
                assert(dfd >= 0);
1✔
1115
                (void) unlinkat(dfd, final, 0);
1✔
1116
        }
1117

1118
        assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
1✔
1119
        return r;
1120
}
1121

1122
static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
83✔
1123
        _cleanup_(erase_and_freep) char *buffer = NULL;
83✔
1124
        _cleanup_strv_free_erase_ char **l = NULL;
83✔
1125
        size_t size;
83✔
1126
        int r;
83✔
1127

1128
        assert(req);
83✔
1129
        assert(req->credential);
83✔
1130
        assert(ret);
83✔
1131

1132
        r = read_credential(req->credential, (void**) &buffer, &size);
83✔
1133
        if (IN_SET(r, -ENXIO, -ENOENT)) /* No credentials passed or this credential not defined? */
83✔
1134
                return -ENOKEY;
1135

1136
        l = strv_parse_nulstr(buffer, size);
2✔
1137
        if (!l)
2✔
1138
                return -ENOMEM;
1139

1140
        if (strv_isempty(l))
2✔
1141
                return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password in credential.");
×
1142

1143
        *ret = TAKE_PTR(l);
2✔
1144
        return 0;
2✔
1145
}
1146

1147
int ask_password_auto(
83✔
1148
                const AskPasswordRequest *req,
1149
                AskPasswordFlags flags,
1150
                char ***ret) {
1151

1152
        int r;
83✔
1153

1154
        assert(req);
83✔
1155
        assert(ret);
83✔
1156

1157
        /* Returns the following well-known errors:
1158
         *
1159
         *      -ETIME → a timeout was specified and hit
1160
         *    -EUNATCH → couldn't ask interactively and no cached password available either
1161
         *     -ENOENT → the specified flag file disappeared
1162
         *  -ECANCELED → the user explicitly cancelled the request
1163
         *      -EINTR → SIGINT/SIGTERM where received during the query
1164
         *    -ENOEXEC → headless mode was requested but no password could be acquired non-interactively
1165
         * -ECONNRESET → a POLLHUP has been seen on the specified hup_fd
1166
         */
1167

1168
        if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && req->credential) {
83✔
1169
                r = ask_password_credential(req, flags, ret);
83✔
1170
                if (r != -ENOKEY)
83✔
1171
                        return r;
1172
        }
1173

1174
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) &&
81✔
1175
            req->keyring &&
4✔
1176
            (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) || !isatty_safe(STDIN_FILENO)) &&
4✔
1177
            FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) {
4✔
1178
                r = ask_password_keyring(req, flags, ret);
4✔
1179
                if (r != -ENOKEY)
4✔
1180
                        return r;
1181
        }
1182

1183
        if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO))
81✔
1184
                return ask_password_tty(req, flags, ret);
×
1185

1186
        if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
81✔
1187
                return ask_password_agent(req, flags, ret);
1✔
1188

1189
        return -EUNATCH;
1190
}
1191

1192
int acquire_user_ask_password_directory(char **ret) {
3✔
1193
        int r;
3✔
1194

1195
        r = xdg_user_runtime_dir("systemd/ask-password", ret);
3✔
1196
        if (r == -ENXIO) {
3✔
1197
                if (ret)
3✔
1198
                        *ret = NULL;
3✔
1199
                return 0;
3✔
1200
        }
1201
        if (r < 0)
×
1202
                return r;
×
1203

1204
        return 1;
1205
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc