mirror of
https://github.com/systemd/systemd
synced 2024-10-15 12:34:37 +00:00
Merge pull request #32210 from YHNdnzj/execcommand-done
core: modernize ExecCommand parsing and fix one memory leak
This commit is contained in:
commit
8e7c157387
|
@ -1306,18 +1306,24 @@ int bus_set_transient_exec_command(
|
||||||
sd_bus_message *message,
|
sd_bus_message *message,
|
||||||
UnitWriteFlags flags,
|
UnitWriteFlags flags,
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
bool is_ex_prop = endswith(name, "Ex");
|
|
||||||
unsigned n = 0;
|
const char *ex_prop = endswith(ASSERT_PTR(name), "Ex");
|
||||||
|
size_t n = 0;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* Drop Ex from the written setting. E.g. ExecStart=, not ExecStartEx=. */
|
assert(u);
|
||||||
const char *written_name = is_ex_prop ? strndupa_safe(name, strlen(name) - 2) : name;
|
assert(exec_command);
|
||||||
|
assert(message);
|
||||||
|
assert(error);
|
||||||
|
|
||||||
r = sd_bus_message_enter_container(message, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
|
/* Drop Ex from the written setting. E.g. ExecStart=, not ExecStartEx=. */
|
||||||
|
const char *written_name = ex_prop ? strndupa_safe(name, ex_prop - name) : name;
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(message, 'a', ex_prop ? "(sasas)" : "(sasb)");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
while ((r = sd_bus_message_enter_container(message, 'r', is_ex_prop ? "sasas" : "sasb")) > 0) {
|
while ((r = sd_bus_message_enter_container(message, 'r', ex_prop ? "sasas" : "sasb")) > 0) {
|
||||||
_cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
|
_cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
|
||||||
const char *path;
|
const char *path;
|
||||||
int b;
|
int b;
|
||||||
|
@ -1339,7 +1345,7 @@ int bus_set_transient_exec_command(
|
||||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||||
"\"%s\" argv cannot be empty", name);
|
"\"%s\" argv cannot be empty", name);
|
||||||
|
|
||||||
r = is_ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
|
r = ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -1348,29 +1354,28 @@ int bus_set_transient_exec_command(
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||||
ExecCommand *c;
|
_cleanup_(exec_command_freep) ExecCommand *c = NULL;
|
||||||
|
|
||||||
c = new0(ExecCommand, 1);
|
c = new(ExecCommand, 1);
|
||||||
if (!c)
|
if (!c)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
c->path = strdup(path);
|
*c = (ExecCommand) {
|
||||||
if (!c->path) {
|
.argv = TAKE_PTR(argv),
|
||||||
free(c);
|
};
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->argv = TAKE_PTR(argv);
|
r = path_simplify_alloc(path, &c->path);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
if (is_ex_prop) {
|
if (ex_prop) {
|
||||||
r = exec_command_flags_from_strv(ex_opts, &c->flags);
|
r = exec_command_flags_from_strv(ex_opts, &c->flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
} else
|
} else if (b)
|
||||||
c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
|
c->flags |= EXEC_COMMAND_IGNORE_FAILURE;
|
||||||
|
|
||||||
path_simplify(c->path);
|
exec_command_append_list(exec_command, TAKE_PTR(c));
|
||||||
exec_command_append_list(exec_command, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
|
|
|
@ -672,13 +672,19 @@ void exec_command_done_array(ExecCommand *c, size_t n) {
|
||||||
exec_command_done(i);
|
exec_command_done(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExecCommand* exec_command_free(ExecCommand *c) {
|
||||||
|
if (!c)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
exec_command_done(c);
|
||||||
|
return mfree(c);
|
||||||
|
}
|
||||||
|
|
||||||
ExecCommand* exec_command_free_list(ExecCommand *c) {
|
ExecCommand* exec_command_free_list(ExecCommand *c) {
|
||||||
ExecCommand *i;
|
ExecCommand *i;
|
||||||
|
|
||||||
while ((i = LIST_POP(command, c))) {
|
while ((i = LIST_POP(command, c)))
|
||||||
exec_command_done(i);
|
exec_command_free(i);
|
||||||
free(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -487,10 +487,13 @@ int exec_spawn(Unit *unit,
|
||||||
|
|
||||||
void exec_command_done(ExecCommand *c);
|
void exec_command_done(ExecCommand *c);
|
||||||
void exec_command_done_array(ExecCommand *c, size_t n);
|
void exec_command_done_array(ExecCommand *c, size_t n);
|
||||||
|
ExecCommand* exec_command_free(ExecCommand *c);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecCommand*, exec_command_free);
|
||||||
ExecCommand* exec_command_free_list(ExecCommand *c);
|
ExecCommand* exec_command_free_list(ExecCommand *c);
|
||||||
void exec_command_free_array(ExecCommand **c, size_t n);
|
void exec_command_free_array(ExecCommand **c, size_t n);
|
||||||
void exec_command_reset_status_array(ExecCommand *c, size_t n);
|
void exec_command_reset_status_array(ExecCommand *c, size_t n);
|
||||||
void exec_command_reset_status_list_array(ExecCommand **c, size_t n);
|
void exec_command_reset_status_list_array(ExecCommand **c, size_t n);
|
||||||
|
|
||||||
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
|
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
|
||||||
void exec_command_append_list(ExecCommand **l, ExecCommand *e);
|
void exec_command_append_list(ExecCommand **l, ExecCommand *e);
|
||||||
int exec_command_set(ExecCommand *c, const char *path, ...) _sentinel_;
|
int exec_command_set(ExecCommand *c, const char *path, ...) _sentinel_;
|
||||||
|
|
|
@ -856,9 +856,7 @@ int config_parse_exec(
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
ExecCommand **e = ASSERT_PTR(data);
|
ExecCommand **e = ASSERT_PTR(data);
|
||||||
const Unit *u = userdata;
|
const Unit *u = ASSERT_PTR(userdata);
|
||||||
const char *p;
|
|
||||||
bool semicolon;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(filename);
|
assert(filename);
|
||||||
|
@ -873,15 +871,11 @@ int config_parse_exec(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = rvalue;
|
const char *p = rvalue;
|
||||||
|
bool semicolon;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_cleanup_free_ char *path = NULL, *firstword = NULL;
|
_cleanup_free_ char *path = NULL, *firstword = NULL;
|
||||||
ExecCommandFlags flags = 0;
|
|
||||||
bool ignore = false, separate_argv0 = false;
|
|
||||||
_cleanup_free_ ExecCommand *nce = NULL;
|
|
||||||
_cleanup_strv_free_ char **n = NULL;
|
|
||||||
size_t nlen = 0;
|
|
||||||
const char *f;
|
|
||||||
|
|
||||||
semicolon = false;
|
semicolon = false;
|
||||||
|
|
||||||
|
@ -895,25 +889,30 @@ int config_parse_exec(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
f = firstword;
|
const char *f = firstword;
|
||||||
for (;;) {
|
bool ignore, separate_argv0 = false;
|
||||||
/* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
|
ExecCommandFlags flags = 0;
|
||||||
* exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
|
|
||||||
* argv[0]; if it's prefixed with :, we will not do environment variable substitution;
|
|
||||||
* if it's prefixed with +, it will be run with full privileges and no sandboxing; if
|
|
||||||
* it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
|
|
||||||
* it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
|
|
||||||
* capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
|
|
||||||
* other sandboxing, with some special exceptions for changing UID.
|
|
||||||
*
|
|
||||||
* The idea is that '!!' may be used to write services that can take benefit of systemd's
|
|
||||||
* UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
|
|
||||||
* privilege dropping within the daemon if the kernel does not offer that. */
|
|
||||||
|
|
||||||
if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
|
for (;; f++) {
|
||||||
|
/* We accept an absolute path as first argument. Valid prefixes and their effect:
|
||||||
|
*
|
||||||
|
* "-": Ignore if the path doesn't exist
|
||||||
|
* "@": Allow overridding argv[0] (supplied as a separate argument)
|
||||||
|
* ":": Disable environment variable substitution
|
||||||
|
* "+": Run with full privileges and no sandboxing
|
||||||
|
* "!": Apply sandboxing except for user/group credentials
|
||||||
|
* "!!": Apply user/group credentials if the kernel supports ambient capabilities -
|
||||||
|
* if it doesn't we don't apply the credentials themselves, but do apply
|
||||||
|
* most other sandboxing, with some special exceptions for changing UID.
|
||||||
|
*
|
||||||
|
* The idea is that '!!' may be used to write services that can take benefit of
|
||||||
|
* systemd's UID/GID dropping if the kernel supports ambient creds, but provide
|
||||||
|
* an automatic fallback to privilege dropping within the daemon if the kernel
|
||||||
|
* does not offer that. */
|
||||||
|
|
||||||
|
if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE))
|
||||||
flags |= EXEC_COMMAND_IGNORE_FAILURE;
|
flags |= EXEC_COMMAND_IGNORE_FAILURE;
|
||||||
ignore = true;
|
else if (*f == '@' && !separate_argv0)
|
||||||
} else if (*f == '@' && !separate_argv0)
|
|
||||||
separate_argv0 = true;
|
separate_argv0 = true;
|
||||||
else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
|
else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
|
||||||
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
|
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
|
||||||
|
@ -926,9 +925,10 @@ int config_parse_exec(
|
||||||
flags |= EXEC_COMMAND_AMBIENT_MAGIC;
|
flags |= EXEC_COMMAND_AMBIENT_MAGIC;
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
f++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignore = FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE);
|
||||||
|
|
||||||
r = unit_path_printf(u, f, &path);
|
r = unit_path_printf(u, f, &path);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
|
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
|
||||||
|
@ -938,19 +938,18 @@ int config_parse_exec(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isempty(path)) {
|
if (isempty(path)) {
|
||||||
/* First word is either "-" or "@" with no command. */
|
|
||||||
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
||||||
"Empty path in command line%s: '%s'",
|
"Empty path in command line%s: %s",
|
||||||
ignore ? ", ignoring" : "", rvalue);
|
ignore ? ", ignoring" : "", rvalue);
|
||||||
return ignore ? 0 : -ENOEXEC;
|
return ignore ? 0 : -ENOEXEC;
|
||||||
}
|
}
|
||||||
if (!string_is_safe(path)) {
|
if (!string_is_safe(path)) {
|
||||||
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
||||||
"Executable name contains special characters%s: %s",
|
"Executable path contains special characters%s: %s",
|
||||||
ignore ? ", ignoring" : "", path);
|
ignore ? ", ignoring" : "", path);
|
||||||
return ignore ? 0 : -ENOEXEC;
|
return ignore ? 0 : -ENOEXEC;
|
||||||
}
|
}
|
||||||
if (endswith(path, "/")) {
|
if (path_implies_directory(path)) {
|
||||||
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
||||||
"Executable path specifies a directory%s: %s",
|
"Executable path specifies a directory%s: %s",
|
||||||
ignore ? ", ignoring" : "", path);
|
ignore ? ", ignoring" : "", path);
|
||||||
|
@ -964,92 +963,71 @@ int config_parse_exec(
|
||||||
return ignore ? 0 : -ENOEXEC;
|
return ignore ? 0 : -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!separate_argv0) {
|
_cleanup_strv_free_ char **args = NULL;
|
||||||
char *w = NULL;
|
|
||||||
|
|
||||||
if (!GREEDY_REALLOC0(n, nlen + 2))
|
if (!separate_argv0)
|
||||||
|
if (strv_extend(&args, path) < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
w = strdup(path);
|
|
||||||
if (!w)
|
|
||||||
return log_oom();
|
|
||||||
n[nlen++] = w;
|
|
||||||
n[nlen] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
path_simplify(path);
|
|
||||||
|
|
||||||
while (!isempty(p)) {
|
while (!isempty(p)) {
|
||||||
_cleanup_free_ char *word = NULL, *resolved = NULL;
|
_cleanup_free_ char *word = NULL, *resolved = NULL;
|
||||||
|
|
||||||
/* Check explicitly for an unquoted semicolon as
|
/* Check explicitly for an unquoted semicolon as command separator token. */
|
||||||
* command separator token. */
|
|
||||||
if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
|
if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
|
||||||
p++;
|
p++;
|
||||||
p += strspn(p, WHITESPACE);
|
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||||
semicolon = true;
|
semicolon = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
|
/* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
|
||||||
* extract_first_word() would return the same for all of those. */
|
* extract_first_word() would return the same for all of those. */
|
||||||
if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
|
if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
|
||||||
char *w;
|
|
||||||
|
|
||||||
p += 2;
|
p += 2;
|
||||||
p += strspn(p, WHITESPACE);
|
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||||
|
|
||||||
if (!GREEDY_REALLOC0(n, nlen + 2))
|
if (strv_extend(&args, ";") < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
w = strdup(";");
|
|
||||||
if (!w)
|
|
||||||
return log_oom();
|
|
||||||
n[nlen++] = w;
|
|
||||||
n[nlen] = NULL;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
|
r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
|
||||||
if (r == 0)
|
|
||||||
break;
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return ignore ? 0 : -ENOEXEC;
|
return ignore ? 0 : -ENOEXEC;
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
r = unit_full_printf(u, word, &resolved);
|
r = unit_full_printf(u, word, &resolved);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
|
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
|
||||||
"Failed to resolve unit specifiers in %s%s: %m",
|
"Failed to resolve unit specifiers in '%s'%s: %m",
|
||||||
word, ignore ? ", ignoring" : "");
|
word, ignore ? ", ignoring" : "");
|
||||||
return ignore ? 0 : -ENOEXEC;
|
return ignore ? 0 : -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GREEDY_REALLOC(n, nlen + 2))
|
if (strv_consume(&args, TAKE_PTR(resolved)) < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
n[nlen++] = TAKE_PTR(resolved);
|
|
||||||
n[nlen] = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!n || !n[0]) {
|
if (strv_isempty(args)) {
|
||||||
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
|
||||||
"Empty executable name or zeroeth argument%s: %s",
|
"Empty executable name or zeroeth argument%s: %s",
|
||||||
ignore ? ", ignoring" : "", rvalue);
|
ignore ? ", ignoring" : "", rvalue);
|
||||||
return ignore ? 0 : -ENOEXEC;
|
return ignore ? 0 : -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
nce = new0(ExecCommand, 1);
|
ExecCommand *nec = new(ExecCommand, 1);
|
||||||
if (!nce)
|
if (!nec)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
nce->argv = TAKE_PTR(n);
|
*nec = (ExecCommand) {
|
||||||
nce->path = TAKE_PTR(path);
|
.path = path_simplify(TAKE_PTR(path)),
|
||||||
nce->flags = flags;
|
.argv = TAKE_PTR(args),
|
||||||
|
.flags = flags,
|
||||||
|
};
|
||||||
|
|
||||||
exec_command_append_list(e, nce);
|
exec_command_append_list(e, nec);
|
||||||
|
|
||||||
/* Do not _cleanup_free_ these. */
|
|
||||||
nce = NULL;
|
|
||||||
|
|
||||||
rvalue = p;
|
rvalue = p;
|
||||||
} while (semicolon);
|
} while (semicolon);
|
||||||
|
|
Loading…
Reference in a new issue