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