]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
Merge pull request #1607 from keszybz/lz4-remove-v1
[thirdparty/systemd.git] / src / basic / env-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "alloc-util.h"
30 #include "env-util.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 bool strv_env_is_valid(char **e) {
127 char **p, **q;
128
129 STRV_FOREACH(p, e) {
130 size_t k;
131
132 if (!env_assignment_is_valid(*p))
133 return false;
134
135 /* Check if there are duplicate assginments */
136 k = strcspn(*p, "=");
137 STRV_FOREACH(q, p + 1)
138 if (strneq(*p, *q, k) && (*q)[k] == '=')
139 return false;
140 }
141
142 return true;
143 }
144
145 bool strv_env_name_is_valid(char **l) {
146 char **p, **q;
147
148 STRV_FOREACH(p, l) {
149 if (!env_name_is_valid(*p))
150 return false;
151
152 STRV_FOREACH(q, p + 1)
153 if (streq(*p, *q))
154 return false;
155 }
156
157 return true;
158 }
159
160 bool strv_env_name_or_assignment_is_valid(char **l) {
161 char **p, **q;
162
163 STRV_FOREACH(p, l) {
164 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
165 return false;
166
167 STRV_FOREACH(q, p + 1)
168 if (streq(*p, *q))
169 return false;
170 }
171
172 return true;
173 }
174
175 static int env_append(char **r, char ***k, char **a) {
176 assert(r);
177 assert(k);
178
179 if (!a)
180 return 0;
181
182 /* Add the entries of a to *k unless they already exist in *r
183 * in which case they are overridden instead. This assumes
184 * there is enough space in the r array. */
185
186 for (; *a; a++) {
187 char **j;
188 size_t n;
189
190 n = strcspn(*a, "=");
191
192 if ((*a)[n] == '=')
193 n++;
194
195 for (j = r; j < *k; j++)
196 if (strneq(*j, *a, n))
197 break;
198
199 if (j >= *k)
200 (*k)++;
201 else
202 free(*j);
203
204 *j = strdup(*a);
205 if (!*j)
206 return -ENOMEM;
207 }
208
209 return 0;
210 }
211
212 char **strv_env_merge(unsigned n_lists, ...) {
213 size_t n = 0;
214 char **l, **k, **r;
215 va_list ap;
216 unsigned i;
217
218 /* Merges an arbitrary number of environment sets */
219
220 va_start(ap, n_lists);
221 for (i = 0; i < n_lists; i++) {
222 l = va_arg(ap, char**);
223 n += strv_length(l);
224 }
225 va_end(ap);
226
227 r = new(char*, n+1);
228 if (!r)
229 return NULL;
230
231 k = r;
232
233 va_start(ap, n_lists);
234 for (i = 0; i < n_lists; i++) {
235 l = va_arg(ap, char**);
236 if (env_append(r, &k, l) < 0)
237 goto fail;
238 }
239 va_end(ap);
240
241 *k = NULL;
242
243 return r;
244
245 fail:
246 va_end(ap);
247 strv_free(r);
248
249 return NULL;
250 }
251
252 _pure_ static bool env_match(const char *t, const char *pattern) {
253 assert(t);
254 assert(pattern);
255
256 /* pattern a matches string a
257 * a matches a=
258 * a matches a=b
259 * a= matches a=
260 * a=b matches a=b
261 * a= does not match a
262 * a=b does not match a=
263 * a=b does not match a
264 * a=b does not match a=c */
265
266 if (streq(t, pattern))
267 return true;
268
269 if (!strchr(pattern, '=')) {
270 size_t l = strlen(pattern);
271
272 return strneq(t, pattern, l) && t[l] == '=';
273 }
274
275 return false;
276 }
277
278 char **strv_env_delete(char **x, unsigned n_lists, ...) {
279 size_t n, i = 0;
280 char **k, **r;
281 va_list ap;
282
283 /* Deletes every entry from x that is mentioned in the other
284 * string lists */
285
286 n = strv_length(x);
287
288 r = new(char*, n+1);
289 if (!r)
290 return NULL;
291
292 STRV_FOREACH(k, x) {
293 unsigned v;
294
295 va_start(ap, n_lists);
296 for (v = 0; v < n_lists; v++) {
297 char **l, **j;
298
299 l = va_arg(ap, char**);
300 STRV_FOREACH(j, l)
301 if (env_match(*k, *j))
302 goto skip;
303 }
304 va_end(ap);
305
306 r[i] = strdup(*k);
307 if (!r[i]) {
308 strv_free(r);
309 return NULL;
310 }
311
312 i++;
313 continue;
314
315 skip:
316 va_end(ap);
317 }
318
319 r[i] = NULL;
320
321 assert(i <= n);
322
323 return r;
324 }
325
326 char **strv_env_unset(char **l, const char *p) {
327
328 char **f, **t;
329
330 if (!l)
331 return NULL;
332
333 assert(p);
334
335 /* Drops every occurrence of the env var setting p in the
336 * string list. Edits in-place. */
337
338 for (f = t = l; *f; f++) {
339
340 if (env_match(*f, p)) {
341 free(*f);
342 continue;
343 }
344
345 *(t++) = *f;
346 }
347
348 *t = NULL;
349 return l;
350 }
351
352 char **strv_env_unset_many(char **l, ...) {
353
354 char **f, **t;
355
356 if (!l)
357 return NULL;
358
359 /* Like strv_env_unset() but applies many at once. Edits in-place. */
360
361 for (f = t = l; *f; f++) {
362 bool found = false;
363 const char *p;
364 va_list ap;
365
366 va_start(ap, l);
367
368 while ((p = va_arg(ap, const char*))) {
369 if (env_match(*f, p)) {
370 found = true;
371 break;
372 }
373 }
374
375 va_end(ap);
376
377 if (found) {
378 free(*f);
379 continue;
380 }
381
382 *(t++) = *f;
383 }
384
385 *t = NULL;
386 return l;
387 }
388
389 char **strv_env_set(char **x, const char *p) {
390
391 char **k, **r;
392 char* m[2] = { (char*) p, NULL };
393
394 /* Overrides the env var setting of p, returns a new copy */
395
396 r = new(char*, strv_length(x)+2);
397 if (!r)
398 return NULL;
399
400 k = r;
401 if (env_append(r, &k, x) < 0)
402 goto fail;
403
404 if (env_append(r, &k, m) < 0)
405 goto fail;
406
407 *k = NULL;
408
409 return r;
410
411 fail:
412 strv_free(r);
413 return NULL;
414 }
415
416 char *strv_env_get_n(char **l, const char *name, size_t k) {
417 char **i;
418
419 assert(name);
420
421 if (k <= 0)
422 return NULL;
423
424 STRV_FOREACH(i, l)
425 if (strneq(*i, name, k) &&
426 (*i)[k] == '=')
427 return *i + k + 1;
428
429 return NULL;
430 }
431
432 char *strv_env_get(char **l, const char *name) {
433 assert(name);
434
435 return strv_env_get_n(l, name, strlen(name));
436 }
437
438 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
439 char **p, **q;
440 int k = 0;
441
442 STRV_FOREACH(p, e) {
443 size_t n;
444 bool duplicate = false;
445
446 if (!env_assignment_is_valid(*p)) {
447 if (invalid_callback)
448 invalid_callback(*p, userdata);
449 free(*p);
450 continue;
451 }
452
453 n = strcspn(*p, "=");
454 STRV_FOREACH(q, p + 1)
455 if (strneq(*p, *q, n) && (*q)[n] == '=') {
456 duplicate = true;
457 break;
458 }
459
460 if (duplicate) {
461 free(*p);
462 continue;
463 }
464
465 e[k++] = *p;
466 }
467
468 if (e)
469 e[k] = NULL;
470
471 return e;
472 }
473
474 char *replace_env(const char *format, char **env) {
475 enum {
476 WORD,
477 CURLY,
478 VARIABLE
479 } state = WORD;
480
481 const char *e, *word = format;
482 char *r = NULL, *k;
483
484 assert(format);
485
486 for (e = format; *e; e ++) {
487
488 switch (state) {
489
490 case WORD:
491 if (*e == '$')
492 state = CURLY;
493 break;
494
495 case CURLY:
496 if (*e == '{') {
497 k = strnappend(r, word, e-word-1);
498 if (!k)
499 goto fail;
500
501 free(r);
502 r = k;
503
504 word = e-1;
505 state = VARIABLE;
506
507 } else if (*e == '$') {
508 k = strnappend(r, word, e-word);
509 if (!k)
510 goto fail;
511
512 free(r);
513 r = k;
514
515 word = e+1;
516 state = WORD;
517 } else
518 state = WORD;
519 break;
520
521 case VARIABLE:
522 if (*e == '}') {
523 const char *t;
524
525 t = strempty(strv_env_get_n(env, word+2, e-word-2));
526
527 k = strappend(r, t);
528 if (!k)
529 goto fail;
530
531 free(r);
532 r = k;
533
534 word = e+1;
535 state = WORD;
536 }
537 break;
538 }
539 }
540
541 k = strnappend(r, word, e-word);
542 if (!k)
543 goto fail;
544
545 free(r);
546 return k;
547
548 fail:
549 free(r);
550 return NULL;
551 }
552
553 char **replace_env_argv(char **argv, char **env) {
554 char **ret, **i;
555 unsigned k = 0, l = 0;
556
557 l = strv_length(argv);
558
559 ret = new(char*, l+1);
560 if (!ret)
561 return NULL;
562
563 STRV_FOREACH(i, argv) {
564
565 /* If $FOO appears as single word, replace it by the split up variable */
566 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
567 char *e;
568 char **w, **m = NULL;
569 unsigned q;
570
571 e = strv_env_get(env, *i+1);
572 if (e) {
573 int r;
574
575 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
576 if (r < 0) {
577 ret[k] = NULL;
578 strv_free(ret);
579 return NULL;
580 }
581 } else
582 m = NULL;
583
584 q = strv_length(m);
585 l = l + q - 1;
586
587 w = realloc(ret, sizeof(char*) * (l+1));
588 if (!w) {
589 ret[k] = NULL;
590 strv_free(ret);
591 strv_free(m);
592 return NULL;
593 }
594
595 ret = w;
596 if (m) {
597 memcpy(ret + k, m, q * sizeof(char*));
598 free(m);
599 }
600
601 k += q;
602 continue;
603 }
604
605 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
606 ret[k] = replace_env(*i, env);
607 if (!ret[k]) {
608 strv_free(ret);
609 return NULL;
610 }
611 k++;
612 }
613
614 ret[k] = NULL;
615 return ret;
616 }
617
618 int getenv_bool(const char *p) {
619 const char *e;
620
621 e = getenv(p);
622 if (!e)
623 return -ENXIO;
624
625 return parse_boolean(e);
626 }