]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/env-util.c
tree-wide: drop string.h when string-util.h or friends are included
[thirdparty/systemd.git] / src / basic / env-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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
LP
18
19#define VALID_CHARS_ENV_NAME \
4b549144 20 DIGITS LETTERS \
4d1a6904
LP
21 "_"
22
4d1a6904
LP
23static 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. */
d3e66e68 40 if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
4d1a6904
LP
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
50bool 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
57bool env_value_is_valid(const char *e) {
58 if (!e)
59 return false;
60
61 if (!utf8_is_valid(e))
62 return false;
63
b4346b9a
FB
64 /* bash allows tabs and newlines in environment variables, and so
65 * should we */
66 if (string_has_cc(e, "\t\n"))
4d1a6904
LP
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. */
21c491e1 74 if (strlen(e) > sc_arg_max() - 3)
4d1a6904
LP
75 return false;
76
77 return true;
78}
79
80bool 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
5f9cfd4c 95 * cannot be either, but let's leave room for one trailing NUL
4d1a6904 96 * byte. */
21c491e1 97 if (strlen(e) > sc_arg_max() - 1)
4d1a6904
LP
98 return false;
99
100 return true;
101}
102
103bool 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
f21f31b2 112 /* Check if there are duplicate assignments */
4d1a6904
LP
113 k = strcspn(*p, "=");
114 STRV_FOREACH(q, p + 1)
641906e9 115 if (strneq(*p, *q, k) && (*q)[k] == '=')
4d1a6904
LP
116 return false;
117 }
118
119 return true;
120}
121
b4c14404 122bool strv_env_name_is_valid(char **l) {
68ac147e 123 char **p;
b4c14404
FB
124
125 STRV_FOREACH(p, l) {
126 if (!env_name_is_valid(*p))
127 return false;
128
68ac147e
LP
129 if (strv_contains(p + 1, *p))
130 return false;
b4c14404
FB
131 }
132
133 return true;
134}
135
123b964a 136bool strv_env_name_or_assignment_is_valid(char **l) {
68ac147e 137 char **p;
123b964a
LP
138
139 STRV_FOREACH(p, l) {
140 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
141 return false;
142
68ac147e
LP
143 if (strv_contains(p + 1, *p))
144 return false;
123b964a
LP
145 }
146
147 return true;
148}
149
4d1a6904
LP
150static int env_append(char **r, char ***k, char **a) {
151 assert(r);
152 assert(k);
2a13184a 153 assert(*k >= r);
4d1a6904
LP
154
155 if (!a)
156 return 0;
157
2a13184a
LP
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. */
4d1a6904
LP
164
165 for (; *a; a++) {
2a13184a 166 char **j, *c;
4d1a6904
LP
167 size_t n;
168
169 n = strcspn(*a, "=");
4d1a6904
LP
170 if ((*a)[n] == '=')
171 n++;
172
173 for (j = r; j < *k; j++)
641906e9 174 if (strneq(*j, *a, n))
4d1a6904
LP
175 break;
176
2a13184a
LP
177 c = strdup(*a);
178 if (!c)
4d1a6904 179 return -ENOMEM;
2a13184a
LP
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 */
4d1a6904
LP
187 }
188
189 return 0;
190}
191
da6053d0 192char **strv_env_merge(size_t n_lists, ...) {
2d3ff1de
LP
193 _cleanup_strv_free_ char **ret = NULL;
194 size_t n = 0, i;
195 char **l, **k;
4d1a6904 196 va_list ap;
4d1a6904
LP
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
2d3ff1de
LP
207 ret = new(char*, n+1);
208 if (!ret)
4d1a6904
LP
209 return NULL;
210
2d3ff1de
LP
211 *ret = NULL;
212 k = ret;
4d1a6904
LP
213
214 va_start(ap, n_lists);
215 for (i = 0; i < n_lists; i++) {
216 l = va_arg(ap, char**);
2d3ff1de
LP
217 if (env_append(ret, &k, l) < 0) {
218 va_end(ap);
219 return NULL;
220 }
4d1a6904
LP
221 }
222 va_end(ap);
223
2d3ff1de 224 return TAKE_PTR(ret);
4d1a6904
LP
225}
226
c8cebc36 227static bool env_match(const char *t, const char *pattern) {
4d1a6904
LP
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
641906e9 247 return strneq(t, pattern, l) && t[l] == '=';
4d1a6904
LP
248 }
249
250 return false;
251}
252
99003e01
ZJS
253static 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
da6053d0 266char **strv_env_delete(char **x, size_t n_lists, ...) {
4d1a6904
LP
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) {
da6053d0 281 size_t v;
4d1a6904
LP
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
314char **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
43d03a83 324 * string list. Edits in-place. */
4d1a6904
LP
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 }
43d03a83
LP
335
336 *t = NULL;
337 return l;
338}
339
340char **strv_env_unset_many(char **l, ...) {
43d03a83
LP
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 }
4d1a6904
LP
371
372 *t = NULL;
373 return l;
374}
375
54ac3494 376int strv_env_replace(char ***l, char *p) {
99003e01 377 const char *t, *name;
04effffd
LP
378 char **f;
379 int r;
54ac3494
ZJS
380
381 assert(p);
382
04effffd
LP
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.
54ac3494
ZJS
385 */
386
99003e01 387 t = strchr(p, '=');
04effffd
LP
388 if (!t)
389 return -EINVAL;
99003e01
ZJS
390
391 name = strndupa(p, t - p);
392
04effffd 393 STRV_FOREACH(f, *l)
99003e01
ZJS
394 if (env_entry_has_name(*f, name)) {
395 free_and_replace(*f, p);
396 strv_env_unset(f + 1, *f);
54ac3494
ZJS
397 return 0;
398 }
399
400 /* We didn't find a match, we need to append p or create a new strv */
04effffd
LP
401 r = strv_push(l, p);
402 if (r < 0)
403 return r;
404
54ac3494
ZJS
405 return 1;
406}
407
4d1a6904 408char **strv_env_set(char **x, const char *p) {
fcbb6574
LP
409 _cleanup_strv_free_ char **ret = NULL;
410 size_t n, m;
7de91ea6 411 char **k;
4d1a6904
LP
412
413 /* Overrides the env var setting of p, returns a new copy */
414
fcbb6574
LP
415 n = strv_length(x);
416 m = n + 2;
417 if (m < n) /* overflow? */
4d1a6904
LP
418 return NULL;
419
fcbb6574
LP
420 ret = new(char*, m);
421 if (!ret)
7de91ea6 422 return NULL;
4d1a6904 423
fcbb6574
LP
424 *ret = NULL;
425 k = ret;
426
427 if (env_append(ret, &k, x) < 0)
7de91ea6 428 return NULL;
4d1a6904 429
fcbb6574
LP
430 if (env_append(ret, &k, STRV_MAKE(p)) < 0)
431 return NULL;
4d1a6904 432
fcbb6574 433 return TAKE_PTR(ret);
4d1a6904
LP
434}
435
37f3ffca 436char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
4d1a6904
LP
437 char **i;
438
439 assert(name);
440
441 if (k <= 0)
442 return NULL;
443
6162512c 444 STRV_FOREACH_BACKWARDS(i, l)
641906e9 445 if (strneq(*i, name, k) &&
4d1a6904
LP
446 (*i)[k] == '=')
447 return *i + k + 1;
448
37f3ffca
RS
449 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
450 const char *t;
451
452 t = strndupa(name, k);
453 return getenv(t);
454 };
455
4d1a6904
LP
456 return NULL;
457}
458
459char *strv_env_get(char **l, const char *name) {
460 assert(name);
461
37f3ffca 462 return strv_env_get_n(l, name, strlen(name), 0);
4d1a6904
LP
463}
464
039f0e70 465char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
4d1a6904
LP
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)) {
039f0e70
LP
474 if (invalid_callback)
475 invalid_callback(*p, userdata);
4d1a6904
LP
476 free(*p);
477 continue;
478 }
479
480 n = strcspn(*p, "=");
481 STRV_FOREACH(q, p + 1)
641906e9 482 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
483 duplicate = true;
484 break;
485 }
486
487 if (duplicate) {
488 free(*p);
489 continue;
490 }
491
492 e[k++] = *p;
493 }
494
5b4fb02d
LP
495 if (e)
496 e[k] = NULL;
497
4d1a6904
LP
498 return e;
499}
3c800095 500
4bed076c 501char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
3c800095
RC
502 enum {
503 WORD,
504 CURLY,
ccad1fd0
ZJS
505 VARIABLE,
506 VARIABLE_RAW,
b82f58bf
RS
507 TEST,
508 DEFAULT_VALUE,
509 ALTERNATE_VALUE,
3c800095
RC
510 } state = WORD;
511
b82f58bf 512 const char *e, *word = format, *test_value;
cb4499d0
ZJS
513 char *k;
514 _cleanup_free_ char *r = NULL;
b82f58bf
RS
515 size_t i, len;
516 int nest = 0;
3c800095
RC
517
518 assert(format);
519
00d4b1e6 520 for (e = format, i = 0; *e && i < n; e ++, i ++)
3c800095
RC
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)
cb4499d0 532 return NULL;
3c800095 533
00d4b1e6 534 free_and_replace(r, k);
3c800095
RC
535
536 word = e-1;
537 state = VARIABLE;
b82f58bf 538 nest++;
3c800095
RC
539 } else if (*e == '$') {
540 k = strnappend(r, word, e-word);
541 if (!k)
cb4499d0 542 return NULL;
3c800095 543
00d4b1e6 544 free_and_replace(r, k);
3c800095
RC
545
546 word = e+1;
547 state = WORD;
ccad1fd0
ZJS
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
00d4b1e6 554 free_and_replace(r, k);
ccad1fd0
ZJS
555
556 word = e-1;
557 state = VARIABLE_RAW;
558
3c800095
RC
559 } else
560 state = WORD;
561 break;
562
563 case VARIABLE:
564 if (*e == '}') {
565 const char *t;
566
37f3ffca 567 t = strv_env_get_n(env, word+2, e-word-2, flags);
3c800095 568
b910cc72 569 k = strjoin(r, t);
3c800095 570 if (!k)
cb4499d0 571 return NULL;
3c800095 572
00d4b1e6 573 free_and_replace(r, k);
3c800095 574
b82f58bf
RS
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--;
1d046f57 614 if (nest == 0) {
b82f58bf
RS
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
b910cc72 625 k = strjoin(r, t);
b82f58bf
RS
626 if (!k)
627 return NULL;
628
00d4b1e6 629 free_and_replace(r, k);
b82f58bf 630
3c800095
RC
631 word = e+1;
632 state = WORD;
633 }
634 break;
ccad1fd0
ZJS
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
b910cc72 644 k = strjoin(r, t);
ccad1fd0
ZJS
645 if (!k)
646 return NULL;
647
00d4b1e6 648 free_and_replace(r, k);
ccad1fd0
ZJS
649
650 word = e--;
4bed076c 651 i--;
ccad1fd0
ZJS
652 state = WORD;
653 }
654 break;
3c800095 655 }
3c800095 656
ccad1fd0
ZJS
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);
b910cc72 663 return strjoin(r, t);
ccad1fd0
ZJS
664 } else
665 return strnappend(r, word, e-word);
3c800095
RC
666}
667
668char **replace_env_argv(char **argv, char **env) {
669 char **ret, **i;
da6053d0 670 size_t k = 0, l = 0;
3c800095
RC
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 */
4c701096 681 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
3c800095
RC
682 char *e;
683 char **w, **m = NULL;
da6053d0 684 size_t q;
3c800095
RC
685
686 e = strv_env_get(env, *i+1);
687 if (e) {
688 int r;
689
4ec85141 690 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
3c800095
RC
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
62d74c78 702 w = reallocarray(ret, l + 1, sizeof(char *));
3c800095
RC
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 */
37f3ffca 721 ret[k] = replace_env(*i, env, 0);
3c800095
RC
722 if (!ret[k]) {
723 strv_free(ret);
724 return NULL;
725 }
726 k++;
727 }
728
729 ret[k] = NULL;
730 return ret;
731}
b41b9d2a
LP
732
733int 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}
fe902fa4 742
71e0accc
ZJS
743int 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}