]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[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 <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 static 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. */
41 if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
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
51 bool 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
58 bool env_value_is_valid(const char *e) {
59 if (!e)
60 return false;
61
62 if (!utf8_is_valid(e))
63 return false;
64
65 /* bash allows tabs and newlines in environment variables, and so
66 * should we */
67 if (string_has_cc(e, "\t\n"))
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. */
75 if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
76 return false;
77
78 return true;
79 }
80
81 bool 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
96 * cannot be either, but let's leave room for one trailing NUL
97 * byte. */
98 if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
99 return false;
100
101 return true;
102 }
103
104 bool 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
113 /* Check if there are duplicate assignments */
114 k = strcspn(*p, "=");
115 STRV_FOREACH(q, p + 1)
116 if (strneq(*p, *q, k) && (*q)[k] == '=')
117 return false;
118 }
119
120 return true;
121 }
122
123 bool strv_env_name_is_valid(char **l) {
124 char **p;
125
126 STRV_FOREACH(p, l) {
127 if (!env_name_is_valid(*p))
128 return false;
129
130 if (strv_contains(p + 1, *p))
131 return false;
132 }
133
134 return true;
135 }
136
137 bool strv_env_name_or_assignment_is_valid(char **l) {
138 char **p;
139
140 STRV_FOREACH(p, l) {
141 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
142 return false;
143
144 if (strv_contains(p + 1, *p))
145 return false;
146 }
147
148 return true;
149 }
150
151 static int env_append(char **r, char ***k, char **a) {
152 assert(r);
153 assert(k);
154 assert(*k >= r);
155
156 if (!a)
157 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 for (; *a; a++) {
167 char **j, *c;
168 size_t n;
169
170 n = strcspn(*a, "=");
171 if ((*a)[n] == '=')
172 n++;
173
174 for (j = r; j < *k; j++)
175 if (strneq(*j, *a, n))
176 break;
177
178 c = strdup(*a);
179 if (!c)
180 return -ENOMEM;
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 */
188 }
189
190 return 0;
191 }
192
193 char **strv_env_merge(size_t n_lists, ...) {
194 _cleanup_strv_free_ char **ret = NULL;
195 size_t n = 0, i;
196 char **l, **k;
197 va_list ap;
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
208 ret = new(char*, n+1);
209 if (!ret)
210 return NULL;
211
212 *ret = NULL;
213 k = ret;
214
215 va_start(ap, n_lists);
216 for (i = 0; i < n_lists; i++) {
217 l = va_arg(ap, char**);
218 if (env_append(ret, &k, l) < 0) {
219 va_end(ap);
220 return NULL;
221 }
222 }
223 va_end(ap);
224
225 return TAKE_PTR(ret);
226 }
227
228 static bool env_match(const char *t, const char *pattern) {
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
248 return strneq(t, pattern, l) && t[l] == '=';
249 }
250
251 return false;
252 }
253
254 static 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
267 char **strv_env_delete(char **x, size_t n_lists, ...) {
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) {
282 size_t v;
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
315 char **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
325 * string list. Edits in-place. */
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 }
336
337 *t = NULL;
338 return l;
339 }
340
341 char **strv_env_unset_many(char **l, ...) {
342
343 char **f, **t;
344
345 if (!l)
346 return NULL;
347
348 /* Like strv_env_unset() but applies many at once. Edits in-place. */
349
350 for (f = t = l; *f; f++) {
351 bool found = false;
352 const char *p;
353 va_list ap;
354
355 va_start(ap, l);
356
357 while ((p = va_arg(ap, const char*))) {
358 if (env_match(*f, p)) {
359 found = true;
360 break;
361 }
362 }
363
364 va_end(ap);
365
366 if (found) {
367 free(*f);
368 continue;
369 }
370
371 *(t++) = *f;
372 }
373
374 *t = NULL;
375 return l;
376 }
377
378 int strv_env_replace(char ***l, char *p) {
379 const char *t, *name;
380 char **f;
381 int r;
382
383 assert(p);
384
385 /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
386 * in-place. Does not copy p. p must be a valid key=value assignment.
387 */
388
389 t = strchr(p, '=');
390 if (!t)
391 return -EINVAL;
392
393 name = strndupa(p, t - p);
394
395 STRV_FOREACH(f, *l)
396 if (env_entry_has_name(*f, name)) {
397 free_and_replace(*f, p);
398 strv_env_unset(f + 1, *f);
399 return 0;
400 }
401
402 /* We didn't find a match, we need to append p or create a new strv */
403 r = strv_push(l, p);
404 if (r < 0)
405 return r;
406
407 return 1;
408 }
409
410 char **strv_env_set(char **x, const char *p) {
411
412 _cleanup_strv_free_ char **ret = NULL;
413 size_t n, m;
414 char **k;
415
416 /* Overrides the env var setting of p, returns a new copy */
417
418 n = strv_length(x);
419 m = n + 2;
420 if (m < n) /* overflow? */
421 return NULL;
422
423 ret = new(char*, m);
424 if (!ret)
425 return NULL;
426
427 *ret = NULL;
428 k = ret;
429
430 if (env_append(ret, &k, x) < 0)
431 return NULL;
432
433 if (env_append(ret, &k, STRV_MAKE(p)) < 0)
434 return NULL;
435
436 return TAKE_PTR(ret);
437 }
438
439 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
440 char **i;
441
442 assert(name);
443
444 if (k <= 0)
445 return NULL;
446
447 STRV_FOREACH_BACKWARDS(i, l)
448 if (strneq(*i, name, k) &&
449 (*i)[k] == '=')
450 return *i + k + 1;
451
452 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
453 const char *t;
454
455 t = strndupa(name, k);
456 return getenv(t);
457 };
458
459 return NULL;
460 }
461
462 char *strv_env_get(char **l, const char *name) {
463 assert(name);
464
465 return strv_env_get_n(l, name, strlen(name), 0);
466 }
467
468 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
469 char **p, **q;
470 int k = 0;
471
472 STRV_FOREACH(p, e) {
473 size_t n;
474 bool duplicate = false;
475
476 if (!env_assignment_is_valid(*p)) {
477 if (invalid_callback)
478 invalid_callback(*p, userdata);
479 free(*p);
480 continue;
481 }
482
483 n = strcspn(*p, "=");
484 STRV_FOREACH(q, p + 1)
485 if (strneq(*p, *q, n) && (*q)[n] == '=') {
486 duplicate = true;
487 break;
488 }
489
490 if (duplicate) {
491 free(*p);
492 continue;
493 }
494
495 e[k++] = *p;
496 }
497
498 if (e)
499 e[k] = NULL;
500
501 return e;
502 }
503
504 char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
505 enum {
506 WORD,
507 CURLY,
508 VARIABLE,
509 VARIABLE_RAW,
510 TEST,
511 DEFAULT_VALUE,
512 ALTERNATE_VALUE,
513 } state = WORD;
514
515 const char *e, *word = format, *test_value;
516 char *k;
517 _cleanup_free_ char *r = NULL;
518 size_t i, len;
519 int nest = 0;
520
521 assert(format);
522
523 for (e = format, i = 0; *e && i < n; e ++, i ++)
524 switch (state) {
525
526 case WORD:
527 if (*e == '$')
528 state = CURLY;
529 break;
530
531 case CURLY:
532 if (*e == '{') {
533 k = strnappend(r, word, e-word-1);
534 if (!k)
535 return NULL;
536
537 free_and_replace(r, k);
538
539 word = e-1;
540 state = VARIABLE;
541 nest++;
542 } else if (*e == '$') {
543 k = strnappend(r, word, e-word);
544 if (!k)
545 return NULL;
546
547 free_and_replace(r, k);
548
549 word = e+1;
550 state = WORD;
551
552 } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
553 k = strnappend(r, word, e-word-1);
554 if (!k)
555 return NULL;
556
557 free_and_replace(r, k);
558
559 word = e-1;
560 state = VARIABLE_RAW;
561
562 } else
563 state = WORD;
564 break;
565
566 case VARIABLE:
567 if (*e == '}') {
568 const char *t;
569
570 t = strv_env_get_n(env, word+2, e-word-2, flags);
571
572 k = strappend(r, t);
573 if (!k)
574 return NULL;
575
576 free_and_replace(r, k);
577
578 word = e+1;
579 state = WORD;
580 } else if (*e == ':') {
581 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
582 /* Treat this as unsupported syntax, i.e. do no replacement */
583 state = WORD;
584 else {
585 len = e-word-2;
586 state = TEST;
587 }
588 }
589 break;
590
591 case TEST:
592 if (*e == '-')
593 state = DEFAULT_VALUE;
594 else if (*e == '+')
595 state = ALTERNATE_VALUE;
596 else {
597 state = WORD;
598 break;
599 }
600
601 test_value = e+1;
602 break;
603
604 case DEFAULT_VALUE: /* fall through */
605 case ALTERNATE_VALUE:
606 assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
607
608 if (*e == '{') {
609 nest++;
610 break;
611 }
612
613 if (*e != '}')
614 break;
615
616 nest--;
617 if (nest == 0) {
618 const char *t;
619 _cleanup_free_ char *v = NULL;
620
621 t = strv_env_get_n(env, word+2, len, flags);
622
623 if (t && state == ALTERNATE_VALUE)
624 t = v = replace_env_n(test_value, e-test_value, env, flags);
625 else if (!t && state == DEFAULT_VALUE)
626 t = v = replace_env_n(test_value, e-test_value, env, flags);
627
628 k = strappend(r, t);
629 if (!k)
630 return NULL;
631
632 free_and_replace(r, k);
633
634 word = e+1;
635 state = WORD;
636 }
637 break;
638
639 case VARIABLE_RAW:
640 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
641
642 if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
643 const char *t;
644
645 t = strv_env_get_n(env, word+1, e-word-1, flags);
646
647 k = strappend(r, t);
648 if (!k)
649 return NULL;
650
651 free_and_replace(r, k);
652
653 word = e--;
654 i--;
655 state = WORD;
656 }
657 break;
658 }
659
660 if (state == VARIABLE_RAW) {
661 const char *t;
662
663 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
664
665 t = strv_env_get_n(env, word+1, e-word-1, flags);
666 return strappend(r, t);
667 } else
668 return strnappend(r, word, e-word);
669 }
670
671 char **replace_env_argv(char **argv, char **env) {
672 char **ret, **i;
673 size_t k = 0, l = 0;
674
675 l = strv_length(argv);
676
677 ret = new(char*, l+1);
678 if (!ret)
679 return NULL;
680
681 STRV_FOREACH(i, argv) {
682
683 /* If $FOO appears as single word, replace it by the split up variable */
684 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
685 char *e;
686 char **w, **m = NULL;
687 size_t q;
688
689 e = strv_env_get(env, *i+1);
690 if (e) {
691 int r;
692
693 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
694 if (r < 0) {
695 ret[k] = NULL;
696 strv_free(ret);
697 return NULL;
698 }
699 } else
700 m = NULL;
701
702 q = strv_length(m);
703 l = l + q - 1;
704
705 w = reallocarray(ret, l + 1, sizeof(char *));
706 if (!w) {
707 ret[k] = NULL;
708 strv_free(ret);
709 strv_free(m);
710 return NULL;
711 }
712
713 ret = w;
714 if (m) {
715 memcpy(ret + k, m, q * sizeof(char*));
716 free(m);
717 }
718
719 k += q;
720 continue;
721 }
722
723 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
724 ret[k] = replace_env(*i, env, 0);
725 if (!ret[k]) {
726 strv_free(ret);
727 return NULL;
728 }
729 k++;
730 }
731
732 ret[k] = NULL;
733 return ret;
734 }
735
736 int getenv_bool(const char *p) {
737 const char *e;
738
739 e = getenv(p);
740 if (!e)
741 return -ENXIO;
742
743 return parse_boolean(e);
744 }
745
746 int getenv_bool_secure(const char *p) {
747 const char *e;
748
749 e = secure_getenv(p);
750 if (!e)
751 return -ENXIO;
752
753 return parse_boolean(e);
754 }