chiark / gitweb /
Prep v238: Uncomment now needed headers and unmask now needed functions in 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                 switch (state) {
545
546                 case WORD:
547                         if (*e == '$')
548                                 state = CURLY;
549                         break;
550
551                 case CURLY:
552                         if (*e == '{') {
553                                 k = strnappend(r, word, e-word-1);
554                                 if (!k)
555                                         return NULL;
556
557                                 free_and_replace(r, k);
558
559                                 word = e-1;
560                                 state = VARIABLE;
561                                 nest++;
562                         } else if (*e == '$') {
563                                 k = strnappend(r, word, e-word);
564                                 if (!k)
565                                         return NULL;
566
567                                 free_and_replace(r, k);
568
569                                 word = e+1;
570                                 state = WORD;
571
572                         } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
573                                 k = strnappend(r, word, e-word-1);
574                                 if (!k)
575                                         return NULL;
576
577                                 free_and_replace(r, k);
578
579                                 word = e-1;
580                                 state = VARIABLE_RAW;
581
582                         } else
583                                 state = WORD;
584                         break;
585
586                 case VARIABLE:
587                         if (*e == '}') {
588                                 const char *t;
589
590                                 t = strv_env_get_n(env, word+2, e-word-2, flags);
591
592                                 k = strappend(r, t);
593                                 if (!k)
594                                         return NULL;
595
596                                 free_and_replace(r, k);
597
598                                 word = e+1;
599                                 state = WORD;
600                         } else if (*e == ':') {
601                                 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
602                                         /* Treat this as unsupported syntax, i.e. do no replacement */
603                                         state = WORD;
604                                 else {
605                                         len = e-word-2;
606                                         state = TEST;
607                                 }
608                         }
609                         break;
610
611                 case TEST:
612                         if (*e == '-')
613                                 state = DEFAULT_VALUE;
614                         else if (*e == '+')
615                                 state = ALTERNATE_VALUE;
616                         else {
617                                 state = WORD;
618                                 break;
619                         }
620
621                         test_value = e+1;
622                         break;
623
624                 case DEFAULT_VALUE: /* fall through */
625                 case ALTERNATE_VALUE:
626                         assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
627
628                         if (*e == '{') {
629                                 nest++;
630                                 break;
631                         }
632
633                         if (*e != '}')
634                                 break;
635
636                         nest--;
637                         if (nest == 0) {
638                                 const char *t;
639                                 _cleanup_free_ char *v = NULL;
640
641                                 t = strv_env_get_n(env, word+2, len, flags);
642
643                                 if (t && state == ALTERNATE_VALUE)
644                                         t = v = replace_env_n(test_value, e-test_value, env, flags);
645                                 else if (!t && state == DEFAULT_VALUE)
646                                         t = v = replace_env_n(test_value, e-test_value, env, flags);
647
648                                 k = strappend(r, t);
649                                 if (!k)
650                                         return NULL;
651
652                                 free_and_replace(r, k);
653
654                                 word = e+1;
655                                 state = WORD;
656                         }
657                         break;
658
659                 case VARIABLE_RAW:
660                         assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
661
662                         if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
663                                 const char *t;
664
665                                 t = strv_env_get_n(env, word+1, e-word-1, flags);
666
667                                 k = strappend(r, t);
668                                 if (!k)
669                                         return NULL;
670
671                                 free_and_replace(r, k);
672
673                                 word = e--;
674                                 i--;
675                                 state = WORD;
676                         }
677                         break;
678                 }
679
680         if (state == VARIABLE_RAW) {
681                 const char *t;
682
683                 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
684
685                 t = strv_env_get_n(env, word+1, e-word-1, flags);
686                 return strappend(r, t);
687         } else
688                 return strnappend(r, word, e-word);
689 }
690
691 char **replace_env_argv(char **argv, char **env) {
692         char **ret, **i;
693         unsigned k = 0, l = 0;
694
695         l = strv_length(argv);
696
697         ret = new(char*, l+1);
698         if (!ret)
699                 return NULL;
700
701         STRV_FOREACH(i, argv) {
702
703                 /* If $FOO appears as single word, replace it by the split up variable */
704                 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
705                         char *e;
706                         char **w, **m = NULL;
707                         unsigned q;
708
709                         e = strv_env_get(env, *i+1);
710                         if (e) {
711                                 int r;
712
713                                 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
714                                 if (r < 0) {
715                                         ret[k] = NULL;
716                                         strv_free(ret);
717                                         return NULL;
718                                 }
719                         } else
720                                 m = NULL;
721
722                         q = strv_length(m);
723                         l = l + q - 1;
724
725                         w = reallocarray(ret, l + 1, sizeof(char *));
726                         if (!w) {
727                                 ret[k] = NULL;
728                                 strv_free(ret);
729                                 strv_free(m);
730                                 return NULL;
731                         }
732
733                         ret = w;
734                         if (m) {
735                                 memcpy(ret + k, m, q * sizeof(char*));
736                                 free(m);
737                         }
738
739                         k += q;
740                         continue;
741                 }
742
743                 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
744                 ret[k] = replace_env(*i, env, 0);
745                 if (!ret[k]) {
746                         strv_free(ret);
747                         return NULL;
748                 }
749                 k++;
750         }
751
752         ret[k] = NULL;
753         return ret;
754 }
755 #endif // 0
756
757 int getenv_bool(const char *p) {
758         const char *e;
759
760         e = getenv(p);
761         if (!e)
762                 return -ENXIO;
763
764         return parse_boolean(e);
765 }
766
767 #if 0 /// UNNEEDED by elogind
768 int getenv_bool_secure(const char *p) {
769         const char *e;
770
771         e = secure_getenv(p);
772         if (!e)
773                 return -ENXIO;
774
775         return parse_boolean(e);
776 }
777
778 int serialize_environment(FILE *f, char **environment) {
779         char **e;
780
781         STRV_FOREACH(e, environment) {
782                 _cleanup_free_ char *ce;
783
784                 ce = cescape(*e);
785                 if (!ce)
786                         return -ENOMEM;
787
788                 fprintf(f, "env=%s\n", ce);
789         }
790
791         /* caller should call ferror() */
792
793         return 0;
794 }
795
796 int deserialize_environment(char ***environment, const char *line) {
797         char *uce;
798         int r;
799
800         assert(line);
801         assert(environment);
802
803         assert(startswith(line, "env="));
804         r = cunescape(line + 4, 0, &uce);
805         if (r < 0)
806                 return r;
807
808         return strv_env_replace(environment, uce);
809 }
810 #endif // 0