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