
Changes: - Configuration files are no longer located by libpipewire, which allows us to control the paths that are being looked up. This is a requirement for installations where pipewire and wireplumber are built using different prefixes, in which case the configuration files of wireplumber end up being installed in a place that libpipewire doesn't look into... - The location of conf files is now again $prefix/share/wireplumber, /etc/wireplumber and $XDG_CONFIG_HOME/wireplumber, instead of using the pipewire directories. Also, since the previous commits, we now also support $XDG_CONFIG_DIRS/wireplumber (typically /etc/xdg/wireplumber) and $XDG_DATA_DIRS/wireplumber for system-wide configuration. - Since libpipewire doesn't expose the parser, we now also do the parsing of sections ourselves. This has the advantage that we can optimize it a bit for our use case. - The WpConf API has changed to not be a singleton and it is a property of WpCore instead. The configuration is now expected to be opened before the core is created, which allows the caller to identify configuration errors in advance. By not being a singleton, we can also reuse the WpConf API to open other SPA-JSON files. - WpConf also now has a lazy loading mechanism. The configuration files are mmap'ed and the various sections are located in advance, but not parsed until they are actually requested. Also, the sections are not copied in memory, unlike what happens in libpipewire. They are only copied when merging is needed. - WpCore now disables loading of a configuration file in pw_context, if a WpConf is provided. This is to have complete control here. The 'context.spa-libs' and 'context.modules' sections are still loaded, but we load them in WpConf and pass them down to pw_context for parsing. If a WpConf is not provided, pw_context is left to load the default configuration file (client.conf normally).
159 lines
4.0 KiB
C
159 lines
4.0 KiB
C
/* PipeWire */
|
|
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
/*
|
|
This is a partial copy of functions from libpipewire's conf.c that is meant to
|
|
live here temporarily until pw_context_parse_conf_section() is fixed upstream.
|
|
See https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/1925
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <spa/utils/result.h>
|
|
#include <spa/utils/string.h>
|
|
#include <spa/utils/json.h>
|
|
#include <spa/utils/cleanup.h>
|
|
|
|
#include <pipewire/impl.h>
|
|
|
|
struct data {
|
|
struct pw_context *context;
|
|
struct pw_properties *props;
|
|
int count;
|
|
};
|
|
|
|
/* context.spa-libs = {
|
|
* <factory-name regex> = <library-name>
|
|
* }
|
|
*/
|
|
static int parse_spa_libs(void *user_data, const char *location,
|
|
const char *section, const char *str, size_t len)
|
|
{
|
|
struct data *d = user_data;
|
|
struct pw_context *context = d->context;
|
|
struct spa_json it[2];
|
|
char key[512], value[512];
|
|
|
|
spa_json_init(&it[0], str, len);
|
|
if (spa_json_enter_object(&it[0], &it[1]) < 0) {
|
|
pw_log_error("config file error: context.spa-libs is not an object");
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
|
|
if (spa_json_get_string(&it[1], value, sizeof(value)) > 0) {
|
|
pw_context_add_spa_lib(context, key, value);
|
|
d->count++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int load_module(struct pw_context *context, const char *key, const char *args, const char *flags)
|
|
{
|
|
if (pw_context_load_module(context, key, args, NULL) == NULL) {
|
|
if (errno == ENOENT && flags && strstr(flags, "ifexists") != NULL) {
|
|
pw_log_info("%p: skipping unavailable module %s",
|
|
context, key);
|
|
} else if (flags == NULL || strstr(flags, "nofail") == NULL) {
|
|
pw_log_error("%p: could not load mandatory module \"%s\": %m",
|
|
context, key);
|
|
return -errno;
|
|
} else {
|
|
pw_log_info("%p: could not load optional module \"%s\": %m",
|
|
context, key);
|
|
}
|
|
} else {
|
|
pw_log_info("%p: loaded module %s", context, key);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* context.modules = [
|
|
* { name = <module-name>
|
|
* ( args = { <key> = <value> ... } )
|
|
* ( flags = [ ( ifexists ) ( nofail ) ]
|
|
* ( condition = [ { key = value, .. } .. ] )
|
|
* }
|
|
* ]
|
|
*/
|
|
static int parse_modules(void *user_data, const char *location,
|
|
const char *section, const char *str, size_t len)
|
|
{
|
|
struct data *d = user_data;
|
|
struct pw_context *context = d->context;
|
|
struct spa_json it[4];
|
|
char key[512];
|
|
int res = 0;
|
|
|
|
spa_autofree char *s = strndup(str, len);
|
|
spa_json_init(&it[0], s, len);
|
|
if (spa_json_enter_array(&it[0], &it[1]) < 0) {
|
|
pw_log_error("config file error: context.modules is not an array");
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
|
|
char *name = NULL, *args = NULL, *flags = NULL;
|
|
bool have_match = true;
|
|
|
|
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
|
|
const char *val;
|
|
int len;
|
|
|
|
if ((len = spa_json_next(&it[2], &val)) <= 0)
|
|
break;
|
|
|
|
if (spa_streq(key, "name")) {
|
|
name = (char*)val;
|
|
spa_json_parse_stringn(val, len, name, len+1);
|
|
} else if (spa_streq(key, "args")) {
|
|
if (spa_json_is_container(val, len))
|
|
len = spa_json_container_len(&it[2], val, len);
|
|
|
|
args = (char*)val;
|
|
spa_json_parse_stringn(val, len, args, len+1);
|
|
} else if (spa_streq(key, "flags")) {
|
|
if (spa_json_is_container(val, len))
|
|
len = spa_json_container_len(&it[2], val, len);
|
|
flags = (char*)val;
|
|
spa_json_parse_stringn(val, len, flags, len+1);
|
|
}
|
|
}
|
|
if (!have_match)
|
|
continue;
|
|
|
|
if (name != NULL)
|
|
res = load_module(context, name, args, flags);
|
|
|
|
if (res < 0)
|
|
break;
|
|
|
|
d->count++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _pw_context_parse_conf_section(struct pw_context *context,
|
|
struct pw_properties *conf, const char *section)
|
|
{
|
|
struct data data = { .context = context };
|
|
int res;
|
|
|
|
if (spa_streq(section, "context.spa-libs"))
|
|
res = pw_conf_section_for_each(&conf->dict, section,
|
|
parse_spa_libs, &data);
|
|
else if (spa_streq(section, "context.modules"))
|
|
res = pw_conf_section_for_each(&conf->dict, section,
|
|
parse_modules, &data);
|
|
else
|
|
res = -EINVAL;
|
|
|
|
return res == 0 ? data.count : res;
|
|
}
|