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

systemd / systemd / 13297401319

12 Feb 2025 09:02PM UTC coverage: 71.784% (-0.06%) from 71.84%
13297401319

push

github

poettering
update TODO

293477 of 408836 relevant lines covered (71.78%)

714381.64 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 "macro.h"
15
#include "parse-util.h"
16
#include "path-util.h"
17
#include "process-util.h"
18
#include "stdio-util.h"
19
#include "string-util.h"
20
#include "strv.h"
21
#include "syslog-util.h"
22
#include "utf8.h"
23

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

29
static bool env_name_is_valid_n(const char *e, size_t n) {
146,488✔
30

31
        if (n == SIZE_MAX)
146,488✔
32
                n = strlen_ptr(e);
×
33

34
        if (n <= 0)
146,488✔
35
                return false;
36

37
        assert(e);
146,479✔
38

39
        if (ascii_isdigit(e[0]))
146,479✔
40
                return false;
41

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

48
        for (const char *p = e; p < e + n; p++)
2,005,057✔
49
                if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
1,858,615✔
50
                        return false;
51

52
        return true;
53
}
54

55
bool env_name_is_valid(const char *e) {
7,968✔
56
        return env_name_is_valid_n(e, strlen_ptr(e));
15,935✔
57
}
58

59
bool env_value_is_valid(const char *e) {
138,127✔
60
        if (!e)
138,127✔
61
                return false;
62

63
        if (!utf8_is_valid(e))
138,127✔
64
                return false;
65

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

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

76
        return true;
77
}
78

79
bool env_assignment_is_valid(const char *e) {
140,423✔
80
        const char *eq;
140,423✔
81

82
        eq = strchr(e, '=');
140,423✔
83
        if (!eq)
140,423✔
84
                return false;
85

86
        if (!env_name_is_valid_n(e, eq - e))
138,134✔
87
                return false;
88

89
        if (!env_value_is_valid(eq + 1))
138,119✔
90
                return false;
91

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

97
        return true;
98
}
99

100
bool strv_env_is_valid(char **e) {
153✔
101
        STRV_FOREACH(p, e) {
316✔
102
                size_t k;
164✔
103

104
                if (!env_assignment_is_valid(*p))
164✔
105
                        return false;
106

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

114
        return true;
115
}
116

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

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

126
        return true;
127
}
128

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

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

138
        return true;
139
}
140

141
static int env_append(char **e, char ***k, char **a) {
132,584✔
142
        assert(e);
132,584✔
143
        assert(k);
132,584✔
144
        assert(*k >= e);
132,584✔
145

146
        if (!a)
132,584✔
147
                return 0;
148

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

156
        for (; *a; a++) {
374,702✔
157
                char **j, *c;
310,403✔
158
                size_t n;
310,403✔
159

160
                n = strcspn(*a, "=");
310,403✔
161
                if ((*a)[n] == '=')
310,403✔
162
                        n++;
310,401✔
163

164
                for (j = e; j < *k; j++)
1,731,638✔
165
                        if (strneq(*j, *a, n))
1,421,304✔
166
                                break;
167

168
                c = strdup(*a);
310,403✔
169
                if (!c)
310,403✔
170
                        return -ENOMEM;
132,584✔
171

172
                if (j >= *k) { /* Append to the end? */
310,403✔
173
                        (*k)[0] = c;
310,334✔
174
                        (*k)[1] = NULL;
310,334✔
175
                        (*k)++;
310,334✔
176
                } else
177
                        free_and_replace(*j, c); /* Override existing item */
69✔
178
        }
179

180
        return 0;
181
}
182

183
char** _strv_env_merge(char **first, ...) {
44,073✔
184
        _cleanup_strv_free_ char **merged = NULL;
44,073✔
185
        char **k;
44,073✔
186
        va_list ap;
44,073✔
187

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

190
        size_t n = strv_length(first);
44,073✔
191

192
        va_start(ap, first);
44,073✔
193
        for (;;) {
221,095✔
194
                char **l;
132,584✔
195

196
                l = va_arg(ap, char**);
132,584✔
197
                if (l == POINTER_MAX)
132,584✔
198
                        break;
199

200
                n += strv_length(l);
88,511✔
201
        }
202
        va_end(ap);
44,073✔
203

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

209
        if (env_append(merged, &k, first) < 0)
44,073✔
210
                return NULL;
211

212
        va_start(ap, first);
44,073✔
213
        for (;;) {
132,584✔
214
                char **l;
132,584✔
215

216
                l = va_arg(ap, char**);
132,584✔
217
                if (l == POINTER_MAX)
132,584✔
218
                        break;
219

220
                if (env_append(merged, &k, l) < 0) {
88,511✔
221
                        va_end(ap);
×
222
                        return NULL;
×
223
                }
224
        }
225
        va_end(ap);
44,073✔
226

227
        return TAKE_PTR(merged);
44,073✔
228
}
229

230
static bool env_match(const char *t, const char *pattern) {
231,938✔
231
        assert(t);
231,938✔
232
        assert(pattern);
231,938✔
233

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

244
        if (streq(t, pattern))
231,938✔
245
                return true;
246

247
        if (!strchr(pattern, '=')) {
231,937✔
248
                t = startswith(t, pattern);
227,475✔
249

250
                return t && *t == '=';
452,880✔
251
        }
252

253
        return false;
254
}
255

256
static bool env_entry_has_name(const char *entry, const char *name) {
8,938✔
257
        const char *t;
8,938✔
258

259
        assert(entry);
8,938✔
260
        assert(name);
8,938✔
261

262
        t = startswith(entry, name);
8,938✔
263
        if (!t)
8,938✔
264
                return false;
265

266
        return *t == '=';
811✔
267
}
268

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

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

277
        n = strv_length(x);
40✔
278

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

283
        STRV_FOREACH(k, x) {
444✔
284
                va_start(ap, n_lists);
404✔
285
                for (size_t v = 0; v < n_lists; v++) {
770✔
286
                        char **l;
407✔
287

288
                        l = va_arg(ap, char**);
407✔
289
                        STRV_FOREACH(j, l)
5,470✔
290
                                if (env_match(*k, *j))
5,104✔
291
                                        goto skip;
41✔
292
                }
293
                va_end(ap);
363✔
294

295
                t[i] = strdup(*k);
363✔
296
                if (!t[i])
363✔
297
                        return NULL;
298

299
                i++;
363✔
300
                continue;
363✔
301

302
        skip:
41✔
303
                va_end(ap);
41✔
304
        }
305

306
        t[i] = NULL;
40✔
307

308
        assert(i <= n);
40✔
309

310
        return TAKE_PTR(t);
40✔
311
}
312

313
char** strv_env_unset(char **l, const char *p) {
1,909✔
314
        assert(p);
1,909✔
315

316
        if (!l)
1,909✔
317
                return NULL;
318

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

322
        char **f, **t;
323
        for (f = t = l; *f; f++) {
7,567✔
324
                if (env_match(*f, p)) {
5,660✔
325
                        free(*f);
5✔
326
                        continue;
5✔
327
                }
328

329
                *(t++) = *f;
5,655✔
330
        }
331

332
        *t = NULL;
1,907✔
333
        return l;
1,907✔
334
}
335

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

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

342
        char **f, **t;
343
        for (f = t = l; *f; f++) {
12,864✔
344
                bool found = false;
11,413✔
345
                const char *p;
11,413✔
346
                va_list ap;
11,413✔
347

348
                va_start(ap, l);
11,413✔
349

350
                while ((p = va_arg(ap, const char*)))
230,562✔
351
                        if (env_match(*f, p)) {
221,174✔
352
                                found = true;
353
                                break;
354
                        }
355

356
                va_end(ap);
11,413✔
357

358
                if (found) {
11,413✔
359
                        free(*f);
2,025✔
360
                        continue;
2,025✔
361
                }
362

363
                *(t++) = *f;
9,388✔
364
        }
365

366
        *t = NULL;
1,451✔
367
        return l;
1,451✔
368
}
369

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

374
        assert(p);
6,554✔
375

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

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

387
        name = strndupa_safe(p, t - p);
6,553✔
388

389
        STRV_FOREACH(f, *l)
14,681✔
390
                if (env_entry_has_name(*f, name)) {
8,938✔
391
                        free_and_replace(*f, p);
810✔
392
                        strv_env_unset(f + 1, *f);
810✔
393
                        return 0;
810✔
394
                }
395

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

401
        return 1;
402
}
403

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

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

411
        return strv_env_replace_consume(l, p);
185✔
412
}
413

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

420
        if (strchr(assignment, '=')) {
17✔
421
                if (!env_assignment_is_valid(assignment))
4✔
422
                        return -EINVAL;
423

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

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

437
        return strv_env_replace_consume(l, p);
11✔
438
}
439

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

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

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

451
        char *p = strjoin(key, "=", value);
2,973✔
452
        if (!p)
2,973✔
453
                return -ENOMEM;
454

455
        return strv_env_replace_consume(l, p);
2,973✔
456
}
457

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

461
        assert(l);
232✔
462
        assert(key);
232✔
463

464
        if (!env_name_is_valid(key))
232✔
465
                return -EINVAL;
232✔
466

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

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

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

484
        return strv_env_replace_consume(l, p);
229✔
485
}
486

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

491
        assert(l);
4✔
492

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

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

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

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

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

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

526
        return 0;
3✔
527
}
528

529
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
783✔
530
        assert(name);
783✔
531

532
        if (k == SIZE_MAX)
783✔
533
                k = strlen(name);
388✔
534
        if (k <= 0)
783✔
535
                return NULL;
536

537
        STRV_FOREACH_BACKWARDS(i, l)
7,010✔
538
                if (strneq(*i, name, k) && (*i)[k] == '=')
3,367✔
539
                        return (char*) *i + k + 1;
507✔
540

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

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

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

552
        return NULL;
553
}
554

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

558
        assert(name);
18,713✔
559

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

564
        return result;
18,713✔
565
}
566

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

572
        assert(ret);
17✔
573

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

579
        STRV_FOREACH_PAIR(key, value, l) {
186✔
580
                char *s;
169✔
581

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

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

591
        *ret = TAKE_PTR(v);
17✔
592
        return 0;
17✔
593
}
594

595
char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
11,074✔
596
        int k = 0;
11,074✔
597

598
        STRV_FOREACH(p, e) {
146,596✔
599
                size_t n;
135,522✔
600
                bool duplicate = false;
135,522✔
601

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

609
                n = strcspn(*p, "=");
135,511✔
610
                STRV_FOREACH(q, p + 1)
965,480✔
611
                        if (strneq(*p, *q, n) && (*q)[n] == '=') {
829,971✔
612
                                duplicate = true;
613
                                break;
614
                        }
615

616
                if (duplicate) {
135,511✔
617
                        free(*p);
2✔
618
                        continue;
2✔
619
                }
620

621
                e[k++] = *p;
135,509✔
622
        }
623

624
        if (e)
11,074✔
625
                e[k] = NULL;
11,074✔
626

627
        return e;
11,074✔
628
}
629

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

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

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

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

649
        char *e;
386✔
650
        int r;
386✔
651

652
        assert(l == 0 || name);
386✔
653
        assert(ret);
386✔
654

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

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

672
        *ret = e;
386✔
673
        return !!e;
386✔
674
}
675

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

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

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

702
        assert(format);
30,107✔
703

704
        if (n == SIZE_MAX)
30,107✔
705
                n = strlen(format);
30,097✔
706

707
        pu = ret_unset_variables ? &unset_variables : NULL;
30,107✔
708
        pb = ret_bad_variables ? &bad_variables : NULL;
30,107✔
709

710
        for (e = format, i = 0; *e && i < n; e++, i++)
514,089✔
711
                switch (state) {
483,982✔
712

713
                case WORD:
478,670✔
714
                        if (*e == '$')
478,670✔
715
                                state = CURLY;
652✔
716
                        break;
717

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

723
                                word = e-1;
387✔
724
                                state = VARIABLE;
387✔
725
                                nest++;
387✔
726

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

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

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

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

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

745
                case VARIABLE:
4,366✔
746
                        if (*e == '}') {
4,366✔
747
                                char *t;
348✔
748

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

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

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

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

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

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

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

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

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

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

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

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

815
                                        t = v;
5✔
816
                                }
817

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

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

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

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

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

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

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

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

853
        if (state == VARIABLE_RAW) {
30,107✔
854
                char *t;
7✔
855

856
                assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
7✔
857

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

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

865
        } else if (!strextendn(&s, word, e-word))
30,100✔
866
                return -ENOMEM;
867

868
        if (ret_unset_variables)
30,107✔
869
                *ret_unset_variables = TAKE_PTR(unset_variables);
30,018✔
870
        if (ret_bad_variables)
30,107✔
871
                *ret_bad_variables = TAKE_PTR(bad_variables);
30,018✔
872

873
        if (ret)
30,107✔
874
                *ret = TAKE_PTR(s);
30,107✔
875

876
        return 0;
877
}
878

879
int replace_env_argv(
10,935✔
880
                char **argv,
881
                char **env,
882
                char ***ret,
883
                char ***ret_unset_variables,
884
                char ***ret_bad_variables) {
885

886
        _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
10,935✔
887
        size_t k = 0, l = 0;
10,935✔
888
        int r;
10,935✔
889

890
        l = strv_length(argv);
10,935✔
891

892
        n = new(char*, l+1);
10,935✔
893
        if (!n)
10,935✔
894
                return -ENOMEM;
895

896
        STRV_FOREACH(i, argv) {
40,973✔
897
                const char *word = *i;
30,038✔
898

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

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

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

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

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

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

938
                _cleanup_strv_free_ char **u = NULL, **b = NULL;
30,033✔
939

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

953
                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
30,033✔
954
                if (r < 0)
30,033✔
955
                        return r;
956

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

962
        if (ret_unset_variables) {
10,935✔
963
                strv_sort_uniq(unset_variables);
10,934✔
964
                *ret_unset_variables = TAKE_PTR(unset_variables);
10,934✔
965
        }
966
        if (ret_bad_variables) {
10,935✔
967
                strv_sort_uniq(bad_variables);
10,934✔
968
                *ret_bad_variables = TAKE_PTR(bad_variables);
10,934✔
969
        }
970

971
        *ret = TAKE_PTR(n);
10,935✔
972
        return 0;
10,935✔
973
}
974

975
int getenv_bool(const char *p) {
274,970✔
976
        const char *e;
274,970✔
977

978
        e = getenv(p);
274,970✔
979
        if (!e)
274,970✔
980
                return -ENXIO;
981

982
        return parse_boolean(e);
1,241✔
983
}
984

985
int secure_getenv_bool(const char *p) {
801,281✔
986
        const char *e;
801,281✔
987

988
        e = secure_getenv(p);
801,281✔
989
        if (!e)
801,281✔
990
                return -ENXIO;
991

992
        return parse_boolean(e);
2,350✔
993
}
994

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

998
        assert(p);
124✔
999

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

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

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

1010
        if (value)
76✔
1011
                return RET_NERRNO(setenv(name, value, overwrite));
32✔
1012

1013
        return RET_NERRNO(unsetenv(name));
44✔
1014
}
1015

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

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

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

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

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

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

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

1039
        if (streq_ptr(e, "*"))
1,374✔
1040
                return 0;
1041

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

1046
        return 1;
1047
}
1048

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

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

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

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

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

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

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

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

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

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

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

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

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

1102
        assert(name);
167✔
1103

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

1108
        e = getenv(name);
167✔
1109
        if (!e) {
167✔
1110
                if (ret)
70✔
1111
                        *ret = NULL;
69✔
1112
                return 0;
70✔
1113
        }
1114

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

1121
        string_erase(e);
97✔
1122

1123
        if (unsetenv(name) < 0)
97✔
1124
                return -errno;
×
1125

1126
        if (ret)
97✔
1127
                *ret = TAKE_PTR(a);
97✔
1128

1129
        return 1;
1130
}
1131

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

1135
        clearenv();
×
1136

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

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

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

1148
        return 0;
1149
}
1150

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

1156
        assert(name);
3,062✔
1157

1158
        if (!valuef)
3,062✔
1159
                return RET_NERRNO(unsetenv(name));
×
1160

1161
        va_start(ap, valuef);
3,062✔
1162
        r = vasprintf(&value, valuef, ap);
3,062✔
1163
        va_end(ap);
3,062✔
1164

1165
        if (r < 0)
3,062✔
1166
                return -ENOMEM;
1167

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

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