diff options
Diffstat (limited to 'core/modulemanager.lua')
-rw-r--r-- | core/modulemanager.lua | 120 |
1 files changed, 107 insertions, 13 deletions
diff --git a/core/modulemanager.lua b/core/modulemanager.lua index a824d36a..0ddf175b 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -6,12 +6,16 @@ -- COPYING file in the source package for more information. -- +local array = require "util.array"; local logger = require "util.logger"; local log = logger.init("modulemanager"); local config = require "core.configmanager"; local pluginloader = require "util.pluginloader"; +local envload = require "util.envload"; local set = require "util.set"; +local core_features = require "core.features".available; + local new_multitable = require "util.multitable".new; local api = require "core.moduleapi"; -- Module API container @@ -22,9 +26,28 @@ local xpcall = require "util.xpcall".xpcall; local debug_traceback = debug.traceback; local setmetatable, rawget = setmetatable, rawget; local ipairs, pairs, type, t_insert = ipairs, pairs, type, table.insert; - -local autoload_modules = {prosody.platform, "presence", "message", "iq", "offline", "c2s", "s2s", "s2s_auth_certs"}; -local component_inheritable_modules = {"tls", "saslauth", "dialback", "iq", "s2s"}; +local lua_version = _VERSION:match("5%.%d$"); + +local autoload_modules = { + prosody.platform, + "presence", + "message", + "iq", + "offline", + "c2s", + "s2s", + "s2s_auth_certs", +}; +local component_inheritable_modules = { + "tls", + "saslauth", + "dialback", + "iq", + "s2s", + "s2s_bidi", + "smacks", + "server_contact_info", +}; -- We need this to let modules access the real global namespace local _G = _G; @@ -32,6 +55,38 @@ local _G = _G; local _ENV = nil; -- luacheck: std none +local loader = pluginloader.init({ + load_filter_cb = function (path, content) + local metadata = {}; + for line in content:gmatch("([^\r\n]+)\r?\n") do + local key, value = line:match("^%-%-%% *([%w_]+): *(.+)$"); + if key then + value = value:gsub("%s+$", ""); + metadata[key] = value; + end + end + + if metadata.conflicts then + local conflicts_features = set.new(array.collect(metadata.conflicts:gmatch("[^, ]+"))); + local conflicted_features = set.intersection(conflicts_features, core_features); + if not conflicted_features:empty() then + log("warn", "Not loading module, due to conflicting features '%s': %s", conflicted_features, path); + return; -- Don't load this module + end + end + if metadata.requires then + local required_features = set.new(array.collect(metadata.requires:gmatch("[^, ]+"))); + local missing_features = required_features - core_features; + if not missing_features:empty() then + log("warn", "Not loading module, due to missing features '%s': %s", missing_features, path); + return; -- Don't load this module + end + end + + return path, content, metadata; + end; +}); + local load_modules_for_host, load, unload, reload, get_module, get_items; local get_modules, is_loaded, module_has_method, call_module_method; @@ -56,13 +111,6 @@ local function get_modules_for_host(host) end local modules = (global_modules + set.new(host_modules_enabled)) - set.new(host_modules_disabled); - -- COMPAT w/ pre 0.8 - if modules:contains("console") then - log("error", "The mod_console plugin has been renamed to mod_admin_telnet. Please update your config."); - modules:remove("console"); - modules:add("admin_telnet"); - end - if modules:contains("vcard") and modules:contains("vcard_legacy") then log("error", "The mod_vcard_legacy plugin replaces mod_vcard but both are enabled. Please update your config."); modules:remove("vcard"); @@ -141,6 +189,7 @@ local function do_load_module(host, module_name, state) if module_has_method(mod, "add_host") then local _log = logger.init(host..":"..module_name); local host_module_api = setmetatable({ + global = false, host = host, event_handlers = new_multitable(), items = {}; _log = _log, log = function (self, ...) return _log(...); end; --luacheck: ignore 212/self },{ @@ -171,13 +220,51 @@ local function do_load_module(host, module_name, state) local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); api_instance.environment = pluginenv; - local mod, err = pluginloader.load_code(module_name, nil, pluginenv); + local mod, err, meta = loader:load_code(module_name, nil, pluginenv); if not mod then log("error", "Unable to load module '%s': %s", module_name or "nil", err or "nil"); + api_instance:set_status("error", "Failed to load (see log)"); return nil, err; end api_instance.path = err; + api_instance.meta = meta; + + local custom_plugins = prosody.paths.installer; + if custom_plugins and err:sub(1, #custom_plugins+1) == custom_plugins.."/" then + -- Stage 1: Make it work (you are here) + -- Stage 2: Make it less hacky (TODO) + local manifest = {}; + local luarocks_path = custom_plugins.."/lib/luarocks/rocks-"..lua_version; + local manifest_filename = luarocks_path.."/manifest"; + local load_manifest, err = envload.envloadfile(manifest_filename, manifest); + if not load_manifest then + -- COMPAT Luarocks 2.x + log("debug", "Could not load LuaRocks 3.x manifest, trying 2.x", err); + luarocks_path = custom_plugins.."/lib/luarocks/rocks"; + manifest_filename = luarocks_path.."/manifest"; + load_manifest, err = envload.envloadfile(manifest_filename, manifest); + end + if not load_manifest then + log("error", "Could not load manifest of installed plugins: %s", err, load_manifest); + else + local ok, err = xpcall(load_manifest, debug_traceback); + if not ok then + log("error", "Could not load manifest of installed plugins: %s", err); + elseif type(manifest.modules) ~= "table" then + log("debug", "Expected 'table' but manifest.modules = %q", manifest.modules); + log("error", "Can't look up resource path for mod_%s because '%s' does not appear to be a LuaRocks manifest", module_name, manifest_filename); + else + local versions = manifest.modules["mod_"..module_name]; + if type(versions) == "table" and versions[1] then + -- Not going to deal with multiple installed versions + api_instance.resource_path = luarocks_path.."/"..versions[1]; + else + log("debug", "mod_%s does not appear in the installation manifest", module_name); + end + end + end + end modulemap[host][module_name] = pluginenv; local ok, err = xpcall(mod, debug_traceback); @@ -187,6 +274,7 @@ local function do_load_module(host, module_name, state) ok, err = call_module_method(pluginenv, "load"); if not ok then log("warn", "Error loading module '%s' on '%s': %s", module_name, host, err or "nil"); + api_instance:set_status("warn", "Error during load (see log)"); end end api_instance.reloading, api_instance.saved_state = nil, nil; @@ -209,6 +297,9 @@ local function do_load_module(host, module_name, state) if not ok then modulemap[api_instance.host][module_name] = nil; log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); + api_instance:set_status("warn", "Error during load (see log)"); + else + api_instance:set_status("core", "Loaded", false); end return ok and pluginenv, err; end @@ -217,7 +308,7 @@ local function do_reload_module(host, name) local mod = get_module(host, name); if not mod then return nil, "module-not-loaded"; end - local _mod, err = pluginloader.load_code(name); -- checking for syntax errors + local _mod, err = loader:load_code(name); -- checking for syntax errors if not _mod then log("error", "Unable to load module '%s': %s", name or "nil", err or "nil"); return nil, err; @@ -225,7 +316,8 @@ local function do_reload_module(host, name) local saved; if module_has_method(mod, "save") then - local ok, ret, err = call_module_method(mod, "save"); + -- FIXME What goes in 'err' here? + local ok, ret, err = call_module_method(mod, "save"); -- luacheck: ignore 211/err if ok then saved = ret; else @@ -340,4 +432,6 @@ return { is_loaded = is_loaded; module_has_method = module_has_method; call_module_method = call_module_method; + + loader = loader; }; |