]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <stdlib.h> | |
4 | #include <unistd.h> | |
5 | ||
6 | #include "alloc-util.h" | |
7 | #include "env-util.h" | |
8 | #include "errno-util.h" | |
9 | #include "extract-word.h" | |
10 | #include "format-util.h" | |
11 | #include "log.h" | |
12 | #include "parse-util.h" | |
13 | #include "path-util.h" | |
14 | #include "process-util.h" | |
15 | #include "string-util.h" | |
16 | #include "strv.h" | |
17 | #include "syslog-util.h" | |
18 | #include "utf8.h" | |
19 | ||
20 | /* We follow bash for the character set. Different shells have different rules. */ | |
21 | #define VALID_BASH_ENV_NAME_CHARS \ | |
22 | DIGITS LETTERS \ | |
23 | "_" | |
24 | ||
25 | size_t sc_arg_max(void) { | |
26 | long l = sysconf(_SC_ARG_MAX); | |
27 | assert(l > 0); | |
28 | return (size_t) l; | |
29 | } | |
30 | ||
31 | static bool env_name_is_valid_n(const char *e, size_t n) { | |
32 | ||
33 | if (n == SIZE_MAX) | |
34 | n = strlen_ptr(e); | |
35 | ||
36 | if (n == 0) | |
37 | return false; | |
38 | ||
39 | assert(e); | |
40 | ||
41 | if (ascii_isdigit(e[0])) | |
42 | return false; | |
43 | ||
44 | /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment | |
45 | * hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as | |
46 | * longest possible variable name. */ | |
47 | if (_unlikely_(n > sc_arg_max() - 2)) | |
48 | return false; | |
49 | ||
50 | for (const char *p = e; p < e + n; p++) | |
51 | if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p)) | |
52 | return false; | |
53 | ||
54 | return true; | |
55 | } | |
56 | ||
57 | bool env_name_is_valid(const char *e) { | |
58 | return env_name_is_valid_n(e, SIZE_MAX); | |
59 | } | |
60 | ||
61 | bool env_value_is_valid(const char *e) { | |
62 | if (!e) | |
63 | return false; | |
64 | ||
65 | if (!utf8_is_valid(e)) | |
66 | return false; | |
67 | ||
68 | /* Note that variable *values* may contain control characters, in particular NL, TAB, BS, DEL, ESC… | |
69 | * When printing those variables with show-environment, we'll escape them. Make sure to print | |
70 | * environment variables carefully! */ | |
71 | ||
72 | /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment | |
73 | * hence cannot be either. Discounting the shortest possible variable name of length 1, the equal | |
74 | * sign and trailing NUL this hence leaves ARG_MAX-3 as longest possible variable value. */ | |
75 | if (_unlikely_(strlen(e) > sc_arg_max() - 3)) | |
76 | return false; | |
77 | ||
78 | return true; | |
79 | } | |
80 | ||
81 | bool env_assignment_is_valid(const char *e) { | |
82 | const char *eq; | |
83 | ||
84 | assert(e); | |
85 | ||
86 | eq = strchr(e, '='); | |
87 | if (!eq) | |
88 | return false; | |
89 | ||
90 | if (!env_name_is_valid_n(e, eq - e)) | |
91 | return false; | |
92 | ||
93 | if (!env_value_is_valid(eq + 1)) | |
94 | return false; | |
95 | ||
96 | /* POSIX says the overall size of the environment block cannot be > ARG_MAX, hence the individual | |
97 | * variable assignments cannot be either, but let's leave room for one trailing NUL byte. */ | |
98 | if (_unlikely_(strlen(e) > sc_arg_max() - 1)) | |
99 | return false; | |
100 | ||
101 | return true; | |
102 | } | |
103 | ||
104 | bool strv_env_is_valid(char * const *e) { | |
105 | STRV_FOREACH(p, e) { | |
106 | size_t k; | |
107 | ||
108 | if (!env_assignment_is_valid(*p)) | |
109 | return false; | |
110 | ||
111 | /* Check if there are duplicate assignments */ | |
112 | k = strcspn(*p, "="); | |
113 | STRV_FOREACH(q, p + 1) | |
114 | if (strneq(*p, *q, k) && (*q)[k] == '=') | |
115 | return false; | |
116 | } | |
117 | ||
118 | return true; | |
119 | } | |
120 | ||
121 | bool strv_env_name_is_valid(char * const *l) { | |
122 | STRV_FOREACH(p, l) { | |
123 | if (!env_name_is_valid(*p)) | |
124 | return false; | |
125 | ||
126 | if (strv_contains(p + 1, *p)) | |
127 | return false; | |
128 | } | |
129 | ||
130 | return true; | |
131 | } | |
132 | ||
133 | bool strv_env_name_or_assignment_is_valid(char * const *l) { | |
134 | STRV_FOREACH(p, l) { | |
135 | if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) | |
136 | return false; | |
137 | ||
138 | if (strv_contains(p + 1, *p)) | |
139 | return false; | |
140 | } | |
141 | ||
142 | return true; | |
143 | } | |
144 | ||
145 | static int env_append(char **e, char ***k, char **a) { | |
146 | assert(e); | |
147 | assert(k); | |
148 | assert(*k >= e); | |
149 | ||
150 | if (!a) | |
151 | return 0; | |
152 | ||
153 | /* Expects the following arguments: 'e' shall point to the beginning of an strv we are going to append to, 'k' | |
154 | * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv. | |
155 | * | |
156 | * This call adds every entry of 'a' to 'e', either overriding an existing matching entry, or appending to it. | |
157 | * | |
158 | * This call assumes 'e' has enough pre-allocated space to grow by all of 'a''s items. */ | |
159 | ||
160 | for (; *a; a++) { | |
161 | char **j, *c; | |
162 | size_t n; | |
163 | ||
164 | n = strcspn(*a, "="); | |
165 | if ((*a)[n] == '=') | |
166 | n++; | |
167 | ||
168 | for (j = e; j < *k; j++) | |
169 | if (strneq(*j, *a, n)) | |
170 | break; | |
171 | ||
172 | c = strdup(*a); | |
173 | if (!c) | |
174 | return -ENOMEM; | |
175 | ||
176 | if (j >= *k) { /* Append to the end? */ | |
177 | (*k)[0] = c; | |
178 | (*k)[1] = NULL; | |
179 | (*k)++; | |
180 | } else | |
181 | free_and_replace(*j, c); /* Override existing item */ | |
182 | } | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | char** _strv_env_merge(char **first, ...) { | |
188 | _cleanup_strv_free_ char **merged = NULL; | |
189 | char **k; | |
190 | va_list ap; | |
191 | ||
192 | /* Merges an arbitrary number of environment sets */ | |
193 | ||
194 | size_t n = strv_length(first); | |
195 | ||
196 | va_start(ap, first); | |
197 | for (;;) { | |
198 | char **l; | |
199 | ||
200 | l = va_arg(ap, char**); | |
201 | if (l == POINTER_MAX) | |
202 | break; | |
203 | ||
204 | n += strv_length(l); | |
205 | } | |
206 | va_end(ap); | |
207 | ||
208 | k = merged = new(char*, n + 1); | |
209 | if (!merged) | |
210 | return NULL; | |
211 | merged[0] = NULL; | |
212 | ||
213 | if (env_append(merged, &k, first) < 0) | |
214 | return NULL; | |
215 | ||
216 | va_start(ap, first); | |
217 | for (;;) { | |
218 | char **l; | |
219 | ||
220 | l = va_arg(ap, char**); | |
221 | if (l == POINTER_MAX) | |
222 | break; | |
223 | ||
224 | if (env_append(merged, &k, l) < 0) { | |
225 | va_end(ap); | |
226 | return NULL; | |
227 | } | |
228 | } | |
229 | va_end(ap); | |
230 | ||
231 | return TAKE_PTR(merged); | |
232 | } | |
233 | ||
234 | static bool env_match(const char *t, const char *pattern) { | |
235 | assert(t); | |
236 | assert(pattern); | |
237 | ||
238 | /* pattern a matches string a | |
239 | * a matches a= | |
240 | * a matches a=b | |
241 | * a= matches a= | |
242 | * a=b matches a=b | |
243 | * a= does not match a | |
244 | * a=b does not match a= | |
245 | * a=b does not match a | |
246 | * a=b does not match a=c */ | |
247 | ||
248 | if (streq(t, pattern)) | |
249 | return true; | |
250 | ||
251 | if (!strchr(pattern, '=')) { | |
252 | t = startswith(t, pattern); | |
253 | ||
254 | return t && *t == '='; | |
255 | } | |
256 | ||
257 | return false; | |
258 | } | |
259 | ||
260 | static bool env_entry_has_name(const char *entry, const char *name) { | |
261 | const char *t; | |
262 | ||
263 | assert(entry); | |
264 | assert(name); | |
265 | ||
266 | t = startswith(entry, name); | |
267 | if (!t) | |
268 | return false; | |
269 | ||
270 | return *t == '='; | |
271 | } | |
272 | ||
273 | char** strv_env_delete(char **x, size_t n_lists, ...) { | |
274 | size_t n, i = 0; | |
275 | _cleanup_strv_free_ char **t = NULL; | |
276 | va_list ap; | |
277 | ||
278 | /* Deletes every entry from x that is mentioned in the other | |
279 | * string lists */ | |
280 | ||
281 | n = strv_length(x); | |
282 | ||
283 | t = new(char*, n+1); | |
284 | if (!t) | |
285 | return NULL; | |
286 | ||
287 | STRV_FOREACH(k, x) { | |
288 | va_start(ap, n_lists); | |
289 | for (size_t v = 0; v < n_lists; v++) { | |
290 | char **l; | |
291 | ||
292 | l = va_arg(ap, char**); | |
293 | STRV_FOREACH(j, l) | |
294 | if (env_match(*k, *j)) | |
295 | goto skip; | |
296 | } | |
297 | va_end(ap); | |
298 | ||
299 | t[i] = strdup(*k); | |
300 | if (!t[i]) | |
301 | return NULL; | |
302 | ||
303 | i++; | |
304 | continue; | |
305 | ||
306 | skip: | |
307 | va_end(ap); | |
308 | } | |
309 | ||
310 | t[i] = NULL; | |
311 | ||
312 | assert(i <= n); | |
313 | ||
314 | return TAKE_PTR(t); | |
315 | } | |
316 | ||
317 | char** strv_env_unset(char **l, const char *p) { | |
318 | assert(p); | |
319 | ||
320 | if (!l) | |
321 | return NULL; | |
322 | ||
323 | /* Drops every occurrence of the env var setting p in the | |
324 | * string list. Edits in-place. */ | |
325 | ||
326 | char **f, **t; | |
327 | for (f = t = l; *f; f++) { | |
328 | if (env_match(*f, p)) { | |
329 | free(*f); | |
330 | continue; | |
331 | } | |
332 | ||
333 | *(t++) = *f; | |
334 | } | |
335 | ||
336 | *t = NULL; | |
337 | return l; | |
338 | } | |
339 | ||
340 | char** strv_env_unset_many_internal(char **l, ...) { | |
341 | if (!l) | |
342 | return NULL; | |
343 | ||
344 | /* Like strv_env_unset() but applies many at once. Edits in-place. */ | |
345 | ||
346 | char **f, **t; | |
347 | for (f = t = l; *f; f++) { | |
348 | bool found = false; | |
349 | const char *p; | |
350 | va_list ap; | |
351 | ||
352 | va_start(ap, l); | |
353 | ||
354 | while ((p = va_arg(ap, const char*))) | |
355 | if (env_match(*f, p)) { | |
356 | found = true; | |
357 | break; | |
358 | } | |
359 | ||
360 | va_end(ap); | |
361 | ||
362 | if (found) { | |
363 | free(*f); | |
364 | continue; | |
365 | } | |
366 | ||
367 | *(t++) = *f; | |
368 | } | |
369 | ||
370 | *t = NULL; | |
371 | return l; | |
372 | } | |
373 | ||
374 | int strv_env_replace_consume(char ***l, char *p) { | |
375 | const char *t, *name; | |
376 | int r; | |
377 | ||
378 | assert(l); | |
379 | assert(p); | |
380 | ||
381 | /* Replace first occurrence of the env var or add a new one in the string list. Drop other | |
382 | * occurrences. Edits in-place. Does not copy p and CONSUMES p EVEN ON FAILURE. | |
383 | * | |
384 | * p must be a valid key=value assignment. */ | |
385 | ||
386 | t = strchr(p, '='); | |
387 | if (!t) { | |
388 | free(p); | |
389 | return -EINVAL; | |
390 | } | |
391 | ||
392 | name = strndupa_safe(p, t - p); | |
393 | ||
394 | STRV_FOREACH(f, *l) | |
395 | if (env_entry_has_name(*f, name)) { | |
396 | free_and_replace(*f, p); | |
397 | strv_env_unset(f + 1, *f); | |
398 | return 0; | |
399 | } | |
400 | ||
401 | /* We didn't find a match, we need to append p or create a new strv */ | |
402 | r = strv_consume(l, p); | |
403 | if (r < 0) | |
404 | return r; | |
405 | ||
406 | return 1; | |
407 | } | |
408 | ||
409 | int strv_env_replace_strdup(char ***l, const char *assignment) { | |
410 | /* Like strv_env_replace_consume(), but copies the argument. */ | |
411 | ||
412 | assert(l); | |
413 | assert(assignment); | |
414 | ||
415 | char *p = strdup(assignment); | |
416 | if (!p) | |
417 | return -ENOMEM; | |
418 | ||
419 | return strv_env_replace_consume(l, p); | |
420 | } | |
421 | ||
422 | int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) { | |
423 | char *p; | |
424 | ||
425 | /* Like strv_env_replace_strdup(), but pulls the variable from the environment of | |
426 | * the calling program, if a variable name without value is specified. */ | |
427 | ||
428 | assert(l); | |
429 | assert(assignment); | |
430 | ||
431 | if (strchr(assignment, '=')) { | |
432 | if (!env_assignment_is_valid(assignment)) | |
433 | return -EINVAL; | |
434 | ||
435 | p = strdup(assignment); | |
436 | } else { | |
437 | if (!env_name_is_valid(assignment)) | |
438 | return -EINVAL; | |
439 | ||
440 | /* If we can't find the variable in our environment, we will use | |
441 | * the empty string. This way "passthrough" is equivalent to passing | |
442 | * --setenv=FOO=$FOO in the shell. */ | |
443 | p = strjoin(assignment, "=", secure_getenv(assignment)); | |
444 | } | |
445 | if (!p) | |
446 | return -ENOMEM; | |
447 | ||
448 | return strv_env_replace_consume(l, p); | |
449 | } | |
450 | ||
451 | int strv_env_assign(char ***l, const char *key, const char *value) { | |
452 | assert(l); | |
453 | ||
454 | if (!env_name_is_valid(key)) | |
455 | return -EINVAL; | |
456 | ||
457 | /* NULL removes assignment, "" creates an empty assignment. */ | |
458 | ||
459 | if (!value) { | |
460 | strv_env_unset(*l, key); | |
461 | return 0; | |
462 | } | |
463 | ||
464 | char *p = strjoin(key, "=", value); | |
465 | if (!p) | |
466 | return -ENOMEM; | |
467 | ||
468 | return strv_env_replace_consume(l, p); | |
469 | } | |
470 | ||
471 | int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) { | |
472 | int r; | |
473 | ||
474 | assert(l); | |
475 | assert(key); | |
476 | ||
477 | if (!env_name_is_valid(key)) | |
478 | return -EINVAL; | |
479 | ||
480 | if (!valuef) { | |
481 | strv_env_unset(*l, key); | |
482 | return 0; | |
483 | } | |
484 | ||
485 | _cleanup_free_ char *value = NULL; | |
486 | va_list ap; | |
487 | va_start(ap, valuef); | |
488 | r = vasprintf(&value, valuef, ap); | |
489 | va_end(ap); | |
490 | if (r < 0) | |
491 | return -ENOMEM; | |
492 | ||
493 | char *p = strjoin(key, "=", value); | |
494 | if (!p) | |
495 | return -ENOMEM; | |
496 | ||
497 | return strv_env_replace_consume(l, p); | |
498 | } | |
499 | ||
500 | int _strv_env_assign_many(char ***l, ...) { | |
501 | va_list ap; | |
502 | int r; | |
503 | ||
504 | assert(l); | |
505 | ||
506 | va_start(ap, l); | |
507 | for (;;) { | |
508 | const char *key, *value; | |
509 | ||
510 | key = va_arg(ap, const char *); | |
511 | if (!key) | |
512 | break; | |
513 | ||
514 | if (!env_name_is_valid(key)) { | |
515 | va_end(ap); | |
516 | return -EINVAL; | |
517 | } | |
518 | ||
519 | value = va_arg(ap, const char *); | |
520 | if (!value) { | |
521 | strv_env_unset(*l, key); | |
522 | continue; | |
523 | } | |
524 | ||
525 | char *p = strjoin(key, "=", value); | |
526 | if (!p) { | |
527 | va_end(ap); | |
528 | return -ENOMEM; | |
529 | } | |
530 | ||
531 | r = strv_env_replace_consume(l, p); | |
532 | if (r < 0) { | |
533 | va_end(ap); | |
534 | return r; | |
535 | } | |
536 | } | |
537 | va_end(ap); | |
538 | ||
539 | return 0; | |
540 | } | |
541 | ||
542 | char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) { | |
543 | assert(name); | |
544 | ||
545 | if (k == SIZE_MAX) | |
546 | k = strlen(name); | |
547 | if (k <= 0) | |
548 | return NULL; | |
549 | ||
550 | STRV_FOREACH_BACKWARDS(i, l) | |
551 | if (strneq(*i, name, k) && (*i)[k] == '=') | |
552 | return (char*) *i + k + 1; | |
553 | ||
554 | if (flags & REPLACE_ENV_USE_ENVIRONMENT) { | |
555 | const char *t; | |
556 | ||
557 | /* Safety check that the name is not overly long, before we do a stack allocation */ | |
558 | if (k > (size_t) sysconf(_SC_ARG_MAX) - 2) | |
559 | return NULL; | |
560 | ||
561 | t = strndupa_safe(name, k); | |
562 | return secure_getenv(t); | |
563 | }; | |
564 | ||
565 | return NULL; | |
566 | } | |
567 | ||
568 | char* strv_env_pairs_get(char **l, const char *name) { | |
569 | char *result = NULL; | |
570 | ||
571 | assert(name); | |
572 | ||
573 | STRV_FOREACH_PAIR(key, value, l) | |
574 | if (streq(*key, name)) | |
575 | result = *value; | |
576 | ||
577 | return result; | |
578 | } | |
579 | ||
580 | int strv_env_get_merged(char **l, char ***ret) { | |
581 | _cleanup_strv_free_ char **v = NULL; | |
582 | size_t n = 0; | |
583 | int r; | |
584 | ||
585 | assert(ret); | |
586 | ||
587 | /* This converts a strv with pairs of environment variable name + value into a strv of name and | |
588 | * value concatenated with a "=" separator. E.g. | |
589 | * input : { "NAME", "value", "FOO", "var" } | |
590 | * output : { "NAME=value", "FOO=var" } */ | |
591 | ||
592 | STRV_FOREACH_PAIR(key, value, l) { | |
593 | char *s; | |
594 | ||
595 | s = strjoin(*key, "=", *value); | |
596 | if (!s) | |
597 | return -ENOMEM; | |
598 | ||
599 | r = strv_consume_with_size(&v, &n, s); | |
600 | if (r < 0) | |
601 | return r; | |
602 | } | |
603 | ||
604 | *ret = TAKE_PTR(v); | |
605 | return 0; | |
606 | } | |
607 | ||
608 | char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { | |
609 | int k = 0; | |
610 | ||
611 | STRV_FOREACH(p, e) { | |
612 | size_t n; | |
613 | bool duplicate = false; | |
614 | ||
615 | if (!env_assignment_is_valid(*p)) { | |
616 | if (invalid_callback) | |
617 | invalid_callback(*p, userdata); | |
618 | free(*p); | |
619 | continue; | |
620 | } | |
621 | ||
622 | n = strcspn(*p, "="); | |
623 | STRV_FOREACH(q, p + 1) | |
624 | if (strneq(*p, *q, n) && (*q)[n] == '=') { | |
625 | duplicate = true; | |
626 | break; | |
627 | } | |
628 | ||
629 | if (duplicate) { | |
630 | free(*p); | |
631 | continue; | |
632 | } | |
633 | ||
634 | e[k++] = *p; | |
635 | } | |
636 | ||
637 | if (e) | |
638 | e[k] = NULL; | |
639 | ||
640 | return e; | |
641 | } | |
642 | ||
643 | static int strv_extend_with_length(char ***l, const char *s, size_t n) { | |
644 | char *c; | |
645 | ||
646 | c = strndup(s, n); | |
647 | if (!c) | |
648 | return -ENOMEM; | |
649 | ||
650 | return strv_consume(l, c); | |
651 | } | |
652 | ||
653 | static int strv_env_get_n_validated( | |
654 | char **env, | |
655 | const char *name, | |
656 | size_t l, | |
657 | ReplaceEnvFlags flags, | |
658 | char **ret, /* points into the env block! do not free! */ | |
659 | char ***unset_variables, /* updated in place */ | |
660 | char ***bad_variables) { /* ditto */ | |
661 | ||
662 | char *e; | |
663 | int r; | |
664 | ||
665 | assert(l == 0 || name); | |
666 | assert(ret); | |
667 | ||
668 | if (env_name_is_valid_n(name, l)) { | |
669 | e = strv_env_get_n(env, name, l, flags); | |
670 | if (!e && unset_variables) { | |
671 | r = strv_extend_with_length(unset_variables, name, l); | |
672 | if (r < 0) | |
673 | return r; | |
674 | } | |
675 | } else { | |
676 | e = NULL; /* Resolve invalid variable names the same way as unset ones */ | |
677 | ||
678 | if (bad_variables) { | |
679 | r = strv_extend_with_length(bad_variables, name, l); | |
680 | if (r < 0) | |
681 | return r; | |
682 | } | |
683 | } | |
684 | ||
685 | *ret = e; | |
686 | return !!e; | |
687 | } | |
688 | ||
689 | int replace_env_full( | |
690 | const char *format, | |
691 | size_t length, | |
692 | char **env, | |
693 | ReplaceEnvFlags flags, | |
694 | char **ret, | |
695 | char ***ret_unset_variables, | |
696 | char ***ret_bad_variables) { | |
697 | ||
698 | enum { | |
699 | WORD, | |
700 | CURLY, | |
701 | VARIABLE, | |
702 | VARIABLE_RAW, | |
703 | TEST, | |
704 | DEFAULT_VALUE, | |
705 | ALTERNATE_VALUE, | |
706 | } state = WORD; | |
707 | ||
708 | _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL; | |
709 | const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */ | |
710 | _cleanup_free_ char *s = NULL; | |
711 | char ***pu, ***pb; | |
712 | size_t i, len = 0; /* len is initialized to appease gcc */ | |
713 | int nest = 0, r; | |
714 | ||
715 | assert(format); | |
716 | ||
717 | if (length == SIZE_MAX) | |
718 | length = strlen(format); | |
719 | ||
720 | pu = ret_unset_variables ? &unset_variables : NULL; | |
721 | pb = ret_bad_variables ? &bad_variables : NULL; | |
722 | ||
723 | for (e = format, i = 0; *e && i < length; e++, i++) | |
724 | switch (state) { | |
725 | ||
726 | case WORD: | |
727 | if (*e == '$') | |
728 | state = CURLY; | |
729 | break; | |
730 | ||
731 | case CURLY: | |
732 | if (*e == '{') { | |
733 | if (!strextendn(&s, word, e-word-1)) | |
734 | return -ENOMEM; | |
735 | ||
736 | word = e-1; | |
737 | state = VARIABLE; | |
738 | nest++; | |
739 | ||
740 | } else if (*e == '$') { | |
741 | if (!strextendn(&s, word, e-word)) | |
742 | return -ENOMEM; | |
743 | ||
744 | word = e+1; | |
745 | state = WORD; | |
746 | ||
747 | } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { | |
748 | if (!strextendn(&s, word, e-word-1)) | |
749 | return -ENOMEM; | |
750 | ||
751 | word = e-1; | |
752 | state = VARIABLE_RAW; | |
753 | ||
754 | } else | |
755 | state = WORD; | |
756 | break; | |
757 | ||
758 | case VARIABLE: | |
759 | if (*e == '}') { | |
760 | char *t; | |
761 | ||
762 | r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb); | |
763 | if (r < 0) | |
764 | return r; | |
765 | ||
766 | if (!strextend(&s, t)) | |
767 | return -ENOMEM; | |
768 | ||
769 | word = e+1; | |
770 | state = WORD; | |
771 | nest--; | |
772 | } else if (*e == ':') { | |
773 | if (flags & REPLACE_ENV_ALLOW_EXTENDED) { | |
774 | len = e - word - 2; | |
775 | state = TEST; | |
776 | } else | |
777 | /* Treat this as unsupported syntax, i.e. do no replacement */ | |
778 | state = WORD; | |
779 | } | |
780 | break; | |
781 | ||
782 | case TEST: | |
783 | if (*e == '-') | |
784 | state = DEFAULT_VALUE; | |
785 | else if (*e == '+') | |
786 | state = ALTERNATE_VALUE; | |
787 | else { | |
788 | state = WORD; | |
789 | break; | |
790 | } | |
791 | ||
792 | test_value = e+1; | |
793 | break; | |
794 | ||
795 | case DEFAULT_VALUE: /* fall through */ | |
796 | case ALTERNATE_VALUE: | |
797 | assert(flags & REPLACE_ENV_ALLOW_EXTENDED); | |
798 | ||
799 | if (*e == '{') { | |
800 | nest++; | |
801 | break; | |
802 | } | |
803 | ||
804 | if (*e != '}') | |
805 | break; | |
806 | ||
807 | nest--; | |
808 | if (nest == 0) { | |
809 | _cleanup_strv_free_ char **u = NULL, **b = NULL; | |
810 | _cleanup_free_ char *v = NULL; | |
811 | char *t = NULL; | |
812 | ||
813 | r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb); | |
814 | if (r < 0) | |
815 | return r; | |
816 | ||
817 | if (t && state == ALTERNATE_VALUE) { | |
818 | r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL); | |
819 | if (r < 0) | |
820 | return r; | |
821 | ||
822 | t = v; | |
823 | } else if (!t && state == DEFAULT_VALUE) { | |
824 | r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL); | |
825 | if (r < 0) | |
826 | return r; | |
827 | ||
828 | t = v; | |
829 | } | |
830 | ||
831 | r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true); | |
832 | if (r < 0) | |
833 | return r; | |
834 | r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true); | |
835 | if (r < 0) | |
836 | return r; | |
837 | ||
838 | if (!strextend(&s, t)) | |
839 | return -ENOMEM; | |
840 | ||
841 | word = e+1; | |
842 | state = WORD; | |
843 | } | |
844 | break; | |
845 | ||
846 | case VARIABLE_RAW: | |
847 | assert(flags & REPLACE_ENV_ALLOW_BRACELESS); | |
848 | ||
849 | if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { | |
850 | char *t = NULL; | |
851 | ||
852 | r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables); | |
853 | if (r < 0) | |
854 | return r; | |
855 | ||
856 | if (!strextend(&s, t)) | |
857 | return -ENOMEM; | |
858 | ||
859 | word = e--; | |
860 | i--; | |
861 | state = WORD; | |
862 | } | |
863 | break; | |
864 | } | |
865 | ||
866 | if (state == VARIABLE_RAW) { | |
867 | char *t; | |
868 | ||
869 | assert(flags & REPLACE_ENV_ALLOW_BRACELESS); | |
870 | ||
871 | r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables); | |
872 | if (r < 0) | |
873 | return r; | |
874 | ||
875 | if (!strextend(&s, t)) | |
876 | return -ENOMEM; | |
877 | ||
878 | } else if (!strextendn(&s, word, e-word)) | |
879 | return -ENOMEM; | |
880 | ||
881 | if (ret_unset_variables) | |
882 | *ret_unset_variables = TAKE_PTR(unset_variables); | |
883 | if (ret_bad_variables) | |
884 | *ret_bad_variables = TAKE_PTR(bad_variables); | |
885 | ||
886 | if (ret) | |
887 | *ret = TAKE_PTR(s); | |
888 | ||
889 | return 0; | |
890 | } | |
891 | ||
892 | int replace_env_argv( | |
893 | char **argv, | |
894 | char **env, | |
895 | char ***ret, | |
896 | char ***ret_unset_variables, | |
897 | char ***ret_bad_variables) { | |
898 | ||
899 | _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL; | |
900 | size_t k = 0, l; | |
901 | int r; | |
902 | ||
903 | assert(!strv_isempty(argv)); | |
904 | assert(ret); | |
905 | ||
906 | l = strv_length(argv); | |
907 | ||
908 | n = new(char*, l+1); | |
909 | if (!n) | |
910 | return -ENOMEM; | |
911 | ||
912 | STRV_FOREACH(i, argv) { | |
913 | const char *word = *i; | |
914 | ||
915 | /* If $FOO appears as single word, replace it by the split up variable */ | |
916 | if (word[0] == '$' && !IN_SET(word[1], '{', '$')) { | |
917 | _cleanup_strv_free_ char **m = NULL; | |
918 | const char *name = word + 1; | |
919 | char *e, **w; | |
920 | size_t q; | |
921 | ||
922 | if (env_name_is_valid(name)) { | |
923 | e = strv_env_get(env, name); | |
924 | if (e) | |
925 | r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); | |
926 | else if (ret_unset_variables) | |
927 | r = strv_extend(&unset_variables, name); | |
928 | else | |
929 | r = 0; | |
930 | } else if (ret_bad_variables) | |
931 | r = strv_extend(&bad_variables, name); | |
932 | else | |
933 | r = 0; | |
934 | if (r < 0) | |
935 | return r; | |
936 | ||
937 | q = strv_length(m); | |
938 | l = l + q - 1; | |
939 | ||
940 | w = reallocarray(n, l + 1, sizeof(char*)); | |
941 | if (!w) | |
942 | return -ENOMEM; | |
943 | ||
944 | n = w; | |
945 | if (m) { | |
946 | memcpy(n + k, m, (q + 1) * sizeof(char*)); | |
947 | m = mfree(m); | |
948 | } | |
949 | ||
950 | k += q; | |
951 | continue; | |
952 | } | |
953 | ||
954 | _cleanup_strv_free_ char **u = NULL, **b = NULL; | |
955 | ||
956 | /* If ${FOO} appears as part of a word, replace it by the variable as-is */ | |
957 | r = replace_env_full( | |
958 | word, | |
959 | /* length= */ SIZE_MAX, | |
960 | env, | |
961 | /* flags= */ 0, | |
962 | n + k, | |
963 | ret_unset_variables ? &u : NULL, | |
964 | ret_bad_variables ? &b : NULL); | |
965 | if (r < 0) | |
966 | return r; | |
967 | n[++k] = NULL; | |
968 | ||
969 | r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true); | |
970 | if (r < 0) | |
971 | return r; | |
972 | ||
973 | r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true); | |
974 | if (r < 0) | |
975 | return r; | |
976 | } | |
977 | ||
978 | if (ret_unset_variables) { | |
979 | strv_sort_uniq(unset_variables); | |
980 | *ret_unset_variables = TAKE_PTR(unset_variables); | |
981 | } | |
982 | if (ret_bad_variables) { | |
983 | strv_sort_uniq(bad_variables); | |
984 | *ret_bad_variables = TAKE_PTR(bad_variables); | |
985 | } | |
986 | ||
987 | *ret = TAKE_PTR(n); | |
988 | return 0; | |
989 | } | |
990 | ||
991 | int getenv_bool(const char *p) { | |
992 | const char *e; | |
993 | ||
994 | e = getenv(p); | |
995 | if (!e) | |
996 | return -ENXIO; | |
997 | ||
998 | return parse_boolean(e); | |
999 | } | |
1000 | ||
1001 | int secure_getenv_bool(const char *p) { | |
1002 | const char *e; | |
1003 | ||
1004 | e = secure_getenv(p); | |
1005 | if (!e) | |
1006 | return -ENXIO; | |
1007 | ||
1008 | return parse_boolean(e); | |
1009 | } | |
1010 | ||
1011 | int secure_getenv_uint64(const char *p, uint64_t *ret) { | |
1012 | const char *e; | |
1013 | ||
1014 | assert(p); | |
1015 | ||
1016 | e = secure_getenv(p); | |
1017 | if (!e) | |
1018 | return -ENXIO; | |
1019 | ||
1020 | return safe_atou64(e, ret); | |
1021 | } | |
1022 | ||
1023 | int set_unset_env(const char *name, const char *value, bool overwrite) { | |
1024 | assert(name); | |
1025 | ||
1026 | if (value) | |
1027 | return RET_NERRNO(setenv(name, value, overwrite)); | |
1028 | ||
1029 | return RET_NERRNO(unsetenv(name)); | |
1030 | } | |
1031 | ||
1032 | int putenv_dup(const char *assignment, bool override) { | |
1033 | const char *e, *n; | |
1034 | ||
1035 | e = strchr(assignment, '='); | |
1036 | if (!e) | |
1037 | return -EINVAL; | |
1038 | ||
1039 | n = strndupa_safe(assignment, e - assignment); | |
1040 | ||
1041 | /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */ | |
1042 | return RET_NERRNO(setenv(n, e + 1, override)); | |
1043 | } | |
1044 | ||
1045 | int setenv_systemd_exec_pid(bool update_only) { | |
1046 | const char *e; | |
1047 | int r; | |
1048 | ||
1049 | /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */ | |
1050 | ||
1051 | e = secure_getenv("SYSTEMD_EXEC_PID"); | |
1052 | if (!e && update_only) | |
1053 | return 0; | |
1054 | ||
1055 | if (streq_ptr(e, "*")) | |
1056 | return 0; | |
1057 | ||
1058 | r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached()); | |
1059 | if (r < 0) | |
1060 | return r; | |
1061 | ||
1062 | return 1; | |
1063 | } | |
1064 | ||
1065 | int setenv_systemd_log_level(void) { | |
1066 | _cleanup_free_ char *val = NULL; | |
1067 | int r; | |
1068 | ||
1069 | r = log_level_to_string_alloc(log_get_max_level(), &val); | |
1070 | if (r < 0) | |
1071 | return r; | |
1072 | ||
1073 | return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true)); | |
1074 | } | |
1075 | ||
1076 | int getenv_path_list(const char *name, char ***ret_paths) { | |
1077 | _cleanup_strv_free_ char **l = NULL; | |
1078 | const char *e; | |
1079 | int r; | |
1080 | ||
1081 | assert(name); | |
1082 | assert(ret_paths); | |
1083 | ||
1084 | e = secure_getenv(name); | |
1085 | if (!e) | |
1086 | return -ENXIO; | |
1087 | ||
1088 | r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |
1089 | if (r < 0) | |
1090 | return log_debug_errno(r, "Failed to parse $%s: %m", name); | |
1091 | ||
1092 | STRV_FOREACH(p, l) { | |
1093 | if (!path_is_absolute(*p)) | |
1094 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
1095 | "Path '%s' is not absolute, refusing.", *p); | |
1096 | ||
1097 | if (!path_is_normalized(*p)) | |
1098 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
1099 | "Path '%s' is not normalized, refusing.", *p); | |
1100 | ||
1101 | if (path_equal(*p, "/")) | |
1102 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
1103 | "Path '%s' is the root fs, refusing.", *p); | |
1104 | } | |
1105 | ||
1106 | if (strv_isempty(l)) | |
1107 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
1108 | "No paths specified, refusing."); | |
1109 | ||
1110 | *ret_paths = TAKE_PTR(l); | |
1111 | return 1; | |
1112 | } | |
1113 | ||
1114 | int getenv_steal_erase(const char *name, char **ret) { | |
1115 | _cleanup_(erase_and_freep) char *a = NULL; | |
1116 | char *e; | |
1117 | ||
1118 | assert(name); | |
1119 | ||
1120 | /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes | |
1121 | * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for | |
1122 | * testing, and given that people are likely going to misuse this, be thorough) */ | |
1123 | ||
1124 | e = secure_getenv(name); | |
1125 | if (!e) { | |
1126 | if (ret) | |
1127 | *ret = NULL; | |
1128 | return 0; | |
1129 | } | |
1130 | ||
1131 | if (ret) { | |
1132 | a = strdup(e); | |
1133 | if (!a) | |
1134 | return -ENOMEM; | |
1135 | } | |
1136 | ||
1137 | string_erase(e); | |
1138 | ||
1139 | if (unsetenv(name) < 0) | |
1140 | return -errno; | |
1141 | ||
1142 | if (ret) | |
1143 | *ret = TAKE_PTR(a); | |
1144 | ||
1145 | return 1; | |
1146 | } | |
1147 | ||
1148 | int setenvf(const char *name, bool overwrite, const char *valuef, ...) { | |
1149 | _cleanup_free_ char *value = NULL; | |
1150 | va_list ap; | |
1151 | int r; | |
1152 | ||
1153 | assert(name); | |
1154 | ||
1155 | if (!valuef) | |
1156 | return RET_NERRNO(unsetenv(name)); | |
1157 | ||
1158 | va_start(ap, valuef); | |
1159 | r = vasprintf(&value, valuef, ap); | |
1160 | va_end(ap); | |
1161 | ||
1162 | if (r < 0) | |
1163 | return -ENOMEM; | |
1164 | ||
1165 | /* Try to suppress writes if the value is already set correctly (simply because memory management of | |
1166 | * environment variables sucks a bit. */ | |
1167 | if (streq_ptr(getenv(name), value)) | |
1168 | return 0; | |
1169 | ||
1170 | return RET_NERRNO(setenv(name, value, overwrite)); | |
1171 | } |