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