]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/unit: add UNIT_ESCAPE_EXEC_SYNTAX
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 3 Apr 2023 12:45:46 +0000 (14:45 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 24 Apr 2023 08:02:30 +0000 (10:02 +0200)
Unfortunately we can't escape $ when ':' is used to prohibit variable expansion:
  ExecStart=:echo $$
is not the same as
  ExecStart=:echo $

This just adds the functionality and the unittests, without using it anywhere
for real yet.

src/core/unit.c
src/core/unit.h
src/test/test-core-unit.c

index a6a0328e4ddde36392b82c2ba45c1f3d5bb29de5..6e20e86a6373650899d100682f528b97f822f995 100644 (file)
@@ -36,6 +36,7 @@
 #include "load-dropin.h"
 #include "load-fragment.h"
 #include "log.h"
+#include "logarithm.h"
 #include "macro.h"
 #include "missing_audit.h"
 #include "mkdir-label.h"
@@ -4312,7 +4313,7 @@ static const char* unit_drop_in_dir(Unit *u, UnitWriteFlags flags) {
 
 const char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) {
         assert(s);
-        assert(!FLAGS_SET(flags, UNIT_ESCAPE_EXEC_SYNTAX_ENV | UNIT_ESCAPE_C));
+        assert(popcount(flags & (UNIT_ESCAPE_EXEC_SYNTAX_ENV | UNIT_ESCAPE_EXEC_SYNTAX | UNIT_ESCAPE_C)) <= 1);
         assert(buf);
 
         _cleanup_free_ char *t = NULL;
@@ -4334,15 +4335,17 @@ const char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf)
         /* We either do C-escaping or shell-escaping, to additionally escape characters that we parse for
          * ExecStart= and friends, i.e. '$' and quotes. */
 
-        if (flags & UNIT_ESCAPE_EXEC_SYNTAX_ENV) {
+        if (flags & (UNIT_ESCAPE_EXEC_SYNTAX_ENV | UNIT_ESCAPE_EXEC_SYNTAX)) {
                 char *t2;
 
-                t2 = strreplace(s, "$", "$$");
-                if (!t2)
-                        return NULL;
-                free_and_replace(t, t2);
+                if (flags & UNIT_ESCAPE_EXEC_SYNTAX_ENV) {
+                        t2 = strreplace(s, "$", "$$");
+                        if (!t2)
+                                return NULL;
+                        free_and_replace(t, t2);
+                }
 
-                t2 = shell_escape(t, "\"");
+                t2 = shell_escape(t ?: s, "\"");
                 if (!t2)
                         return NULL;
                 free_and_replace(t, t2);
index ff29b785bbe80490c5e777f6ca106315d2f73bac..06e5237e2c50ba315bb463561399fc482f525656 100644 (file)
@@ -542,8 +542,11 @@ typedef enum UnitWriteFlags {
         /* Escape elements of ExecStart= syntax, incl. prevention of variable expansion */
         UNIT_ESCAPE_EXEC_SYNTAX_ENV = 1 << 4,
 
+        /* Escape elements of ExecStart=: syntax (no variable expansion) */
+        UNIT_ESCAPE_EXEC_SYNTAX     = 1 << 5,
+
         /* Apply C escaping before writing */
-        UNIT_ESCAPE_C               = 1 << 5,
+        UNIT_ESCAPE_C               = 1 << 6,
 } UnitWriteFlags;
 
 /* Returns true if neither persistent, nor runtime storage is requested, i.e. this is a check invocation only */
index 91e6cdd6a3687f57d5ef504cbf990ca220e4c725..1a08507d1d6ba4b1a0890d6d62123ffb52bb914a 100644 (file)
@@ -7,15 +7,18 @@
 
 static void test_unit_escape_setting_one(
                 const char *s,
+                const char *expected_exec_env,
                 const char *expected_exec,
                 const char *expected_c) {
 
-        _cleanup_free_ char *a = NULL, *b, *c,
-                *s_esc, *a_esc, *b_esc, *c_esc;
+        _cleanup_free_ char *a = NULL, *b, *c, *d,
+                *s_esc, *a_esc, *b_esc, *c_esc, *d_esc;
         const char *t;
 
+        if (!expected_exec_env)
+                expected_exec_env = s;
         if (!expected_exec)
-                expected_exec = s;
+                expected_exec = expected_exec_env;
         if (!expected_c)
                 expected_c = expected_exec;
         assert_se(s_esc = cescape(s));
@@ -30,37 +33,46 @@ static void test_unit_escape_setting_one(
         assert_se(b_esc = cescape(t));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
         assert_se(b == NULL || streq(b, t));
-        assert_se(streq(t, expected_exec));
+        assert_se(streq(t, expected_exec_env));
 
-        assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_C, &c));
+        assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX, &c));
         assert_se(c_esc = cescape(t));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
         assert_se(c == NULL || streq(c, t));
+        assert_se(streq(t, expected_exec));
+
+        assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_C, &d));
+        assert_se(d_esc = cescape(t));
+        log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
+        assert_se(d == NULL || streq(d, t));
         assert_se(streq(t, expected_c));
 }
 
 TEST(unit_escape_setting) {
-        test_unit_escape_setting_one("/sbin/sbash", NULL, NULL);
-        test_unit_escape_setting_one("$", "$$", "$");
-        test_unit_escape_setting_one("$$", "$$$$", "$$");
-        test_unit_escape_setting_one("'", "'", "\\'");
-        test_unit_escape_setting_one("\"", "\\\"", NULL);
-        test_unit_escape_setting_one("\t", "\\t", NULL);
-        test_unit_escape_setting_one(" ", NULL, NULL);
-        test_unit_escape_setting_one("$;'\"\t\n", "$$;'\\\"\\t\\n", "$;\\'\\\"\\t\\n");
+        test_unit_escape_setting_one("/sbin/sbash", NULL, NULL, NULL);
+        test_unit_escape_setting_one("$", "$$", "$", "$");
+        test_unit_escape_setting_one("$$", "$$$$", "$$", "$$");
+        test_unit_escape_setting_one("'", "'", NULL, "\\'");
+        test_unit_escape_setting_one("\"", "\\\"", NULL, NULL);
+        test_unit_escape_setting_one("\t", "\\t", NULL, NULL);
+        test_unit_escape_setting_one(" ", NULL, NULL, NULL);
+        test_unit_escape_setting_one("$;'\"\t\n", "$$;'\\\"\\t\\n", "$;'\\\"\\t\\n", "$;\\'\\\"\\t\\n");
 }
 
 static void test_unit_concat_strv_one(
                 char **s,
                 const char *expected_none,
+                const char *expected_exec_env,
                 const char *expected_exec,
                 const char *expected_c) {
 
-        _cleanup_free_ char *a, *b, *c,
-                *s_ser, *s_esc, *a_esc, *b_esc, *c_esc;
+        _cleanup_free_ char *a, *b, *c, *d,
+                *s_ser, *s_esc, *a_esc, *b_esc, *c_esc, *d_esc;
 
         assert_se(s_ser = strv_join(s, "_"));
         assert_se(s_esc = cescape(s_ser));
+        if (!expected_exec_env)
+                expected_exec_env = expected_none;
         if (!expected_exec)
                 expected_exec = expected_none;
         if (!expected_c)
@@ -74,26 +86,34 @@ static void test_unit_concat_strv_one(
         assert_se(b = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV));
         assert_se(b_esc = cescape(b));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
-        assert_se(streq(b, expected_exec));
+        assert_se(streq(b, expected_exec_env));
 
-        assert_se(c = unit_concat_strv(s, UNIT_ESCAPE_C));
+        assert_se(c = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX));
         assert_se(c_esc = cescape(c));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
-        assert_se(streq(c, expected_c));
+        assert_se(streq(c, expected_exec));
+
+        assert_se(d = unit_concat_strv(s, UNIT_ESCAPE_C));
+        assert_se(d_esc = cescape(d));
+        log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
+        assert_se(streq(d, expected_c));
 }
 
 TEST(unit_concat_strv) {
         test_unit_concat_strv_one(STRV_MAKE("a", "b", "c"),
                                   "\"a\" \"b\" \"c\"",
                                   NULL,
+                                  NULL,
                                   NULL);
         test_unit_concat_strv_one(STRV_MAKE("a", " ", "$", "$$", ""),
                                   "\"a\" \" \" \"$\" \"$$\" \"\"",
                                   "\"a\" \" \" \"$$\" \"$$$$\" \"\"",
+                                  NULL,
                                   NULL);
         test_unit_concat_strv_one(STRV_MAKE("\n", " ", "\t"),
                                   "\"\n\" \" \" \"\t\"",
                                   "\"\\n\" \" \" \"\\t\"",
+                                  "\"\\n\" \" \" \"\\t\"",
                                   "\"\\n\" \" \" \"\\t\"");
 }