Files
wireplumber/modules/module-lua-scripting/module.c
George Kiagiadakis ecb6100b77 m-lua-scripting: wrap every script in a WpPlugin and expose async activation api
This allows scripts to declare when they have finished their loading,
so we can now also know when wireplumber is done loading and ready to
handle clients

Related to !313
2022-04-23 20:14:59 +03:00

246 lines
7.0 KiB
C

/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include <wplua/wplua.h>
#include <pipewire/keys.h>
#include "script.h"
void wp_lua_scripting_api_init (lua_State *L);
gboolean wp_lua_scripting_load_configuration (const gchar * conf_file,
WpCore * core, GError ** error);
struct _WpLuaScriptingPlugin
{
WpComponentLoader parent;
GPtrArray *scripts; /* element-type: WpPlugin* */
lua_State *L;
};
static int
wp_lua_scripting_package_loader (lua_State *L)
{
luaL_checktype (L, 2, LUA_TFUNCTION);
wplua_push_sandbox (L);
lua_pushvalue (L, 2);
lua_call (L, 1, 1);
return 1;
}
static int
wp_lua_scripting_package_searcher (lua_State *L)
{
const gchar *name = luaL_checkstring (L, 1);
g_autoptr (GError) error = NULL;
g_autofree gchar *filename = g_strdup_printf ("%s.lua", name);
g_autofree gchar *script = wp_find_file (
WP_LOOKUP_DIR_ENV_DATA |
WP_LOOKUP_DIR_XDG_CONFIG_HOME |
WP_LOOKUP_DIR_ETC |
WP_LOOKUP_DIR_PREFIX_SHARE,
filename, "scripts/lib");
if (!script) {
lua_pushliteral (L, "script not found");
return 1;
}
/* 1. loader (function) */
lua_pushcfunction (L, wp_lua_scripting_package_loader);
/* 2. loader data (param to 1) */
if (!wplua_load_path (L, script, &error)) {
lua_pop (L, 1);
lua_pushstring (L, error->message);
return 1;
}
/* 3. script path */
lua_pushstring (L, script);
return 3;
}
static void
wp_lua_scripting_enable_package_searcher (lua_State *L)
{
/* table.insert(package.searchers, 2, wp_lua_scripting_package_searcher) */
lua_getglobal (L, "table");
lua_getfield (L, -1, "insert");
lua_remove (L, -2);
lua_getglobal (L, "package");
lua_getfield (L, -1, "searchers");
lua_remove (L, -2);
lua_pushinteger (L, 2);
lua_pushcfunction (L, wp_lua_scripting_package_searcher);
lua_call (L, 3, 0);
}
G_DECLARE_FINAL_TYPE (WpLuaScriptingPlugin, wp_lua_scripting_plugin,
WP, LUA_SCRIPTING_PLUGIN, WpComponentLoader)
G_DEFINE_TYPE (WpLuaScriptingPlugin, wp_lua_scripting_plugin,
WP_TYPE_COMPONENT_LOADER)
static void
wp_lua_scripting_plugin_init (WpLuaScriptingPlugin * self)
{
self->scripts = g_ptr_array_new_with_free_func (g_object_unref);
}
static void
wp_lua_scripting_plugin_finalize (GObject * object)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (object);
g_clear_pointer (&self->scripts, g_ptr_array_unref);
G_OBJECT_CLASS (wp_lua_scripting_plugin_parent_class)->finalize (object);
}
static void
wp_lua_scripting_plugin_enable (WpPlugin * plugin, WpTransition * transition)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (plugin);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (plugin));
WpCore *export_core;
/* init lua engine */
self->L = wplua_new ();
lua_pushliteral (self->L, "wireplumber_core");
lua_pushlightuserdata (self->L, core);
lua_settable (self->L, LUA_REGISTRYINDEX);
/* initialize secondary connection to pipewire */
export_core = g_object_get_data (G_OBJECT (core), "wireplumber.export-core");
if (export_core) {
lua_pushliteral (self->L, "wireplumber_export_core");
wplua_pushobject (self->L, export_core);
lua_settable (self->L, LUA_REGISTRYINDEX);
}
wp_lua_scripting_api_init (self->L);
wp_lua_scripting_enable_package_searcher (self->L);
wplua_enable_sandbox (self->L, WP_LUA_SANDBOX_ISOLATE_ENV);
/* register scripts that were queued in for loading */
for (guint i = 0; i < self->scripts->len; i++) {
WpPlugin *script = g_ptr_array_index (self->scripts, i);
g_object_set (script, "lua-engine", self->L, NULL);
wp_plugin_register (g_object_ref (script));
}
g_ptr_array_set_size (self->scripts, 0);
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
}
static void
wp_lua_scripting_plugin_disable (WpPlugin * plugin)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (plugin);
g_clear_pointer (&self->L, wplua_unref);
}
static gboolean
wp_lua_scripting_plugin_supports_type (WpComponentLoader * cl,
const gchar * type)
{
return (!g_strcmp0 (type, "script/lua") || !g_strcmp0 (type, "config/lua"));
}
static gchar *
find_script (const gchar * script, WpCore *core)
{
g_autoptr (WpProperties) p = wp_core_get_properties (core);
const gchar *str = wp_properties_get (p, "wireplumber.daemon");
gboolean daemon = !g_strcmp0 (str, "true");
if ((!daemon || g_path_is_absolute (script)) &&
g_file_test (script, G_FILE_TEST_IS_REGULAR))
return g_strdup (script);
return wp_find_file (WP_LOOKUP_DIR_ENV_DATA |
WP_LOOKUP_DIR_XDG_CONFIG_HOME |
WP_LOOKUP_DIR_ETC |
WP_LOOKUP_DIR_PREFIX_SHARE,
script, "scripts");
}
static gboolean
wp_lua_scripting_plugin_load (WpComponentLoader * cl, const gchar * component,
const gchar * type, GVariant * args, GError ** error)
{
WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (cl);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (cl));
/* interpret component as a script */
if (!g_strcmp0 (type, "script/lua")) {
g_autofree gchar *filename = NULL;
g_autofree gchar *pluginname = NULL;
g_autoptr (WpPlugin) script = NULL;
filename = find_script (component, core);
if (!filename) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Could not locate script '%s'", component);
return FALSE;
}
pluginname = g_strdup_printf ("script:%s", component);
script = g_object_new (WP_TYPE_LUA_SCRIPT,
"core", core,
"name", pluginname,
"filename", filename,
"arguments", args,
NULL);
if (self->L) {
g_object_set (script, "lua-engine", self->L, NULL);
wp_plugin_register (g_steal_pointer (&script));
} else {
/* keep in a list and delay registering until the plugin is enabled */
g_ptr_array_add (self->scripts, g_steal_pointer (&script));
}
return TRUE;
}
/* interpret component as a configuration file */
else if (!g_strcmp0 (type, "config/lua")) {
return wp_lua_scripting_load_configuration (component, core, error);
}
g_return_val_if_reached (FALSE);
}
static void
wp_lua_scripting_plugin_class_init (WpLuaScriptingPluginClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpPluginClass *plugin_class = (WpPluginClass *) klass;
WpComponentLoaderClass *cl_class = (WpComponentLoaderClass *) klass;
object_class->finalize = wp_lua_scripting_plugin_finalize;
plugin_class->enable = wp_lua_scripting_plugin_enable;
plugin_class->disable = wp_lua_scripting_plugin_disable;
cl_class->supports_type = wp_lua_scripting_plugin_supports_type;
cl_class->load = wp_lua_scripting_plugin_load;
}
WP_PLUGIN_EXPORT gboolean
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
wp_plugin_register (g_object_new (wp_lua_scripting_plugin_get_type (),
"name", "lua-scripting",
"core", core,
NULL));
return TRUE;
}