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