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