core: add new UnsetEnvironment= setting for unit files
With this setting we can explicitly unset specific variables for processes of a unit, as last step of assembling the environment block for them. This is useful to fix #6407. While we are at it, greatly expand the documentation on how the environment block for forked off processes is assembled.
This commit is contained in:
+43
-1
@@ -758,6 +758,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UnsetEnvironment", "as", NULL, offsetof(ExecContext, unset_environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -1849,6 +1850,47 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "UnsetEnvironment")) {
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL, **q = NULL;
|
||||
|
||||
r = sd_bus_message_read_strv(message, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_full_printf_strv(u, l, &q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strv_env_name_or_assignment_is_valid(q))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
if (strv_length(q) == 0) {
|
||||
c->unset_environment = strv_free(c->unset_environment);
|
||||
unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=");
|
||||
} else {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
char **e;
|
||||
|
||||
e = strv_env_merge(2, c->unset_environment, q);
|
||||
if (!e)
|
||||
return -ENOMEM;
|
||||
|
||||
strv_free(c->unset_environment);
|
||||
c->unset_environment = e;
|
||||
|
||||
/* We write just the new settings out to file, with unresolved specifiers */
|
||||
joined = strv_join_quoted(l);
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
|
||||
unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=%s", joined);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "TimerSlackNSec")) {
|
||||
|
||||
nsec_t n;
|
||||
@@ -1965,7 +2007,7 @@ int bus_exec_context_set_transient_property(
|
||||
return r;
|
||||
|
||||
if (!strv_env_name_is_valid(l))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block.");
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
if (strv_isempty(l)) {
|
||||
|
||||
@@ -1673,8 +1673,10 @@ static int build_pass_environment(const ExecContext *c, char ***ret) {
|
||||
x = strjoin(*i, "=", v);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
pass_env[n_env++] = x;
|
||||
pass_env[n_env] = NULL;
|
||||
x = NULL;
|
||||
@@ -3031,6 +3033,19 @@ static int exec_child(
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!strv_isempty(context->unset_environment)) {
|
||||
char **ee = NULL;
|
||||
|
||||
ee = strv_env_delete(accum_env, 1, context->unset_environment);
|
||||
if (!ee) {
|
||||
*exit_status = EXIT_MEMORY;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strv_free(accum_env);
|
||||
accum_env = ee;
|
||||
}
|
||||
|
||||
final_argv = replace_env_argv(argv, accum_env);
|
||||
if (!final_argv) {
|
||||
*exit_status = EXIT_MEMORY;
|
||||
@@ -3222,6 +3237,7 @@ void exec_context_done(ExecContext *c) {
|
||||
c->environment = strv_free(c->environment);
|
||||
c->environment_files = strv_free(c->environment_files);
|
||||
c->pass_environment = strv_free(c->pass_environment);
|
||||
c->unset_environment = strv_free(c->unset_environment);
|
||||
|
||||
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
|
||||
c->rlimit[l] = mfree(c->rlimit[l]);
|
||||
@@ -3582,6 +3598,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||
STRV_FOREACH(e, c->pass_environment)
|
||||
fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
|
||||
|
||||
STRV_FOREACH(e, c->unset_environment)
|
||||
fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
|
||||
|
||||
fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
|
||||
|
||||
for (dt = 0; dt < _EXEC_DIRECTORY_MAX; dt++) {
|
||||
|
||||
@@ -133,6 +133,7 @@ struct ExecContext {
|
||||
char **environment;
|
||||
char **environment_files;
|
||||
char **pass_environment;
|
||||
char **unset_environment;
|
||||
|
||||
struct rlimit *rlimit[_RLIMIT_MAX];
|
||||
char *working_directory, *root_directory, *root_image;
|
||||
|
||||
@@ -35,6 +35,7 @@ $1.UMask, config_parse_mode, 0,
|
||||
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
|
||||
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
|
||||
$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
|
||||
$1.UnsetEnvironment, config_parse_unset_environ, 0, offsetof($1, exec_context.unset_environment)
|
||||
$1.DynamicUser, config_parse_bool, true, offsetof($1, exec_context.dynamic_user)
|
||||
$1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context)
|
||||
$1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context)
|
||||
|
||||
+90
-10
@@ -2174,16 +2174,17 @@ int config_parse_environ(const char *unit,
|
||||
}
|
||||
}
|
||||
|
||||
int config_parse_pass_environ(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
int config_parse_pass_environ(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
const char *whole_rvalue = rvalue;
|
||||
char*** passenv = data;
|
||||
@@ -2238,6 +2239,85 @@ int config_parse_pass_environ(const char *unit,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_unset_environ(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_strv_free_ char **n = NULL;
|
||||
const char *whole_rvalue = rvalue;
|
||||
size_t nlen = 0, nbufsize = 0;
|
||||
char*** unsetenv = data;
|
||||
Unit *u = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Empty assignment resets the list */
|
||||
*unsetenv = strv_free(*unsetenv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *k = NULL;
|
||||
|
||||
r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
|
||||
break;
|
||||
}
|
||||
|
||||
if (u) {
|
||||
r = unit_full_printf(u, word, &k);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to resolve specifiers, ignoring: %s", word);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
k = word;
|
||||
word = NULL;
|
||||
}
|
||||
|
||||
if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
|
||||
return log_oom();
|
||||
|
||||
n[nlen++] = k;
|
||||
n[nlen] = NULL;
|
||||
k = NULL;
|
||||
}
|
||||
|
||||
if (n) {
|
||||
r = strv_extend_strv(unsetenv, n, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_ip_tos(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
|
||||
@@ -79,6 +79,7 @@ int config_parse_syscall_archs(const char *unit, const char *filename, unsigned
|
||||
int config_parse_syscall_errno(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_pass_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_unset_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_cpu_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
||||
@@ -641,7 +641,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
r = sd_bus_message_append(m, "v", "i", (int32_t) q);
|
||||
|
||||
} else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
|
||||
} else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
|
||||
const char *p;
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "as");
|
||||
@@ -668,6 +668,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
log_error("Invalid environment assignment: %s", word);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (streq(field, "UnsetEnvironment")) {
|
||||
if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) {
|
||||
log_error("Invalid environment name or assignment: %s", word);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else { /* PassEnvironment */
|
||||
if (!env_name_is_valid(word)) {
|
||||
log_error("Invalid environment variable name: %s", word);
|
||||
|
||||
Reference in New Issue
Block a user