]>
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 | ||
40975963 YW |
461 | int _strv_env_assign_many(char ***l, ...) { |
462 | va_list ap; | |
463 | int r; | |
464 | ||
465 | assert(l); | |
466 | ||
467 | va_start(ap, l); | |
468 | for (;;) { | |
469 | const char *key, *value; | |
470 | ||
471 | key = va_arg(ap, const char *); | |
472 | if (!key) | |
473 | break; | |
474 | ||
475 | if (!env_name_is_valid(key)) { | |
476 | va_end(ap); | |
477 | return -EINVAL; | |
478 | } | |
479 | ||
480 | value = va_arg(ap, const char *); | |
481 | if (!value) { | |
482 | strv_env_unset(*l, key); | |
483 | continue; | |
484 | } | |
485 | ||
486 | char *p = strjoin(key, "=", value); | |
487 | if (!p) { | |
488 | va_end(ap); | |
489 | return -ENOMEM; | |
490 | } | |
491 | ||
492 | r = strv_env_replace_consume(l, p); | |
493 | if (r < 0) { | |
494 | va_end(ap); | |
495 | return r; | |
496 | } | |
497 | } | |
498 | va_end(ap); | |
499 | ||
500 | return 0; | |
501 | } | |
502 | ||
37f3ffca | 503 | char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { |
4d1a6904 LP |
504 | assert(name); |
505 | ||
506 | if (k <= 0) | |
507 | return NULL; | |
508 | ||
6162512c | 509 | STRV_FOREACH_BACKWARDS(i, l) |
641906e9 | 510 | if (strneq(*i, name, k) && |
4d1a6904 LP |
511 | (*i)[k] == '=') |
512 | return *i + k + 1; | |
513 | ||
37f3ffca RS |
514 | if (flags & REPLACE_ENV_USE_ENVIRONMENT) { |
515 | const char *t; | |
516 | ||
2f82562b | 517 | t = strndupa_safe(name, k); |
37f3ffca RS |
518 | return getenv(t); |
519 | }; | |
520 | ||
4d1a6904 LP |
521 | return NULL; |
522 | } | |
523 | ||
524 | char *strv_env_get(char **l, const char *name) { | |
525 | assert(name); | |
526 | ||
37f3ffca | 527 | return strv_env_get_n(l, name, strlen(name), 0); |
4d1a6904 LP |
528 | } |
529 | ||
42e6a77b | 530 | char *strv_env_pairs_get(char **l, const char *name) { |
de010b0b | 531 | char *result = NULL; |
42e6a77b LB |
532 | |
533 | assert(name); | |
534 | ||
535 | STRV_FOREACH_PAIR(key, value, l) | |
536 | if (streq(*key, name)) | |
537 | result = *value; | |
538 | ||
539 | return result; | |
540 | } | |
541 | ||
039f0e70 | 542 | char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { |
4d1a6904 LP |
543 | int k = 0; |
544 | ||
545 | STRV_FOREACH(p, e) { | |
546 | size_t n; | |
547 | bool duplicate = false; | |
548 | ||
549 | if (!env_assignment_is_valid(*p)) { | |
039f0e70 LP |
550 | if (invalid_callback) |
551 | invalid_callback(*p, userdata); | |
4d1a6904 LP |
552 | free(*p); |
553 | continue; | |
554 | } | |
555 | ||
556 | n = strcspn(*p, "="); | |
557 | STRV_FOREACH(q, p + 1) | |
641906e9 | 558 | if (strneq(*p, *q, n) && (*q)[n] == '=') { |
4d1a6904 LP |
559 | duplicate = true; |
560 | break; | |
561 | } | |
562 | ||
563 | if (duplicate) { | |
564 | free(*p); | |
565 | continue; | |
566 | } | |
567 | ||
568 | e[k++] = *p; | |
569 | } | |
570 | ||
5b4fb02d LP |
571 | if (e) |
572 | e[k] = NULL; | |
573 | ||
4d1a6904 LP |
574 | return e; |
575 | } | |
3c800095 | 576 | |
4bed076c | 577 | char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { |
3c800095 RC |
578 | enum { |
579 | WORD, | |
580 | CURLY, | |
ccad1fd0 ZJS |
581 | VARIABLE, |
582 | VARIABLE_RAW, | |
b82f58bf RS |
583 | TEST, |
584 | DEFAULT_VALUE, | |
585 | ALTERNATE_VALUE, | |
3c800095 RC |
586 | } state = WORD; |
587 | ||
1dbd0bdb | 588 | const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */ |
cb4499d0 | 589 | char *k; |
5013d6de | 590 | _cleanup_free_ char *s = NULL; |
1dbd0bdb | 591 | size_t i, len = 0; /* len is initialized to appease gcc */ |
b82f58bf | 592 | int nest = 0; |
3c800095 RC |
593 | |
594 | assert(format); | |
595 | ||
00d4b1e6 | 596 | for (e = format, i = 0; *e && i < n; e ++, i ++) |
3c800095 RC |
597 | switch (state) { |
598 | ||
599 | case WORD: | |
600 | if (*e == '$') | |
601 | state = CURLY; | |
602 | break; | |
603 | ||
604 | case CURLY: | |
605 | if (*e == '{') { | |
5013d6de | 606 | k = strnappend(s, word, e-word-1); |
3c800095 | 607 | if (!k) |
cb4499d0 | 608 | return NULL; |
3c800095 | 609 | |
5013d6de | 610 | free_and_replace(s, k); |
3c800095 RC |
611 | |
612 | word = e-1; | |
613 | state = VARIABLE; | |
b82f58bf | 614 | nest++; |
3c800095 | 615 | } else if (*e == '$') { |
5013d6de | 616 | k = strnappend(s, word, e-word); |
3c800095 | 617 | if (!k) |
cb4499d0 | 618 | return NULL; |
3c800095 | 619 | |
5013d6de | 620 | free_and_replace(s, k); |
3c800095 RC |
621 | |
622 | word = e+1; | |
623 | state = WORD; | |
ccad1fd0 | 624 | |
b45c068d | 625 | } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { |
5013d6de | 626 | k = strnappend(s, word, e-word-1); |
ccad1fd0 ZJS |
627 | if (!k) |
628 | return NULL; | |
629 | ||
5013d6de | 630 | free_and_replace(s, k); |
ccad1fd0 ZJS |
631 | |
632 | word = e-1; | |
633 | state = VARIABLE_RAW; | |
634 | ||
3c800095 RC |
635 | } else |
636 | state = WORD; | |
637 | break; | |
638 | ||
639 | case VARIABLE: | |
640 | if (*e == '}') { | |
641 | const char *t; | |
642 | ||
37f3ffca | 643 | t = strv_env_get_n(env, word+2, e-word-2, flags); |
3c800095 | 644 | |
5013d6de | 645 | if (!strextend(&s, t)) |
cb4499d0 | 646 | return NULL; |
3c800095 | 647 | |
b82f58bf RS |
648 | word = e+1; |
649 | state = WORD; | |
5ef97a71 | 650 | nest--; |
b82f58bf | 651 | } else if (*e == ':') { |
1dbd0bdb ZJS |
652 | if (flags & REPLACE_ENV_ALLOW_EXTENDED) { |
653 | len = e - word - 2; | |
654 | state = TEST; | |
655 | } else | |
b82f58bf RS |
656 | /* Treat this as unsupported syntax, i.e. do no replacement */ |
657 | state = WORD; | |
b82f58bf RS |
658 | } |
659 | break; | |
660 | ||
661 | case TEST: | |
662 | if (*e == '-') | |
663 | state = DEFAULT_VALUE; | |
664 | else if (*e == '+') | |
665 | state = ALTERNATE_VALUE; | |
666 | else { | |
667 | state = WORD; | |
668 | break; | |
669 | } | |
670 | ||
671 | test_value = e+1; | |
672 | break; | |
673 | ||
674 | case DEFAULT_VALUE: /* fall through */ | |
675 | case ALTERNATE_VALUE: | |
676 | assert(flags & REPLACE_ENV_ALLOW_EXTENDED); | |
677 | ||
678 | if (*e == '{') { | |
679 | nest++; | |
680 | break; | |
681 | } | |
682 | ||
683 | if (*e != '}') | |
684 | break; | |
685 | ||
686 | nest--; | |
1d046f57 | 687 | if (nest == 0) { |
b82f58bf RS |
688 | const char *t; |
689 | _cleanup_free_ char *v = NULL; | |
690 | ||
691 | t = strv_env_get_n(env, word+2, len, flags); | |
692 | ||
693 | if (t && state == ALTERNATE_VALUE) | |
694 | t = v = replace_env_n(test_value, e-test_value, env, flags); | |
695 | else if (!t && state == DEFAULT_VALUE) | |
696 | t = v = replace_env_n(test_value, e-test_value, env, flags); | |
697 | ||
5013d6de | 698 | if (!strextend(&s, t)) |
b82f58bf RS |
699 | return NULL; |
700 | ||
3c800095 RC |
701 | word = e+1; |
702 | state = WORD; | |
703 | } | |
704 | break; | |
ccad1fd0 ZJS |
705 | |
706 | case VARIABLE_RAW: | |
707 | assert(flags & REPLACE_ENV_ALLOW_BRACELESS); | |
708 | ||
b45c068d | 709 | if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { |
ccad1fd0 ZJS |
710 | const char *t; |
711 | ||
712 | t = strv_env_get_n(env, word+1, e-word-1, flags); | |
713 | ||
5013d6de | 714 | if (!strextend(&s, t)) |
ccad1fd0 ZJS |
715 | return NULL; |
716 | ||
ccad1fd0 | 717 | word = e--; |
4bed076c | 718 | i--; |
ccad1fd0 ZJS |
719 | state = WORD; |
720 | } | |
721 | break; | |
3c800095 | 722 | } |
3c800095 | 723 | |
ccad1fd0 ZJS |
724 | if (state == VARIABLE_RAW) { |
725 | const char *t; | |
726 | ||
727 | assert(flags & REPLACE_ENV_ALLOW_BRACELESS); | |
728 | ||
729 | t = strv_env_get_n(env, word+1, e-word-1, flags); | |
5013d6de | 730 | return strjoin(s, t); |
ccad1fd0 | 731 | } else |
5013d6de | 732 | return strnappend(s, word, e-word); |
3c800095 RC |
733 | } |
734 | ||
735 | char **replace_env_argv(char **argv, char **env) { | |
cc09d8a5 | 736 | _cleanup_strv_free_ char **ret = NULL; |
da6053d0 | 737 | size_t k = 0, l = 0; |
3c800095 RC |
738 | |
739 | l = strv_length(argv); | |
740 | ||
741 | ret = new(char*, l+1); | |
742 | if (!ret) | |
743 | return NULL; | |
744 | ||
745 | STRV_FOREACH(i, argv) { | |
746 | ||
747 | /* If $FOO appears as single word, replace it by the split up variable */ | |
4c701096 | 748 | if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) { |
3c800095 | 749 | char *e; |
93eceb59 DT |
750 | char **w; |
751 | _cleanup_strv_free_ char **m = NULL; | |
da6053d0 | 752 | size_t q; |
3c800095 RC |
753 | |
754 | e = strv_env_get(env, *i+1); | |
755 | if (e) { | |
756 | int r; | |
757 | ||
90e30d76 | 758 | r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); |
3c800095 RC |
759 | if (r < 0) { |
760 | ret[k] = NULL; | |
3c800095 RC |
761 | return NULL; |
762 | } | |
93eceb59 | 763 | } |
3c800095 RC |
764 | |
765 | q = strv_length(m); | |
766 | l = l + q - 1; | |
767 | ||
62d74c78 | 768 | w = reallocarray(ret, l + 1, sizeof(char *)); |
3c800095 RC |
769 | if (!w) { |
770 | ret[k] = NULL; | |
3c800095 RC |
771 | return NULL; |
772 | } | |
773 | ||
774 | ret = w; | |
775 | if (m) { | |
776 | memcpy(ret + k, m, q * sizeof(char*)); | |
93eceb59 | 777 | m = mfree(m); |
3c800095 RC |
778 | } |
779 | ||
780 | k += q; | |
781 | continue; | |
782 | } | |
783 | ||
784 | /* If ${FOO} appears as part of a word, replace it by the variable as-is */ | |
37f3ffca | 785 | ret[k] = replace_env(*i, env, 0); |
cc09d8a5 | 786 | if (!ret[k]) |
3c800095 | 787 | return NULL; |
3c800095 RC |
788 | k++; |
789 | } | |
790 | ||
791 | ret[k] = NULL; | |
cc09d8a5 | 792 | return TAKE_PTR(ret); |
3c800095 | 793 | } |
b41b9d2a LP |
794 | |
795 | int getenv_bool(const char *p) { | |
796 | const char *e; | |
797 | ||
798 | e = getenv(p); | |
799 | if (!e) | |
800 | return -ENXIO; | |
801 | ||
802 | return parse_boolean(e); | |
803 | } | |
fe902fa4 | 804 | |
71e0accc ZJS |
805 | int getenv_bool_secure(const char *p) { |
806 | const char *e; | |
807 | ||
808 | e = secure_getenv(p); | |
809 | if (!e) | |
810 | return -ENXIO; | |
811 | ||
812 | return parse_boolean(e); | |
813 | } | |
063f9f0d | 814 | |
3fa8a114 JSMR |
815 | int getenv_uint64_secure(const char *p, uint64_t *ret) { |
816 | const char *e; | |
817 | ||
818 | assert(p); | |
819 | ||
820 | e = secure_getenv(p); | |
821 | if (!e) | |
822 | return -ENXIO; | |
823 | ||
824 | return safe_atou64(e, ret); | |
825 | } | |
826 | ||
063f9f0d | 827 | int set_unset_env(const char *name, const char *value, bool overwrite) { |
7c248223 | 828 | assert(name); |
063f9f0d ZJS |
829 | |
830 | if (value) | |
7c248223 LP |
831 | return RET_NERRNO(setenv(name, value, overwrite)); |
832 | ||
833 | return RET_NERRNO(unsetenv(name)); | |
063f9f0d | 834 | } |
cfd1c6e2 | 835 | |
fa256f43 ZJS |
836 | int putenv_dup(const char *assignment, bool override) { |
837 | const char *e, *n; | |
838 | ||
839 | e = strchr(assignment, '='); | |
840 | if (!e) | |
841 | return -EINVAL; | |
842 | ||
2f82562b | 843 | n = strndupa_safe(assignment, e - assignment); |
fa256f43 ZJS |
844 | |
845 | /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */ | |
7c248223 | 846 | return RET_NERRNO(setenv(n, e + 1, override)); |
fa256f43 ZJS |
847 | } |
848 | ||
cfd1c6e2 YW |
849 | int setenv_systemd_exec_pid(bool update_only) { |
850 | char str[DECIMAL_STR_MAX(pid_t)]; | |
851 | const char *e; | |
852 | ||
853 | /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */ | |
854 | ||
855 | e = secure_getenv("SYSTEMD_EXEC_PID"); | |
856 | if (!e && update_only) | |
857 | return 0; | |
858 | ||
859 | if (streq_ptr(e, "*")) | |
860 | return 0; | |
861 | ||
862 | xsprintf(str, PID_FMT, getpid_cached()); | |
863 | ||
864 | if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0) | |
865 | return -errno; | |
866 | ||
867 | return 1; | |
868 | } | |
93547f28 LB |
869 | |
870 | int getenv_path_list(const char *name, char ***ret_paths) { | |
871 | _cleanup_strv_free_ char **l = NULL; | |
872 | const char *e; | |
93547f28 LB |
873 | int r; |
874 | ||
875 | assert(name); | |
876 | assert(ret_paths); | |
877 | ||
93547f28 LB |
878 | e = secure_getenv(name); |
879 | if (!e) | |
33d31c0e | 880 | return -ENXIO; |
93547f28 LB |
881 | |
882 | r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |
883 | if (r < 0) | |
884 | return log_debug_errno(r, "Failed to parse $%s: %m", name); | |
885 | ||
886 | STRV_FOREACH(p, l) { | |
887 | if (!path_is_absolute(*p)) | |
888 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
889 | "Path '%s' is not absolute, refusing.", *p); | |
890 | ||
891 | if (!path_is_normalized(*p)) | |
892 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
893 | "Path '%s' is not normalized, refusing.", *p); | |
894 | ||
895 | if (path_equal(*p, "/")) | |
896 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
897 | "Path '%s' is the root fs, refusing.", *p); | |
898 | } | |
899 | ||
900 | if (strv_isempty(l)) | |
901 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
902 | "No paths specified, refusing."); | |
903 | ||
904 | *ret_paths = TAKE_PTR(l); | |
33d31c0e | 905 | return 1; |
93547f28 | 906 | } |
7a6abbe9 | 907 | |
e99ca147 LP |
908 | int getenv_steal_erase(const char *name, char **ret) { |
909 | _cleanup_(erase_and_freep) char *a = NULL; | |
910 | char *e; | |
7a6abbe9 LP |
911 | |
912 | assert(name); | |
913 | ||
e99ca147 LP |
914 | /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes |
915 | * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for | |
916 | * testing, and given that people are likely going to misuse this, be thorough) */ | |
917 | ||
918 | e = getenv(name); | |
919 | if (!e) { | |
920 | if (ret) | |
921 | *ret = NULL; | |
7a6abbe9 | 922 | return 0; |
e99ca147 | 923 | } |
7a6abbe9 | 924 | |
e99ca147 LP |
925 | if (ret) { |
926 | a = strdup(e); | |
927 | if (!a) | |
928 | return -ENOMEM; | |
929 | } | |
930 | ||
931 | string_erase(e); | |
7a6abbe9 LP |
932 | |
933 | if (unsetenv(name) < 0) | |
934 | return -errno; | |
935 | ||
e99ca147 LP |
936 | if (ret) |
937 | *ret = TAKE_PTR(a); | |
938 | ||
7a6abbe9 LP |
939 | return 1; |
940 | } |