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