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

systemd / systemd / 15057632786

15 May 2025 09:01PM UTC coverage: 72.267% (+0.02%) from 72.244%
15057632786

push

github

bluca
man: document how to hook stuff into system wakeup

Fixes: #6364

298523 of 413084 relevant lines covered (72.27%)

738132.88 hits per line

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

96.06
/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) {
128,693✔
31

32
        if (n == SIZE_MAX)
128,693✔
33
                n = strlen_ptr(e);
×
34

35
        if (n <= 0)
128,693✔
36
                return false;
37

38
        assert(e);
128,684✔
39

40
        if (ascii_isdigit(e[0]))
128,684✔
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)
128,680✔
47
                return false;
48

49
        for (const char *p = e; p < e + n; p++)
1,719,137✔
50
                if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
1,590,490✔
51
                        return false;
52

53
        return true;
54
}
55

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

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

64
        if (!utf8_is_valid(e))
120,494✔
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)
120,493✔
75
                return false;
×
76

77
        return true;
78
}
79

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

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

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

90
        if (!env_value_is_valid(eq + 1))
120,486✔
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)
120,486✔
96
                return false;
×
97

98
        return true;
99
}
100

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

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

108
                /* Check if there are duplicate assignments */
109
                k = strcspn(*p, "=");
203✔
110
                STRV_FOREACH(q, p + 1)
235✔
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) {
101,271✔
143
        assert(e);
101,271✔
144
        assert(k);
101,271✔
145
        assert(*k >= e);
101,271✔
146

147
        if (!a)
101,271✔
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++) {
316,195✔
158
                char **j, *c;
266,774✔
159
                size_t n;
266,774✔
160

161
                n = strcspn(*a, "=");
266,774✔
162
                if ((*a)[n] == '=')
266,774✔
163
                        n++;
266,772✔
164

165
                for (j = e; j < *k; j++)
1,641,128✔
166
                        if (strneq(*j, *a, n))
1,374,442✔
167
                                break;
168

169
                c = strdup(*a);
266,774✔
170
                if (!c)
266,774✔
171
                        return -ENOMEM;
101,271✔
172

173
                if (j >= *k) { /* Append to the end? */
266,774✔
174
                        (*k)[0] = c;
266,686✔
175
                        (*k)[1] = NULL;
266,686✔
176
                        (*k)++;
266,686✔
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, ...) {
31,384✔
185
        _cleanup_strv_free_ char **merged = NULL;
31,384✔
186
        char **k;
31,384✔
187
        va_list ap;
31,384✔
188

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

191
        size_t n = strv_length(first);
31,384✔
192

193
        va_start(ap, first);
31,384✔
194
        for (;;) {
171,158✔
195
                char **l;
101,271✔
196

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

201
                n += strv_length(l);
69,887✔
202
        }
203
        va_end(ap);
31,384✔
204

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

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

213
        va_start(ap, first);
31,384✔
214
        for (;;) {
101,271✔
215
                char **l;
101,271✔
216

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

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

228
        return TAKE_PTR(merged);
31,384✔
229
}
230

231
static bool env_match(const char *t, const char *pattern) {
273,229✔
232
        assert(t);
273,229✔
233
        assert(pattern);
273,229✔
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))
273,229✔
246
                return true;
247

248
        if (!strchr(pattern, '=')) {
273,228✔
249
                t = startswith(t, pattern);
267,365✔
250

251
                return t && *t == '=';
531,772✔
252
        }
253

254
        return false;
255
}
256

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

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

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

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

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

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

278
        n = strv_length(x);
275✔
279

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

284
        STRV_FOREACH(k, x) {
2,901✔
285
                va_start(ap, n_lists);
2,626✔
286
                for (size_t v = 0; v < n_lists; v++) {
4,979✔
287
                        char **l;
2,629✔
288

289
                        l = va_arg(ap, char**);
2,629✔
290
                        STRV_FOREACH(j, l)
10,836✔
291
                                if (env_match(*k, *j))
8,483✔
292
                                        goto skip;
276✔
293
                }
294
                va_end(ap);
2,350✔
295

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

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

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

307
        t[i] = NULL;
275✔
308

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

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

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

317
        if (!l)
2,372✔
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,890✔
325
                if (env_match(*f, p)) {
7,520✔
326
                        free(*f);
112✔
327
                        continue;
112✔
328
                }
329

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

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

337
char** strv_env_unset_many_internal(char **l, ...) {
1,596✔
338
        if (!l)
1,596✔
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,367✔
345
                bool found = false;
13,771✔
346
                const char *p;
13,771✔
347
                va_list ap;
13,771✔
348

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

351
                while ((p = va_arg(ap, const char*)))
268,426✔
352
                        if (env_match(*f, p)) {
257,226✔
353
                                found = true;
354
                                break;
355
                        }
356

357
                va_end(ap);
13,771✔
358

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

364
                *(t++) = *f;
11,200✔
365
        }
366

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

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

375
        assert(p);
7,094✔
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, '=');
7,094✔
383
        if (!t) {
7,094✔
384
                free(p);
1✔
385
                return -EINVAL;
1✔
386
        }
387

388
        name = strndupa_safe(p, t - p);
7,093✔
389

390
        STRV_FOREACH(f, *l)
17,405✔
391
                if (env_entry_has_name(*f, name)) {
11,587✔
392
                        free_and_replace(*f, p);
1,275✔
393
                        strv_env_unset(f + 1, *f);
1,275✔
394
                        return 0;
1,275✔
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,818✔
399
        if (r < 0)
5,818✔
400
                return r;
×
401

402
        return 1;
403
}
404

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

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

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

415
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
34✔
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;
34✔
420

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

425
                p = strdup(assignment);
21✔
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)
28✔
436
                return -ENOMEM;
437

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

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

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

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

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

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

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

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

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

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

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

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

485
        return strv_env_replace_consume(l, p);
319✔
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,494✔
531
        assert(name);
1,494✔
532

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

538
        STRV_FOREACH_BACKWARDS(i, l)
9,777✔
539
                if (strneq(*i, name, k) && (*i)[k] == '=')
4,198✔
540
                        return (char*) *i + k + 1;
620✔
541

542
        if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
874✔
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) {
24✔
569
        _cleanup_strv_free_ char **v = NULL;
24✔
570
        size_t n = 0;
24✔
571
        int r;
24✔
572

573
        assert(ret);
24✔
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) {
226✔
581
                char *s;
202✔
582

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

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

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

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

599
        STRV_FOREACH(p, e) {
127,813✔
600
                size_t n;
118,216✔
601
                bool duplicate = false;
118,216✔
602

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

610
                n = strcspn(*p, "=");
118,205✔
611
                STRV_FOREACH(q, p + 1)
855,690✔
612
                        if (strneq(*p, *q, n) && (*q)[n] == '=') {
737,487✔
613
                                duplicate = true;
614
                                break;
615
                        }
616

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

622
                e[k++] = *p;
118,203✔
623
        }
624

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

628
        return e;
9,597✔
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(
437✔
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;
437✔
651
        int r;
437✔
652

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

656
        if (env_name_is_valid_n(name, l)) {
437✔
657
                e = strv_env_get_n(env, name, l, flags);
434✔
658
                if (!e && unset_variables) {
434✔
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;
437✔
674
        return !!e;
437✔
675
}
676

677
int replace_env_full(
26,992✔
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,992✔
687
                WORD,
688
                CURLY,
689
                VARIABLE,
690
                VARIABLE_RAW,
691
                TEST,
692
                DEFAULT_VALUE,
693
                ALTERNATE_VALUE,
694
        } state = WORD;
26,992✔
695

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

703
        assert(format);
26,992✔
704

705
        if (n == SIZE_MAX)
26,992✔
706
                n = strlen(format);
26,982✔
707

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

711
        for (e = format, i = 0; *e && i < n; e++, i++)
449,999✔
712
                switch (state) {
423,007✔
713

714
                case WORD:
416,970✔
715
                        if (*e == '$')
416,970✔
716
                                state = CURLY;
701✔
717
                        break;
718

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

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

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

732
                                word = e+1;
52✔
733
                                state = WORD;
52✔
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:
5,042✔
747
                        if (*e == '}') {
5,042✔
748
                                char *t;
399✔
749

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

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

757
                                word = e+1;
399✔
758
                                state = WORD;
399✔
759
                                nest--;
399✔
760
                        } else if (*e == ':') {
4,643✔
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,992✔
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,985✔
867
                return -ENOMEM;
868

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

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

877
        return 0;
878
}
879

880
int replace_env_argv(
9,414✔
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,414✔
888
        size_t k = 0, l;
9,414✔
889
        int r;
9,414✔
890

891
        assert(!strv_isempty(argv));
9,414✔
892
        assert(ret);
9,414✔
893

894
        l = strv_length(argv);
9,414✔
895

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

900
        STRV_FOREACH(i, argv) {
36,337✔
901
                const char *word = *i;
26,923✔
902

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

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

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

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

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

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

942
                _cleanup_strv_free_ char **u = NULL, **b = NULL;
26,918✔
943

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

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

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

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

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

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

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

986
        return parse_boolean(e);
1,365✔
987
}
988

989
int secure_getenv_bool(const char *p) {
723,791✔
990
        const char *e;
723,791✔
991

992
        e = secure_getenv(p);
723,791✔
993
        if (!e)
723,791✔
994
                return -ENXIO;
995

996
        return parse_boolean(e);
2,382✔
997
}
998

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

1002
        assert(p);
130✔
1003

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

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

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

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

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

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

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

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

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

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

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

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

1043
        if (streq_ptr(e, "*"))
1,549✔
1044
                return 0;
1045

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

1050
        return 1;
1051
}
1052

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

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

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

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

1069
        assert(name);
1,169✔
1070
        assert(ret_paths);
1,169✔
1071

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

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

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

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

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

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

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

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

1106
        assert(name);
193✔
1107

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

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

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

1125
        string_erase(e);
107✔
1126

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

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

1133
        return 1;
1134
}
1135

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

1141
        assert(name);
2,655✔
1142

1143
        if (!valuef)
2,655✔
1144
                return RET_NERRNO(unsetenv(name));
×
1145

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

1150
        if (r < 0)
2,655✔
1151
                return -ENOMEM;
1152

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

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