• 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

97.45
/src/basic/proc-cmdline.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdlib.h>
4

5
#include "alloc-util.h"
6
#include "extract-word.h"
7
#include "fileio.h"
8
#include "getopt-defs.h"
9
#include "initrd-util.h"
10
#include "log.h"
11
#include "parse-util.h"
12
#include "proc-cmdline.h"
13
#include "process-util.h"
14
#include "string-util.h"
15
#include "strv.h"
16
#include "virt.h"
17

18
int proc_cmdline_filter_pid1_args(char **argv, char ***ret) {
3,312✔
19
        enum {
3,312✔
20
                COMMON_GETOPT_ARGS,
21
                SYSTEMD_GETOPT_ARGS,
22
                SHUTDOWN_GETOPT_ARGS,
23
        };
24
        static const struct option options[] = {
3,312✔
25
                COMMON_GETOPT_OPTIONS,
26
                SYSTEMD_GETOPT_OPTIONS,
27
                SHUTDOWN_GETOPT_OPTIONS,
28
        };
29
        static const char *short_options = SYSTEMD_GETOPT_SHORT_OPTIONS;
3,312✔
30

31
        _cleanup_strv_free_ char **filtered = NULL;
3,312✔
32
        int state, r;
3,312✔
33

34
        assert(argv);
3,312✔
35
        assert(ret);
3,312✔
36

37
        /* Currently, we do not support '-', '+', and ':' at the beginning. */
38
        assert(!IN_SET(short_options[0], '-', '+', ':'));
3,312✔
39

40
        /* Filter out all known options. */
41
        state = no_argument;
3,312✔
42
        STRV_FOREACH(p, strv_skip(argv, 1)) {
81,130✔
43
                int prev_state = state;
77,820✔
44
                const char *a = *p;
77,820✔
45

46
                /* Reset the state for the next step. */
47
                state = no_argument;
77,820✔
48

49
                if (prev_state == required_argument ||
77,820✔
UNCOV
50
                    (prev_state == optional_argument && a[0] != '-'))
×
51
                        /* Handled as an argument of the previous option, filtering out the string. */
52
                        continue;
9✔
53

54
                if (a[0] != '-') {
77,811✔
55
                        /* Not an option, accepting the string. */
56
                        r = strv_extend(&filtered, a);
77,433✔
57
                        if (r < 0)
77,433✔
58
                                return r;
59
                        continue;
77,433✔
60
                }
61

62
                if (a[1] == '-') {
378✔
63
                        if (a[2] == '\0') {
365✔
64
                                /* "--" is specified, accepting remaining strings. */
65
                                r = strv_extend_strv(&filtered, strv_skip(p, 1), /* filter_duplicates = */ false);
2✔
66
                                if (r < 0)
2✔
67
                                        return r;
68
                                break;
69
                        }
70

71
                        /* long option, e.g. --foo */
72
                        FOREACH_ELEMENT(option, options) {
5,397✔
73
                                const char *q = startswith(a + 2, option->name);
5,393✔
74
                                if (!q || !IN_SET(q[0], '=', '\0'))
5,393✔
75
                                        continue;
5,034✔
76

77
                                /* Found matching option, updating the state if necessary. */
78
                                if (q[0] == '\0' && option->has_arg == required_argument)
359✔
79
                                        state = required_argument;
5✔
80

81
                                break;
82
                        }
83
                        continue;
363✔
84
                }
85

86
                /* short option(s), e.g. -x or -xyz */
87
                while (a && *++a != '\0')
29✔
88
                        for (const char *q = short_options; *q != '\0'; q++) {
100✔
89
                                if (*q != *a)
92✔
90
                                        continue;
79✔
91

92
                                /* Found matching short option. */
93

94
                                if (q[1] == ':') {
13✔
95
                                        /* An argument is required or optional, and remaining part
96
                                         * is handled as argument if exists. */
97
                                        state = a[1] != '\0' ? no_argument :
9✔
98
                                                q[2] == ':' ? optional_argument : required_argument;
4✔
99

100
                                        a = NULL; /* Not necessary to parse remaining part. */
101
                                }
102
                                break;
103
                        }
104
        }
105

106
        *ret = TAKE_PTR(filtered);
3,312✔
107
        return 0;
3,312✔
108
}
109

110
int proc_cmdline(char **ret) {
2✔
111
        const char *e;
2✔
112

113
        assert(ret);
2✔
114

115
        /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
116
        e = secure_getenv("SYSTEMD_PROC_CMDLINE");
2✔
117
        if (e)
2✔
118
                return strdup_to(ret, e);
2✔
119

UNCOV
120
        if (detect_container() > 0)
×
UNCOV
121
                return pid_get_cmdline(1, SIZE_MAX, 0, ret);
×
122

123
        return read_full_file("/proc/cmdline", ret, /* ret_size= */  NULL);
×
124
}
125

126
static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) {
137,153✔
127
        const char *e;
137,153✔
128
        int r;
137,153✔
129

130
        assert(ret);
137,153✔
131

132
        /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
133
        e = secure_getenv("SYSTEMD_PROC_CMDLINE");
137,153✔
134
        if (e)
137,153✔
135
                return strv_split_full(ret, e, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
170✔
136

137
        if (detect_container() > 0) {
136,983✔
138
                _cleanup_strv_free_ char **args = NULL;
4,773✔
139

140
                r = pid_get_cmdline_strv(1, /* flags = */ 0, &args);
4,773✔
141
                if (r < 0)
4,773✔
142
                        return r;
143

144
                if (filter_pid1_args)
4,701✔
145
                        return proc_cmdline_filter_pid1_args(args, ret);
3,307✔
146

147
                *ret = TAKE_PTR(args);
1,394✔
148
                return 0;
1,394✔
149

150
        } else {
151
                _cleanup_free_ char *s = NULL;
132,210✔
152

153
                r = read_full_file("/proc/cmdline", &s, NULL);
132,210✔
154
                if (r < 0)
132,210✔
155
                        return r;
156

157
                return strv_split_full(ret, s, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
132,210✔
158
        }
159
}
160

161
int proc_cmdline_strv(char ***ret) {
2,125✔
162
        return proc_cmdline_strv_internal(ret, /* filter_pid1_args = */ false);
2,125✔
163
}
164

165
static char *mangle_word(const char *word, ProcCmdlineFlags flags) {
6,272,074✔
166
        char *c;
6,272,074✔
167

168
        c = startswith(word, "rd.");
6,272,074✔
169
        if (c) {
6,272,074✔
170
                /* Filter out arguments that are intended only for the initrd */
171

172
                if (!in_initrd())
1,462✔
173
                        return NULL;
174

175
                if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX))
321✔
176
                        return c;
241✔
177

178
        } else if (FLAGS_SET(flags, PROC_CMDLINE_RD_STRICT) && in_initrd())
6,270,612✔
179
                /* And optionally filter out arguments that are intended only for the host */
180
                return NULL;
81✔
181

182
        return (char*) word;
183
}
184

185
static int proc_cmdline_parse_strv(char **args, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
7,788✔
186
        int r;
7,788✔
187

188
        assert(parse_item);
7,788✔
189

190
        STRV_FOREACH(word, args) {
283,275✔
191
                char *key, *value;
275,505✔
192

193
                key = mangle_word(*word, flags);
275,505✔
194
                if (!key)
275,505✔
195
                        continue;
540✔
196

197
                value = strchr(key, '=');
274,965✔
198
                if (value)
274,965✔
199
                        *(value++) = '\0';
251,933✔
200

201
                r = parse_item(key, value, data);
274,965✔
202
                if (r < 0)
274,965✔
203
                        return r;
204
        }
205

206
        return 0;
207
}
208

209
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
7,860✔
210
        _cleanup_strv_free_ char **args = NULL;
7,860✔
211
        int r;
7,860✔
212

213
        assert(parse_item);
7,860✔
214

215
        /* The PROC_CMDLINE_VALUE_OPTIONAL and PROC_CMDLINE_TRUE_WHEN_MISSING flags don't really make sense
216
         * for proc_cmdline_parse(), let's make this clear. */
217
        assert(!(flags & (PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_TRUE_WHEN_MISSING)));
7,860✔
218

219
        r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true);
7,860✔
220
        if (r < 0)
7,860✔
221
                return r;
222

223
        return proc_cmdline_parse_strv(args, parse_item, data, flags);
7,788✔
224
}
225

226
static bool relaxed_equal_char(char a, char b) {
16,834,960✔
227
        return a == b ||
24,652,845✔
228
                (a == '_' && b == '-') ||
16,834,960✔
229
                (a == '-' && b == '_');
7,817,877✔
230
}
231

232
char* proc_cmdline_key_startswith(const char *s, const char *prefix) {
5,995,868✔
233
        assert(s);
5,995,868✔
234
        assert(prefix);
5,995,868✔
235

236
        /* Much like startswith(), but considers "-" and "_" the same */
237

238
        for (; *prefix != 0; s++, prefix++)
6,619,593✔
239
                if (!relaxed_equal_char(*s, *prefix))
6,617,600✔
240
                        return NULL;
241

242
        return (char*) s;
243
}
244

245
bool proc_cmdline_key_streq(const char *x, const char *y) {
1,838,979✔
246
        assert(x);
1,838,979✔
247
        assert(y);
1,838,979✔
248

249
        /* Much like streq(), but considers "-" and "_" the same */
250

251
        for (; *x != 0 || *y != 0; x++, y++)
10,232,356✔
252
                if (!relaxed_equal_char(*x, *y))
10,217,360✔
253
                        return false;
254

255
        return true;
256
}
257

258
static int cmdline_get_key(char **args, const char *key, ProcCmdlineFlags flags, char **ret_value) {
127,168✔
259
        _cleanup_free_ char *v = NULL;
127,168✔
260
        bool found = false;
127,168✔
261
        int r;
127,168✔
262

263
        assert(key);
127,168✔
264

265
        STRV_FOREACH(p, args) {
5,716,421✔
266
                const char *word;
5,589,255✔
267

268
                word = mangle_word(*p, flags);
5,589,255✔
269
                if (!word)
5,589,255✔
270
                        continue;
562✔
271

272
                if (ret_value) {
5,588,693✔
273
                        const char *e;
5,588,663✔
274

275
                        e = proc_cmdline_key_startswith(word, key);
5,588,663✔
276
                        if (!e)
5,588,663✔
277
                                continue;
5,588,478✔
278

279
                        if (*e == '=') {
185✔
280
                                r = free_and_strdup(&v, e+1);
179✔
281
                                if (r < 0)
179✔
282
                                        return r;
283

284
                                found = true;
285

286
                        } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
6✔
287
                                found = true;
4✔
288

289
                } else {
290
                        if (proc_cmdline_key_streq(word, key)) {
30✔
291
                                found = true;
292
                                break; /* we found what we were looking for */
293
                        }
294
                }
295
        }
296

297
        if (ret_value)
127,168✔
298
                *ret_value = TAKE_PTR(v);
127,162✔
299

300
        return found;
127,168✔
301
}
302

303
int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
127,172✔
UNCOV
304
        _cleanup_strv_free_ char **args = NULL;
×
305
        int r;
127,172✔
306

307
        /* Looks for a specific key on the kernel command line. Supports three modes:
308
         *
309
         * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
310
         *    "=" is searched for, and the value following it is returned in "ret_value".
311
         *
312
         * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
313
         *    word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
314
         *    also accepted, and "value" is returned as NULL.
315
         *
316
         * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
317
         *
318
         * In all three cases, > 0 is returned if the key is found, 0 if not. */
319

320
        /* PROC_CMDLINE_TRUE_WHEN_MISSING doesn't really make sense for proc_cmdline_get_key(). */
321
        assert(!FLAGS_SET(flags, PROC_CMDLINE_TRUE_WHEN_MISSING));
127,172✔
322

323
        if (isempty(key))
254,344✔
324
                return -EINVAL;
325

326
        if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
127,170✔
327
                return -EINVAL;
328

329
        r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true);
127,168✔
330
        if (r < 0)
127,168✔
331
                return r;
332

333
        return cmdline_get_key(args, key, flags, ret_value);
127,168✔
334
}
335

336
int proc_cmdline_get_bool(const char *key, ProcCmdlineFlags flags, bool *ret) {
1,129✔
337
        _cleanup_free_ char *v = NULL;
1,129✔
338
        int r;
1,129✔
339

340
        assert(ret);
1,129✔
341

342
        r = proc_cmdline_get_key(key, (flags & ~PROC_CMDLINE_TRUE_WHEN_MISSING) | PROC_CMDLINE_VALUE_OPTIONAL, &v);
1,129✔
343
        if (r < 0)
1,129✔
344
                return r;
345
        if (r == 0) { /* key not specified at all */
1,128✔
346
                *ret = FLAGS_SET(flags, PROC_CMDLINE_TRUE_WHEN_MISSING);
1,015✔
347
                return 0;
1,015✔
348
        }
349

350
        if (v) { /* key with parameter passed */
113✔
351
                r = parse_boolean(v);
111✔
352
                if (r < 0)
111✔
353
                        return r;
354
                *ret = r;
110✔
355
        } else /* key without parameter passed */
356
                *ret = true;
2✔
357

358
        return 1;
359
}
360

361
static int cmdline_get_key_ap(ProcCmdlineFlags flags, char* const* args, va_list ap) {
1,971✔
362
        int r, ret = 0;
1,971✔
363

364
        for (;;) {
16,454✔
365
                char **v;
16,454✔
366
                const char *k, *e;
16,454✔
367

368
                k = va_arg(ap, const char*);
16,454✔
369
                if (!k)
16,454✔
370
                        break;
371

372
                assert_se(v = va_arg(ap, char**));
14,483✔
373

374
                STRV_FOREACH(p, args) {
421,797✔
375
                        const char *word;
407,314✔
376

377
                        word = mangle_word(*p, flags);
407,314✔
378
                        if (!word)
407,314✔
379
                                continue;
120✔
380

381
                        e = proc_cmdline_key_startswith(word, k);
407,194✔
382
                        if (e && *e == '=') {
407,194✔
383
                                r = free_and_strdup(v, e + 1);
1,799✔
384
                                if (r < 0)
1,799✔
385
                                        return r;
386

387
                                ret++;
1,799✔
388
                        }
389
                }
390
        }
391

392
        return ret;
393
}
394

395
int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
1,971✔
396
        _cleanup_strv_free_ char **args = NULL;
1,971✔
397
        int r;
1,971✔
398
        va_list ap;
1,971✔
399

400
        /* The PROC_CMDLINE_VALUE_OPTIONAL and PROC_CMDLINE_TRUE_WHEN_MISSING flags don't really make sense
401
         * for proc_cmdline_get_key_many, let's make this clear. */
402
        assert(!(flags & (PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_TRUE_WHEN_MISSING)));
1,971✔
403

404
        /* This call may clobber arguments on failure! */
405

406
        r = proc_cmdline_strv(&args);
1,971✔
407
        if (r < 0)
1,971✔
408
                return r;
409

410
        va_start(ap, flags);
1,971✔
411
        r = cmdline_get_key_ap(flags, args, ap);
1,971✔
412
        va_end(ap);
1,971✔
413

414
        return r;
1,971✔
415
}
416

417
bool proc_cmdline_value_missing(const char *key, const char *value) {
7,975✔
418
        assert(key);
7,975✔
419

420
        if (!value) {
7,975✔
421
                log_warning("Missing argument for %s= kernel command line switch, ignoring.", key);
3✔
422
                return true;
3✔
423
        }
424

425
        return false;
426
}
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