<para>For each of the specified
                                 commands, the first argument must be
-                                an absolute and literal path to an
-                                executable. Optionally, if the
-                                absolute file name is prefixed with
-                                <literal>@</literal>, the second token
-                                will be passed as
+                                an absolute path to an executable.
+                                Optionally, if this file name is
+                                prefixed with <literal>@</literal>,
+                                the second token will be passed as
                                 <literal>argv[0]</literal> to the
                                 executed process, followed by the
                                 further arguments specified. If the
 
                 <para>Each command line is split on whitespace, with
                 the first item being the command to execute, and the
-                subsequent items being the arguments.  Double quotes
+                subsequent items being the arguments. Double quotes
                 ("...") and single quotes ('...') may be used, in
                 which case everything until the next matching quote
-                becomes part of the same argument. Quotes themselves
-                are removed after parsing. In addition, a trailing
-                backslash (<literal>\</literal>) may be used to merge
-                lines. </para>
+                becomes part of the same argument. C-style escapes are
+                also supported, see table below. Quotes themselves are
+                removed after parsing and escape sequences
+                substituted. In addition, a trailing backslash
+                (<literal>\</literal>) may be used to merge lines.
+                </para>
 
                 <para>This syntax is intended to be very similar to
                 shell syntax, but only the meta-characters and
                 <emphasis>other elements of shell syntax are not
                 supported</emphasis>.</para>
 
+                <para>The command to execute must an absolute path
+                name. It may contain spaces, but control characters
+                are not allowed.</para>
+
                 <para>The command line accepts <literal>%</literal>
                 specifiers as described in
                 <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
                 <literal>>/dev/null</literal>,
                 <literal>&</literal>, <literal>;</literal>, and
                 <literal>/bin/ls</literal>.</para>
+
+                <table>
+                        <title>C escapes supported in command lines and environment variables</title>
+                        <tgroup cols='2'>
+                                <colspec colname='escape' />
+                                <colspec colname='meaning' />
+                                <thead>
+                                        <row>
+                                                <entry>Literal</entry>
+                                                <entry>Actual value</entry>
+                                        </row>
+                                </thead>
+                                <tbody>
+                                        <row>
+                                                <entry><literal>\a</literal></entry>
+                                                <entry>bell</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\b</literal></entry>
+                                                <entry>backspace</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\f</literal></entry>
+                                                <entry>form feed</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\n</literal></entry>
+                                                <entry>newline</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\r</literal></entry>
+                                                <entry>carriage return</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\t</literal></entry>
+                                                <entry>tab</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\v</literal></entry>
+                                                <entry>vertical tab</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\\</literal></entry>
+                                                <entry>backslash</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\"</literal></entry>
+                                                <entry>double quotation mark</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\'</literal></entry>
+                                                <entry>single quotation mark</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\s</literal></entry>
+                                                <entry>space</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\x<replaceable>xx</replaceable></literal></entry>
+                                                <entry>character number <replaceable>xx</replaceable> in hexadecimal encoding</entry>
+                                        </row>
+                                        <row>
+                                                <entry><literal>\<replaceable>nnn</replaceable></literal></entry>
+                                                <entry>character number <replaceable>nnn</replaceable> in octal encoding</entry>
+                                        </row>
+                                </tbody>
+                        </tgroup>
+                </table>
         </refsect1>
 
         <refsect1>
 
          * overriding of argv[0]. */
         for (;;) {
                 int i;
-                const char *word, *state;
+                const char *word, *state, *reason;
                 size_t l;
-                bool honour_argv0 = false, ignore = false;
+                bool separate_argv0 = false, ignore = false;
 
                 path = NULL;
                 nce = NULL;
                 if (rvalue[0] == 0)
                         break;
 
-                for (i = 0; i < 2; i++) {
-                        if (rvalue[0] == '-' && !ignore) {
-                                ignore = true;
-                                rvalue ++;
-                        }
-
-                        if (rvalue[0] == '@' && !honour_argv0) {
-                                honour_argv0 = true;
-                                rvalue ++;
-                        }
-                }
-
-                if (*rvalue != '/') {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Executable path is not absolute, ignoring: %s", rvalue);
-                        return 0;
-                }
-
                 k = 0;
                 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
-                        if (strneq(word, ";", MAX(l, 1U)))
-                                goto found;
+                        if (k == 0) {
+                                for (i = 0; i < 2; i++) {
+                                        if (*word == '-' && !ignore) {
+                                                ignore = true;
+                                                word ++;
+                                        }
+
+                                        if (*word == '@' && !separate_argv0) {
+                                                separate_argv0 = true;
+                                                word ++;
+                                        }
+                                }
+                        } else
+                                if (strneq(word, ";", MAX(l, 1U)))
+                                        goto found;
 
                         k++;
                 }
                 }
 
         found:
-                n = new(char*, k + !honour_argv0);
+                n = new(char*, k + !separate_argv0);
                 if (!n)
                         return log_oom();
 
                 k = 0;
                 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
-                        if (strneq(word, ";", MAX(l, 1U)))
-                                break;
-                        else if (strneq(word, "\\;", MAX(l, 1U))) {
-                                word ++;
-                                l --;
-                        }
+                        char *c;
+                        unsigned skip;
 
-                        if (honour_argv0 && word == rvalue) {
-                                assert(!path);
+                        if (separate_argv0 ? path == NULL : k == 0) {
+                                /* first word, very special */
+                                skip = separate_argv0 + ignore;
 
-                                path = strndup(word, l);
-                                if (!path) {
-                                        r = log_oom();
-                                        goto fail;
-                                }
+                                /* skip special chars in the beginning */
+                                assert(skip < l);
 
-                                if (!utf8_is_valid(path)) {
-                                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
-                                        r = 0;
-                                        goto fail;
-                                }
+                        } else if (strneq(word, ";", MAX(l, 1U)))
+                                /* new commandline */
+                                break;
 
-                        } else {
-                                char *c;
+                        else
+                                skip = strneq(word, "\\;", MAX(l, 1U));
 
-                                c = n[k++] = cunescape_length(word, l);
-                                if (!c) {
-                                        r = log_oom();
-                                        goto fail;
-                                }
+                        c = cunescape_length(word + skip, l - skip);
+                        if (!c) {
+                                r = log_oom();
+                                goto fail;
+                        }
 
-                                if (!utf8_is_valid(c)) {
-                                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
-                                        r = 0;
-                                        goto fail;
-                                }
+                        if (!utf8_is_valid(c)) {
+                                log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+                                r = 0;
+                                goto fail;
                         }
+
+                        /* where to stuff this? */
+                        if (separate_argv0 && path == NULL)
+                                path = c;
+                        else
+                                n[k++] = c;
                 }
 
                 n[k] = NULL;
 
-                if (!n[0]) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Invalid command line, ignoring: %s", rvalue);
-                        r = 0;
-                        goto fail;
-                }
+                log_debug("path: %s", path ?: n[0]);
+
+                if (!n[0])
+                        reason = "Empty executable name or zeroeth argument";
+                else if (!string_is_safe(path ?: n[0]))
+                        reason = "Executable path contains special characters";
+                else if (!path_is_absolute(path ?: n[0]))
+                        reason = "Executable path is not absolute";
+                else if (endswith(path ?: n[0], "/"))
+                        reason = "Executable path specifies a directory";
+                else
+                        goto ok;
+
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "%s, ignoring: %s", reason, rvalue);
+                r = 0;
+                goto fail;
 
+ok:
                 if (!path) {
                         path = strdup(n[0]);
                         if (!path) {
                         }
                 }
 
-                assert(path_is_absolute(path));
-
                 nce = new0(ExecCommand, 1);
                 if (!nce) {
                         r = log_oom();
 
                               const char* argv2,
                               bool ignore) {
         assert_se(c);
-        log_info("%s %s %s %s",
+        log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
+                 path, argv0 ?: path, argv1, argv2);
+        log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
                  c->path, c->argv[0], c->argv[1], c->argv[2]);
         assert_se(streq(c->path, path));
-        assert_se(streq(c->argv[0], argv0));
-        assert_se(streq(c->argv[1], argv1));
+        assert_se(streq(c->argv[0], argv0 ?: path));
+        assert_se(streq_ptr(c->argv[1], argv1));
         assert_se(streq_ptr(c->argv[2], argv2));
         assert_se(c->ignore == ignore);
 }
 
 static void test_config_parse_exec(void) {
-        /* int config_parse_exec( */
-        /*         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_exec(
+                 const char *filename,
+                 unsigned line,
+                 const char *section,
+                 unsigned section_line,
+                 const char *lvalue,
+                 int ltype,
+                 const char *rvalue,
+                 void *data,
+                 void *userdata) */
         int r;
 
         ExecCommand *c = NULL, *c1;
+        const char *ccc;
 
-        /* basic test */
+        log_info("/* basic test */");
         r = config_parse_exec(NULL, "fake", 1, "section", 1,
                               "LValue", 0, "/RValue r1",
                               &c, NULL);
         check_execcommand(c, "/RValue", "/RValue", "r1", NULL, false);
 
         r = config_parse_exec(NULL, "fake", 2, "section", 1,
-                              "LValue", 0, "/RValue///slashes/// r1",
+                              "LValue", 0, "/RValue///slashes r1///",
                               &c, NULL);
-       /* test slashes */
+
+        log_info("/* test slashes */");
         assert_se(r >= 0);
         c1 = c->command_next;
-        check_execcommand(c1, "/RValue/slashes", "/RValue///slashes///", "r1", NULL, false);
+        check_execcommand(c1, "/RValue/slashes", "/RValue///slashes", "r1///", NULL, false);
 
-        /* honour_argv0 */
+        log_info("/* trailing slash */");
+        r = config_parse_exec(NULL, "fake", 4, "section", 1,
+                              "LValue", 0, "/RValue/ argv0 r1",
+                              &c, NULL);
+        assert_se(r == 0);
+        assert_se(c1->command_next == NULL);
+
+        log_info("/* honour_argv0 */");
         r = config_parse_exec(NULL, "fake", 3, "section", 1,
-                              "LValue", 0, "@/RValue///slashes2/// argv0 r1",
+                              "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
                               &c, NULL);
         assert_se(r >= 0);
         c1 = c1->command_next;
-        check_execcommand(c1, "/RValue/slashes2", "argv0", "r1", NULL, false);
+        check_execcommand(c1, "/RValue/slashes2", "///argv0", "r1", NULL, false);
 
-        /* ignore && honour_argv0 */
+        log_info("/* ignore && honour_argv0 */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
-                              "LValue", 0, "-@/RValue///slashes3/// argv0a r1",
+                              "LValue", 0, "-@/RValue///slashes3 argv0a r1",
                               &c, NULL);
         assert_se(r >= 0);
         c1 = c1->command_next;
         check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);
 
-        /* ignore && honour_argv0 */
+        log_info("/* ignore && honour_argv0 */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
-                              "LValue", 0, "@-/RValue///slashes4/// argv0b r1",
+                              "LValue", 0, "@-/RValue///slashes4 argv0b r1",
                               &c, NULL);
         assert_se(r >= 0);
         c1 = c1->command_next;
         check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);
 
-        /* ignore && ignore */
+        log_info("/* ignore && ignore */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "--/RValue argv0 r1",
                               &c, NULL);
         assert_se(r == 0);
         assert_se(c1->command_next == NULL);
 
-        /* ignore && ignore */
+        log_info("/* ignore && ignore (2) */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "-@-/RValue argv0 r1",
                               &c, NULL);
         assert_se(r == 0);
         assert_se(c1->command_next == NULL);
 
-        /* semicolon */
+        log_info("/* semicolon */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
                               "LValue", 0,
                               "-@/RValue argv0 r1 ; "
         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
 
         c1 = c1->command_next;
-        check_execcommand(c1, "/goo/goo", "/goo/goo", "boo", NULL, false);
+        check_execcommand(c1, "/goo/goo", NULL, "boo", NULL, false);
 
-        /* trailing semicolon */
+        log_info("/* trailing semicolon */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
                               "LValue", 0,
                               "-@/RValue argv0 r1 ; ",
 
         assert_se(c1->command_next == NULL);
 
-        /* escaped semicolon */
+        log_info("/* escaped semicolon */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
                               "LValue", 0,
                               "/bin/find \\;",
                               &c, NULL);
         assert_se(r >= 0);
         c1 = c1->command_next;
-        check_execcommand(c1, "/bin/find", "/bin/find", ";", NULL, false);
+        check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
 
-        /* escaped semicolon with following arg */
+        log_info("/* escaped semicolon with following arg */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
                               "LValue", 0,
                               "/sbin/find \\; x",
         assert_se(r >= 0);
         c1 = c1->command_next;
         check_execcommand(c1,
-                          "/sbin/find", "/sbin/find", ";", "x", false);
+                          "/sbin/find", NULL, ";", "x", false);
+
+        log_info("/* spaces in the filename */");
+        r = config_parse_exec(NULL, "fake", 5, "section", 1,
+                              "LValue", 0,
+                              "\"/PATH WITH SPACES/daemon\" -1 -2",
+                              &c, NULL);
+        assert_se(r >= 0);
+        c1 = c1->command_next;
+        check_execcommand(c1,
+                          "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
+
+        log_info("/* spaces in the filename, no args */");
+        r = config_parse_exec(NULL, "fake", 5, "section", 1,
+                              "LValue", 0,
+                              "\"/PATH WITH SPACES/daemon -1 -2\"",
+                              &c, NULL);
+        assert_se(r >= 0);
+        c1 = c1->command_next;
+        check_execcommand(c1,
+                          "/PATH WITH SPACES/daemon -1 -2", NULL, NULL, NULL, false);
+
+        log_info("/* spaces in the filename, everything quoted */");
+        r = config_parse_exec(NULL, "fake", 5, "section", 1,
+                              "LValue", 0,
+                              "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
+                              &c, NULL);
+        assert_se(r >= 0);
+        c1 = c1->command_next;
+        check_execcommand(c1,
+                          "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
+
+        log_info("/* escaped spaces in the filename */");
+        r = config_parse_exec(NULL, "fake", 5, "section", 1,
+                              "LValue", 0,
+                              "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
+                              &c, NULL);
+        assert_se(r >= 0);
+        c1 = c1->command_next;
+        check_execcommand(c1,
+                          "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
+
+        log_info("/* escaped spaces in the filename (2) */");
+        r = config_parse_exec(NULL, "fake", 5, "section", 1,
+                              "LValue", 0,
+                              "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
+                              &c, NULL);
+        assert_se(r >= 0);
+        c1 = c1->command_next;
+        check_execcommand(c1,
+                          "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
+
+        for (ccc = "abfnrtv\\\'\"x"; *ccc; ccc++) {
+                /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
+                char path[] = "/path\\X";
+                path[sizeof(path) - 2] = *ccc;
+
+                log_info("/* invalid character: \\%c */", *ccc);
+                r = config_parse_exec(NULL, "fake", 4, "section", 1,
+                                      "LValue", 0, path,
+                                      &c, NULL);
+                assert_se(r == 0);
+                assert_se(c1->command_next == NULL);
+        }
+
+        log_info("/* valid character: \\s */");
+        r = config_parse_exec(NULL, "fake", 4, "section", 1,
+                              "LValue", 0, "/path\\s",
+                              &c, NULL);
+        assert_se(r >= 0);
+        c1 = c1->command_next;
+        check_execcommand(c1, "/path ", NULL, NULL, NULL, false);
+
+        log_info("/* trailing backslash: \\ */");
+        /* backslash is invalid */
+        r = config_parse_exec(NULL, "fake", 4, "section", 1,
+                              "LValue", 0, "/path\\",
+                              &c, NULL);
+        assert_se(r == 0);
+        assert_se(c1->command_next == NULL);
 
         exec_command_free_list(c);
 }