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