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