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