main: move the code that loads the components section into the library
This allows it to be reused in other wireplumber clients and tests
This commit is contained in:
@@ -97,23 +97,26 @@ on_component_loader_load_done (WpComponentLoader * cl, GAsyncResult * res,
|
|||||||
WpCore *core = g_task_get_source_object (task);
|
WpCore *core = g_task_get_source_object (task);
|
||||||
|
|
||||||
o = wp_component_loader_load_finish (cl, res, &error);
|
o = wp_component_loader_load_finish (cl, res, &error);
|
||||||
if (!o) {
|
if (error) {
|
||||||
g_task_return_error (task, g_steal_pointer (&error));
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_trace_object (cl, "loaded object " WP_OBJECT_FORMAT, WP_OBJECT_ARGS (o));
|
if (o) {
|
||||||
|
wp_trace_object (cl, "loaded object " WP_OBJECT_FORMAT, WP_OBJECT_ARGS (o));
|
||||||
|
|
||||||
/* store object in the registry */
|
/* store object in the registry */
|
||||||
wp_registry_register_object (wp_core_get_registry (core), g_object_ref (o));
|
wp_registry_register_object (wp_core_get_registry (core), g_object_ref (o));
|
||||||
|
|
||||||
if (WP_IS_OBJECT (o)) {
|
if (WP_IS_OBJECT (o)) {
|
||||||
/* WpObject needs to be activated */
|
/* WpObject needs to be activated */
|
||||||
wp_object_activate (WP_OBJECT (o), WP_OBJECT_FEATURES_ALL, NULL,
|
wp_object_activate (WP_OBJECT (o), WP_OBJECT_FEATURES_ALL, NULL,
|
||||||
(GAsyncReadyCallback) on_object_activated, g_steal_pointer (&task));
|
(GAsyncReadyCallback) on_object_activated, g_steal_pointer (&task));
|
||||||
} else {
|
return;
|
||||||
g_task_return_boolean (task, TRUE);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -122,6 +125,10 @@ on_component_loader_load_done (WpComponentLoader * cl, GAsyncResult * res,
|
|||||||
* The \a type will determine which component loader to use. The following types
|
* The \a type will determine which component loader to use. The following types
|
||||||
* are built-in and will always work without a component loader:
|
* are built-in and will always work without a component loader:
|
||||||
* - "module" - Loads a WirePlumber module
|
* - "module" - Loads a WirePlumber module
|
||||||
|
* - "array" - Loads multiple components interpreting the \a args as a JSON
|
||||||
|
* array with component definitions, as they would appear in the
|
||||||
|
* configuration file. When this type is used, \a component is ignored and
|
||||||
|
* can be NULL
|
||||||
*
|
*
|
||||||
* \ingroup wpcomponentloader
|
* \ingroup wpcomponentloader
|
||||||
* \param self the core
|
* \param self the core
|
||||||
|
@@ -12,6 +12,324 @@
|
|||||||
|
|
||||||
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-internal-comp-loader")
|
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-internal-comp-loader")
|
||||||
|
|
||||||
|
/*** ComponentData ***/
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
NO_FAIL = 0x1,
|
||||||
|
IF_EXISTS = 0x2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ComponentData
|
||||||
|
{
|
||||||
|
gchar *name;
|
||||||
|
gchar *type;
|
||||||
|
gint priority;
|
||||||
|
gint flags;
|
||||||
|
WpSpaJson *deps;
|
||||||
|
};
|
||||||
|
typedef struct _ComponentData ComponentData;
|
||||||
|
|
||||||
|
static gint
|
||||||
|
component_cmp_func (const ComponentData *a, const ComponentData *b)
|
||||||
|
{
|
||||||
|
return b->priority - a->priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
component_equal_func (const ComponentData *a, ComponentData * b)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
g_str_equal (a->name, b->name) && g_str_equal (a->type, b->type) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
component_data_free (ComponentData *self)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&self->name, g_free);
|
||||||
|
g_clear_pointer (&self->type, g_free);
|
||||||
|
g_clear_pointer (&self->deps, wp_spa_json_unref);
|
||||||
|
g_slice_free (ComponentData, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ComponentData, component_data_free)
|
||||||
|
|
||||||
|
/*** components parser ***/
|
||||||
|
|
||||||
|
static gint
|
||||||
|
pick_default_component_priority (const char *type)
|
||||||
|
{
|
||||||
|
if (g_str_equal (type, "module"))
|
||||||
|
/* regular module default priority */
|
||||||
|
return 110;
|
||||||
|
else if (g_str_equal (type, "script/lua"))
|
||||||
|
/* Lua Script default priority */
|
||||||
|
return 100;
|
||||||
|
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
json_to_components_list (GList **list, WpSpaJson *json)
|
||||||
|
{
|
||||||
|
g_autoptr (WpIterator) it = NULL;
|
||||||
|
g_auto (GValue) item = G_VALUE_INIT;
|
||||||
|
|
||||||
|
it = wp_spa_json_new_iterator (json);
|
||||||
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
||||||
|
WpSpaJson *cjson = g_value_get_boxed (&item);
|
||||||
|
g_autoptr (ComponentData) comp = g_slice_new0 (ComponentData);
|
||||||
|
g_autoptr (WpSpaJson) deps = NULL;
|
||||||
|
g_autoptr (WpSpaJson) flags = NULL;
|
||||||
|
|
||||||
|
/* Parse name and type (mandatory) */
|
||||||
|
if (!wp_spa_json_is_object (cjson) ||
|
||||||
|
!wp_spa_json_object_get (cjson,
|
||||||
|
"name", "s", &comp->name,
|
||||||
|
"type", "s", &comp->type,
|
||||||
|
NULL)) {
|
||||||
|
wp_warning ("component must have both a 'name' and a 'type'");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse priority (optional) */
|
||||||
|
if (!wp_spa_json_object_get (cjson, "priority", "i", &comp->priority,
|
||||||
|
NULL))
|
||||||
|
comp->priority = pick_default_component_priority (comp->type);
|
||||||
|
|
||||||
|
/* Parse deps (optional) */
|
||||||
|
if (wp_spa_json_object_get (cjson, "deps", "J", &deps, NULL)) {
|
||||||
|
if (wp_spa_json_is_array (deps)) {
|
||||||
|
comp->deps = g_steal_pointer (&deps);
|
||||||
|
} else {
|
||||||
|
wp_warning ("skipping component %s as its 'deps' is not a JSON array",
|
||||||
|
comp->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse flags (optional) */
|
||||||
|
if (wp_spa_json_object_get (cjson, "flags", "J", &flags, NULL)) {
|
||||||
|
if (flags && wp_spa_json_is_array (flags)) {
|
||||||
|
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (flags);
|
||||||
|
g_auto (GValue) item = G_VALUE_INIT;
|
||||||
|
|
||||||
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
||||||
|
WpSpaJson *flag = g_value_get_boxed (&item);
|
||||||
|
g_autofree gchar *flag_str = wp_spa_json_parse_string (flag);
|
||||||
|
|
||||||
|
if (g_str_equal (flag_str, "ifexists"))
|
||||||
|
comp->flags |= IF_EXISTS;
|
||||||
|
else if (g_str_equal (flag_str, "nofail"))
|
||||||
|
comp->flags |= NO_FAIL;
|
||||||
|
else
|
||||||
|
wp_warning ("flag '%s' is not valid for component '%s'", flag_str,
|
||||||
|
comp->name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wp_warning ("skipping component %s as its 'flags' is not a JSON array",
|
||||||
|
comp->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert component into the list if it does not exist */
|
||||||
|
if (!g_list_find_custom (*list, comp,
|
||||||
|
(GCompareFunc) component_equal_func)) {
|
||||||
|
wp_trace ("appended component '%s' of type '%s' with priority '%d'",
|
||||||
|
comp->name, comp->type, comp->priority);
|
||||||
|
*list = g_list_insert_sorted (*list, g_steal_pointer (&comp),
|
||||||
|
(GCompareFunc) component_cmp_func);
|
||||||
|
} else {
|
||||||
|
wp_debug ("ignoring component '%s' as it is already defined previously",
|
||||||
|
comp->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** WpComponentArrayLoadTask ***/
|
||||||
|
|
||||||
|
struct _WpComponentArrayLoadTask
|
||||||
|
{
|
||||||
|
WpTransition parent;
|
||||||
|
WpSpaJson *json;
|
||||||
|
GList *components;
|
||||||
|
GList *components_iter;
|
||||||
|
ComponentData *curr_component;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STEP_PARSE = WP_TRANSITION_STEP_CUSTOM_START,
|
||||||
|
STEP_LOAD_NEXT_1,
|
||||||
|
STEP_LOAD_NEXT_2,
|
||||||
|
STEP_CLEANUP,
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE (WpComponentArrayLoadTask, wp_component_array_load_task,
|
||||||
|
WP, COMPONENT_ARRAY_LOAD_TASK, WpTransition)
|
||||||
|
G_DEFINE_TYPE (WpComponentArrayLoadTask, wp_component_array_load_task,
|
||||||
|
WP_TYPE_TRANSITION)
|
||||||
|
|
||||||
|
static void
|
||||||
|
wp_component_array_load_task_init (WpComponentArrayLoadTask * self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
component_meets_dependencies (WpCore *core, ComponentData *comp)
|
||||||
|
{
|
||||||
|
g_autoptr (WpConf) conf = NULL;
|
||||||
|
g_autoptr (WpIterator) it = NULL;
|
||||||
|
g_auto (GValue) item = G_VALUE_INIT;
|
||||||
|
|
||||||
|
if (!comp->deps)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* Note that we consider the dependency valid by default if it is not
|
||||||
|
* found in the settings configuration section */
|
||||||
|
conf = wp_conf_get_instance (core);
|
||||||
|
it = wp_spa_json_new_iterator (comp->deps);
|
||||||
|
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
||||||
|
WpSpaJson *dep = g_value_get_boxed (&item);
|
||||||
|
g_autofree gchar *dep_str = wp_spa_json_parse_string (dep);
|
||||||
|
gboolean value = wp_conf_get_value_boolean (conf,
|
||||||
|
"wireplumber.settings", dep_str, TRUE);
|
||||||
|
if (!value)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
wp_component_array_load_task_get_next_step (WpTransition * transition, guint step)
|
||||||
|
{
|
||||||
|
WpComponentArrayLoadTask *self = WP_COMPONENT_ARRAY_LOAD_TASK (transition);
|
||||||
|
|
||||||
|
switch (step) {
|
||||||
|
case WP_TRANSITION_STEP_NONE: return STEP_PARSE;
|
||||||
|
case STEP_PARSE: return STEP_LOAD_NEXT_1;
|
||||||
|
case STEP_LOAD_NEXT_1:
|
||||||
|
return (self->components_iter) ? STEP_LOAD_NEXT_2 : STEP_CLEANUP;
|
||||||
|
case STEP_LOAD_NEXT_2:
|
||||||
|
return (self->components_iter) ? STEP_LOAD_NEXT_1 : STEP_CLEANUP;
|
||||||
|
case STEP_CLEANUP: return WP_TRANSITION_STEP_NONE;
|
||||||
|
default:
|
||||||
|
g_return_val_if_reached (WP_TRANSITION_STEP_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_component_loaded (WpCore *core, GAsyncResult *res, gpointer data)
|
||||||
|
{
|
||||||
|
WpComponentArrayLoadTask *self = WP_COMPONENT_ARRAY_LOAD_TASK (data);
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
g_return_if_fail (self->curr_component);
|
||||||
|
|
||||||
|
if (!wp_core_load_component_finish (core, res, &error)) {
|
||||||
|
if (self->curr_component->flags & IF_EXISTS &&
|
||||||
|
error->domain == G_IO_ERROR &&
|
||||||
|
error->code == G_IO_ERROR_NOT_FOUND) {
|
||||||
|
wp_info_object (self, "skipping component '%s' with 'ifexists' flag "
|
||||||
|
"because the file does not exist", self->curr_component->name);
|
||||||
|
goto next;
|
||||||
|
} else if (self->curr_component->flags & NO_FAIL) {
|
||||||
|
wp_info_object (self, "skipping component '%s' with 'nofail' flag "
|
||||||
|
"due to error: %s", self->curr_component->name, error->message);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_transition_return_error (WP_TRANSITION (self), g_error_new (
|
||||||
|
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||||
|
"failed to activate component '%s': %s", self->curr_component->name,
|
||||||
|
error->message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
wp_transition_advance (WP_TRANSITION (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wp_component_array_load_task_execute_step (WpTransition * transition, guint step)
|
||||||
|
{
|
||||||
|
WpComponentArrayLoadTask *self = WP_COMPONENT_ARRAY_LOAD_TASK (transition);
|
||||||
|
WpCore *core = wp_transition_get_source_object (transition);
|
||||||
|
|
||||||
|
switch (step) {
|
||||||
|
case STEP_PARSE:
|
||||||
|
if (!wp_spa_json_is_array (self->json)) {
|
||||||
|
wp_transition_return_error (transition, g_error_new (
|
||||||
|
WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||||
|
"components section is not a JSON array"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_to_components_list (&self->components, self->json);
|
||||||
|
self->components_iter = g_list_first (self->components);
|
||||||
|
wp_transition_advance (transition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STEP_LOAD_NEXT_1:
|
||||||
|
case STEP_LOAD_NEXT_2:
|
||||||
|
self->curr_component = (ComponentData *) self->components_iter->data;
|
||||||
|
|
||||||
|
/* Advance iterator */
|
||||||
|
self->components_iter = g_list_next (self->components_iter);
|
||||||
|
|
||||||
|
/* Skip component if its dependencies are not met */
|
||||||
|
if (!component_meets_dependencies (core, self->curr_component)) {
|
||||||
|
wp_info_object (self, "... skipping component '%s' as its dependencies "
|
||||||
|
"are not met", self->curr_component->name);
|
||||||
|
wp_transition_advance (transition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the component */
|
||||||
|
wp_debug_object (self,
|
||||||
|
"... loading component '%s' ('%s') with priority '%d' and flags '%x'",
|
||||||
|
self->curr_component->name, self->curr_component->type,
|
||||||
|
self->curr_component->priority, self->curr_component->flags);
|
||||||
|
wp_core_load_component (core, self->curr_component->name,
|
||||||
|
self->curr_component->type, NULL, NULL,
|
||||||
|
(GAsyncReadyCallback) on_component_loaded, self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STEP_CLEANUP:
|
||||||
|
case WP_TRANSITION_STEP_ERROR:
|
||||||
|
g_list_free_full (g_steal_pointer (&self->components),
|
||||||
|
(GDestroyNotify) component_data_free);
|
||||||
|
g_clear_pointer (&self->json, wp_spa_json_unref);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wp_component_array_load_task_class_init (WpComponentArrayLoadTaskClass * klass)
|
||||||
|
{
|
||||||
|
WpTransitionClass * transition_class = (WpTransitionClass *) klass;
|
||||||
|
transition_class->get_next_step = wp_component_array_load_task_get_next_step;
|
||||||
|
transition_class->execute_step = wp_component_array_load_task_execute_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WpTransition *
|
||||||
|
wp_component_array_load_task_new (WpSpaJson *json,
|
||||||
|
gpointer source_object, GCancellable * cancellable,
|
||||||
|
GAsyncReadyCallback callback, gpointer callback_data)
|
||||||
|
{
|
||||||
|
WpTransition *t = wp_transition_new (wp_component_array_load_task_get_type (),
|
||||||
|
source_object, cancellable, callback, callback_data);
|
||||||
|
WpComponentArrayLoadTask *task = WP_COMPONENT_ARRAY_LOAD_TASK (t);
|
||||||
|
task->json = wp_spa_json_ref (json);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** WpInternalCompLoader ***/
|
||||||
|
|
||||||
struct _WpInternalCompLoader
|
struct _WpInternalCompLoader
|
||||||
{
|
{
|
||||||
GObject parent;
|
GObject parent;
|
||||||
@@ -73,7 +391,7 @@ static gboolean
|
|||||||
wp_internal_comp_loader_supports_type (WpComponentLoader * cl,
|
wp_internal_comp_loader_supports_type (WpComponentLoader * cl,
|
||||||
const gchar * type)
|
const gchar * type)
|
||||||
{
|
{
|
||||||
return g_str_equal (type, "module");
|
return g_str_equal (type, "module") || g_str_equal (type, "array");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -81,18 +399,29 @@ wp_internal_comp_loader_load (WpComponentLoader * self, WpCore * core,
|
|||||||
const gchar * component, const gchar * type, WpSpaJson * args,
|
const gchar * component, const gchar * type, WpSpaJson * args,
|
||||||
GCancellable * cancellable, GAsyncReadyCallback callback, gpointer data)
|
GCancellable * cancellable, GAsyncReadyCallback callback, gpointer data)
|
||||||
{
|
{
|
||||||
g_autoptr (GTask) task = g_task_new (self, cancellable, callback, data);
|
if (g_str_equal (type, "module")) {
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr (GTask) task = g_task_new (self, cancellable, callback, data);
|
||||||
g_autoptr (GObject) o = NULL;
|
g_autoptr (GError) error = NULL;
|
||||||
|
g_autoptr (GObject) o = NULL;
|
||||||
|
|
||||||
g_task_set_source_tag (task, wp_internal_comp_loader_load);
|
g_task_set_source_tag (task, wp_internal_comp_loader_load);
|
||||||
|
|
||||||
/* load Module */
|
/* load module */
|
||||||
o = load_module (core, component, args, &error);
|
o = load_module (core, component, args, &error);
|
||||||
if (o)
|
if (o)
|
||||||
g_task_return_pointer (task, g_steal_pointer (&o), g_object_unref);
|
g_task_return_pointer (task, g_steal_pointer (&o), g_object_unref);
|
||||||
else
|
else
|
||||||
g_task_return_error (task, g_steal_pointer (&error));
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
|
}
|
||||||
|
else if (g_str_equal (type, "array")) {
|
||||||
|
WpTransition *task = wp_component_array_load_task_new (args, core,
|
||||||
|
cancellable, callback, data);
|
||||||
|
wp_transition_set_source_tag (task, wp_internal_comp_loader_load);
|
||||||
|
wp_transition_advance (task);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GObject *
|
static GObject *
|
||||||
@@ -102,7 +431,12 @@ wp_internal_comp_loader_load_finish (WpComponentLoader * self,
|
|||||||
g_return_val_if_fail (
|
g_return_val_if_fail (
|
||||||
g_async_result_is_tagged (res, wp_internal_comp_loader_load), NULL);
|
g_async_result_is_tagged (res, wp_internal_comp_loader_load), NULL);
|
||||||
|
|
||||||
return g_task_propagate_pointer (G_TASK (res), error);
|
if (G_IS_TASK (res))
|
||||||
|
return g_task_propagate_pointer (G_TASK (res), error);
|
||||||
|
else {
|
||||||
|
wp_transition_finish (res, error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
265
src/main.c
265
src/main.c
@@ -38,65 +38,18 @@ static GOptionEntry entries[] =
|
|||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _ComponentData
|
|
||||||
{
|
|
||||||
gchar *name;
|
|
||||||
gchar *type;
|
|
||||||
gint priority;
|
|
||||||
gint flags;
|
|
||||||
WpSpaJson *deps;
|
|
||||||
};
|
|
||||||
typedef struct _ComponentData ComponentData;
|
|
||||||
|
|
||||||
static gint
|
|
||||||
component_cmp_func (const ComponentData *a, const ComponentData *b)
|
|
||||||
{
|
|
||||||
return b->priority - a->priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
component_equal_func (const ComponentData *a, ComponentData * b)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
g_str_equal (a->name, b->name) && g_str_equal (a->type, b->type) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
component_data_free (ComponentData *self)
|
|
||||||
{
|
|
||||||
g_clear_pointer (&self->name, g_free);
|
|
||||||
g_clear_pointer (&self->type, g_free);
|
|
||||||
g_clear_pointer (&self->deps, wp_spa_json_unref);
|
|
||||||
g_slice_free (ComponentData, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ComponentData, component_data_free)
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
NO_FAIL = 0x1,
|
|
||||||
IF_EXISTS = 0x2
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_plugin_loaded (WpCore *core, GAsyncResult *res, gpointer user_data);
|
|
||||||
|
|
||||||
/*** WpInitTransition ***/
|
/*** WpInitTransition ***/
|
||||||
|
|
||||||
struct _WpInitTransition
|
struct _WpInitTransition
|
||||||
{
|
{
|
||||||
WpTransition parent;
|
WpTransition parent;
|
||||||
WpObjectManager *om;
|
WpObjectManager *om;
|
||||||
GList *components;
|
|
||||||
GList *components_iter;
|
|
||||||
ComponentData *curr_component;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
STEP_CONNECT = WP_TRANSITION_STEP_CUSTOM_START,
|
STEP_CONNECT = WP_TRANSITION_STEP_CUSTOM_START,
|
||||||
STEP_CHECK_MEDIA_SESSION,
|
STEP_CHECK_MEDIA_SESSION,
|
||||||
STEP_PARSE_COMPONENTS,
|
STEP_LOAD_COMPONENTS,
|
||||||
STEP_LOAD_ENABLE_COMPONENTS,
|
|
||||||
STEP_CLEANUP,
|
STEP_CLEANUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,9 +79,8 @@ wp_init_transition_get_next_step (WpTransition * transition, guint step)
|
|||||||
return STEP_CONNECT;
|
return STEP_CONNECT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case STEP_CHECK_MEDIA_SESSION: return STEP_PARSE_COMPONENTS;
|
case STEP_CHECK_MEDIA_SESSION: return STEP_LOAD_COMPONENTS;
|
||||||
case STEP_PARSE_COMPONENTS: return STEP_LOAD_ENABLE_COMPONENTS;
|
case STEP_LOAD_COMPONENTS: return STEP_CLEANUP;
|
||||||
case STEP_LOAD_ENABLE_COMPONENTS: return STEP_CLEANUP;
|
|
||||||
case STEP_CLEANUP: return WP_TRANSITION_STEP_NONE;
|
case STEP_CLEANUP: return WP_TRANSITION_STEP_NONE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -136,99 +88,6 @@ wp_init_transition_get_next_step (WpTransition * transition, guint step)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
component_meets_dependencies (WpCore *core, ComponentData *comp)
|
|
||||||
{
|
|
||||||
g_autoptr (WpConf) conf = NULL;
|
|
||||||
g_autoptr (WpIterator) it = NULL;
|
|
||||||
g_auto (GValue) item = G_VALUE_INIT;
|
|
||||||
|
|
||||||
if (!comp->deps)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* Note that we consider the dependency valid by default if it is not
|
|
||||||
* found in the settings configuration section */
|
|
||||||
conf = wp_conf_get_instance (core);
|
|
||||||
it = wp_spa_json_new_iterator (comp->deps);
|
|
||||||
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
||||||
WpSpaJson *dep = g_value_get_boxed (&item);
|
|
||||||
g_autofree gchar *dep_str = wp_spa_json_parse_string (dep);
|
|
||||||
gboolean value = wp_conf_get_value_boolean (conf,
|
|
||||||
"wireplumber.settings", dep_str, TRUE);
|
|
||||||
if (!value)
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
load_enable_components (WpInitTransition *self)
|
|
||||||
{
|
|
||||||
WpCore *core = wp_transition_get_source_object (WP_TRANSITION (self));
|
|
||||||
|
|
||||||
while (self->components_iter) {
|
|
||||||
self->curr_component = (ComponentData *) self->components_iter->data;
|
|
||||||
|
|
||||||
/* Advance */
|
|
||||||
self->components_iter = g_list_next (self->components_iter);
|
|
||||||
|
|
||||||
/* Skip component if its dependencies are not met */
|
|
||||||
if (!component_meets_dependencies (core, self->curr_component)) {
|
|
||||||
wp_info_object (self, "... skipping component '%s' as its dependencies "
|
|
||||||
"are not met", self->curr_component->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load the component */
|
|
||||||
wp_debug_object (self,
|
|
||||||
"... loading component '%s' ('%s') with priority '%d' and flags '%x'",
|
|
||||||
self->curr_component->name, self->curr_component->type,
|
|
||||||
self->curr_component->priority, self->curr_component->flags);
|
|
||||||
wp_core_load_component (core, self->curr_component->name,
|
|
||||||
self->curr_component->type, NULL, NULL,
|
|
||||||
(GAsyncReadyCallback) on_plugin_loaded, self);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->curr_component = NULL;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_plugin_loaded (WpCore *core, GAsyncResult *res, gpointer data)
|
|
||||||
{
|
|
||||||
WpInitTransition *self = data;
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
g_return_if_fail (self->curr_component);
|
|
||||||
|
|
||||||
if (!wp_core_load_component_finish (core, res, &error)) {
|
|
||||||
if (self->curr_component->flags & IF_EXISTS &&
|
|
||||||
error->domain == G_IO_ERROR &&
|
|
||||||
error->code == G_IO_ERROR_NOT_FOUND) {
|
|
||||||
wp_info_object (self, "skipping component '%s' with 'ifexists' flag "
|
|
||||||
"because the file does not exist", self->curr_component->name);
|
|
||||||
goto next;
|
|
||||||
} else if (self->curr_component->flags & NO_FAIL) {
|
|
||||||
wp_info_object (self, "skipping component '%s' with 'nofail' flag "
|
|
||||||
"due to error: %s", self->curr_component->name, error->message);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_transition_return_error (WP_TRANSITION (self), g_error_new (
|
|
||||||
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
|
|
||||||
"failed to activate component '%s': %s", self->curr_component->name,
|
|
||||||
error->message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
next:
|
|
||||||
/* load and enable the rest of components */
|
|
||||||
if (load_enable_components (self))
|
|
||||||
wp_transition_advance (WP_TRANSITION (self));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
check_media_session (WpObjectManager * om, WpInitTransition *self)
|
check_media_session (WpObjectManager * om, WpInitTransition *self)
|
||||||
{
|
{
|
||||||
@@ -242,100 +101,20 @@ check_media_session (WpObjectManager * om, WpInitTransition *self)
|
|||||||
wp_transition_advance (WP_TRANSITION (self));
|
wp_transition_advance (WP_TRANSITION (self));
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
|
||||||
pick_default_component_priority (const char *type)
|
|
||||||
{
|
|
||||||
if (g_str_equal (type, "module"))
|
|
||||||
/* regular module default priority */
|
|
||||||
return 110;
|
|
||||||
else if (g_str_equal (type, "script/lua"))
|
|
||||||
/* Lua Script default priority */
|
|
||||||
return 100;
|
|
||||||
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
append_json_components (GList **list, WpSpaJson *json)
|
on_components_loaded (WpCore *core, GAsyncResult *res, gpointer data)
|
||||||
{
|
{
|
||||||
g_autoptr (WpIterator) it = NULL;
|
WpTransition *self = data;
|
||||||
g_auto (GValue) item = G_VALUE_INIT;
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
if (!wp_spa_json_is_array (json)) {
|
if (!wp_core_load_component_finish (core, res, &error)) {
|
||||||
wp_warning ("components section is not a JSON array, skipping...");
|
wp_transition_return_error (self, g_error_new (
|
||||||
|
WP_DOMAIN_DAEMON, WP_EXIT_CONFIG,
|
||||||
|
"failed to load components: %s", error->message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
it = wp_spa_json_new_iterator (json);
|
wp_transition_advance (self);
|
||||||
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
||||||
WpSpaJson *cjson = g_value_get_boxed (&item);
|
|
||||||
g_autoptr (ComponentData) comp = g_slice_new0 (ComponentData);
|
|
||||||
g_autoptr (WpSpaJson) deps = NULL;
|
|
||||||
g_autoptr (WpSpaJson) flags = NULL;
|
|
||||||
|
|
||||||
/* Parse name and type (mandatory) */
|
|
||||||
if (!wp_spa_json_is_object (cjson) ||
|
|
||||||
!wp_spa_json_object_get (cjson,
|
|
||||||
"name", "s", &comp->name,
|
|
||||||
"type", "s", &comp->type,
|
|
||||||
NULL)) {
|
|
||||||
wp_warning ("component must have both a 'name' and a 'type'");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse priority (optional) */
|
|
||||||
if (!wp_spa_json_object_get (cjson, "priority", "i", &comp->priority,
|
|
||||||
NULL))
|
|
||||||
comp->priority = pick_default_component_priority (comp->type);
|
|
||||||
|
|
||||||
/* Parse deps (optional) */
|
|
||||||
if (wp_spa_json_object_get (cjson, "deps", "J", &deps, NULL)) {
|
|
||||||
if (wp_spa_json_is_array (deps)) {
|
|
||||||
comp->deps = g_steal_pointer (&deps);
|
|
||||||
} else {
|
|
||||||
wp_warning ("skipping component %s as its 'deps' is not a JSON array",
|
|
||||||
comp->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse flags (optional) */
|
|
||||||
if (wp_spa_json_object_get (cjson, "flags", "J", &flags, NULL)) {
|
|
||||||
if (flags && wp_spa_json_is_array (flags)) {
|
|
||||||
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (flags);
|
|
||||||
g_auto (GValue) item = G_VALUE_INIT;
|
|
||||||
|
|
||||||
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
|
|
||||||
WpSpaJson *flag = g_value_get_boxed (&item);
|
|
||||||
g_autofree gchar *flag_str = wp_spa_json_parse_string (flag);
|
|
||||||
|
|
||||||
if (g_str_equal (flag_str, "ifexists"))
|
|
||||||
comp->flags |= IF_EXISTS;
|
|
||||||
else if (g_str_equal (flag_str, "nofail"))
|
|
||||||
comp->flags |= NO_FAIL;
|
|
||||||
else
|
|
||||||
wp_warning ("flag '%s' is not valid for component '%s'", flag_str,
|
|
||||||
comp->name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wp_warning ("skipping component %s as its 'flags' is not a JSON array",
|
|
||||||
comp->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert component into the list if it does not exist */
|
|
||||||
if (!g_list_find_custom (*list, comp,
|
|
||||||
(GCompareFunc) component_equal_func)) {
|
|
||||||
wp_trace ("appended component '%s' of type '%s' with priority '%d'",
|
|
||||||
comp->name, comp->type, comp->priority);
|
|
||||||
*list = g_list_insert_sorted (*list, g_steal_pointer (&comp),
|
|
||||||
(GCompareFunc) component_cmp_func);
|
|
||||||
} else {
|
|
||||||
wp_debug ("ignoring component '%s' as it is already defined previously",
|
|
||||||
comp->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -403,36 +182,24 @@ wp_init_transition_execute_step (WpTransition * transition, guint step)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case STEP_PARSE_COMPONENTS: {
|
case STEP_LOAD_COMPONENTS: {
|
||||||
g_autoptr (WpConf) conf = wp_conf_get_instance (core);
|
g_autoptr (WpConf) conf = wp_conf_get_instance (core);
|
||||||
g_autoptr (WpSpaJson) json_comps = NULL;
|
g_autoptr (WpSpaJson) json_comps = NULL;
|
||||||
|
|
||||||
wp_info_object (self, "parsing components...");
|
wp_info_object (self, "parsing & loading components...");
|
||||||
|
|
||||||
/* Append components that are defined in the configuration section */
|
/* Load components that are defined in the configuration section */
|
||||||
json_comps = wp_conf_get_section (conf, "wireplumber.components", NULL);
|
json_comps = wp_conf_get_section (conf, "wireplumber.components", NULL);
|
||||||
if (json_comps)
|
wp_core_load_component (core, NULL, "array", json_comps, NULL,
|
||||||
append_json_components (&self->components, json_comps);
|
(GAsyncReadyCallback) on_components_loaded, self);
|
||||||
|
|
||||||
self->components_iter = g_list_first (self->components);
|
|
||||||
wp_transition_advance (transition);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case STEP_LOAD_ENABLE_COMPONENTS:
|
|
||||||
wp_info_object (self, "loading and enabling components...");
|
|
||||||
if (load_enable_components (self))
|
|
||||||
wp_transition_advance (WP_TRANSITION (self));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STEP_CLEANUP:
|
case STEP_CLEANUP:
|
||||||
wp_info_object (self, "WirePlumber initialized");
|
wp_info_object (self, "WirePlumber initialized");
|
||||||
G_GNUC_FALLTHROUGH;
|
G_GNUC_FALLTHROUGH;
|
||||||
|
|
||||||
case WP_TRANSITION_STEP_ERROR:
|
case WP_TRANSITION_STEP_ERROR:
|
||||||
g_clear_object (&self->om);
|
g_clear_object (&self->om);
|
||||||
g_list_free_full (g_steal_pointer (&self->components),
|
|
||||||
(GDestroyNotify) component_data_free);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
Reference in New Issue
Block a user