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,
|
end,
|
||||||
case Ret of
|
case Ret of
|
||||||
{ok, Y} ->
|
{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 ->
|
||||||
Err
|
Err
|
||||||
end.
|
end.
|
||||||
|
@ -527,6 +533,18 @@ read_erlang_file(File, _) ->
|
||||||
Err
|
Err
|
||||||
end.
|
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().
|
-spec validate(term()) -> {ok, [{atom(), term()}]} | error_return().
|
||||||
validate(Y1) ->
|
validate(Y1) ->
|
||||||
case pre_validate(Y1) of
|
case pre_validate(Y1) of
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
-export([host_config/0]).
|
-export([host_config/0]).
|
||||||
-export([hosts/0]).
|
-export([hosts/0]).
|
||||||
-export([include_config_file/0, include_config_file/1]).
|
-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_auth_only_rule/0, jwt_auth_only_rule/1]).
|
||||||
-export([jwt_jid_field/0, jwt_jid_field/1]).
|
-export([jwt_jid_field/0, jwt_jid_field/1]).
|
||||||
-export([jwt_key/0, jwt_key/1]).
|
-export([jwt_key/0, jwt_key/1]).
|
||||||
|
@ -449,6 +450,10 @@ include_config_file() ->
|
||||||
include_config_file(Host) ->
|
include_config_file(Host) ->
|
||||||
ejabberd_config:get_option({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().
|
-spec jwt_auth_only_rule() -> atom().
|
||||||
jwt_auth_only_rule() ->
|
jwt_auth_only_rule() ->
|
||||||
jwt_auth_only_rule(global).
|
jwt_auth_only_rule(global).
|
||||||
|
|
|
@ -183,6 +183,8 @@ opt_type(hosts) ->
|
||||||
econf:non_empty(econf:list(econf:domain(), [unique]));
|
econf:non_empty(econf:list(econf:domain(), [unique]));
|
||||||
opt_type(include_config_file) ->
|
opt_type(include_config_file) ->
|
||||||
econf:any();
|
econf:any();
|
||||||
|
opt_type(install_contrib_modules) ->
|
||||||
|
econf:list(econf:atom());
|
||||||
opt_type(language) ->
|
opt_type(language) ->
|
||||||
econf:lang();
|
econf:lang();
|
||||||
opt_type(ldap_backups) ->
|
opt_type(ldap_backups) ->
|
||||||
|
@ -517,6 +519,7 @@ options() ->
|
||||||
{access_rules, []},
|
{access_rules, []},
|
||||||
{acme, #{}},
|
{acme, #{}},
|
||||||
{allow_contrib_modules, true},
|
{allow_contrib_modules, true},
|
||||||
|
{install_contrib_modules, []},
|
||||||
{allow_multiple_connections, false},
|
{allow_multiple_connections, false},
|
||||||
{anonymous_protocol, sasl_anon},
|
{anonymous_protocol, sasl_anon},
|
||||||
{api_permissions,
|
{api_permissions,
|
||||||
|
@ -736,6 +739,7 @@ globals() ->
|
||||||
fqdn,
|
fqdn,
|
||||||
hosts,
|
hosts,
|
||||||
host_config,
|
host_config,
|
||||||
|
install_contrib_modules,
|
||||||
listen,
|
listen,
|
||||||
loglevel,
|
loglevel,
|
||||||
log_rotate_count,
|
log_rotate_count,
|
||||||
|
|
|
@ -303,6 +303,8 @@ doc() ->
|
||||||
#{value => "true | false",
|
#{value => "true | false",
|
||||||
desc =>
|
desc =>
|
||||||
?T("Whether to allow installation of third-party modules or not. "
|
?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'.")}},
|
"The default value is 'true'.")}},
|
||||||
{allow_multiple_connections,
|
{allow_multiple_connections,
|
||||||
#{value => "true | false",
|
#{value => "true | false",
|
||||||
|
@ -670,6 +672,13 @@ doc() ->
|
||||||
"file 'Filename'. The options that do not match this "
|
"file 'Filename'. The options that do not match this "
|
||||||
"criteria are not accepted. The default value is to include "
|
"criteria are not accepted. The default value is to include "
|
||||||
"all options.")}}]},
|
"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,
|
{jwt_auth_only_rule,
|
||||||
#{value => ?T("AccessName"),
|
#{value => ?T("AccessName"),
|
||||||
desc =>
|
desc =>
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
installed_command/0, installed/0, installed/1,
|
installed_command/0, installed/0, installed/1,
|
||||||
install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0,
|
install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0,
|
||||||
add_sources/1, add_sources/2, del_sources/1, modules_dir/0,
|
add_sources/1, add_sources/2, del_sources/1, modules_dir/0,
|
||||||
|
install_contrib_modules/2,
|
||||||
config_dir/0, get_commands_spec/0]).
|
config_dir/0, get_commands_spec/0]).
|
||||||
-export([modules_configs/0, module_ebin_dir/1]).
|
-export([modules_configs/0, module_ebin_dir/1]).
|
||||||
-export([compile_erlang_file/2, compile_elixir_file/2]).
|
-export([compile_erlang_file/2, compile_elixir_file/2]).
|
||||||
|
@ -215,10 +216,13 @@ installed_command() ->
|
||||||
[short_spec(Item) || Item <- installed()].
|
[short_spec(Item) || Item <- installed()].
|
||||||
|
|
||||||
install(Module) when is_atom(Module) ->
|
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) 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],
|
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} ->
|
{_, _, false} ->
|
||||||
{error, not_allowed};
|
{error, not_allowed};
|
||||||
{[], _, _} ->
|
{[], _, _} ->
|
||||||
|
@ -227,10 +231,10 @@ install(Package) when is_binary(Package) ->
|
||||||
{error, conflict};
|
{error, conflict};
|
||||||
{[Attrs], _, _} ->
|
{[Attrs], _, _} ->
|
||||||
Module = misc:binary_to_atom(Package),
|
Module = misc:binary_to_atom(Package),
|
||||||
case compile_and_install(Module, Attrs) of
|
case compile_and_install(Module, Attrs, Config) of
|
||||||
ok ->
|
ok ->
|
||||||
code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]),
|
code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]),
|
||||||
ejabberd_config:reload(),
|
ejabberd_config_reload(Config),
|
||||||
copy_commit_json(Package, Attrs),
|
copy_commit_json(Package, Attrs),
|
||||||
case erlang:function_exported(Module, post_install, 0) of
|
case erlang:function_exported(Module, post_install, 0) of
|
||||||
true -> Module:post_install();
|
true -> Module:post_install();
|
||||||
|
@ -242,6 +246,14 @@ install(Package) when is_binary(Package) ->
|
||||||
end
|
end
|
||||||
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(Module) when is_atom(Module) ->
|
||||||
uninstall(misc:atom_to_binary(Module));
|
uninstall(misc:atom_to_binary(Module));
|
||||||
uninstall(Package) when is_binary(Package) ->
|
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) ->
|
short_spec({Module, Attrs}) when is_atom(Module), is_list(Attrs) ->
|
||||||
{Module, proplists:get_value(summary, 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().
|
ejabberd_option:allow_contrib_modules().
|
||||||
|
|
||||||
%% -- build functions
|
%% -- build functions
|
||||||
|
@ -526,7 +544,7 @@ check_sources(Module) ->
|
||||||
_ -> {error, Result}
|
_ -> {error, Result}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
compile_and_install(Module, Spec) ->
|
compile_and_install(Module, Spec, Config) ->
|
||||||
SrcDir = module_src_dir(Module),
|
SrcDir = module_src_dir(Module),
|
||||||
LibDir = module_lib_dir(Module),
|
LibDir = module_lib_dir(Module),
|
||||||
case filelib:is_dir(SrcDir) of
|
case filelib:is_dir(SrcDir) of
|
||||||
|
@ -534,7 +552,7 @@ compile_and_install(Module, Spec) ->
|
||||||
case compile_deps(SrcDir) of
|
case compile_deps(SrcDir) of
|
||||||
ok ->
|
ok ->
|
||||||
case compile(SrcDir) of
|
case compile(SrcDir) of
|
||||||
ok -> install(Module, Spec, SrcDir, LibDir);
|
ok -> install(Module, Spec, SrcDir, LibDir, Config);
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
Error ->
|
Error ->
|
||||||
|
@ -543,7 +561,7 @@ compile_and_install(Module, Spec) ->
|
||||||
false ->
|
false ->
|
||||||
Path = proplists:get_value(url, Spec, ""),
|
Path = proplists:get_value(url, Spec, ""),
|
||||||
case add_sources(Module, Path) of
|
case add_sources(Module, Path) of
|
||||||
ok -> compile_and_install(Module, Spec);
|
ok -> compile_and_install(Module, Spec, Config);
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
@ -648,7 +666,7 @@ compile_elixir_file(_, File) ->
|
||||||
{error, {compilation_failed, File}}.
|
{error, {compilation_failed, File}}.
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
install(Module, Spec, SrcDir, LibDir) ->
|
install(Module, Spec, SrcDir, LibDir, Config) ->
|
||||||
{ok, CurDir} = file:get_cwd(),
|
{ok, CurDir} = file:get_cwd(),
|
||||||
file:set_cwd(SrcDir),
|
file:set_cwd(SrcDir),
|
||||||
Files1 = [{File, copy(File, filename:join(LibDir, File))}
|
Files1 = [{File, copy(File, filename:join(LibDir, File))}
|
||||||
|
@ -660,7 +678,7 @@ install(Module, Spec, SrcDir, LibDir) ->
|
||||||
Errors = lists:dropwhile(fun({_, ok}) -> true;
|
Errors = lists:dropwhile(fun({_, ok}) -> true;
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end, Files1++Files2++Files3),
|
end, Files1++Files2++Files3),
|
||||||
inform_module_configuration(Module, LibDir, Files1),
|
inform_module_configuration(Module, LibDir, Files1, Config),
|
||||||
Result = case Errors of
|
Result = case Errors of
|
||||||
[{F, {error, E}}|_] ->
|
[{F, {error, E}}|_] ->
|
||||||
{error, {F, E}};
|
{error, {F, E}};
|
||||||
|
@ -672,11 +690,11 @@ install(Module, Spec, SrcDir, LibDir) ->
|
||||||
file:set_cwd(CurDir),
|
file:set_cwd(CurDir),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
inform_module_configuration(Module, LibDir, Files1) ->
|
inform_module_configuration(Module, LibDir, Files1, Config) ->
|
||||||
Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true;
|
Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true;
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end, Files1),
|
end, Files1),
|
||||||
AlreadyConfigured = lists:keymember(Module, 1, ejabberd_config:get_option(modules)),
|
AlreadyConfigured = lists:keymember(Module, 1, get_modules(Config)),
|
||||||
case {Res, AlreadyConfigured} of
|
case {Res, AlreadyConfigured} of
|
||||||
{[{ConfigPath, ok}], false} ->
|
{[{ConfigPath, ok}], false} ->
|
||||||
FullConfigPath = filename:join(LibDir, ConfigPath),
|
FullConfigPath = filename:join(LibDir, ConfigPath),
|
||||||
|
@ -697,6 +715,12 @@ inform_module_configuration(Module, LibDir, Files1) ->
|
||||||
[Module])
|
[Module])
|
||||||
end.
|
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
|
%% -- minimalist rebar spec parser, only support git
|
||||||
|
|
||||||
fetch_rebar_deps(SrcDir) ->
|
fetch_rebar_deps(SrcDir) ->
|
||||||
|
@ -1220,3 +1244,14 @@ list_modules_parse_uninstall(Query) ->
|
||||||
end,
|
end,
|
||||||
installed()),
|
installed()),
|
||||||
ok.
|
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