./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/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, 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
45 -ifdef(TEST).
46
47 %% required for unit tests
48 start(Pairs, AllowedHostTypes) ->
49 just_ok(gen_server:start({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], [])).
50
51 stop() ->
52 gen_server:stop(?MODULE).
53
54 -else.
55
56 start(Pairs, AllowedHostTypes) ->
57 329 ChildSpec =
58 {?MODULE,
59 {?MODULE, start_link, [Pairs, AllowedHostTypes]},
60 permanent, infinity, worker, [?MODULE]},
61 329 just_ok(supervisor:start_child(ejabberd_sup, ChildSpec)).
62
63 %% required for integration tests
64 stop() ->
65 256 supervisor:terminate_child(ejabberd_sup, ?MODULE),
66 256 supervisor:delete_child(ejabberd_sup, ?MODULE),
67 256 ok.
68
69 -endif.
70
71 start_link(Pairs, AllowedHostTypes) ->
72 329 gen_server:start_link({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], []).
73
74 get_host_type(Domain) ->
75 64425 case ets:lookup(?TABLE, Domain) of
76 [] ->
77 3679 {error, not_found};
78 [{_Domain, HostType, _Source}] ->
79 60746 {ok, HostType}
80 end.
81
82 is_static(Domain) ->
83 177 case ets:lookup(?TABLE, Domain) of
84 [{_Domain, _HostType, _Source = config}] ->
85 14 true;
86 _ ->
87 163 false
88 end.
89
90 is_host_type_allowed(HostType) ->
91 544 ets:member(?HOST_TYPE_TABLE, HostType).
92
93 get_all_static() ->
94 2 pairs(ets:match(?TABLE, {'$1', '$2', config})).
95
96 get_all_dynamic() ->
97 1 pairs(ets:match(?TABLE, {'$1', '$2', {dynamic, '_'}})).
98
99 get_domains_by_host_type(HostType) when is_binary(HostType) ->
100 33 heads(ets:match(?TABLE, {'$1', HostType, '_'})).
101
102 domains_count() ->
103 39 ets:info(?TABLE, size).
104
105 -spec for_each_domain(host_type(), fun((host_type(), domain())-> any())) -> ok.
106 for_each_domain(HostType, Func) ->
107 381 ets:safe_fixtable(?TABLE, true),
108 381 MS = ets:fun2ms(fun({Domain, HT, _}) when HT =:= HostType ->
109 [HostType, Domain]
110 end),
111 381 Selection = ets:select(?TABLE, MS, 100),
112 381 for_each_selected_domain(Selection, Func),
113 381 ets:safe_fixtable(?TABLE, false),
114 381 ok.
115
116 get_all_outdated(CurrentSource) ->
117 1 MS = ets:fun2ms(fun({Domain, HostType, {dynamic, Src}}) when Src =/= CurrentSource ->
118 {Domain, HostType}
119 end),
120 1 ets:select(?TABLE, MS).
121
122 heads(List) ->
123 33 [H || [H|_] <- List].
124
125 pairs(List) ->
126 3 [{K, V} || [K, V] <- List].
127
128 insert(Domain, HostType, Source) ->
129 389 gen_server:call(?MODULE, {insert, Domain, HostType, Source}).
130
131 delete(Domain) ->
132 84 gen_server:call(?MODULE, {delete, Domain}).
133
134 get_start_args() ->
135 3 gen_server:call(?MODULE, get_start_args).
136
137 %%--------------------------------------------------------------------
138 %% gen_server callbacks
139 %%--------------------------------------------------------------------
140 init([Pairs, AllowedHostTypes]) ->
141 329 mongoose_loader_state:init(),
142 329 ets:new(?TABLE, [set, named_table, protected, {read_concurrency, true}]),
143 329 ets:new(?HOST_TYPE_TABLE, [set, named_table, protected, {read_concurrency, true}]),
144 329 insert_host_types(?HOST_TYPE_TABLE, AllowedHostTypes),
145 329 insert_initial(?TABLE, Pairs),
146 329 {ok, #{initial_pairs => Pairs,
147 initial_host_types => AllowedHostTypes}}.
148
149 handle_call({delete, Domain}, _From, State) ->
150 84 Result = handle_delete(Domain),
151 84 {reply, Result, State};
152 handle_call({insert, Domain, HostType, Source}, _From, State) ->
153 389 Result = handle_insert(Domain, HostType, Source),
154 389 {reply, Result, State};
155 handle_call(get_start_args, _From, State = #{initial_pairs := Pairs,
156 initial_host_types := AllowedHostTypes}) ->
157 3 {reply, [Pairs, AllowedHostTypes], State};
158 handle_call(Request, From, State) ->
159
:-(
?UNEXPECTED_CALL(Request, From),
160
:-(
{reply, ok, State}.
161
162 handle_cast(Msg, State) ->
163
:-(
?UNEXPECTED_CAST(Msg),
164
:-(
{noreply, State}.
165
166 handle_info(Info, State) ->
167
:-(
?UNEXPECTED_INFO(Info),
168
:-(
{noreply, State}.
169
170 terminate(_Reason, _State) ->
171
:-(
ok.
172
173 code_change(_OldVsn, State, _Extra) ->
174
:-(
{ok, State}.
175
176 %%--------------------------------------------------------------------
177 %% internal functions
178 %%--------------------------------------------------------------------
179 381 for_each_selected_domain('$end_of_table', _) -> ok;
180 for_each_selected_domain({MatchList, Continuation}, Func) ->
181 378 [safely:apply_and_log(Func, Args, log_context(Args)) || Args <- MatchList],
182 378 Selection = ets:select(Continuation),
183 378 for_each_selected_domain(Selection, Func).
184
185 log_context(Args) ->
186 537 #{what => domain_operation_failed,
187 args => Args}.
188
189 insert_initial(Tab, Pairs) ->
190 329 lists:foreach(fun({Domain, HostType}) ->
191 604 insert_initial_pair(Tab, Domain, HostType)
192 end, Pairs).
193
194 insert_initial_pair(Tab, Domain, HostType) ->
195 604 ets:insert_new(Tab, new_object(Domain, HostType, config)).
196
197 new_object(Domain, HostType, Source) ->
198 924 {Domain, HostType, Source}.
199
200 329 just_ok({ok, _}) -> ok;
201
:-(
just_ok(Other) -> Other.
202
203 insert_host_types(Tab, AllowedHostTypes) ->
204 329 lists:foreach(fun(HostType) ->
205 1796 ets:insert_new(Tab, {HostType})
206 end, AllowedHostTypes),
207 329 ok.
208
209 handle_delete(Domain) ->
210 84 case ets:lookup(?TABLE, Domain) of
211 [{Domain, _HostType, _Source = config}] ->
212 %% Ignore any static domains
213
:-(
?LOG_ERROR(#{what => domain_static_but_was_in_db, domain => Domain}),
214
:-(
{error, static};
215 [] ->
216 %% nothing to remove
217 20 ok;
218 [{Domain, HostType, _Source}] ->
219 64 ets:delete(?TABLE, Domain),
220 64 mongoose_lazy_routing:maybe_remove_domain(HostType, Domain),
221 64 mongoose_subdomain_core:remove_domain(HostType, Domain),
222 64 ok
223 end.
224
225 handle_insert(Domain, HostType, Source) ->
226 389 case is_host_type_allowed(HostType) of
227 true ->
228 322 case ets:lookup(?TABLE, Domain) of
229 [{Domain, _HostType, _Source = config}] ->
230 %% Ignore any static domains
231 1 ?LOG_ERROR(#{what => domain_static_but_in_db, domain => Domain}),
232 1 {error, static};
233 [] ->
234 316 ets:insert_new(?TABLE, new_object(Domain, HostType, {dynamic, Source})),
235 316 mongoose_subdomain_core:add_domain(HostType, Domain),
236 316 ok;
237 [{Domain, HT, _Source}] when HT =:= HostType ->
238 4 ets:insert(?TABLE, new_object(Domain, HostType, {dynamic, Source})),
239 4 ok;
240 [{Domain, HT, _Source}] when HT =/= HostType ->
241 1 ?LOG_ERROR(#{what => ignore_domain_from_db_with_different_host_type,
242 domain => Domain,
243 core_host_type => HT,
244
:-(
db_host_type => HostType}),
245 1 {error, bad_insert}
246 end;
247 false ->
248 67 ?LOG_ERROR(#{what => ignore_domain_from_db_with_unknown_host_type,
249
:-(
domain => Domain, host_type => HostType}),
250 67 {error, unknown_host_type}
251
252 end.
253
Line Hits Source