]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/env-util.c
list: make LIST_FOREACH() and LIST_FOREACH_BACKWARDS() safer
[thirdparty/systemd.git] / src / basic / env-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4d1a6904 2
11c3a366 3#include <errno.h>
4d1a6904 4#include <limits.h>
11c3a366
TA
5#include <stdarg.h>
6#include <stdlib.h>
4d1a6904
LP
7#include <unistd.h>
8
b5efdb8a 9#include "alloc-util.h"
b5efdb8a 10#include "env-util.h"
7c248223 11#include "errno-util.h"
fe902fa4 12#include "escape.h"
11c3a366
TA
13#include "extract-word.h"
14#include "macro.h"
93cc7779 15#include "parse-util.h"
93547f28 16#include "path-util.h"
cfd1c6e2
YW
17#include "process-util.h"
18#include "stdio-util.h"
07630cea 19#include "string-util.h"
4d1a6904
LP
20#include "strv.h"
21#include "utf8.h"
4d1a6904 22
b45c068d
ZJS
23/* We follow bash for the character set. Different shells have different rules. */
24#define VALID_BASH_ENV_NAME_CHARS \
4b549144 25 DIGITS LETTERS \
4d1a6904
LP
26 "_"
27
b45c068d 28static bool env_name_is_valid_n(const char *e, size_t n) {
4d1a6904
LP
29 if (!e)
30 return false;
31
32 if (n <= 0)
33 return false;
34
ff461576
ZJS
35 if (e[0] >= '0' && e[0] <= '9')
36 return false;
37
4d1a6904
LP
38 /* POSIX says the overall size of the environment block cannot
39 * be > ARG_MAX, an individual assignment hence cannot be
40 * either. Discounting the equal sign and trailing NUL this
41 * hence leaves ARG_MAX-2 as longest possible variable
42 * name. */
d3e66e68 43 if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
4d1a6904
LP
44 return false;
45
5b935a38 46 for (const char *p = e; p < e + n; p++)
ff461576 47 if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
4d1a6904
LP
48 return false;
49
50 return true;
51}
52
53bool env_name_is_valid(const char *e) {
451ae5a1 54 return env_name_is_valid_n(e, strlen_ptr(e));
4d1a6904
LP
55}
56
57bool env_value_is_valid(const char *e) {
58 if (!e)
59 return false;
60
61 if (!utf8_is_valid(e))
62 return false;
63
30927a24
ZJS
64 /* Note that variable *values* may contain control characters, in particular NL, TAB, BS, DEL, ESC…
65 * When printing those variables with show-environment, we'll escape them. Make sure to print
66 * environment variables carefully! */
4d1a6904 67
30927a24
ZJS
68 /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
69 * hence cannot be either. Discounting the shortest possible variable name of length 1, the equal
70 * sign and trailing NUL this hence leaves ARG_MAX-3 as longest possible variable value. */
21c491e1 71 if (strlen(e) > sc_arg_max() - 3)
4d1a6904
LP
72 return false;
73
74 return true;
75}
76
77bool env_assignment_is_valid(const char *e) {
78 const char *eq;
79
80 eq = strchr(e, '=');
81 if (!eq)
82 return false;
83
84 if (!env_name_is_valid_n(e, eq - e))
85 return false;
86
87 if (!env_value_is_valid(eq + 1))
88 return false;
89
30927a24
ZJS
90 /* POSIX says the overall size of the environment block cannot be > ARG_MAX, hence the individual
91 * variable assignments cannot be either, but let's leave room for one trailing NUL byte. */
21c491e1 92 if (strlen(e) > sc_arg_max() - 1)
4d1a6904
LP
93 return false;
94
95 return true;
96}
97
98bool strv_env_is_valid(char **e) {
99 char **p, **q;
100
101 STRV_FOREACH(p, e) {
102 size_t k;
103
104 if (!env_assignment_is_valid(*p))
105 return false;
106
f21f31b2 107 /* Check if there are duplicate assignments */
4d1a6904
LP
108 k = strcspn(*p, "=");
109 STRV_FOREACH(q, p + 1)
641906e9 110 if (strneq(*p, *q, k) && (*q)[k] == '=')
4d1a6904
LP
111 return false;
112 }
113
114 return true;
115}
116
b4c14404 117bool strv_env_name_is_valid(char **l) {
68ac147e 118 char **p;
b4c14404
FB
119
120 STRV_FOREACH(p, l) {
121 if (!env_name_is_valid(*p))
122 return false;
123
68ac147e
LP
124 if (strv_contains(p + 1, *p))
125 return false;
b4c14404
FB
126 }
127
128 return true;
129}
130
123b964a 131bool strv_env_name_or_assignment_is_valid(char **l) {
68ac147e 132 char **p;
123b964a
LP
133
134 STRV_FOREACH(p, l) {
135 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
136 return false;
137
68ac147e
LP
138 if (strv_contains(p + 1, *p))
139 return false;
123b964a
LP
140 }
141
142 return true;
143}
144
4d1a6904
LP
145static int env_append(char **r, char ***k, char **a) {
146 assert(r);
147 assert(k);
2a13184a 148 assert(*k >= r);
4d1a6904
LP
149
150 if (!a)
151 return 0;
152
2a13184a
LP
153 /* Expects the following arguments: 'r' 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 'r', either overriding an existing matching entry, or appending to it.
157 *
158 * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */
4d1a6904
LP
159
160 for (; *a; a++) {
2a13184a 161 char **j, *c;
4d1a6904
LP
162 size_t n;
163
164 n = strcspn(*a, "=");
4d1a6904
LP
165 if ((*a)[n] == '=')
166 n++;
167
168 for (j = r; j < *k; j++)
641906e9 169 if (strneq(*j, *a, n))
4d1a6904
LP
170 break;
171
2a13184a
LP
172 c = strdup(*a);
173 if (!c)
4d1a6904 174 return -ENOMEM;
2a13184a
LP
175
176 if (j >= *k) { /* Append to the end? */
177 (*k)[0] = c;
178 (*k)[1] = NULL;
179 (*k)++;
180 } else
181 free_and_replace(*j, c); /* Override existing item */
4d1a6904
LP
182 }
183
184 return 0;
185}
186
4ab3d29f
ZJS
187char** _strv_env_merge(char **first, ...) {
188 _cleanup_strv_free_ char **merged = NULL;
189 char **k;
4d1a6904 190 va_list ap;
4d1a6904
LP
191
192 /* Merges an arbitrary number of environment sets */
193
4ab3d29f
ZJS
194 size_t n = strv_length(first);
195
196 va_start(ap, first);
197 for (;;) {
198 char **l;
199
4d1a6904 200 l = va_arg(ap, char**);
4ab3d29f
ZJS
201 if (l == POINTER_MAX)
202 break;
203
4d1a6904
LP
204 n += strv_length(l);
205 }
206 va_end(ap);
207
4ab3d29f
ZJS
208 k = merged = new(char*, n + 1);
209 if (!merged)
4d1a6904 210 return NULL;
4ab3d29f 211 merged[0] = NULL;
4d1a6904 212
4ab3d29f
ZJS
213 if (env_append(merged, &k, first) < 0)
214 return NULL;
215
216 va_start(ap, first);
217 for (;;) {
218 char **l;
4d1a6904 219
4d1a6904 220 l = va_arg(ap, char**);
4ab3d29f
ZJS
221 if (l == POINTER_MAX)
222 break;
223
224 if (env_append(merged, &k, l) < 0) {
2d3ff1de
LP
225 va_end(ap);
226 return NULL;
227 }
4d1a6904
LP
228 }
229 va_end(ap);
230
4ab3d29f 231 return TAKE_PTR(merged);
4d1a6904
LP
232}
233
c8cebc36 234static bool env_match(const char *t, const char *pattern) {
4d1a6904
LP
235 assert(t);
236 assert(pattern);
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))
249 return true;
250
251 if (!strchr(pattern, '=')) {
252 size_t l = strlen(pattern);
253
641906e9 254 return strneq(t, pattern, l) && t[l] == '=';
4d1a6904
LP
255 }
256
257 return false;
258}
259
99003e01
ZJS
260static bool env_entry_has_name(const char *entry, const char *name) {
261 const char *t;
262
263 assert(entry);
264 assert(name);
265
266 t = startswith(entry, name);
267 if (!t)
268 return false;
269
270 return *t == '=';
271}
272
da6053d0 273char **strv_env_delete(char **x, size_t n_lists, ...) {
4d1a6904
LP
274 size_t n, i = 0;
275 char **k, **r;
276 va_list ap;
277
278 /* Deletes every entry from x that is mentioned in the other
279 * string lists */
280
281 n = strv_length(x);
282
283 r = new(char*, n+1);
284 if (!r)
285 return NULL;
286
287 STRV_FOREACH(k, x) {
4d1a6904 288 va_start(ap, n_lists);
5b935a38 289 for (size_t v = 0; v < n_lists; v++) {
4d1a6904
LP
290 char **l, **j;
291
292 l = va_arg(ap, char**);
293 STRV_FOREACH(j, l)
294 if (env_match(*k, *j))
295 goto skip;
296 }
297 va_end(ap);
298
299 r[i] = strdup(*k);
300 if (!r[i]) {
301 strv_free(r);
302 return NULL;
303 }
304
305 i++;
306 continue;
307
308 skip:
309 va_end(ap);
310 }
311
312 r[i] = NULL;
313
314 assert(i <= n);
315
316 return r;
317}
318
319char **strv_env_unset(char **l, const char *p) {
4d1a6904
LP
320 char **f, **t;
321
322 if (!l)
323 return NULL;
324
325 assert(p);
326
327 /* Drops every occurrence of the env var setting p in the
43d03a83 328 * string list. Edits in-place. */
4d1a6904
LP
329
330 for (f = t = l; *f; f++) {
331
332 if (env_match(*f, p)) {
333 free(*f);
334 continue;
335 }
336
337 *(t++) = *f;
338 }
43d03a83
LP
339
340 *t = NULL;
341 return l;
342}
343
344char **strv_env_unset_many(char **l, ...) {
43d03a83
LP
345 char **f, **t;
346
347 if (!l)
348 return NULL;
349
350 /* Like strv_env_unset() but applies many at once. Edits in-place. */
351
352 for (f = t = l; *f; f++) {
353 bool found = false;
354 const char *p;
355 va_list ap;
356
357 va_start(ap, l);
358
359 while ((p = va_arg(ap, const char*))) {
360 if (env_match(*f, p)) {
361 found = true;
362 break;
363 }
364 }
365
366 va_end(ap);
367
368 if (found) {
369 free(*f);
370 continue;
371 }
372
373 *(t++) = *f;
374 }
4d1a6904
LP
375
376 *t = NULL;
377 return l;
378}
379
13734c75 380int strv_env_replace_consume(char ***l, char *p) {
99003e01 381 const char *t, *name;
04effffd
LP
382 char **f;
383 int r;
54ac3494
ZJS
384
385 assert(p);
386
aaf057c4 387 /* Replace first occurrence of the env var or add a new one in the string list. Drop other
13734c75
ZJS
388 * occurrences. Edits in-place. Does not copy p and CONSUMES p EVEN ON FAILURE.
389 *
390 * p must be a valid key=value assignment. */
54ac3494 391
99003e01 392 t = strchr(p, '=');
13734c75
ZJS
393 if (!t) {
394 free(p);
04effffd 395 return -EINVAL;
13734c75 396 }
99003e01 397
2f82562b 398 name = strndupa_safe(p, t - p);
99003e01 399
04effffd 400 STRV_FOREACH(f, *l)
99003e01
ZJS
401 if (env_entry_has_name(*f, name)) {
402 free_and_replace(*f, p);
403 strv_env_unset(f + 1, *f);
54ac3494
ZJS
404 return 0;
405 }
406
407 /* We didn't find a match, we need to append p or create a new strv */
13734c75 408 r = strv_consume(l, p);
04effffd
LP
409 if (r < 0)
410 return r;
411
54ac3494
ZJS
412 return 1;
413}
414
aaf057c4 415int strv_env_replace_strdup(char ***l, const char *assignment) {
13734c75 416 /* Like strv_env_replace_consume(), but copies the argument. */
aaf057c4 417
13734c75 418 char *p = strdup(assignment);
aaf057c4
ZJS
419 if (!p)
420 return -ENOMEM;
421
13734c75 422 return strv_env_replace_consume(l, p);
f08231fe
ZJS
423}
424
a14af47e
ZJS
425int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
426 /* Like strv_env_replace_strdup(), but pulls the variable from the environment of
427 * the calling program, if a variable name without value is specified.
428 */
429 char *p;
430
431 if (strchr(assignment, '=')) {
432 if (!env_assignment_is_valid(assignment))
433 return -EINVAL;
434
435 p = strdup(assignment);
436 } else {
437 if (!env_name_is_valid(assignment))
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));
444 }
445 if (!p)
446 return -ENOMEM;
447
448 return strv_env_replace_consume(l, p);
449}
450
f08231fe 451int strv_env_assign(char ***l, const char *key, const char *value) {
f08231fe
ZJS
452 if (!env_name_is_valid(key))
453 return -EINVAL;
454
455 /* NULL removes assignment, "" creates an empty assignment. */
456
457 if (!value) {
458 strv_env_unset(*l, key);
459 return 0;
460 }
461
462 char *p = strjoin(key, "=", value);
463 if (!p)
464 return -ENOMEM;
465
13734c75 466 return strv_env_replace_consume(l, p);
aaf057c4
ZJS
467}
468
37f3ffca 469char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
4d1a6904
LP
470 char **i;
471
472 assert(name);
473
474 if (k <= 0)
475 return NULL;
476
6162512c 477 STRV_FOREACH_BACKWARDS(i, l)
641906e9 478 if (strneq(*i, name, k) &&
4d1a6904
LP
479 (*i)[k] == '=')
480 return *i + k + 1;
481
37f3ffca
RS
482 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
483 const char *t;
484
2f82562b 485 t = strndupa_safe(name, k);
37f3ffca
RS
486 return getenv(t);
487 };
488
4d1a6904
LP
489 return NULL;
490}
491
492char *strv_env_get(char **l, const char *name) {
493 assert(name);
494
37f3ffca 495 return strv_env_get_n(l, name, strlen(name), 0);
4d1a6904
LP
496}
497
42e6a77b
LB
498char *strv_env_pairs_get(char **l, const char *name) {
499 char **key, **value, *result = NULL;
500
501 assert(name);
502
503 STRV_FOREACH_PAIR(key, value, l)
504 if (streq(*key, name))
505 result = *value;
506
507 return result;
508}
509
039f0e70 510char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
4d1a6904
LP
511 char **p, **q;
512 int k = 0;
513
514 STRV_FOREACH(p, e) {
515 size_t n;
516 bool duplicate = false;
517
518 if (!env_assignment_is_valid(*p)) {
039f0e70
LP
519 if (invalid_callback)
520 invalid_callback(*p, userdata);
4d1a6904
LP
521 free(*p);
522 continue;
523 }
524
525 n = strcspn(*p, "=");
526 STRV_FOREACH(q, p + 1)
641906e9 527 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
528 duplicate = true;
529 break;
530 }
531
532 if (duplicate) {
533 free(*p);
534 continue;
535 }
536
537 e[k++] = *p;
538 }
539
5b4fb02d
LP
540 if (e)
541 e[k] = NULL;
542
4d1a6904
LP
543 return e;
544}
3c800095 545
4bed076c 546char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
3c800095
RC
547 enum {
548 WORD,
549 CURLY,
ccad1fd0
ZJS
550 VARIABLE,
551 VARIABLE_RAW,
b82f58bf
RS
552 TEST,
553 DEFAULT_VALUE,
554 ALTERNATE_VALUE,
3c800095
RC
555 } state = WORD;
556
1dbd0bdb 557 const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
cb4499d0
ZJS
558 char *k;
559 _cleanup_free_ char *r = NULL;
1dbd0bdb 560 size_t i, len = 0; /* len is initialized to appease gcc */
b82f58bf 561 int nest = 0;
3c800095
RC
562
563 assert(format);
564
00d4b1e6 565 for (e = format, i = 0; *e && i < n; e ++, i ++)
3c800095
RC
566 switch (state) {
567
568 case WORD:
569 if (*e == '$')
570 state = CURLY;
571 break;
572
573 case CURLY:
574 if (*e == '{') {
575 k = strnappend(r, word, e-word-1);
576 if (!k)
cb4499d0 577 return NULL;
3c800095 578
00d4b1e6 579 free_and_replace(r, k);
3c800095
RC
580
581 word = e-1;
582 state = VARIABLE;
b82f58bf 583 nest++;
3c800095
RC
584 } else if (*e == '$') {
585 k = strnappend(r, word, e-word);
586 if (!k)
cb4499d0 587 return NULL;
3c800095 588
00d4b1e6 589 free_and_replace(r, k);
3c800095
RC
590
591 word = e+1;
592 state = WORD;
ccad1fd0 593
b45c068d 594 } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
ccad1fd0
ZJS
595 k = strnappend(r, word, e-word-1);
596 if (!k)
597 return NULL;
598
00d4b1e6 599 free_and_replace(r, k);
ccad1fd0
ZJS
600
601 word = e-1;
602 state = VARIABLE_RAW;
603
3c800095
RC
604 } else
605 state = WORD;
606 break;
607
608 case VARIABLE:
609 if (*e == '}') {
610 const char *t;
611
37f3ffca 612 t = strv_env_get_n(env, word+2, e-word-2, flags);
3c800095 613
2b070200 614 if (!strextend(&r, t))
cb4499d0 615 return NULL;
3c800095 616
b82f58bf
RS
617 word = e+1;
618 state = WORD;
5ef97a71 619 nest--;
b82f58bf 620 } else if (*e == ':') {
1dbd0bdb
ZJS
621 if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
622 len = e - word - 2;
623 state = TEST;
624 } else
b82f58bf
RS
625 /* Treat this as unsupported syntax, i.e. do no replacement */
626 state = WORD;
b82f58bf
RS
627 }
628 break;
629
630 case TEST:
631 if (*e == '-')
632 state = DEFAULT_VALUE;
633 else if (*e == '+')
634 state = ALTERNATE_VALUE;
635 else {
636 state = WORD;
637 break;
638 }
639
640 test_value = e+1;
641 break;
642
643 case DEFAULT_VALUE: /* fall through */
644 case ALTERNATE_VALUE:
645 assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
646
647 if (*e == '{') {
648 nest++;
649 break;
650 }
651
652 if (*e != '}')
653 break;
654
655 nest--;
1d046f57 656 if (nest == 0) {
b82f58bf
RS
657 const char *t;
658 _cleanup_free_ char *v = NULL;
659
660 t = strv_env_get_n(env, word+2, len, flags);
661
662 if (t && state == ALTERNATE_VALUE)
663 t = v = replace_env_n(test_value, e-test_value, env, flags);
664 else if (!t && state == DEFAULT_VALUE)
665 t = v = replace_env_n(test_value, e-test_value, env, flags);
666
2b070200 667 if (!strextend(&r, t))
b82f58bf
RS
668 return NULL;
669
3c800095
RC
670 word = e+1;
671 state = WORD;
672 }
673 break;
ccad1fd0
ZJS
674
675 case VARIABLE_RAW:
676 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
677
b45c068d 678 if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
ccad1fd0
ZJS
679 const char *t;
680
681 t = strv_env_get_n(env, word+1, e-word-1, flags);
682
2b070200 683 if (!strextend(&r, t))
ccad1fd0
ZJS
684 return NULL;
685
ccad1fd0 686 word = e--;
4bed076c 687 i--;
ccad1fd0
ZJS
688 state = WORD;
689 }
690 break;
3c800095 691 }
3c800095 692
ccad1fd0
ZJS
693 if (state == VARIABLE_RAW) {
694 const char *t;
695
696 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
697
698 t = strv_env_get_n(env, word+1, e-word-1, flags);
b910cc72 699 return strjoin(r, t);
ccad1fd0
ZJS
700 } else
701 return strnappend(r, word, e-word);
3c800095
RC
702}
703
704char **replace_env_argv(char **argv, char **env) {
705 char **ret, **i;
da6053d0 706 size_t k = 0, l = 0;
3c800095
RC
707
708 l = strv_length(argv);
709
710 ret = new(char*, l+1);
711 if (!ret)
712 return NULL;
713
714 STRV_FOREACH(i, argv) {
715
716 /* If $FOO appears as single word, replace it by the split up variable */
4c701096 717 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
3c800095
RC
718 char *e;
719 char **w, **m = NULL;
da6053d0 720 size_t q;
3c800095
RC
721
722 e = strv_env_get(env, *i+1);
723 if (e) {
724 int r;
725
90e30d76 726 r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
3c800095
RC
727 if (r < 0) {
728 ret[k] = NULL;
729 strv_free(ret);
730 return NULL;
731 }
732 } else
733 m = NULL;
734
735 q = strv_length(m);
736 l = l + q - 1;
737
62d74c78 738 w = reallocarray(ret, l + 1, sizeof(char *));
3c800095
RC
739 if (!w) {
740 ret[k] = NULL;
741 strv_free(ret);
742 strv_free(m);
743 return NULL;
744 }
745
746 ret = w;
747 if (m) {
748 memcpy(ret + k, m, q * sizeof(char*));
749 free(m);
750 }
751
752 k += q;
753 continue;
754 }
755
756 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
37f3ffca 757 ret[k] = replace_env(*i, env, 0);
3c800095
RC
758 if (!ret[k]) {
759 strv_free(ret);
760 return NULL;
761 }
762 k++;
763 }
764
765 ret[k] = NULL;
766 return ret;
767}
b41b9d2a
LP
768
769int getenv_bool(const char *p) {
770 const char *e;
771
772 e = getenv(p);
773 if (!e)
774 return -ENXIO;
775
776 return parse_boolean(e);
777}
fe902fa4 778
71e0accc
ZJS
779int getenv_bool_secure(const char *p) {
780 const char *e;
781
782 e = secure_getenv(p);
783 if (!e)
784 return -ENXIO;
785
786 return parse_boolean(e);
787}
063f9f0d
ZJS
788
789int set_unset_env(const char *name, const char *value, bool overwrite) {
7c248223 790 assert(name);
063f9f0d
ZJS
791
792 if (value)
7c248223
LP
793 return RET_NERRNO(setenv(name, value, overwrite));
794
795 return RET_NERRNO(unsetenv(name));
063f9f0d 796}
cfd1c6e2 797
fa256f43
ZJS
798int putenv_dup(const char *assignment, bool override) {
799 const char *e, *n;
800
801 e = strchr(assignment, '=');
802 if (!e)
803 return -EINVAL;
804
2f82562b 805 n = strndupa_safe(assignment, e - assignment);
fa256f43
ZJS
806
807 /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
7c248223 808 return RET_NERRNO(setenv(n, e + 1, override));
fa256f43
ZJS
809}
810
cfd1c6e2
YW
811int setenv_systemd_exec_pid(bool update_only) {
812 char str[DECIMAL_STR_MAX(pid_t)];
813 const char *e;
814
815 /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
816
817 e = secure_getenv("SYSTEMD_EXEC_PID");
818 if (!e && update_only)
819 return 0;
820
821 if (streq_ptr(e, "*"))
822 return 0;
823
824 xsprintf(str, PID_FMT, getpid_cached());
825
826 if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
827 return -errno;
828
829 return 1;
830}
93547f28
LB
831
832int getenv_path_list(const char *name, char ***ret_paths) {
833 _cleanup_strv_free_ char **l = NULL;
834 const char *e;
835 char **p;
836 int r;
837
838 assert(name);
839 assert(ret_paths);
840
93547f28
LB
841 e = secure_getenv(name);
842 if (!e)
33d31c0e 843 return -ENXIO;
93547f28
LB
844
845 r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
846 if (r < 0)
847 return log_debug_errno(r, "Failed to parse $%s: %m", name);
848
849 STRV_FOREACH(p, l) {
850 if (!path_is_absolute(*p))
851 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
852 "Path '%s' is not absolute, refusing.", *p);
853
854 if (!path_is_normalized(*p))
855 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
856 "Path '%s' is not normalized, refusing.", *p);
857
858 if (path_equal(*p, "/"))
859 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
860 "Path '%s' is the root fs, refusing.", *p);
861 }
862
863 if (strv_isempty(l))
864 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
865 "No paths specified, refusing.");
866
867 *ret_paths = TAKE_PTR(l);
33d31c0e 868 return 1;
93547f28 869}
7a6abbe9 870
e99ca147
LP
871int getenv_steal_erase(const char *name, char **ret) {
872 _cleanup_(erase_and_freep) char *a = NULL;
873 char *e;
7a6abbe9
LP
874
875 assert(name);
876
e99ca147
LP
877 /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes
878 * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for
879 * testing, and given that people are likely going to misuse this, be thorough) */
880
881 e = getenv(name);
882 if (!e) {
883 if (ret)
884 *ret = NULL;
7a6abbe9 885 return 0;
e99ca147 886 }
7a6abbe9 887
e99ca147
LP
888 if (ret) {
889 a = strdup(e);
890 if (!a)
891 return -ENOMEM;
892 }
893
894 string_erase(e);
7a6abbe9
LP
895
896 if (unsetenv(name) < 0)
897 return -errno;
898
e99ca147
LP
899 if (ret)
900 *ret = TAKE_PTR(a);
901
7a6abbe9
LP
902 return 1;
903}