]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
Merge pull request #8476 from EliaGeretto/n550jv-touchpad-fix
[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 and newlines in environment variables, and so
88 * should we */
89 if (string_has_cc(e, "\t\n"))
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 switch (state) {
544
545 case WORD:
546 if (*e == '$')
547 state = CURLY;
548 break;
549
550 case CURLY:
551 if (*e == '{') {
552 k = strnappend(r, word, e-word-1);
553 if (!k)
554 return NULL;
555
556 free_and_replace(r, k);
557
558 word = e-1;
559 state = VARIABLE;
560 nest++;
561 } else if (*e == '$') {
562 k = strnappend(r, word, e-word);
563 if (!k)
564 return NULL;
565
566 free_and_replace(r, k);
567
568 word = e+1;
569 state = WORD;
570
571 } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
572 k = strnappend(r, word, e-word-1);
573 if (!k)
574 return NULL;
575
576 free_and_replace(r, k);
577
578 word = e-1;
579 state = VARIABLE_RAW;
580
581 } else
582 state = WORD;
583 break;
584
585 case VARIABLE:
586 if (*e == '}') {
587 const char *t;
588
589 t = strv_env_get_n(env, word+2, e-word-2, flags);
590
591 k = strappend(r, t);
592 if (!k)
593 return NULL;
594
595 free_and_replace(r, k);
596
597 word = e+1;
598 state = WORD;
599 } else if (*e == ':') {
600 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
601 /* Treat this as unsupported syntax, i.e. do no replacement */
602 state = WORD;
603 else {
604 len = e-word-2;
605 state = TEST;
606 }
607 }
608 break;
609
610 case TEST:
611 if (*e == '-')
612 state = DEFAULT_VALUE;
613 else if (*e == '+')
614 state = ALTERNATE_VALUE;
615 else {
616 state = WORD;
617 break;
618 }
619
620 test_value = e+1;
621 break;
622
623 case DEFAULT_VALUE: /* fall through */
624 case ALTERNATE_VALUE:
625 assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
626
627 if (*e == '{') {
628 nest++;
629 break;
630 }
631
632 if (*e != '}')
633 break;
634
635 nest--;
636 if (nest == 0) {
637 const char *t;
638 _cleanup_free_ char *v = NULL;
639
640 t = strv_env_get_n(env, word+2, len, flags);
641
642 if (t && state == ALTERNATE_VALUE)
643 t = v = replace_env_n(test_value, e-test_value, env, flags);
644 else if (!t && state == DEFAULT_VALUE)
645 t = v = replace_env_n(test_value, e-test_value, env, flags);
646
647 k = strappend(r, t);
648 if (!k)
649 return NULL;
650
651 free_and_replace(r, k);
652
653 word = e+1;
654 state = WORD;
655 }
656 break;
657
658 case VARIABLE_RAW:
659 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
660
661 if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
662 const char *t;
663
664 t = strv_env_get_n(env, word+1, e-word-1, flags);
665
666 k = strappend(r, t);
667 if (!k)
668 return NULL;
669
670 free_and_replace(r, k);
671
672 word = e--;
673 i--;
674 state = WORD;
675 }
676 break;
677 }
678
679 if (state == VARIABLE_RAW) {
680 const char *t;
681
682 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
683
684 t = strv_env_get_n(env, word+1, e-word-1, flags);
685 return strappend(r, t);
686 } else
687 return strnappend(r, word, e-word);
688 }
689
690 char **replace_env_argv(char **argv, char **env) {
691 char **ret, **i;
692 unsigned k = 0, l = 0;
693
694 l = strv_length(argv);
695
696 ret = new(char*, l+1);
697 if (!ret)
698 return NULL;
699
700 STRV_FOREACH(i, argv) {
701
702 /* If $FOO appears as single word, replace it by the split up variable */
703 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
704 char *e;
705 char **w, **m = NULL;
706 unsigned q;
707
708 e = strv_env_get(env, *i+1);
709 if (e) {
710 int r;
711
712 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
713 if (r < 0) {
714 ret[k] = NULL;
715 strv_free(ret);
716 return NULL;
717 }
718 } else
719 m = NULL;
720
721 q = strv_length(m);
722 l = l + q - 1;
723
724 w = reallocarray(ret, l + 1, sizeof(char *));
725 if (!w) {
726 ret[k] = NULL;
727 strv_free(ret);
728 strv_free(m);
729 return NULL;
730 }
731
732 ret = w;
733 if (m) {
734 memcpy(ret + k, m, q * sizeof(char*));
735 free(m);
736 }
737
738 k += q;
739 continue;
740 }
741
742 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
743 ret[k] = replace_env(*i, env, 0);
744 if (!ret[k]) {
745 strv_free(ret);
746 return NULL;
747 }
748 k++;
749 }
750
751 ret[k] = NULL;
752 return ret;
753 }
754
755 int getenv_bool(const char *p) {
756 const char *e;
757
758 e = getenv(p);
759 if (!e)
760 return -ENXIO;
761
762 return parse_boolean(e);
763 }
764
765 int getenv_bool_secure(const char *p) {
766 const char *e;
767
768 e = secure_getenv(p);
769 if (!e)
770 return -ENXIO;
771
772 return parse_boolean(e);
773 }
774
775 int serialize_environment(FILE *f, char **environment) {
776 char **e;
777
778 STRV_FOREACH(e, environment) {
779 _cleanup_free_ char *ce;
780
781 ce = cescape(*e);
782 if (!ce)
783 return -ENOMEM;
784
785 fprintf(f, "env=%s\n", ce);
786 }
787
788 /* caller should call ferror() */
789
790 return 0;
791 }
792
793 int deserialize_environment(char ***environment, const char *line) {
794 char *uce;
795 int r;
796
797 assert(line);
798 assert(environment);
799
800 assert(startswith(line, "env="));
801 r = cunescape(line + 4, 0, &uce);
802 if (r < 0)
803 return r;
804
805 return strv_env_replace(environment, uce);
806 }