diff options
Diffstat (limited to 'src/gen_mod.erl')
-rw-r--r-- | src/gen_mod.erl | 120 |
1 files changed, 69 insertions, 51 deletions
diff --git a/src/gen_mod.erl b/src/gen_mod.erl index a477ec295..3b63ed74a 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -32,8 +32,7 @@ -export([init/1, start_link/0, start_child/3, start_child/4, stop_child/1, stop_child/2, config_reloaded/0]). --export([start_module/2, start_module/3, - stop_module/2, stop_module_keep_config/2, +-export([start_module/2, stop_module/2, stop_module_keep_config/2, get_opt/2, get_opt_hosts/2, opt_type/1, is_equal_opt/3, get_module_opt/3, get_module_opt_host/3, loaded_modules/1, loaded_modules_with_opts/1, @@ -63,7 +62,8 @@ -record(ejabberd_module, {module_host = {undefined, <<"">>} :: {atom(), binary()}, - opts = [] :: opts() | '_' | '$2'}). + opts = [] :: opts() | '_' | '$2', + order = 0 :: integer()}). -type opts() :: [{atom(), any()}]. -type db_type() :: atom(). @@ -152,7 +152,7 @@ sort_modules(Host, ModOpts) -> [Mod, DepMod]), ?ERROR_MSG(ErrTxt, []), digraph:del_vertex(G, Mod), - maybe_halt_ejabberd(ErrTxt); + maybe_halt_ejabberd(); false when Type == soft -> ?WARNING_MSG("Module '~s' is recommended for " "module '~s' but is not found in " @@ -171,7 +171,11 @@ sort_modules(Host, ModOpts) -> end end, Deps) end, ModOpts), - Result = [digraph:vertex(G, V) || V <- digraph_utils:topsort(G)], + {Result, _} = lists:mapfoldl( + fun(V, Order) -> + {M, O} = digraph:vertex(G, V), + {{M, O, Order}, Order+1} + end, 1, digraph_utils:topsort(G)), digraph:delete(G), Result. @@ -180,8 +184,8 @@ sort_modules(Host, ModOpts) -> start_modules(Host) -> Modules = get_modules_options(Host), lists:foreach( - fun({Module, Opts}) -> - start_module(Host, Module, Opts) + fun({Module, Opts, Order}) -> + start_module(Host, Module, Opts, Order) end, Modules). -spec start_module(binary(), atom()) -> ok | {ok, pid()} | {error, not_found_in_config}. @@ -189,18 +193,18 @@ start_modules(Host) -> start_module(Host, Module) -> Modules = get_modules_options(Host), case lists:keyfind(Module, 1, Modules) of - {_, Opts} -> - start_module(Host, Module, Opts); + {_, Opts, Order} -> + start_module(Host, Module, Opts, Order); false -> {error, not_found_in_config} end. --spec start_module(binary(), atom(), opts()) -> ok | {ok, pid()}. -start_module(Host, Module, Opts) -> - start_module(Host, Module, Opts, true). +-spec start_module(binary(), atom(), opts(), integer()) -> ok | {ok, pid()}. +start_module(Host, Module, Opts, Order) -> + start_module(Host, Module, Opts, Order, true). --spec start_module(binary(), atom(), opts(), boolean()) -> ok | {ok, pid()}. -start_module(Host, Module, Opts0, NeedValidation) -> +-spec start_module(binary(), atom(), opts(), integer(), boolean()) -> ok | {ok, pid()}. +start_module(Host, Module, Opts0, Order, NeedValidation) -> ?DEBUG("Loading ~s at ~s", [Module, Host]), Res = if NeedValidation -> validate_opts(Host, Module, Opts0); @@ -209,7 +213,7 @@ start_module(Host, Module, Opts0, NeedValidation) -> end, case Res of {ok, Opts} -> - store_options(Host, Module, Opts), + store_options(Host, Module, Opts, Order), try case Module:start(Host, Opts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; @@ -236,22 +240,17 @@ start_module(Host, Module, Opts0, NeedValidation) -> erlang:get_stacktrace()]) end, ?CRITICAL_MSG(ErrorText, []), - maybe_halt_ejabberd(ErrorText), + maybe_halt_ejabberd(), erlang:raise(Class, Reason, erlang:get_stacktrace()) end; - {error, ErrorText} -> - maybe_halt_ejabberd(ErrorText) + {error, _ErrorText} -> + maybe_halt_ejabberd() end. -spec reload_modules(binary()) -> ok. reload_modules(Host) -> - NewMods = ejabberd_config:get_option({modules, Host}, []), - OldMods = ets:select( - ejabberd_modules, - ets:fun2ms( - fun(#ejabberd_module{module_host = {M, H}, opts = O}) - when H == Host -> {M, O} - end)), + NewMods = get_modules_options(Host), + OldMods = lists:reverse(loaded_modules_with_opts(Host)), lists:foreach( fun({Mod, _Opts}) -> case lists:keymember(Mod, 1, NewMods) of @@ -262,10 +261,10 @@ reload_modules(Host) -> end end, OldMods), lists:foreach( - fun({Mod, Opts}) -> + fun({Mod, Opts, Order}) -> case lists:keymember(Mod, 1, OldMods) of false -> - start_module(Host, Mod, Opts); + start_module(Host, Mod, Opts, Order); true -> ok end @@ -273,12 +272,12 @@ reload_modules(Host) -> lists:foreach( fun({Mod, OldOpts}) -> case lists:keyfind(Mod, 1, NewMods) of - {_, NewOpts0} -> + {_, NewOpts0, Order} -> case validate_opts(Host, Mod, NewOpts0) of {ok, OldOpts} -> ok; {ok, NewOpts} -> - reload_module(Host, Mod, NewOpts, OldOpts); + reload_module(Host, Mod, NewOpts, OldOpts, Order); {error, _} -> ok end; @@ -287,12 +286,12 @@ reload_modules(Host) -> end end, OldMods). --spec reload_module(binary(), module(), opts(), opts()) -> ok | {ok, pid()}. -reload_module(Host, Module, NewOpts, OldOpts) -> +-spec reload_module(binary(), module(), opts(), opts(), integer()) -> ok | {ok, pid()}. +reload_module(Host, Module, NewOpts, OldOpts, Order) -> case erlang:function_exported(Module, reload, 3) of true -> ?DEBUG("Reloading ~s at ~s", [Module, Host]), - store_options(Host, Module, NewOpts), + store_options(Host, Module, NewOpts, Order), try case Module:reload(Host, NewOpts, OldOpts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; @@ -310,23 +309,22 @@ reload_module(Host, Module, NewOpts, OldOpts) -> ?WARNING_MSG("Module ~s doesn't support reloading " "and will be restarted", [Module]), stop_module(Host, Module), - start_module(Host, Module, NewOpts, false) + start_module(Host, Module, NewOpts, Order, false) end. --spec store_options(binary(), module(), opts()) -> true. -store_options(Host, Module, Opts) -> +-spec store_options(binary(), module(), opts(), integer()) -> true. +store_options(Host, Module, Opts, Order) -> ets:insert(ejabberd_modules, #ejabberd_module{module_host = {Module, Host}, - opts = Opts}). + opts = Opts, order = Order}). -maybe_halt_ejabberd(ErrorText) -> +maybe_halt_ejabberd() -> case is_app_running(ejabberd) of false -> ?CRITICAL_MSG("ejabberd initialization was aborted " "because a module start failed.", []), - timer:sleep(3000), - erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199)); + ejabberd:halt(); true -> ok end. @@ -347,7 +345,7 @@ stop_modules() -> -spec stop_modules(binary()) -> ok. stop_modules(Host) -> - Modules = lists:reverse(get_modules_options(Host)), + Modules = lists:reverse(loaded_modules_with_opts(Host)), lists:foreach( fun({Module, _Args}) -> stop_module_keep_config(Host, Module) @@ -555,7 +553,8 @@ validate_opts(Host, Module, Opts0) -> undef -> Opts; Validators -> - validate_opts(Host, Module, Opts, Required, Validators) + Opts1 = validate_opts(Host, Module, Opts, Required, Validators), + remove_duplicated_opts(Opts1) end} catch _:{missing_required_option, Opt} -> ErrTxt = io_lib:format("Module '~s' is missing required option '~s'", @@ -680,6 +679,16 @@ merge_opts(Opts, DefaultOpts) -> end end, Result, Opts). +remove_duplicated_opts([{Opt, Val}, {Opt, _Default}|Opts]) -> + [{Opt, Val}|remove_duplicated_opts(Opts)]; +remove_duplicated_opts([{Opt, [{SubOpt, _}|_] = SubOpts}|Opts]) + when is_atom(SubOpt) -> + [{Opt, remove_duplicated_opts(SubOpts)}|remove_duplicated_opts(Opts)]; +remove_duplicated_opts([OptVal|Opts]) -> + [OptVal|remove_duplicated_opts(Opts)]; +remove_duplicated_opts([]) -> + []. + -spec get_submodules(binary(), module(), opts()) -> [module()]. get_submodules(Host, Module, Opts) -> try Module:mod_options(Host) of @@ -788,17 +797,26 @@ is_db_configured(Type, Host) -> -spec loaded_modules(binary()) -> [atom()]. loaded_modules(Host) -> - ets:select(ejabberd_modules, - [{#ejabberd_module{_ = '_', module_host = {'$1', Host}}, - [], ['$1']}]). + Mods = ets:select( + ejabberd_modules, + ets:fun2ms( + fun(#ejabberd_module{module_host = {Mod, H}, + order = Order}) when H == Host -> + {Mod, Order} + end)), + [Mod || {Mod, _} <- lists:keysort(2, Mods)]. -spec loaded_modules_with_opts(binary()) -> [{atom(), opts()}]. loaded_modules_with_opts(Host) -> - ets:select(ejabberd_modules, - [{#ejabberd_module{_ = '_', module_host = {'$1', Host}, - opts = '$2'}, - [], [{{'$1', '$2'}}]}]). + Mods = ets:select( + ejabberd_modules, + ets:fun2ms( + fun(#ejabberd_module{module_host = {Mod, H}, opts = Opts, + order = Order}) when H == Host -> + {Mod, Opts, Order} + end)), + [{Mod, Opts} || {Mod, Opts, _} <- lists:keysort(3, Mods)]. -spec get_hosts(opts(), binary()) -> [binary()]. @@ -815,9 +833,9 @@ get_hosts(Opts, Prefix) -> Hosts end. --spec get_module_proc(binary(), {frontend, atom()} | atom()) -> atom(). -get_module_proc(Host, {frontend, Base}) -> - get_module_proc(<<"frontend_", Host/binary>>, Base); +-spec get_module_proc(binary() | global, atom()) -> atom(). +get_module_proc(global, Base) -> + get_module_proc(<<"global">>, Base); get_module_proc(Host, Base) -> binary_to_atom( <<(erlang:atom_to_binary(Base, latin1))/binary, "_", Host/binary>>, |