Skip to content
Snippets Groups Projects
Commit 6f740a2a authored by George Kiagiadakis's avatar George Kiagiadakis
Browse files

src: implement module loading

parent f1bb4e35
Branches
Tags
No related merge requests found
......@@ -38,3 +38,9 @@ gnome.generate_gir(wp_lib,
includes: ['GLib-2.0', 'GObject-2.0'],
install: true,
)
wp_dep = declare_dependency(
link_with: wp_lib,
include_directories: wp_lib_include_dir,
dependencies: [gobject_dep]
)
......@@ -11,9 +11,13 @@ project('wireplumber', ['c'],
wireplumber_api_version = '0.1'
gobject_dep = dependency('gobject-2.0')
gmodule_dep = dependency('gmodule-2.0')
gio_dep = dependency('gio-2.0')
pipewire_dep = dependency('libpipewire-0.3')
gnome = import('gnome')
wp_lib_include_dir = include_directories('lib')
subdir('lib')
subdir('src')
......@@ -10,10 +10,17 @@
#include "core.h"
#include "loop-source.h"
#include "module-loader.h"
#include "utils.h"
#include <wp/plugin-registry.h>
#include <wp/proxy-registry.h>
#include <pipewire/pipewire.h>
#include <glib-unix.h>
#include <gio/gio.h>
#define WIREPLUMBER_DEFAULT_CONFIG_FILE "wireplumber.conf"
struct _WpCore
{
......@@ -26,32 +33,27 @@ struct _WpCore
struct pw_remote *remote;
struct spa_hook remote_listener;
struct pw_core_proxy *core_proxy;
struct spa_hook core_proxy_listener;
struct pw_registry_proxy *registry_proxy;
struct spa_hook registry_proxy_listener;
WpModuleLoader *module_loader;
WpPluginRegistry *plugin_registry;
WpProxyRegistry *proxy_registry;
GError *exit_error;
};
G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT);
static const struct pw_registry_proxy_events registry_events = {
PW_VERSION_REGISTRY_PROXY_EVENTS,
//.global = registry_global,
//.global_remove = registry_global_remove,
};
static const struct pw_core_proxy_events core_events = {
PW_VERSION_CORE_EVENTS,
//.done = core_done
};
static gboolean
signal_handler (gpointer data)
{
WpCore *self = WP_CORE (data);
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_INTERRUPTED,
"interrupted by signal");
return G_SOURCE_CONTINUE;
}
static void on_state_changed (void * data,
enum pw_remote_state old_state,
enum pw_remote_state new_state,
const char * error)
static void
remote_state_changed (void * data, enum pw_remote_state old_state,
enum pw_remote_state new_state, const char * error)
{
WpCore *self = WP_CORE (data);
......@@ -60,20 +62,7 @@ static void on_state_changed (void * data,
pw_remote_state_as_string (new_state));
switch (new_state) {
case PW_REMOTE_STATE_CONNECTED:
self->core_proxy = pw_remote_get_core_proxy (self->remote);
pw_core_proxy_add_listener (self->core_proxy, &self->core_proxy_listener,
&core_events, self);
self->registry_proxy = pw_core_proxy_get_registry (self->core_proxy,
PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
pw_registry_proxy_add_listener (self->registry_proxy,
&self->registry_proxy_listener, &registry_events, self);
break;
case PW_REMOTE_STATE_UNCONNECTED:
self->core_proxy = NULL;
self->registry_proxy = NULL;
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_DISCONNECTED, "disconnected");
break;
......@@ -89,9 +78,122 @@ static void on_state_changed (void * data,
static const struct pw_remote_events remote_events = {
PW_VERSION_REMOTE_EVENTS,
.state_changed = on_state_changed,
.state_changed = remote_state_changed,
};
static gboolean
wp_core_parse_commands_file (WpCore * self, GInputStream * stream,
GError ** error)
{
gchar buffer[4096];
gssize bytes_read;
gchar *cur, *linestart, *saveptr;
gchar *cmd, *abi, *module;
gint lineno = 1;
gboolean eof = FALSE;
linestart = cur = buffer;
do {
bytes_read = g_input_stream_read (stream, cur, sizeof (buffer), NULL, error);
if (bytes_read < 0)
return FALSE;
else if (bytes_read == 0) {
eof = TRUE;
/* terminate the remaining data, so that we consume it all */
if (cur != linestart) {
*cur = '\n';
}
}
bytes_read += (cur - linestart);
while (cur - buffer < bytes_read) {
while (cur - buffer < bytes_read && *cur != '\n')
cur++;
if (*cur == '\n') {
/* found the end of a line */
*cur = '\0';
/* tokenize and execute */
cmd = strtok_r (linestart, " ", &saveptr);
if (g_strcmp0 (cmd, "load-module")) {
abi = strtok_r (NULL, " ", &saveptr);
module = strtok_r (NULL, " ", &saveptr);
if (!abi || !module) {
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT,
"expected ABI and MODULE at line %i", lineno);
return FALSE;
} else if (!wp_module_loader_load (self->module_loader,
self->plugin_registry, abi, module, error)) {
return FALSE;
}
} else {
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT,
"unknown command '%s' at line %i", cmd, lineno);
return FALSE;
}
/* continue with the next line */
linestart = ++cur;
lineno++;
}
}
/* reached the end of the data that was read */
if (cur - linestart >= sizeof (buffer)) {
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED,
"line %i exceeds the maximum allowed line size (%d bytes)",
lineno, (gint) sizeof (buffer));
return FALSE;
} else if (cur - linestart > 0) {
/* we have unparsed data, move it to the
* beginning of the buffer and continue */
strncpy (buffer, linestart, cur - linestart);
linestart = buffer;
cur = buffer + (cur - linestart);
}
} while (!eof);
return TRUE;
}
static gboolean
wp_core_load_commands_file (WpCore * self)
{
g_autoptr (GFile) file = NULL;
g_autoptr (GError) error = NULL;
g_autoptr (GFileInputStream) istream = NULL;
const gchar *filename;
filename = g_getenv ("WIREPLUMBER_CONFIG_FILE");
if (!filename)
filename = WIREPLUMBER_DEFAULT_CONFIG_FILE;
file = g_file_new_for_path (filename);
istream = g_file_read (file, NULL, &error);
if (!istream) {
g_propagate_error (&self->exit_error, error);
error = NULL;
g_main_loop_quit (self->loop);
return FALSE;
}
if (!wp_core_parse_commands_file (self, G_INPUT_STREAM (istream), &error)) {
g_propagate_prefixed_error (&self->exit_error, error, "Failed to read %s: ",
filename);
error = NULL;
g_main_loop_quit (self->loop);
return FALSE;
}
return TRUE;
}
static void
wp_core_init (WpCore * self)
{
......@@ -104,6 +206,9 @@ wp_core_init (WpCore * self)
pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events,
self);
self->proxy_registry = wp_proxy_registry_new (self->remote);
self->plugin_registry = wp_plugin_registry_new ();
}
static void
......@@ -111,6 +216,15 @@ wp_core_finalize (GObject * obj)
{
WpCore *self = WP_CORE (obj);
/* ensure all proxies and plugins are unrefed,
* so that the registries can be disposed */
g_object_run_dispose (G_OBJECT (self->plugin_registry));
g_object_run_dispose (G_OBJECT (self->proxy_registry));
g_clear_object (&self->plugin_registry);
g_clear_object (&self->proxy_registry);
g_clear_object (&self->module_loader);
spa_hook_remove (&self->remote_listener);
pw_remote_destroy (self->remote);
......@@ -143,12 +257,13 @@ wp_core_get_instance (void)
}
static gboolean
signal_handler (gpointer data)
wp_core_run_in_idle (WpCore * self)
{
WpCore *self = WP_CORE (data);
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_INTERRUPTED,
"interrupted by signal");
return G_SOURCE_CONTINUE;
if (!wp_core_load_commands_file (self)) goto out;
if (pw_remote_connect (self->remote) < 0) goto out;
out:
return G_SOURCE_REMOVE;
}
void
......@@ -158,7 +273,7 @@ wp_core_run (WpCore * self, GError ** error)
g_unix_signal_add (SIGTERM, signal_handler, self);
g_unix_signal_add (SIGHUP, signal_handler, self);
g_idle_add ((GSourceFunc) pw_remote_connect, self->remote);
g_idle_add ((GSourceFunc) wp_core_run_in_idle, self);
g_main_loop_run (self->loop);
......
......@@ -2,6 +2,7 @@ wp_sources = [
'core.c',
'loop-source.c',
'main.c',
'module-loader.c',
'utils.c',
]
......@@ -9,5 +10,5 @@ executable('wireplumber',
wp_sources,
c_args : [ '-D_GNU_SOURCE', '-DG_LOG_USE_STRUCTURED' ],
install: true,
dependencies : [gobject_dep, pipewire_dep],
dependencies : [gobject_dep, gmodule_dep, gio_dep, pipewire_dep, wp_dep],
)
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "module-loader.h"
#include "utils.h"
#include <wp/plugin.h>
struct _WpModuleLoader
{
GObject parent;
const gchar *module_dir;
};
G_DEFINE_TYPE (WpModuleLoader, wp_module_loader, G_TYPE_OBJECT);
static void
wp_module_loader_init (WpModuleLoader * self)
{
self->module_dir = g_getenv ("WIREPLUMBER_MODULE_DIR");
}
static void
wp_module_loader_class_init (WpModuleLoaderClass * klass)
{
}
WpModuleLoader *
wp_module_loader_new (void)
{
return g_object_new (wp_module_loader_get_type (), NULL);
}
static gboolean
wp_module_loader_load_c (WpModuleLoader * self, WpPluginRegistry * registry,
const gchar * module_name, GError ** error)
{
g_autofree gchar *module_path = NULL;
GModule *module;
gpointer module_init;
typedef void (*WpModuleInitFunc)(WpPluginRegistry *);
module_path = g_module_build_path (self->module_dir, module_name);
module = g_module_open (module_path, G_MODULE_BIND_LOCAL);
if (!module) {
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED,
"Failed to open module %s: %s", module_path, g_module_error ());
return FALSE;
}
if (!g_module_symbol (module, G_STRINGIFY (WP_MODULE_INIT_SYMBOL),
&module_init)) {
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED,
"Failed to locate symbol " G_STRINGIFY (WP_MODULE_INIT_SYMBOL) " in %s",
module_path);
g_module_close (module);
return FALSE;
}
((WpModuleInitFunc) module_init) (registry);
return TRUE;
}
gboolean
wp_module_loader_load (WpModuleLoader * self, WpPluginRegistry * registry,
const gchar * abi, const gchar * module_name, GError ** error)
{
if (!g_strcmp0 (abi, "C")) {
return wp_module_loader_load_c (self, registry, module_name, error);
} else {
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT,
"unknown module ABI %s", abi);
return FALSE;
}
}
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef __WIREPLUMBER_MODULE_LOADER_H__
#define __WIREPLUMBER_MODULE_LOADER_H__
#include <glib-object.h>
#include <wp/plugin-registry.h>
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (WpModuleLoader, wp_module_loader, WP, MODULE_LOADER, GObject)
WpModuleLoader * wp_module_loader_new (void);
gboolean wp_module_loader_load (WpModuleLoader * self,
WpPluginRegistry * registry, const gchar * abi, const gchar * module_name,
GError ** error);
G_END_DECLS
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment