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

systemd / systemd / 14554080340

19 Apr 2025 11:46AM UTC coverage: 72.101% (-0.03%) from 72.13%
14554080340

push

github

web-flow
Add two new paragraphs to coding style about header files (#37188)

296880 of 411754 relevant lines covered (72.1%)

687547.52 hits per line

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

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

3
#include <errno.h>
4
#include <limits.h>
5
#include <stdarg.h>
6
#include <stdlib.h>
7
#include <unistd.h>
8

9
#include "alloc-util.h"
10
#include "env-util.h"
11
#include "errno-util.h"
12
#include "escape.h"
13
#include "extract-word.h"
14
#include "log.h"
15
#include "macro.h"
16
#include "parse-util.h"
17
#include "path-util.h"
18
#include "process-util.h"
19
#include "stdio-util.h"
20
#include "string-util.h"
21
#include "strv.h"
22
#include "syslog-util.h"
23
#include "utf8.h"
24

25
/* We follow bash for the character set. Different shells have different rules. */
26
#define VALID_BASH_ENV_NAME_CHARS               \
27
        DIGITS LETTERS                          \
28
        "_"
29

30
static bool env_name_is_valid_n(const char *e, size_t n) {
126,262✔
31

32
        if (n == SIZE_MAX)
126,262✔
33
                n = strlen_ptr(e);
×
34

35
        if (n <= 0)
126,262✔
36
                return false;
37

38
        assert(e);
126,253✔
39

40
        if (ascii_isdigit(e[0]))
126,253✔
41
                return false;
42

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

49
        for (const char *p = e; p < e + n; p++)
1,689,634✔
50
                if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
1,563,418✔
51
                        return false;
52

53
        return true;
54
}
55

56
bool env_name_is_valid(const char *e) {
7,631✔
57
        return env_name_is_valid_n(e, strlen_ptr(e));
15,261✔
58
}
59

60
bool env_value_is_valid(const char *e) {
118,194✔
61
        if (!e)
118,194✔
62
                return false;
63

64
        if (!utf8_is_valid(e))
118,194✔
65
                return false;
66

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

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

77
        return true;
78
}
79

80
bool env_assignment_is_valid(const char *e) {
119,849✔
81
        const char *eq;
119,849✔
82

83
        eq = strchr(e, '=');
119,849✔
84
        if (!eq)
119,849✔
85
                return false;
86

87
        if (!env_name_is_valid_n(e, eq - e))
118,201✔
88
                return false;
89

90
        if (!env_value_is_valid(eq + 1))
118,186✔
91
                return false;
92

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

98
        return true;
99
}
100

101
bool strv_env_is_valid(char **e) {
190✔
102
        STRV_FOREACH(p, e) {
390✔
103
                size_t k;
201✔
104

105
                if (!env_assignment_is_valid(*p))
201✔
106
                        return false;
107

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

115
        return true;
116
}
117

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

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

127
        return true;
128
}
129

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

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

139
        return true;
140
}
141

142
static int env_append(char **e, char ***k, char **a) {
100,074✔
143
        assert(e);
100,074✔
144
        assert(k);
100,074✔
145
        assert(*k >= e);
100,074✔
146

147
        if (!a)
100,074✔
148
                return 0;
149

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

157
        for (; *a; a++) {
310,717✔
158
                char **j, *c;
261,924✔
159
                size_t n;
261,924✔
160

161
                n = strcspn(*a, "=");
261,924✔
162
                if ((*a)[n] == '=')
261,924✔
163
                        n++;
261,922✔
164

165
                for (j = e; j < *k; j++)
1,595,213✔
166
                        if (strneq(*j, *a, n))
1,333,377✔
167
                                break;
168

169
                c = strdup(*a);
261,924✔
170
                if (!c)
261,924✔
171
                        return -ENOMEM;
100,074✔
172

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

181
        return 0;
182
}
183

184
char** _strv_env_merge(char **first, ...) {
30,938✔
185
        _cleanup_strv_free_ char **merged = NULL;
30,938✔
186
        char **k;
30,938✔
187
        va_list ap;
30,938✔
188

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

191
        size_t n = strv_length(first);
30,938✔
192

193
        va_start(ap, first);
30,938✔
194
        for (;;) {
169,210✔
195
                char **l;
100,074✔
196

197
                l = va_arg(ap, char**);
100,074✔
198
                if (l == POINTER_MAX)
100,074✔
199
                        break;
200

201
                n += strv_length(l);
69,136✔
202
        }
203
        va_end(ap);
30,938✔
204

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

210
        if (env_append(merged, &k, first) < 0)
30,938✔
211
                return NULL;
212

213
        va_start(ap, first);
30,938✔
214
        for (;;) {
100,074✔
215
                char **l;
100,074✔
216

217
                l = va_arg(ap, char**);
100,074✔
218
                if (l == POINTER_MAX)
100,074✔
219
                        break;
220

221
                if (env_append(merged, &k, l) < 0) {
69,136✔
222
                        va_end(ap);
×
223
                        return NULL;
×
224
                }
225
        }
226
        va_end(ap);
30,938✔
227

228
        return TAKE_PTR(merged);
30,938✔
229
}
230

231
static bool env_match(const char *t, const char *pattern) {
267,500✔
232
        assert(t);
267,500✔
233
        assert(pattern);
267,500✔
234

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

245
        if (streq(t, pattern))
267,500✔
246
                return true;
247

248
        if (!strchr(pattern, '=')) {
267,499✔
249
                t = startswith(t, pattern);
261,742✔
250

251
                return t && *t == '=';
520,585✔
252
        }
253

254
        return false;
255
}
256

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

260
        assert(entry);
11,338✔
261
        assert(name);
11,338✔
262

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

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

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

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

278
        n = strv_length(x);
271✔
279

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

284
        STRV_FOREACH(k, x) {
2,853✔
285
                va_start(ap, n_lists);
2,582✔
286
                for (size_t v = 0; v < n_lists; v++) {
4,895✔
287
                        char **l;
2,585✔
288

289
                        l = va_arg(ap, char**);
2,585✔
290
                        STRV_FOREACH(j, l)
10,232✔
291
                                if (env_match(*k, *j))
7,919✔
292
                                        goto skip;
272✔
293
                }
294
                va_end(ap);
2,310✔
295

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

300
                i++;
2,310✔
301
                continue;
2,310✔
302

303
        skip:
272✔
304
                va_end(ap);
272✔
305
        }
306

307
        t[i] = NULL;
271✔
308

309
        assert(i <= n);
271✔
310

311
        return TAKE_PTR(t);
271✔
312
}
313

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

317
        if (!l)
2,370✔
318
                return NULL;
319

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

323
        char **f, **t;
324
        for (f = t = l; *f; f++) {
9,795✔
325
                if (env_match(*f, p)) {
7,427✔
326
                        free(*f);
113✔
327
                        continue;
113✔
328
                }
329

330
                *(t++) = *f;
7,314✔
331
        }
332

333
        *t = NULL;
2,368✔
334
        return l;
2,368✔
335
}
336

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

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

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

349
                va_start(ap, l);
13,485✔
350

351
                while ((p = va_arg(ap, const char*)))
263,124✔
352
                        if (env_match(*f, p)) {
252,154✔
353
                                found = true;
354
                                break;
355
                        }
356

357
                va_end(ap);
13,485✔
358

359
                if (found) {
13,485✔
360
                        free(*f);
2,515✔
361
                        continue;
2,515✔
362
                }
363

364
                *(t++) = *f;
10,970✔
365
        }
366

367
        *t = NULL;
1,571✔
368
        return l;
1,571✔
369
}
370

371
int strv_env_replace_consume(char ***l, char *p) {
6,975✔
372
        const char *t, *name;
6,975✔
373
        int r;
6,975✔
374

375
        assert(p);
6,975✔
376

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

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

388
        name = strndupa_safe(p, t - p);
6,974✔
389

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

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

402
        return 1;
403
}
404

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

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

412
        return strv_env_replace_consume(l, p);
120✔
413
}
414

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

421
        if (strchr(assignment, '=')) {
26✔
422
                if (!env_assignment_is_valid(assignment))
13✔
423
                        return -EINVAL;
424

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

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

438
        return strv_env_replace_consume(l, p);
20✔
439
}
440

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

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

447
        if (!value) {
4,280✔
448
                strv_env_unset(*l, key);
1,095✔
449
                return 0;
1,095✔
450
        }
451

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

456
        return strv_env_replace_consume(l, p);
3,185✔
457
}
458

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

462
        assert(l);
280✔
463
        assert(key);
280✔
464

465
        if (!env_name_is_valid(key))
280✔
466
                return -EINVAL;
280✔
467

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

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

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

485
        return strv_env_replace_consume(l, p);
277✔
486
}
487

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

492
        assert(l);
4✔
493

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

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

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

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

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

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

527
        return 0;
3✔
528
}
529

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

533
        if (k == SIZE_MAX)
1,408✔
534
                k = strlen(name);
969✔
535
        if (k <= 0)
1,408✔
536
                return NULL;
537

538
        STRV_FOREACH_BACKWARDS(i, l)
8,885✔
539
                if (strneq(*i, name, k) && (*i)[k] == '=')
3,796✔
540
                        return (char*) *i + k + 1;
592✔
541

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

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

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

553
        return NULL;
554
}
555

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

559
        assert(name);
18,717✔
560

561
        STRV_FOREACH_PAIR(key, value, l)
164,151✔
562
                if (streq(*key, name))
145,434✔
563
                        result = *value;
6,113✔
564

565
        return result;
18,717✔
566
}
567

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

573
        assert(ret);
25✔
574

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

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

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

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

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

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

599
        STRV_FOREACH(p, e) {
125,473✔
600
                size_t n;
115,952✔
601
                bool duplicate = false;
115,952✔
602

603
                if (!env_assignment_is_valid(*p)) {
115,952✔
604
                        if (invalid_callback)
11✔
605
                                invalid_callback(*p, userdata);
×
606
                        free(*p);
11✔
607
                        continue;
11✔
608
                }
609

610
                n = strcspn(*p, "=");
115,941✔
611
                STRV_FOREACH(q, p + 1)
825,833✔
612
                        if (strneq(*p, *q, n) && (*q)[n] == '=') {
709,894✔
613
                                duplicate = true;
614
                                break;
615
                        }
616

617
                if (duplicate) {
115,941✔
618
                        free(*p);
2✔
619
                        continue;
2✔
620
                }
621

622
                e[k++] = *p;
115,939✔
623
        }
624

625
        if (e)
9,521✔
626
                e[k] = NULL;
9,521✔
627

628
        return e;
9,521✔
629
}
630

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

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

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

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

650
        char *e;
430✔
651
        int r;
430✔
652

653
        assert(l == 0 || name);
430✔
654
        assert(ret);
430✔
655

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

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

673
        *ret = e;
430✔
674
        return !!e;
430✔
675
}
676

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

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

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

703
        assert(format);
26,826✔
704

705
        if (n == SIZE_MAX)
26,826✔
706
                n = strlen(format);
26,816✔
707

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

711
        for (e = format, i = 0; *e && i < n; e++, i++)
447,731✔
712
                switch (state) {
420,905✔
713

714
                case WORD:
414,941✔
715
                        if (*e == '$')
414,941✔
716
                                state = CURLY;
692✔
717
                        break;
718

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

724
                                word = e-1;
431✔
725
                                state = VARIABLE;
431✔
726
                                nest++;
431✔
727

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

732
                                word = e+1;
50✔
733
                                state = WORD;
50✔
734

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

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

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

746
                case VARIABLE:
4,978✔
747
                        if (*e == '}') {
4,978✔
748
                                char *t;
392✔
749

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

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

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

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

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

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

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

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

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

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

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

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

816
                                        t = v;
5✔
817
                                }
818

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

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

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

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

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

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

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

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

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

857
                assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
7✔
858

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

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

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

869
        if (ret_unset_variables)
26,826✔
870
                *ret_unset_variables = TAKE_PTR(unset_variables);
26,737✔
871
        if (ret_bad_variables)
26,826✔
872
                *ret_bad_variables = TAKE_PTR(bad_variables);
26,737✔
873

874
        if (ret)
26,826✔
875
                *ret = TAKE_PTR(s);
26,826✔
876

877
        return 0;
878
}
879

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

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

891
        l = strv_length(argv);
9,362✔
892

893
        n = new(char*, l+1);
9,362✔
894
        if (!n)
9,362✔
895
                return -ENOMEM;
896

897
        STRV_FOREACH(i, argv) {
36,119✔
898
                const char *word = *i;
26,757✔
899

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

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

922
                        q = strv_length(m);
5✔
923
                        l = l + q - 1;
5✔
924

925
                        w = reallocarray(n, l + 1, sizeof(char*));
5✔
926
                        if (!w)
5✔
927
                                return -ENOMEM;
928

929
                        n = w;
5✔
930
                        if (m) {
5✔
931
                                memcpy(n + k, m, (q + 1) * sizeof(char*));
3✔
932
                                m = mfree(m);
3✔
933
                        }
934

935
                        k += q;
5✔
936
                        continue;
5✔
937
                }
938

939
                _cleanup_strv_free_ char **u = NULL, **b = NULL;
26,752✔
940

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

954
                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
26,752✔
955
                if (r < 0)
26,752✔
956
                        return r;
957

958
                r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
26,752✔
959
                if (r < 0)
26,752✔
960
                        return r;
961
        }
962

963
        if (ret_unset_variables) {
9,362✔
964
                strv_sort_uniq(unset_variables);
9,361✔
965
                *ret_unset_variables = TAKE_PTR(unset_variables);
9,361✔
966
        }
967
        if (ret_bad_variables) {
9,362✔
968
                strv_sort_uniq(bad_variables);
9,361✔
969
                *ret_bad_variables = TAKE_PTR(bad_variables);
9,361✔
970
        }
971

972
        *ret = TAKE_PTR(n);
9,362✔
973
        return 0;
9,362✔
974
}
975

976
int getenv_bool(const char *p) {
253,960✔
977
        const char *e;
253,960✔
978

979
        e = getenv(p);
253,960✔
980
        if (!e)
253,960✔
981
                return -ENXIO;
982

983
        return parse_boolean(e);
1,078✔
984
}
985

986
int secure_getenv_bool(const char *p) {
694,543✔
987
        const char *e;
694,543✔
988

989
        e = secure_getenv(p);
694,543✔
990
        if (!e)
694,543✔
991
                return -ENXIO;
992

993
        return parse_boolean(e);
1,834✔
994
}
995

996
int secure_getenv_uint64(const char *p, uint64_t *ret) {
130✔
997
        const char *e;
130✔
998

999
        assert(p);
130✔
1000

1001
        e = secure_getenv(p);
130✔
1002
        if (!e)
130✔
1003
                return -ENXIO;
1004

1005
        return safe_atou64(e, ret);
×
1006
}
1007

1008
int set_unset_env(const char *name, const char *value, bool overwrite) {
78✔
1009
        assert(name);
78✔
1010

1011
        if (value)
78✔
1012
                return RET_NERRNO(setenv(name, value, overwrite));
33✔
1013

1014
        return RET_NERRNO(unsetenv(name));
45✔
1015
}
1016

1017
int putenv_dup(const char *assignment, bool override) {
4✔
1018
        const char *e, *n;
4✔
1019

1020
        e = strchr(assignment, '=');
4✔
1021
        if (!e)
4✔
1022
                return -EINVAL;
1023

1024
        n = strndupa_safe(assignment, e - assignment);
4✔
1025

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

1030
int setenv_systemd_exec_pid(bool update_only) {
1,533✔
1031
        const char *e;
1,533✔
1032
        int r;
1,533✔
1033

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

1036
        e = secure_getenv("SYSTEMD_EXEC_PID");
1,533✔
1037
        if (!e && update_only)
1,533✔
1038
                return 0;
1039

1040
        if (streq_ptr(e, "*"))
1,532✔
1041
                return 0;
1042

1043
        r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
1,531✔
1044
        if (r < 0)
1,531✔
1045
                return r;
×
1046

1047
        return 1;
1048
}
1049

1050
int setenv_systemd_log_level(void) {
2,220✔
1051
        _cleanup_free_ char *val = NULL;
2,220✔
1052
        int r;
2,220✔
1053

1054
        r = log_level_to_string_alloc(log_get_max_level(), &val);
2,220✔
1055
        if (r < 0)
2,220✔
1056
                return r;
1057

1058
        return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
2,220✔
1059
}
1060

1061
int getenv_path_list(const char *name, char ***ret_paths) {
1,171✔
1062
        _cleanup_strv_free_ char **l = NULL;
1,171✔
1063
        const char *e;
1,171✔
1064
        int r;
1,171✔
1065

1066
        assert(name);
1,171✔
1067
        assert(ret_paths);
1,171✔
1068

1069
        e = secure_getenv(name);
1,171✔
1070
        if (!e)
1,171✔
1071
                return -ENXIO;
1072

1073
        r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
12✔
1074
        if (r < 0)
12✔
1075
                return log_debug_errno(r, "Failed to parse $%s: %m", name);
×
1076

1077
        STRV_FOREACH(p, l) {
19✔
1078
                if (!path_is_absolute(*p))
18✔
1079
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
7✔
1080
                                               "Path '%s' is not absolute, refusing.", *p);
1081

1082
                if (!path_is_normalized(*p))
11✔
1083
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
1084
                                               "Path '%s' is not normalized, refusing.", *p);
1085

1086
                if (path_equal(*p, "/"))
8✔
1087
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1088
                                               "Path '%s' is the root fs, refusing.", *p);
1089
        }
1090

1091
        if (strv_isempty(l))
1✔
1092
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
1093
                                       "No paths specified, refusing.");
1094

1095
        *ret_paths = TAKE_PTR(l);
1✔
1096
        return 1;
1✔
1097
}
1098

1099
int getenv_steal_erase(const char *name, char **ret) {
193✔
1100
        _cleanup_(erase_and_freep) char *a = NULL;
193✔
1101
        char *e;
193✔
1102

1103
        assert(name);
193✔
1104

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

1109
        e = secure_getenv(name);
193✔
1110
        if (!e) {
193✔
1111
                if (ret)
86✔
1112
                        *ret = NULL;
85✔
1113
                return 0;
86✔
1114
        }
1115

1116
        if (ret) {
107✔
1117
                a = strdup(e);
107✔
1118
                if (!a)
107✔
1119
                        return -ENOMEM;
1120
        }
1121

1122
        string_erase(e);
107✔
1123

1124
        if (unsetenv(name) < 0)
107✔
1125
                return -errno;
×
1126

1127
        if (ret)
107✔
1128
                *ret = TAKE_PTR(a);
107✔
1129

1130
        return 1;
1131
}
1132

1133
int set_full_environment(char **env) {
×
1134
        int r;
×
1135

1136
        clearenv();
×
1137

1138
        STRV_FOREACH(e, env) {
×
1139
                _cleanup_free_ char *k = NULL, *v = NULL;
×
1140

1141
                r = split_pair(*e, "=", &k, &v);
×
1142
                if (r < 0)
×
1143
                        return r;
1144

1145
                if (setenv(k, v, /* overwrite= */ true) < 0)
×
1146
                        return -errno;
×
1147
        }
1148

1149
        return 0;
1150
}
1151

1152
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
2,673✔
1153
        _cleanup_free_ char *value = NULL;
2,673✔
1154
        va_list ap;
2,673✔
1155
        int r;
2,673✔
1156

1157
        assert(name);
2,673✔
1158

1159
        if (!valuef)
2,673✔
1160
                return RET_NERRNO(unsetenv(name));
×
1161

1162
        va_start(ap, valuef);
2,673✔
1163
        r = vasprintf(&value, valuef, ap);
2,673✔
1164
        va_end(ap);
2,673✔
1165

1166
        if (r < 0)
2,673✔
1167
                return -ENOMEM;
1168

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

1174
        return RET_NERRNO(setenv(name, value, overwrite));
2,673✔
1175
}
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