m-lua-scripting: refactor as a WpComponentLoader

also, add support for loading lua config files, denoted by the
"config/lua" component type
This commit is contained in:
George Kiagiadakis
2021-02-01 17:36:25 +02:00
parent 5647f0bf9e
commit 2d2668100c
5 changed files with 309 additions and 225 deletions

View File

@@ -0,0 +1,226 @@
/* 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/pipewire.h>
static gboolean
add_spa_libs (lua_State *L, WpCore * core, GError ** error)
{
switch (lua_getglobal (L, "spa_libs")) {
case LUA_TTABLE:
break;
case LUA_TNIL:
goto done;
default:
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"Expected 'spa_libs' to be a table");
return FALSE;
}
lua_pushnil (L);
while (lua_next (L, -2)) {
if (lua_type (L, -2) != LUA_TSTRING ||
lua_type (L, -1) != LUA_TSTRING) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"'spa_libs' must be a table with string keys and string values");
return FALSE;
}
const gchar *regex = lua_tostring (L, -2);
const gchar *lib = lua_tostring (L, -1);
int ret = pw_context_add_spa_lib (wp_core_get_pw_context (core), regex,
lib);
if (ret < 0) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"failed to add spa lib ('%s' on '%s'): %s", regex, lib,
g_strerror (-ret));
return FALSE;
}
lua_pop (L, 1); /* pop the value */
}
done:
lua_pop (L, 1); /* pop the spa_libs table */
return TRUE;
}
static GVariant *
lua_to_gvariant (lua_State *L, int index)
{
GVariantBuilder b;
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
int table = lua_absindex (L, index);
lua_pushnil (L);
while (lua_next (L, table)) {
/* each argument must have a string as key */
if (lua_type (L, -2) != LUA_TSTRING) {
wp_warning ("skipping bad component argument key");
continue; /* skip, it's probably harmless */
}
const char *key = lua_tostring (L, -2);
switch (lua_type (L, -1)) {
case LUA_TBOOLEAN:
g_variant_builder_add (&b, "{sv}", key,
g_variant_new_boolean (lua_toboolean (L, -1)));
break;
case LUA_TNUMBER:
if (lua_isinteger (L, -1)) {
g_variant_builder_add (&b, "{sv}", key,
g_variant_new_int64 (lua_tointeger (L, -1)));
} else {
g_variant_builder_add (&b, "{sv}", key,
g_variant_new_double (lua_tonumber (L, -1)));
}
break;
case LUA_TSTRING:
g_variant_builder_add (&b, "{sv}", key,
g_variant_new_string (lua_tostring (L, -1)));
break;
case LUA_TTABLE:
g_variant_builder_add (&b, "{sv}", key, lua_to_gvariant (L, -1));
break;
default:
wp_warning ("skipping bad component argument value");
break;
}
lua_pop (L, 1);
}
return g_variant_builder_end (&b);
}
static gboolean
load_components (lua_State *L, WpCore * core, GError ** error)
{
switch (lua_getglobal (L, "components")) {
case LUA_TTABLE:
break;
case LUA_TNIL:
goto done;
default:
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"Expected 'components' to be a table");
return FALSE;
}
lua_pushnil (L);
while (lua_next (L, -2)) {
/* value must be a table */
if (lua_type (L, -1) != LUA_TTABLE) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"'components' must be a table with tables as values");
return FALSE;
}
/* record indexes to the current key and value of the components table */
int key = lua_absindex (L, -2);
int table = lua_absindex (L, -1);
/* get component */
if (lua_geti (L, table, 1) != LUA_TSTRING) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"components['%s'] has a non-string or unspecified component name",
lua_tostring (L, key));
return FALSE;
}
const char * component = lua_tostring (L, -1);
/* get component type */
if (lua_getfield (L, table, "type") != LUA_TSTRING) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"components['%s'] has a non-string or unspecified component type",
lua_tostring (L, key));
return FALSE;
}
const char * type = lua_tostring (L, -1);
/* optional component arguments */
GVariant *args = NULL;
if (lua_getfield (L, table, "args") == LUA_TTABLE) {
args = lua_to_gvariant (L, -1);
}
if (!wp_core_load_component (core, component, type, args, error))
return FALSE;
/* clear the stack up to the key */
lua_settop (L, key);
}
done:
lua_pop (L, 1); /* pop the components table */
return TRUE;
}
gboolean
wp_lua_scripting_load_configuration (const gchar * conf_file,
WpCore * core, GError ** error)
{
g_autofree gchar * path = NULL;
g_autoptr (lua_State) L = wplua_new ();
gboolean found = FALSE;
wplua_enable_sandbox (L, WP_LUA_SANDBOX_MINIMAL_STD);
/* load conf_file itself */
path = g_build_filename (wp_get_config_dir (), conf_file, NULL);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
wp_info ("loading config file: %s", path);
if (!wplua_load_path (L, path, error))
return FALSE;
found = TRUE;
}
g_clear_pointer (&path, g_free);
/* aggregate split files from the ${conf_file}.d subdirectory */
path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.d",
wp_get_config_dir (), conf_file);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
g_autoptr (GDir) conf_dir = g_dir_open (path, 0, error);
if (!conf_dir)
return FALSE;
const gchar *filename = NULL;
while ((filename = g_dir_read_name (conf_dir))) {
/* Only parse files that have the proper extension */
if (g_str_has_suffix (filename, ".lua")) {
g_autofree gchar * file = g_build_filename (path, filename, NULL);
wp_info ("loading config file: %s", file);
if (!wplua_load_path (L, file, error))
return FALSE;
found = TRUE;
}
}
}
if (!found) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Could not locate configuration file '%s'", conf_file);
return FALSE;
}
if (!add_spa_libs (L, core, error))
return FALSE;
if (!load_components (L, core, error))
return FALSE;
return TRUE;
}

View File

@@ -1,91 +0,0 @@
/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include <wplua/wplua.h>
struct _WpLuaScriptingEngine
{
GObject parent;
lua_State *L;
};
enum {
SIGNAL_INIT_LUA_CONTEXT,
N_SIGNALS,
};
static guint signals[N_SIGNALS] = {0};
static void wp_lua_scripting_engine_parser_iface_init (WpConfigParserInterface * iface);
G_DECLARE_FINAL_TYPE (WpLuaScriptingEngine, wp_lua_scripting_engine,
WP, LUA_SCRIPTING_ENGINE, GObject)
G_DEFINE_TYPE_WITH_CODE (WpLuaScriptingEngine, wp_lua_scripting_engine,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (WP_TYPE_CONFIG_PARSER,
wp_lua_scripting_engine_parser_iface_init))
static void
wp_lua_scripting_engine_init (WpLuaScriptingEngine * self)
{
}
static void
wp_lua_scripting_engine_finalize (GObject * object)
{
WpLuaScriptingEngine * self = WP_LUA_SCRIPTING_ENGINE (object);
g_clear_pointer (&self->L, wplua_free);
G_OBJECT_CLASS (wp_lua_scripting_engine_parent_class)->finalize (object);
}
static void
wp_lua_scripting_engine_class_init (WpLuaScriptingEngineClass * klass)
{
GObjectClass * object_class = (GObjectClass *) klass;
object_class->finalize = wp_lua_scripting_engine_finalize;
signals[SIGNAL_INIT_LUA_CONTEXT] = g_signal_new ("init-lua-context",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
static gboolean
wp_lua_scripting_engine_add_file (WpConfigParser * parser, const gchar * file)
{
WpLuaScriptingEngine * self = WP_LUA_SCRIPTING_ENGINE (parser);
g_autoptr (GError) error = NULL;
if (!wplua_load_path (self->L, file, &error)) {
wp_warning_object (self, "%s", error->message);
if (error->domain != WP_DOMAIN_LUA || error->code != WP_LUA_ERROR_RUNTIME)
return FALSE;
}
return TRUE;
}
static void
wp_lua_scripting_engine_reset (WpConfigParser * parser)
{
WpLuaScriptingEngine * self = WP_LUA_SCRIPTING_ENGINE (parser);
g_clear_pointer (&self->L, wplua_free);
self->L = wplua_new ();
g_signal_emit (self, signals[SIGNAL_INIT_LUA_CONTEXT], 0, self->L);
wplua_enable_sandbox (self->L);
}
static void
wp_lua_scripting_engine_parser_iface_init (WpConfigParserInterface * iface)
{
iface->add_file = wp_lua_scripting_engine_add_file;
iface->reset = wp_lua_scripting_engine_reset;
}

View File

@@ -1,22 +0,0 @@
/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#ifndef __WIREPLUMBER_LUA_SCRIPTING_ENGINE_H__
#define __WIREPLUMBER_LUA_SCRIPTING_ENGINE_H__
#include <wp/wp.h>
#include <wplua/wplua.h>
G_BEGIN_DECLS
#define WP_TYPE_LUA_SCRIPTING_ENGINE \
(wp_lua_scripting_engine_get_type ())
G_END_DECLS
#endif