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