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