1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-31 16:21:26 +03:00

Merge pull request #18601 from keszybz/env-assign-cleanup

Envvar assignment cleanup
This commit is contained in:
Lennart Poettering 2021-02-16 22:36:48 +01:00 committed by GitHub
commit 356b7a58f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 128 additions and 209 deletions

View File

@ -385,11 +385,9 @@ static int load_env_file_push(
if (!p)
return -ENOMEM;
r = strv_env_replace(m, p);
if (r < 0) {
free(p);
r = strv_env_replace_consume(m, p);
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;

View File

@ -363,20 +363,23 @@ char **strv_env_unset_many(char **l, ...) {
return l;
}
int strv_env_replace(char ***l, char *p) {
int strv_env_replace_consume(char ***l, char *p) {
const char *t, *name;
char **f;
int r;
assert(p);
/* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
* in-place. Does not copy p. p must be a valid key=value assignment.
*/
/* Replace first occurrence of the env var or add a new one in the string list. Drop other
* occurrences. Edits in-place. Does not copy p and CONSUMES p EVEN ON FAILURE.
*
* p must be a valid key=value assignment. */
t = strchr(p, '=');
if (!t)
if (!t) {
free(p);
return -EINVAL;
}
name = strndupa(p, t - p);
@ -388,39 +391,39 @@ int strv_env_replace(char ***l, char *p) {
}
/* We didn't find a match, we need to append p or create a new strv */
r = strv_push(l, p);
r = strv_consume(l, p);
if (r < 0)
return r;
return 1;
}
char **strv_env_set(char **x, const char *p) {
_cleanup_strv_free_ char **ret = NULL;
size_t n, m;
char **k;
int strv_env_replace_strdup(char ***l, const char *assignment) {
/* Like strv_env_replace_consume(), but copies the argument. */
/* Overrides the env var setting of p, returns a new copy */
char *p = strdup(assignment);
if (!p)
return -ENOMEM;
n = strv_length(x);
m = n + 2;
if (m < n) /* overflow? */
return NULL;
return strv_env_replace_consume(l, p);
}
ret = new(char*, m);
if (!ret)
return NULL;
int strv_env_assign(char ***l, const char *key, const char *value) {
if (!env_name_is_valid(key))
return -EINVAL;
*ret = NULL;
k = ret;
/* NULL removes assignment, "" creates an empty assignment. */
if (env_append(ret, &k, x) < 0)
return NULL;
if (!value) {
strv_env_unset(*l, key);
return 0;
}
if (env_append(ret, &k, STRV_MAKE(p)) < 0)
return NULL;
char *p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
return TAKE_PTR(ret);
return strv_env_replace_consume(l, p);
}
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {

View File

@ -42,10 +42,11 @@ bool strv_env_name_or_assignment_is_valid(char **l);
char **strv_env_merge(size_t n_lists, ...);
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_set(char **x, const char *p); /* New copy ... */
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 ... */
int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
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_;

View File

@ -2636,7 +2636,7 @@ int config_parse_environ(
}
for (const char *p = rvalue;; ) {
_cleanup_free_ char *word = NULL, *k = NULL;
_cleanup_free_ char *word = NULL, *resolved = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
if (r == 0)
@ -2650,26 +2650,24 @@ int config_parse_environ(
}
if (u) {
r = unit_full_printf(u, word, &k);
r = unit_full_printf(u, word, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to resolve unit specifiers in %s, ignoring: %m", word);
continue;
}
} else
k = TAKE_PTR(word);
resolved = TAKE_PTR(word);
if (!env_assignment_is_valid(k)) {
if (!env_assignment_is_valid(resolved)) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Invalid environment assignment, ignoring: %s", k);
"Invalid environment assignment, ignoring: %s", resolved);
continue;
}
r = strv_env_replace(env, k);
r = strv_env_replace_consume(env, TAKE_PTR(resolved));
if (r < 0)
return log_oom();
k = NULL;
return log_error_errno(r, "Failed to update environment: %m");
}
}

View File

@ -453,16 +453,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
if (env_assignment_is_valid(value)) {
char **env;
env = strv_env_set(arg_default_environment, value);
if (!env)
if (!env_assignment_is_valid(value))
log_warning("Environment variable assignment '%s' is not valid. Ignoring.", value);
else {
r = strv_env_replace_strdup(&arg_default_environment, value);
if (r < 0)
return log_oom();
arg_default_environment = env;
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
}
} else if (proc_cmdline_key_streq(key, "systemd.machine_id")) {

View File

@ -642,22 +642,14 @@ int manager_default_environment(Manager *m) {
/* Import locale variables LC_*= from configuration */
(void) locale_setup(&m->transient_environment);
} else {
_cleanup_free_ char *k = NULL;
/* The user manager passes its own environment
* along to its children, except for $PATH. */
/* The user manager passes its own environment along to its children, except for $PATH. */
m->transient_environment = strv_copy(environ);
if (!m->transient_environment)
return log_oom();
k = strdup("PATH=" DEFAULT_USER_PATH);
if (!k)
return log_oom();
r = strv_env_replace(&m->transient_environment, k);
r = strv_env_replace_strdup(&m->transient_environment, "PATH=" DEFAULT_USER_PATH);
if (r < 0)
return log_oom();
TAKE_PTR(k);
}
sanitize_environment(m->transient_environment);

View File

@ -2512,7 +2512,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SETENV: {
_cleanup_free_ char **l = NULL, **k = NULL;
_cleanup_free_ char **l = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *ne = NULL;
JsonVariant *e;
@ -2525,7 +2525,8 @@ static int parse_argv(int argc, char *argv[]) {
}
if (!env_assignment_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Environment assignment '%s' not valid.", optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Environment assignment '%s' not valid.", optarg);
e = json_variant_by_key(arg_identity_extra, "environment");
if (e) {
@ -2534,13 +2535,13 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(r, "Failed to parse JSON environment field: %m");
}
k = strv_env_set(l, optarg);
if (!k)
return log_oom();
r = strv_env_replace_strdup(&l, optarg);
if (r < 0)
return log_error_errno(r, "Failed to replace JSON environment field: %m");
strv_sort(k);
strv_sort(l);
r = json_variant_new_array_strv(&ne, k);
r = json_variant_new_array_strv(&ne, l);
if (r < 0)
return log_error_errno(r, "Failed to allocate environment list JSON: %m");

View File

@ -423,25 +423,11 @@ static int context_write_data_machine_info(Context *c) {
return r;
for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
_cleanup_free_ char *t = NULL;
char **u;
assert(name[p]);
if (isempty(c->data[p])) {
strv_env_unset(l, name[p]);
continue;
}
t = strjoin(name[p], "=", c->data[p]);
if (!t)
return -ENOMEM;
u = strv_env_set(l, t);
if (!u)
return -ENOMEM;
strv_free_and_replace(l, u);
r = strv_env_assign(&l, name[p], empty_to_null(c->data[p]));
if (r < 0)
return r;
}
if (strv_isempty(l)) {

View File

@ -66,9 +66,7 @@ static void context_free_vconsole(Context *c) {
}
static void context_free_locale(Context *c) {
int p;
for (p = 0; p < _VARIABLE_LC_MAX; p++)
for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
c->locale[p] = mfree(c->locale[p]);
}
@ -85,9 +83,7 @@ void context_clear(Context *c) {
};
void locale_simplify(char *locale[_VARIABLE_LC_MAX]) {
int p;
for (p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++)
for (LocaleVariable p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++)
if (isempty(locale[p]) || streq_ptr(locale[VARIABLE_LANG], locale[p]))
locale[p] = mfree(locale[p]);
}
@ -138,13 +134,11 @@ int locale_read_data(Context *c, sd_bus_message *m) {
if (r < 0)
return r;
} else {
int p;
c->locale_mtime = USEC_INFINITY;
context_free_locale(c);
/* Fill in what we got passed from systemd. */
for (p = 0; p < _VARIABLE_LC_MAX; p++) {
for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
const char *name;
name = locale_variable_to_string(p);
@ -294,30 +288,16 @@ int x11_read_data(Context *c, sd_bus_message *m) {
int locale_write_data(Context *c, char ***settings) {
_cleanup_strv_free_ char **l = NULL;
struct stat st;
int r, p;
int r;
/* Set values will be returned as strv in *settings on success. */
for (p = 0; p < _VARIABLE_LC_MAX; p++) {
_cleanup_free_ char *t = NULL;
char **u;
const char *name;
name = locale_variable_to_string(p);
assert(name);
if (isempty(c->locale[p]))
continue;
if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
return -ENOMEM;
u = strv_env_set(l, t);
if (!u)
return -ENOMEM;
strv_free_and_replace(l, u);
}
for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
if (!isempty(c->locale[p])) {
r = strv_env_assign(&l, locale_variable_to_string(p), c->locale[p]);
if (r < 0)
return r;
}
if (strv_isempty(l)) {
if (unlink("/etc/locale.conf") < 0)
@ -348,39 +328,13 @@ int vconsole_write_data(Context *c) {
if (r < 0 && r != -ENOENT)
return r;
if (isempty(c->vc_keymap))
l = strv_env_unset(l, "KEYMAP");
else {
_cleanup_free_ char *s = NULL;
char **u;
r = strv_env_assign(&l, "KEYMAP", empty_to_null(c->vc_keymap));
if (r < 0)
return r;
s = strjoin("KEYMAP=", c->vc_keymap);
if (!s)
return -ENOMEM;
u = strv_env_set(l, s);
if (!u)
return -ENOMEM;
strv_free_and_replace(l, u);
}
if (isempty(c->vc_keymap_toggle))
l = strv_env_unset(l, "KEYMAP_TOGGLE");
else {
_cleanup_free_ char *s = NULL;
char **u;
s = strjoin("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
if (!s)
return -ENOMEM;
u = strv_env_set(l, s);
if (!u)
return -ENOMEM;
strv_free_and_replace(l, u);
}
r = strv_env_assign(&l, "KEYMAP_TOGGLE", empty_to_null(c->vc_keymap_toggle));
if (r < 0)
return r;
if (strv_isempty(l)) {
if (unlink("/etc/vconsole.conf") < 0)

View File

@ -55,7 +55,6 @@ static void status_info_clear(StatusInfo *info) {
static void print_overridden_variables(void) {
_cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {};
bool print_warning = true;
LocaleVariable j;
int r;
if (arg_transport != BUS_TRANSPORT_LOCAL)
@ -82,7 +81,7 @@ static void print_overridden_variables(void) {
return;
}
for (j = 0; j < _VARIABLE_LC_MAX; j++)
for (LocaleVariable j = 0; j < _VARIABLE_LC_MAX; j++)
if (variables[j]) {
if (print_warning) {
log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n"

View File

@ -37,8 +37,7 @@ static int locale_update_system_manager(Context *c, sd_bus *bus) {
_cleanup_strv_free_ char **l_set = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
size_t c_set, c_unset;
LocaleVariable p;
size_t c_set = 0, c_unset = 0;
int r;
assert(bus);
@ -51,7 +50,7 @@ static int locale_update_system_manager(Context *c, sd_bus *bus) {
if (!l_set)
return log_oom();
for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) {
for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
const char *name;
name = locale_variable_to_string(p);
@ -178,7 +177,7 @@ static int property_get_locale(
Context *c = userdata;
_cleanup_strv_free_ char **l = NULL;
int p, q, r;
int r;
r = locale_read_data(c, reply);
if (r < 0)
@ -188,7 +187,7 @@ static int property_get_locale(
if (!l)
return -ENOMEM;
for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) {
for (LocaleVariable p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) {
char *t;
const char *name;

View File

@ -1110,17 +1110,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'E': {
char **n;
if (!env_assignment_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Environment variable assignment '%s' is not valid.", optarg);
r = strv_env_replace_strdup(&arg_setenv, optarg);
if (r < 0)
return r;
n = strv_env_set(arg_setenv, optarg);
if (!n)
return log_oom();
strv_free_and_replace(arg_setenv, n);
arg_settings_mask |= SETTING_ENVIRONMENT;
break;
}

View File

@ -278,18 +278,12 @@ static int gather_environment_generate(int fd, void *arg) {
return r;
STRV_FOREACH_PAIR(x, y, new) {
char *p;
if (!env_name_is_valid(*x)) {
log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
continue;
}
p = strjoin(*x, "=", *y);
if (!p)
return -ENOMEM;
r = strv_env_replace(env, p);
r = strv_env_assign(env, *x, *y);
if (r < 0)
return r;
@ -297,7 +291,7 @@ static int gather_environment_generate(int fd, void *arg) {
return -errno;
}
return r;
return 0;
}
static int gather_environment_collect(int fd, void *arg) {

View File

@ -175,7 +175,7 @@ int deserialize_dual_timestamp(const char *value, dual_timestamp *t) {
}
int deserialize_environment(const char *value, char ***list) {
_cleanup_free_ char *unescaped = NULL;
char *unescaped;
int r;
assert(value);
@ -187,11 +187,9 @@ int deserialize_environment(const char *value, char ***list) {
if (r < 0)
return log_error_errno(r, "Failed to unescape: %m");
r = strv_env_replace(list, unescaped);
r = strv_env_replace_consume(list, unescaped);
if (r < 0)
return log_error_errno(r, "Failed to append environment variable: %m");
unescaped = NULL; /* now part of 'list' */
return 0;
}

View File

@ -608,7 +608,6 @@ static int json_dispatch_access_mode(const char *name, JsonVariant *variant, Jso
static int json_dispatch_environment(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
_cleanup_strv_free_ char **n = NULL;
char ***l = userdata;
size_t i;
int r;
if (json_variant_is_null(variant)) {
@ -619,8 +618,7 @@ static int json_dispatch_environment(const char *name, JsonVariant *variant, Jso
if (!json_variant_is_array(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
for (i = 0; i < json_variant_elements(variant); i++) {
_cleanup_free_ char *c = NULL;
for (size_t i = 0; i < json_variant_elements(variant); i++) {
JsonVariant *e;
const char *a;
@ -633,19 +631,12 @@ static int json_dispatch_environment(const char *name, JsonVariant *variant, Jso
if (!env_assignment_is_valid(a))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
c = strdup(a);
if (!c)
return json_log_oom(variant, flags);
r = strv_env_replace(&n, c);
r = strv_env_replace_strdup(&n, a);
if (r < 0)
return json_log_oom(variant, flags);
c = NULL;
}
strv_free_and_replace(*l, n);
return 0;
return strv_free_and_replace(*l, n);
}
int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
@ -1471,7 +1462,7 @@ int user_group_record_mangle(
JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
JsonVariant *array[ELEMENTSOF(mask_field) * 2];
size_t n_retain = 0, i;
size_t n_retain = 0;
UserRecordMask m = 0;
int r;
@ -1496,7 +1487,7 @@ int user_group_record_mangle(
return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Stripping everything from record, refusing.");
/* Check if we have the special sections and if they match our flags set */
for (i = 0; i < ELEMENTSOF(mask_field); i++) {
for (size_t i = 0; i < ELEMENTSOF(mask_field); i++) {
JsonVariant *e, *k;
if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), mask_field[i].mask)) {
@ -1535,16 +1526,15 @@ int user_group_record_mangle(
r = json_variant_new_object(&w, array, n_retain);
if (r < 0)
return json_log(v, json_flags, r, "Failed to allocate new object: %m");
} else {
} else
/* And now check if there's anything else in the record */
for (i = 0; i < json_variant_elements(v); i += 2) {
for (size_t i = 0; i < json_variant_elements(v); i += 2) {
const char *f;
bool special = false;
size_t j;
assert_se(f = json_variant_string(json_variant_by_index(v, i)));
for (j = 0; j < ELEMENTSOF(mask_field); j++)
for (size_t j = 0; j < ELEMENTSOF(mask_field); j++)
if (streq(f, mask_field[j].name)) { /* already covered in the loop above */
special = true;
continue;
@ -1558,7 +1548,6 @@ int user_group_record_mangle(
break;
}
}
}
if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");

View File

@ -59,24 +59,6 @@ static void test_strv_env_unset(void) {
assert_se(strv_length(l) == 2);
}
static void test_strv_env_set(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **l = NULL, **r = NULL;
l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES");
assert_se(l);
r = strv_env_set(l, "WALDO=WALDO");
assert_se(r);
assert_se(streq(r[0], "PIEP"));
assert_se(streq(r[1], "SCHLUMPF=SMURFF"));
assert_se(streq(r[2], "NANANANA=YES"));
assert_se(streq(r[3], "WALDO=WALDO"));
assert_se(strv_length(r) == 4);
}
static void test_strv_env_merge(void) {
log_info("/* %s */", __func__);
@ -107,6 +89,37 @@ static void test_strv_env_merge(void) {
assert_se(strv_length(r) == 5);
}
static void test_strv_env_replace_strdup(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **a = NULL;
assert_se(strv_env_replace_strdup(&a, "a=a") == 1);
assert_se(strv_env_replace_strdup(&a, "b=b") == 1);
assert_se(strv_env_replace_strdup(&a, "a=A") == 0);
assert_se(strv_length(a) == 2);
strv_sort(a);
assert_se(streq(a[0], "a=A"));
assert_se(streq(a[1], "b=b"));
}
static void test_strv_env_assign(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **a = NULL;
assert_se(strv_env_assign(&a, "a", "a") == 1);
assert_se(strv_env_assign(&a, "b", "b") == 1);
assert_se(strv_env_assign(&a, "a", "A") == 0);
assert_se(strv_env_assign(&a, "b", NULL) == 0);
assert_se(strv_env_assign(&a, "a=", "B") == -EINVAL);
assert_se(strv_length(a) == 1);
assert_se(streq(a[0], "a=A"));
}
static void test_env_strv_get_n(void) {
log_info("/* %s */", __func__);
@ -378,8 +391,9 @@ int main(int argc, char *argv[]) {
test_strv_env_delete();
test_strv_env_get();
test_strv_env_unset();
test_strv_env_set();
test_strv_env_merge();
test_strv_env_replace_strdup();
test_strv_env_assign();
test_env_strv_get_n();
test_replace_env(false);
test_replace_env(true);