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