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