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