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

systemd / systemd / 15357952084

30 May 2025 07:44PM UTC coverage: 72.024% (-0.04%) from 72.064%
15357952084

push

github

yuwata
terminal-util: fix typo

Follow-up for 5321b957b.

299538 of 415888 relevant lines covered (72.02%)

703250.55 hits per line

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

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

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

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

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

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

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

33
        if (n == SIZE_MAX)
128,373✔
34
                n = strlen_ptr(e);
7,791✔
35

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

39
        assert(e);
128,364✔
40

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

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

50
        for (const char *p = e; p < e + n; p++)
1,718,481✔
51
                if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
1,590,154✔
52
                        return false;
53

54
        return true;
55
}
56

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

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

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

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

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

78
        return true;
79
}
80

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

84
        assert(e);
121,826✔
85

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

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

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

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

101
        return true;
102
}
103

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

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

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

118
        return true;
119
}
120

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

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

130
        return true;
131
}
132

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

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

142
        return true;
143
}
144

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

150
        if (!a)
101,313✔
151
                return 0;
152

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

160
        for (; *a; a++) {
316,019✔
161
                char **j, *c;
266,557✔
162
                size_t n;
266,557✔
163

164
                n = strcspn(*a, "=");
266,557✔
165
                if ((*a)[n] == '=')
266,557✔
166
                        n++;
266,555✔
167

168
                for (j = e; j < *k; j++)
1,639,148✔
169
                        if (strneq(*j, *a, n))
1,372,665✔
170
                                break;
171

172
                c = strdup(*a);
266,557✔
173
                if (!c)
266,557✔
174
                        return -ENOMEM;
101,313✔
175

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

184
        return 0;
185
}
186

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

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

194
        size_t n = strv_length(first);
31,401✔
195

196
        va_start(ap, first);
31,401✔
197
        for (;;) {
171,225✔
198
                char **l;
101,313✔
199

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

204
                n += strv_length(l);
69,912✔
205
        }
206
        va_end(ap);
31,401✔
207

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

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

216
        va_start(ap, first);
31,401✔
217
        for (;;) {
101,313✔
218
                char **l;
101,313✔
219

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

224
                if (env_append(merged, &k, l) < 0) {
69,912✔
225
                        va_end(ap);
×
226
                        return NULL;
×
227
                }
228
        }
229
        va_end(ap);
31,401✔
230

231
        return TAKE_PTR(merged);
31,401✔
232
}
233

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

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

248
        if (streq(t, pattern))
269,679✔
249
                return true;
250

251
        if (!strchr(pattern, '=')) {
269,678✔
252
                t = startswith(t, pattern);
263,875✔
253

254
                return t && *t == '=';
524,826✔
255
        }
256

257
        return false;
258
}
259

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

263
        assert(entry);
15,283✔
264
        assert(name);
15,283✔
265

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

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

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

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

281
        n = strv_length(x);
269✔
282

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

287
        STRV_FOREACH(k, x) {
2,790✔
288
                va_start(ap, n_lists);
2,521✔
289
                for (size_t v = 0; v < n_lists; v++) {
4,775✔
290
                        char **l;
2,524✔
291

292
                        l = va_arg(ap, char**);
2,524✔
293
                        STRV_FOREACH(j, l)
9,345✔
294
                                if (env_match(*k, *j))
7,091✔
295
                                        goto skip;
270✔
296
                }
297
                va_end(ap);
2,251✔
298

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

303
                i++;
2,251✔
304
                continue;
2,251✔
305

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

310
        t[i] = NULL;
269✔
311

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

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

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

320
        if (!l)
2,368✔
321
                return NULL;
322

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

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

333
                *(t++) = *f;
7,348✔
334
        }
335

336
        *t = NULL;
2,366✔
337
        return l;
2,366✔
338
}
339

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

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

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

352
                va_start(ap, l);
13,643✔
353

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

360
                va_end(ap);
13,643✔
361

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

367
                *(t++) = *f;
11,100✔
368
        }
369

370
        *t = NULL;
1,588✔
371
        return l;
1,588✔
372
}
373

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

378
        assert(l);
7,531✔
379
        assert(p);
7,531✔
380

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

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

392
        name = strndupa_safe(p, t - p);
7,530✔
393

394
        STRV_FOREACH(f, *l)
21,542✔
395
                if (env_entry_has_name(*f, name)) {
15,283✔
396
                        free_and_replace(*f, p);
1,271✔
397
                        strv_env_unset(f + 1, *f);
1,271✔
398
                        return 0;
1,271✔
399
                }
400

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

406
        return 1;
407
}
408

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

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

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

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

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

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

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

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

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

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

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

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

454
        if (!env_name_is_valid(key))
4,355✔
455
                return -EINVAL;
456

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

459
        if (!value) {
4,354✔
460
                strv_env_unset(*l, key);
1,093✔
461
                return 0;
1,093✔
462
        }
463

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

468
        return strv_env_replace_consume(l, p);
3,261✔
469
}
470

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

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

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

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

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

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

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

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

504
        assert(l);
4✔
505

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

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

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

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

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

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

539
        return 0;
3✔
540
}
541

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

545
        if (k == SIZE_MAX)
11,048✔
546
                k = strlen(name);
10,605✔
547
        if (k <= 0)
11,048✔
548
                return NULL;
549

550
        STRV_FOREACH_BACKWARDS(i, l)
249,531✔
551
                if (strneq(*i, name, k) && (*i)[k] == '=')
119,389✔
552
                        return (char*) *i + k + 1;
762✔
553

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

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

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

565
        return NULL;
566
}
567

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

571
        assert(name);
18,747✔
572

573
        STRV_FOREACH_PAIR(key, value, l)
164,211✔
574
                if (streq(*key, name))
145,464✔
575
                        result = *value;
6,128✔
576

577
        return result;
18,747✔
578
}
579

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

585
        assert(ret);
24✔
586

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

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

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

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

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

608
char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
9,599✔
609
        int k = 0;
9,599✔
610

611
        STRV_FOREACH(p, e) {
127,451✔
612
                size_t n;
117,852✔
613
                bool duplicate = false;
117,852✔
614

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

622
                n = strcspn(*p, "=");
117,841✔
623
                STRV_FOREACH(q, p + 1)
852,402✔
624
                        if (strneq(*p, *q, n) && (*q)[n] == '=') {
734,563✔
625
                                duplicate = true;
626
                                break;
627
                        }
628

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

634
                e[k++] = *p;
117,839✔
635
        }
636

637
        if (e)
9,599✔
638
                e[k] = NULL;
9,599✔
639

640
        return e;
9,599✔
641
}
642

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

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

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

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

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

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

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

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

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

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

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

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

715
        assert(format);
26,987✔
716

717
        if (n == SIZE_MAX)
26,987✔
718
                n = strlen(format);
26,977✔
719

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

723
        for (e = format, i = 0; *e && i < n; e++, i++)
450,810✔
724
                switch (state) {
423,823✔
725

726
                case WORD:
417,770✔
727
                        if (*e == '$')
417,770✔
728
                                state = CURLY;
699✔
729
                        break;
730

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

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

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

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

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

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

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

758
                case VARIABLE:
5,060✔
759
                        if (*e == '}') {
5,060✔
760
                                char *t;
396✔
761

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

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

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

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

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

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

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

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

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

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

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

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

828
                                        t = v;
5✔
829
                                }
830

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

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

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

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

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

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

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

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

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

869
                assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
7✔
870

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

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

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

881
        if (ret_unset_variables)
26,987✔
882
                *ret_unset_variables = TAKE_PTR(unset_variables);
26,898✔
883
        if (ret_bad_variables)
26,987✔
884
                *ret_bad_variables = TAKE_PTR(bad_variables);
26,898✔
885

886
        if (ret)
26,987✔
887
                *ret = TAKE_PTR(s);
26,987✔
888

889
        return 0;
890
}
891

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

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

903
        assert(!strv_isempty(argv));
9,415✔
904
        assert(ret);
9,415✔
905

906
        l = strv_length(argv);
9,415✔
907

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

912
        STRV_FOREACH(i, argv) {
36,333✔
913
                const char *word = *i;
26,918✔
914

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

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

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

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

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

950
                        k += q;
5✔
951
                        continue;
5✔
952
                }
953

954
                _cleanup_strv_free_ char **u = NULL, **b = NULL;
26,913✔
955

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

969
                r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
26,913✔
970
                if (r < 0)
26,913✔
971
                        return r;
972

973
                r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
26,913✔
974
                if (r < 0)
26,913✔
975
                        return r;
976
        }
977

978
        if (ret_unset_variables) {
9,415✔
979
                strv_sort_uniq(unset_variables);
9,414✔
980
                *ret_unset_variables = TAKE_PTR(unset_variables);
9,414✔
981
        }
982
        if (ret_bad_variables) {
9,415✔
983
                strv_sort_uniq(bad_variables);
9,414✔
984
                *ret_bad_variables = TAKE_PTR(bad_variables);
9,414✔
985
        }
986

987
        *ret = TAKE_PTR(n);
9,415✔
988
        return 0;
9,415✔
989
}
990

991
int getenv_bool(const char *p) {
258,432✔
992
        const char *e;
258,432✔
993

994
        e = getenv(p);
258,432✔
995
        if (!e)
258,432✔
996
                return -ENXIO;
997

998
        return parse_boolean(e);
1,348✔
999
}
1000

1001
int secure_getenv_bool(const char *p) {
725,427✔
1002
        const char *e;
725,427✔
1003

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

1008
        return parse_boolean(e);
2,428✔
1009
}
1010

1011
int secure_getenv_uint64(const char *p, uint64_t *ret) {
130✔
1012
        const char *e;
130✔
1013

1014
        assert(p);
130✔
1015

1016
        e = secure_getenv(p);
130✔
1017
        if (!e)
130✔
1018
                return -ENXIO;
1019

1020
        return safe_atou64(e, ret);
×
1021
}
1022

1023
int set_unset_env(const char *name, const char *value, bool overwrite) {
78✔
1024
        assert(name);
78✔
1025

1026
        if (value)
78✔
1027
                return RET_NERRNO(setenv(name, value, overwrite));
33✔
1028

1029
        return RET_NERRNO(unsetenv(name));
45✔
1030
}
1031

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

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

1039
        n = strndupa_safe(assignment, e - assignment);
4✔
1040

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

1045
int setenv_systemd_exec_pid(bool update_only) {
1,548✔
1046
        const char *e;
1,548✔
1047
        int r;
1,548✔
1048

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

1051
        e = secure_getenv("SYSTEMD_EXEC_PID");
1,548✔
1052
        if (!e && update_only)
1,548✔
1053
                return 0;
1054

1055
        if (streq_ptr(e, "*"))
1,547✔
1056
                return 0;
1057

1058
        r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
1,546✔
1059
        if (r < 0)
1,546✔
1060
                return r;
×
1061

1062
        return 1;
1063
}
1064

1065
int setenv_systemd_log_level(void) {
2,458✔
1066
        _cleanup_free_ char *val = NULL;
2,458✔
1067
        int r;
2,458✔
1068

1069
        r = log_level_to_string_alloc(log_get_max_level(), &val);
2,458✔
1070
        if (r < 0)
2,458✔
1071
                return r;
1072

1073
        return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
2,458✔
1074
}
1075

1076
int getenv_path_list(const char *name, char ***ret_paths) {
1,201✔
1077
        _cleanup_strv_free_ char **l = NULL;
1,201✔
1078
        const char *e;
1,201✔
1079
        int r;
1,201✔
1080

1081
        assert(name);
1,201✔
1082
        assert(ret_paths);
1,201✔
1083

1084
        e = secure_getenv(name);
1,201✔
1085
        if (!e)
1,201✔
1086
                return -ENXIO;
1087

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

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

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

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

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

1110
        *ret_paths = TAKE_PTR(l);
1✔
1111
        return 1;
1✔
1112
}
1113

1114
int getenv_steal_erase(const char *name, char **ret) {
193✔
1115
        _cleanup_(erase_and_freep) char *a = NULL;
193✔
1116
        char *e;
193✔
1117

1118
        assert(name);
193✔
1119

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

1124
        e = secure_getenv(name);
193✔
1125
        if (!e) {
193✔
1126
                if (ret)
86✔
1127
                        *ret = NULL;
85✔
1128
                return 0;
86✔
1129
        }
1130

1131
        if (ret) {
107✔
1132
                a = strdup(e);
107✔
1133
                if (!a)
107✔
1134
                        return -ENOMEM;
1135
        }
1136

1137
        string_erase(e);
107✔
1138

1139
        if (unsetenv(name) < 0)
107✔
1140
                return -errno;
×
1141

1142
        if (ret)
107✔
1143
                *ret = TAKE_PTR(a);
107✔
1144

1145
        return 1;
1146
}
1147

1148
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
2,653✔
1149
        _cleanup_free_ char *value = NULL;
2,653✔
1150
        va_list ap;
2,653✔
1151
        int r;
2,653✔
1152

1153
        assert(name);
2,653✔
1154

1155
        if (!valuef)
2,653✔
1156
                return RET_NERRNO(unsetenv(name));
×
1157

1158
        va_start(ap, valuef);
2,653✔
1159
        r = vasprintf(&value, valuef, ap);
2,653✔
1160
        va_end(ap);
2,653✔
1161

1162
        if (r < 0)
2,653✔
1163
                return -ENOMEM;
1164

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

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