summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2016-08-04 12:00:00 -0400
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2017-02-20 18:49:14 -0500
commit37f3ffca273e5238794019caede7b7cd33a5de3a (patch)
treee33c5556d5cb2dd89064d8c3344b904fd8853f47 /src
parentd8ad241f54b8c4ac76aafd960d89b47b0ed87fb6 (diff)
basic: add new merge_env_file function
merge_env_file is a new function, that's like load_env_file, but takes a pre-existing environment as an input argument. New environment entries are merged. Variable expansion is performed. Falling back to the process environment is supported (when a flag is set). Alternatively this could be implemented as passing an additional fallback environment array, but later on we're adding another flag to allow braceless expansion, and the two flags can be combined in one arg, so there's less stuff to pass around.
Diffstat (limited to 'src')
-rw-r--r--src/basic/env-util.c17
-rw-r--r--src/basic/env-util.h8
-rw-r--r--src/basic/fileio.c28
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/test/test-env-util.c31
-rw-r--r--src/test/test-fileio.c52
6 files changed, 131 insertions, 7 deletions
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 86ac07e1b6..99a130008b 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -454,7 +454,7 @@ fail:
return NULL;
}
-char *strv_env_get_n(char **l, const char *name, size_t k) {
+char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char **i;
assert(name);
@@ -467,13 +467,20 @@ char *strv_env_get_n(char **l, const char *name, size_t k) {
(*i)[k] == '=')
return *i + k + 1;
+ if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
+ const char *t;
+
+ t = strndupa(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));
+ return strv_env_get_n(l, name, strlen(name), 0);
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
@@ -512,7 +519,7 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
-char *replace_env(const char *format, char **env) {
+char *replace_env(const char *format, char **env, unsigned flags) {
enum {
WORD,
CURLY,
@@ -563,7 +570,7 @@ char *replace_env(const char *format, char **env) {
if (*e == '}') {
const char *t;
- t = strv_env_get_n(env, word+2, e-word-2);
+ t = strv_env_get_n(env, word+2, e-word-2, flags);
k = strappend(r, t);
if (!k)
@@ -643,7 +650,7 @@ char **replace_env_argv(char **argv, char **env) {
}
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env);
+ ret[k] = replace_env(*i, env, 0);
if (!ret[k]) {
strv_free(ret);
return NULL;
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index 90df5b1cd9..4e83dcb43a 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -29,7 +29,11 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
-char *replace_env(const char *format, char **env);
+enum {
+ REPLACE_ENV_USE_ENVIRONMENT = 1u,
+};
+
+char *replace_env(const char *format, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
bool strv_env_is_valid(char **e);
@@ -47,7 +51,7 @@ char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace(char ***l, char *p); /* In place ... */
-char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
+char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index a1e4978125..49dd52bfd9 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -762,6 +762,34 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
return 0;
}
+static int merge_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ char ***env = userdata;
+ char *expanded_value;
+
+ assert(env);
+
+ expanded_value = replace_env(value, *env, REPLACE_ENV_USE_ENVIRONMENT);
+ if (!expanded_value)
+ return -ENOMEM;
+
+ free_and_replace(value, expanded_value);
+
+ return load_env_file_push(filename, line, key, value, env, n_pushed);
+}
+
+int merge_env_file(
+ char ***env,
+ FILE *f,
+ const char *fname) {
+
+ return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
+}
+
static void write_env_var(FILE *f, const char *v) {
const char *p;
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 64852b15a8..e547614cc4 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+int merge_env_file(char ***env, FILE *f, const char *fname);
+
int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c
index e004c518fb..f44cb3d57b 100644
--- a/src/test/test-env-util.c
+++ b/src/test/test-env-util.c
@@ -112,6 +112,36 @@ static void test_strv_env_merge(void) {
assert_se(strv_length(r) == 5);
}
+static void test_env_strv_get_n(void) {
+ const char *_env[] = {
+ "FOO=NO NO NO",
+ "FOO=BAR BAR",
+ "BAR=waldo",
+ "PATH=unset",
+ NULL
+ };
+ char **env = (char**) _env;
+
+ assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR"));
+ assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
+ assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR"));
+ assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
+
+ assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset"));
+ assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset"));
+ assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
+ assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
+
+ env[3] = NULL; /* kill our $PATH */
+
+ assert_se(!strv_env_get_n(env, "PATH__", 4, 0));
+ assert_se(!strv_env_get_n(env, "PATH", 4, 0));
+ assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT),
+ getenv("PATH")));
+ assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT),
+ getenv("PATH")));
+}
+
static void test_replace_env_arg(void) {
const char *env[] = {
"FOO=BAR BAR",
@@ -225,6 +255,7 @@ int main(int argc, char *argv[]) {
test_strv_env_unset();
test_strv_env_set();
test_strv_env_merge();
+ test_env_strv_get_n();
test_replace_env_arg();
test_env_clean();
test_env_name_is_valid();
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index a38bb874a9..84f394a713 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -206,6 +206,56 @@ static void test_parse_multiline_env_file(void) {
unlink(p);
}
+static void test_merge_env_file(void) {
+ char t[] = "/tmp/test-fileio-XXXXXX";
+ int fd, r;
+ FILE *f;
+ _cleanup_strv_free_ char **a = NULL;
+ char **i;
+
+ fd = mkostemp_safe(t);
+ assert_se(fd >= 0);
+
+ log_info("/* %s (%s) */", __func__, t);
+
+ f = fdopen(fd, "w");
+ assert_se(f);
+
+ r = write_string_stream(f,
+ "one=1 \n"
+ "twelve=${one}2\n"
+ "twentyone=2${one}\n"
+ "one=2\n"
+ "twentytwo=2${one}\n", false);
+ assert(r >= 0);
+
+ r = merge_env_file(&a, NULL, t);
+ assert_se(r >= 0);
+ strv_sort(a);
+
+ STRV_FOREACH(i, a)
+ log_info("Got: <%s>", *i);
+
+ assert_se(streq(a[0], "one=2"));
+ assert_se(streq(a[1], "twelve=12"));
+ assert_se(streq(a[2], "twentyone=21"));
+ assert_se(streq(a[3], "twentytwo=22"));
+ assert_se(a[4] == NULL);
+
+
+ r = merge_env_file(&a, NULL, t);
+ assert_se(r >= 0);
+ strv_sort(a);
+
+ STRV_FOREACH(i, a)
+ log_info("Got2: <%s>", *i);
+
+ assert_se(streq(a[0], "one=2"));
+ assert_se(streq(a[1], "twelve=12"));
+ assert_se(streq(a[2], "twentyone=21"));
+ assert_se(streq(a[3], "twentytwo=22"));
+ assert_se(a[4] == NULL);
+}
static void test_executable_is_script(void) {
char t[] = "/tmp/test-executable-XXXXXX";
@@ -557,11 +607,13 @@ static void test_tempfn(void) {
}
int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
log_parse_environment();
log_open();
test_parse_env_file();
test_parse_multiline_env_file();
+ test_merge_env_file();
test_executable_is_script();
test_status_field();
test_capeff();