]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
Merge pull request #1983 from dmedri/master
[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 <limits.h>
23 #include <unistd.h>
24
25 #include "alloc-util.h"
26 #include "def.h"
27 #include "env-util.h"
28 #include "parse-util.h"
29 #include "string-util.h"
30 #include "strv.h"
31 #include "utf8.h"
32 #include "util.h"
33
34 #define VALID_CHARS_ENV_NAME \
35 DIGITS LETTERS \
36 "_"
37
38 #ifndef ARG_MAX
39 #define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
40 #endif
41
42 static bool env_name_is_valid_n(const char *e, size_t n) {
43 const char *p;
44
45 if (!e)
46 return false;
47
48 if (n <= 0)
49 return false;
50
51 if (e[0] >= '0' && e[0] <= '9')
52 return false;
53
54 /* POSIX says the overall size of the environment block cannot
55 * be > ARG_MAX, an individual assignment hence cannot be
56 * either. Discounting the equal sign and trailing NUL this
57 * hence leaves ARG_MAX-2 as longest possible variable
58 * name. */
59 if (n > ARG_MAX - 2)
60 return false;
61
62 for (p = e; p < e + n; p++)
63 if (!strchr(VALID_CHARS_ENV_NAME, *p))
64 return false;
65
66 return true;
67 }
68
69 bool env_name_is_valid(const char *e) {
70 if (!e)
71 return false;
72
73 return env_name_is_valid_n(e, strlen(e));
74 }
75
76 bool env_value_is_valid(const char *e) {
77 if (!e)
78 return false;
79
80 if (!utf8_is_valid(e))
81 return false;
82
83 /* bash allows tabs in environment variables, and so should
84 * we */
85 if (string_has_cc(e, "\t"))
86 return false;
87
88 /* POSIX says the overall size of the environment block cannot
89 * be > ARG_MAX, an individual assignment hence cannot be
90 * either. Discounting the shortest possible variable name of
91 * length 1, the equal sign and trailing NUL this hence leaves
92 * ARG_MAX-3 as longest possible variable value. */
93 if (strlen(e) > ARG_MAX - 3)
94 return false;
95
96 return true;
97 }
98
99 bool env_assignment_is_valid(const char *e) {
100 const char *eq;
101
102 eq = strchr(e, '=');
103 if (!eq)
104 return false;
105
106 if (!env_name_is_valid_n(e, eq - e))
107 return false;
108
109 if (!env_value_is_valid(eq + 1))
110 return false;
111
112 /* POSIX says the overall size of the environment block cannot
113 * be > ARG_MAX, hence the individual variable assignments
114 * cannot be either, but let's leave room for one trailing NUL
115 * byte. */
116 if (strlen(e) > ARG_MAX - 1)
117 return false;
118
119 return true;
120 }
121
122 bool strv_env_is_valid(char **e) {
123 char **p, **q;
124
125 STRV_FOREACH(p, e) {
126 size_t k;
127
128 if (!env_assignment_is_valid(*p))
129 return false;
130
131 /* Check if there are duplicate assginments */
132 k = strcspn(*p, "=");
133 STRV_FOREACH(q, p + 1)
134 if (strneq(*p, *q, k) && (*q)[k] == '=')
135 return false;
136 }
137
138 return true;
139 }
140
141 bool strv_env_name_is_valid(char **l) {
142 char **p, **q;
143
144 STRV_FOREACH(p, l) {
145 if (!env_name_is_valid(*p))
146 return false;
147
148 STRV_FOREACH(q, p + 1)
149 if (streq(*p, *q))
150 return false;
151 }
152
153 return true;
154 }
155
156 bool strv_env_name_or_assignment_is_valid(char **l) {
157 char **p, **q;
158
159 STRV_FOREACH(p, l) {
160 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
161 return false;
162
163 STRV_FOREACH(q, p + 1)
164 if (streq(*p, *q))
165 return false;
166 }
167
168 return true;
169 }
170
171 static int env_append(char **r, char ***k, char **a) {
172 assert(r);
173 assert(k);
174
175 if (!a)
176 return 0;
177
178 /* Add the entries of a to *k unless they already exist in *r
179 * in which case they are overridden instead. This assumes
180 * there is enough space in the r array. */
181
182 for (; *a; a++) {
183 char **j;
184 size_t n;
185
186 n = strcspn(*a, "=");
187
188 if ((*a)[n] == '=')
189 n++;
190
191 for (j = r; j < *k; j++)
192 if (strneq(*j, *a, n))
193 break;
194
195 if (j >= *k)
196 (*k)++;
197 else
198 free(*j);
199
200 *j = strdup(*a);
201 if (!*j)
202 return -ENOMEM;
203 }
204
205 return 0;
206 }
207
208 char **strv_env_merge(unsigned n_lists, ...) {
209 size_t n = 0;
210 char **l, **k, **r;
211 va_list ap;
212 unsigned i;
213
214 /* Merges an arbitrary number of environment sets */
215
216 va_start(ap, n_lists);
217 for (i = 0; i < n_lists; i++) {
218 l = va_arg(ap, char**);
219 n += strv_length(l);
220 }
221 va_end(ap);
222
223 r = new(char*, n+1);
224 if (!r)
225 return NULL;
226
227 k = r;
228
229 va_start(ap, n_lists);
230 for (i = 0; i < n_lists; i++) {
231 l = va_arg(ap, char**);
232 if (env_append(r, &k, l) < 0)
233 goto fail;
234 }
235 va_end(ap);
236
237 *k = NULL;
238
239 return r;
240
241 fail:
242 va_end(ap);
243 strv_free(r);
244
245 return NULL;
246 }
247
248 _pure_ static bool env_match(const char *t, const char *pattern) {
249 assert(t);
250 assert(pattern);
251
252 /* pattern a matches string a
253 * a matches a=
254 * a matches a=b
255 * a= matches a=
256 * a=b matches a=b
257 * a= does not match a
258 * a=b does not match a=
259 * a=b does not match a
260 * a=b does not match a=c */
261
262 if (streq(t, pattern))
263 return true;
264
265 if (!strchr(pattern, '=')) {
266 size_t l = strlen(pattern);
267
268 return strneq(t, pattern, l) && t[l] == '=';
269 }
270
271 return false;
272 }
273
274 char **strv_env_delete(char **x, unsigned 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 unsigned 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 char **strv_env_set(char **x, const char *p) {
386
387 char **k, **r;
388 char* m[2] = { (char*) p, NULL };
389
390 /* Overrides the env var setting of p, returns a new copy */
391
392 r = new(char*, strv_length(x)+2);
393 if (!r)
394 return NULL;
395
396 k = r;
397 if (env_append(r, &k, x) < 0)
398 goto fail;
399
400 if (env_append(r, &k, m) < 0)
401 goto fail;
402
403 *k = NULL;
404
405 return r;
406
407 fail:
408 strv_free(r);
409 return NULL;
410 }
411
412 char *strv_env_get_n(char **l, const char *name, size_t k) {
413 char **i;
414
415 assert(name);
416
417 if (k <= 0)
418 return NULL;
419
420 STRV_FOREACH(i, l)
421 if (strneq(*i, name, k) &&
422 (*i)[k] == '=')
423 return *i + k + 1;
424
425 return NULL;
426 }
427
428 char *strv_env_get(char **l, const char *name) {
429 assert(name);
430
431 return strv_env_get_n(l, name, strlen(name));
432 }
433
434 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
435 char **p, **q;
436 int k = 0;
437
438 STRV_FOREACH(p, e) {
439 size_t n;
440 bool duplicate = false;
441
442 if (!env_assignment_is_valid(*p)) {
443 if (invalid_callback)
444 invalid_callback(*p, userdata);
445 free(*p);
446 continue;
447 }
448
449 n = strcspn(*p, "=");
450 STRV_FOREACH(q, p + 1)
451 if (strneq(*p, *q, n) && (*q)[n] == '=') {
452 duplicate = true;
453 break;
454 }
455
456 if (duplicate) {
457 free(*p);
458 continue;
459 }
460
461 e[k++] = *p;
462 }
463
464 if (e)
465 e[k] = NULL;
466
467 return e;
468 }
469
470 char *replace_env(const char *format, char **env) {
471 enum {
472 WORD,
473 CURLY,
474 VARIABLE
475 } state = WORD;
476
477 const char *e, *word = format;
478 char *r = NULL, *k;
479
480 assert(format);
481
482 for (e = format; *e; e ++) {
483
484 switch (state) {
485
486 case WORD:
487 if (*e == '$')
488 state = CURLY;
489 break;
490
491 case CURLY:
492 if (*e == '{') {
493 k = strnappend(r, word, e-word-1);
494 if (!k)
495 goto fail;
496
497 free(r);
498 r = k;
499
500 word = e-1;
501 state = VARIABLE;
502
503 } else if (*e == '$') {
504 k = strnappend(r, word, e-word);
505 if (!k)
506 goto fail;
507
508 free(r);
509 r = k;
510
511 word = e+1;
512 state = WORD;
513 } else
514 state = WORD;
515 break;
516
517 case VARIABLE:
518 if (*e == '}') {
519 const char *t;
520
521 t = strempty(strv_env_get_n(env, word+2, e-word-2));
522
523 k = strappend(r, t);
524 if (!k)
525 goto fail;
526
527 free(r);
528 r = k;
529
530 word = e+1;
531 state = WORD;
532 }
533 break;
534 }
535 }
536
537 k = strnappend(r, word, e-word);
538 if (!k)
539 goto fail;
540
541 free(r);
542 return k;
543
544 fail:
545 free(r);
546 return NULL;
547 }
548
549 char **replace_env_argv(char **argv, char **env) {
550 char **ret, **i;
551 unsigned k = 0, l = 0;
552
553 l = strv_length(argv);
554
555 ret = new(char*, l+1);
556 if (!ret)
557 return NULL;
558
559 STRV_FOREACH(i, argv) {
560
561 /* If $FOO appears as single word, replace it by the split up variable */
562 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
563 char *e;
564 char **w, **m = NULL;
565 unsigned q;
566
567 e = strv_env_get(env, *i+1);
568 if (e) {
569 int r;
570
571 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
572 if (r < 0) {
573 ret[k] = NULL;
574 strv_free(ret);
575 return NULL;
576 }
577 } else
578 m = NULL;
579
580 q = strv_length(m);
581 l = l + q - 1;
582
583 w = realloc(ret, sizeof(char*) * (l+1));
584 if (!w) {
585 ret[k] = NULL;
586 strv_free(ret);
587 strv_free(m);
588 return NULL;
589 }
590
591 ret = w;
592 if (m) {
593 memcpy(ret + k, m, q * sizeof(char*));
594 free(m);
595 }
596
597 k += q;
598 continue;
599 }
600
601 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
602 ret[k] = replace_env(*i, env);
603 if (!ret[k]) {
604 strv_free(ret);
605 return NULL;
606 }
607 k++;
608 }
609
610 ret[k] = NULL;
611 return ret;
612 }
613
614 int getenv_bool(const char *p) {
615 const char *e;
616
617 e = getenv(p);
618 if (!e)
619 return -ENXIO;
620
621 return parse_boolean(e);
622 }