summaryrefslogtreecommitdiff
path: root/src/libnm-systemd-shared/src/basic/env-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/env-util.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c367
1 files changed, 286 insertions, 81 deletions
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index fa2753bccb..c12caa2e55 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -29,20 +29,21 @@
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
- if (!e)
- return false;
+
+ if (n == SIZE_MAX)
+ n = strlen_ptr(e);
if (n <= 0)
return false;
+ assert(e);
+
if (ascii_isdigit(e[0]))
return false;
- /* POSIX says the overall size of the environment block cannot
- * be > ARG_MAX, an individual assignment hence cannot be
- * either. Discounting the equal sign and trailing NUL this
- * hence leaves ARG_MAX-2 as longest possible variable
- * name. */
+ /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
+ * hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as
+ * longest possible variable name. */
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
@@ -246,9 +247,9 @@ static bool env_match(const char *t, const char *pattern) {
return true;
if (!strchr(pattern, '=')) {
- size_t l = strlen(pattern);
+ t = startswith(t, pattern);
- return strneq(t, pattern, l) && t[l] == '=';
+ return t && *t == '=';
}
return false;
@@ -460,6 +461,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
return strv_env_replace_consume(l, p);
}
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+ int r;
+
+ assert(l);
+ assert(key);
+
+ if (!env_name_is_valid(key))
+ return -EINVAL;
+
+ if (!valuef) {
+ strv_env_unset(*l, key);
+ return 0;
+ }
+
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ va_start(ap, valuef);
+ r = vasprintf(&value, valuef, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ char *p = strjoin(key, "=", value);
+ if (!p)
+ return -ENOMEM;
+
+ return strv_env_replace_consume(l, p);
+}
+
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
@@ -502,32 +532,31 @@ int _strv_env_assign_many(char ***l, ...) {
return 0;
}
-char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
assert(name);
+ if (k == SIZE_MAX)
+ k = strlen(name);
if (k <= 0)
return NULL;
STRV_FOREACH_BACKWARDS(i, l)
- if (strneq(*i, name, k) &&
- (*i)[k] == '=')
- return *i + k + 1;
+ if (strneq(*i, name, k) && (*i)[k] == '=')
+ return (char*) *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
+ /* Safety check that the name is not overly long, before we do a stack allocation */
+ if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
+ return NULL;
+
t = strndupa_safe(name, k);
return getenv(t);
};
return NULL;
}
-
-char *strv_env_get(char **l, const char *name) {
- assert(name);
-
- return strv_env_get_n(l, name, strlen(name), 0);
-}
#endif /* NM_IGNORED */
char *strv_env_pairs_get(char **l, const char *name) {
@@ -578,7 +607,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
-char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
+static int strv_extend_with_length(char ***l, const char *s, size_t n) {
+ char *c;
+
+ c = strndup(s, n);
+ if (!c)
+ return -ENOMEM;
+
+ return strv_consume(l, c);
+}
+
+static int strv_env_get_n_validated(
+ char **env,
+ const char *name,
+ size_t l,
+ ReplaceEnvFlags flags,
+ char **ret, /* points into the env block! do not free! */
+ char ***unset_variables, /* updated in place */
+ char ***bad_variables) { /* ditto */
+
+ char *e;
+ int r;
+
+ assert(l == 0 || name);
+ assert(ret);
+
+ if (env_name_is_valid_n(name, l)) {
+ e = strv_env_get_n(env, name, l, flags);
+ if (!e && unset_variables) {
+ r = strv_extend_with_length(unset_variables, name, l);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ e = NULL; /* Resolve invalid variable names the same way as unset ones */
+
+ if (bad_variables) {
+ r = strv_extend_with_length(bad_variables, name, l);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = e;
+ return !!e;
+}
+
+int replace_env_full(
+ const char *format,
+ size_t n,
+ char **env,
+ ReplaceEnvFlags flags,
+ char **ret,
+ char ***ret_unset_variables,
+ char ***ret_bad_variables) {
+
enum {
WORD,
CURLY,
@@ -589,15 +672,22 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
ALTERNATE_VALUE,
} state = WORD;
+ _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
- char *k;
_cleanup_free_ char *s = NULL;
+ char ***pu, ***pb, *k;
size_t i, len = 0; /* len is initialized to appease gcc */
- int nest = 0;
+ int nest = 0, r;
assert(format);
- for (e = format, i = 0; *e && i < n; e ++, i ++)
+ if (n == SIZE_MAX)
+ n = strlen(format);
+
+ pu = ret_unset_variables ? &unset_variables : NULL;
+ pb = ret_bad_variables ? &bad_variables : NULL;
+
+ for (e = format, i = 0; *e && i < n; e++, i++)
switch (state) {
case WORD:
@@ -609,27 +699,28 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
if (*e == '{') {
k = strnappend(s, word, e-word-1);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
word = e-1;
state = VARIABLE;
nest++;
+
} else if (*e == '$') {
k = strnappend(s, word, e-word);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
word = e+1;
state = WORD;
- } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
+ } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(s, word, e-word-1);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
@@ -642,12 +733,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
case VARIABLE:
if (*e == '}') {
- const char *t;
+ char *t;
- t = strv_env_get_n(env, word+2, e-word-2, flags);
+ r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e+1;
state = WORD;
@@ -689,18 +782,37 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
nest--;
if (nest == 0) {
- const char *t;
+ _cleanup_strv_free_ char **u = NULL, **b = NULL;
_cleanup_free_ char *v = NULL;
+ char *t = NULL;
+
+ r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
+ if (r < 0)
+ return r;
+
+ if (t && state == ALTERNATE_VALUE) {
+ r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+ if (r < 0)
+ return r;
+
+ t = v;
+ } else if (!t && state == DEFAULT_VALUE) {
+ r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+ if (r < 0)
+ return r;
- t = strv_env_get_n(env, word+2, len, flags);
+ t = v;
+ }
- if (t && state == ALTERNATE_VALUE)
- t = v = replace_env_n(test_value, e-test_value, env, flags);
- else if (!t && state == DEFAULT_VALUE)
- t = v = replace_env_n(test_value, e-test_value, env, flags);
+ r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+ r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e+1;
state = WORD;
@@ -711,12 +823,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
- const char *t;
+ char *t = NULL;
- t = strv_env_get_n(env, word+1, e-word-1, flags);
+ r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e--;
i--;
@@ -726,58 +840,83 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
}
if (state == VARIABLE_RAW) {
- const char *t;
+ char *t;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
- t = strv_env_get_n(env, word+1, e-word-1, flags);
- return strjoin(s, t);
- } else
- return strnappend(s, word, e-word);
+ r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+ if (r < 0)
+ return r;
+
+ if (!strextend(&s, t))
+ return -ENOMEM;
+
+ } else if (!strextendn(&s, word, e-word))
+ return -ENOMEM;
+
+ if (ret_unset_variables)
+ *ret_unset_variables = TAKE_PTR(unset_variables);
+ if (ret_bad_variables)
+ *ret_bad_variables = TAKE_PTR(bad_variables);
+
+ if (ret)
+ *ret = TAKE_PTR(s);
+
+ return 0;
}
-char **replace_env_argv(char **argv, char **env) {
- _cleanup_strv_free_ char **ret = NULL;
+int replace_env_argv(
+ char **argv,
+ char **env,
+ char ***ret,
+ char ***ret_unset_variables,
+ char ***ret_bad_variables) {
+
+ _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
size_t k = 0, l = 0;
+ int r;
l = strv_length(argv);
- ret = new(char*, l+1);
- if (!ret)
- return NULL;
+ n = new(char*, l+1);
+ if (!n)
+ return -ENOMEM;
STRV_FOREACH(i, argv) {
+ const char *word = *i;
/* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
- char *e;
- char **w;
+ if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
_cleanup_strv_free_ char **m = NULL;
+ const char *name = word + 1;
+ char *e, **w;
size_t q;
- e = strv_env_get(env, *i+1);
- if (e) {
- int r;
-
- r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
- if (r < 0) {
- ret[k] = NULL;
- return NULL;
- }
- }
+ if (env_name_is_valid(name)) {
+ e = strv_env_get(env, name);
+ if (e)
+ r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
+ else if (ret_unset_variables)
+ r = strv_extend(&unset_variables, name);
+ else
+ r = 0;
+ } else if (ret_bad_variables)
+ r = strv_extend(&bad_variables, name);
+ else
+ r = 0;
+ if (r < 0)
+ return r;
q = strv_length(m);
l = l + q - 1;
- w = reallocarray(ret, l + 1, sizeof(char *));
- if (!w) {
- ret[k] = NULL;
- return NULL;
- }
+ w = reallocarray(n, l + 1, sizeof(char*));
+ if (!w)
+ return -ENOMEM;
- ret = w;
+ n = w;
if (m) {
- memcpy(ret + k, m, q * sizeof(char*));
+ memcpy(n + k, m, (q + 1) * sizeof(char*));
m = mfree(m);
}
@@ -785,15 +924,41 @@ char **replace_env_argv(char **argv, char **env) {
continue;
}
+ _cleanup_strv_free_ char **u = NULL, **b = NULL;
+
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env, 0);
- if (!ret[k])
- return NULL;
- k++;
+ r = replace_env_full(
+ word,
+ /* length= */ SIZE_MAX,
+ env,
+ /* flags= */ 0,
+ n + k,
+ ret_unset_variables ? &u : NULL,
+ ret_bad_variables ? &b : NULL);
+ if (r < 0)
+ return r;
+ n[++k] = NULL;
+
+ r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+
+ r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_unset_variables) {
+ strv_uniq(strv_sort(unset_variables));
+ *ret_unset_variables = TAKE_PTR(unset_variables);
+ }
+ if (ret_bad_variables) {
+ strv_uniq(strv_sort(bad_variables));
+ *ret_bad_variables = TAKE_PTR(bad_variables);
}
- ret[k] = NULL;
- return TAKE_PTR(ret);
+ *ret = TAKE_PTR(n);
+ return 0;
}
#endif /* NM_IGNORED */
@@ -853,8 +1018,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
- char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
+ int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@@ -865,10 +1030,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
- xsprintf(str, PID_FMT, getpid_cached());
-
- if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
- return -errno;
+ r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+ if (r < 0)
+ return r;
return 1;
}
@@ -944,4 +1108,45 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1;
}
+
+int set_full_environment(char **env) {
+ int r;
+
+ clearenv();
+
+ STRV_FOREACH(e, env) {
+ _cleanup_free_ char *k = NULL, *v = NULL;
+
+ r = split_pair(*e, "=", &k, &v);
+ if (r < 0)
+ return r;
+
+ if (setenv(k, v, /* overwrite= */ true) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ int r;
+
+ assert(name);
+
+ if (!valuef)
+ return RET_NERRNO(unsetenv(name));
+
+ va_start(ap, valuef);
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ r = vasprintf(&value, valuef, ap);
+ REENABLE_WARNING;
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return RET_NERRNO(setenv(name, value, overwrite));
+}
#endif /* NM_IGNORED */