mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-09 13:30:06 -05:00
conf: add conditions to modules, exec and objects
Make it possible to conditionally load modules, objects and exec by adding match rules for context properties. This makes it possible to only load a module when property is set, which makes it possible to unset a property in a local config to disable module loading. One example is the x11 modules, which can then be disabled on a per user bases based on config overrides.
This commit is contained in:
parent
b9999b292d
commit
5552ff7fdd
2 changed files with 118 additions and 73 deletions
|
|
@ -44,6 +44,9 @@ context.properties = {
|
||||||
vm.overrides = {
|
vm.overrides = {
|
||||||
default.clock.min-quantum = 1024
|
default.clock.min-quantum = 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# keys checked below to disable module loading
|
||||||
|
module.x11.bell = true
|
||||||
}
|
}
|
||||||
|
|
||||||
context.spa-libs = {
|
context.spa-libs = {
|
||||||
|
|
@ -68,13 +71,16 @@ context.spa-libs = {
|
||||||
|
|
||||||
context.modules = [
|
context.modules = [
|
||||||
#{ name = <module-name>
|
#{ name = <module-name>
|
||||||
# [ args = { <key> = <value> ... } ]
|
# ( args = { <key> = <value> ... } )
|
||||||
# [ flags = [ [ ifexists ] [ nofail ] ]
|
# ( flags = [ ( ifexists ) ( nofail ) ] )
|
||||||
|
# ( condition = [ { <key> = <value> ... } ... ] )
|
||||||
#}
|
#}
|
||||||
#
|
#
|
||||||
# Loads a module with the given parameters.
|
# Loads a module with the given parameters.
|
||||||
# If ifexists is given, the module is ignored when it is not found.
|
# If ifexists is given, the module is ignored when it is not found.
|
||||||
# If nofail is given, module initialization failures are ignored.
|
# If nofail is given, module initialization failures are ignored.
|
||||||
|
# If condition is given, the module is loaded only when the context
|
||||||
|
# properties all match the match rules.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Uses realtime scheduling to boost the audio thread priorities. This uses
|
# Uses realtime scheduling to boost the audio thread priorities. This uses
|
||||||
|
|
@ -167,17 +173,21 @@ context.modules = [
|
||||||
#x11.xauthority = null
|
#x11.xauthority = null
|
||||||
}
|
}
|
||||||
flags = [ ifexists nofail ]
|
flags = [ ifexists nofail ]
|
||||||
|
condition = [ { module.x11.bell = true } ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
context.objects = [
|
context.objects = [
|
||||||
#{ factory = <factory-name>
|
#{ factory = <factory-name>
|
||||||
# [ args = { <key> = <value> ... } ]
|
# ( args = { <key> = <value> ... } )
|
||||||
# [ flags = [ [ nofail ] ]
|
# ( flags = [ ( nofail ) ] )
|
||||||
|
# ( condition = [ { <key> = <value> ... } ... ] )
|
||||||
#}
|
#}
|
||||||
#
|
#
|
||||||
# Creates an object from a PipeWire factory with the given parameters.
|
# Creates an object from a PipeWire factory with the given parameters.
|
||||||
# If nofail is given, errors are ignored (and no object is created).
|
# If nofail is given, errors are ignored (and no object is created).
|
||||||
|
# If condition is given, the object is created only when the context properties
|
||||||
|
# all match the match rules.
|
||||||
#
|
#
|
||||||
#{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
|
#{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
|
||||||
#{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = [ nofail ] }
|
#{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = [ nofail ] }
|
||||||
|
|
@ -256,9 +266,14 @@ context.objects = [
|
||||||
]
|
]
|
||||||
|
|
||||||
context.exec = [
|
context.exec = [
|
||||||
#{ path = <program-name> [ args = "<arguments>" ] }
|
#{ path = <program-name>
|
||||||
|
# ( args = "<arguments>" )
|
||||||
|
# ( condition = [ { <key> = <value> ... } ... ] )
|
||||||
|
#}
|
||||||
#
|
#
|
||||||
# Execute the given program with arguments.
|
# Execute the given program with arguments.
|
||||||
|
# If condition is given, the program is executed only when the context
|
||||||
|
# properties all match the match rules.
|
||||||
#
|
#
|
||||||
# You can optionally start the session manager here,
|
# You can optionally start the session manager here,
|
||||||
# but it is better to start it as a systemd service.
|
# but it is better to start it as a systemd service.
|
||||||
|
|
|
||||||
|
|
@ -604,11 +604,71 @@ static int load_module(struct pw_context *context, const char *key, const char *
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* {
|
||||||
|
* # all keys must match the value. ~ in value starts regex.
|
||||||
|
* <key> = <value>
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* context.modules = [
|
* context.modules = [
|
||||||
* { name = <module-name>
|
* { name = <module-name>
|
||||||
* [ args = { <key> = <value> ... } ]
|
* ( args = { <key> = <value> ... } )
|
||||||
* [ flags = [ [ ifexists ] [ nofail ] ]
|
* ( flags = [ ( ifexists ) ( nofail ) ]
|
||||||
|
* ( condition = [ { key = value, .. } .. ] )
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
|
|
@ -617,7 +677,7 @@ static int parse_modules(void *user_data, const char *location,
|
||||||
{
|
{
|
||||||
struct data *d = user_data;
|
struct data *d = user_data;
|
||||||
struct pw_context *context = d->context;
|
struct pw_context *context = d->context;
|
||||||
struct spa_json it[3];
|
struct spa_json it[4];
|
||||||
char key[512], *s;
|
char key[512], *s;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
|
|
@ -631,6 +691,7 @@ static int parse_modules(void *user_data, const char *location,
|
||||||
|
|
||||||
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
||||||
char *name = NULL, *args = NULL, *flags = NULL;
|
char *name = NULL, *args = NULL, *flags = NULL;
|
||||||
|
bool have_match = true;
|
||||||
|
|
||||||
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
||||||
const char *val;
|
const char *val;
|
||||||
|
|
@ -653,8 +714,16 @@ static int parse_modules(void *user_data, const char *location,
|
||||||
len = spa_json_container_len(&it[2], val, len);
|
len = spa_json_container_len(&it[2], val, len);
|
||||||
flags = (char*)val;
|
flags = (char*)val;
|
||||||
spa_json_parse_stringn(val, len, flags, len+1);
|
spa_json_parse_stringn(val, len, flags, len+1);
|
||||||
|
} else if (spa_streq(key, "condition")) {
|
||||||
|
if (!spa_json_is_array(val, len))
|
||||||
|
break;
|
||||||
|
spa_json_enter(&it[2], &it[3]);
|
||||||
|
have_match = find_match(&it[3], &context->properties->dict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!have_match)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (name != NULL)
|
if (name != NULL)
|
||||||
res = load_module(context, name, args, flags);
|
res = load_module(context, name, args, flags);
|
||||||
|
|
||||||
|
|
@ -698,8 +767,9 @@ static int create_object(struct pw_context *context, const char *key, const char
|
||||||
/*
|
/*
|
||||||
* context.objects = [
|
* context.objects = [
|
||||||
* { factory = <factory-name>
|
* { factory = <factory-name>
|
||||||
* [ args = { <key> = <value> ... } ]
|
* ( args = { <key> = <value> ... } )
|
||||||
* [ flags = [ [ nofail ] ] ]
|
* ( flags = [ ( nofail ) ] )
|
||||||
|
* ( condition = [ { key = value, .. } .. ] )
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
|
|
@ -708,7 +778,7 @@ static int parse_objects(void *user_data, const char *location,
|
||||||
{
|
{
|
||||||
struct data *d = user_data;
|
struct data *d = user_data;
|
||||||
struct pw_context *context = d->context;
|
struct pw_context *context = d->context;
|
||||||
struct spa_json it[3];
|
struct spa_json it[4];
|
||||||
char key[512], *s;
|
char key[512], *s;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
|
|
@ -722,6 +792,7 @@ static int parse_objects(void *user_data, const char *location,
|
||||||
|
|
||||||
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
||||||
char *factory = NULL, *args = NULL, *flags = NULL;
|
char *factory = NULL, *args = NULL, *flags = NULL;
|
||||||
|
bool have_match = true;
|
||||||
|
|
||||||
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
||||||
const char *val;
|
const char *val;
|
||||||
|
|
@ -745,8 +816,16 @@ static int parse_objects(void *user_data, const char *location,
|
||||||
|
|
||||||
flags = (char*)val;
|
flags = (char*)val;
|
||||||
spa_json_parse_stringn(val, len, flags, len+1);
|
spa_json_parse_stringn(val, len, flags, len+1);
|
||||||
|
} else if (spa_streq(key, "condition")) {
|
||||||
|
if (!spa_json_is_array(val, len))
|
||||||
|
break;
|
||||||
|
spa_json_enter(&it[2], &it[3]);
|
||||||
|
have_match = find_match(&it[3], &context->properties->dict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!have_match)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (factory != NULL)
|
if (factory != NULL)
|
||||||
res = create_object(context, factory, args, flags);
|
res = create_object(context, factory, args, flags);
|
||||||
|
|
||||||
|
|
@ -807,8 +886,9 @@ static int do_exec(struct pw_context *context, const char *key, const char *args
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* context.exec = [
|
* context.exec = [
|
||||||
* { path = <program-name>
|
* { path = <program-name>
|
||||||
* [ args = "<arguments>" ]
|
* ( args = "<arguments>" )
|
||||||
|
* ( condition = [ { key = value, .. } .. ] )
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
|
|
@ -817,7 +897,7 @@ static int parse_exec(void *user_data, const char *location,
|
||||||
{
|
{
|
||||||
struct data *d = user_data;
|
struct data *d = user_data;
|
||||||
struct pw_context *context = d->context;
|
struct pw_context *context = d->context;
|
||||||
struct spa_json it[3];
|
struct spa_json it[4];
|
||||||
char key[512], *s;
|
char key[512], *s;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
|
|
@ -831,6 +911,7 @@ static int parse_exec(void *user_data, const char *location,
|
||||||
|
|
||||||
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
||||||
char *path = NULL, *args = NULL;
|
char *path = NULL, *args = NULL;
|
||||||
|
bool have_match = true;
|
||||||
|
|
||||||
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
||||||
const char *val;
|
const char *val;
|
||||||
|
|
@ -845,8 +926,16 @@ static int parse_exec(void *user_data, const char *location,
|
||||||
} else if (spa_streq(key, "args")) {
|
} else if (spa_streq(key, "args")) {
|
||||||
args = (char*)val;
|
args = (char*)val;
|
||||||
spa_json_parse_stringn(val, len, args, len+1);
|
spa_json_parse_stringn(val, len, args, len+1);
|
||||||
|
} else if (spa_streq(key, "condition")) {
|
||||||
|
if (!spa_json_is_array(val, len))
|
||||||
|
break;
|
||||||
|
spa_json_enter(&it[2], &it[3]);
|
||||||
|
have_match = find_match(&it[3], &context->properties->dict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!have_match)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (path != NULL)
|
if (path != NULL)
|
||||||
res = do_exec(context, path, args);
|
res = do_exec(context, path, args);
|
||||||
|
|
||||||
|
|
@ -1014,65 +1103,6 @@ int pw_context_conf_update_props(struct pw_context *context,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* {
|
|
||||||
* # all keys must match the value. ~ in value starts regex.
|
|
||||||
* <key> = <value>
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [
|
* [
|
||||||
* {
|
* {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue