chiark / gitweb /
tree-wide: drop license boilerplate
[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(unsigned n_lists, ...) {
201         size_t n = 0;
202         char **l, **k, **r;
203         va_list ap;
204         unsigned 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, unsigned 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                 unsigned 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, **r;
422         char* m[2] = { (char*) p, NULL };
423
424         /* Overrides the env var setting of p, returns a new copy */
425
426         r = new(char*, strv_length(x)+2);
427         if (!r)
428                 return NULL;
429
430         k = r;
431         if (env_append(r, &k, x) < 0)
432                 goto fail;
433
434         if (env_append(r, &k, m) < 0)
435                 goto fail;
436
437         *k = NULL;
438
439         return r;
440
441 fail:
442         strv_free(r);
443         return NULL;
444 }
445
446 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
447         char **i;
448
449         assert(name);
450
451         if (k <= 0)
452                 return NULL;
453
454         STRV_FOREACH_BACKWARDS(i, l)
455                 if (strneq(*i, name, k) &&
456                     (*i)[k] == '=')
457                         return *i + k + 1;
458
459         if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
460                 const char *t;
461
462                 t = strndupa(name, k);
463                 return getenv(t);
464         };
465
466         return NULL;
467 }
468
469 char *strv_env_get(char **l, const char *name) {
470         assert(name);
471
472         return strv_env_get_n(l, name, strlen(name), 0);
473 }
474
475 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
476         char **p, **q;
477         int k = 0;
478
479         STRV_FOREACH(p, e) {
480                 size_t n;
481                 bool duplicate = false;
482
483                 if (!env_assignment_is_valid(*p)) {
484                         if (invalid_callback)
485                                 invalid_callback(*p, userdata);
486                         free(*p);
487                         continue;
488                 }
489
490                 n = strcspn(*p, "=");
491                 STRV_FOREACH(q, p + 1)
492                         if (strneq(*p, *q, n) && (*q)[n] == '=') {
493                                 duplicate = true;
494                                 break;
495                         }
496
497                 if (duplicate) {
498                         free(*p);
499                         continue;
500                 }
501
502                 e[k++] = *p;
503         }
504
505         if (e)
506                 e[k] = NULL;
507
508         return e;
509 }
510
511 char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
512         enum {
513                 WORD,
514                 CURLY,
515                 VARIABLE,
516                 VARIABLE_RAW,
517                 TEST,
518                 DEFAULT_VALUE,
519                 ALTERNATE_VALUE,
520         } state = WORD;
521
522         const char *e, *word = format, *test_value;
523         char *k;
524         _cleanup_free_ char *r = NULL;
525         size_t i, len;
526         int nest = 0;
527
528         assert(format);
529
530         for (e = format, i = 0; *e && i < n; e ++, i ++)
531                 switch (state) {
532
533                 case WORD:
534                         if (*e == '$')
535                                 state = CURLY;
536                         break;
537
538                 case CURLY:
539                         if (*e == '{') {
540                                 k = strnappend(r, word, e-word-1);
541                                 if (!k)
542                                         return NULL;
543
544                                 free_and_replace(r, k);
545
546                                 word = e-1;
547                                 state = VARIABLE;
548                                 nest++;
549                         } else if (*e == '$') {
550                                 k = strnappend(r, word, e-word);
551                                 if (!k)
552                                         return NULL;
553
554                                 free_and_replace(r, k);
555
556                                 word = e+1;
557                                 state = WORD;
558
559                         } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
560                                 k = strnappend(r, word, e-word-1);
561                                 if (!k)
562                                         return NULL;
563
564                                 free_and_replace(r, k);
565
566                                 word = e-1;
567                                 state = VARIABLE_RAW;
568
569                         } else
570                                 state = WORD;
571                         break;
572
573                 case VARIABLE:
574                         if (*e == '}') {
575                                 const char *t;
576
577                                 t = strv_env_get_n(env, word+2, e-word-2, flags);
578
579                                 k = strappend(r, t);
580                                 if (!k)
581                                         return NULL;
582
583                                 free_and_replace(r, k);
584
585                                 word = e+1;
586                                 state = WORD;
587                         } else if (*e == ':') {
588                                 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
589                                         /* Treat this as unsupported syntax, i.e. do no replacement */
590                                         state = WORD;
591                                 else {
592                                         len = e-word-2;
593                                         state = TEST;
594                                 }
595                         }
596                         break;
597
598                 case TEST:
599                         if (*e == '-')
600                                 state = DEFAULT_VALUE;
601                         else if (*e == '+')
602                                 state = ALTERNATE_VALUE;
603                         else {
604                                 state = WORD;
605                                 break;
606                         }
607
608                         test_value = e+1;
609                         break;
610
611                 case DEFAULT_VALUE: /* fall through */
612                 case ALTERNATE_VALUE:
613                         assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
614
615                         if (*e == '{') {
616                                 nest++;
617                                 break;
618                         }
619
620                         if (*e != '}')
621                                 break;
622
623                         nest--;
624                         if (nest == 0) {
625                                 const char *t;
626                                 _cleanup_free_ char *v = NULL;
627
628                                 t = strv_env_get_n(env, word+2, len, flags);
629
630                                 if (t && state == ALTERNATE_VALUE)
631                                         t = v = replace_env_n(test_value, e-test_value, env, flags);
632                                 else if (!t && state == DEFAULT_VALUE)
633                                         t = v = replace_env_n(test_value, e-test_value, env, flags);
634
635                                 k = strappend(r, t);
636                                 if (!k)
637                                         return NULL;
638
639                                 free_and_replace(r, k);
640
641                                 word = e+1;
642                                 state = WORD;
643                         }
644                         break;
645
646                 case VARIABLE_RAW:
647                         assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
648
649                         if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
650                                 const char *t;
651
652                                 t = strv_env_get_n(env, word+1, e-word-1, flags);
653
654                                 k = strappend(r, t);
655                                 if (!k)
656                                         return NULL;
657
658                                 free_and_replace(r, k);
659
660                                 word = e--;
661                                 i--;
662                                 state = WORD;
663                         }
664                         break;
665                 }
666
667         if (state == VARIABLE_RAW) {
668                 const char *t;
669
670                 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
671
672                 t = strv_env_get_n(env, word+1, e-word-1, flags);
673                 return strappend(r, t);
674         } else
675                 return strnappend(r, word, e-word);
676 }
677
678 char **replace_env_argv(char **argv, char **env) {
679         char **ret, **i;
680         unsigned k = 0, l = 0;
681
682         l = strv_length(argv);
683
684         ret = new(char*, l+1);
685         if (!ret)
686                 return NULL;
687
688         STRV_FOREACH(i, argv) {
689
690                 /* If $FOO appears as single word, replace it by the split up variable */
691                 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
692                         char *e;
693                         char **w, **m = NULL;
694                         unsigned q;
695
696                         e = strv_env_get(env, *i+1);
697                         if (e) {
698                                 int r;
699
700                                 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
701                                 if (r < 0) {
702                                         ret[k] = NULL;
703                                         strv_free(ret);
704                                         return NULL;
705                                 }
706                         } else
707                                 m = NULL;
708
709                         q = strv_length(m);
710                         l = l + q - 1;
711
712                         w = reallocarray(ret, l + 1, sizeof(char *));
713                         if (!w) {
714                                 ret[k] = NULL;
715                                 strv_free(ret);
716                                 strv_free(m);
717                                 return NULL;
718                         }
719
720                         ret = w;
721                         if (m) {
722                                 memcpy(ret + k, m, q * sizeof(char*));
723                                 free(m);
724                         }
725
726                         k += q;
727                         continue;
728                 }
729
730                 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
731                 ret[k] = replace_env(*i, env, 0);
732                 if (!ret[k]) {
733                         strv_free(ret);
734                         return NULL;
735                 }
736                 k++;
737         }
738
739         ret[k] = NULL;
740         return ret;
741 }
742 #endif // 0
743
744 int getenv_bool(const char *p) {
745         const char *e;
746
747         e = getenv(p);
748         if (!e)
749                 return -ENXIO;
750
751         return parse_boolean(e);
752 }
753
754 #if 0 /// UNNEEDED by elogind
755 int getenv_bool_secure(const char *p) {
756         const char *e;
757
758         e = secure_getenv(p);
759         if (!e)
760                 return -ENXIO;
761
762         return parse_boolean(e);
763 }
764
765 int serialize_environment(FILE *f, char **environment) {
766         char **e;
767
768         STRV_FOREACH(e, environment) {
769                 _cleanup_free_ char *ce;
770
771                 ce = cescape(*e);
772                 if (!ce)
773                         return -ENOMEM;
774
775                 fprintf(f, "env=%s\n", ce);
776         }
777
778         /* caller should call ferror() */
779
780         return 0;
781 }
782
783 int deserialize_environment(char ***environment, const char *line) {
784         char *uce;
785         int r;
786
787         assert(line);
788         assert(environment);
789
790         assert(startswith(line, "env="));
791         r = cunescape(line + 4, 0, &uce);
792         if (r < 0)
793                 return r;
794
795         return strv_env_replace(environment, uce);
796 }
797 #endif // 0