• 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

84.93
/src/basic/conf-files.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdio.h>
4
#include <stdlib.h>
5

6
#include "alloc-util.h"
7
#include "chase.h"
8
#include "conf-files.h"
9
#include "dirent-util.h"
10
#include "errno-util.h"
11
#include "fd-util.h"
12
#include "fileio.h"
13
#include "glyph-util.h"
14
#include "hashmap.h"
15
#include "log.h"
16
#include "nulstr-util.h"
17
#include "path-util.h"
18
#include "set.h"
19
#include "stat-util.h"
20
#include "string-util.h"
21
#include "strv.h"
22

23
static int files_add(
37,748✔
24
                DIR *dir,
25
                const char *dirpath,
26
                Hashmap **files,
27
                Set **masked,
28
                const char *suffix,
29
                unsigned flags) {
30

31
        int r;
37,748✔
32

33
        assert(dir);
37,748✔
34
        assert(dirpath);
37,748✔
35
        assert(files);
37,748✔
36
        assert(masked);
37,748✔
37

38
        FOREACH_DIRENT(de, dir, return -errno) {
305,275✔
39
                _cleanup_free_ char *n = NULL, *p = NULL;
192,031✔
40
                struct stat st;
192,031✔
41

42
                /* Does this match the suffix? */
43
                if (suffix && !endswith(de->d_name, suffix))
192,031✔
44
                        continue;
89,554✔
45

46
                /* Has this file already been found in an earlier directory? */
47
                if (hashmap_contains(*files, de->d_name)) {
102,477✔
48
                        log_debug("Skipping overridden file '%s/%s'.", dirpath, de->d_name);
125✔
49
                        continue;
125✔
50
                }
51

52
                /* Has this been masked in an earlier directory? */
53
                if ((flags & CONF_FILES_FILTER_MASKED) && set_contains(*masked, de->d_name)) {
102,352✔
54
                        log_debug("File '%s/%s' is masked by previous entry.", dirpath, de->d_name);
15✔
55
                        continue;
15✔
56
                }
57

58
                /* Read file metadata if we shall validate the check for file masks, for node types or whether the node is marked executable. */
59
                if (flags & (CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR|CONF_FILES_DIRECTORY|CONF_FILES_EXECUTABLE))
102,337✔
60
                        if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) {
2,860✔
UNCOV
61
                                log_debug_errno(errno, "Failed to stat '%s/%s', ignoring: %m", dirpath, de->d_name);
×
UNCOV
62
                                continue;
×
63
                        }
64

65
                /* Is this a masking entry? */
66
                if ((flags & CONF_FILES_FILTER_MASKED))
2,860✔
67
                        if (null_or_empty(&st)) {
2,599✔
68
                                /* Mark this one as masked */
69
                                r = set_put_strdup(masked, de->d_name);
25✔
70
                                if (r < 0)
25✔
71
                                        return r;
72

73
                                log_debug("File '%s/%s' is a mask.", dirpath, de->d_name);
25✔
74
                                continue;
25✔
75
                        }
76

77
                /* Does this node have the right type? */
78
                if (flags & (CONF_FILES_REGULAR|CONF_FILES_DIRECTORY))
102,312✔
79
                        if (!((flags & CONF_FILES_DIRECTORY) && S_ISDIR(st.st_mode)) &&
2,760✔
80
                            !((flags & CONF_FILES_REGULAR) && S_ISREG(st.st_mode))) {
2,609✔
UNCOV
81
                                log_debug("Ignoring '%s/%s', as it does not have the right type.", dirpath, de->d_name);
×
UNCOV
82
                                continue;
×
83
                        }
84

85
                /* Does this node have the executable bit set? */
86
                if (flags & CONF_FILES_EXECUTABLE)
102,312✔
87
                        /* As requested: check if the file is marked executable. Note that we don't check access(X_OK)
88
                         * here, as we care about whether the file is marked executable at all, and not whether it is
89
                         * executable for us, because if so, such errors are stuff we should log about. */
90

91
                        if ((st.st_mode & 0111) == 0) { /* not executable */
899✔
UNCOV
92
                                log_debug("Ignoring '%s/%s', as it is not marked executable.", dirpath, de->d_name);
×
UNCOV
93
                                continue;
×
94
                        }
95

96
                n = strdup(de->d_name);
102,312✔
97
                if (!n)
102,312✔
98
                        return -ENOMEM;
99

100
                if ((flags & CONF_FILES_BASENAME))
102,312✔
101
                        r = hashmap_ensure_put(files, &string_hash_ops_free, n, n);
12✔
102
                else {
103
                        p = path_join(dirpath, de->d_name);
102,300✔
104
                        if (!p)
102,300✔
105
                                return -ENOMEM;
106

107
                        r = hashmap_ensure_put(files, &string_hash_ops_free_free, n, p);
102,300✔
108
                }
109
                if (r < 0)
102,312✔
110
                        return r;
111
                assert(r > 0);
102,312✔
112

113
                TAKE_PTR(n);
102,312✔
114
                TAKE_PTR(p);
102,312✔
115
        }
116

117
        return 0;
118
}
119

120
static int copy_and_sort_files_from_hashmap(Hashmap *fh, char ***ret) {
104,957✔
121
        _cleanup_free_ char **sv = NULL;
104,957✔
122
        char **files;
104,957✔
123
        int r;
104,957✔
124

125
        assert(ret);
104,957✔
126

127
        r = hashmap_dump_sorted(fh, (void***) &sv, /* ret_n = */ NULL);
104,957✔
128
        if (r < 0)
104,957✔
129
                return r;
130

131
        /* The entries in the array given by hashmap_dump_sorted() are still owned by the hashmap. */
132
        files = strv_copy(sv);
104,957✔
133
        if (!files)
104,957✔
134
                return -ENOMEM;
135

136
        *ret = files;
104,957✔
137
        return 0;
104,957✔
138
}
139

140
int conf_files_list_strv(
104,949✔
141
                char ***ret,
142
                const char *suffix,
143
                const char *root,
144
                unsigned flags,
145
                const char * const *dirs) {
146

147
        _cleanup_hashmap_free_ Hashmap *fh = NULL;
104,949✔
148
        _cleanup_set_free_ Set *masked = NULL;
104,949✔
149
        int r;
104,949✔
150

151
        assert(ret);
104,949✔
152

153
        STRV_FOREACH(p, dirs) {
719,102✔
154
                _cleanup_closedir_ DIR *dir = NULL;
614,153✔
155
                _cleanup_free_ char *path = NULL;
614,153✔
156

157
                r = chase_and_opendir(*p, root, CHASE_PREFIX_ROOT, &path, &dir);
614,153✔
158
                if (r < 0) {
614,153✔
159
                        if (r != -ENOENT)
576,417✔
UNCOV
160
                                log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p);
×
161
                        continue;
576,417✔
162
                }
163

164
                r = files_add(dir, path, &fh, &masked, suffix, flags);
37,736✔
165
                if (r == -ENOMEM)
37,736✔
UNCOV
166
                        return r;
×
167
                if (r < 0)
37,736✔
168
                        log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path);
37,736✔
169
        }
170

171
        return copy_and_sort_files_from_hashmap(fh, ret);
104,949✔
172
}
173

174
int conf_files_list_strv_at(
8✔
175
                char ***ret,
176
                const char *suffix,
177
                int rfd,
178
                unsigned flags,
179
                const char * const *dirs) {
180

181
        _cleanup_hashmap_free_ Hashmap *fh = NULL;
8✔
182
        _cleanup_set_free_ Set *masked = NULL;
8✔
183
        int r;
8✔
184

185
        assert(rfd >= 0 || rfd == AT_FDCWD);
8✔
186
        assert(ret);
8✔
187

188
        STRV_FOREACH(p, dirs) {
20✔
189
                _cleanup_closedir_ DIR *dir = NULL;
12✔
190
                _cleanup_free_ char *path = NULL;
12✔
191

192
                r = chase_and_opendirat(rfd, *p, CHASE_AT_RESOLVE_IN_ROOT, &path, &dir);
12✔
193
                if (r < 0) {
12✔
UNCOV
194
                        if (r != -ENOENT)
×
UNCOV
195
                                log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p);
×
UNCOV
196
                        continue;
×
197
                }
198

199
                r = files_add(dir, path, &fh, &masked, suffix, flags);
12✔
200
                if (r == -ENOMEM)
12✔
201
                        return r;
×
202
                if (r < 0)
12✔
203
                        log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path);
12✔
204
        }
205

206
        return copy_and_sort_files_from_hashmap(fh, ret);
8✔
207
}
208

209
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path) {
74✔
210
        /* Insert a path into strv, at the place honouring the usual sorting rules:
211
         * - we first compare by the basename
212
         * - and then we compare by dirname, allowing just one file with the given
213
         *   basename.
214
         * This means that we will
215
         * - add a new entry if basename(path) was not on the list,
216
         * - do nothing if an entry with higher priority was already present,
217
         * - do nothing if our new entry matches the existing entry,
218
         * - replace the existing entry if our new entry has higher priority.
219
         */
220
        size_t i, n;
74✔
221
        char *t;
74✔
222
        int r;
74✔
223

224
        n = strv_length(*strv);
74✔
225
        for (i = 0; i < n; i++) {
107✔
226
                int c;
101✔
227

228
                c = path_compare_filename((*strv)[i], path);
101✔
229
                if (c == 0)
101✔
230
                        /* Oh, there already is an entry with a matching name (the last component). */
231
                        STRV_FOREACH(dir, dirs) {
92✔
232
                                _cleanup_free_ char *rdir = NULL;
92✔
233
                                char *p1, *p2;
92✔
234

235
                                rdir = path_join(root, *dir);
92✔
236
                                if (!rdir)
92✔
237
                                        return -ENOMEM;
238

239
                                p1 = path_startswith((*strv)[i], rdir);
92✔
240
                                if (p1)
92✔
241
                                        /* Existing entry with higher priority
242
                                         * or same priority, no need to do anything. */
243
                                        return 0;
244

245
                                p2 = path_startswith(path, *dir);
33✔
246
                                if (p2) {
33✔
247
                                        /* Our new entry has higher priority */
248

249
                                        t = path_join(root, path);
3✔
250
                                        if (!t)
3✔
UNCOV
251
                                                return log_oom();
×
252

253
                                        return free_and_replace((*strv)[i], t);
3✔
254
                                }
255
                        }
256

257
                else if (c > 0)
39✔
258
                        /* Following files have lower priority, let's go insert our
259
                         * new entry. */
260
                        break;
261

262
                /* … we are not there yet, let's continue */
263
        }
264

265
        /* The new file has lower priority than all the existing entries */
266
        t = path_join(root, path);
12✔
267
        if (!t)
12✔
268
                return -ENOMEM;
269

270
        r = strv_insert(strv, i, t);
12✔
271
        if (r < 0)
12✔
UNCOV
272
                free(t);
×
273

274
        return r;
275
}
276

277
int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir) {
4✔
278
        return conf_files_list_strv(ret, suffix, root, flags, STRV_MAKE_CONST(dir));
4✔
279
}
280

281
int conf_files_list_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dir) {
4✔
282
        return conf_files_list_strv_at(ret, suffix, rfd, flags, STRV_MAKE_CONST(dir));
4✔
283
}
284

285
int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs) {
13,297✔
286
        _cleanup_strv_free_ char **d = NULL;
13,297✔
287

288
        assert(ret);
13,297✔
289

290
        d = strv_split_nulstr(dirs);
13,297✔
291
        if (!d)
13,297✔
292
                return -ENOMEM;
293

294
        return conf_files_list_strv(ret, suffix, root, flags, (const char**) d);
13,297✔
295
}
296

UNCOV
297
int conf_files_list_nulstr_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dirs) {
×
UNCOV
298
        _cleanup_strv_free_ char **d = NULL;
×
299

UNCOV
300
        assert(ret);
×
301

302
        d = strv_split_nulstr(dirs);
×
303
        if (!d)
×
304
                return -ENOMEM;
305

UNCOV
306
        return conf_files_list_strv_at(ret, suffix, rfd, flags, (const char**) d);
×
307
}
308

309
int conf_files_list_with_replacement(
633✔
310
                const char *root,
311
                char **config_dirs,
312
                const char *replacement,
313
                char ***ret_files,
314
                char **ret_replace_file) {
315

UNCOV
316
        _cleanup_strv_free_ char **f = NULL;
×
317
        _cleanup_free_ char *p = NULL;
633✔
318
        int r;
633✔
319

320
        assert(config_dirs);
633✔
321
        assert(ret_files);
633✔
322
        assert(ret_replace_file || !replacement);
633✔
323

324
        r = conf_files_list_strv(&f, ".conf", root, 0, (const char* const*) config_dirs);
633✔
325
        if (r < 0)
633✔
UNCOV
326
                return log_error_errno(r, "Failed to enumerate config files: %m");
×
327

328
        if (replacement) {
633✔
329
                r = conf_files_insert(&f, root, config_dirs, replacement);
35✔
330
                if (r < 0)
35✔
331
                        return log_error_errno(r, "Failed to extend config file list: %m");
×
332

333
                p = path_join(root, replacement);
35✔
334
                if (!p)
35✔
UNCOV
335
                        return log_oom();
×
336
        }
337

338
        *ret_files = TAKE_PTR(f);
633✔
339
        if (ret_replace_file)
633✔
340
                *ret_replace_file = TAKE_PTR(p);
633✔
341

342
        return 0;
343
}
344

345
int conf_files_list_dropins(
47,798✔
346
                char ***ret,
347
                const char *dropin_dirname,
348
                const char *root,
349
                const char * const *dirs) {
350

351
        _cleanup_strv_free_ char **dropin_dirs = NULL;
47,798✔
352
        const char *suffix;
47,798✔
353
        int r;
47,798✔
354

355
        assert(ret);
47,798✔
356
        assert(dropin_dirname);
47,798✔
357
        assert(dirs);
47,798✔
358

359
        suffix = strjoina("/", dropin_dirname);
238,990✔
360
        r = strv_extend_strv_concat(&dropin_dirs, dirs, suffix);
47,798✔
361
        if (r < 0)
47,798✔
362
                return r;
363

364
        return conf_files_list_strv(ret, ".conf", root, 0, (const char* const*) dropin_dirs);
47,798✔
365
}
366

367
/**
368
 * Open and read a config file.
369
 *
370
 * The <fn> argument may be:
371
 * - '-', meaning stdin.
372
 * - a file name without a path. In this case <config_dirs> are searched.
373
 * - a path, either relative or absolute. In this case <fn> is opened directly.
374
 *
375
 * This method is only suitable for configuration files which have a flat layout without dropins.
376
 */
377
int conf_file_read(
15,690✔
378
                const char *root,
379
                const char **config_dirs,
380
                const char *fn,
381
                parse_line_t parse_line,
382
                void *userdata,
383
                bool ignore_enoent,
384
                bool *invalid_config) {
385

386
        _cleanup_fclose_ FILE *_f = NULL;
15,690✔
387
        _cleanup_free_ char *_fn = NULL;
15,690✔
388
        unsigned v = 0;
15,690✔
389
        FILE *f;
15,690✔
390
        int r = 0;
15,690✔
391

392
        assert(fn);
15,690✔
393

394
        if (streq(fn, "-")) {
15,690✔
395
                f = stdin;
175✔
396
                fn = "<stdin>";
175✔
397

398
                log_debug("Reading config from stdin%s", glyph(GLYPH_ELLIPSIS));
207✔
399

400
        } else if (is_path(fn)) {
15,515✔
401
                r = path_make_absolute_cwd(fn, &_fn);
15,515✔
402
                if (r < 0)
15,515✔
UNCOV
403
                        return log_error_errno(r, "Failed to make path absolute: %m");
×
404
                fn = _fn;
15,515✔
405

406
                f = _f = fopen(fn, "re");
15,515✔
407
                if (!_f)
15,515✔
408
                        r = -errno;
358✔
409
                else
410
                        log_debug("Reading config file \"%s\"%s", fn, glyph(GLYPH_ELLIPSIS));
30,021✔
411

412
        } else {
UNCOV
413
                r = search_and_fopen(fn, "re", root, config_dirs, &_f, &_fn);
×
UNCOV
414
                if (r >= 0) {
×
UNCOV
415
                        f = _f;
×
UNCOV
416
                        fn = _fn;
×
UNCOV
417
                        log_debug("Reading config file \"%s\"%s", fn, glyph(GLYPH_ELLIPSIS));
×
418
                }
419
        }
420

421
        if (r == -ENOENT && ignore_enoent) {
15,690✔
422
                log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
358✔
423
                return 0; /* No error, but nothing happened. */
358✔
424
        }
425
        if (r < 0)
15,332✔
UNCOV
426
                return log_error_errno(r, "Failed to read '%s': %m", fn);
×
427

428
        r = 1;  /* We entered the part where we may modify state. */
429

430
        for (;;) {
199,347✔
431
                _cleanup_free_ char *line = NULL;
184,015✔
432
                bool invalid_line = false;
199,347✔
433
                int k;
199,347✔
434

435
                k = read_stripped_line(f, LONG_LINE_MAX, &line);
199,347✔
436
                if (k < 0)
199,347✔
UNCOV
437
                        return log_error_errno(k, "Failed to read '%s': %m", fn);
×
438
                if (k == 0)
199,347✔
439
                        break;
440

441
                v++;
184,015✔
442

443
                if (IN_SET(line[0], 0, '#'))
184,015✔
444
                        continue;
118,704✔
445

446
                k = parse_line(fn, v, line, invalid_config ? &invalid_line : NULL, userdata);
65,986✔
447
                if (k < 0 && invalid_line)
65,311✔
448
                        /* Allow reporting with a special code if the caller requested this. */
449
                        *invalid_config = true;
10✔
450
                else
451
                        /* The first error, if any, becomes our return value. */
452
                        RET_GATHER(r, k);
65,301✔
453
        }
454

455
        if (ferror(f))
15,332✔
UNCOV
456
                RET_GATHER(r, log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read from file %s.", fn));
×
457

458
        return r;
459
}
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