mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
New option install_contrib_modules
This option is read during ejabberd start or config reload. It installs the listed modules which aren't yet installed, as long as allow_contrib_modules is not disabled. Edit ejabberd.yml and configure the desired ejabberd-contrib modules, add them in the install_contrib_modules option, finally start ejabberd (or reload config).
This commit is contained in:
parent
3263e81972
commit
c333cc0776
@ -502,7 +502,13 @@ read_file(File, Opts) ->
|
||||
end,
|
||||
case Ret of
|
||||
{ok, Y} ->
|
||||
validate(Y);
|
||||
InstalledModules = maybe_install_contrib_modules(Y),
|
||||
ValResult = validate(Y),
|
||||
case InstalledModules of
|
||||
[] -> ok;
|
||||
_ -> spawn(fun() -> timer:sleep(5000), ?MODULE:reload() end)
|
||||
end,
|
||||
ValResult;
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
@ -527,6 +533,18 @@ read_erlang_file(File, _) ->
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec maybe_install_contrib_modules(term()) -> [atom()].
|
||||
maybe_install_contrib_modules(Options) ->
|
||||
case {lists:keysearch(allow_contrib_modules, 1, Options),
|
||||
lists:keysearch(install_contrib_modules, 1, Options)} of
|
||||
{Allow, {value, {_, InstallContribModules}}}
|
||||
when (Allow == false) or
|
||||
(Allow == {value, {allow_contrib_modules, true}}) ->
|
||||
ext_mod:install_contrib_modules(InstallContribModules, Options);
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec validate(term()) -> {ok, [{atom(), term()}]} | error_return().
|
||||
validate(Y1) ->
|
||||
case pre_validate(Y1) of
|
||||
|
@ -52,6 +52,7 @@
|
||||
-export([host_config/0]).
|
||||
-export([hosts/0]).
|
||||
-export([include_config_file/0, include_config_file/1]).
|
||||
-export([install_contrib_modules/0]).
|
||||
-export([jwt_auth_only_rule/0, jwt_auth_only_rule/1]).
|
||||
-export([jwt_jid_field/0, jwt_jid_field/1]).
|
||||
-export([jwt_key/0, jwt_key/1]).
|
||||
@ -449,6 +450,10 @@ include_config_file() ->
|
||||
include_config_file(Host) ->
|
||||
ejabberd_config:get_option({include_config_file, Host}).
|
||||
|
||||
-spec install_contrib_modules() -> [atom()].
|
||||
install_contrib_modules() ->
|
||||
ejabberd_config:get_option({install_contrib_modules, global}).
|
||||
|
||||
-spec jwt_auth_only_rule() -> atom().
|
||||
jwt_auth_only_rule() ->
|
||||
jwt_auth_only_rule(global).
|
||||
|
@ -183,6 +183,8 @@ opt_type(hosts) ->
|
||||
econf:non_empty(econf:list(econf:domain(), [unique]));
|
||||
opt_type(include_config_file) ->
|
||||
econf:any();
|
||||
opt_type(install_contrib_modules) ->
|
||||
econf:list(econf:atom());
|
||||
opt_type(language) ->
|
||||
econf:lang();
|
||||
opt_type(ldap_backups) ->
|
||||
@ -517,6 +519,7 @@ options() ->
|
||||
{access_rules, []},
|
||||
{acme, #{}},
|
||||
{allow_contrib_modules, true},
|
||||
{install_contrib_modules, []},
|
||||
{allow_multiple_connections, false},
|
||||
{anonymous_protocol, sasl_anon},
|
||||
{api_permissions,
|
||||
@ -736,6 +739,7 @@ globals() ->
|
||||
fqdn,
|
||||
hosts,
|
||||
host_config,
|
||||
install_contrib_modules,
|
||||
listen,
|
||||
loglevel,
|
||||
log_rotate_count,
|
||||
|
@ -303,6 +303,8 @@ doc() ->
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Whether to allow installation of third-party modules or not. "
|
||||
"See https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib"
|
||||
"[ejabberd-contrib] documentation section. "
|
||||
"The default value is 'true'.")}},
|
||||
{allow_multiple_connections,
|
||||
#{value => "true | false",
|
||||
@ -670,6 +672,13 @@ doc() ->
|
||||
"file 'Filename'. The options that do not match this "
|
||||
"criteria are not accepted. The default value is to include "
|
||||
"all options.")}}]},
|
||||
{install_contrib_modules,
|
||||
#{value => "[Module, ...]",
|
||||
desc =>
|
||||
?T("Modules to install from "
|
||||
"https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib"
|
||||
"[ejabberd-contrib] at start time. "
|
||||
"The default value is an empty list of modules: '[]'.")}},
|
||||
{jwt_auth_only_rule,
|
||||
#{value => ?T("AccessName"),
|
||||
desc =>
|
||||
|
@ -33,6 +33,7 @@
|
||||
installed_command/0, installed/0, installed/1,
|
||||
install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0,
|
||||
add_sources/1, add_sources/2, del_sources/1, modules_dir/0,
|
||||
install_contrib_modules/2,
|
||||
config_dir/0, get_commands_spec/0]).
|
||||
-export([modules_configs/0, module_ebin_dir/1]).
|
||||
-export([compile_erlang_file/2, compile_elixir_file/2]).
|
||||
@ -215,10 +216,13 @@ installed_command() ->
|
||||
[short_spec(Item) || Item <- installed()].
|
||||
|
||||
install(Module) when is_atom(Module) ->
|
||||
install(misc:atom_to_binary(Module));
|
||||
install(misc:atom_to_binary(Module), undefined);
|
||||
install(Package) when is_binary(Package) ->
|
||||
install(Package, undefined).
|
||||
|
||||
install(Package, Config) when is_binary(Package) ->
|
||||
Spec = [S || {Mod, S} <- available(), misc:atom_to_binary(Mod)==Package],
|
||||
case {Spec, installed(Package), is_contrib_allowed()} of
|
||||
case {Spec, installed(Package), is_contrib_allowed(Config)} of
|
||||
{_, _, false} ->
|
||||
{error, not_allowed};
|
||||
{[], _, _} ->
|
||||
@ -227,10 +231,10 @@ install(Package) when is_binary(Package) ->
|
||||
{error, conflict};
|
||||
{[Attrs], _, _} ->
|
||||
Module = misc:binary_to_atom(Package),
|
||||
case compile_and_install(Module, Attrs) of
|
||||
case compile_and_install(Module, Attrs, Config) of
|
||||
ok ->
|
||||
code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]),
|
||||
ejabberd_config:reload(),
|
||||
ejabberd_config_reload(Config),
|
||||
copy_commit_json(Package, Attrs),
|
||||
case erlang:function_exported(Module, post_install, 0) of
|
||||
true -> Module:post_install();
|
||||
@ -242,6 +246,14 @@ install(Package) when is_binary(Package) ->
|
||||
end
|
||||
end.
|
||||
|
||||
ejabberd_config_reload(Config) when is_list(Config) ->
|
||||
%% Don't reload config when ejabberd is starting
|
||||
%% because it will be reloaded after installing
|
||||
%% all the external modules from install_contrib_modules
|
||||
ok;
|
||||
ejabberd_config_reload(undefined) ->
|
||||
ejabberd_config:reload().
|
||||
|
||||
uninstall(Module) when is_atom(Module) ->
|
||||
uninstall(misc:atom_to_binary(Module));
|
||||
uninstall(Package) when is_binary(Package) ->
|
||||
@ -483,7 +495,13 @@ modules_spec(Dir, Path) ->
|
||||
short_spec({Module, Attrs}) when is_atom(Module), is_list(Attrs) ->
|
||||
{Module, proplists:get_value(summary, Attrs, "")}.
|
||||
|
||||
is_contrib_allowed() ->
|
||||
is_contrib_allowed(Config) when is_list(Config) ->
|
||||
case lists:keyfind(allow_contrib_modules, 1, Config) of
|
||||
false -> true;
|
||||
{_, false} -> false;
|
||||
{_, true} -> true
|
||||
end;
|
||||
is_contrib_allowed(undefined) ->
|
||||
ejabberd_option:allow_contrib_modules().
|
||||
|
||||
%% -- build functions
|
||||
@ -526,7 +544,7 @@ check_sources(Module) ->
|
||||
_ -> {error, Result}
|
||||
end.
|
||||
|
||||
compile_and_install(Module, Spec) ->
|
||||
compile_and_install(Module, Spec, Config) ->
|
||||
SrcDir = module_src_dir(Module),
|
||||
LibDir = module_lib_dir(Module),
|
||||
case filelib:is_dir(SrcDir) of
|
||||
@ -534,7 +552,7 @@ compile_and_install(Module, Spec) ->
|
||||
case compile_deps(SrcDir) of
|
||||
ok ->
|
||||
case compile(SrcDir) of
|
||||
ok -> install(Module, Spec, SrcDir, LibDir);
|
||||
ok -> install(Module, Spec, SrcDir, LibDir, Config);
|
||||
Error -> Error
|
||||
end;
|
||||
Error ->
|
||||
@ -543,7 +561,7 @@ compile_and_install(Module, Spec) ->
|
||||
false ->
|
||||
Path = proplists:get_value(url, Spec, ""),
|
||||
case add_sources(Module, Path) of
|
||||
ok -> compile_and_install(Module, Spec);
|
||||
ok -> compile_and_install(Module, Spec, Config);
|
||||
Error -> Error
|
||||
end
|
||||
end.
|
||||
@ -648,7 +666,7 @@ compile_elixir_file(_, File) ->
|
||||
{error, {compilation_failed, File}}.
|
||||
-endif.
|
||||
|
||||
install(Module, Spec, SrcDir, LibDir) ->
|
||||
install(Module, Spec, SrcDir, LibDir, Config) ->
|
||||
{ok, CurDir} = file:get_cwd(),
|
||||
file:set_cwd(SrcDir),
|
||||
Files1 = [{File, copy(File, filename:join(LibDir, File))}
|
||||
@ -660,7 +678,7 @@ install(Module, Spec, SrcDir, LibDir) ->
|
||||
Errors = lists:dropwhile(fun({_, ok}) -> true;
|
||||
(_) -> false
|
||||
end, Files1++Files2++Files3),
|
||||
inform_module_configuration(Module, LibDir, Files1),
|
||||
inform_module_configuration(Module, LibDir, Files1, Config),
|
||||
Result = case Errors of
|
||||
[{F, {error, E}}|_] ->
|
||||
{error, {F, E}};
|
||||
@ -672,11 +690,11 @@ install(Module, Spec, SrcDir, LibDir) ->
|
||||
file:set_cwd(CurDir),
|
||||
Result.
|
||||
|
||||
inform_module_configuration(Module, LibDir, Files1) ->
|
||||
inform_module_configuration(Module, LibDir, Files1, Config) ->
|
||||
Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true;
|
||||
(_) -> false
|
||||
end, Files1),
|
||||
AlreadyConfigured = lists:keymember(Module, 1, ejabberd_config:get_option(modules)),
|
||||
AlreadyConfigured = lists:keymember(Module, 1, get_modules(Config)),
|
||||
case {Res, AlreadyConfigured} of
|
||||
{[{ConfigPath, ok}], false} ->
|
||||
FullConfigPath = filename:join(LibDir, ConfigPath),
|
||||
@ -697,6 +715,12 @@ inform_module_configuration(Module, LibDir, Files1) ->
|
||||
[Module])
|
||||
end.
|
||||
|
||||
get_modules(Config) when is_list(Config) ->
|
||||
{modules, Modules} = lists:keyfind(modules, 1, Config),
|
||||
Modules;
|
||||
get_modules(undefined) ->
|
||||
ejabberd_config:get_option(modules).
|
||||
|
||||
%% -- minimalist rebar spec parser, only support git
|
||||
|
||||
fetch_rebar_deps(SrcDir) ->
|
||||
@ -1220,3 +1244,14 @@ list_modules_parse_uninstall(Query) ->
|
||||
end,
|
||||
installed()),
|
||||
ok.
|
||||
|
||||
install_contrib_modules(Modules, Config) ->
|
||||
lists:filter(fun(Module) ->
|
||||
case install(misc:atom_to_binary(Module), Config) of
|
||||
{error, conflict} ->
|
||||
false;
|
||||
ok ->
|
||||
true
|
||||
end
|
||||
end,
|
||||
Modules).
|
||||
|
Loading…
Reference in New Issue
Block a user