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

systemd / systemd / 25947781207

15 May 2026 11:26PM UTC coverage: 72.557% (+0.2%) from 72.364%
25947781207

push

github

web-flow
udev,sd-device: drop old database support (#40801)

Closes #40776.

12 of 18 new or added lines in 2 files covered. (66.67%)

5741 existing lines in 74 files now uncovered.

330189 of 455073 relevant lines covered (72.56%)

1313919.72 hits per line

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

97.4
/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 "initrd-util.h"
9
#include "log.h"
10
#include "parse-util.h"
11
#include "proc-cmdline.h"
12
#include "process-util.h"
13
#include "string-util.h"
14
#include "strv.h"
15
#include "virt.h"
16

17
typedef enum ArgType {
18
        no_argument,
19
        required_argument,
20
        optional_argument,
21
} ArgType;
22

23
int proc_cmdline_filter_pid1_args(char **argv, char ***ret) {
3,685✔
24
        const struct {
3,685✔
25
                const char *name;
26
                ArgType has_arg;
27
        } options[] = {
3,685✔
28
                { "log-level",                required_argument },
29
                { "log-target",               required_argument },
30
                { "log-color",                optional_argument },
31
                { "log-location",             optional_argument },
32
                { "log-time",                 optional_argument },
33
                { "unit",                     required_argument },
34
                { "system",                   no_argument,      },
35
                { "user",                     no_argument,      },
36
                { "test",                     no_argument,      },
37
                { "no-pager",                 no_argument,      },
38
                { "help",                     no_argument,      },
39
                { "version",                  no_argument,      },
40
                { "dump-configuration-items", no_argument,      },
41
                { "dump-bus-properties",      no_argument,      },
42
                { "bus-introspect",           required_argument },
43
                { "dump-core",                optional_argument },
44
                { "crash-chvt",               required_argument },
45
                { "crash-vt",                 required_argument },
46
                { "crash-shell",              optional_argument },
47
                { "crash-reboot",             optional_argument },
48
                { "crash-action",             required_argument },
49
                { "confirm-spawn",            optional_argument },
50
                { "show-status",              optional_argument },
51
                { "deserialize",              required_argument },
52
                { "switched-root",            no_argument,      },
53
                { "default-standard-output",  required_argument },
54
                { "default-standard-error",   required_argument },
55
                { "machine-id",               required_argument },
56
                { "service-watchdogs",        required_argument },
57
                { "exit-code",                required_argument },
58
                { "timeout",                  required_argument },
59
        };
60
        const char *short_options = "hDbsz:";
3,685✔
61

62
        _cleanup_strv_free_ char **filtered = NULL;
3,685✔
63
        int state, r;
3,685✔
64

65
        assert(argv);
3,685✔
66
        assert(ret);
3,685✔
67

68
        /* Currently, we do not support '-', '+', and ':' at the beginning. */
69
        assert(!IN_SET(short_options[0], '-', '+', ':'));
3,685✔
70

71
        /* Filter out all known options. */
72
        state = no_argument;
3,685✔
73
        STRV_FOREACH(p, strv_skip(argv, 1)) {
64,934✔
74
                int prev_state = state;
61,251✔
75
                const char *a = *p;
61,251✔
76

77
                /* Reset the state for the next step. */
78
                state = no_argument;
61,251✔
79

80
                if (prev_state == required_argument ||
61,251✔
UNCOV
81
                    (prev_state == optional_argument && a[0] != '-'))
×
82
                        /* Handled as an argument of the previous option, filtering out the string. */
83
                        continue;
9✔
84

85
                if (a[0] != '-') {
61,242✔
86
                        /* Not an option, accepting the string. */
87
                        r = strv_extend(&filtered, a);
60,456✔
88
                        if (r < 0)
60,456✔
89
                                return r;
90
                        continue;
60,456✔
91
                }
92

93
                if (a[1] == '-') {
786✔
94
                        if (a[2] == '\0') {
773✔
95
                                /* "--" is specified, accepting remaining strings. */
96
                                r = strv_extend_strv(&filtered, strv_skip(p, 1), /* filter_duplicates= */ false);
2✔
97
                                if (r < 0)
2✔
98
                                        return r;
99
                                break;
100
                        }
101

102
                        /* long option, e.g. --foo */
103
                        FOREACH_ELEMENT(option, options) {
11,900✔
104
                                const char *q = startswith(a + 2, option->name);
11,896✔
105
                                if (!q || !IN_SET(q[0], '=', '\0'))
11,896✔
106
                                        continue;
11,129✔
107

108
                                /* Found matching option, updating the state if necessary. */
109
                                if (q[0] == '\0' && option->has_arg == required_argument)
767✔
110
                                        state = required_argument;
5✔
111

112
                                break;
113
                        }
114
                        continue;
771✔
115
                }
116

117
                /* short option(s), e.g. -x or -xyz */
118
                while (a && *++a != '\0')
29✔
119
                        for (const char *q = short_options; *q != '\0'; q++) {
100✔
120
                                if (*q != *a)
92✔
121
                                        continue;
79✔
122

123
                                /* Found matching short option. */
124

125
                                if (q[1] == ':') {
13✔
126
                                        /* An argument is required or optional, and remaining part
127
                                         * is handled as argument if exists. */
128
                                        state = a[1] != '\0' ? no_argument :
9✔
129
                                                q[2] == ':' ? optional_argument : required_argument;
4✔
130

131
                                        a = NULL; /* Not necessary to parse remaining part. */
132
                                }
133
                                break;
134
                        }
135
        }
136

137
        *ret = TAKE_PTR(filtered);
3,685✔
138
        return 0;
3,685✔
139
}
140

141
int proc_cmdline(char **ret) {
2✔
142
        assert(ret);
2✔
143

144
        /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
145
        const char *e = secure_getenv("SYSTEMD_PROC_CMDLINE");
2✔
146
        if (e)
2✔
147
                return strdup_to(ret, e);
2✔
148

UNCOV
149
        if (detect_container() > 0)
×
UNCOV
150
                return pid_get_cmdline(1, SIZE_MAX, 0, ret);
×
151

UNCOV
152
        return read_full_file("/proc/cmdline", ret, /* ret_size= */  NULL);
×
153
}
154

155
static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) {
142,997✔
156
        int r;
142,997✔
157

158
        assert(ret);
142,997✔
159

160
        /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
161
        const char *e = secure_getenv("SYSTEMD_PROC_CMDLINE");
142,997✔
162
        if (e)
142,997✔
163
                return strv_split_full(ret, e, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
172✔
164

165
        if (detect_container() > 0) {
142,825✔
166
                _cleanup_strv_free_ char **args = NULL;
5,112✔
167

168
                r = pid_get_cmdline_strv(1, /* flags= */ 0, &args);
5,112✔
169
                if (r < 0)
5,112✔
170
                        return r;
171

172
                if (filter_pid1_args)
5,037✔
173
                        return proc_cmdline_filter_pid1_args(args, ret);
3,680✔
174

175
                *ret = TAKE_PTR(args);
1,357✔
176
                return 0;
1,357✔
177

178
        } else {
179
                _cleanup_free_ char *s = NULL;
137,713✔
180

181
                r = read_full_file("/proc/cmdline", &s, NULL);
137,713✔
182
                if (r < 0)
137,713✔
183
                        return r;
184

185
                return strv_split_full(ret, s, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
137,713✔
186
        }
187
}
188

189
int proc_cmdline_strv(char ***ret) {
1,986✔
190
        return proc_cmdline_strv_internal(ret, /* filter_pid1_args= */ false);
1,986✔
191
}
192

193
static char *mangle_word(const char *word, ProcCmdlineFlags flags) {
6,454,328✔
194
        char *c = (char*) startswith(word, "rd.");
6,454,328✔
195
        if (c) {
6,454,328✔
196
                /* Filter out arguments that are intended only for the initrd */
197

198
                if (!in_initrd())
1,433✔
199
                        return NULL;
200

201
                if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX))
209✔
202
                        return c;
154✔
203

204
        } else if (FLAGS_SET(flags, PROC_CMDLINE_RD_STRICT) && in_initrd())
6,452,895✔
205
                /* And optionally filter out arguments that are intended only for the host */
206
                return NULL;
81✔
207

208
        return (char*) word;
209
}
210

211
#define mangle_word(word, flags) const_generic(word, mangle_word(word, flags))
212

213
static int proc_cmdline_parse_strv(char **args, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
9,102✔
214
        int r;
9,102✔
215

216
        assert(parse_item);
9,102✔
217

218
        STRV_FOREACH(word, args) {
314,932✔
219
                char *key, *value;
305,848✔
220

221
                key = mangle_word(*word, flags);
305,848✔
222
                if (!key)
305,848✔
223
                        continue;
572✔
224

225
                value = strchr(key, '=');
305,276✔
226
                if (value)
305,276✔
227
                        *(value++) = '\0';
280,417✔
228

229
                r = parse_item(key, value, data);
305,276✔
230
                if (r < 0)
305,276✔
231
                        return r;
232
        }
233

234
        return 0;
235
}
236

237
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
9,177✔
238
        _cleanup_strv_free_ char **args = NULL;
9,177✔
239
        int r;
9,177✔
240

241
        assert(parse_item);
9,177✔
242

243
        /* The PROC_CMDLINE_VALUE_OPTIONAL and PROC_CMDLINE_TRUE_WHEN_MISSING flags don't really make sense
244
         * for proc_cmdline_parse(), let's make this clear. */
245
        assert(!(flags & (PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_TRUE_WHEN_MISSING)));
9,177✔
246

247
        r = proc_cmdline_strv_internal(&args, /* filter_pid1_args= */ true);
9,177✔
248
        if (r < 0)
9,177✔
249
                return r;
250

251
        return proc_cmdline_parse_strv(args, parse_item, data, flags);
9,102✔
252
}
253

254
static bool relaxed_equal_char(char a, char b) {
17,659,547✔
255
        return a == b ||
25,865,354✔
256
                (a == '_' && b == '-') ||
17,659,547✔
257
                (a == '-' && b == '_');
8,205,799✔
258
}
259

260
char* proc_cmdline_key_startswith(const char *s, const char *prefix) {
6,147,728✔
261
        assert(s);
6,147,728✔
262
        assert(prefix);
6,147,728✔
263

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

266
        for (; *prefix != 0; s++, prefix++)
6,635,415✔
267
                if (!relaxed_equal_char(*s, *prefix))
6,634,868✔
268
                        return NULL;
269

270
        return (char*) s;
271
}
272

273
bool proc_cmdline_key_streq(const char *x, const char *y) {
2,076,583✔
274
        assert(x);
2,076,583✔
275
        assert(y);
2,076,583✔
276

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

279
        for (; *x != 0 || *y != 0; x++, y++)
11,042,663✔
280
                if (!relaxed_equal_char(*x, *y))
11,024,679✔
281
                        return false;
282

283
        return true;
284
}
285

286
static int cmdline_get_key(char **args, const char *key, ProcCmdlineFlags flags, char **ret_value) {
131,834✔
287
        _cleanup_free_ char *v = NULL;
131,834✔
288
        bool found = false;
131,834✔
289
        int r;
131,834✔
290

291
        assert(key);
131,834✔
292

293
        STRV_FOREACH(p, args) {
5,926,062✔
294
                const char *word;
5,794,230✔
295

296
                word = mangle_word(*p, flags);
5,794,230✔
297
                if (!word)
5,794,230✔
298
                        continue;
669✔
299

300
                if (ret_value) {
5,793,561✔
301
                        const char *e;
5,793,531✔
302

303
                        e = proc_cmdline_key_startswith(word, key);
5,793,531✔
304
                        if (!e)
5,793,531✔
305
                                continue;
5,793,316✔
306

307
                        if (*e == '=') {
215✔
308
                                r = free_and_strdup(&v, e+1);
209✔
309
                                if (r < 0)
209✔
310
                                        return r;
311

312
                                found = true;
313

314
                        } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
6✔
315
                                found = true;
4✔
316

317
                } else {
318
                        if (proc_cmdline_key_streq(word, key)) {
30✔
319
                                found = true;
320
                                break; /* we found what we were looking for */
321
                        }
322
                }
323
        }
324

325
        if (ret_value)
131,834✔
326
                *ret_value = TAKE_PTR(v);
131,828✔
327

328
        return found;
131,834✔
329
}
330

331
int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
131,838✔
UNCOV
332
        _cleanup_strv_free_ char **args = NULL;
×
333
        int r;
131,838✔
334

335
        /* Looks for a specific key on the kernel command line. Supports three modes:
336
         *
337
         * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
338
         *    "=" is searched for, and the value following it is returned in "ret_value".
339
         *
340
         * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
341
         *    word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
342
         *    also accepted, and "value" is returned as NULL.
343
         *
344
         * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
345
         *
346
         * In all three cases, > 0 is returned if the key is found, 0 if not. */
347

348
        /* PROC_CMDLINE_TRUE_WHEN_MISSING doesn't really make sense for proc_cmdline_get_key(). */
349
        assert(!FLAGS_SET(flags, PROC_CMDLINE_TRUE_WHEN_MISSING));
131,838✔
350

351
        if (isempty(key))
263,676✔
352
                return -EINVAL;
353

354
        if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
131,836✔
355
                return -EINVAL;
356

357
        r = proc_cmdline_strv_internal(&args, /* filter_pid1_args= */ true);
131,834✔
358
        if (r < 0)
131,834✔
359
                return r;
360

361
        return cmdline_get_key(args, key, flags, ret_value);
131,834✔
362
}
363

364
int proc_cmdline_get_bool(const char *key, ProcCmdlineFlags flags, bool *ret) {
2,042✔
365
        _cleanup_free_ char *v = NULL;
2,042✔
366
        int r;
2,042✔
367

368
        assert(ret);
2,042✔
369

370
        r = proc_cmdline_get_key(key, (flags & ~PROC_CMDLINE_TRUE_WHEN_MISSING) | PROC_CMDLINE_VALUE_OPTIONAL, &v);
2,042✔
371
        if (r < 0)
2,042✔
372
                return r;
373
        if (r == 0) { /* key not specified at all */
2,041✔
374
                *ret = FLAGS_SET(flags, PROC_CMDLINE_TRUE_WHEN_MISSING);
1,919✔
375
                return 0;
1,919✔
376
        }
377

378
        if (v) { /* key with parameter passed */
122✔
379
                r = parse_boolean(v);
120✔
380
                if (r < 0)
120✔
381
                        return r;
382
                *ret = r;
119✔
383
        } else /* key without parameter passed */
384
                *ret = true;
2✔
385

386
        return 1;
387
}
388

389
static int cmdline_get_key_ap(ProcCmdlineFlags flags, char* const* args, va_list ap) {
1,654✔
390
        int r, ret = 0;
1,654✔
391

392
        for (;;) {
17,885✔
393
                char **v;
17,885✔
394
                const char *k, *e;
17,885✔
395

396
                k = va_arg(ap, const char*);
17,885✔
397
                if (!k)
17,885✔
398
                        break;
399

400
                assert_se(v = va_arg(ap, char**));
16,231✔
401

402
                STRV_FOREACH(p, args) {
370,481✔
403
                        const char *word;
354,250✔
404

405
                        word = mangle_word(*p, flags);
354,250✔
406
                        if (!word)
354,250✔
407
                                continue;
64✔
408

409
                        e = proc_cmdline_key_startswith(word, k);
354,186✔
410
                        if (e && *e == '=') {
354,186✔
411
                                r = free_and_strdup(v, e + 1);
323✔
412
                                if (r < 0)
323✔
413
                                        return r;
414

415
                                ret++;
323✔
416
                        }
417
                }
418
        }
419

420
        return ret;
421
}
422

423
int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
1,654✔
424
        _cleanup_strv_free_ char **args = NULL;
1,654✔
425
        int r;
1,654✔
426
        va_list ap;
1,654✔
427

428
        /* The PROC_CMDLINE_VALUE_OPTIONAL and PROC_CMDLINE_TRUE_WHEN_MISSING flags don't really make sense
429
         * for proc_cmdline_get_key_many, let's make this clear. */
430
        assert(!(flags & (PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_TRUE_WHEN_MISSING)));
1,654✔
431

432
        /* This call may clobber arguments on failure! */
433

434
        r = proc_cmdline_strv(&args);
1,654✔
435
        if (r < 0)
1,654✔
436
                return r;
437

438
        va_start(ap, flags);
1,654✔
439
        r = cmdline_get_key_ap(flags, args, ap);
1,654✔
440
        va_end(ap);
1,654✔
441

442
        return r;
1,654✔
443
}
444

445
bool proc_cmdline_value_missing(const char *key, const char *value) {
9,660✔
446
        assert(key);
9,660✔
447

448
        if (!value) {
9,660✔
449
                log_warning("Missing argument for %s= kernel command line switch, ignoring.", key);
3✔
450
                return true;
451
        }
452

453
        return false;
454
}
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