From 7f4a0ba8cbeaa739d2ce4f1fc13731d2f3a5c605 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 26 Apr 2024 17:06:14 +0200 Subject: [PATCH] conf: handle array of arguments in context.exec We handeled "arg1 arg2 ... " before and used to split between spaces to get the arguments for execvp but that doesn't work so well when there are arguments with spaces. Instead use JSON parsing to get the array of arguments. This make it possible to use [ arg1 arg2 .. ] and quote each arg separately. You can still use the old method and even double escape: "\"arg1\" \"arg2 with spaces\"" or [ "arg1" "arg2 with spaces" ] --- doc/dox/config/pipewire.conf.5.md | 2 +- src/daemon/minimal.conf.in | 2 +- src/daemon/pipewire.conf.in | 4 +- src/pipewire/conf.c | 61 ++++++++++++++++++++++++------- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/doc/dox/config/pipewire.conf.5.md b/doc/dox/config/pipewire.conf.5.md index d382d2f78..b29c2b249 100644 --- a/doc/dox/config/pipewire.conf.5.md +++ b/doc/dox/config/pipewire.conf.5.md @@ -413,7 +413,7 @@ part of the initialization of the PipeWire program. ```json context.exec = [ #{ path = - # ( args = "" ) + # ( args = "" | [ ... ] ) # ( condition = [ { = ... } ... ] ) #} ] diff --git a/src/daemon/minimal.conf.in b/src/daemon/minimal.conf.in index 15ef3dbb1..031402c79 100644 --- a/src/daemon/minimal.conf.in +++ b/src/daemon/minimal.conf.in @@ -426,7 +426,7 @@ context.objects = [ context.exec = [ #{ path = - # ( args = "" ) + # ( args = "" | [ ... ] ) # ( condition = [ { = ... } ... ] ) #} # diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index aad8063aa..71145133c 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -319,7 +319,7 @@ context.objects = [ context.exec = [ #{ path = - # ( args = "" ) + # ( args = "" | [ ... ] ) # ( condition = [ { = ... } ... ] ) #} # @@ -339,6 +339,6 @@ context.exec = [ # It can be interesting to start another daemon here that listens # on another address with the -a option (eg. -a tcp:4713). # - @pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf" + @pulse_comment@{ path = "@pipewire_path@" args = [ "-c" "pipewire-pulse.conf" ] @pulse_comment@ condition = [ { exec.pipewire-pulse = null } { exec.pipewire-pulse = true } ] } ] diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index 541b0fdc6..c77dda763 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -927,38 +927,69 @@ static int parse_objects(void *user_data, const char *location, return res; } -static int do_exec(struct pw_context *context, const char *key, const char *args) +static char **pw_strv_insert_at(char **strv, int len, int pos, const char *str) { - int pid, res, n_args; + char **n; + + if (len < 0) { + len = 0; + while (strv != NULL && strv[len] != NULL) + len++; + } + if (pos < 0 || pos > len) + pos = len+1; + + n = realloc(strv, sizeof(char*) * (len + 2)); + if (n == NULL) { + free(strv); + return NULL; + } + strv = n; + + memmove(strv+pos+1, strv+pos, sizeof(char*) * (len+1-pos)); + strv[pos] = strdup(str); + return strv; +} + +static int do_exec(struct pw_context *context, const char *path, const char *args) +{ + int pid, res; pid = fork(); if (pid == 0) { - char *cmd, **argv; + char **arg; + int n_args; /* Double fork to avoid zombies; we don't want to set SIGCHLD handler */ pid = fork(); if (pid < 0) { pw_log_error("fork error: %m"); - exit(1); + goto done; } else if (pid != 0) { exit(0); } - cmd = spa_aprintf("%s %s", key, args ? args : ""); - argv = pw_split_strv(cmd, " \t", INT_MAX, &n_args); - free(cmd); - - pw_log_info("exec %s '%s'", key, args); - res = execvp(key, argv); - pw_free_strv(argv); + arg = pw_strv_parse(args, strlen(args), INT_MAX, &n_args); + if (arg == NULL) { + pw_log_error("error parsing arguments: %m"); + goto done; + } + arg = pw_strv_insert_at(arg, n_args, 0, path); + if (arg == NULL) { + pw_log_error("error constructing arguments: %m"); + goto done; + } + pw_log_info("exec %s '%s'", path, args); + res = execvp(path, arg); + pw_free_strv(arg); if (res == -1) { res = -errno; - pw_log_error("execvp error '%s': %m", key); + pw_log_error("execvp error '%s': %m", path); } - +done: exit(1); } else if (pid < 0) { pw_log_error("fork error: %m"); @@ -976,7 +1007,7 @@ static int do_exec(struct pw_context *context, const char *key, const char *args /* * context.exec = [ * { path = - * ( args = "" ) + * ( args = "" | [ ... ] ) * ( condition = [ { key = value, .. } .. ] ) * } * ] @@ -1016,6 +1047,8 @@ static int parse_exec(void *user_data, const char *location, path = (char*)val; spa_json_parse_stringn(val, l, path, l+1); } else if (spa_streq(key, "args")) { + if (spa_json_is_container(val, l)) + l = spa_json_container_len(&it[2], val, l); args = (char*)val; spa_json_parse_stringn(val, l, args, l+1); } else if (spa_streq(key, "condition")) {