./ct_report/coverage/mongoose_domain_core.COVER.html

1 %% Generally, you should not call anything from this module.
2 %% Use mongoose_domain_api module instead.
3 -module(mongoose_domain_core).
4 -behaviour(gen_server).
5
6 -include("mongoose_logger.hrl").
7
8 %% required for ets:fun2ms/1 pseudo function
9 -include_lib("stdlib/include/ms_transform.hrl").
10
11 -export([start/0, start/2, stop/0]).
12 -export([start_link/2]).
13 -export([get_host_type/1]).
14 -export([is_static/1]).
15
16 %% API, used by DB module
17 -export([insert/3,
18 delete/1]).
19
20 -export([get_all_static/0,
21 get_all_dynamic/0,
22 get_all_outdated/1,
23 get_domains_by_host_type/1,
24 domains_count/0]).
25
26 -export([for_each_domain/2]).
27
28 -export([is_host_type_allowed/1]).
29
30 %% For testing
31 -export([get_start_args/0]).
32
33 %% gen_server callbacks
34 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
35 terminate/2, code_change/3]).
36
37 -ignore_xref([get_start_args/0, start_link/2, start/2, stop/0]).
38
39 -define(TABLE, ?MODULE).
40 -define(HOST_TYPE_TABLE, mongoose_domain_core_host_types).
41
42 -type host_type() :: mongooseim:host_type().
43 -type domain() :: mongooseim:domain_name().
44 -type pair() :: {domain(), host_type()}.
45
46 start() ->
47 79 Pairs = get_static_pairs(),
48 79 AllowedHostTypes = mongoose_config:get_opt(host_types),
49 79 start(Pairs, AllowedHostTypes).
50
51 -ifdef(TEST).
52
53 %% required for unit tests
54 start(Pairs, AllowedHostTypes) ->
55 just_ok(gen_server:start({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], [])).
56
57 stop() ->
58 gen_server:stop(?MODULE).
59
60 -else.
61
62 start(Pairs, AllowedHostTypes) ->
63 344 ChildSpec =
64 {?MODULE,
65 {?MODULE, start_link, [Pairs, AllowedHostTypes]},
66 permanent, infinity, worker, [?MODULE]},
67 344 just_ok(supervisor:start_child(ejabberd_sup, ChildSpec)).
68
69 %% required for integration tests
70 stop() ->
71 268 supervisor:terminate_child(ejabberd_sup, ?MODULE),
72 268 supervisor:delete_child(ejabberd_sup, ?MODULE),
73 268 ok.
74
75 -endif.
76
77 start_link(Pairs, AllowedHostTypes) ->
78 344 gen_server:start_link({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], []).
79
80 get_host_type(Domain) ->
81 72385 case ets:lookup(?TABLE, Domain) of
82 [] ->
83 3917 {error, not_found};
84 [{_Domain, HostType, _Source}] ->
85 68468 {ok, HostType}
86 end.
87
88 is_static(Domain) ->
89 213 case ets:lookup(?TABLE, Domain) of
90 [{_Domain, _HostType, _Source = config}] ->
91 14 true;
92 _ ->
93 199 false
94 end.
95
96 is_host_type_allowed(HostType) ->
97 568 ets:member(?HOST_TYPE_TABLE, HostType).
98
99 get_all_static() ->
100 2 pairs(ets:match(?TABLE, {'$1', '$2', config})).
101
102 get_all_dynamic() ->
103 1 pairs(ets:match(?TABLE, {'$1', '$2', {dynamic, '_'}})).
104
105 get_domains_by_host_type(HostType) when is_binary(HostType) ->
106 33 heads(ets:match(?TABLE, {'$1', HostType, '_'})).
107
108 domains_count() ->
109 33 ets:info(?TABLE, size).
110
111 -spec for_each_domain(host_type(), fun((host_type(), domain())-> any())) -> ok.
112 for_each_domain(HostType, Func) ->
113 390 ets:safe_fixtable(?TABLE, true),
114 390 MS = ets:fun2ms(fun({Domain, HT, _}) when HT =:= HostType ->
115 [HostType, Domain]
116 end),
117 390 Selection = ets:select(?TABLE, MS, 100),
118 390 for_each_selected_domain(Selection, Func),
119 390 ets:safe_fixtable(?TABLE, false),
120 390 ok.
121
122 get_all_outdated(CurrentSource) ->
123 1 MS = ets:fun2ms(fun({Domain, HostType, {dynamic, Src}}) when Src =/= CurrentSource ->
124 {Domain, HostType}
125 end),
126 1 ets:select(?TABLE, MS).
127
128 heads(List) ->
129 33 [H || [H|_] <- List].
130
131 pairs(List) ->
132 3 [{K, V} || [K, V] <- List].
133
134 insert(Domain, HostType, Source) ->
135 410 gen_server:call(?MODULE, {insert, Domain, HostType, Source}).
136
137 delete(Domain) ->
138 84 gen_server:call(?MODULE, {delete, Domain}).
139
140 get_start_args() ->
141
:-(
gen_server:call(?MODULE, get_start_args).
142
143 %%--------------------------------------------------------------------
144 %% gen_server callbacks
145 %%--------------------------------------------------------------------
146 init([Pairs, AllowedHostTypes]) ->
147 344 mongoose_loader_state:init(),
148 344 ets:new(?TABLE, [set, named_table, protected, {read_concurrency, true}]),
149 344 ets:new(?HOST_TYPE_TABLE, [set, named_table, protected, {read_concurrency, true}]),
150 344 insert_host_types(?HOST_TYPE_TABLE, AllowedHostTypes),
151 344 insert_initial(?TABLE, Pairs),
152 344 {ok, #{initial_pairs => Pairs,
153 initial_host_types => AllowedHostTypes}}.
154
155 handle_call({delete, Domain}, _From, State) ->
156 84 Result = handle_delete(Domain),
157 84 {reply, Result, State};
158 handle_call({insert, Domain, HostType, Source}, _From, State) ->
159 410 Result = handle_insert(Domain, HostType, Source),
160 410 {reply, Result, State};
161 handle_call(get_start_args, _From, State = #{initial_pairs := Pairs,
162 initial_host_types := AllowedHostTypes}) ->
163
:-(
{reply, [Pairs, AllowedHostTypes], State};
164 handle_call(Request, From, State) ->
165
:-(
?UNEXPECTED_CALL(Request, From),
166
:-(
{reply, ok, State}.
167
168 handle_cast(Msg, State) ->
169
:-(
?UNEXPECTED_CAST(Msg),
170
:-(
{noreply, State}.
171
172 handle_info(Info, State) ->
173
:-(
?UNEXPECTED_INFO(Info),
174
:-(
{noreply, State}.
175
176 terminate(_Reason, _State) ->
177
:-(
ok.
178
179 code_change(_OldVsn, State, _Extra) ->
180
:-(
{ok, State}.
181
182 %%--------------------------------------------------------------------
183 %% internal functions
184 %%--------------------------------------------------------------------
185 %% Domains should be nameprepped using `jid:nameprep'
186 -spec get_static_pairs() -> [pair()].
187 get_static_pairs() ->
188 79 [{H, H} || H <- mongoose_config:get_opt(hosts)].
189
190 390 for_each_selected_domain('$end_of_table', _) -> ok;
191 for_each_selected_domain({MatchList, Continuation}, Func) ->
192 387 [safely:apply_and_log(Func, Args, log_context(Args)) || Args <- MatchList],
193 387 Selection = ets:select(Continuation),
194 387 for_each_selected_domain(Selection, Func).
195
196 log_context(Args) ->
197 546 #{what => domain_operation_failed,
198 args => Args}.
199
200 insert_initial(Tab, Pairs) ->
201 344 lists:foreach(fun({Domain, HostType}) ->
202 631 insert_initial_pair(Tab, Domain, HostType)
203 end, Pairs).
204
205 insert_initial_pair(Tab, Domain, HostType) ->
206 631 ets:insert_new(Tab, new_object(Domain, HostType, config)).
207
208 new_object(Domain, HostType, Source) ->
209 971 {Domain, HostType, Source}.
210
211 344 just_ok({ok, _}) -> ok;
212
:-(
just_ok(Other) -> Other.
213
214 insert_host_types(Tab, AllowedHostTypes) ->
215 344 lists:foreach(fun(HostType) ->
216 1882 ets:insert_new(Tab, {HostType})
217 end, AllowedHostTypes),
218 344 ok.
219
220 handle_delete(Domain) ->
221 84 case ets:lookup(?TABLE, Domain) of
222 [{Domain, _HostType, _Source = config}] ->
223 %% Ignore any static domains
224
:-(
?LOG_ERROR(#{what => domain_static_but_was_in_db, domain => Domain}),
225
:-(
{error, static};
226 [] ->
227 %% nothing to remove
228 16 ok;
229 [{Domain, HostType, _Source}] ->
230 68 ets:delete(?TABLE, Domain),
231 68 mongoose_lazy_routing:maybe_remove_domain(HostType, Domain),
232 68 mongoose_subdomain_core:remove_domain(HostType, Domain),
233 68 ok
234 end.
235
236 handle_insert(Domain, HostType, Source) ->
237 410 case is_host_type_allowed(HostType) of
238 true ->
239 342 case ets:lookup(?TABLE, Domain) of
240 [{Domain, _HostType, _Source = config}] ->
241 %% Ignore any static domains
242 1 ?LOG_ERROR(#{what => domain_static_but_in_db, domain => Domain}),
243 1 {error, static};
244 [] ->
245 335 ets:insert_new(?TABLE, new_object(Domain, HostType, {dynamic, Source})),
246 335 mongoose_subdomain_core:add_domain(HostType, Domain),
247 335 ok;
248 [{Domain, HT, _Source}] when HT =:= HostType ->
249 5 ets:insert(?TABLE, new_object(Domain, HostType, {dynamic, Source})),
250 5 ok;
251 [{Domain, HT, _Source}] when HT =/= HostType ->
252 1 ?LOG_ERROR(#{what => ignore_domain_from_db_with_different_host_type,
253 domain => Domain,
254 core_host_type => HT,
255
:-(
db_host_type => HostType}),
256 1 {error, bad_insert}
257 end;
258 false ->
259 68 ?LOG_ERROR(#{what => ignore_domain_from_db_with_unknown_host_type,
260
:-(
domain => Domain, host_type => HostType}),
261 68 {error, unknown_host_type}
262
263 end.
264
Line Hits Source