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