2022-12-21 11:59:58 -05:00
|
|
|
/* WirePlumber
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2022 Collabora Ltd.
|
|
|
|
|
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "core.h"
|
|
|
|
|
#include "conf.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "object-interest.h"
|
|
|
|
|
#include "private/registry.h"
|
|
|
|
|
|
log: implement a log topics system, like pipewire
The intention is to make checks for enabled log topics faster.
Every topic has its own structure that is statically defined in the file
where the logs are printed from. The structure is initialized transparently
when it is first used and it contains all the log level flags for the levels
that this topic should print messages. It is then checked on the wp_log()
macro before printing the message.
Topics from SPA/PipeWire are also handled natively, so messages are printed
directly without checking if the topic is enabled, since the PipeWire and SPA
macros do the checking themselves.
Messages coming from GLib are checked inside the handler.
An internal WpLogFields object is used to manage the state of each log
message, populating all the fields appropriately from the place they
are coming from (wp_log, spa_log, glib log), formatting the message and
then printing it. For printing to the journald, we still use the glib
message handler, converting all the needed fields to GLogField on demand.
That message handler does not do any checks for the topic or the level, so
we can just call it to send the message.
2023-05-16 11:51:29 +03:00
|
|
|
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-conf")
|
|
|
|
|
|
|
|
|
|
#define OVERRIDE_SECTION_PREFIX "override."
|
|
|
|
|
|
2022-12-21 11:59:58 -05:00
|
|
|
/*! \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;
|
2023-06-17 20:07:58 +02:00
|
|
|
const gchar *value = NULL;
|
2022-12-21 11:59:58 -05:00
|
|
|
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);
|
2023-06-17 20:07:58 +02:00
|
|
|
value = val = wp_spa_json_parse_string (p);
|
2022-12-21 11:59:58 -05:00
|
|
|
|
2023-06-17 20:07:58 +02:00
|
|
|
if (value[0] == '~') {
|
2022-12-21 11:59:58 -05:00
|
|
|
verb = WP_CONSTRAINT_VERB_MATCHES;
|
2023-06-17 20:07:58 +02:00
|
|
|
value++;
|
|
|
|
|
}
|
2022-12-21 11:59:58 -05:00
|
|
|
|
|
|
|
|
wp_object_interest_add_constraint (interest,
|
|
|
|
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, key, verb,
|
2023-06-17 20:07:58 +02:00
|
|
|
g_variant_new_string (value));
|
2022-12-21 11:59:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|