]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/env-util.c
Merge pull request #1843 from teg/ndisc
[thirdparty/systemd.git] / src / basic / env-util.c
CommitLineData
4d1a6904
LP
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>
4d1a6904
LP
23#include <unistd.h>
24
b5efdb8a 25#include "alloc-util.h"
07630cea 26#include "def.h"
b5efdb8a 27#include "env-util.h"
b41b9d2a 28#include "parse-util.h"
07630cea 29#include "string-util.h"
4d1a6904
LP
30#include "strv.h"
31#include "utf8.h"
32#include "util.h"
4d1a6904
LP
33
34#define VALID_CHARS_ENV_NAME \
4b549144 35 DIGITS LETTERS \
4d1a6904
LP
36 "_"
37
38#ifndef ARG_MAX
39#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
40#endif
41
42static 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
69bool 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
76bool env_value_is_valid(const char *e) {
77 if (!e)
78 return false;
79
80 if (!utf8_is_valid(e))
81 return false;
82
6294aa76
LP
83 /* bash allows tabs in environment variables, and so should
84 * we */
85 if (string_has_cc(e, "\t"))
4d1a6904
LP
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
99bool 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
5f9cfd4c 114 * cannot be either, but let's leave room for one trailing NUL
4d1a6904
LP
115 * byte. */
116 if (strlen(e) > ARG_MAX - 1)
117 return false;
118
119 return true;
120}
121
122bool 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)
641906e9 134 if (strneq(*p, *q, k) && (*q)[k] == '=')
4d1a6904
LP
135 return false;
136 }
137
138 return true;
139}
140
123b964a
LP
141bool 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
4d1a6904
LP
156static 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++)
641906e9 177 if (strneq(*j, *a, n))
4d1a6904
LP
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
193char **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
226fail:
227 va_end(ap);
228 strv_free(r);
229
230 return NULL;
231}
232
44a6b1b6 233_pure_ static bool env_match(const char *t, const char *pattern) {
4d1a6904
LP
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
641906e9 253 return strneq(t, pattern, l) && t[l] == '=';
4d1a6904
LP
254 }
255
256 return false;
257}
258
259char **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
307char **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
43d03a83 317 * string list. Edits in-place. */
4d1a6904
LP
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 }
43d03a83
LP
328
329 *t = NULL;
330 return l;
331}
332
333char **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 }
4d1a6904
LP
365
366 *t = NULL;
367 return l;
368}
369
370char **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
392fail:
393 strv_free(r);
394 return NULL;
395}
396
397char *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)
641906e9 406 if (strneq(*i, name, k) &&
4d1a6904
LP
407 (*i)[k] == '=')
408 return *i + k + 1;
409
410 return NULL;
411}
412
413char *strv_env_get(char **l, const char *name) {
414 assert(name);
415
416 return strv_env_get_n(l, name, strlen(name));
417}
418
039f0e70 419char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
4d1a6904
LP
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)) {
039f0e70
LP
428 if (invalid_callback)
429 invalid_callback(*p, userdata);
4d1a6904
LP
430 free(*p);
431 continue;
432 }
433
434 n = strcspn(*p, "=");
435 STRV_FOREACH(q, p + 1)
641906e9 436 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
437 duplicate = true;
438 break;
439 }
440
441 if (duplicate) {
442 free(*p);
443 continue;
444 }
445
446 e[k++] = *p;
447 }
448
5b4fb02d
LP
449 if (e)
450 e[k] = NULL;
451
4d1a6904
LP
452 return e;
453}
3c800095
RC
454
455char *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
529fail:
530 free(r);
531 return NULL;
532}
533
534char **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 */
df553b58 547 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
3c800095
RC
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
8adaf7bd 556 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
3c800095
RC
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}
b41b9d2a
LP
598
599int 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}