if (!exec_chars)
return -ENOMEM;
- a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS);
+ a = unit_concat_strv(c->argv, UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_EXEC_SYNTAX);
if (!a)
return -ENOMEM;
_cleanup_free_ char *t = NULL;
const char *p;
- p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t);
+ p = unit_escape_setting(c->path,
+ UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_EXEC_SYNTAX, &t);
if (!p)
return -ENOMEM;
}
char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) {
- char *ret = NULL;
+ assert(!FLAGS_SET(flags, UNIT_ESCAPE_EXEC_SYNTAX | UNIT_ESCAPE_C));
+
+ _cleanup_free_ char *t = NULL;
if (!s)
return NULL;
- /* Escapes the input string as requested. Returns the escaped string. If 'buf' is specified then the allocated
- * return buffer pointer is also written to *buf, except if no escaping was necessary, in which case *buf is
- * set to NULL, and the input pointer is returned as-is. This means the return value always contains a properly
- * escaped version, but *buf when passed only contains a pointer if an allocation was necessary. If *buf is
- * not specified, then the return value always needs to be freed. Callers can use this to optimize memory
- * allocations. */
+ /* Escapes the input string as requested. Returns the escaped string. If 'buf' is specified then the
+ * allocated return buffer pointer is also written to *buf, except if no escaping was necessary, in
+ * which case *buf is set to NULL, and the input pointer is returned as-is. This means the return
+ * value always contains a properly escaped version, but *buf when passed only contains a pointer if
+ * an allocation was necessary. If *buf is not specified, then the return value always needs to be
+ * freed. Callers can use this to optimize memory allocations. */
if (flags & UNIT_ESCAPE_SPECIFIERS) {
- ret = specifier_escape(s);
- if (!ret)
+ t = specifier_escape(s);
+ if (!t)
return NULL;
- s = ret;
+ s = t;
}
- if (flags & UNIT_ESCAPE_C) {
- char *a;
+ /* We either do c-escaping or shell-escaping, to additionally escape characters that we parse for
+ * ExecStart= and friend, i.e. '$' and ';' and quotes. */
+
+ if (flags & UNIT_ESCAPE_EXEC_SYNTAX) {
+ char *t2 = shell_escape(s, "$;'\"");
+ if (!t2)
+ return NULL;
+ free_and_replace(t, t2);
+
+ s = t;
- a = cescape(s);
- free(ret);
- if (!a)
+ } else if (flags & UNIT_ESCAPE_C) {
+ char *t2 = cescape(s);
+ if (!t2)
return NULL;
+ free_and_replace(t, t2);
- ret = a;
+ s = t;
}
if (buf) {
- *buf = ret;
- return ret ?: (char*) s;
+ *buf = TAKE_PTR(t);
+ return (char*) s;
}
- return ret ?: strdup(s);
+ return TAKE_PTR(t) ?: strdup(s);
}
char* unit_concat_strv(char **l, UnitWriteFlags flags) {
_cleanup_free_ char *result = NULL;
size_t n = 0;
- /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command lines in a
- * way suitable for ExecStart= stanzas */
+ /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command
+ * lines in a way suitable for ExecStart= stanzas. */
STRV_FOREACH(i, l) {
_cleanup_free_ char *buf = NULL;
/* Flags used when writing drop-in files or transient unit files */
typedef enum UnitWriteFlags {
/* Write a runtime unit file or drop-in (i.e. one below /run) */
- UNIT_RUNTIME = 1 << 0,
+ UNIT_RUNTIME = 1 << 0,
/* Write a persistent drop-in (i.e. one below /etc) */
- UNIT_PERSISTENT = 1 << 1,
+ UNIT_PERSISTENT = 1 << 1,
/* Place this item in the per-unit-type private section, instead of [Unit] */
- UNIT_PRIVATE = 1 << 2,
+ UNIT_PRIVATE = 1 << 2,
/* Apply specifier escaping before writing */
- UNIT_ESCAPE_SPECIFIERS = 1 << 3,
+ UNIT_ESCAPE_SPECIFIERS = 1 << 3,
+
+ /* Escape elements of ExecStart= syntax before writing */
+ UNIT_ESCAPE_EXEC_SYNTAX = 1 << 4,
/* Apply C escaping before writing */
- UNIT_ESCAPE_C = 1 << 4,
+ UNIT_ESCAPE_C = 1 << 5,
} UnitWriteFlags;
/* Returns true if neither persistent, nor runtime storage is requested, i.e. this is a check invocation only */