void *userdata) {
ExecCommand **e = ASSERT_PTR(data);
- const Unit *u = userdata;
- const char *p;
- bool semicolon;
+ const Unit *u = ASSERT_PTR(userdata);
int r;
assert(filename);
return 0;
}
- p = rvalue;
+ const char *p = rvalue;
+ bool semicolon;
+
do {
_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;
continue;
}
- f = firstword;
- for (;;) {
- /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
- * 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.
+ const char *f = firstword;
+ bool ignore, separate_argv0 = false;
+ ExecCommandFlags flags = 0;
+
+ 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. */
+ * 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)) {
+ if (*f == '-' && !(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;
else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
flags |= EXEC_COMMAND_AMBIENT_MAGIC;
} else
break;
- f++;
}
+ ignore = FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE);
+
r = unit_path_printf(u, f, &path);
if (r < 0) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
}
if (isempty(path)) {
- /* First word is either "-" or "@" with no command. */
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);
return ignore ? 0 : -ENOEXEC;
}
if (!string_is_safe(path)) {
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);
return ignore ? 0 : -ENOEXEC;
}
- if (endswith(path, "/")) {
+ if (path_implies_directory(path)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Executable path specifies a directory%s: %s",
ignore ? ", ignoring" : "", path);
return ignore ? 0 : -ENOEXEC;
}
- if (!separate_argv0) {
- char *w = NULL;
+ _cleanup_strv_free_ char **args = NULL;
- if (!GREEDY_REALLOC0(n, nlen + 2))
+ if (!separate_argv0)
+ if (strv_extend(&args, path) < 0)
return log_oom();
- w = strdup(path);
- if (!w)
- return log_oom();
- n[nlen++] = w;
- n[nlen] = NULL;
- }
-
- path_simplify(path);
-
while (!isempty(p)) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
- /* Check explicitly for an unquoted semicolon as
- * command separator token. */
+ /* Check explicitly for an unquoted semicolon as command separator token. */
if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
p++;
- p += strspn(p, WHITESPACE);
+ p = skip_leading_chars(p, /* bad = */ NULL);
semicolon = true;
break;
}
/* 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]))) {
- char *w;
-
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();
- w = strdup(";");
- if (!w)
- return log_oom();
- n[nlen++] = w;
- n[nlen] = NULL;
continue;
}
r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
- if (r == 0)
- break;
if (r < 0)
return ignore ? 0 : -ENOEXEC;
+ if (r == 0)
+ break;
r = unit_full_printf(u, word, &resolved);
if (r < 0) {
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" : "");
return ignore ? 0 : -ENOEXEC;
}
- if (!GREEDY_REALLOC(n, nlen + 2))
+ if (strv_consume(&args, TAKE_PTR(resolved)) < 0)
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,
"Empty executable name or zeroeth argument%s: %s",
ignore ? ", ignoring" : "", rvalue);
return ignore ? 0 : -ENOEXEC;
}
- nce = new0(ExecCommand, 1);
- if (!nce)
+ ExecCommand *nec = new(ExecCommand, 1);
+ if (!nec)
return log_oom();
- nce->argv = TAKE_PTR(n);
- nce->path = TAKE_PTR(path);
- nce->flags = flags;
-
- exec_command_append_list(e, nce);
+ *nec = (ExecCommand) {
+ .path = path_simplify(TAKE_PTR(path)),
+ .argv = TAKE_PTR(args),
+ .flags = flags,
+ };
- /* Do not _cleanup_free_ these. */
- nce = NULL;
+ exec_command_append_list(e, nec);
rvalue = p;
} while (semicolon);