/* WirePlumber * * Copyright © 2020 Collabora Ltd. * @author George Kiagiadakis * * SPDX-License-Identifier: MIT */ #include #include #include 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; }