]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/env-util.c
a392af737cf67c0cf0e4ff1223e8fec199518b43
[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 "def.h"
26 #include "string-util.h"
27 #include "strv.h"
28 #include "utf8.h"
29 #include "util.h"
30 #include "env-util.h"
31
32 #define VALID_CHARS_ENV_NAME \
33 DIGITS LETTERS \
34 "_"
35
36 #ifndef ARG_MAX
37 #define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
38 #endif
39
40 static bool env_name_is_valid_n(const char *e, size_t n) {
41 const char *p;
42
43 if (!e)
44 return false;
45
46 if (n <= 0)
47 return false;
48
49 if (e[0] >= '0' && e[0] <= '9')
50 return false;
51
52 /* POSIX says the overall size of the environment block cannot
53 * be > ARG_MAX, an individual assignment hence cannot be
54 * either. Discounting the equal sign and trailing NUL this
55 * hence leaves ARG_MAX-2 as longest possible variable
56 * name. */
57 if (n > ARG_MAX - 2)
58 return false;
59
60 for (p = e; p < e + n; p++)
61 if (!strchr(VALID_CHARS_ENV_NAME, *p))
62 return false;
63
64 return true;
65 }
66
67 bool env_name_is_valid(const char *e) {
68 if (!e)
69 return false;
70
71 return env_name_is_valid_n(e, strlen(e));
72 }
73
74 bool env_value_is_valid(const char *e) {
75 if (!e)
76 return false;
77
78 if (!utf8_is_valid(e))
79 return false;
80
81 /* bash allows tabs in environment variables, and so should
82 * we */
83 if (string_has_cc(e, "\t"))
84 return false;
85
86 /* POSIX says the overall size of the environment block cannot
87 * be > ARG_MAX, an individual assignment hence cannot be
88 * either. Discounting the shortest possible variable name of
89 * length 1, the equal sign and trailing NUL this hence leaves
90 * ARG_MAX-3 as longest possible variable value. */
91 if (strlen(e) > ARG_MAX - 3)
92 return false;
93
94 return true;
95 }
96
97 bool env_assignment_is_valid(const char *e) {
98 const char *eq;
99
100 eq = strchr(e, '=');
101 if (!eq)
102 return false;
103
104 if (!env_name_is_valid_n(e, eq - e))
105 return false;
106
107 if (!env_value_is_valid(eq + 1))
108 return false;
109
110 /* POSIX says the overall size of the environment block cannot
111 * be > ARG_MAX, hence the individual variable assignments
112 * cannot be either, but let's leave room for one trailing NUL
113 * byte. */
114 if (strlen(e) > ARG_MAX - 1)
115 return false;
116
117 return true;
118 }
119
120 bool strv_env_is_valid(char **e) {
121 char **p, **q;
122
123 STRV_FOREACH(p, e) {
124 size_t k;
125
126 if (!env_assignment_is_valid(*p))
127 return false;
128
129 /* Check if there are duplicate assginments */
130 k = strcspn(*p, "=");
131 STRV_FOREACH(q, p + 1)
132 if (strneq(*p, *q, k) && (*q)[k] == '=')
133 return false;
134 }
135
136 return true;
137 }
138
139 bool strv_env_name_or_assignment_is_valid(char **l) {
140 char **p, **q;
141
142 STRV_FOREACH(p, l) {
143 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
144 return false;
145
146 STRV_FOREACH(q, p + 1)
147 if (streq(*p, *q))
148 return false;
149 }
150
151 return true;
152 }
153
154 static int env_append(char **r, char ***k, char **a) {
155 assert(r);
156 assert(k);
157
158 if (!a)
159 return 0;
160
161 /* Add the entries of a to *k unless they already exist in *r
162 * in which case they are overridden instead. This assumes
163 * there is enough space in the r array. */
164
165 for (; *a; a++) {
166 char **j;
167 size_t n;
168
169 n = strcspn(*a, "=");
170
171 if ((*a)[n] == '=')
172 n++;
173
174 for (j = r; j < *k; j++)
175 if (strneq(*j, *a, n))
176 break;
177
178 if (j >= *k)
179 (*k)++;
180 else
181 free(*j);
182
183 *j = strdup(*a);
184 if (!*j)
185 return -ENOMEM;
186 }
187
188 return 0;
189 }
190
191 char **strv_env_merge(unsigned n_lists, ...) {
192 size_t n = 0;
193 char **l, **k, **r;
194 va_list ap;
195 unsigned i;
196
197 /* Merges an arbitrary number of environment sets */
198
199 va_start(ap, n_lists);
200 for (i = 0; i < n_lists; i++) {
201 l = va_arg(ap, char**);
202 n += strv_length(l);
203 }
204 va_end(ap);
205
206 r = new(char*, n+1);
207 if (!r)
208 return NULL;
209
210 k = r;
211
212 va_start(ap, n_lists);
213 for (i = 0; i < n_lists; i++) {
214 l = va_arg(ap, char**);
215 if (env_append(r, &k, l) < 0)
216 goto fail;
217 }
218 va_end(ap);
219
220 *k = NULL;
221
222 return r;
223
224 fail:
225 va_end(ap);
226 strv_free(r);
227
228 return NULL;
229 }
230
231 _pure_ static bool env_match(const char *t, const char *pattern) {
232 assert(t);
233 assert(pattern);
234
235 /* pattern a matches string a
236 * a matches a=
237 * a matches a=b
238 * a= matches a=
239 * a=b matches a=b
240 * a= does not match a
241 * a=b does not match a=
242 * a=b does not match a
243 * a=b does not match a=c */
244
245 if (streq(t, pattern))
246 return true;
247
248 if (!strchr(pattern, '=')) {
249 size_t l = strlen(pattern);
250
251 return strneq(t, pattern, l) && t[l] == '=';
252 }
253
254 return false;
255 }
256
257 char **strv_env_delete(char **x, unsigned n_lists, ...) {
258 size_t n, i = 0;
259 char **k, **r;
260 va_list ap;
261
262 /* Deletes every entry from x that is mentioned in the other
263 * string lists */
264
265 n = strv_length(x);
266
267 r = new(char*, n+1);
268 if (!r)
269 return NULL;
270
271 STRV_FOREACH(k, x) {
272 unsigned v;
273
274 va_start(ap, n_lists);
275 for (v = 0; v < n_lists; v++) {
276 char **l, **j;
277
278 l = va_arg(ap, char**);
279 STRV_FOREACH(j, l)
280 if (env_match(*k, *j))
281 goto skip;
282 }
283 va_end(ap);
284
285 r[i] = strdup(*k);
286 if (!r[i]) {
287 strv_free(r);
288 return NULL;
289 }
290
291 i++;
292 continue;
293
294 skip:
295 va_end(ap);
296 }
297
298 r[i] = NULL;
299
300 assert(i <= n);
301
302 return r;
303 }
304
305 char **strv_env_unset(char **l, const char *p) {
306
307 char **f, **t;
308
309 if (!l)
310 return NULL;
311
312 assert(p);
313
314 /* Drops every occurrence of the env var setting p in the
315 * string list. Edits in-place. */
316
317 for (f = t = l; *f; f++) {
318
319 if (env_match(*f, p)) {
320 free(*f);
321 continue;
322 }
323
324 *(t++) = *f;
325 }
326
327 *t = NULL;
328 return l;
329 }
330
331 char **strv_env_unset_many(char **l, ...) {
332
333 char **f, **t;
334
335 if (!l)
336 return NULL;
337
338 /* Like strv_env_unset() but applies many at once. Edits in-place. */
339
340 for (f = t = l; *f; f++) {
341 bool found = false;
342 const char *p;
343 va_list ap;
344
345 va_start(ap, l);
346
347 while ((p = va_arg(ap, const char*))) {
348 if (env_match(*f, p)) {
349 found = true;
350 break;
351 }
352 }
353
354 va_end(ap);
355
356 if (found) {
357 free(*f);
358 continue;
359 }
360
361 *(t++) = *f;
362 }
363
364 *t = NULL;
365 return l;
366 }
367
368 char **strv_env_set(char **x, const char *p) {
369
370 char **k, **r;
371 char* m[2] = { (char*) p, NULL };
372
373 /* Overrides the env var setting of p, returns a new copy */
374
375 r = new(char*, strv_length(x)+2);
376 if (!r)
377 return NULL;
378
379 k = r;
380 if (env_append(r, &k, x) < 0)
381 goto fail;
382
383 if (env_append(r, &k, m) < 0)
384 goto fail;
385
386 *k = NULL;
387
388 return r;
389
390 fail:
391 strv_free(r);
392 return NULL;
393 }
394
395 char *strv_env_get_n(char **l, const char *name, size_t k) {
396 char **i;
397
398 assert(name);
399
400 if (k <= 0)
401 return NULL;
402
403 STRV_FOREACH(i, l)
404 if (strneq(*i, name, k) &&
405 (*i)[k] == '=')
406 return *i + k + 1;
407
408 return NULL;
409 }
410
411 char *strv_env_get(char **l, const char *name) {
412 assert(name);
413
414 return strv_env_get_n(l, name, strlen(name));
415 }
416
417 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
418 char **p, **q;
419 int k = 0;
420
421 STRV_FOREACH(p, e) {
422 size_t n;
423 bool duplicate = false;
424
425 if (!env_assignment_is_valid(*p)) {
426 if (invalid_callback)
427 invalid_callback(*p, userdata);
428 free(*p);
429 continue;
430 }
431
432 n = strcspn(*p, "=");
433 STRV_FOREACH(q, p + 1)
434 if (strneq(*p, *q, n) && (*q)[n] == '=') {
435 duplicate = true;
436 break;
437 }
438
439 if (duplicate) {
440 free(*p);
441 continue;
442 }
443
444 e[k++] = *p;
445 }
446
447 if (e)
448 e[k] = NULL;
449
450 return e;
451 }
452
453 char *replace_env(const char *format, char **env) {
454 enum {
455 WORD,
456 CURLY,
457 VARIABLE
458 } state = WORD;
459
460 const char *e, *word = format;
461 char *r = NULL, *k;
462
463 assert(format);
464
465 for (e = format; *e; e ++) {
466
467 switch (state) {
468
469 case WORD:
470 if (*e == '$')
471 state = CURLY;
472 break;
473
474 case CURLY:
475 if (*e == '{') {
476 k = strnappend(r, word, e-word-1);
477 if (!k)
478 goto fail;
479
480 free(r);
481 r = k;
482
483 word = e-1;
484 state = VARIABLE;
485
486 } else if (*e == '$') {
487 k = strnappend(r, word, e-word);
488 if (!k)
489 goto fail;
490
491 free(r);
492 r = k;
493
494 word = e+1;
495 state = WORD;
496 } else
497 state = WORD;
498 break;
499
500 case VARIABLE:
501 if (*e == '}') {
502 const char *t;
503
504 t = strempty(strv_env_get_n(env, word+2, e-word-2));
505
506 k = strappend(r, t);
507 if (!k)
508 goto fail;
509
510 free(r);
511 r = k;
512
513 word = e+1;
514 state = WORD;
515 }
516 break;
517 }
518 }
519
520 k = strnappend(r, word, e-word);
521 if (!k)
522 goto fail;
523
524 free(r);
525 return k;
526
527 fail:
528 free(r);
529 return NULL;
530 }
531
532 char **replace_env_argv(char **argv, char **env) {
533 char **ret, **i;
534 unsigned k = 0, l = 0;
535
536 l = strv_length(argv);
537
538 ret = new(char*, l+1);
539 if (!ret)
540 return NULL;
541
542 STRV_FOREACH(i, argv) {
543
544 /* If $FOO appears as single word, replace it by the split up variable */
545 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
546 char *e;
547 char **w, **m = NULL;
548 unsigned q;
549
550 e = strv_env_get(env, *i+1);
551 if (e) {
552 int r;
553
554 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
555 if (r < 0) {
556 ret[k] = NULL;
557 strv_free(ret);
558 return NULL;
559 }
560 } else
561 m = NULL;
562
563 q = strv_length(m);
564 l = l + q - 1;
565
566 w = realloc(ret, sizeof(char*) * (l+1));
567 if (!w) {
568 ret[k] = NULL;
569 strv_free(ret);
570 strv_free(m);
571 return NULL;
572 }
573
574 ret = w;
575 if (m) {
576 memcpy(ret + k, m, q * sizeof(char*));
577 free(m);
578 }
579
580 k += q;
581 continue;
582 }
583
584 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
585 ret[k] = replace_env(*i, env);
586 if (!ret[k]) {
587 strv_free(ret);
588 return NULL;
589 }
590 k++;
591 }
592
593 ret[k] = NULL;
594 return ret;
595 }