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

systemd / systemd / 13555867618

26 Feb 2025 11:41PM UTC coverage: 71.82% (+0.002%) from 71.818%
13555867618

push

github

bluca
core/service: do not propagate reload for combined RELOADING=1 + READY=1 when notify-reload

Follow-up for 3bd28bf72

SERVICE_RELOAD_SIGNAL state can only be reached via explicit reload jobs,
and we have a clear distinction between that and plain RELOADING=1
notifications, the latter of which is issued by clients doing reload
outside of our job engine. I.e. upon SERVICE_RELOAD_SIGNAL + RELOADING=1
we don't propagate reload jobs again, since that's done during transaction
construction stage already. The handling of combined RELOADING=1 + READY=1
so far is bogus however, as it tries to propagate duplicate reload jobs.
Amend this by following the logic for standalone RELOADING=1.

1 of 10 new or added lines in 1 file covered. (10.0%)

1279 existing lines in 56 files now uncovered.

294184 of 409611 relevant lines covered (71.82%)

717228.6 hits per line

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

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

3
#include <stdbool.h>
4
#include <stddef.h>
5

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

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

33
        _cleanup_strv_free_ char **filtered = NULL;
3,480✔
34
        int state, r;
3,480✔
35

36
        assert(argv);
3,480✔
37
        assert(ret);
3,480✔
38

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

42
        /* Filter out all known options. */
43
        state = no_argument;
3,480✔
44
        STRV_FOREACH(p, strv_skip(argv, 1)) {
85,171✔
45
                int prev_state = state;
81,693✔
46
                const char *a = *p;
81,693✔
47

48
                /* Reset the state for the next step. */
49
                state = no_argument;
81,693✔
50

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

56
                if (a[0] != '-') {
81,684✔
57
                        /* Not an option, accepting the string. */
58
                        r = strv_extend(&filtered, a);
81,300✔
59
                        if (r < 0)
81,300✔
60
                                return r;
61
                        continue;
81,300✔
62
                }
63

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

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

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

83
                                break;
84
                        }
85
                        continue;
369✔
86
                }
87

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

94
                                /* Found matching short option. */
95

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

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

108
        *ret = TAKE_PTR(filtered);
3,480✔
109
        return 0;
3,480✔
110
}
111

112
int proc_cmdline(char **ret) {
10✔
113
        const char *e;
10✔
114

115
        assert(ret);
10✔
116

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

122
        if (detect_container() > 0)
×
123
                return pid_get_cmdline(1, SIZE_MAX, 0, ret);
×
124

125
        return read_virtual_file("/proc/cmdline", SIZE_MAX, ret, NULL);
×
126
}
127

128
static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) {
131,666✔
129
        const char *e;
131,666✔
130
        int r;
131,666✔
131

132
        assert(ret);
131,666✔
133

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

139
        if (detect_container() > 0) {
131,496✔
140
                _cleanup_strv_free_ char **args = NULL;
4,840✔
141

142
                r = pid_get_cmdline_strv(1, /* flags = */ 0, &args);
4,840✔
143
                if (r < 0)
4,840✔
144
                        return r;
145

146
                if (filter_pid1_args)
4,770✔
147
                        return proc_cmdline_filter_pid1_args(args, ret);
3,475✔
148

149
                *ret = TAKE_PTR(args);
1,295✔
150
                return 0;
1,295✔
151

152
        } else {
153
                _cleanup_free_ char *s = NULL;
126,656✔
154

155
                r = read_full_file("/proc/cmdline", &s, NULL);
126,656✔
156
                if (r < 0)
126,656✔
157
                        return r;
158

159
                return strv_split_full(ret, s, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
126,656✔
160
        }
161
}
162

163
int proc_cmdline_strv(char ***ret) {
2,564✔
164
        return proc_cmdline_strv_internal(ret, /* filter_pid1_args = */ false);
2,564✔
165
}
166

167
static char *mangle_word(const char *word, ProcCmdlineFlags flags) {
6,047,489✔
168
        char *c;
6,047,489✔
169

170
        c = startswith(word, "rd.");
6,047,489✔
171
        if (c) {
6,047,489✔
172
                /* Filter out arguments that are intended only for the initrd */
173

174
                if (!in_initrd())
1,405✔
175
                        return NULL;
176

177
                if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX))
311✔
178
                        return c;
236✔
179

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

184
        return (char*) word;
185
}
186

187
static int proc_cmdline_parse_strv(char **args, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
9,236✔
188
        int r;
9,236✔
189

190
        assert(parse_item);
9,236✔
191

192
        STRV_FOREACH(word, args) {
342,324✔
193
                char *key, *value;
333,106✔
194

195
                key = mangle_word(*word, flags);
333,106✔
196
                if (!key)
333,106✔
197
                        continue;
528✔
198

199
                value = strchr(key, '=');
332,578✔
200
                if (value)
332,578✔
201
                        *(value++) = '\0';
305,202✔
202

203
                r = parse_item(key, value, data);
332,578✔
204
                if (r < 0)
332,578✔
205
                        return r;
206
        }
207

208
        return 0;
209
}
210

211
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
9,306✔
212
        _cleanup_strv_free_ char **args = NULL;
9,306✔
213
        int r;
9,306✔
214

215
        assert(parse_item);
9,306✔
216

217
        /* The PROC_CMDLINE_VALUE_OPTIONAL and PROC_CMDLINE_TRUE_WHEN_MISSING flags don't really make sense
218
         * for proc_cmdline_parse(), let's make this clear. */
219
        assert(!(flags & (PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_TRUE_WHEN_MISSING)));
9,306✔
220

221
        r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true);
9,306✔
222
        if (r < 0)
9,306✔
223
                return r;
224

225
        return proc_cmdline_parse_strv(args, parse_item, data, flags);
9,236✔
226
}
227

228
static bool relaxed_equal_char(char a, char b) {
18,570,789✔
229
        return a == b ||
26,479,643✔
230
                (a == '_' && b == '-') ||
18,570,789✔
231
                (a == '-' && b == '_');
7,908,846✔
232
}
233

234
char* proc_cmdline_key_startswith(const char *s, const char *prefix) {
5,713,717✔
235
        assert(s);
5,713,717✔
236
        assert(prefix);
5,713,717✔
237

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

240
        for (; *prefix != 0; s++, prefix++)
6,399,129✔
241
                if (!relaxed_equal_char(*s, *prefix))
6,396,915✔
242
                        return NULL;
243

244
        return (char*) s;
245
}
246

247
bool proc_cmdline_key_streq(const char *x, const char *y) {
2,215,032✔
248
        assert(x);
2,215,032✔
249
        assert(y);
2,215,032✔
250

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

253
        for (; *x != 0 || *y != 0; x++, y++)
12,191,582✔
254
                if (!relaxed_equal_char(*x, *y))
12,173,874✔
255
                        return false;
256

257
        return true;
258
}
259

260
static int cmdline_get_key(char **args, const char *key, ProcCmdlineFlags flags, char **ret_value) {
119,796✔
261
        _cleanup_free_ char *v = NULL;
119,796✔
262
        bool found = false;
119,796✔
263
        int r;
119,796✔
264

265
        assert(key);
119,796✔
266

267
        STRV_FOREACH(p, args) {
5,376,407✔
268
                const char *word;
5,256,613✔
269

270
                word = mangle_word(*p, flags);
5,256,613✔
271
                if (!word)
5,256,613✔
272
                        continue;
523✔
273

274
                if (ret_value) {
5,256,090✔
275
                        const char *e;
5,256,060✔
276

277
                        e = proc_cmdline_key_startswith(word, key);
5,256,060✔
278
                        if (!e)
5,256,060✔
279
                                continue;
5,255,772✔
280

281
                        if (*e == '=') {
288✔
282
                                r = free_and_strdup(&v, e+1);
282✔
283
                                if (r < 0)
282✔
284
                                        return r;
285

286
                                found = true;
287

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

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

299
        if (ret_value)
119,796✔
300
                *ret_value = TAKE_PTR(v);
119,790✔
301

302
        return found;
119,796✔
303
}
304

305
int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
119,800✔
UNCOV
306
        _cleanup_strv_free_ char **args = NULL;
×
307
        int r;
119,800✔
308

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

322
        /* PROC_CMDLINE_TRUE_WHEN_MISSING doesn't really make sense for proc_cmdline_get_key(). */
323
        assert(!FLAGS_SET(flags, PROC_CMDLINE_TRUE_WHEN_MISSING));
119,800✔
324

325
        if (isempty(key))
239,600✔
326
                return -EINVAL;
327

328
        if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
119,798✔
329
                return -EINVAL;
330

331
        r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true);
119,796✔
332
        if (r < 0)
119,796✔
333
                return r;
334

335
        return cmdline_get_key(args, key, flags, ret_value);
119,796✔
336
}
337

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

342
        assert(ret);
1,603✔
343

344
        r = proc_cmdline_get_key(key, (flags & ~PROC_CMDLINE_TRUE_WHEN_MISSING) | PROC_CMDLINE_VALUE_OPTIONAL, &v);
1,603✔
345
        if (r < 0)
1,603✔
346
                return r;
347
        if (r == 0) { /* key not specified at all */
1,602✔
348
                *ret = FLAGS_SET(flags, PROC_CMDLINE_TRUE_WHEN_MISSING);
1,490✔
349
                return 0;
1,490✔
350
        }
351

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

360
        return 1;
361
}
362

363
static int cmdline_get_key_ap(ProcCmdlineFlags flags, char* const* args, va_list ap) {
2,098✔
364
        int r, ret = 0;
2,098✔
365

366
        for (;;) {
17,207✔
367
                char **v;
17,207✔
368
                const char *k, *e;
17,207✔
369

370
                k = va_arg(ap, const char*);
17,207✔
371
                if (!k)
17,207✔
372
                        break;
373

374
                assert_se(v = va_arg(ap, char**));
15,109✔
375

376
                STRV_FOREACH(p, args) {
472,879✔
377
                        const char *word;
457,770✔
378

379
                        word = mangle_word(*p, flags);
457,770✔
380
                        if (!word)
457,770✔
381
                                continue;
124✔
382

383
                        e = proc_cmdline_key_startswith(word, k);
457,646✔
384
                        if (e && *e == '=') {
457,646✔
385
                                r = free_and_strdup(v, e + 1);
1,917✔
386
                                if (r < 0)
1,917✔
387
                                        return r;
388

389
                                ret++;
1,917✔
390
                        }
391
                }
392
        }
393

394
        return ret;
395
}
396

397
int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
2,098✔
398
        _cleanup_strv_free_ char **args = NULL;
2,098✔
399
        int r;
2,098✔
400
        va_list ap;
2,098✔
401

402
        /* The PROC_CMDLINE_VALUE_OPTIONAL and PROC_CMDLINE_TRUE_WHEN_MISSING flags don't really make sense
403
         * for proc_cmdline_get_key_many, let's make this clear. */
404
        assert(!(flags & (PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_TRUE_WHEN_MISSING)));
2,098✔
405

406
        /* This call may clobber arguments on failure! */
407

408
        r = proc_cmdline_strv(&args);
2,098✔
409
        if (r < 0)
2,098✔
410
                return r;
411

412
        va_start(ap, flags);
2,098✔
413
        r = cmdline_get_key_ap(flags, args, ap);
2,098✔
414
        va_end(ap);
2,098✔
415

416
        return r;
2,098✔
417
}
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