diff --git a/pipewire-jack/src/match-rules.c b/pipewire-jack/src/match-rules.c deleted file mode 100644 index e1aaf1c9a..000000000 --- a/pipewire-jack/src/match-rules.c +++ /dev/null @@ -1,141 +0,0 @@ -/* PipeWire - * - * Copyright © 2021 Wim Taymans - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include "config.h" - -#include -#include - -#include - -static bool find_match(struct spa_json *arr, const struct spa_dict *props) -{ - struct spa_json it[1]; - - while (spa_json_enter_object(arr, &it[0]) > 0) { - char key[256], val[1024]; - const char *str, *value; - int match = 0, fail = 0; - int len; - - while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) { - bool success = false; - - if ((len = spa_json_next(&it[0], &value)) <= 0) - break; - - str = spa_dict_lookup(props, key); - - if (spa_json_is_null(value, len)) { - success = str == NULL; - } else { - if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0) - continue; - value = val; - len = strlen(val); - } - if (str != NULL) { - if (value[0] == '~') { - regex_t preg; - if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) { - if (regexec(&preg, str, 0, NULL, 0) == 0) - success = true; - regfree(&preg); - } - } else if (strncmp(str, value, len) == 0 && - strlen(str) == (size_t)len) { - success = true; - } - } - if (success) { - match++; - pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); - } - else - fail++; - } - if (match > 0 && fail == 0) - return true; - } - return false; -} - -int pw_jack_match_rules(const char *rules, size_t size, const struct spa_dict *props, - int (*matched) (void *data, const char *action, const char *val, size_t len), - void *data) -{ - const char *val; - struct spa_json it[4], actions; - int count = 0; - - spa_json_init(&it[0], rules, size); - if (spa_json_enter_array(&it[0], &it[1]) < 0) - return 0; - - while (spa_json_enter_object(&it[1], &it[2]) > 0) { - char key[64]; - bool have_match = false, have_actions = false; - - while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) { - if (spa_streq(key, "matches")) { - if (spa_json_enter_array(&it[2], &it[3]) < 0) - break; - - have_match = find_match(&it[3], props); - } - else if (spa_streq(key, "actions")) { - if (spa_json_enter_object(&it[2], &actions) > 0) - have_actions = true; - } - else if (spa_json_next(&it[2], &val) <= 0) - break; - } - if (!have_match || !have_actions) - continue; - - while (spa_json_get_string(&actions, key, sizeof(key)) > 0) { - int res, len; - pw_log_debug("action %s", key); - - if ((len = spa_json_next(&actions, &val)) <= 0) - break; - - if (spa_json_is_container(val, len)) - len = spa_json_container_len(&actions, val, len); - - if ((res = matched(data, key, val, len)) < 0) - return res; - - count += res; - } - } - return count; -} diff --git a/pipewire-jack/src/meson.build b/pipewire-jack/src/meson.build index ac06ea491..bb1a7f0e9 100644 --- a/pipewire-jack/src/meson.build +++ b/pipewire-jack/src/meson.build @@ -1,7 +1,6 @@ pipewire_jack_sources = [ 'export.c', 'pipewire-jack.c', - 'match-rules.c', 'ringbuffer.c', 'uuid.c', ] diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index 0a14a645e..17d713394 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -3087,7 +3087,8 @@ static void varargs_parse (struct client *c, jack_options_t options, va_list ap) } -static int execute_match(void *data, const char *action, const char *val, int len) +static int execute_match(void *data, const char *location, const char *action, + const char *val, size_t len) { struct client *client = data; if (spa_streq(action, "update-props")) @@ -3095,19 +3096,6 @@ static int execute_match(void *data, const char *action, const char *val, int le return 1; } -static int apply_jack_rules(void *data, const char *location, const char *section, - const char *str, size_t len) -{ - struct client *client = data; - const struct pw_properties *p = - pw_context_get_properties(client->context.context); - - if (p != NULL) - pw_jack_match_rules(str, len, &p->dict, execute_match, client); - - return 0; -} - SPA_EXPORT jack_client_t * jack_client_open (const char *client_name, jack_options_t options, @@ -3171,9 +3159,8 @@ jack_client_t * jack_client_open (const char *client_name, if ((str = getenv("PIPEWIRE_PROPS")) != NULL) pw_properties_update_string(client->props, str, strlen(str)); - - pw_context_conf_section_for_each(client->context.context, "jack.rules", - apply_jack_rules, client); + pw_context_conf_section_match_rules(client->context.context, "jack.rules", + &client->props->dict, execute_match, client); client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true); client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", false); diff --git a/src/modules/module-protocol-pulse/quirks.c b/src/modules/module-protocol-pulse/quirks.c index e20925edc..e00eafe91 100644 --- a/src/modules/module-protocol-pulse/quirks.c +++ b/src/modules/module-protocol-pulse/quirks.c @@ -46,111 +46,8 @@ static uint64_t parse_quirks(const char *str) return 0; } -static bool find_match(struct spa_json *arr, const struct spa_dict *props) -{ - struct spa_json it[1]; - - while (spa_json_enter_object(arr, &it[0]) > 0) { - char key[256], val[1024]; - const char *str, *value; - int match = 0, fail = 0; - int len; - - while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) { - bool success = false; - - if ((len = spa_json_next(&it[0], &value)) <= 0) - break; - - str = spa_dict_lookup(props, key); - - if (spa_json_is_null(value, len)) { - success = str == NULL; - } else { - if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0) - continue; - value = val; - len = strlen(val); - } - if (str != NULL) { - if (value[0] == '~') { - regex_t preg; - if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) { - if (regexec(&preg, str, 0, NULL, 0) == 0) - success = true; - regfree(&preg); - } - } else if (strncmp(str, value, len) == 0 && - strlen(str) == (size_t)len) { - success = true; - } - } - if (success) { - match++; - pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); - } - else - fail++; - } - if (match > 0 && fail == 0) - return true; - } - return false; -} - -static int pw_conf_match_rules(const char *rules, size_t size, const struct spa_dict *props, - int (*matched) (void *data, const char *action, const char *val, int len), - void *data) -{ - const char *val; - struct spa_json it[4], actions; - int count = 0; - - spa_json_init(&it[0], rules, size); - if (spa_json_enter_array(&it[0], &it[1]) < 0) - return 0; - - while (spa_json_enter_object(&it[1], &it[2]) > 0) { - char key[64]; - bool have_match = false, have_actions = false; - - while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) { - if (spa_streq(key, "matches")) { - if (spa_json_enter_array(&it[2], &it[3]) < 0) - break; - - have_match = find_match(&it[3], props); - } - else if (spa_streq(key, "actions")) { - if (spa_json_enter_object(&it[2], &actions) > 0) - have_actions = true; - } - else if (spa_json_next(&it[2], &val) <= 0) - break; - } - if (!have_match || !have_actions) - continue; - - while (spa_json_get_string(&actions, key, sizeof(key)) > 0) { - int res, len; - pw_log_debug("action %s", key); - - if ((len = spa_json_next(&actions, &val)) <= 0) - break; - - if (spa_json_is_container(val, len)) - len = spa_json_container_len(&actions, val, len); - - if ((res = matched(data, key, val, len)) < 0) - return res; - - count += res; - } - } - return count; -} - -static int client_rule_matched(void *data, const char *action, const char *val, int len) +static int apply_match(void *data, const char *location, const char *action, + const char *val, size_t len) { struct client *client = data; @@ -170,19 +67,10 @@ static int client_rule_matched(void *data, const char *action, const char *val, return 0; } -static int apply_pulse_rules(void *data, const char *location, const char *section, - const char *str, size_t len) -{ - struct client *client = data; - pw_conf_match_rules(str, len, &client->props->dict, - client_rule_matched, client); - return 0; -} - int client_update_quirks(struct client *client) { struct impl *impl = client->impl; struct pw_context *context = impl->context; - return pw_context_conf_section_for_each(context, "pulse.rules", - apply_pulse_rules, client); + return pw_context_conf_section_match_rules(context, "pulse.rules", + &client->props->dict, apply_match, client); } diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index 85287eefa..50bc7e471 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef HAVE_PWD_H #include #endif @@ -810,6 +811,7 @@ exit: return res; } + SPA_EXPORT int pw_context_conf_section_for_each(struct pw_context *context, const char *section, int (*callback) (void *data, const char *location, const char *section, @@ -882,3 +884,153 @@ int pw_context_conf_update_props(struct pw_context *context, update_props, &data); return data.count; } + +struct match { + const struct spa_dict *props; + int (*matched) (void *data, const char *location, const char *action, + const char *val, size_t len); + void *data; +}; + +/* + * { + * # all keys must match the value. ~ in value starts regex. + * = + * ... + * } + */ +static bool find_match(struct spa_json *arr, const struct spa_dict *props) +{ + struct spa_json it[1]; + + while (spa_json_enter_object(arr, &it[0]) > 0) { + char key[256], val[1024]; + const char *str, *value; + int match = 0, fail = 0; + int len; + + while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) { + bool success = false; + + if ((len = spa_json_next(&it[0], &value)) <= 0) + break; + + str = spa_dict_lookup(props, key); + + if (spa_json_is_null(value, len)) { + success = str == NULL; + } else { + if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0) + continue; + value = val; + len = strlen(val); + } + if (str != NULL) { + if (value[0] == '~') { + regex_t preg; + if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) { + if (regexec(&preg, str, 0, NULL, 0) == 0) + success = true; + regfree(&preg); + } + } else if (strncmp(str, value, len) == 0 && + strlen(str) == (size_t)len) { + success = true; + } + } + if (success) { + match++; + pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); + } + else + fail++; + } + if (match > 0 && fail == 0) + return true; + } + return false; +} + +/** + * rules = [ + * { + * matches = [ + * # any of the items in matches needs to match, it one does, + * # actions are emited. + * { + * # all keys must match the value. ~ in value starts regex. + * = + * ... + * } + * ... + * ] + * actions = { + * = + * ... + * } + * } + * ] + */ +static int match_rules(void *data, const char *location, const char *section, + const char *str, size_t len) +{ + struct match *match = data; + const struct spa_dict *props = match->props; + const char *val; + struct spa_json it[4], actions; + + spa_json_init(&it[0], str, len); + if (spa_json_enter_array(&it[0], &it[1]) < 0) + return 0; + + while (spa_json_enter_object(&it[1], &it[2]) > 0) { + char key[64]; + bool have_match = false, have_actions = false; + + while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) { + if (spa_streq(key, "matches")) { + if (spa_json_enter_array(&it[2], &it[3]) < 0) + break; + + have_match = find_match(&it[3], props); + } + else if (spa_streq(key, "actions")) { + if (spa_json_enter_object(&it[2], &actions) > 0) + have_actions = true; + } + else if (spa_json_next(&it[2], &val) <= 0) + break; + } + if (!have_match || !have_actions) + continue; + + while (spa_json_get_string(&actions, key, sizeof(key)) > 0) { + int res, len; + pw_log_debug("action %s", key); + + if ((len = spa_json_next(&actions, &val)) <= 0) + break; + + if (spa_json_is_container(val, len)) + len = spa_json_container_len(&actions, val, len); + + if ((res = match->matched(match->data, location, key, val, len)) < 0) + return res; + } + } + return 0; +} + +SPA_EXPORT +int pw_context_conf_section_match_rules(struct pw_context *context, const char *section, + struct spa_dict *props, + int (*callback) (void *data, const char *location, const char *action, + const char *str, size_t len), + void *data) +{ + struct match match = { + .props = props, + .matched = callback, + .data = data }; + return pw_context_conf_section_for_each(context, section, match_rules, &match); +} diff --git a/src/pipewire/context.h b/src/pipewire/context.h index 5ec91725b..84baf31d0 100644 --- a/src/pipewire/context.h +++ b/src/pipewire/context.h @@ -123,6 +123,12 @@ int pw_context_conf_section_for_each(struct pw_context *context, const char *sec int (*callback) (void *data, const char *location, const char *section, const char *str, size_t len), void *data); +/** emit callback for all matched properties. Since 0.3.46 */ +int pw_context_conf_section_match_rules(struct pw_context *context, const char *section, + struct spa_dict *props, + int (*callback) (void *data, const char *location, const char *action, + const char *str, size_t len), + void *data); /** Get the context support objects */ const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support);