mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
conf: improve matching rules a bit more
Handle "null", null, !null, "!null", !"null" and "!\"null\"" matches, copy some docs from wireplumber about the rules and add some more cases.
This commit is contained in:
parent
c52c56621d
commit
0e380de809
2 changed files with 140 additions and 21 deletions
|
|
@ -434,13 +434,105 @@ The general rules object follows the following pattern:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
Match rules are an array of rules.
|
||||||
|
|
||||||
The rules is an array of things to match and what actions to perform
|
A rule is always a JSON object with two keys: matches and actions. The matches key is used to
|
||||||
when a match is found.
|
define the conditions that need to be met for the rule to be evaluated as true, and the actions
|
||||||
|
key is used to define the actions that are performed when the rule is evaluated as true.
|
||||||
|
|
||||||
|
The matches key is always a JSON array of objects, where each object defines a condition that needs
|
||||||
|
to be met. Each condition is a list of key-value pairs, where the key is the name of the property
|
||||||
|
that is being matched, and the value is the value that the property needs to have. Within a condition,
|
||||||
|
all the key-value pairs are combined with a logical AND, and all the conditions in the matches
|
||||||
|
array are combined with a logical OR.
|
||||||
|
|
||||||
|
The actions key is always a JSON object, where each key-value pair defines an action that is
|
||||||
|
performed when the rule is evaluated as true. The action name is specific to the rule and is
|
||||||
|
defined by the rule’s documentation, but most frequently you will see the update-props action,
|
||||||
|
which is used to update the properties of the matched object.
|
||||||
|
|
||||||
|
In the matches array, it is also possible to use regular expressions to match property values.
|
||||||
|
For example, to match all nodes with a name that starts with my_, you can use the following condition:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = "~my_.*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The ~ character signifies that the value is a regular expression. The exact syntax of the regular
|
||||||
|
expressions is the POSIX extended regex syntax, as described in the regex (7) man page.
|
||||||
|
|
||||||
|
In addition to regular expressions, you may also use the ! character to negate a condition. For
|
||||||
|
example, to match all nodes with a name that does not start with my_, you can use the following condition:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = "!~my_.*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The ! character can be used with or without a regular expression. For example, to match all
|
||||||
|
nodes with a name that is not equal to my_node, you can use the following condition:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = "!my_node"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The null value has a special meaning; it checks if the property is not available
|
||||||
|
(or unset). To check if a property is not set:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
To check the existence of a property, one can use the !null condition, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = "!null"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
node.name = !null # simplified syntax
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
To handle the "null" string, one needs to escape the string. For example, to check
|
||||||
|
if a property has the string value "null", use:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
To handle anything but the "null" string, use:
|
||||||
|
|
||||||
|
```
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
node.name = "!\"null\""
|
||||||
|
}
|
||||||
|
{
|
||||||
|
node.name = !"null" # simplified syntax
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
The available actions and their values depend on the specific rule
|
|
||||||
that is used. Usually it is possible to update some properties or set
|
|
||||||
some quirks on the object.
|
|
||||||
|
|
||||||
# CONTEXT PROPERTIES RULES @IDX@ pipewire.conf
|
# CONTEXT PROPERTIES RULES @IDX@ pipewire.conf
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -594,6 +594,14 @@ static int load_module(struct pw_context *context, const char *key, const char *
|
||||||
* <key> = <value>
|
* <key> = <value>
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
|
*
|
||||||
|
* Some things that can match:
|
||||||
|
*
|
||||||
|
* null -> matches when the property is not found
|
||||||
|
* "null" -> matches when the property is found and has the string "null"
|
||||||
|
* !null -> matches when the property is found (any value)
|
||||||
|
* "!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)
|
static bool find_match(struct spa_json *arr, const struct spa_dict *props)
|
||||||
{
|
{
|
||||||
|
|
@ -606,47 +614,66 @@ static bool find_match(struct spa_json *arr, const struct spa_dict *props)
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) {
|
while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) {
|
||||||
bool success = false, is_null;
|
bool success = false, is_null, reg = false, parse_string = true;
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
|
|
||||||
if ((len = spa_json_next(&it[0], &value)) <= 0)
|
if ((len = spa_json_next(&it[0], &value)) <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (len > 0 && value[0] == '!') {
|
/* first decode a string, when there was a string, we assume it
|
||||||
|
* can not be null but the "null" string, unless there is a modifier,
|
||||||
|
* see below. */
|
||||||
|
if (spa_json_is_string(value, len)) {
|
||||||
|
if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0)
|
||||||
|
continue;
|
||||||
|
value = val;
|
||||||
|
len = strlen(val);
|
||||||
|
parse_string = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse the modifiers, after the modifier we unescape the string
|
||||||
|
* again to be able to detect and handle null and "null" */
|
||||||
|
if (len > skip && value[skip] == '!') {
|
||||||
success = !success;
|
success = !success;
|
||||||
skip++;
|
skip++;
|
||||||
|
parse_string = true;
|
||||||
|
}
|
||||||
|
if (len > skip && value[skip] == '~') {
|
||||||
|
reg = true;
|
||||||
|
skip++;
|
||||||
|
parse_string = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = spa_dict_lookup(props, key);
|
str = spa_dict_lookup(props, key);
|
||||||
|
|
||||||
is_null = spa_json_is_null(value+skip, len-skip);
|
/* parse the remaining part of the string, if there was a modifier,
|
||||||
|
* we need to check for null again. Otherwise null was in quotes without
|
||||||
|
* a modifier. */
|
||||||
|
is_null = parse_string && spa_json_is_null(value+skip, len-skip);
|
||||||
if (is_null || str == NULL) {
|
if (is_null || str == NULL) {
|
||||||
if (is_null && str == NULL)
|
if (is_null && str == NULL)
|
||||||
success = !success;
|
success = !success;
|
||||||
} else {
|
} else {
|
||||||
if (spa_json_parse_stringn(value+skip, len-skip, val, sizeof(val)) < 0)
|
/* only unescape string once or again after modifier */
|
||||||
|
if (!parse_string) {
|
||||||
|
memmove(val, value+skip, len-skip);
|
||||||
|
val[len-skip] = '\0';
|
||||||
|
} else if (spa_json_parse_stringn(value+skip, len-skip, val, sizeof(val)) < 0)
|
||||||
continue;
|
continue;
|
||||||
value = val;
|
|
||||||
len = strlen(val);
|
if (reg) {
|
||||||
if (len > 0 && value[0] == '!') {
|
|
||||||
success = !success;
|
|
||||||
skip++;
|
|
||||||
}
|
|
||||||
if (value[skip] == '~') {
|
|
||||||
regex_t preg;
|
regex_t preg;
|
||||||
int res;
|
int res;
|
||||||
skip++;
|
if ((res = regcomp(&preg, val, REG_EXTENDED | REG_NOSUB)) != 0) {
|
||||||
if ((res = regcomp(&preg, value+skip, REG_EXTENDED | REG_NOSUB)) != 0) {
|
|
||||||
char errbuf[1024];
|
char errbuf[1024];
|
||||||
regerror(res, &preg, errbuf, sizeof(errbuf));
|
regerror(res, &preg, errbuf, sizeof(errbuf));
|
||||||
pw_log_warn("invalid regex %s: %s", value+skip, errbuf);
|
pw_log_warn("invalid regex %s: %s", val, errbuf);
|
||||||
} else {
|
} else {
|
||||||
if (regexec(&preg, str, 0, NULL, 0) == 0)
|
if (regexec(&preg, str, 0, NULL, 0) == 0)
|
||||||
success = !success;
|
success = !success;
|
||||||
regfree(&preg);
|
regfree(&preg);
|
||||||
}
|
}
|
||||||
} else if (strncmp(str, value+skip, len-skip) == 0 &&
|
} else if (strcmp(str, val) == 0) {
|
||||||
strlen(str) == (size_t)(len-skip)) {
|
|
||||||
success = !success;
|
success = !success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue