2022-12-21 11:59:58 -05:00
|
|
|
/* WirePlumber
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2022 Collabora Ltd.
|
|
|
|
|
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define G_LOG_DOMAIN "wp-conf"
|
2023-01-09 06:03:31 -05:00
|
|
|
#define OVERRIDE_SECTION_PREFIX "override."
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
#include "core.h"
|
|
|
|
|
#include "conf.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "object-interest.h"
|
|
|
|
|
#include "private/registry.h"
|
|
|
|
|
|
|
|
|
|
/*! \defgroup wpconf WpConf */
|
|
|
|
|
/*!
|
|
|
|
|
* \struct WpConf
|
|
|
|
|
*
|
|
|
|
|
* WpConf allows accessing the different sections of the wireplumber
|
|
|
|
|
* configuration.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct _WpConf
|
|
|
|
|
{
|
|
|
|
|
GObject parent;
|
|
|
|
|
|
|
|
|
|
/* Props */
|
|
|
|
|
GWeakRef core;
|
|
|
|
|
|
|
|
|
|
GHashTable *sections;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
PROP_0,
|
|
|
|
|
PROP_CORE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (WpConf, wp_conf, G_TYPE_OBJECT)
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_conf_init (WpConf * self)
|
|
|
|
|
{
|
|
|
|
|
g_weak_ref_init (&self->core, NULL);
|
|
|
|
|
|
|
|
|
|
self->sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
|
|
|
|
(GDestroyNotify) wp_spa_json_unref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_conf_set_property (GObject * object, guint property_id,
|
|
|
|
|
const GValue * value, GParamSpec * pspec)
|
|
|
|
|
{
|
|
|
|
|
WpConf *self = WP_CONF (object);
|
|
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
|
case PROP_CORE:
|
|
|
|
|
g_weak_ref_set (&self->core, g_value_get_object (value));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_conf_get_property (GObject * object, guint property_id,
|
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
|
{
|
|
|
|
|
WpConf *self = WP_CONF (object);
|
|
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
|
case PROP_CORE:
|
|
|
|
|
g_value_take_object (value, g_weak_ref_get (&self->core));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_conf_finalize (GObject * object)
|
|
|
|
|
{
|
|
|
|
|
WpConf *self = WP_CONF (object);
|
|
|
|
|
|
|
|
|
|
g_clear_pointer (&self->sections, g_hash_table_unref);
|
|
|
|
|
g_weak_ref_clear (&self->core);
|
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (wp_conf_parent_class)->finalize (object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wp_conf_class_init (WpConfClass * klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass * object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
|
|
object_class->finalize = wp_conf_finalize;
|
|
|
|
|
object_class->set_property = wp_conf_set_property;
|
|
|
|
|
object_class->get_property = wp_conf_get_property;
|
|
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class, PROP_CORE,
|
|
|
|
|
g_param_spec_object ("core", "core", "The WpCore", WP_TYPE_CORE,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Returns the WpConf instance that is associated with the
|
|
|
|
|
* given core.
|
|
|
|
|
*
|
|
|
|
|
* This method will also create the instance and register it with the core
|
|
|
|
|
* if it had not been created before.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param core the core
|
|
|
|
|
* \returns (transfer full): the WpConf instance
|
|
|
|
|
*/
|
|
|
|
|
WpConf *
|
|
|
|
|
wp_conf_get_instance (WpCore *core)
|
|
|
|
|
{
|
|
|
|
|
WpRegistry *registry = wp_core_get_registry (core);
|
|
|
|
|
WpConf *conf = wp_registry_find_object (registry,
|
|
|
|
|
(GEqualFunc) WP_IS_CONF, NULL);
|
|
|
|
|
|
|
|
|
|
if (G_UNLIKELY (!conf)) {
|
|
|
|
|
conf = g_object_new (WP_TYPE_CONF,
|
|
|
|
|
"core", core,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
wp_registry_register_object (registry, g_object_ref (conf));
|
|
|
|
|
|
|
|
|
|
wp_info_object (conf, "created wpconf object");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conf;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
static WpSpaJson * merge_json (WpSpaJson *old, WpSpaJson *new);
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
static WpSpaJson *
|
|
|
|
|
merge_json_objects (WpSpaJson *old, WpSpaJson *new)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJsonBuilder) b = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (wp_spa_json_is_object (old), NULL);
|
|
|
|
|
g_return_val_if_fail (wp_spa_json_is_object (new), NULL);
|
|
|
|
|
|
|
|
|
|
b = wp_spa_json_builder_new_object ();
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
/* Add all properties from 'old' that don't exist in 'new' */
|
2022-12-21 11:59:58 -05:00
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (old);
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
g_autoptr (WpSpaJson) key = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) val = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) j = NULL;
|
|
|
|
|
g_autofree gchar *str = NULL;
|
2023-01-09 06:03:31 -05:00
|
|
|
const gchar *key_str;
|
|
|
|
|
g_autofree gchar *override_key_str = NULL;
|
|
|
|
|
gboolean override;
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
key = g_value_dup_boxed (&item);
|
2023-01-09 06:03:31 -05:00
|
|
|
key_str = str = wp_spa_json_parse_string (key);
|
|
|
|
|
g_return_val_if_fail (key_str, NULL);
|
|
|
|
|
override = g_str_has_prefix (str, OVERRIDE_SECTION_PREFIX);
|
|
|
|
|
if (override)
|
|
|
|
|
key_str += strlen (OVERRIDE_SECTION_PREFIX);
|
|
|
|
|
override_key_str = g_strdup_printf (OVERRIDE_SECTION_PREFIX "%s", key_str);
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
g_value_unset (&item);
|
|
|
|
|
g_return_val_if_fail (wp_iterator_next (it, &item), NULL);
|
|
|
|
|
val = g_value_dup_boxed (&item);
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
if (!wp_spa_json_object_get (new, key_str, "J", &j, NULL) &&
|
|
|
|
|
!wp_spa_json_object_get (new, override_key_str, "J", &j, NULL)) {
|
|
|
|
|
wp_spa_json_builder_add_property (b, key_str);
|
2022-12-21 11:59:58 -05:00
|
|
|
wp_spa_json_builder_add_json (b, val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
/* Add properties from 'new' that don't exist in 'old'. If a property
|
|
|
|
|
* exists in 'old' and does not have the 'override.' prefix, recursively
|
|
|
|
|
* merge it before adding it. Otherwise override it. */
|
2022-12-21 11:59:58 -05:00
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (new);
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
g_autoptr (WpSpaJson) key = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) val = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) j = NULL;
|
|
|
|
|
g_autofree gchar *str = NULL;
|
2023-01-09 06:03:31 -05:00
|
|
|
const gchar *key_str;
|
|
|
|
|
g_autofree gchar *override_key_str = NULL;
|
|
|
|
|
gboolean override;
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
key = g_value_dup_boxed (&item);
|
2023-01-09 06:03:31 -05:00
|
|
|
key_str = str = wp_spa_json_parse_string (key);
|
|
|
|
|
g_return_val_if_fail (key_str, NULL);
|
|
|
|
|
override = g_str_has_prefix (str, OVERRIDE_SECTION_PREFIX);
|
|
|
|
|
if (override)
|
|
|
|
|
key_str += strlen (OVERRIDE_SECTION_PREFIX);
|
|
|
|
|
override_key_str = g_strdup_printf (OVERRIDE_SECTION_PREFIX "%s", key_str);
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
g_value_unset (&item);
|
|
|
|
|
g_return_val_if_fail (wp_iterator_next (it, &item), NULL);
|
|
|
|
|
val = g_value_dup_boxed (&item);
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
if (!override &&
|
|
|
|
|
(wp_spa_json_object_get (old, key_str, "J", &j, NULL) ||
|
|
|
|
|
wp_spa_json_object_get (old, override_key_str, "J", &j, NULL))) {
|
|
|
|
|
g_autoptr (WpSpaJson) merged = merge_json (j, val);
|
|
|
|
|
if (!merged) {
|
|
|
|
|
wp_warning ("skipping merge of %s as JSON values are not compatible",
|
|
|
|
|
key_str);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
wp_spa_json_builder_add_property (b, key_str);
|
|
|
|
|
wp_spa_json_builder_add_json (b, merged);
|
|
|
|
|
} else {
|
|
|
|
|
wp_spa_json_builder_add_property (b, key_str);
|
2022-12-21 11:59:58 -05:00
|
|
|
wp_spa_json_builder_add_json (b, val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wp_spa_json_builder_end (b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WpSpaJson *
|
|
|
|
|
merge_json_arrays (WpSpaJson *old, WpSpaJson *new)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJsonBuilder) b = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (wp_spa_json_is_array (old), NULL);
|
|
|
|
|
g_return_val_if_fail (wp_spa_json_is_array (new), NULL);
|
|
|
|
|
|
|
|
|
|
b = wp_spa_json_builder_new_array ();
|
|
|
|
|
|
|
|
|
|
/* Add all elements from 'old' */
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (old);
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
WpSpaJson *j = g_value_get_boxed (&item);
|
|
|
|
|
wp_spa_json_builder_add_json (b, j);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add all elements from 'new' */
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (new);
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
WpSpaJson *j = g_value_get_boxed (&item);
|
|
|
|
|
wp_spa_json_builder_add_json (b, j);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wp_spa_json_builder_end (b);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
static WpSpaJson *
|
|
|
|
|
merge_json (WpSpaJson *old, WpSpaJson *new)
|
|
|
|
|
{
|
|
|
|
|
if (wp_spa_json_is_array (old) && wp_spa_json_is_array (new))
|
|
|
|
|
return merge_json_arrays (old, new);
|
|
|
|
|
else if (wp_spa_json_is_object (old) && wp_spa_json_is_object (new))
|
|
|
|
|
return merge_json_objects (old, new);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-21 11:59:58 -05:00
|
|
|
static gint
|
|
|
|
|
merge_section_cb (void *data, const char *location, const char *section,
|
|
|
|
|
const char *str, size_t len)
|
|
|
|
|
{
|
|
|
|
|
WpSpaJson **res_section = (WpSpaJson **)data;
|
|
|
|
|
g_autoptr (WpSpaJson) json = NULL;
|
2023-01-09 06:03:31 -05:00
|
|
|
gboolean override;
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
g_return_val_if_fail (res_section, -EINVAL);
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
override = g_str_has_prefix (section, OVERRIDE_SECTION_PREFIX);
|
|
|
|
|
if (override)
|
|
|
|
|
section += strlen (OVERRIDE_SECTION_PREFIX);
|
|
|
|
|
|
|
|
|
|
wp_debug ("loading section %s (override=%d) from %s", section, override,
|
|
|
|
|
location);
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
/* Only allow sections to be objects or arrays */
|
|
|
|
|
json = wp_spa_json_new_from_stringn (str, len);
|
2023-01-09 06:03:31 -05:00
|
|
|
if (!wp_spa_json_is_container (json)) {
|
2022-12-21 11:59:58 -05:00
|
|
|
wp_warning (
|
|
|
|
|
"skipping section %s from %s as it is not JSON object or array",
|
|
|
|
|
section, location);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
/* Merge section if it was defined previously and the 'override.' prefix is
|
|
|
|
|
* not used */
|
|
|
|
|
if (!override && *res_section) {
|
|
|
|
|
g_autoptr (WpSpaJson) merged = merge_json (*res_section, json);
|
|
|
|
|
if (!merged) {
|
2022-12-21 11:59:58 -05:00
|
|
|
wp_warning (
|
2023-01-09 06:03:31 -05:00
|
|
|
"skipping merge of %s from %s as JSON values are not compatible",
|
2022-12-21 11:59:58 -05:00
|
|
|
section, location);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_clear_pointer (res_section, wp_spa_json_unref);
|
|
|
|
|
*res_section = g_steal_pointer (&merged);
|
2023-01-09 06:03:31 -05:00
|
|
|
wp_debug ("section %s from %s loaded", location, section);
|
2022-12-21 11:59:58 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-09 06:03:31 -05:00
|
|
|
/* Otherwise always replace */
|
|
|
|
|
else {
|
2022-12-21 11:59:58 -05:00
|
|
|
g_clear_pointer (res_section, wp_spa_json_unref);
|
2023-01-09 06:03:31 -05:00
|
|
|
*res_section = g_steal_pointer (&json);
|
|
|
|
|
wp_debug ("section %s from %s loaded", location, section);
|
2022-12-21 11:59:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ensure_section_loaded (WpConf *self, const gchar *section)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpCore) core = NULL;
|
|
|
|
|
struct pw_context *pw_ctx = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) json_section = NULL;
|
2023-01-09 06:03:31 -05:00
|
|
|
g_autofree gchar *override_section = NULL;
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
if (g_hash_table_contains (self->sections, section))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
core = g_weak_ref_get (&self->core);
|
|
|
|
|
g_return_if_fail (core);
|
|
|
|
|
pw_ctx = wp_core_get_pw_context (core);
|
|
|
|
|
g_return_if_fail (pw_ctx);
|
|
|
|
|
|
|
|
|
|
pw_context_conf_section_for_each (pw_ctx, section, merge_section_cb,
|
|
|
|
|
&json_section);
|
2023-01-09 06:03:31 -05:00
|
|
|
override_section = g_strdup_printf (OVERRIDE_SECTION_PREFIX "%s", section);
|
|
|
|
|
pw_context_conf_section_for_each (pw_ctx, override_section, merge_section_cb,
|
|
|
|
|
&json_section);
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
if (json_section)
|
|
|
|
|
g_hash_table_insert (self->sections, g_strdup (section),
|
|
|
|
|
g_steal_pointer (&json_section));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This method will get the JSON value of a specific section from the
|
|
|
|
|
* configuration. If the same section is defined in multiple locations, the
|
|
|
|
|
* sections with the same name will be either merged in case of arrays and
|
|
|
|
|
* objects, or overridden in case of boolean, int, double and strings. The
|
|
|
|
|
* passed fallback value will be returned if the section does not exist.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section the section name
|
|
|
|
|
* \param fallback (transfer full)(nullable): the fallback value
|
|
|
|
|
* \returns (transfer full): the JSON value of the section
|
|
|
|
|
*/
|
|
|
|
|
WpSpaJson *
|
|
|
|
|
wp_conf_get_section (WpConf *self, const gchar *section, WpSpaJson *fallback)
|
|
|
|
|
{
|
|
|
|
|
WpSpaJson *s;
|
|
|
|
|
g_autoptr (WpSpaJson) fb = fallback;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), NULL);
|
|
|
|
|
|
|
|
|
|
ensure_section_loaded (self, section);
|
|
|
|
|
|
|
|
|
|
s = g_hash_table_lookup (self->sections, section);
|
|
|
|
|
if (!s)
|
|
|
|
|
return fb ? g_steal_pointer (&fb) : NULL;
|
|
|
|
|
|
|
|
|
|
return wp_spa_json_ref (s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This is a convenient function to access a JSON value from an object
|
|
|
|
|
* section in the configuration. If the section is an array, or the key does
|
|
|
|
|
* not exist in the object section, it will return the passed fallback value.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section the section name
|
|
|
|
|
* \param key the key name
|
|
|
|
|
* \param fallback (transfer full)(nullable): the fallback value
|
|
|
|
|
* \returns (transfer full): the JSON value of the section's key if it exists,
|
|
|
|
|
* or the passed fallback value otherwise
|
|
|
|
|
*/
|
|
|
|
|
WpSpaJson *
|
|
|
|
|
wp_conf_get_value (WpConf *self, const gchar *section, const gchar *key,
|
|
|
|
|
WpSpaJson *fallback)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJson) s = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) fb = fallback;
|
|
|
|
|
WpSpaJson *v;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), NULL);
|
|
|
|
|
g_return_val_if_fail (section, NULL);
|
|
|
|
|
g_return_val_if_fail (key, NULL);
|
|
|
|
|
|
|
|
|
|
s = wp_conf_get_section (self, section, NULL);
|
|
|
|
|
if (!s)
|
|
|
|
|
goto return_fallback;
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_object (s)) {
|
|
|
|
|
wp_warning_object (self,
|
|
|
|
|
"Cannot get JSON key %s from %s as section is not an JSON object",
|
|
|
|
|
key, section);
|
|
|
|
|
goto return_fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wp_spa_json_object_get (s, key, "J", &v, NULL))
|
|
|
|
|
return v;
|
|
|
|
|
|
|
|
|
|
return_fallback:
|
|
|
|
|
return fb ? g_steal_pointer (&fb) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This is a convenient function to access a boolean value from an object
|
|
|
|
|
* section in the configuration. If the section is an array, or the key does
|
|
|
|
|
* not exist in the object section, it will return the passed fallback value.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section the section name
|
|
|
|
|
* \param key the key name
|
|
|
|
|
* \param fallback the fallback value
|
|
|
|
|
* \returns the boolean value of the section's key if it exists and could be
|
|
|
|
|
* parsed, or the passed fallback value otherwise
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
wp_conf_get_value_boolean (WpConf *self, const gchar *section,
|
|
|
|
|
const gchar *key, gboolean fallback)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJson) s = NULL;
|
|
|
|
|
gboolean v;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), FALSE);
|
|
|
|
|
g_return_val_if_fail (section, FALSE);
|
|
|
|
|
g_return_val_if_fail (key, FALSE);
|
|
|
|
|
|
|
|
|
|
s = wp_conf_get_section (self, section, NULL);
|
|
|
|
|
if (!s)
|
|
|
|
|
return fallback;
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_object (s)) {
|
|
|
|
|
wp_warning_object (self,
|
|
|
|
|
"Cannot get boolean key %s from %s as section is not an JSON object",
|
|
|
|
|
key, section);
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wp_spa_json_object_get (s, key, "b", &v, NULL) ? v : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This is a convenient function to access a int value from an object
|
|
|
|
|
* section in the configuration. If the section is an array, or the key does
|
|
|
|
|
* not exist in the object section, it will return the passed fallback value.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section the section name
|
|
|
|
|
* \param key the key name
|
|
|
|
|
* \param fallback the fallback value
|
|
|
|
|
* \returns the int value of the section's key if it exists and could be
|
|
|
|
|
* parsed, or the passed fallback value otherwise
|
|
|
|
|
*/
|
|
|
|
|
gint
|
|
|
|
|
wp_conf_get_value_int (WpConf *self, const gchar *section,
|
|
|
|
|
const gchar *key, gint fallback)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJson) s = NULL;
|
|
|
|
|
gint v;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), 0);
|
|
|
|
|
g_return_val_if_fail (section, 0);
|
|
|
|
|
g_return_val_if_fail (key, 0);
|
|
|
|
|
|
|
|
|
|
s = wp_conf_get_section (self, section, NULL);
|
|
|
|
|
if (!s)
|
|
|
|
|
return fallback;
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_object (s)) {
|
|
|
|
|
wp_warning_object (self,
|
|
|
|
|
"Cannot get int key %s from %s as section is not an JSON object",
|
|
|
|
|
key, section);
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wp_spa_json_object_get (s, key, "i", &v, NULL) ? v : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This is a convenient function to access a float value from an object
|
|
|
|
|
* section in the configuration. If the section is an array, or the key does
|
|
|
|
|
* not exist in the object section, it will return the passed fallback value.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section the section name
|
|
|
|
|
* \param key the key name
|
|
|
|
|
* \param fallback the fallback value
|
|
|
|
|
* \returns the float value of the section's key if it exists and could be
|
|
|
|
|
* parsed, or the passed fallback value otherwise
|
|
|
|
|
*/
|
|
|
|
|
float
|
|
|
|
|
wp_conf_get_value_float (WpConf *self, const gchar *section,
|
|
|
|
|
const gchar *key, float fallback)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJson) s = NULL;
|
|
|
|
|
float v;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), 0);
|
|
|
|
|
g_return_val_if_fail (section, 0);
|
|
|
|
|
g_return_val_if_fail (key, 0);
|
|
|
|
|
|
|
|
|
|
s = wp_conf_get_section (self, section, NULL);
|
|
|
|
|
if (!s)
|
|
|
|
|
return fallback;
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_object (s)) {
|
|
|
|
|
wp_warning_object (self,
|
|
|
|
|
"Cannot get float key %s from %s as section is not an JSON object",
|
|
|
|
|
key, section);
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wp_spa_json_object_get (s, key, "f", &v, NULL) ? v : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This is a convenient function to access a string value from an object
|
|
|
|
|
* section in the configuration. If the section is an array, or the key does
|
|
|
|
|
* not exist in the object section, it will return the passed fallback value.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section the section name
|
|
|
|
|
* \param key the key name
|
|
|
|
|
* \param fallback (nullable): the fallback value
|
|
|
|
|
* \returns (transfer full): the string value of the section's key if it exists
|
|
|
|
|
* and could be parsed, or the passed fallback value otherwise
|
|
|
|
|
*/
|
|
|
|
|
gchar *
|
|
|
|
|
wp_conf_get_value_string (WpConf *self, const gchar *section,
|
|
|
|
|
const gchar *key, const gchar *fallback)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJson) s = NULL;
|
|
|
|
|
gchar *v;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), NULL);
|
|
|
|
|
g_return_val_if_fail (section, NULL);
|
|
|
|
|
g_return_val_if_fail (key, NULL);
|
|
|
|
|
|
|
|
|
|
s = wp_conf_get_section (self, section, NULL);
|
|
|
|
|
if (!s)
|
|
|
|
|
goto return_fallback;
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_object (s)) {
|
|
|
|
|
wp_warning_object (self,
|
|
|
|
|
"Cannot get string key %s from %s as section is not an JSON object",
|
|
|
|
|
key, section);
|
|
|
|
|
goto return_fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wp_spa_json_object_get (s, key, "s", &v, NULL))
|
|
|
|
|
return v;
|
|
|
|
|
|
|
|
|
|
return_fallback:
|
|
|
|
|
return fallback ? g_strdup (fallback) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
matches_properties (WpSpaJson *match, WpProperties *props)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = NULL;
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (match, FALSE);
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_array (match))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
it = wp_spa_json_new_iterator (match);
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
WpSpaJson *match = g_value_get_boxed (&item);
|
|
|
|
|
if (match && wp_spa_json_is_object (match)) {
|
|
|
|
|
g_autoptr (WpObjectInterest) interest =
|
|
|
|
|
wp_object_interest_new_type (WP_TYPE_PROPERTIES);
|
|
|
|
|
g_autoptr (WpIterator) it_match = wp_spa_json_new_iterator (match);
|
|
|
|
|
g_auto (GValue) item_match = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
for (; wp_iterator_next (it_match, &item_match);
|
|
|
|
|
g_value_unset (&item_match)) {
|
|
|
|
|
WpSpaJson *p;
|
|
|
|
|
g_autofree gchar *key = NULL;
|
|
|
|
|
g_autofree gchar *val = NULL;
|
|
|
|
|
WpConstraintVerb verb = WP_CONSTRAINT_VERB_EQUALS;
|
|
|
|
|
|
|
|
|
|
p = g_value_get_boxed (&item_match);
|
|
|
|
|
key = wp_spa_json_parse_string (p);
|
|
|
|
|
|
|
|
|
|
g_value_unset (&item_match);
|
|
|
|
|
g_return_val_if_fail (wp_iterator_next (it_match, &item_match), FALSE);
|
|
|
|
|
p = g_value_get_boxed (&item_match);
|
|
|
|
|
val = wp_spa_json_parse_string (p);
|
|
|
|
|
|
|
|
|
|
if (val[0] == '~')
|
|
|
|
|
verb = WP_CONSTRAINT_VERB_MATCHES;
|
|
|
|
|
|
|
|
|
|
wp_object_interest_add_constraint (interest,
|
|
|
|
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, key, verb,
|
|
|
|
|
g_variant_new_string (val + 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wp_object_interest_matches (interest, props))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
apply_properties (WpSpaJson *update_props, WpProperties *applied_props)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = NULL;
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (update_props, FALSE);
|
|
|
|
|
g_return_val_if_fail (applied_props, FALSE);
|
|
|
|
|
|
|
|
|
|
if (!wp_spa_json_is_object (update_props))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
it = wp_spa_json_new_iterator (update_props);
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
g_autoptr (WpSpaJson) key = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) val = NULL;
|
|
|
|
|
g_autofree gchar *key_str = NULL;
|
|
|
|
|
g_autofree gchar *val_str = NULL;
|
|
|
|
|
|
|
|
|
|
key = g_value_dup_boxed (&item);
|
|
|
|
|
key_str = wp_spa_json_parse_string (key);
|
|
|
|
|
g_return_val_if_fail (key_str, FALSE);
|
|
|
|
|
|
|
|
|
|
g_value_unset (&item);
|
|
|
|
|
g_return_val_if_fail (wp_iterator_next (it, &item), FALSE);
|
|
|
|
|
val = g_value_dup_boxed (&item);
|
|
|
|
|
val_str = wp_spa_json_parse_string (val);
|
|
|
|
|
g_return_val_if_fail (val_str, FALSE);
|
|
|
|
|
|
|
|
|
|
wp_properties_set (applied_props, key_str, val_str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
apply_rules_json (WpSpaJson *rules, WpProperties *match_props,
|
|
|
|
|
WpProperties *applied_props)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpIterator) it = NULL;
|
|
|
|
|
g_auto (GValue) item = G_VALUE_INIT;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (rules, FALSE);
|
|
|
|
|
g_return_val_if_fail (match_props, FALSE);
|
|
|
|
|
|
|
|
|
|
it = wp_spa_json_new_iterator (rules);
|
|
|
|
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
|
|
|
g_autoptr (WpSpaJson) match = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) update_props = NULL;
|
|
|
|
|
WpSpaJson *rule = g_value_get_boxed (&item);
|
|
|
|
|
if (rule &&
|
|
|
|
|
wp_spa_json_is_object (rule) &&
|
|
|
|
|
wp_spa_json_object_get (rule,
|
|
|
|
|
"matches", "J", &match,
|
|
|
|
|
"update-props", "J", &update_props,
|
|
|
|
|
NULL) &&
|
|
|
|
|
matches_properties (match, match_props)) {
|
|
|
|
|
if (!applied_props)
|
|
|
|
|
return apply_properties (update_props, match_props);
|
|
|
|
|
|
|
|
|
|
wp_properties_update (applied_props, match_props);
|
|
|
|
|
return apply_properties (update_props, applied_props);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* This function applies the rules on a given matched properties. If the
|
|
|
|
|
* applied_props param is not NULL, The applied rules are copied into
|
|
|
|
|
* applied_props and matched_props is not altered. The fallback rules are only
|
|
|
|
|
* used if the rules section cannot be found in the configuration, or the
|
|
|
|
|
* section is not an JSON array.
|
|
|
|
|
*
|
|
|
|
|
* \ingroup wpconf
|
|
|
|
|
* \param self the configuration
|
|
|
|
|
* \param section name of the configuration section that has the rules
|
|
|
|
|
* \param match_props (transfer none)(inout): the properties to match the rules
|
|
|
|
|
* \param applied_props (transfer none)(nullable)(out): the properties with
|
|
|
|
|
* the rules applied
|
|
|
|
|
* \param fallback (transfer full)(nullable): The fallback rules to apply if the
|
|
|
|
|
* rules section could not be found in the configuration, or the section is not
|
|
|
|
|
* a JSON array.
|
|
|
|
|
* \returns TRUE if rules were applied, FALSE otherwise
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
wp_conf_apply_rules (WpConf *self, const gchar *section,
|
|
|
|
|
WpProperties *match_props, WpProperties *applied_props, WpSpaJson *fallback)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr (WpSpaJson) s = NULL;
|
|
|
|
|
g_autoptr (WpSpaJson) fb = fallback;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_CONF (self), FALSE);
|
|
|
|
|
g_return_val_if_fail (section, FALSE);
|
|
|
|
|
g_return_val_if_fail (match_props, FALSE);
|
|
|
|
|
|
|
|
|
|
s = wp_conf_get_section (self, section, NULL);
|
|
|
|
|
if (!s || !wp_spa_json_is_array (s))
|
|
|
|
|
return fb ? apply_rules_json (fb, match_props, applied_props) : FALSE;
|
|
|
|
|
|
|
|
|
|
return apply_rules_json (s, match_props, applied_props);
|
|
|
|
|
}
|