]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/env-util.c
basic/fileio: add helper function for a set of two common checks
[thirdparty/systemd.git] / src / basic / env-util.c
CommitLineData
4d1a6904
LP
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
11c3a366 20#include <errno.h>
4d1a6904 21#include <limits.h>
11c3a366
TA
22#include <stdarg.h>
23#include <stdlib.h>
24#include <string.h>
4d1a6904
LP
25#include <unistd.h>
26
b5efdb8a 27#include "alloc-util.h"
b5efdb8a 28#include "env-util.h"
fe902fa4 29#include "escape.h"
11c3a366
TA
30#include "extract-word.h"
31#include "macro.h"
93cc7779 32#include "parse-util.h"
07630cea 33#include "string-util.h"
4d1a6904
LP
34#include "strv.h"
35#include "utf8.h"
4d1a6904
LP
36
37#define VALID_CHARS_ENV_NAME \
4b549144 38 DIGITS LETTERS \
4d1a6904
LP
39 "_"
40
41#ifndef ARG_MAX
42#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
43#endif
44
45static 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
72bool 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
79bool env_value_is_valid(const char *e) {
80 if (!e)
81 return false;
82
83 if (!utf8_is_valid(e))
84 return false;
85
6294aa76
LP
86 /* bash allows tabs in environment variables, and so should
87 * we */
88 if (string_has_cc(e, "\t"))
4d1a6904
LP
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
102bool 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
5f9cfd4c 117 * cannot be either, but let's leave room for one trailing NUL
4d1a6904
LP
118 * byte. */
119 if (strlen(e) > ARG_MAX - 1)
120 return false;
121
122 return true;
123}
124
125bool 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)
641906e9 137 if (strneq(*p, *q, k) && (*q)[k] == '=')
4d1a6904
LP
138 return false;
139 }
140
141 return true;
142}
143
b4c14404
FB
144bool 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
123b964a
LP
159bool 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
4d1a6904
LP
174static 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++)
641906e9 195 if (strneq(*j, *a, n))
4d1a6904
LP
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
211char **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
244fail:
245 va_end(ap);
246 strv_free(r);
247
248 return NULL;
249}
250
44a6b1b6 251_pure_ static bool env_match(const char *t, const char *pattern) {
4d1a6904
LP
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
641906e9 271 return strneq(t, pattern, l) && t[l] == '=';
4d1a6904
LP
272 }
273
274 return false;
275}
276
277char **strv_env_delete(char **x, unsigned n_lists, ...) {
278 size_t n, i = 0;
279 char **k, **r;
280 va_list ap;
281
282 /* Deletes every entry from x that is mentioned in the other
283 * string lists */
284
285 n = strv_length(x);
286
287 r = new(char*, n+1);
288 if (!r)
289 return NULL;
290
291 STRV_FOREACH(k, x) {
292 unsigned v;
293
294 va_start(ap, n_lists);
295 for (v = 0; v < n_lists; v++) {
296 char **l, **j;
297
298 l = va_arg(ap, char**);
299 STRV_FOREACH(j, l)
300 if (env_match(*k, *j))
301 goto skip;
302 }
303 va_end(ap);
304
305 r[i] = strdup(*k);
306 if (!r[i]) {
307 strv_free(r);
308 return NULL;
309 }
310
311 i++;
312 continue;
313
314 skip:
315 va_end(ap);
316 }
317
318 r[i] = NULL;
319
320 assert(i <= n);
321
322 return r;
323}
324
325char **strv_env_unset(char **l, const char *p) {
326
327 char **f, **t;
328
329 if (!l)
330 return NULL;
331
332 assert(p);
333
334 /* Drops every occurrence of the env var setting p in the
43d03a83 335 * string list. Edits in-place. */
4d1a6904
LP
336
337 for (f = t = l; *f; f++) {
338
339 if (env_match(*f, p)) {
340 free(*f);
341 continue;
342 }
343
344 *(t++) = *f;
345 }
43d03a83
LP
346
347 *t = NULL;
348 return l;
349}
350
351char **strv_env_unset_many(char **l, ...) {
352
353 char **f, **t;
354
355 if (!l)
356 return NULL;
357
358 /* Like strv_env_unset() but applies many at once. Edits in-place. */
359
360 for (f = t = l; *f; f++) {
361 bool found = false;
362 const char *p;
363 va_list ap;
364
365 va_start(ap, l);
366
367 while ((p = va_arg(ap, const char*))) {
368 if (env_match(*f, p)) {
369 found = true;
370 break;
371 }
372 }
373
374 va_end(ap);
375
376 if (found) {
377 free(*f);
378 continue;
379 }
380
381 *(t++) = *f;
382 }
4d1a6904
LP
383
384 *t = NULL;
385 return l;
386}
387
54ac3494
ZJS
388int strv_env_replace(char ***l, char *p) {
389 char **f;
390
391 assert(p);
392
393 /* Replace first occurrence of the env var or add a new one in the
394 * string list. Drop other occurences. Edits in-place. Does not copy p.
395 */
396
397 for (f = *l; f && *f; f++)
398 if (env_match(*f, p)) {
a1e45b8b
ZJS
399 free(*f);
400 *f = p;
54ac3494
ZJS
401 strv_env_unset(f + 1, p);
402 return 0;
403 }
404
405 /* We didn't find a match, we need to append p or create a new strv */
406 if (strv_push(l, p) < 0)
407 return -ENOMEM;
408 return 1;
409}
410
4d1a6904
LP
411char **strv_env_set(char **x, const char *p) {
412
413 char **k, **r;
414 char* m[2] = { (char*) p, NULL };
415
416 /* Overrides the env var setting of p, returns a new copy */
417
418 r = new(char*, strv_length(x)+2);
419 if (!r)
420 return NULL;
421
422 k = r;
423 if (env_append(r, &k, x) < 0)
424 goto fail;
425
426 if (env_append(r, &k, m) < 0)
427 goto fail;
428
429 *k = NULL;
430
431 return r;
432
433fail:
434 strv_free(r);
435 return NULL;
436}
437
438char *strv_env_get_n(char **l, const char *name, size_t k) {
439 char **i;
440
441 assert(name);
442
443 if (k <= 0)
444 return NULL;
445
446 STRV_FOREACH(i, l)
641906e9 447 if (strneq(*i, name, k) &&
4d1a6904
LP
448 (*i)[k] == '=')
449 return *i + k + 1;
450
451 return NULL;
452}
453
454char *strv_env_get(char **l, const char *name) {
455 assert(name);
456
457 return strv_env_get_n(l, name, strlen(name));
458}
459
039f0e70 460char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
4d1a6904
LP
461 char **p, **q;
462 int k = 0;
463
464 STRV_FOREACH(p, e) {
465 size_t n;
466 bool duplicate = false;
467
468 if (!env_assignment_is_valid(*p)) {
039f0e70
LP
469 if (invalid_callback)
470 invalid_callback(*p, userdata);
4d1a6904
LP
471 free(*p);
472 continue;
473 }
474
475 n = strcspn(*p, "=");
476 STRV_FOREACH(q, p + 1)
641906e9 477 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
478 duplicate = true;
479 break;
480 }
481
482 if (duplicate) {
483 free(*p);
484 continue;
485 }
486
487 e[k++] = *p;
488 }
489
5b4fb02d
LP
490 if (e)
491 e[k] = NULL;
492
4d1a6904
LP
493 return e;
494}
3c800095
RC
495
496char *replace_env(const char *format, char **env) {
497 enum {
498 WORD,
499 CURLY,
500 VARIABLE
501 } state = WORD;
502
503 const char *e, *word = format;
504 char *r = NULL, *k;
505
506 assert(format);
507
508 for (e = format; *e; e ++) {
509
510 switch (state) {
511
512 case WORD:
513 if (*e == '$')
514 state = CURLY;
515 break;
516
517 case CURLY:
518 if (*e == '{') {
519 k = strnappend(r, word, e-word-1);
520 if (!k)
521 goto fail;
522
523 free(r);
524 r = k;
525
526 word = e-1;
527 state = VARIABLE;
528
529 } else if (*e == '$') {
530 k = strnappend(r, word, e-word);
531 if (!k)
532 goto fail;
533
534 free(r);
535 r = k;
536
537 word = e+1;
538 state = WORD;
539 } else
540 state = WORD;
541 break;
542
543 case VARIABLE:
544 if (*e == '}') {
545 const char *t;
546
547 t = strempty(strv_env_get_n(env, word+2, e-word-2));
548
549 k = strappend(r, t);
550 if (!k)
551 goto fail;
552
553 free(r);
554 r = k;
555
556 word = e+1;
557 state = WORD;
558 }
559 break;
560 }
561 }
562
563 k = strnappend(r, word, e-word);
564 if (!k)
565 goto fail;
566
567 free(r);
568 return k;
569
570fail:
6b430fdb 571 return mfree(r);
3c800095
RC
572}
573
574char **replace_env_argv(char **argv, char **env) {
575 char **ret, **i;
576 unsigned k = 0, l = 0;
577
578 l = strv_length(argv);
579
580 ret = new(char*, l+1);
581 if (!ret)
582 return NULL;
583
584 STRV_FOREACH(i, argv) {
585
586 /* If $FOO appears as single word, replace it by the split up variable */
df553b58 587 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
3c800095
RC
588 char *e;
589 char **w, **m = NULL;
590 unsigned q;
591
592 e = strv_env_get(env, *i+1);
593 if (e) {
594 int r;
595
8adaf7bd 596 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
3c800095
RC
597 if (r < 0) {
598 ret[k] = NULL;
599 strv_free(ret);
600 return NULL;
601 }
602 } else
603 m = NULL;
604
605 q = strv_length(m);
606 l = l + q - 1;
607
608 w = realloc(ret, sizeof(char*) * (l+1));
609 if (!w) {
610 ret[k] = NULL;
611 strv_free(ret);
612 strv_free(m);
613 return NULL;
614 }
615
616 ret = w;
617 if (m) {
618 memcpy(ret + k, m, q * sizeof(char*));
619 free(m);
620 }
621
622 k += q;
623 continue;
624 }
625
626 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
627 ret[k] = replace_env(*i, env);
628 if (!ret[k]) {
629 strv_free(ret);
630 return NULL;
631 }
632 k++;
633 }
634
635 ret[k] = NULL;
636 return ret;
637}
b41b9d2a
LP
638
639int getenv_bool(const char *p) {
640 const char *e;
641
642 e = getenv(p);
643 if (!e)
644 return -ENXIO;
645
646 return parse_boolean(e);
647}
fe902fa4
ZJS
648
649int serialize_environment(FILE *f, char **environment) {
650 char **e;
651
652 STRV_FOREACH(e, environment) {
653 _cleanup_free_ char *ce;
654
655 ce = cescape(*e);
656 if (!ce)
657 return -ENOMEM;
658
659 fprintf(f, "env=%s\n", *e);
660 }
661
662 /* caller should call ferror() */
663
664 return 0;
665}
666
667int deserialize_environment(char ***environment, const char *line) {
668 char *uce = NULL;
669 int r;
670
671 assert(line);
672 assert(environment);
673
674 assert(startswith(line, "env="));
675 r = cunescape(line + 4, UNESCAPE_RELAX, &uce);
676 if (r < 0)
677 return r;
678
679 return strv_env_replace(environment, uce);
680}