From 9243ed0cbda3ad1afa41ed11f65e457928bc93f1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 22 Oct 2024 18:34:59 +0200 Subject: [PATCH] pulse-server: add condition support in pulse.cmd So that a config override can disable the execution of the command by setting the property to false in the pulse.properties config override. Expose some conf.c method for this purpose. --- src/daemon/pipewire-pulse.conf.in | 14 +++++++++---- src/modules/module-protocol-pulse.c | 6 ++++-- src/modules/module-protocol-pulse/cmd.c | 28 ++++++++++++++++++------- src/pipewire/conf.c | 11 +++++----- src/pipewire/conf.h | 4 ++++ 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in index 9103f21c1..affb993dd 100644 --- a/src/daemon/pipewire-pulse.conf.in +++ b/src/daemon/pipewire-pulse.conf.in @@ -60,11 +60,17 @@ context.exec = [ # load-module : loads a module with args and flags # args = " " # ( flags = [ nofail ] ) +# ( condition = [ { = , ... } ... ] ) +# conditions will check the pulse.properties key/values. pulse.cmd = [ - { cmd = "load-module" args = "module-always-sink" flags = [ ] } - { cmd = "load-module" args = "module-device-manager" flags = [ ] } - { cmd = "load-module" args = "module-device-restore" flags = [ ] } - { cmd = "load-module" args = "module-stream-restore" flags = [ ] } + { cmd = "load-module" args = "module-always-sink" flags = [ ] + condition = [ { pulse.cmd.always-sink = !false } ] } + { cmd = "load-module" args = "module-device-manager" flags = [ ] + condition = [ { pulse.cmd.device-manager = !false } ] } + { cmd = "load-module" args = "module-device-restore" flags = [ ] + condition = [ { pulse.cmd.device-restore = !false } ] } + { cmd = "load-module" args = "module-stream-restore" flags = [ ] + condition = [ { pulse.cmd.stream-restore = !false } ] } #{ cmd = "load-module" args = "module-switch-on-connect" } #{ cmd = "load-module" args = "module-gsettings" flags = [ nofail ] } ] diff --git a/src/modules/module-protocol-pulse.c b/src/modules/module-protocol-pulse.c index f63253112..77aa9603a 100644 --- a/src/modules/module-protocol-pulse.c +++ b/src/modules/module-protocol-pulse.c @@ -256,10 +256,12 @@ * # Extra commands can be executed here. * # load-module : loads a module with args and flags * # args = " " - * # flags = [ "no-fail" ] + * # ( flags = [ "no-fail" ] ) + * # ( condition = [ { = , ... } ... ] ) + * # conditions will check the pulse.properties key/values. * pulse.cmd = [ * { cmd = "load-module" args = "module-always-sink" flags = [ ] } - * #{ cmd = "load-module" args = "module-switch-on-connect" } + * #{ cmd = "load-module" args = "module-switch-on-connect" condition = [ { pulse.cmd.switch-on-connect = true } ] * #{ cmd = "load-module" args = "module-gsettings" flags = [ "nofail" ] } * ] *\endcode diff --git a/src/modules/module-protocol-pulse/cmd.c b/src/modules/module-protocol-pulse/cmd.c index 8f5200c6f..78b630e70 100644 --- a/src/modules/module-protocol-pulse/cmd.c +++ b/src/modules/module-protocol-pulse/cmd.c @@ -68,7 +68,7 @@ static int parse_cmd(void *user_data, const char *location, const char *section, const char *str, size_t len) { struct impl *impl = user_data; - struct spa_json it[2]; + struct spa_json it[3]; char key[512]; int res = 0; @@ -81,24 +81,36 @@ static int parse_cmd(void *user_data, const char *location, while (spa_json_enter_object(&it[0], &it[1]) > 0) { char *cmd = NULL, *args = NULL, *flags = NULL; const char *val; - int len; + bool have_match = true; + int l; - while ((len = spa_json_object_next(&it[1], key, sizeof(key), &val)) > 0) { + while ((l = spa_json_object_next(&it[1], key, sizeof(key), &val)) > 0) { if (spa_streq(key, "cmd")) { cmd = (char*)val; - spa_json_parse_stringn(val, len, cmd, len+1); + spa_json_parse_stringn(val, l, cmd, l+1); } else if (spa_streq(key, "args")) { args = (char*)val; - spa_json_parse_stringn(val, len, args, len+1); + spa_json_parse_stringn(val, l, args, l+1); } else if (spa_streq(key, "flags")) { - if (spa_json_is_container(val, len)) - len = spa_json_container_len(&it[1], val, len); + if (spa_json_is_container(val, l)) + l = spa_json_container_len(&it[1], val, l); flags = (char*)val; - spa_json_parse_stringn(val, len, flags, len+1); + spa_json_parse_stringn(val, l, flags, l+1); + } else if (spa_streq(key, "condition")) { + if (!spa_json_is_array(val, l)) { + pw_log_warn("expected array for condition in '%.*s'", + (int)l, str); + break; + } + spa_json_enter(&it[1], &it[2]); + have_match = pw_conf_find_match(&it[2], &impl->props->dict, true); } else { pw_log_warn("unknown pulse.cmd key %s", key); } } + if (!have_match) + continue; + if (cmd != NULL) res = do_cmd(impl, cmd, args, flags); if (res < 0) diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index bd773bee8..e040841e5 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -628,7 +628,8 @@ static int load_module(struct pw_context *context, const char *key, const char * * "!null" -> same as !null * !"null" and "!\"null\"" matches anything that is not the string "null" */ -static bool find_match(struct spa_json *arr, const struct spa_dict *props, bool condition) +SPA_EXPORT +bool pw_conf_find_match(struct spa_json *arr, const struct spa_dict *props, bool condition) { struct spa_json it[1]; const char *as = arr->cur; @@ -784,7 +785,7 @@ static int parse_modules(void *user_data, const char *location, break; } spa_json_enter(&it[1], &it[2]); - have_match = find_match(&it[2], &context->properties->dict, true); + have_match = pw_conf_find_match(&it[2], &context->properties->dict, true); } else { pw_log_warn("unknown module key '%s' in '%.*s'", key, (int)len, str); @@ -886,7 +887,7 @@ static int parse_objects(void *user_data, const char *location, break; } spa_json_enter(&it[1], &it[2]); - have_match = find_match(&it[2], &context->properties->dict, true); + have_match = pw_conf_find_match(&it[2], &context->properties->dict, true); } else { pw_log_warn("unknown object key '%s' in '%.*s'", key, (int)len, str); @@ -1056,7 +1057,7 @@ static int parse_exec(void *user_data, const char *location, goto next; } spa_json_enter(&it[1], &it[2]); - have_match = find_match(&it[2], &context->properties->dict, true); + have_match = pw_conf_find_match(&it[2], &context->properties->dict, true); } else { pw_log_warn("unknown exec key '%s' in '%.*s'", key, (int)len, str); @@ -1296,7 +1297,7 @@ int pw_conf_match_rules(const char *str, size_t len, const char *location, } spa_json_enter(&it[1], &it[2]); - have_match = find_match(&it[2], props, false); + have_match = pw_conf_find_match(&it[2], props, false); } else if (spa_streq(key, "actions")) { if (!spa_json_is_object(val, l)) { diff --git a/src/pipewire/conf.h b/src/pipewire/conf.h index 66898b1f9..783c1356a 100644 --- a/src/pipewire/conf.h +++ b/src/pipewire/conf.h @@ -5,6 +5,8 @@ #ifndef PIPEWIRE_CONF_H #define PIPEWIRE_CONF_H +#include + #include /** \defgroup pw_conf Configuration @@ -21,6 +23,8 @@ int pw_conf_load_conf(const char *prefix, const char *name, struct pw_properties int pw_conf_load_state(const char *prefix, const char *name, struct pw_properties *conf); int pw_conf_save_state(const char *prefix, const char *name, const struct pw_properties *conf); +bool pw_conf_find_match(struct spa_json *arr, const struct spa_dict *props, bool condition); + int pw_conf_section_update_props(const struct spa_dict *conf, const char *section, struct pw_properties *props);