diff --git a/src/basic/escape.c b/src/basic/escape.c index 2a3a0e31a1..fcade5a1b4 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -8,6 +8,7 @@ #include "escape.h" #include "hexdecoct.h" #include "macro.h" +#include "strv.h" #include "utf8.h" int cescape_char(char c, char *buf) { @@ -542,3 +543,23 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) { return str_realloc(buf); } + +char* quote_command_line(char **argv) { + _cleanup_free_ char *result = NULL; + + assert(argv); + + char **a; + STRV_FOREACH(a, argv) { + _cleanup_free_ char *t = NULL; + + t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY); + if (!t) + return NULL; + + if (!strextend_with_separator(&result, " ", t)) + return NULL; + } + + return TAKE_PTR(result); +} diff --git a/src/basic/escape.h b/src/basic/escape.h index 907b572bd4..e9d48d227a 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -68,3 +68,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl char* shell_escape(const char *s, const char *bad); char* shell_maybe_quote(const char *s, ShellEscapeFlags flags); +char* quote_command_line(char **argv); diff --git a/src/core/execute.c b/src/core/execute.c index f760d6ae07..0535170abf 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -52,6 +52,7 @@ #include "env-file.h" #include "env-util.h" #include "errno-list.h" +#include "escape.h" #include "execute.h" #include "exit-status.h" #include "fd-util.h" @@ -3638,8 +3639,6 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p return 0; } -static char *exec_command_line(char **argv); - static int exec_parameters_get_cgroup_path(const ExecParameters *params, char **ret) { bool using_subcgroup; char *p; @@ -3842,7 +3841,7 @@ static int exec_child( const char *vc = params->confirm_spawn; _cleanup_free_ char *cmdline = NULL; - cmdline = exec_command_line(command->argv); + cmdline = quote_command_line(command->argv); if (!cmdline) { *exit_status = EXIT_MEMORY; return log_oom(); @@ -4688,12 +4687,15 @@ static int exec_child( if (DEBUG_LOGGING) { _cleanup_free_ char *line = NULL; - line = exec_command_line(final_argv); - if (line) - log_unit_struct(unit, LOG_DEBUG, - "EXECUTABLE=%s", executable, - LOG_UNIT_MESSAGE(unit, "Executing: %s", line), - LOG_UNIT_INVOCATION_ID(unit)); + line = quote_command_line(final_argv); + if (!line) { + *exit_status = EXIT_MEMORY; + return log_oom(); + } + + log_unit_struct(unit, LOG_DEBUG, + "EXECUTABLE=%s", executable, + LOG_UNIT_MESSAGE(unit, "Executing: %s", line)); } if (exec_fd >= 0) { @@ -4777,7 +4779,7 @@ int exec_spawn(Unit *unit, if (r < 0) return log_unit_error_errno(unit, r, "Failed to load environment files: %m"); - line = exec_command_line(command->argv); + line = quote_command_line(command->argv); if (!line) return log_oom(); @@ -5992,46 +5994,6 @@ void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) { prefix, s->status); } -static char *exec_command_line(char **argv) { - size_t k; - char *n, *p, **a; - bool first = true; - - assert(argv); - - k = 1; - STRV_FOREACH(a, argv) - k += strlen(*a)+3; - - n = new(char, k); - if (!n) - return NULL; - - p = n; - STRV_FOREACH(a, argv) { - - if (!first) - *(p++) = ' '; - else - first = false; - - if (strpbrk(*a, WHITESPACE)) { - *(p++) = '\''; - p = stpcpy(p, *a); - *(p++) = '\''; - } else - p = stpcpy(p, *a); - - } - - *p = 0; - - /* FIXME: this doesn't really handle arguments that have - * spaces and ticks in them */ - - return n; -} - static void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { _cleanup_free_ char *cmd = NULL; const char *prefix2; @@ -6042,7 +6004,7 @@ static void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { prefix = strempty(prefix); prefix2 = strjoina(prefix, "\t"); - cmd = exec_command_line(c->argv); + cmd = quote_command_line(c->argv); fprintf(f, "%sCommand Line: %s\n", prefix, cmd ? cmd : strerror_safe(ENOMEM)); diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index b11ebb3f65..1159af46cd 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -37,11 +37,11 @@ int bus_container_connect_socket(sd_bus *b) { r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); if (r < 0) - return r; + return log_debug_errno(r, "Failed to open namespace of PID "PID_FMT": %m", b->nspid); b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (b->input_fd < 0) - return -errno; + return log_debug_errno(errno, "Failed to create a socket: %m"); b->input_fd = fd_move_above_stdio(b->input_fd); @@ -50,12 +50,12 @@ int bus_container_connect_socket(sd_bus *b) { bus_socket_setup(b); if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) - return -errno; + return log_debug_errno(errno, "Failed to create a socket pair: %m"); r = namespace_fork("(sd-buscntrns)", "(sd-buscntr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child); if (r < 0) - return r; + return log_debug_errno(r, "Failed to create namespace for (sd-buscntr): %m"); if (r == 0) { pair[0] = safe_close(pair[0]); @@ -80,20 +80,22 @@ int bus_container_connect_socket(sd_bus *b) { n = read(pair[0], &error_buf, sizeof(error_buf)); if (n < 0) - return -errno; + return log_debug_errno(errno, "Failed to read error status from (sd-buscntr): %m"); if (n > 0) { if (n != sizeof(error_buf)) - return -EIO; + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Read error status of unexpected length %zd from (sd-buscntr): %m", n); if (error_buf < 0) - return -EIO; + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Got unexpected error status from (sd-buscntr): %m"); if (error_buf == EINPROGRESS) return 1; if (error_buf > 0) - return -error_buf; + return log_debug_errno(error_buf, "Got error from (sd-buscntr): %m"); } return bus_socket_start_auth(b); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 378774fe8b..b408d657a5 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -12,6 +12,7 @@ #include "bus-internal.h" #include "bus-message.h" #include "bus-socket.h" +#include "escape.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" @@ -962,8 +963,17 @@ int bus_socket_exec(sd_bus *b) { assert(b->exec_path); assert(b->busexec_pid == 0); - log_debug("sd-bus: starting bus%s%s with %s...", - b->description ? " " : "", strempty(b->description), b->exec_path); + if (DEBUG_LOGGING) { + _cleanup_free_ char *line = NULL; + + if (b->exec_argv) + line = quote_command_line(b->exec_argv); + + log_debug("sd-bus: starting bus%s%s with %s%s", + b->description ? " " : "", strempty(b->description), + line ?: b->exec_path, + b->exec_argv && !line ? "…" : ""); + } r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s); if (r < 0) @@ -984,10 +994,8 @@ int bus_socket_exec(sd_bus *b) { if (b->exec_argv) execvp(b->exec_path, b->exec_argv); - else { - const char *argv[] = { b->exec_path, NULL }; - execvp(b->exec_path, (char**) argv); - } + else + execvp(b->exec_path, STRV_MAKE(b->exec_path)); _exit(EXIT_FAILURE); } diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index a32e2f5e20..6960161c36 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -39,6 +39,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" @@ -1617,7 +1618,7 @@ static int user_and_machine_valid(const char *user_and_machine) { if (!user) return -ENOMEM; - if (!isempty(user) && !valid_user_group_name(user, VALID_USER_RELAX)) + if (!isempty(user) && !valid_user_group_name(user, VALID_USER_RELAX | VALID_USER_ALLOW_NUMERIC)) return false; h++; @@ -1648,17 +1649,25 @@ static int user_and_machine_equivalent(const char *user_and_machine) { /* Otherwise, if we are root, then we can also allow the ".host" syntax, as that's the user this * would connect to. */ - if (geteuid() == 0 && STR_IN_SET(user_and_machine, ".host", "root@.host")) + uid_t uid = geteuid(); + + if (uid == 0 && STR_IN_SET(user_and_machine, ".host", "root@.host", "0@.host")) return true; - /* Otherwise, we have to figure our user name, and compare things with that. */ - un = getusername_malloc(); - if (!un) - return -ENOMEM; + /* Otherwise, we have to figure out our user id and name, and compare things with that. */ + char buf[DECIMAL_STR_MAX(uid_t)]; + xsprintf(buf, UID_FMT, uid); - f = startswith(user_and_machine, un); - if (!f) - return false; + f = startswith(user_and_machine, buf); + if (!f) { + un = getusername_malloc(); + if (!un) + return -ENOMEM; + + f = startswith(user_and_machine, un); + if (!f) + return false; + } return STR_IN_SET(f, "@", "@.host"); } diff --git a/src/test/test-escape.c b/src/test/test-escape.c index 991b135a33..413e8cd860 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -8,6 +8,8 @@ static void test_cescape(void) { _cleanup_free_ char *t; + log_info("/* %s */", __func__); + assert_se(t = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313")); assert_se(streq(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313")); } @@ -15,6 +17,8 @@ static void test_cescape(void) { static void test_xescape(void) { _cleanup_free_ char *t; + log_info("/* %s */", __func__); + assert_se(t = xescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "")); assert_se(streq(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb")); } @@ -26,6 +30,8 @@ static void test_xescape_full(bool eight_bits) { const unsigned full_fit = !eight_bits ? 55 : 46; XEscapeFlags flags = eight_bits * XESCAPE_8_BIT; + log_info("/* %s */", __func__); + for (unsigned i = 0; i < 60; i++) { _cleanup_free_ char *t, *q; @@ -60,6 +66,8 @@ static void test_xescape_full(bool eight_bits) { static void test_cunescape(void) { _cleanup_free_ char *unescaped; + log_info("/* %s */", __func__); + assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0); assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00")); @@ -133,6 +141,8 @@ static void test_shell_escape_one(const char *s, const char *bad, const char *ex } static void test_shell_escape(void) { + log_info("/* %s */", __func__); + test_shell_escape_one("", "", ""); test_shell_escape_one("\\", "", "\\\\"); test_shell_escape_one("foobar", "", "foobar"); @@ -150,6 +160,7 @@ static void test_shell_maybe_quote_one(const char *s, ShellEscapeFlags flags, co } static void test_shell_maybe_quote(void) { + log_info("/* %s */", __func__); test_shell_maybe_quote_one("", 0, ""); test_shell_maybe_quote_one("", SHELL_ESCAPE_EMPTY, "\"\""); @@ -192,6 +203,29 @@ static void test_shell_maybe_quote(void) { test_shell_maybe_quote_one("głąb\002\003rząd", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003rząd'"); } +static void test_quote_command_line_one(char **argv, const char *expected) { + _cleanup_free_ char *s; + + assert_se(s = quote_command_line(argv)); + log_info("%s", s); + assert_se(streq(s, expected)); +} + +static void test_quote_command_line(void) { + log_info("/* %s */", __func__); + + test_quote_command_line_one(STRV_MAKE("true", "true"), + "true true"); + test_quote_command_line_one(STRV_MAKE("true", "with a space"), + "true \"with a space\""); + test_quote_command_line_one(STRV_MAKE("true", "with a 'quote'"), + "true \"with a 'quote'\""); + test_quote_command_line_one(STRV_MAKE("true", "with a \"quote\""), + "true \"with a \\\"quote\\\"\""); + test_quote_command_line_one(STRV_MAKE("true", "$dollar"), + "true \"\\$dollar\""); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -202,6 +236,7 @@ int main(int argc, char *argv[]) { test_cunescape(); test_shell_escape(); test_shell_maybe_quote(); + test_quote_command_line(); return 0; }