/* WirePlumber * * Copyright © 2020 Collabora Ltd. * @author George Kiagiadakis * * SPDX-License-Identifier: MIT */ #include #include #include #include "script.h" #define WP_LOCAL_LOG_TOPIC log_topic_lua_scripting WP_LOG_TOPIC (log_topic_lua_scripting, "m-lua-scripting") void wp_lua_scripting_api_init (lua_State *L); struct _WpLuaScriptingPlugin { WpPlugin parent; 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_base_dirs_find_file (WP_BASE_DIRS_DATA, "scripts/lib", filename); 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) */ wp_debug ("Executing script %s", script); 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); } static void wp_lua_scripting_component_loader_init (WpComponentLoaderInterface * iface); G_DECLARE_FINAL_TYPE (WpLuaScriptingPlugin, wp_lua_scripting_plugin, WP, LUA_SCRIPTING_PLUGIN, WpPlugin) G_DEFINE_TYPE_WITH_CODE (WpLuaScriptingPlugin, wp_lua_scripting_plugin, WP_TYPE_PLUGIN, G_IMPLEMENT_INTERFACE ( WP_TYPE_COMPONENT_LOADER, wp_lua_scripting_component_loader_init)) static void wp_lua_scripting_plugin_init (WpLuaScriptingPlugin * self) { } 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)); /* init lua engine */ self->L = wplua_new (); lua_pushliteral (self->L, "wireplumber_core"); lua_pushlightuserdata (self->L, 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); 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_str_equal (type, "script/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_base_dirs_find_file (WP_BASE_DIRS_DATA, "scripts", script); } static void wp_lua_scripting_plugin_load (WpComponentLoader * cl, WpCore * core, const gchar * component, const gchar * type, WpSpaJson * args, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer data) { WpLuaScriptingPlugin * self = WP_LUA_SCRIPTING_PLUGIN (cl); g_autoptr (GTask) task = task = g_task_new (self, cancellable, callback, data); g_autofree gchar *filepath = NULL; g_autofree gchar *pluginname = NULL; g_autoptr (WpPlugin) script = NULL; g_task_set_source_tag (task, wp_lua_scripting_plugin_load); /* make sure the component loader is activated */ if (!self->L) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Lua script component loader cannot load Lua scripts if not enabled"); return; } /* make sure the type is supported */ if (!g_str_equal (type, "script/lua")) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not load script '%s' as its type is not 'script/lua'", component); return; } /* find the script */ filepath = find_script (component, core); if (!filepath) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not locate script '%s'", component); return; } pluginname = g_strdup_printf ("script:%s", component); script = g_object_new (WP_TYPE_LUA_SCRIPT, "core", core, "name", pluginname, "lua-engine", self->L, "filename", filepath, "arguments", args, NULL); g_task_return_pointer (task, g_steal_pointer (&script), g_object_unref); } static GObject * wp_lua_scripting_plugin_load_finish (WpComponentLoader * self, GAsyncResult * res, GError ** error) { g_return_val_if_fail ( g_async_result_is_tagged (res, wp_lua_scripting_plugin_load), NULL); return g_task_propagate_pointer (G_TASK (res), error); } static void wp_lua_scripting_plugin_class_init (WpLuaScriptingPluginClass * klass) { WpPluginClass *plugin_class = (WpPluginClass *) klass; plugin_class->enable = wp_lua_scripting_plugin_enable; plugin_class->disable = wp_lua_scripting_plugin_disable; } static void wp_lua_scripting_component_loader_init (WpComponentLoaderInterface * iface) { iface->supports_type = wp_lua_scripting_plugin_supports_type; iface->load = wp_lua_scripting_plugin_load; iface->load_finish = wp_lua_scripting_plugin_load_finish; } WP_PLUGIN_EXPORT GObject * wireplumber__module_init (WpCore * core, WpSpaJson * args, GError ** error) { return G_OBJECT (g_object_new (wp_lua_scripting_plugin_get_type (), "name", "lua-scripting", "core", core, NULL)); }