• 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

96.09
/src/basic/env-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

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

6
#include "alloc-util.h"
7
#include "env-util.h"
8
#include "errno-util.h"
9
#include "extract-word.h"
10
#include "format-util.h"
11
#include "log.h"
12
#include "parse-util.h"
13
#include "path-util.h"
14
#include "process-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17
#include "syslog-util.h"
18
#include "utf8.h"
19

20
/* We follow bash for the character set. Different shells have different rules. */
21
#define VALID_BASH_ENV_NAME_CHARS               \
22
        DIGITS LETTERS                          \
23
        "_"
24

25
size_t sc_arg_max(void) {
243,943✔
26
        long l = sysconf(_SC_ARG_MAX);
243,943✔
27
        assert(l > 0);
243,943✔
28
        return (size_t) l;
243,943✔
29
}
30

31
static bool env_name_is_valid_n(const char *e, size_t n) {
128,750✔
32

33
        if (n == SIZE_MAX)
128,750✔
UNCOV
34
                n = strlen_ptr(e);
×
35

36
        if (n <= 0)
128,750✔
37
                return false;
38

39
        assert(e);
128,741✔
40

41
        if (ascii_isdigit(e[0]))
128,741✔
42
                return false;
43

44
        /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
45
         * hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as
46
         * longest possible variable name. */
47
        if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
128,737✔
48
                return false;
49

50
        for (const char *p = e; p < e + n; p++)
1,720,241✔
51
                if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
1,591,537✔
52
                        return false;
53

54
        return true;
55
}
56

57
bool env_name_is_valid(const char *e) {
7,746✔
58
        return env_name_is_valid_n(e, strlen_ptr(e));
15,491✔
59
}
60

61
bool env_value_is_valid(const char *e) {
120,566✔
62
        if (!e)
120,566✔
63
                return false;
64

65
        if (!utf8_is_valid(e))
120,566✔
66
                return false;
67

68
        /* Note that variable *values* may contain control characters, in particular NL, TAB, BS, DEL, ESC…
69
         * When printing those variables with show-environment, we'll escape them. Make sure to print
70
         * environment variables carefully! */
71

72
        /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
73
         * hence cannot be either. Discounting the shortest possible variable name of length 1, the equal
74
         * sign and trailing NUL this hence leaves ARG_MAX-3 as longest possible variable value. */
75
        if (strlen(e) > sc_arg_max() - 3)
120,565✔
UNCOV
76
                return false;
×
77

78
        return true;
79
}
80

81
bool env_assignment_is_valid(const char *e) {
122,251✔
82
        const char *eq;
122,251✔
83

84
        eq = strchr(e, '=');
122,251✔
85
        if (!eq)
122,251✔
86
                return false;
87

88
        if (!env_name_is_valid_n(e, eq - e))
120,573✔
89
                return false;
90

91
        if (!env_value_is_valid(eq + 1))
120,558✔
92
                return false;
93

94
        /* POSIX says the overall size of the environment block cannot be > ARG_MAX, hence the individual
95
         * variable assignments cannot be either, but let's leave room for one trailing NUL byte. */
96
        if (strlen(e) > sc_arg_max() - 1)
120,558✔
UNCOV
97
                return false;
×
98

99
        return true;
100
}
101

102
bool strv_env_is_valid(char **e) {
192✔
103
        STRV_FOREACH(p, e) {
394✔
104
                size_t k;
203✔
105

106
                if (!env_assignment_is_valid(*p))
203✔
107
                        return false;
108

109
                /* Check if there are duplicate assignments */
110
                k = strcspn(*p, "=");
203✔
111
                STRV_FOREACH(q, p + 1)
235✔
112
                        if (strneq(*p, *q, k) && (*q)[k] == '=')
33✔
113
                                return false;
114
        }
115

116
        return true;
117
}
118

119
bool strv_env_name_is_valid(char **l) {
3✔
120
        STRV_FOREACH(p, l) {
8✔
121
                if (!env_name_is_valid(*p))
7✔
122
                        return false;
123

124
                if (strv_contains(p + 1, *p))
6✔
125
                        return false;
126
        }
127

128
        return true;
129
}
130

131
bool strv_env_name_or_assignment_is_valid(char **l) {
1✔
132
        STRV_FOREACH(p, l) {
2✔
133
                if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
1✔
134
                        return false;
135

136
                if (strv_contains(p + 1, *p))
1✔
137
                        return false;
138
        }
139

140
        return true;
141
}
142

143
static int env_append(char **e, char ***k, char **a) {
101,277✔
144
        assert(e);
101,277✔
145
        assert(k);
101,277✔
146
        assert(*k >= e);
101,277✔
147

148
        if (!a)
101,277✔
149
                return 0;
150

151
        /* Expects the following arguments: 'e' shall point to the beginning of an strv we are going to append to, 'k'
152
         * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv.
153
         *
154
         * This call adds every entry of 'a' to 'e', either overriding an existing matching entry, or appending to it.
155
         *
156
         * This call assumes 'e' has enough pre-allocated space to grow by all of 'a''s items. */
157

158
        for (; *a; a++) {
316,375✔
159
                char **j, *c;
266,932✔
160
                size_t n;
266,932✔
161

162
                n = strcspn(*a, "=");
266,932✔
163
                if ((*a)[n] == '=')
266,932✔
164
                        n++;
266,930✔
165

166
                for (j = e; j < *k; j++)
1,643,198✔
167
                        if (strneq(*j, *a, n))
1,376,354✔
168
                                break;
169

170
                c = strdup(*a);
266,932✔
171
                if (!c)
266,932✔
172
                        return -ENOMEM;
101,277✔
173

174
                if (j >= *k) { /* Append to the end? */
266,932✔
175
                        (*k)[0] = c;
266,844✔
176
                        (*k)[1] = NULL;
266,844✔
177
                        (*k)++;
266,844✔
178
                } else
179
                        free_and_replace(*j, c); /* Override existing item */
88✔
180
        }
181

182
        return 0;
183
}
184

185
char** _strv_env_merge(char **first, ...) {
31,391✔
186
        _cleanup_strv_free_ char **merged = NULL;
31,391✔
187
        char **k;
31,391✔
188
        va_list ap;
31,391✔
189

190
        /* Merges an arbitrary number of environment sets */
191

192
        size_t n = strv_length(first);
31,391✔
193

194
        va_start(ap, first);
31,391✔
195
        for (;;) {
171,163✔
196
                char **l;
101,277✔
197

198
                l = va_arg(ap, char**);
101,277✔
199
                if (l == POINTER_MAX)
101,277✔
200
                        break;
201

202
                n += strv_length(l);
69,886✔
203
        }
204
        va_end(ap);
31,391✔
205

206
        k = merged = new(char*, n + 1);
31,391✔
207
        if (!merged)
31,391✔
208
                return NULL;
209
        merged[0] = NULL;
31,391✔
210

211
        if (env_append(merged, &k, first) < 0)
31,391✔
212
                return NULL;
213

214
        va_start(ap, first);
31,391✔
215
        for (;;) {
101,277✔
216
                char **l;
101,277✔
217

218
                l = va_arg(ap, char**);
101,277✔
219
                if (l == POINTER_MAX)
101,277✔
220
                        break;
221

222
                if (env_append(merged, &k, l) < 0) {
69,886✔
223
                        va_end(ap);
×
UNCOV
224
                        return NULL;
×
225
                }
226
        }
227
        va_end(ap);
31,391✔
228

229
        return TAKE_PTR(merged);
31,391✔
230
}
231

232
static bool env_match(const char *t, const char *pattern) {
269,661✔
233
        assert(t);
269,661✔
234
        assert(pattern);
269,661✔
235

236
        /* pattern a matches string a
237
         *         a matches a=
238
         *         a matches a=b
239
         *         a= matches a=
240
         *         a=b matches a=b
241
         *         a= does not match a
242
         *         a=b does not match a=
243
         *         a=b does not match a
244
         *         a=b does not match a=c */
245

246
        if (streq(t, pattern))
269,661✔
247
                return true;
248

249
        if (!strchr(pattern, '=')) {
269,660✔
250
                t = startswith(t, pattern);
263,857✔
251

252
                return t && *t == '=';
524,794✔
253
        }
254

255
        return false;
256
}
257

258
static bool env_entry_has_name(const char *entry, const char *name) {
11,508✔
259
        const char *t;
11,508✔
260

261
        assert(entry);
11,508✔
262
        assert(name);
11,508✔
263

264
        t = startswith(entry, name);
11,508✔
265
        if (!t)
11,508✔
266
                return false;
267

268
        return *t == '=';
1,272✔
269
}
270

271
char** strv_env_delete(char **x, size_t n_lists, ...) {
265✔
272
        size_t n, i = 0;
265✔
273
        _cleanup_strv_free_ char **t = NULL;
265✔
274
        va_list ap;
265✔
275

276
        /* Deletes every entry from x that is mentioned in the other
277
         * string lists */
278

279
        n = strv_length(x);
265✔
280

281
        t = new(char*, n+1);
265✔
282
        if (!t)
265✔
283
                return NULL;
284

285
        STRV_FOREACH(k, x) {
2,781✔
286
                va_start(ap, n_lists);
2,516✔
287
                for (size_t v = 0; v < n_lists; v++) {
4,769✔
288
                        char **l;
2,519✔
289

290
                        l = va_arg(ap, char**);
2,519✔
291
                        STRV_FOREACH(j, l)
9,326✔
292
                                if (env_match(*k, *j))
7,073✔
293
                                        goto skip;
266✔
294
                }
295
                va_end(ap);
2,250✔
296

297
                t[i] = strdup(*k);
2,250✔
298
                if (!t[i])
2,250✔
299
                        return NULL;
300

301
                i++;
2,250✔
302
                continue;
2,250✔
303

304
        skip:
266✔
305
                va_end(ap);
266✔
306
        }
307

308
        t[i] = NULL;
265✔
309

310
        assert(i <= n);
265✔
311

312
        return TAKE_PTR(t);
265✔
313
}
314

315
char** strv_env_unset(char **l, const char *p) {
2,368✔
316
        assert(p);
2,368✔
317

318
        if (!l)
2,368✔
319
                return NULL;
320

321
        /* Drops every occurrence of the env var setting p in the
322
         * string list. Edits in-place. */
323

324
        char **f, **t;
325
        for (f = t = l; *f; f++) {
9,826✔
326
                if (env_match(*f, p)) {
7,460✔
327
                        free(*f);
112✔
328
                        continue;
112✔
329
                }
330

331
                *(t++) = *f;
7,348✔
332
        }
333

334
        *t = NULL;
2,366✔
335
        return l;
2,366✔
336
}
337

338
char** strv_env_unset_many_internal(char **l, ...) {
1,588✔
339
        if (!l)
1,588✔
340
                return NULL;
341

342
        /* Like strv_env_unset() but applies many at once. Edits in-place. */
343

344
        char **f, **t;
345
        for (f = t = l; *f; f++) {
15,231✔
346
                bool found = false;
13,643✔
347
                const char *p;
13,643✔
348
                va_list ap;
13,643✔
349

350
                va_start(ap, l);
13,643✔
351

352
                while ((p = va_arg(ap, const char*)))
266,228✔
353
                        if (env_match(*f, p)) {
255,128✔
354
                                found = true;
355
                                break;
356
                        }
357

358
                va_end(ap);
13,643✔
359

360
                if (found) {
13,643✔
361
                        free(*f);
2,543✔
362
                        continue;
2,543✔
363
                }
364

365
                *(t++) = *f;
11,100✔
366
        }
367

368
        *t = NULL;
1,588✔
369
        return l;
1,588✔
370
}
371

372
int strv_env_replace_consume(char ***l, char *p) {
7,095✔
373
        const char *t, *name;
7,095✔
374
        int r;
7,095✔
375

376
        assert(p);
7,095✔
377

378
        /* Replace first occurrence of the env var or add a new one in the string list. Drop other
379
         * occurrences. Edits in-place. Does not copy p and CONSUMES p EVEN ON FAILURE.
380
         *
381
         * p must be a valid key=value assignment. */
382

383
        t = strchr(p, '=');
7,095✔
384
        if (!t) {
7,095✔
385
                free(p);
1✔
386
                return -EINVAL;
1✔
387
        }
388

389
        name = strndupa_safe(p, t - p);
7,094✔
390

391
        STRV_FOREACH(f, *l)
17,331✔
392
                if (env_entry_has_name(*f, name)) {
11,508✔
393
                        free_and_replace(*f, p);
1,271✔
394
                        strv_env_unset(f + 1, *f);
1,271✔
395
                        return 0;
1,271✔
396
                }
397

398
        /* We didn't find a match, we need to append p or create a new strv */
399
        r = strv_consume(l, p);
5,823✔
400
        if (r < 0)
5,823✔
UNCOV
401
                return r;
×
402

403
        return 1;
404
}
405

406
int strv_env_replace_strdup(char ***l, const char *assignment) {
122✔
407
        /* Like strv_env_replace_consume(), but copies the argument. */
408

409
        char *p = strdup(assignment);
122✔
410
        if (!p)
122✔
411
                return -ENOMEM;
412

413
        return strv_env_replace_consume(l, p);
122✔
414
}
415

416
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
34✔
417
        /* Like strv_env_replace_strdup(), but pulls the variable from the environment of
418
         * the calling program, if a variable name without value is specified.
419
         */
420
        char *p;
34✔
421

422
        if (strchr(assignment, '=')) {
34✔
423
                if (!env_assignment_is_valid(assignment))
21✔
424
                        return -EINVAL;
425

426
                p = strdup(assignment);
21✔
427
        } else {
428
                if (!env_name_is_valid(assignment))
13✔
429
                        return -EINVAL;
430

431
                /* If we can't find the variable in our environment, we will use
432
                 * the empty string. This way "passthrough" is equivalent to passing
433
                 * --setenv=FOO=$FOO in the shell. */
434
                p = strjoin(assignment, "=", secure_getenv(assignment));
7✔
435
        }
436
        if (!p)
28✔
437
                return -ENOMEM;
438

439
        return strv_env_replace_consume(l, p);
28✔
440
}
441

442
int strv_env_assign(char ***l, const char *key, const char *value) {
4,313✔
443
        if (!env_name_is_valid(key))
4,313✔
444
                return -EINVAL;
445

446
        /* NULL removes assignment, "" creates an empty assignment. */
447

448
        if (!value) {
4,312✔
449
                strv_env_unset(*l, key);
1,093✔
450
                return 0;
1,093✔
451
        }
452

453
        char *p = strjoin(key, "=", value);
3,219✔
454
        if (!p)
3,219✔
455
                return -ENOMEM;
456

457
        return strv_env_replace_consume(l, p);
3,219✔
458
}
459

460
int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
322✔
461
        int r;
322✔
462

463
        assert(l);
322✔
464
        assert(key);
322✔
465

466
        if (!env_name_is_valid(key))
322✔
467
                return -EINVAL;
322✔
468

469
        if (!valuef) {
321✔
470
                strv_env_unset(*l, key);
2✔
471
                return 0;
2✔
472
        }
473

474
        _cleanup_free_ char *value = NULL;
319✔
475
        va_list ap;
319✔
476
        va_start(ap, valuef);
319✔
477
        r = vasprintf(&value, valuef, ap);
319✔
478
        va_end(ap);
319✔
479
        if (r < 0)
319✔
480
                return -ENOMEM;
481

482
        char *p = strjoin(key, "=", value);
319✔
483
        if (!p)
319✔
484
                return -ENOMEM;
485

486
        return strv_env_replace_consume(l, p);
319✔
487
}
488

489
int _strv_env_assign_many(char ***l, ...) {
4✔
490
        va_list ap;
4✔
491
        int r;
4✔
492

493
        assert(l);
4✔
494

495
        va_start(ap, l);
4✔
496
        for (;;) {
11✔
497
                const char *key, *value;
11✔
498

499
                key = va_arg(ap, const char *);
11✔
500
                if (!key)
11✔
501
                        break;
502

503
                if (!env_name_is_valid(key)) {
8✔
504
                        va_end(ap);
1✔
505
                        return -EINVAL;
1✔
506
                }
507

508
                value = va_arg(ap, const char *);
7✔
509
                if (!value) {
7✔
510
                        strv_env_unset(*l, key);
1✔
511
                        continue;
1✔
512
                }
513

514
                char *p = strjoin(key, "=", value);
6✔
515
                if (!p) {
6✔
516
                        va_end(ap);
×
UNCOV
517
                        return -ENOMEM;
×
518
                }
519

520
                r = strv_env_replace_consume(l, p);
6✔
521
                if (r < 0) {
6✔
522
                        va_end(ap);
×
UNCOV
523
                        return r;
×
524
                }
525
        }
526
        va_end(ap);
3✔
527

528
        return 0;
3✔
529
}
530

531
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
1,440✔
532
        assert(name);
1,440✔
533

534
        if (k == SIZE_MAX)
1,440✔
535
                k = strlen(name);
1,000✔
536
        if (k <= 0)
1,440✔
537
                return NULL;
538

539
        STRV_FOREACH_BACKWARDS(i, l)
9,603✔
540
                if (strneq(*i, name, k) && (*i)[k] == '=')
4,159✔
541
                        return (char*) *i + k + 1;
614✔
542

543
        if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
826✔
544
                const char *t;
13✔
545

546
                /* Safety check that the name is not overly long, before we do a stack allocation */
547
                if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
13✔
548
                        return NULL;
549

550
                t = strndupa_safe(name, k);
13✔
551
                return secure_getenv(t);
13✔
552
        };
553

554
        return NULL;
555
}
556

557
char* strv_env_pairs_get(char **l, const char *name) {
18,747✔
558
        char *result = NULL;
18,747✔
559

560
        assert(name);
18,747✔
561

562
        STRV_FOREACH_PAIR(key, value, l)
164,211✔
563
                if (streq(*key, name))
145,464✔
564
                        result = *value;
6,128✔
565

566
        return result;
18,747✔
567
}
568

569
int strv_env_get_merged(char **l, char ***ret) {
25✔
570
        _cleanup_strv_free_ char **v = NULL;
25✔
571
        size_t n = 0;
25✔
572
        int r;
25✔
573

574
        assert(ret);
25✔
575

576
        /* This converts a strv with pairs of environment variable name + value into a strv of name and
577
         * value concatenated with a "=" separator. E.g.
578
         * input  : { "NAME", "value", "FOO", "var" }
579
         * output : { "NAME=value", "FOO=var" } */
580

581
        STRV_FOREACH_PAIR(key, value, l) {
238✔
582
                char *s;
213✔
583

584
                s = strjoin(*key, "=", *value);
213✔
585
                if (!s)
213✔
586
                        return -ENOMEM;
587

588
                r = strv_consume_with_size(&v, &n, s);
213✔
589
                if (r < 0)
213✔
590
                        return r;
591
        }
592

593
        *ret = TAKE_PTR(v);
25✔
594
        return 0;
25✔
595
}
596

597
char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
9,595✔
598
        int k = 0;
9,595✔
599

600
        STRV_FOREACH(p, e) {
127,872✔
601
                size_t n;
118,277✔
602
                bool duplicate = false;
118,277✔
603

604
                if (!env_assignment_is_valid(*p)) {
118,277✔
605
                        if (invalid_callback)
11✔
UNCOV
606
                                invalid_callback(*p, userdata);
×
607
                        free(*p);
11✔
608
                        continue;
11✔
609
                }
610

611
                n = strcspn(*p, "=");
118,266✔
612
                STRV_FOREACH(q, p + 1)
856,732✔
613
                        if (strneq(*p, *q, n) && (*q)[n] == '=') {
738,468✔
614
                                duplicate = true;
615
                                break;
616
                        }
617

618
                if (duplicate) {
118,266✔
619
                        free(*p);
2✔
620
                        continue;
2✔
621
                }
622

623
                e[k++] = *p;
118,264✔
624
        }
625

626
        if (e)
9,595✔
627
                e[k] = NULL;
9,595✔
628

629
        return e;
9,595✔
630
}
631

632
static int strv_extend_with_length(char ***l, const char *s, size_t n) {
20✔
633
        char *c;
20✔
634

635
        c = strndup(s, n);
20✔
636
        if (!c)
20✔
637
                return -ENOMEM;
638

639
        return strv_consume(l, c);
20✔
640
}
641

642
static int strv_env_get_n_validated(
431✔
643
                char **env,
644
                const char *name,
645
                size_t l,
646
                ReplaceEnvFlags flags,
647
                char **ret,              /* points into the env block! do not free! */
648
                char ***unset_variables, /* updated in place */
649
                char ***bad_variables) { /* ditto */
650

651
        char *e;
431✔
652
        int r;
431✔
653

654
        assert(l == 0 || name);
431✔
655
        assert(ret);
431✔
656

657
        if (env_name_is_valid_n(name, l)) {
431✔
658
                e = strv_env_get_n(env, name, l, flags);
428✔
659
                if (!e && unset_variables) {
428✔
660
                        r = strv_extend_with_length(unset_variables, name, l);
17✔
661
                        if (r < 0)
17✔
662
                                return r;
663
                }
664
        } else {
665
                e = NULL; /* Resolve invalid variable names the same way as unset ones */
3✔
666

667
                if (bad_variables) {
3✔
668
                        r = strv_extend_with_length(bad_variables, name, l);
3✔
669
                        if (r < 0)
3✔
670
                                return r;
671
                }
672
        }
673

674
        *ret = e;
431✔
675
        return !!e;
431✔
676
}
677

678
int replace_env_full(
26,970✔
679
                const char *format,
680
                size_t n,
681
                char **env,
682
                ReplaceEnvFlags flags,
683
                char **ret,
684
                char ***ret_unset_variables,
685
                char ***ret_bad_variables) {
686

687
        enum {
26,970✔
688
                WORD,
689
                CURLY,
690
                VARIABLE,
691
                VARIABLE_RAW,
692
                TEST,
693
                DEFAULT_VALUE,
694
                ALTERNATE_VALUE,
695
        } state = WORD;
26,970✔
696

697
        _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
26,970✔
698
        const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
26,970✔
699
        _cleanup_free_ char *s = NULL;
26,970✔
700
        char ***pu, ***pb;
26,970✔
701
        size_t i, len = 0; /* len is initialized to appease gcc */
26,970✔
702
        int nest = 0, r;
26,970✔
703

704
        assert(format);
26,970✔
705

706
        if (n == SIZE_MAX)
26,970✔
707
                n = strlen(format);
26,960✔
708

709
        pu = ret_unset_variables ? &unset_variables : NULL;
26,970✔
710
        pb = ret_bad_variables ? &bad_variables : NULL;
26,970✔
711

712
        for (e = format, i = 0; *e && i < n; e++, i++)
450,626✔
713
                switch (state) {
423,656✔
714

715
                case WORD:
417,621✔
716
                        if (*e == '$')
417,621✔
717
                                state = CURLY;
696✔
718
                        break;
719

720
                case CURLY:
696✔
721
                        if (*e == '{') {
696✔
722
                                if (!strextendn(&s, word, e-word-1))
432✔
723
                                        return -ENOMEM;
724

725
                                word = e-1;
432✔
726
                                state = VARIABLE;
432✔
727
                                nest++;
432✔
728

729
                        } else if (*e == '$') {
264✔
730
                                if (!strextendn(&s, word, e-word))
52✔
731
                                        return -ENOMEM;
732

733
                                word = e+1;
52✔
734
                                state = WORD;
52✔
735

736
                        } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
212✔
737
                                if (!strextendn(&s, word, e-word-1))
19✔
738
                                        return -ENOMEM;
739

740
                                word = e-1;
19✔
741
                                state = VARIABLE_RAW;
19✔
742

743
                        } else
744
                                state = WORD;
745
                        break;
746

747
                case VARIABLE:
5,045✔
748
                        if (*e == '}') {
5,045✔
749
                                char *t;
393✔
750

751
                                r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
393✔
752
                                if (r < 0)
393✔
UNCOV
753
                                        return r;
×
754

755
                                if (!strextend(&s, t))
393✔
756
                                        return -ENOMEM;
757

758
                                word = e+1;
393✔
759
                                state = WORD;
393✔
760
                                nest--;
393✔
761
                        } else if (*e == ':') {
4,652✔
762
                                if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
38✔
763
                                        len = e - word - 2;
19✔
764
                                        state = TEST;
19✔
765
                                } else
766
                                        /* Treat this as unsupported syntax, i.e. do no replacement */
767
                                        state = WORD;
768
                        }
769
                        break;
770

771
                case TEST:
19✔
772
                        if (*e == '-')
19✔
773
                                state = DEFAULT_VALUE;
774
                        else if (*e == '+')
9✔
775
                                state = ALTERNATE_VALUE;
776
                        else {
777
                                state = WORD;
778
                                break;
779
                        }
780

781
                        test_value = e+1;
19✔
782
                        break;
19✔
783

784
                case DEFAULT_VALUE: /* fall through */
200✔
785
                case ALTERNATE_VALUE:
786
                        assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
200✔
787

788
                        if (*e == '{') {
200✔
789
                                nest++;
10✔
790
                                break;
10✔
791
                        }
792

793
                        if (*e != '}')
190✔
794
                                break;
795

796
                        nest--;
29✔
797
                        if (nest == 0) {
29✔
798
                                _cleanup_strv_free_ char **u = NULL, **b = NULL;
19✔
799
                                _cleanup_free_ char *v = NULL;
19✔
800
                                char *t = NULL;
19✔
801

802
                                r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
19✔
803
                                if (r < 0)
19✔
804
                                        return r;
805

806
                                if (t && state == ALTERNATE_VALUE) {
19✔
807
                                        r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
15✔
808
                                        if (r < 0)
5✔
809
                                                return r;
810

811
                                        t = v;
5✔
812
                                } else if (!t && state == DEFAULT_VALUE) {
14✔
813
                                        r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
15✔
814
                                        if (r < 0)
5✔
815
                                                return r;
816

817
                                        t = v;
5✔
818
                                }
819

820
                                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
19✔
821
                                if (r < 0)
19✔
822
                                        return r;
823
                                r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
19✔
824
                                if (r < 0)
19✔
825
                                        return r;
826

827
                                if (!strextend(&s, t))
19✔
828
                                        return -ENOMEM;
829

830
                                word = e+1;
19✔
831
                                state = WORD;
19✔
832
                        }
833
                        break;
834

835
                case VARIABLE_RAW:
75✔
836
                        assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
75✔
837

838
                        if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
75✔
839
                                char *t = NULL;
12✔
840

841
                                r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
12✔
842
                                if (r < 0)
12✔
UNCOV
843
                                        return r;
×
844

845
                                if (!strextend(&s, t))
12✔
846
                                        return -ENOMEM;
847

848
                                word = e--;
12✔
849
                                i--;
12✔
850
                                state = WORD;
12✔
851
                        }
852
                        break;
853
                }
854

855
        if (state == VARIABLE_RAW) {
26,970✔
856
                char *t;
7✔
857

858
                assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
7✔
859

860
                r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
7✔
861
                if (r < 0)
7✔
UNCOV
862
                        return r;
×
863

864
                if (!strextend(&s, t))
7✔
865
                        return -ENOMEM;
866

867
        } else if (!strextendn(&s, word, e-word))
26,963✔
868
                return -ENOMEM;
869

870
        if (ret_unset_variables)
26,970✔
871
                *ret_unset_variables = TAKE_PTR(unset_variables);
26,881✔
872
        if (ret_bad_variables)
26,970✔
873
                *ret_bad_variables = TAKE_PTR(bad_variables);
26,881✔
874

875
        if (ret)
26,970✔
876
                *ret = TAKE_PTR(s);
26,970✔
877

878
        return 0;
879
}
880

881
int replace_env_argv(
9,412✔
882
                char **argv,
883
                char **env,
884
                char ***ret,
885
                char ***ret_unset_variables,
886
                char ***ret_bad_variables) {
887

888
        _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
9,412✔
889
        size_t k = 0, l;
9,412✔
890
        int r;
9,412✔
891

892
        assert(!strv_isempty(argv));
9,412✔
893
        assert(ret);
9,412✔
894

895
        l = strv_length(argv);
9,412✔
896

897
        n = new(char*, l+1);
9,412✔
898
        if (!n)
9,412✔
899
                return -ENOMEM;
900

901
        STRV_FOREACH(i, argv) {
36,313✔
902
                const char *word = *i;
26,901✔
903

904
                /* If $FOO appears as single word, replace it by the split up variable */
905
                if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
26,901✔
UNCOV
906
                        _cleanup_strv_free_ char **m = NULL;
×
907
                        const char *name = word + 1;
5✔
908
                        char *e, **w;
5✔
909
                        size_t q;
5✔
910

911
                        if (env_name_is_valid(name)) {
5✔
912
                                e = strv_env_get(env, name);
4✔
913
                                if (e)
4✔
914
                                        r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
3✔
915
                                else if (ret_unset_variables)
1✔
916
                                        r = strv_extend(&unset_variables, name);
1✔
917
                                else
918
                                        r = 0;
919
                        } else if (ret_bad_variables)
1✔
UNCOV
920
                                r = strv_extend(&bad_variables, name);
×
921
                        else
922
                                r = 0;
923
                        if (r < 0)
4✔
924
                                return r;
925

926
                        q = strv_length(m);
5✔
927
                        l = l + q - 1;
5✔
928

929
                        w = reallocarray(n, l + 1, sizeof(char*));
5✔
930
                        if (!w)
5✔
931
                                return -ENOMEM;
932

933
                        n = w;
5✔
934
                        if (m) {
5✔
935
                                memcpy(n + k, m, (q + 1) * sizeof(char*));
3✔
936
                                m = mfree(m);
3✔
937
                        }
938

939
                        k += q;
5✔
940
                        continue;
5✔
941
                }
942

943
                _cleanup_strv_free_ char **u = NULL, **b = NULL;
26,896✔
944

945
                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
946
                r = replace_env_full(
26,911✔
947
                                word,
948
                                /* length= */ SIZE_MAX,
949
                                env,
950
                                /* flags= */ 0,
951
                                n + k,
26,896✔
952
                                ret_unset_variables ? &u : NULL,
953
                                ret_bad_variables ? &b : NULL);
954
                if (r < 0)
26,896✔
955
                        return r;
956
                n[++k] = NULL;
26,896✔
957

958
                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
26,896✔
959
                if (r < 0)
26,896✔
960
                        return r;
961

962
                r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
26,896✔
963
                if (r < 0)
26,896✔
964
                        return r;
965
        }
966

967
        if (ret_unset_variables) {
9,412✔
968
                strv_sort_uniq(unset_variables);
9,411✔
969
                *ret_unset_variables = TAKE_PTR(unset_variables);
9,411✔
970
        }
971
        if (ret_bad_variables) {
9,412✔
972
                strv_sort_uniq(bad_variables);
9,411✔
973
                *ret_bad_variables = TAKE_PTR(bad_variables);
9,411✔
974
        }
975

976
        *ret = TAKE_PTR(n);
9,412✔
977
        return 0;
9,412✔
978
}
979

980
int getenv_bool(const char *p) {
258,721✔
981
        const char *e;
258,721✔
982

983
        e = getenv(p);
258,721✔
984
        if (!e)
258,721✔
985
                return -ENXIO;
986

987
        return parse_boolean(e);
1,346✔
988
}
989

990
int secure_getenv_bool(const char *p) {
724,761✔
991
        const char *e;
724,761✔
992

993
        e = secure_getenv(p);
724,761✔
994
        if (!e)
724,761✔
995
                return -ENXIO;
996

997
        return parse_boolean(e);
2,432✔
998
}
999

1000
int secure_getenv_uint64(const char *p, uint64_t *ret) {
130✔
1001
        const char *e;
130✔
1002

1003
        assert(p);
130✔
1004

1005
        e = secure_getenv(p);
130✔
1006
        if (!e)
130✔
1007
                return -ENXIO;
1008

UNCOV
1009
        return safe_atou64(e, ret);
×
1010
}
1011

1012
int set_unset_env(const char *name, const char *value, bool overwrite) {
78✔
1013
        assert(name);
78✔
1014

1015
        if (value)
78✔
1016
                return RET_NERRNO(setenv(name, value, overwrite));
33✔
1017

1018
        return RET_NERRNO(unsetenv(name));
45✔
1019
}
1020

1021
int putenv_dup(const char *assignment, bool override) {
4✔
1022
        const char *e, *n;
4✔
1023

1024
        e = strchr(assignment, '=');
4✔
1025
        if (!e)
4✔
1026
                return -EINVAL;
1027

1028
        n = strndupa_safe(assignment, e - assignment);
4✔
1029

1030
        /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
1031
        return RET_NERRNO(setenv(n, e + 1, override));
4✔
1032
}
1033

1034
int setenv_systemd_exec_pid(bool update_only) {
1,546✔
1035
        const char *e;
1,546✔
1036
        int r;
1,546✔
1037

1038
        /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
1039

1040
        e = secure_getenv("SYSTEMD_EXEC_PID");
1,546✔
1041
        if (!e && update_only)
1,546✔
1042
                return 0;
1043

1044
        if (streq_ptr(e, "*"))
1,545✔
1045
                return 0;
1046

1047
        r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
1,544✔
1048
        if (r < 0)
1,544✔
UNCOV
1049
                return r;
×
1050

1051
        return 1;
1052
}
1053

1054
int setenv_systemd_log_level(void) {
2,423✔
1055
        _cleanup_free_ char *val = NULL;
2,423✔
1056
        int r;
2,423✔
1057

1058
        r = log_level_to_string_alloc(log_get_max_level(), &val);
2,423✔
1059
        if (r < 0)
2,423✔
1060
                return r;
1061

1062
        return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
2,423✔
1063
}
1064

1065
int getenv_path_list(const char *name, char ***ret_paths) {
1,201✔
1066
        _cleanup_strv_free_ char **l = NULL;
1,201✔
1067
        const char *e;
1,201✔
1068
        int r;
1,201✔
1069

1070
        assert(name);
1,201✔
1071
        assert(ret_paths);
1,201✔
1072

1073
        e = secure_getenv(name);
1,201✔
1074
        if (!e)
1,201✔
1075
                return -ENXIO;
1076

1077
        r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
12✔
1078
        if (r < 0)
12✔
UNCOV
1079
                return log_debug_errno(r, "Failed to parse $%s: %m", name);
×
1080

1081
        STRV_FOREACH(p, l) {
19✔
1082
                if (!path_is_absolute(*p))
18✔
1083
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
7✔
1084
                                               "Path '%s' is not absolute, refusing.", *p);
1085

1086
                if (!path_is_normalized(*p))
11✔
1087
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
1088
                                               "Path '%s' is not normalized, refusing.", *p);
1089

1090
                if (path_equal(*p, "/"))
8✔
1091
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1092
                                               "Path '%s' is the root fs, refusing.", *p);
1093
        }
1094

1095
        if (strv_isempty(l))
1✔
UNCOV
1096
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
1097
                                       "No paths specified, refusing.");
1098

1099
        *ret_paths = TAKE_PTR(l);
1✔
1100
        return 1;
1✔
1101
}
1102

1103
int getenv_steal_erase(const char *name, char **ret) {
193✔
1104
        _cleanup_(erase_and_freep) char *a = NULL;
193✔
1105
        char *e;
193✔
1106

1107
        assert(name);
193✔
1108

1109
        /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes
1110
         * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for
1111
         * testing, and given that people are likely going to misuse this, be thorough) */
1112

1113
        e = secure_getenv(name);
193✔
1114
        if (!e) {
193✔
1115
                if (ret)
86✔
1116
                        *ret = NULL;
85✔
1117
                return 0;
86✔
1118
        }
1119

1120
        if (ret) {
107✔
1121
                a = strdup(e);
107✔
1122
                if (!a)
107✔
1123
                        return -ENOMEM;
1124
        }
1125

1126
        string_erase(e);
107✔
1127

1128
        if (unsetenv(name) < 0)
107✔
UNCOV
1129
                return -errno;
×
1130

1131
        if (ret)
107✔
1132
                *ret = TAKE_PTR(a);
107✔
1133

1134
        return 1;
1135
}
1136

1137
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
2,653✔
1138
        _cleanup_free_ char *value = NULL;
2,653✔
1139
        va_list ap;
2,653✔
1140
        int r;
2,653✔
1141

1142
        assert(name);
2,653✔
1143

1144
        if (!valuef)
2,653✔
UNCOV
1145
                return RET_NERRNO(unsetenv(name));
×
1146

1147
        va_start(ap, valuef);
2,653✔
1148
        r = vasprintf(&value, valuef, ap);
2,653✔
1149
        va_end(ap);
2,653✔
1150

1151
        if (r < 0)
2,653✔
1152
                return -ENOMEM;
1153

1154
        /* Try to suppress writes if the value is already set correctly (simply because memory management of
1155
         * environment variables sucks a bit. */
1156
        if (streq_ptr(getenv(name), value))
2,653✔
1157
                return 0;
1158

1159
        return RET_NERRNO(setenv(name, value, overwrite));
2,653✔
1160
}
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