]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
core: move reset_arguments() to the end of main's finish
[thirdparty/systemd.git] / src / basic / env-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 "string-util.h"
16 #include "strv.h"
17 #include "utf8.h"
18
19 #define VALID_CHARS_ENV_NAME \
20 DIGITS LETTERS \
21 "_"
22
23 static bool env_name_is_valid_n(const char *e, size_t n) {
24 const char *p;
25
26 if (!e)
27 return false;
28
29 if (n <= 0)
30 return false;
31
32 if (e[0] >= '0' && e[0] <= '9')
33 return false;
34
35 /* POSIX says the overall size of the environment block cannot
36 * be > ARG_MAX, an individual assignment hence cannot be
37 * either. Discounting the equal sign and trailing NUL this
38 * hence leaves ARG_MAX-2 as longest possible variable
39 * name. */
40 if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
41 return false;
42
43 for (p = e; p < e + n; p++)
44 if (!strchr(VALID_CHARS_ENV_NAME, *p))
45 return false;
46
47 return true;
48 }
49
50 bool env_name_is_valid(const char *e) {
51 if (!e)
52 return false;
53
54 return env_name_is_valid_n(e, strlen(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 /* bash allows tabs and newlines in environment variables, and so
65 * should we */
66 if (string_has_cc(e, "\t\n"))
67 return false;
68
69 /* POSIX says the overall size of the environment block cannot
70 * be > ARG_MAX, an individual assignment hence cannot be
71 * either. Discounting the shortest possible variable name of
72 * length 1, the equal sign and trailing NUL this hence leaves
73 * ARG_MAX-3 as longest possible variable value. */
74 if (strlen(e) > sc_arg_max() - 3)
75 return false;
76
77 return true;
78 }
79
80 bool env_assignment_is_valid(const char *e) {
81 const char *eq;
82
83 eq = strchr(e, '=');
84 if (!eq)
85 return false;
86
87 if (!env_name_is_valid_n(e, eq - e))
88 return false;
89
90 if (!env_value_is_valid(eq + 1))
91 return false;
92
93 /* POSIX says the overall size of the environment block cannot
94 * be > ARG_MAX, hence the individual variable assignments
95 * cannot be either, but let's leave room for one trailing NUL
96 * byte. */
97 if (strlen(e) > sc_arg_max() - 1)
98 return false;
99
100 return true;
101 }
102
103 bool strv_env_is_valid(char **e) {
104 char **p, **q;
105
106 STRV_FOREACH(p, e) {
107 size_t k;
108
109 if (!env_assignment_is_valid(*p))
110 return false;
111
112 /* Check if there are duplicate assignments */
113 k = strcspn(*p, "=");
114 STRV_FOREACH(q, p + 1)
115 if (strneq(*p, *q, k) && (*q)[k] == '=')
116 return false;
117 }
118
119 return true;
120 }
121
122 bool strv_env_name_is_valid(char **l) {
123 char **p;
124
125 STRV_FOREACH(p, l) {
126 if (!env_name_is_valid(*p))
127 return false;
128
129 if (strv_contains(p + 1, *p))
130 return false;
131 }
132
133 return true;
134 }
135
136 bool strv_env_name_or_assignment_is_valid(char **l) {
137 char **p;
138
139 STRV_FOREACH(p, l) {
140 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
141 return false;
142
143 if (strv_contains(p + 1, *p))
144 return false;
145 }
146
147 return true;
148 }
149
150 static int env_append(char **r, char ***k, char **a) {
151 assert(r);
152 assert(k);
153 assert(*k >= r);
154
155 if (!a)
156 return 0;
157
158 /* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k'
159 * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv.
160 *
161 * This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it.
162 *
163 * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */
164
165 for (; *a; a++) {
166 char **j, *c;
167 size_t n;
168
169 n = strcspn(*a, "=");
170 if ((*a)[n] == '=')
171 n++;
172
173 for (j = r; j < *k; j++)
174 if (strneq(*j, *a, n))
175 break;
176
177 c = strdup(*a);
178 if (!c)
179 return -ENOMEM;
180
181 if (j >= *k) { /* Append to the end? */
182 (*k)[0] = c;
183 (*k)[1] = NULL;
184 (*k)++;
185 } else
186 free_and_replace(*j, c); /* Override existing item */
187 }
188
189 return 0;
190 }
191
192 char **strv_env_merge(size_t n_lists, ...) {
193 _cleanup_strv_free_ char **ret = NULL;
194 size_t n = 0, i;
195 char **l, **k;
196 va_list ap;
197
198 /* Merges an arbitrary number of environment sets */
199
200 va_start(ap, n_lists);
201 for (i = 0; i < n_lists; i++) {
202 l = va_arg(ap, char**);
203 n += strv_length(l);
204 }
205 va_end(ap);
206
207 ret = new(char*, n+1);
208 if (!ret)
209 return NULL;
210
211 *ret = NULL;
212 k = ret;
213
214 va_start(ap, n_lists);
215 for (i = 0; i < n_lists; i++) {
216 l = va_arg(ap, char**);
217 if (env_append(ret, &k, l) < 0) {
218 va_end(ap);
219 return NULL;
220 }
221 }
222 va_end(ap);
223
224 return TAKE_PTR(ret);
225 }
226
227 static bool env_match(const char *t, const char *pattern) {
228 assert(t);
229 assert(pattern);
230
231 /* pattern a matches string a
232 * a matches a=
233 * a matches a=b
234 * a= matches a=
235 * a=b matches a=b
236 * a= does not match a
237 * a=b does not match a=
238 * a=b does not match a
239 * a=b does not match a=c */
240
241 if (streq(t, pattern))
242 return true;
243
244 if (!strchr(pattern, '=')) {
245 size_t l = strlen(pattern);
246
247 return strneq(t, pattern, l) && t[l] == '=';
248 }
249
250 return false;
251 }
252
253 static bool env_entry_has_name(const char *entry, const char *name) {
254 const char *t;
255
256 assert(entry);
257 assert(name);
258
259 t = startswith(entry, name);
260 if (!t)
261 return false;
262
263 return *t == '=';
264 }
265
266 char **strv_env_delete(char **x, size_t n_lists, ...) {
267 size_t n, i = 0;
268 char **k, **r;
269 va_list ap;
270
271 /* Deletes every entry from x that is mentioned in the other
272 * string lists */
273
274 n = strv_length(x);
275
276 r = new(char*, n+1);
277 if (!r)
278 return NULL;
279
280 STRV_FOREACH(k, x) {
281 size_t v;
282
283 va_start(ap, n_lists);
284 for (v = 0; v < n_lists; v++) {
285 char **l, **j;
286
287 l = va_arg(ap, char**);
288 STRV_FOREACH(j, l)
289 if (env_match(*k, *j))
290 goto skip;
291 }
292 va_end(ap);
293
294 r[i] = strdup(*k);
295 if (!r[i]) {
296 strv_free(r);
297 return NULL;
298 }
299
300 i++;
301 continue;
302
303 skip:
304 va_end(ap);
305 }
306
307 r[i] = NULL;
308
309 assert(i <= n);
310
311 return r;
312 }
313
314 char **strv_env_unset(char **l, const char *p) {
315
316 char **f, **t;
317
318 if (!l)
319 return NULL;
320
321 assert(p);
322
323 /* Drops every occurrence of the env var setting p in the
324 * string list. Edits in-place. */
325
326 for (f = t = l; *f; f++) {
327
328 if (env_match(*f, p)) {
329 free(*f);
330 continue;
331 }
332
333 *(t++) = *f;
334 }
335
336 *t = NULL;
337 return l;
338 }
339
340 char **strv_env_unset_many(char **l, ...) {
341 char **f, **t;
342
343 if (!l)
344 return NULL;
345
346 /* Like strv_env_unset() but applies many at once. Edits in-place. */
347
348 for (f = t = l; *f; f++) {
349 bool found = false;
350 const char *p;
351 va_list ap;
352
353 va_start(ap, l);
354
355 while ((p = va_arg(ap, const char*))) {
356 if (env_match(*f, p)) {
357 found = true;
358 break;
359 }
360 }
361
362 va_end(ap);
363
364 if (found) {
365 free(*f);
366 continue;
367 }
368
369 *(t++) = *f;
370 }
371
372 *t = NULL;
373 return l;
374 }
375
376 int strv_env_replace(char ***l, char *p) {
377 const char *t, *name;
378 char **f;
379 int r;
380
381 assert(p);
382
383 /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
384 * in-place. Does not copy p. p must be a valid key=value assignment.
385 */
386
387 t = strchr(p, '=');
388 if (!t)
389 return -EINVAL;
390
391 name = strndupa(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_push(l, p);
402 if (r < 0)
403 return r;
404
405 return 1;
406 }
407
408 char **strv_env_set(char **x, const char *p) {
409 _cleanup_strv_free_ char **ret = NULL;
410 size_t n, m;
411 char **k;
412
413 /* Overrides the env var setting of p, returns a new copy */
414
415 n = strv_length(x);
416 m = n + 2;
417 if (m < n) /* overflow? */
418 return NULL;
419
420 ret = new(char*, m);
421 if (!ret)
422 return NULL;
423
424 *ret = NULL;
425 k = ret;
426
427 if (env_append(ret, &k, x) < 0)
428 return NULL;
429
430 if (env_append(ret, &k, STRV_MAKE(p)) < 0)
431 return NULL;
432
433 return TAKE_PTR(ret);
434 }
435
436 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
437 char **i;
438
439 assert(name);
440
441 if (k <= 0)
442 return NULL;
443
444 STRV_FOREACH_BACKWARDS(i, l)
445 if (strneq(*i, name, k) &&
446 (*i)[k] == '=')
447 return *i + k + 1;
448
449 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
450 const char *t;
451
452 t = strndupa(name, k);
453 return getenv(t);
454 };
455
456 return NULL;
457 }
458
459 char *strv_env_get(char **l, const char *name) {
460 assert(name);
461
462 return strv_env_get_n(l, name, strlen(name), 0);
463 }
464
465 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
466 char **p, **q;
467 int k = 0;
468
469 STRV_FOREACH(p, e) {
470 size_t n;
471 bool duplicate = false;
472
473 if (!env_assignment_is_valid(*p)) {
474 if (invalid_callback)
475 invalid_callback(*p, userdata);
476 free(*p);
477 continue;
478 }
479
480 n = strcspn(*p, "=");
481 STRV_FOREACH(q, p + 1)
482 if (strneq(*p, *q, n) && (*q)[n] == '=') {
483 duplicate = true;
484 break;
485 }
486
487 if (duplicate) {
488 free(*p);
489 continue;
490 }
491
492 e[k++] = *p;
493 }
494
495 if (e)
496 e[k] = NULL;
497
498 return e;
499 }
500
501 char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
502 enum {
503 WORD,
504 CURLY,
505 VARIABLE,
506 VARIABLE_RAW,
507 TEST,
508 DEFAULT_VALUE,
509 ALTERNATE_VALUE,
510 } state = WORD;
511
512 const char *e, *word = format, *test_value;
513 char *k;
514 _cleanup_free_ char *r = NULL;
515 size_t i, len;
516 int nest = 0;
517
518 assert(format);
519
520 for (e = format, i = 0; *e && i < n; e ++, i ++)
521 switch (state) {
522
523 case WORD:
524 if (*e == '$')
525 state = CURLY;
526 break;
527
528 case CURLY:
529 if (*e == '{') {
530 k = strnappend(r, word, e-word-1);
531 if (!k)
532 return NULL;
533
534 free_and_replace(r, k);
535
536 word = e-1;
537 state = VARIABLE;
538 nest++;
539 } else if (*e == '$') {
540 k = strnappend(r, word, e-word);
541 if (!k)
542 return NULL;
543
544 free_and_replace(r, k);
545
546 word = e+1;
547 state = WORD;
548
549 } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
550 k = strnappend(r, word, e-word-1);
551 if (!k)
552 return NULL;
553
554 free_and_replace(r, k);
555
556 word = e-1;
557 state = VARIABLE_RAW;
558
559 } else
560 state = WORD;
561 break;
562
563 case VARIABLE:
564 if (*e == '}') {
565 const char *t;
566
567 t = strv_env_get_n(env, word+2, e-word-2, flags);
568
569 k = strjoin(r, t);
570 if (!k)
571 return NULL;
572
573 free_and_replace(r, k);
574
575 word = e+1;
576 state = WORD;
577 } else if (*e == ':') {
578 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
579 /* Treat this as unsupported syntax, i.e. do no replacement */
580 state = WORD;
581 else {
582 len = e-word-2;
583 state = TEST;
584 }
585 }
586 break;
587
588 case TEST:
589 if (*e == '-')
590 state = DEFAULT_VALUE;
591 else if (*e == '+')
592 state = ALTERNATE_VALUE;
593 else {
594 state = WORD;
595 break;
596 }
597
598 test_value = e+1;
599 break;
600
601 case DEFAULT_VALUE: /* fall through */
602 case ALTERNATE_VALUE:
603 assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
604
605 if (*e == '{') {
606 nest++;
607 break;
608 }
609
610 if (*e != '}')
611 break;
612
613 nest--;
614 if (nest == 0) {
615 const char *t;
616 _cleanup_free_ char *v = NULL;
617
618 t = strv_env_get_n(env, word+2, len, flags);
619
620 if (t && state == ALTERNATE_VALUE)
621 t = v = replace_env_n(test_value, e-test_value, env, flags);
622 else if (!t && state == DEFAULT_VALUE)
623 t = v = replace_env_n(test_value, e-test_value, env, flags);
624
625 k = strjoin(r, t);
626 if (!k)
627 return NULL;
628
629 free_and_replace(r, k);
630
631 word = e+1;
632 state = WORD;
633 }
634 break;
635
636 case VARIABLE_RAW:
637 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
638
639 if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
640 const char *t;
641
642 t = strv_env_get_n(env, word+1, e-word-1, flags);
643
644 k = strjoin(r, t);
645 if (!k)
646 return NULL;
647
648 free_and_replace(r, k);
649
650 word = e--;
651 i--;
652 state = WORD;
653 }
654 break;
655 }
656
657 if (state == VARIABLE_RAW) {
658 const char *t;
659
660 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
661
662 t = strv_env_get_n(env, word+1, e-word-1, flags);
663 return strjoin(r, t);
664 } else
665 return strnappend(r, word, e-word);
666 }
667
668 char **replace_env_argv(char **argv, char **env) {
669 char **ret, **i;
670 size_t k = 0, l = 0;
671
672 l = strv_length(argv);
673
674 ret = new(char*, l+1);
675 if (!ret)
676 return NULL;
677
678 STRV_FOREACH(i, argv) {
679
680 /* If $FOO appears as single word, replace it by the split up variable */
681 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
682 char *e;
683 char **w, **m = NULL;
684 size_t q;
685
686 e = strv_env_get(env, *i+1);
687 if (e) {
688 int r;
689
690 r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
691 if (r < 0) {
692 ret[k] = NULL;
693 strv_free(ret);
694 return NULL;
695 }
696 } else
697 m = NULL;
698
699 q = strv_length(m);
700 l = l + q - 1;
701
702 w = reallocarray(ret, l + 1, sizeof(char *));
703 if (!w) {
704 ret[k] = NULL;
705 strv_free(ret);
706 strv_free(m);
707 return NULL;
708 }
709
710 ret = w;
711 if (m) {
712 memcpy(ret + k, m, q * sizeof(char*));
713 free(m);
714 }
715
716 k += q;
717 continue;
718 }
719
720 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
721 ret[k] = replace_env(*i, env, 0);
722 if (!ret[k]) {
723 strv_free(ret);
724 return NULL;
725 }
726 k++;
727 }
728
729 ret[k] = NULL;
730 return ret;
731 }
732
733 int getenv_bool(const char *p) {
734 const char *e;
735
736 e = getenv(p);
737 if (!e)
738 return -ENXIO;
739
740 return parse_boolean(e);
741 }
742
743 int getenv_bool_secure(const char *p) {
744 const char *e;
745
746 e = secure_getenv(p);
747 if (!e)
748 return -ENXIO;
749
750 return parse_boolean(e);
751 }