1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "alloc-util.h"
31 #include "extract-word.h"
33 #include "parse-util.h"
34 #include "string-util.h"
38 #define VALID_CHARS_ENV_NAME \
43 #define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
46 static bool env_name_is_valid_n(const char *e, size_t n) {
55 if (e[0] >= '0' && e[0] <= '9')
58 /* POSIX says the overall size of the environment block cannot
59 * be > ARG_MAX, an individual assignment hence cannot be
60 * either. Discounting the equal sign and trailing NUL this
61 * hence leaves ARG_MAX-2 as longest possible variable
66 for (p = e; p < e + n; p++)
67 if (!strchr(VALID_CHARS_ENV_NAME, *p))
73 bool env_name_is_valid(const char *e) {
77 return env_name_is_valid_n(e, strlen(e));
80 bool env_value_is_valid(const char *e) {
84 if (!utf8_is_valid(e))
87 /* bash allows tabs in environment variables, and so should
89 if (string_has_cc(e, "\t"))
92 /* POSIX says the overall size of the environment block cannot
93 * be > ARG_MAX, an individual assignment hence cannot be
94 * either. Discounting the shortest possible variable name of
95 * length 1, the equal sign and trailing NUL this hence leaves
96 * ARG_MAX-3 as longest possible variable value. */
97 if (strlen(e) > ARG_MAX - 3)
103 bool env_assignment_is_valid(const char *e) {
110 if (!env_name_is_valid_n(e, eq - e))
113 if (!env_value_is_valid(eq + 1))
116 /* POSIX says the overall size of the environment block cannot
117 * be > ARG_MAX, hence the individual variable assignments
118 * cannot be either, but let's leave room for one trailing NUL
120 if (strlen(e) > ARG_MAX - 1)
126 #if 0 /// UNNEEDED by elogind
127 bool strv_env_is_valid(char **e) {
133 if (!env_assignment_is_valid(*p))
136 /* Check if there are duplicate assginments */
137 k = strcspn(*p, "=");
138 STRV_FOREACH(q, p + 1)
139 if (strneq(*p, *q, k) && (*q)[k] == '=')
146 bool strv_env_name_is_valid(char **l) {
150 if (!env_name_is_valid(*p))
153 STRV_FOREACH(q, p + 1)
161 bool strv_env_name_or_assignment_is_valid(char **l) {
165 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
168 STRV_FOREACH(q, p + 1)
176 static int env_append(char **r, char ***k, char **a) {
183 /* Add the entries of a to *k unless they already exist in *r
184 * in which case they are overridden instead. This assumes
185 * there is enough space in the r array. */
191 n = strcspn(*a, "=");
196 for (j = r; j < *k; j++)
197 if (strneq(*j, *a, n))
213 char **strv_env_merge(unsigned n_lists, ...) {
219 /* Merges an arbitrary number of environment sets */
221 va_start(ap, n_lists);
222 for (i = 0; i < n_lists; i++) {
223 l = va_arg(ap, char**);
234 va_start(ap, n_lists);
235 for (i = 0; i < n_lists; i++) {
236 l = va_arg(ap, char**);
237 if (env_append(r, &k, l) < 0)
253 static bool env_match(const char *t, const char *pattern) {
257 /* pattern a matches string a
262 * a= does not match a
263 * a=b does not match a=
264 * a=b does not match a
265 * a=b does not match a=c */
267 if (streq(t, pattern))
270 if (!strchr(pattern, '=')) {
271 size_t l = strlen(pattern);
273 return strneq(t, pattern, l) && t[l] == '=';
279 static bool env_entry_has_name(const char *entry, const char *name) {
285 t = startswith(entry, name);
292 char **strv_env_delete(char **x, unsigned n_lists, ...) {
297 /* Deletes every entry from x that is mentioned in the other
309 va_start(ap, n_lists);
310 for (v = 0; v < n_lists; v++) {
313 l = va_arg(ap, char**);
315 if (env_match(*k, *j))
340 char **strv_env_unset(char **l, const char *p) {
349 /* Drops every occurrence of the env var setting p in the
350 * string list. Edits in-place. */
352 for (f = t = l; *f; f++) {
354 if (env_match(*f, p)) {
366 char **strv_env_unset_many(char **l, ...) {
373 /* Like strv_env_unset() but applies many at once. Edits in-place. */
375 for (f = t = l; *f; f++) {
382 while ((p = va_arg(ap, const char*))) {
383 if (env_match(*f, p)) {
403 int strv_env_replace(char ***l, char *p) {
405 const char *t, *name;
409 /* Replace first occurrence of the env var or add a new one in the
410 * string list. Drop other occurences. Edits in-place. Does not copy p.
411 * p must be a valid key=value assignment.
417 name = strndupa(p, t - p);
419 for (f = *l; f && *f; f++)
420 if (env_entry_has_name(*f, name)) {
421 free_and_replace(*f, p);
422 strv_env_unset(f + 1, *f);
426 /* We didn't find a match, we need to append p or create a new strv */
427 if (strv_push(l, p) < 0)
432 char **strv_env_set(char **x, const char *p) {
435 char* m[2] = { (char*) p, NULL };
437 /* Overrides the env var setting of p, returns a new copy */
439 r = new(char*, strv_length(x)+2);
444 if (env_append(r, &k, x) < 0)
447 if (env_append(r, &k, m) < 0)
459 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
467 STRV_FOREACH_BACKWARDS(i, l)
468 if (strneq(*i, name, k) &&
472 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
475 t = strndupa(name, k);
482 char *strv_env_get(char **l, const char *name) {
485 return strv_env_get_n(l, name, strlen(name), 0);
488 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
494 bool duplicate = false;
496 if (!env_assignment_is_valid(*p)) {
497 if (invalid_callback)
498 invalid_callback(*p, userdata);
503 n = strcspn(*p, "=");
504 STRV_FOREACH(q, p + 1)
505 if (strneq(*p, *q, n) && (*q)[n] == '=') {
524 char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
535 const char *e, *word = format, *test_value;
537 _cleanup_free_ char *r = NULL;
543 for (e = format, i = 0; *e && i < n; e ++, i ++)
553 k = strnappend(r, word, e-word-1);
557 free_and_replace(r, k);
562 } else if (*e == '$') {
563 k = strnappend(r, word, e-word);
567 free_and_replace(r, k);
572 } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
573 k = strnappend(r, word, e-word-1);
577 free_and_replace(r, k);
580 state = VARIABLE_RAW;
590 t = strv_env_get_n(env, word+2, e-word-2, flags);
596 free_and_replace(r, k);
600 } else if (*e == ':') {
601 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
602 /* Treat this as unsupported syntax, i.e. do no replacement */
613 state = DEFAULT_VALUE;
615 state = ALTERNATE_VALUE;
624 case DEFAULT_VALUE: /* fall through */
625 case ALTERNATE_VALUE:
626 assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
639 _cleanup_free_ char *v = NULL;
641 t = strv_env_get_n(env, word+2, len, flags);
643 if (t && state == ALTERNATE_VALUE)
644 t = v = replace_env_n(test_value, e-test_value, env, flags);
645 else if (!t && state == DEFAULT_VALUE)
646 t = v = replace_env_n(test_value, e-test_value, env, flags);
652 free_and_replace(r, k);
660 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
662 if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
665 t = strv_env_get_n(env, word+1, e-word-1, flags);
671 free_and_replace(r, k);
680 if (state == VARIABLE_RAW) {
683 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
685 t = strv_env_get_n(env, word+1, e-word-1, flags);
686 return strappend(r, t);
688 return strnappend(r, word, e-word);
691 char **replace_env_argv(char **argv, char **env) {
693 unsigned k = 0, l = 0;
695 l = strv_length(argv);
697 ret = new(char*, l+1);
701 STRV_FOREACH(i, argv) {
703 /* If $FOO appears as single word, replace it by the split up variable */
704 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
706 char **w, **m = NULL;
709 e = strv_env_get(env, *i+1);
713 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
725 w = reallocarray(ret, l + 1, sizeof(char *));
735 memcpy(ret + k, m, q * sizeof(char*));
743 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
744 ret[k] = replace_env(*i, env, 0);
756 int getenv_bool(const char *p) {
763 return parse_boolean(e);
766 int getenv_bool_secure(const char *p) {
769 e = secure_getenv(p);
773 return parse_boolean(e);
776 int serialize_environment(FILE *f, char **environment) {
779 STRV_FOREACH(e, environment) {
780 _cleanup_free_ char *ce;
786 fprintf(f, "env=%s\n", ce);
789 /* caller should call ferror() */
794 int deserialize_environment(char ***environment, const char *line) {
801 assert(startswith(line, "env="));
802 r = cunescape(line + 4, 0, &uce);
806 return strv_env_replace(environment, uce);