27
28 -module(gen_mod).
29
30 -author('[email protected]').
31
32 -export([start/0, start_module/3, stop_module/2, stop_module_keep_config/2, get_opt/2,
33 get_opt/3, get_opt_host/3, get_module_opt/4, get_module_opt_host/3,
34 loaded_modules/1, loaded_modules_with_opts/1, get_hosts/2, get_module_proc/2,
35 get_module_proc_existing/2, is_loaded/2]).
36
37 -export([behaviour_info/1]).
38
39 -include("ejabberd.hrl").
40
41 -record(ejabberd_module, {module_host, opts}).
42
43
45
46 behaviour_info(callbacks) -> [{start, 2}, {stop, 1}];
47 behaviour_info(_Other) -> undefined.
48
49 start() ->
50 ets:new(ejabberd_modules,
51 [named_table, public, {keypos, #ejabberd_module.module_host}]),
52 ok.
53
54 start_module(Host, Module, Opts) ->
55 MTokens = string:tokens(atom_to_list(Module), "_"),
56 case lists:split(length(MTokens) - 1, MTokens) of
57 {ModulePlainList, ["odbc"]} ->
58 Module2 = list_to_atom(string:join(ModulePlainList, "_")),
59 ?WARNING_MSG("The module ~p is obsolete. Replace it with ~p and add the option {backend, odbc}",
60 [Module, Module2]),
61 start_module2(Host, Module2, [{backend, odbc} | Opts]);
62 _ -> start_module2(Host, Module, Opts)
63 end.
64
65 start_module2(Host, Module, Opts) ->
66 set_module_opts_mnesia(Host, Module, Opts),
67 ets:insert(ejabberd_modules,
68 #ejabberd_module{module_host = {Module, Host}, opts = Opts}),
69 try Module:start(Host, Opts) catch
70 Class:Reason ->
71 del_module_mnesia(Host, Module),
72 ets:delete(ejabberd_modules, {Module, Host}),
73 ErrorText =
74 io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p~n stacktarce: ~p",
75 [Module, Host, Opts, Class, Reason, erlang:get_stacktrace()]),
76 ?CRITICAL_MSG(ErrorText, []),
77 case is_app_running(ejabberd) of
78 true -> erlang:raise(Class, Reason, erlang:get_stacktrace());
79 false ->
80 ?CRITICAL_MSG("ejabberd initialization was aborted because a module start failed.",
81 []),
82 timer:sleep(3000),
83 erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199))
84 end
85 end.
86
87 is_app_running(AppName) ->
88
89 Timeout = 15000,
90 lists:keymember(AppName, 1, application:which_applications(Timeout)).
91
92
93 stop_module(Host, Module) ->
94 case stop_module_keep_config(Host, Module) of
95 error -> error;
96 ok -> del_module_mnesia(Host, Module)
97 end.
98
99
100
101
102
103
104 stop_module_keep_config(Host, Module) ->
105 case catch Module:stop(Host) of
106 {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), error;
107 {wait, ProcList} when is_list(ProcList) ->
108 lists:foreach(fun wait_for_process/1, ProcList),
109 ets:delete(ejabberd_modules, {Module, Host}),
110 ok;
111 {wait, Process} ->
112 wait_for_process(Process), ets:delete(ejabberd_modules, {Module, Host}), ok;
113 _ -> ets:delete(ejabberd_modules, {Module, Host}), ok
114 end.
115
116 wait_for_process(Process) ->
117 MonitorReference = erlang:monitor(process, Process),
118 wait_for_stop(Process, MonitorReference).
119
120 wait_for_stop(Process, MonitorReference) ->
121 receive
122 {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok
123 after 5000 -> catch exit(whereis(Process), kill), wait_for_stop1(MonitorReference)
124 end.
125
126 wait_for_stop1(MonitorReference) ->
127 receive {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> ok end.
128
129 get_opt(Opt, Opts) ->
130 case lists:keysearch(Opt, 1, Opts) of
131 false ->
132
133 throw({undefined_option, Opt});
134 {value, {_, Val}} -> Val
135 end.
136
137 get_opt(Opt, Opts, Default) ->
138 case lists:keysearch(Opt, 1, Opts) of
139 false -> Default;
140 {value, {_, Val}} -> Val
141 end.
142
143 get_module_opt(global, Module, Opt, Default) ->
144 Hosts = (?MYHOSTS),
145 [Value | Values] = lists:map(fun (Host) -> get_module_opt(Host, Module, Opt, Default)
146 end,
147 Hosts),
148 Same_all = lists:all(fun (Other_value) -> Other_value == Value end, Values),
149 case Same_all of
150 true -> Value;
151 false -> Default
152 end;
153 get_module_opt(Host, Module, Opt, Default) ->
154 OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
155 case OptsList of
156 [] ->
157 OptsList2 = ets:lookup(ejabberd_modules, {Module, global}),
158 case OptsList2 of
159 [] -> Default;
160 [#ejabberd_module{opts = Opts} | _] -> get_opt(Opt, Opts, Default)
161 end;
162 [#ejabberd_module{opts = Opts} | _] -> get_opt(Opt, Opts, Default)
163 end.
164
165 get_module_opt_host(Host, Module, Default) ->
166 Val = get_module_opt(Host, Module, host, Default),
167 re:replace(Val, "@HOST@", Host, [global, {return, list}]).
168
169 get_opt_host(Host, Opts, Default) ->
170 case Host of
171 global ->
172 Val = get_opt(host, Opts, Default),
173 {global, re:replace(Val, ".@HOST@", "", [global, {return, list}])};
174 Host ->
175 Val = get_opt(host, Opts, Default),
176 re:replace(Val, "@HOST@", Host, [global, {return, list}])
177 end.
178
179 loaded_modules(Host) ->
180 ets:select(ejabberd_modules,
181 [{#ejabberd_module{_ = '_', module_host = {'$1', Host}}, [], ['$1']}]).
182
183 loaded_modules_with_opts(Host) ->
184 ets:select(ejabberd_modules,
185 [{#ejabberd_module{_ = '_', module_host = {'$1', Host}, opts = '$2'}, [],
186 [{{'$1', '$2'}}]}]).
187
188 set_module_opts_mnesia(global, _Module, _Opts) ->
189
190 ok;
191 set_module_opts_mnesia(Host, Module, Opts) ->
192 Modules = case ejabberd_config:get_local_option({modules, Host}) of
193 undefined -> [];
194 Ls -> Ls
195 end,
196 Modules1 = lists:keydelete(Module, 1, Modules),
197 Modules2 = [{Module, Opts} | Modules1],
198 ejabberd_config:add_local_option({modules, Host}, Modules2).
199
200 del_module_mnesia(global, _Module) ->
201
202 ok;
203 del_module_mnesia(Host, Module) ->
204 Modules = case ejabberd_config:get_local_option({modules, Host}) of
205 undefined -> [];
206 Ls -> Ls
207 end,
208 Modules1 = lists:keydelete(Module, 1, Modules),
209 ejabberd_config:add_local_option({modules, Host}, Modules1).
210
211 get_hosts(Opts, Prefix) ->
212 case catch gen_mod:get_opt(hosts, Opts) of
213 {'EXIT', _Error1} ->
214 case catch gen_mod:get_opt(host, Opts) of
215 {'EXIT', _Error2} -> [Prefix ++ Host || Host <- ?MYHOSTS];
216 Host -> [Host]
217 end;
218 Hosts -> Hosts
219 end.
220
221 get_module_proc_existing(Host, Base) ->
222 Proc = get_module_proc(Host, Base),
223
224 case {whereis(Proc), Host == global} of
225 {undefined, false} -> get_module_proc(global, Base);
226 {undefined, true} -> not_existing;
227 {_, _} -> Proc
228 end.
229
230 get_module_proc(Host, Base) when is_binary(Host) ->
231 get_module_proc(binary_to_list(Host), Base);
232 get_module_proc(global, Base) -> list_to_atom(atom_to_list(Base) ++ "__global");
233 get_module_proc(Host, {frontend, Base}) -> get_module_proc("frontend_" ++ Host, Base);
234 get_module_proc(Host, Base) -> list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
235
236
237
238 is_loaded(Host, Module) ->
239 ets:member(ejabberd_modules, {Module, Host}) orelse
240 ets:member(ejabberd_modules, {Module, global}).