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

systemd / systemd / 28557109148

01 Jul 2026 11:57PM UTC coverage: 72.68% (-0.2%) from 72.877%
28557109148

push

github

web-flow
core: assorted hardening fixes flagged by kres (#42840)

2 of 16 new or added lines in 5 files covered. (12.5%)

4972 existing lines in 84 files now uncovered.

341360 of 469675 relevant lines covered (72.68%)

1370997.73 hits per line

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

96.31
/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
        ALPHANUMERICAL                          \
23
        "_"
24

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

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

33
        if (n == SIZE_MAX)
173,639✔
34
                n = strlen_ptr(e);
11,852✔
35

36
        if (n == 0)
173,638✔
37
                return false;
38

39
        assert(e);
173,629✔
40

41
        if (ascii_isdigit(e[0]))
173,629✔
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 (_unlikely_(n > sc_arg_max() - 2))
173,624✔
48
                return false;
49

50
        for (const char *p = e; p < e + n; p++)
2,315,500✔
51
                if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
2,141,911✔
52
                        return false;
53

54
        return true;
55
}
56

57
bool env_name_is_valid(const char *e) {
11,852✔
58
        return env_name_is_valid_n(e, SIZE_MAX);
11,852✔
59
}
60

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

65
        if (!utf8_is_valid(e))
161,556✔
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 (_unlikely_(strlen(e) > sc_arg_max() - 3))
161,555✔
76
                return false;
×
77

78
        return true;
79
}
80

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

84
        assert(e);
164,896✔
85

86
        eq = strchr(e, '=');
164,896✔
87
        if (!eq)
164,896✔
88
                return false;
89

90
        if (!env_name_is_valid_n(e, eq - e))
161,561✔
91
                return false;
92

93
        if (!env_value_is_valid(eq + 1))
161,546✔
94
                return false;
95

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

101
        return true;
102
}
103

104
bool strv_env_is_valid(char * const *e) {
476✔
105
        STRV_FOREACH(p, e) {
1,465✔
106
                size_t k;
991✔
107

108
                if (!env_assignment_is_valid(*p))
991✔
109
                        return false;
110

111
                /* Check if there are duplicate assignments */
112
                k = strcspn(*p, "=");
990✔
113
                STRV_FOREACH(q, p + 1)
2,016✔
114
                        if (strneq(*p, *q, k) && (*q)[k] == '=')
1,027✔
115
                                return false;
116
        }
117

118
        return true;
119
}
120

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

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

130
        return true;
131
}
132

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

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

142
        return true;
143
}
144

145
static int env_append(char **e, char ***k, char **a) {
157,334✔
146
        assert(e);
157,334✔
147
        assert(k);
157,334✔
148
        assert(*k >= e);
157,334✔
149

150
        if (!a)
157,334✔
151
                return 0;
152

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

160
        for (; *a; a++) {
484,436✔
161
                char **j, *c;
405,675✔
162
                size_t n;
405,675✔
163

164
                n = strcspn(*a, "=");
405,675✔
165
                if ((*a)[n] == '=')
405,675✔
166
                        n++;
405,673✔
167

168
                for (j = e; j < *k; j++)
2,436,405✔
169
                        if (strneq(*j, *a, n))
2,030,855✔
170
                                break;
171

172
                c = strdup(*a);
405,675✔
173
                if (!c)
405,675✔
174
                        return -ENOMEM;
157,334✔
175

176
                if (j >= *k) { /* Append to the end? */
405,675✔
177
                        (*k)[0] = c;
405,550✔
178
                        (*k)[1] = NULL;
405,550✔
179
                        (*k)++;
405,550✔
180
                } else
181
                        free_and_replace(*j, c); /* Override existing item */
125✔
182
        }
183

184
        return 0;
185
}
186

187
char** _strv_env_merge(char **first, ...) {
53,108✔
188
        _cleanup_strv_free_ char **merged = NULL;
53,108✔
189
        char **k;
53,108✔
190
        va_list ap;
53,108✔
191

192
        /* Merges an arbitrary number of environment sets */
193

194
        size_t n = strv_length(first);
53,108✔
195

196
        va_start(ap, first);
53,108✔
197
        for (;;) {
261,560✔
198
                char **l;
157,334✔
199

200
                l = va_arg(ap, char**);
157,334✔
201
                if (l == POINTER_MAX)
157,334✔
202
                        break;
203

204
                n += strv_length(l);
104,226✔
205
        }
206
        va_end(ap);
53,108✔
207

208
        k = merged = new(char*, n + 1);
53,108✔
209
        if (!merged)
53,108✔
210
                return NULL;
211
        merged[0] = NULL;
53,108✔
212

213
        if (env_append(merged, &k, first) < 0)
53,108✔
214
                return NULL;
215

216
        va_start(ap, first);
53,108✔
217
        for (;;) {
157,334✔
218
                char **l;
157,334✔
219

220
                l = va_arg(ap, char**);
157,334✔
221
                if (l == POINTER_MAX)
157,334✔
222
                        break;
223

224
                if (env_append(merged, &k, l) < 0) {
104,226✔
225
                        va_end(ap);
×
226
                        return NULL;
×
227
                }
228
        }
229
        va_end(ap);
53,108✔
230

231
        return TAKE_PTR(merged);
53,108✔
232
}
233

234
static bool env_match(const char *t, const char *pattern) {
438,922✔
235
        assert(t);
438,922✔
236
        assert(pattern);
438,922✔
237

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

248
        if (streq(t, pattern))
438,922✔
249
                return true;
250

251
        if (!strchr(pattern, '=')) {
438,921✔
252
                t = startswith(t, pattern);
431,015✔
253

254
                return t && *t == '=';
858,223✔
255
        }
256

257
        return false;
258
}
259

260
static bool env_entry_has_name(const char *entry, const char *name) {
18,727✔
261
        const char *t;
18,727✔
262

263
        assert(entry);
18,727✔
264
        assert(name);
18,727✔
265

266
        t = startswith(entry, name);
18,727✔
267
        if (!t)
18,727✔
268
                return false;
269

270
        return *t == '=';
1,782✔
271
}
272

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

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

281
        n = strv_length(x);
414✔
282

283
        t = new(char*, n+1);
414✔
284
        if (!t)
414✔
285
                return NULL;
286

287
        STRV_FOREACH(k, x) {
4,333✔
288
                va_start(ap, n_lists);
3,919✔
289
                for (size_t v = 0; v < n_lists; v++) {
7,426✔
290
                        char **l;
3,922✔
291

292
                        l = va_arg(ap, char**);
3,922✔
293
                        STRV_FOREACH(j, l)
17,673✔
294
                                if (env_match(*k, *j))
10,244✔
295
                                        goto skip;
415✔
296
                }
297
                va_end(ap);
3,504✔
298

299
                t[i] = strdup(*k);
3,504✔
300
                if (!t[i])
3,504✔
301
                        return NULL;
302

303
                i++;
3,504✔
304
                continue;
3,504✔
305

306
        skip:
415✔
307
                va_end(ap);
415✔
308
        }
309

310
        t[i] = NULL;
414✔
311

312
        assert(i <= n);
414✔
313

314
        return TAKE_PTR(t);
414✔
315
}
316

317
char** strv_env_unset(char **l, const char *p) {
3,040✔
318
        assert(p);
3,040✔
319

320
        if (!l)
3,040✔
321
                return NULL;
322

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

326
        char **f, **t;
327
        for (f = t = l; *f; f++) {
12,734✔
328
                if (env_match(*f, p)) {
9,723✔
329
                        free(*f);
119✔
330
                        continue;
119✔
331
                }
332

333
                *(t++) = *f;
9,604✔
334
        }
335

336
        *t = NULL;
3,011✔
337
        return l;
3,011✔
338
}
339

340
char** strv_env_unset_many_internal(char **l, ...) {
2,203✔
341
        if (!l)
2,203✔
342
                return NULL;
343

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

346
        char **f, **t;
347
        for (f = t = l; *f; f++) {
21,097✔
348
                bool found = false;
18,894✔
349
                const char *p;
18,894✔
350
                va_list ap;
18,894✔
351

352
                va_start(ap, l);
18,894✔
353

354
                while ((p = va_arg(ap, const char*)))
434,575✔
355
                        if (env_match(*f, p)) {
418,955✔
356
                                found = true;
357
                                break;
358
                        }
359

360
                va_end(ap);
18,894✔
361

362
                if (found) {
18,894✔
363
                        free(*f);
3,274✔
364
                        continue;
3,274✔
365
                }
366

367
                *(t++) = *f;
15,620✔
368
        }
369

370
        *t = NULL;
2,203✔
371
        return l;
2,203✔
372
}
373

374
int strv_env_replace_consume(char ***l, char *p) {
10,641✔
375
        const char *t, *name;
10,641✔
376
        int r;
10,641✔
377

378
        assert(l);
10,641✔
379
        assert(p);
10,641✔
380

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

386
        t = strchr(p, '=');
10,641✔
387
        if (!t) {
10,641✔
388
                free(p);
1✔
389
                return -EINVAL;
1✔
390
        }
391

392
        name = strndupa_safe(p, t - p);
10,640✔
393

394
        STRV_FOREACH(f, *l)
27,586✔
395
                if (env_entry_has_name(*f, name)) {
18,727✔
396
                        free_and_replace(*f, p);
1,781✔
397
                        strv_env_unset(f + 1, *f);
1,781✔
398
                        return 0;
1,781✔
399
                }
400

401
        /* We didn't find a match, we need to append p or create a new strv */
402
        r = strv_consume(l, p);
8,859✔
403
        if (r < 0)
8,859✔
404
                return r;
×
405

406
        return 1;
407
}
408

409
int strv_env_replace_strdup(char ***l, const char *assignment) {
440✔
410
        /* Like strv_env_replace_consume(), but copies the argument. */
411

412
        assert(l);
440✔
413
        assert(assignment);
440✔
414

415
        char *p = strdup(assignment);
440✔
416
        if (!p)
440✔
417
                return -ENOMEM;
418

419
        return strv_env_replace_consume(l, p);
440✔
420
}
421

422
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
417✔
423
        char *p;
417✔
424

425
        /* Like strv_env_replace_strdup(), but pulls the variable from the environment of
426
         * the calling program, if a variable name without value is specified. */
427

428
        assert(l);
417✔
429
        assert(assignment);
417✔
430

431
        if (strchr(assignment, '=')) {
417✔
432
                if (!env_assignment_is_valid(assignment))
22✔
433
                        return -EINVAL;
434

435
                p = strdup(assignment);
22✔
436
        } else {
437
                if (!env_name_is_valid(assignment))
395✔
438
                        return -EINVAL;
439

440
                /* If we can't find the variable in our environment, we will use
441
                 * the empty string. This way "passthrough" is equivalent to passing
442
                 * --setenv=FOO=$FOO in the shell. */
443
                p = strjoin(assignment, "=", secure_getenv(assignment));
389✔
444
        }
445
        if (!p)
411✔
446
                return -ENOMEM;
447

448
        return strv_env_replace_consume(l, p);
411✔
449
}
450

451
int strv_env_assign(char ***l, const char *key, const char *value) {
5,442✔
452
        assert(l);
5,442✔
453

454
        if (!env_name_is_valid(key))
5,442✔
455
                return -EINVAL;
456

457
        /* NULL removes assignment, "" creates an empty assignment. */
458

459
        if (!value) {
5,441✔
460
                strv_env_unset(*l, key);
1,255✔
461
                return 0;
1,255✔
462
        }
463

464
        char *p = strjoin(key, "=", value);
4,186✔
465
        if (!p)
4,186✔
466
                return -ENOMEM;
467

468
        return strv_env_replace_consume(l, p);
4,186✔
469
}
470

471
int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
600✔
472
        int r;
600✔
473

474
        assert(l);
600✔
475
        assert(key);
600✔
476

477
        if (!env_name_is_valid(key))
600✔
478
                return -EINVAL;
600✔
479

480
        if (!valuef) {
599✔
481
                strv_env_unset(*l, key);
2✔
482
                return 0;
2✔
483
        }
484

485
        _cleanup_free_ char *value = NULL;
597✔
486
        va_list ap;
597✔
487
        va_start(ap, valuef);
597✔
488
        r = vasprintf(&value, valuef, ap);
597✔
489
        va_end(ap);
597✔
490
        if (r < 0)
597✔
491
                return -ENOMEM;
492

493
        char *p = strjoin(key, "=", value);
597✔
494
        if (!p)
597✔
495
                return -ENOMEM;
496

497
        return strv_env_replace_consume(l, p);
597✔
498
}
499

500
int _strv_env_assign_many(char ***l, ...) {
4✔
501
        va_list ap;
4✔
502
        int r;
4✔
503

504
        assert(l);
4✔
505

506
        va_start(ap, l);
4✔
507
        for (;;) {
11✔
508
                const char *key, *value;
11✔
509

510
                key = va_arg(ap, const char *);
11✔
511
                if (!key)
11✔
512
                        break;
513

514
                if (!env_name_is_valid(key)) {
8✔
515
                        va_end(ap);
1✔
516
                        return -EINVAL;
1✔
517
                }
518

519
                value = va_arg(ap, const char *);
7✔
520
                if (!value) {
7✔
521
                        strv_env_unset(*l, key);
1✔
522
                        continue;
1✔
523
                }
524

525
                char *p = strjoin(key, "=", value);
6✔
526
                if (!p) {
6✔
527
                        va_end(ap);
×
528
                        return -ENOMEM;
×
529
                }
530

531
                r = strv_env_replace_consume(l, p);
6✔
532
                if (r < 0) {
6✔
533
                        va_end(ap);
×
534
                        return r;
×
535
                }
536
        }
537
        va_end(ap);
3✔
538

539
        return 0;
3✔
540
}
541

542
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
14,645✔
543
        assert(name);
14,645✔
544

545
        if (k == SIZE_MAX)
14,645✔
546
                k = strlen(name);
14,410✔
547
        if (k <= 0)
14,645✔
548
                return NULL;
549

550
        STRV_FOREACH_BACKWARDS(i, l)
174,664✔
551
                if (strneq(*i, name, k) && (*i)[k] == '=')
160,766✔
552
                        return (char*) *i + k + 1;
747✔
553

554
        if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
13,898✔
555
                const char *t;
13✔
556

557
                /* Safety check that the name is not overly long, before we do a stack allocation */
558
                if (k > sc_arg_max() - 2)
13✔
559
                        return NULL;
560

561
                t = strndupa_safe(name, k);
13✔
562
                return secure_getenv(t);
13✔
563
        };
564

565
        return NULL;
566
}
567

568
char* strv_env_pairs_get(char **l, const char *name) {
25,554✔
569
        char *result = NULL;
25,554✔
570

571
        assert(name);
25,554✔
572

573
        STRV_FOREACH_PAIR(key, value, l)
220,846✔
574
                if (streq(*key, name))
195,292✔
575
                        result = *value;
7,896✔
576

577
        return result;
25,554✔
578
}
579

580
int strv_env_get_merged(char **l, char ***ret) {
37✔
581
        _cleanup_strv_free_ char **v = NULL;
37✔
582
        size_t n = 0;
37✔
583
        int r;
37✔
584

585
        assert(ret);
37✔
586

587
        /* This converts a strv with pairs of environment variable name + value into a strv of name and
588
         * value concatenated with a "=" separator. E.g.
589
         * input  : { "NAME", "value", "FOO", "var" }
590
         * output : { "NAME=value", "FOO=var" } */
591

592
        STRV_FOREACH_PAIR(key, value, l) {
357✔
593
                char *s;
320✔
594

595
                s = strjoin(*key, "=", *value);
320✔
596
                if (!s)
320✔
597
                        return -ENOMEM;
598

599
                r = strv_consume_with_size(&v, &n, s);
320✔
600
                if (r < 0)
320✔
601
                        return r;
602
        }
603

604
        *ret = TAKE_PTR(v);
37✔
605
        return 0;
37✔
606
}
607

608
char** strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
12,746✔
609
        int k = 0;
12,746✔
610

611
        STRV_FOREACH(p, l) {
170,012✔
612
                size_t n;
157,266✔
613
                bool duplicate = false;
157,266✔
614

615
                if (!env_assignment_is_valid(*p)) {
157,266✔
616
                        if (invalid_callback)
11✔
617
                                invalid_callback(*p, userdata);
×
618
                        free(*p);
11✔
619
                        continue;
11✔
620
                }
621

622
                n = strcspn(*p, "=");
157,255✔
623
                STRV_FOREACH(q, p + 1)
1,148,791✔
624
                        if (strneq(*p, *q, n) && (*q)[n] == '=') {
991,538✔
625
                                duplicate = true;
626
                                break;
627
                        }
628

629
                if (duplicate) {
157,255✔
630
                        free(*p);
2✔
631
                        continue;
2✔
632
                }
633

634
                l[k++] = *p;
157,253✔
635
        }
636

637
        if (l)
12,746✔
638
                l[k] = NULL;
12,746✔
639

640
        return l;
12,746✔
641
}
642

643
static int strv_extend_with_length(char ***l, const char *s, size_t n) {
20✔
644
        char *c;
20✔
645

646
        c = strndup(s, n);
20✔
647
        if (!c)
20✔
648
                return -ENOMEM;
649

650
        return strv_consume(l, c);
20✔
651
}
652

653
static int strv_env_get_n_validated(
226✔
654
                char **env,
655
                const char *name,
656
                size_t l,
657
                ReplaceEnvFlags flags,
658
                char **ret,              /* points into the env block! do not free! */
659
                char ***unset_variables, /* updated in place */
660
                char ***bad_variables) { /* ditto */
661

662
        char *e;
226✔
663
        int r;
226✔
664

665
        assert(l == 0 || name);
226✔
666
        assert(ret);
226✔
667

668
        if (env_name_is_valid_n(name, l)) {
226✔
669
                e = strv_env_get_n(env, name, l, flags);
223✔
670
                if (!e && unset_variables) {
223✔
671
                        r = strv_extend_with_length(unset_variables, name, l);
17✔
672
                        if (r < 0)
17✔
673
                                return r;
674
                }
675
        } else {
676
                e = NULL; /* Resolve invalid variable names the same way as unset ones */
3✔
677

678
                if (bad_variables) {
3✔
679
                        r = strv_extend_with_length(bad_variables, name, l);
3✔
680
                        if (r < 0)
3✔
681
                                return r;
682
                }
683
        }
684

685
        *ret = e;
226✔
686
        return !!e;
226✔
687
}
688

689
int replace_env_full(
32,926✔
690
                const char *format,
691
                size_t length,
692
                char **env,
693
                ReplaceEnvFlags flags,
694
                char **ret,
695
                char ***ret_unset_variables,
696
                char ***ret_bad_variables) {
697

698
        enum {
32,926✔
699
                WORD,
700
                CURLY,
701
                VARIABLE,
702
                VARIABLE_RAW,
703
                TEST,
704
                DEFAULT_VALUE,
705
                ALTERNATE_VALUE,
706
        } state = WORD;
32,926✔
707

708
        _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
32,926✔
709
        const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
32,926✔
710
        _cleanup_free_ char *s = NULL;
32,926✔
711
        char ***pu, ***pb;
32,926✔
712
        size_t i, len = 0; /* len is initialized to appease gcc */
32,926✔
713
        int nest = 0, r;
32,926✔
714

715
        assert(format);
32,926✔
716

717
        if (length == SIZE_MAX)
32,926✔
718
                length = strlen(format);
32,916✔
719

720
        pu = ret_unset_variables ? &unset_variables : NULL;
32,926✔
721
        pb = ret_bad_variables ? &bad_variables : NULL;
32,926✔
722

723
        for (e = format, i = 0; *e && i < length; e++, i++)
549,363✔
724
                switch (state) {
516,437✔
725

726
                case WORD:
513,783✔
727
                        if (*e == '$')
513,783✔
728
                                state = CURLY;
515✔
729
                        break;
730

731
                case CURLY:
515✔
732
                        if (*e == '{') {
515✔
733
                                if (!strextendn(&s, word, e-word-1))
228✔
734
                                        return -ENOMEM;
735

736
                                word = e-1;
228✔
737
                                state = VARIABLE;
228✔
738
                                nest++;
228✔
739

740
                        } else if (*e == '$') {
287✔
741
                                if (!strextendn(&s, word, e-word))
52✔
742
                                        return -ENOMEM;
743

744
                                word = e+1;
52✔
745
                                state = WORD;
52✔
746

747
                        } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
235✔
748
                                if (!strextendn(&s, word, e-word-1))
19✔
749
                                        return -ENOMEM;
750

751
                                word = e-1;
19✔
752
                                state = VARIABLE_RAW;
19✔
753

754
                        } else
755
                                state = WORD;
756
                        break;
757

758
                case VARIABLE:
1,845✔
759
                        if (*e == '}') {
1,845✔
760
                                char *t;
188✔
761

762
                                r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
188✔
763
                                if (r < 0)
188✔
764
                                        return r;
×
765

766
                                if (!strextend(&s, t))
188✔
767
                                        return -ENOMEM;
768

769
                                word = e+1;
188✔
770
                                state = WORD;
188✔
771
                                nest--;
188✔
772
                        } else if (*e == ':') {
1,657✔
773
                                if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
39✔
774
                                        len = e - word - 2;
19✔
775
                                        state = TEST;
19✔
776
                                } else
777
                                        /* Treat this as unsupported syntax, i.e. do no replacement */
778
                                        state = WORD;
779
                        }
780
                        break;
781

782
                case TEST:
19✔
783
                        if (*e == '-')
19✔
784
                                state = DEFAULT_VALUE;
785
                        else if (*e == '+')
9✔
786
                                state = ALTERNATE_VALUE;
787
                        else {
788
                                state = WORD;
789
                                break;
790
                        }
791

792
                        test_value = e+1;
19✔
793
                        break;
19✔
794

795
                case DEFAULT_VALUE: /* fall through */
200✔
796
                case ALTERNATE_VALUE:
797
                        assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
200✔
798

799
                        if (*e == '{') {
200✔
800
                                nest++;
10✔
801
                                break;
10✔
802
                        }
803

804
                        if (*e != '}')
190✔
805
                                break;
806

807
                        nest--;
29✔
808
                        if (nest == 0) {
29✔
809
                                _cleanup_strv_free_ char **u = NULL, **b = NULL;
19✔
810
                                _cleanup_free_ char *v = NULL;
19✔
811
                                char *t = NULL;
19✔
812

813
                                r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
19✔
814
                                if (r < 0)
19✔
815
                                        return r;
816

817
                                if (t && state == ALTERNATE_VALUE) {
19✔
818
                                        r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
15✔
819
                                        if (r < 0)
5✔
820
                                                return r;
821

822
                                        t = v;
5✔
823
                                } else if (!t && state == DEFAULT_VALUE) {
14✔
824
                                        r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
15✔
825
                                        if (r < 0)
5✔
826
                                                return r;
827

828
                                        t = v;
5✔
829
                                }
830

831
                                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
19✔
832
                                if (r < 0)
19✔
833
                                        return r;
834
                                r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
19✔
835
                                if (r < 0)
19✔
836
                                        return r;
837

838
                                if (!strextend(&s, t))
19✔
839
                                        return -ENOMEM;
840

841
                                word = e+1;
19✔
842
                                state = WORD;
19✔
843
                        }
844
                        break;
845

846
                case VARIABLE_RAW:
75✔
847
                        assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
75✔
848

849
                        if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
75✔
850
                                char *t = NULL;
12✔
851

852
                                r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
12✔
853
                                if (r < 0)
12✔
854
                                        return r;
×
855

856
                                if (!strextend(&s, t))
12✔
857
                                        return -ENOMEM;
858

859
                                word = e--;
12✔
860
                                i--;
12✔
861
                                state = WORD;
12✔
862
                        }
863
                        break;
864
                }
865

866
        if (state == VARIABLE_RAW) {
32,926✔
867
                char *t;
7✔
868

869
                assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
7✔
870

871
                r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
7✔
872
                if (r < 0)
7✔
873
                        return r;
×
874

875
                if (!strextend(&s, t))
7✔
876
                        return -ENOMEM;
877

878
        } else if (!strextendn(&s, word, e-word))
32,919✔
879
                return -ENOMEM;
880

881
        if (ret_unset_variables)
32,926✔
882
                *ret_unset_variables = TAKE_PTR(unset_variables);
32,836✔
883
        if (ret_bad_variables)
32,926✔
884
                *ret_bad_variables = TAKE_PTR(bad_variables);
32,836✔
885

886
        if (ret)
32,926✔
887
                *ret = TAKE_PTR(s);
32,926✔
888

889
        return 0;
890
}
891

892
int replace_env_argv(
12,418✔
893
                char **argv,
894
                char **env,
895
                char ***ret,
896
                char ***ret_unset_variables,
897
                char ***ret_bad_variables) {
898

899
        _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
12,418✔
900
        size_t k = 0, l;
12,418✔
901
        int r;
12,418✔
902

903
        assert(!strv_isempty(argv));
12,418✔
904
        assert(ret);
12,418✔
905

906
        l = strv_length(argv);
12,418✔
907

908
        n = new(char*, l+1);
12,418✔
909
        if (!n)
12,418✔
910
                return -ENOMEM;
911

912
        STRV_FOREACH(i, argv) {
45,282✔
913
                const char *word = *i;
32,864✔
914

915
                /* If $FOO appears as single word, replace it by the split up variable */
916
                if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
32,864✔
917
                        _cleanup_strv_free_ char **m = NULL;
×
918
                        const char *name = word + 1;
12✔
919
                        char *e, **w;
12✔
920
                        size_t q;
12✔
921

922
                        if (env_name_is_valid(name)) {
12✔
923
                                e = strv_env_get(env, name);
11✔
924
                                if (e)
11✔
925
                                        r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
5✔
926
                                else if (ret_unset_variables)
6✔
927
                                        r = strv_extend(&unset_variables, name);
1✔
928
                                else
929
                                        r = 0;
930
                        } else if (ret_bad_variables)
1✔
931
                                r = strv_extend(&bad_variables, name);
×
932
                        else
933
                                r = 0;
934
                        if (r < 0)
6✔
935
                                return r;
936

937
                        q = strv_length(m);
12✔
938
                        l = l + q - 1;
12✔
939

940
                        w = reallocarray(n, l + 1, sizeof(char*));
12✔
941
                        if (!w)
12✔
942
                                return -ENOMEM;
943

944
                        n = w;
12✔
945
                        if (m) {
12✔
946
                                memcpy(n + k, m, (q + 1) * sizeof(char*));
5✔
947
                                m = mfree(m);
5✔
948
                        }
949

950
                        k += q;
12✔
951
                        /* reallocarray() does not zero-initialize the grown tail, add NUL termination */
952
                        n[k] = NULL;
12✔
953
                        continue;
12✔
954
                }
955

956
                _cleanup_strv_free_ char **u = NULL, **b = NULL;
32,852✔
957

958
                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
959
                r = replace_env_full(
32,868✔
960
                                word,
961
                                /* length= */ SIZE_MAX,
962
                                env,
963
                                /* flags= */ 0,
964
                                n + k,
32,852✔
965
                                ret_unset_variables ? &u : NULL,
966
                                ret_bad_variables ? &b : NULL);
967
                if (r < 0)
32,852✔
968
                        return r;
969
                n[++k] = NULL;
32,852✔
970

971
                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
32,852✔
972
                if (r < 0)
32,852✔
973
                        return r;
974

975
                r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
32,852✔
976
                if (r < 0)
32,852✔
977
                        return r;
978
        }
979

980
        if (ret_unset_variables) {
12,418✔
981
                strv_sort_uniq(unset_variables);
12,413✔
982
                *ret_unset_variables = TAKE_PTR(unset_variables);
12,413✔
983
        }
984
        if (ret_bad_variables) {
12,418✔
985
                strv_sort_uniq(bad_variables);
12,413✔
986
                *ret_bad_variables = TAKE_PTR(bad_variables);
12,413✔
987
        }
988

989
        *ret = TAKE_PTR(n);
12,418✔
990
        return 0;
12,418✔
991
}
992

993
int getenv_bool(const char *p) {
284,372✔
994
        const char *e;
284,372✔
995

996
        e = getenv(p);
284,372✔
997
        if (!e)
284,372✔
998
                return -ENXIO;
999

1000
        return parse_boolean(e);
2,315✔
1001
}
1002

1003
int secure_getenv_bool(const char *p) {
808,800✔
1004
        const char *e;
808,800✔
1005

1006
        e = secure_getenv(p);
808,800✔
1007
        if (!e)
808,800✔
1008
                return -ENXIO;
1009

1010
        return parse_boolean(e);
1,085✔
1011
}
1012

1013
int secure_getenv_uint64(const char *p, uint64_t *ret) {
146✔
1014
        const char *e;
146✔
1015

1016
        assert(p);
146✔
1017

1018
        e = secure_getenv(p);
146✔
1019
        if (!e)
146✔
1020
                return -ENXIO;
1021

UNCOV
1022
        return safe_atou64(e, ret);
×
1023
}
1024

1025
int set_unset_env(const char *name, const char *value, bool overwrite) {
803✔
1026
        assert(name);
803✔
1027

1028
        if (value)
803✔
1029
                return RET_NERRNO(setenv(name, value, overwrite));
97✔
1030

1031
        return RET_NERRNO(unsetenv(name));
706✔
1032
}
1033

1034
int putenv_dup(const char *assignment, bool override) {
4✔
1035
        const char *e, *n;
4✔
1036

1037
        e = strchr(assignment, '=');
4✔
1038
        if (!e)
4✔
1039
                return -EINVAL;
1040

1041
        n = strndupa_safe(assignment, e - assignment);
4✔
1042

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

1047
int setenv_systemd_exec_pid(bool update_only) {
2,209✔
1048
        const char *e;
2,209✔
1049
        int r;
2,209✔
1050

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

1053
        e = secure_getenv("SYSTEMD_EXEC_PID");
2,209✔
1054
        if (!e && update_only)
2,209✔
1055
                return 0;
1056

1057
        if (streq_ptr(e, "*"))
2,208✔
1058
                return 0;
1059

1060
        r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
2,207✔
1061
        if (r < 0)
2,207✔
UNCOV
1062
                return r;
×
1063

1064
        return 1;
1065
}
1066

1067
int setenv_systemd_log_level(void) {
2,479✔
1068
        _cleanup_free_ char *val = NULL;
2,479✔
1069
        int r;
2,479✔
1070

1071
        r = log_level_to_string_alloc(log_get_max_level(), &val);
2,479✔
1072
        if (r < 0)
2,479✔
1073
                return r;
1074

1075
        return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
2,479✔
1076
}
1077

1078
int getenv_path_list(const char *name, char ***ret_paths) {
3,657✔
1079
        _cleanup_strv_free_ char **l = NULL;
3,657✔
1080
        const char *e;
3,657✔
1081
        int r;
3,657✔
1082

1083
        assert(name);
3,657✔
1084
        assert(ret_paths);
3,657✔
1085

1086
        e = secure_getenv(name);
3,657✔
1087
        if (!e)
3,657✔
1088
                return -ENXIO;
1089

1090
        r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
12✔
1091
        if (r < 0)
12✔
UNCOV
1092
                return log_debug_errno(r, "Failed to parse $%s: %m", name);
×
1093

1094
        STRV_FOREACH(p, l) {
19✔
1095
                if (!path_is_absolute(*p))
18✔
1096
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
7✔
1097
                                               "Path '%s' is not absolute, refusing.", *p);
1098

1099
                if (!path_is_normalized(*p))
11✔
1100
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
1101
                                               "Path '%s' is not normalized, refusing.", *p);
1102

1103
                if (path_equal(*p, "/"))
8✔
1104
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1105
                                               "Path '%s' is the root fs, refusing.", *p);
1106
        }
1107

1108
        if (strv_isempty(l))
1✔
UNCOV
1109
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
1110
                                       "No paths specified, refusing.");
1111

1112
        *ret_paths = TAKE_PTR(l);
1✔
1113
        return 1;
1✔
1114
}
1115

1116
int getenv_steal_erase(const char *name, char **ret) {
265✔
1117
        _cleanup_(erase_and_freep) char *a = NULL;
265✔
1118
        char *e;
265✔
1119

1120
        assert(name);
265✔
1121

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

1126
        e = secure_getenv(name);
265✔
1127
        if (!e) {
265✔
1128
                if (ret)
142✔
1129
                        *ret = NULL;
141✔
1130
                return 0;
1131
        }
1132

1133
        if (ret) {
123✔
1134
                a = strdup(e);
123✔
1135
                if (!a)
123✔
1136
                        return -ENOMEM;
1137
        }
1138

1139
        string_erase(e);
123✔
1140

1141
        if (unsetenv(name) < 0)
123✔
UNCOV
1142
                return -errno;
×
1143

1144
        if (ret)
123✔
1145
                *ret = TAKE_PTR(a);
123✔
1146

1147
        return 1;
1148
}
1149

1150
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
7,106✔
1151
        _cleanup_free_ char *value = NULL;
7,106✔
1152
        va_list ap;
7,106✔
1153
        int r;
7,106✔
1154

1155
        assert(name);
7,106✔
1156

1157
        if (!valuef)
7,106✔
UNCOV
1158
                return RET_NERRNO(unsetenv(name));
×
1159

1160
        va_start(ap, valuef);
7,106✔
1161
        r = vasprintf(&value, valuef, ap);
7,106✔
1162
        va_end(ap);
7,106✔
1163

1164
        if (r < 0)
7,106✔
1165
                return -ENOMEM;
1166

1167
        /* Try to suppress writes if the value is already set correctly (simply because memory management of
1168
         * environment variables sucks a bit. */
1169
        if (streq_ptr(getenv(name), value))
7,106✔
1170
                return 0;
1171

1172
        return RET_NERRNO(setenv(name, value, overwrite));
7,106✔
1173
}
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