]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
util: introduce memcmp_safe()
[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 #ifndef ARG_MAX
25 #define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
26 #endif
27
28 static bool env_name_is_valid_n(const char *e, size_t n) {
29 const char *p;
30
31 if (!e)
32 return false;
33
34 if (n <= 0)
35 return false;
36
37 if (e[0] >= '0' && e[0] <= '9')
38 return false;
39
40 /* POSIX says the overall size of the environment block cannot
41 * be > ARG_MAX, an individual assignment hence cannot be
42 * either. Discounting the equal sign and trailing NUL this
43 * hence leaves ARG_MAX-2 as longest possible variable
44 * name. */
45 if (n > ARG_MAX - 2)
46 return false;
47
48 for (p = e; p < e + n; p++)
49 if (!strchr(VALID_CHARS_ENV_NAME, *p))
50 return false;
51
52 return true;
53 }
54
55 bool env_name_is_valid(const char *e) {
56 if (!e)
57 return false;
58
59 return env_name_is_valid_n(e, strlen(e));
60 }
61
62 bool env_value_is_valid(const char *e) {
63 if (!e)
64 return false;
65
66 if (!utf8_is_valid(e))
67 return false;
68
69 /* bash allows tabs and newlines in environment variables, and so
70 * should we */
71 if (string_has_cc(e, "\t\n"))
72 return false;
73
74 /* POSIX says the overall size of the environment block cannot
75 * be > ARG_MAX, an individual assignment hence cannot be
76 * either. Discounting the shortest possible variable name of
77 * length 1, the equal sign and trailing NUL this hence leaves
78 * ARG_MAX-3 as longest possible variable value. */
79 if (strlen(e) > ARG_MAX - 3)
80 return false;
81
82 return true;
83 }
84
85 bool env_assignment_is_valid(const char *e) {
86 const char *eq;
87
88 eq = strchr(e, '=');
89 if (!eq)
90 return false;
91
92 if (!env_name_is_valid_n(e, eq - e))
93 return false;
94
95 if (!env_value_is_valid(eq + 1))
96 return false;
97
98 /* POSIX says the overall size of the environment block cannot
99 * be > ARG_MAX, hence the individual variable assignments
100 * cannot be either, but let's leave room for one trailing NUL
101 * byte. */
102 if (strlen(e) > ARG_MAX - 1)
103 return false;
104
105 return true;
106 }
107
108 bool strv_env_is_valid(char **e) {
109 char **p, **q;
110
111 STRV_FOREACH(p, e) {
112 size_t k;
113
114 if (!env_assignment_is_valid(*p))
115 return false;
116
117 /* Check if there are duplicate assginments */
118 k = strcspn(*p, "=");
119 STRV_FOREACH(q, p + 1)
120 if (strneq(*p, *q, k) && (*q)[k] == '=')
121 return false;
122 }
123
124 return true;
125 }
126
127 bool strv_env_name_is_valid(char **l) {
128 char **p, **q;
129
130 STRV_FOREACH(p, l) {
131 if (!env_name_is_valid(*p))
132 return false;
133
134 STRV_FOREACH(q, p + 1)
135 if (streq(*p, *q))
136 return false;
137 }
138
139 return true;
140 }
141
142 bool strv_env_name_or_assignment_is_valid(char **l) {
143 char **p, **q;
144
145 STRV_FOREACH(p, l) {
146 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
147 return false;
148
149 STRV_FOREACH(q, p + 1)
150 if (streq(*p, *q))
151 return false;
152 }
153
154 return true;
155 }
156
157 static int env_append(char **r, char ***k, char **a) {
158 assert(r);
159 assert(k);
160
161 if (!a)
162 return 0;
163
164 /* Add the entries of a to *k unless they already exist in *r
165 * in which case they are overridden instead. This assumes
166 * there is enough space in the r array. */
167
168 for (; *a; a++) {
169 char **j;
170 size_t n;
171
172 n = strcspn(*a, "=");
173
174 if ((*a)[n] == '=')
175 n++;
176
177 for (j = r; j < *k; j++)
178 if (strneq(*j, *a, n))
179 break;
180
181 if (j >= *k)
182 (*k)++;
183 else
184 free(*j);
185
186 *j = strdup(*a);
187 if (!*j)
188 return -ENOMEM;
189 }
190
191 return 0;
192 }
193
194 char **strv_env_merge(size_t n_lists, ...) {
195 size_t n = 0;
196 char **l, **k, **r;
197 va_list ap;
198 size_t i;
199
200 /* Merges an arbitrary number of environment sets */
201
202 va_start(ap, n_lists);
203 for (i = 0; i < n_lists; i++) {
204 l = va_arg(ap, char**);
205 n += strv_length(l);
206 }
207 va_end(ap);
208
209 r = new(char*, n+1);
210 if (!r)
211 return NULL;
212
213 k = r;
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(r, &k, l) < 0)
219 goto fail;
220 }
221 va_end(ap);
222
223 *k = NULL;
224
225 return r;
226
227 fail:
228 va_end(ap);
229 strv_free(r);
230
231 return NULL;
232 }
233
234 static bool env_match(const char *t, const char *pattern) {
235 assert(t);
236 assert(pattern);
237
238 /* pattern a matches string a
239 * a matches a=
240 * a matches a=b
241 * a= matches a=
242 * a=b matches a=b
243 * a= does not match a
244 * a=b does not match a=
245 * a=b does not match a
246 * a=b does not match a=c */
247
248 if (streq(t, pattern))
249 return true;
250
251 if (!strchr(pattern, '=')) {
252 size_t l = strlen(pattern);
253
254 return strneq(t, pattern, l) && t[l] == '=';
255 }
256
257 return false;
258 }
259
260 static bool env_entry_has_name(const char *entry, const char *name) {
261 const char *t;
262
263 assert(entry);
264 assert(name);
265
266 t = startswith(entry, name);
267 if (!t)
268 return false;
269
270 return *t == '=';
271 }
272
273 char **strv_env_delete(char **x, size_t n_lists, ...) {
274 size_t n, i = 0;
275 char **k, **r;
276 va_list ap;
277
278 /* Deletes every entry from x that is mentioned in the other
279 * string lists */
280
281 n = strv_length(x);
282
283 r = new(char*, n+1);
284 if (!r)
285 return NULL;
286
287 STRV_FOREACH(k, x) {
288 size_t v;
289
290 va_start(ap, n_lists);
291 for (v = 0; v < n_lists; v++) {
292 char **l, **j;
293
294 l = va_arg(ap, char**);
295 STRV_FOREACH(j, l)
296 if (env_match(*k, *j))
297 goto skip;
298 }
299 va_end(ap);
300
301 r[i] = strdup(*k);
302 if (!r[i]) {
303 strv_free(r);
304 return NULL;
305 }
306
307 i++;
308 continue;
309
310 skip:
311 va_end(ap);
312 }
313
314 r[i] = NULL;
315
316 assert(i <= n);
317
318 return r;
319 }
320
321 char **strv_env_unset(char **l, const char *p) {
322
323 char **f, **t;
324
325 if (!l)
326 return NULL;
327
328 assert(p);
329
330 /* Drops every occurrence of the env var setting p in the
331 * string list. Edits in-place. */
332
333 for (f = t = l; *f; f++) {
334
335 if (env_match(*f, p)) {
336 free(*f);
337 continue;
338 }
339
340 *(t++) = *f;
341 }
342
343 *t = NULL;
344 return l;
345 }
346
347 char **strv_env_unset_many(char **l, ...) {
348
349 char **f, **t;
350
351 if (!l)
352 return NULL;
353
354 /* Like strv_env_unset() but applies many at once. Edits in-place. */
355
356 for (f = t = l; *f; f++) {
357 bool found = false;
358 const char *p;
359 va_list ap;
360
361 va_start(ap, l);
362
363 while ((p = va_arg(ap, const char*))) {
364 if (env_match(*f, p)) {
365 found = true;
366 break;
367 }
368 }
369
370 va_end(ap);
371
372 if (found) {
373 free(*f);
374 continue;
375 }
376
377 *(t++) = *f;
378 }
379
380 *t = NULL;
381 return l;
382 }
383
384 int strv_env_replace(char ***l, char *p) {
385 char **f;
386 const char *t, *name;
387
388 assert(p);
389
390 /* Replace first occurrence of the env var or add a new one in the
391 * string list. Drop other occurences. Edits in-place. Does not copy p.
392 * p must be a valid key=value assignment.
393 */
394
395 t = strchr(p, '=');
396 assert(t);
397
398 name = strndupa(p, t - p);
399
400 for (f = *l; f && *f; f++)
401 if (env_entry_has_name(*f, name)) {
402 free_and_replace(*f, p);
403 strv_env_unset(f + 1, *f);
404 return 0;
405 }
406
407 /* We didn't find a match, we need to append p or create a new strv */
408 if (strv_push(l, p) < 0)
409 return -ENOMEM;
410 return 1;
411 }
412
413 char **strv_env_set(char **x, const char *p) {
414
415 char **k;
416 _cleanup_strv_free_ char **r = NULL;
417 char* m[2] = { (char*) p, NULL };
418
419 /* Overrides the env var setting of p, returns a new copy */
420
421 r = new(char*, strv_length(x)+2);
422 if (!r)
423 return NULL;
424
425 k = r;
426 if (env_append(r, &k, x) < 0)
427 return NULL;
428
429 if (env_append(r, &k, m) < 0)
430 return NULL;
431
432 *k = NULL;
433
434 return TAKE_PTR(r);
435 }
436
437 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
438 char **i;
439
440 assert(name);
441
442 if (k <= 0)
443 return NULL;
444
445 STRV_FOREACH_BACKWARDS(i, l)
446 if (strneq(*i, name, k) &&
447 (*i)[k] == '=')
448 return *i + k + 1;
449
450 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
451 const char *t;
452
453 t = strndupa(name, k);
454 return getenv(t);
455 };
456
457 return NULL;
458 }
459
460 char *strv_env_get(char **l, const char *name) {
461 assert(name);
462
463 return strv_env_get_n(l, name, strlen(name), 0);
464 }
465
466 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
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)) {
475 if (invalid_callback)
476 invalid_callback(*p, userdata);
477 free(*p);
478 continue;
479 }
480
481 n = strcspn(*p, "=");
482 STRV_FOREACH(q, p + 1)
483 if (strneq(*p, *q, n) && (*q)[n] == '=') {
484 duplicate = true;
485 break;
486 }
487
488 if (duplicate) {
489 free(*p);
490 continue;
491 }
492
493 e[k++] = *p;
494 }
495
496 if (e)
497 e[k] = NULL;
498
499 return e;
500 }
501
502 char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
503 enum {
504 WORD,
505 CURLY,
506 VARIABLE,
507 VARIABLE_RAW,
508 TEST,
509 DEFAULT_VALUE,
510 ALTERNATE_VALUE,
511 } state = WORD;
512
513 const char *e, *word = format, *test_value;
514 char *k;
515 _cleanup_free_ char *r = NULL;
516 size_t i, len;
517 int nest = 0;
518
519 assert(format);
520
521 for (e = format, i = 0; *e && i < n; e ++, i ++)
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)
533 return NULL;
534
535 free_and_replace(r, k);
536
537 word = e-1;
538 state = VARIABLE;
539 nest++;
540 } else if (*e == '$') {
541 k = strnappend(r, word, e-word);
542 if (!k)
543 return NULL;
544
545 free_and_replace(r, k);
546
547 word = e+1;
548 state = WORD;
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
555 free_and_replace(r, k);
556
557 word = e-1;
558 state = VARIABLE_RAW;
559
560 } else
561 state = WORD;
562 break;
563
564 case VARIABLE:
565 if (*e == '}') {
566 const char *t;
567
568 t = strv_env_get_n(env, word+2, e-word-2, flags);
569
570 k = strappend(r, t);
571 if (!k)
572 return NULL;
573
574 free_and_replace(r, k);
575
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--;
615 if (nest == 0) {
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
630 free_and_replace(r, k);
631
632 word = e+1;
633 state = WORD;
634 }
635 break;
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
649 free_and_replace(r, k);
650
651 word = e--;
652 i--;
653 state = WORD;
654 }
655 break;
656 }
657
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);
667 }
668
669 char **replace_env_argv(char **argv, char **env) {
670 char **ret, **i;
671 size_t k = 0, l = 0;
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 */
682 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
683 char *e;
684 char **w, **m = NULL;
685 size_t q;
686
687 e = strv_env_get(env, *i+1);
688 if (e) {
689 int r;
690
691 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
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
703 w = reallocarray(ret, l + 1, sizeof(char *));
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 */
722 ret[k] = replace_env(*i, env, 0);
723 if (!ret[k]) {
724 strv_free(ret);
725 return NULL;
726 }
727 k++;
728 }
729
730 ret[k] = NULL;
731 return ret;
732 }
733
734 int 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 }
743
744 int 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 }
753
754 int serialize_environment(FILE *f, char **environment) {
755 char **e;
756
757 STRV_FOREACH(e, environment) {
758 _cleanup_free_ char *ce;
759
760 ce = cescape(*e);
761 if (!ce)
762 return -ENOMEM;
763
764 fprintf(f, "env=%s\n", ce);
765 }
766
767 /* caller should call ferror() */
768
769 return 0;
770 }
771
772 int deserialize_environment(char ***environment, const char *line) {
773 char *uce;
774 int r;
775
776 assert(line);
777 assert(environment);
778
779 assert(startswith(line, "env="));
780 r = cunescape(line + 4, 0, &uce);
781 if (r < 0)
782 return r;
783
784 return strv_env_replace(environment, uce);
785 }