./ct_report/coverage/ejabberd_listener.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : ejabberd_listener.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : Manage socket listener
5 %%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(ejabberd_listener).
27 -author('alexey@process-one.net').
28
29 -export([start_link/0,
30 init/1,
31 start_listeners/0,
32 start_listener/1,
33 stop_listeners/0,
34 stop_listener/1
35 ]).
36
37 %% Internal
38 -export([format_error/1, socket_error/6]).
39
40 -ignore_xref([start_link/0, init/1, start_listener/1, stop_listener/1]).
41 -export_type([opts/0, mod/0]).
42
43 -type opts() :: list().
44 -type mod() :: module().
45
46 -include("mongoose.hrl").
47
48 -spec start_link() -> 'ignore' | {'error', _} | {'ok', pid()}.
49 start_link() ->
50 82 supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []).
51
52
53 init(_) ->
54 %bind_tcp_ports(),
55 82 {ok, {{one_for_one, 10, 1}, []}}.
56
57
58 -spec start_listeners() -> {'ok', {{_, _, _}, [any()]}}.
59 start_listeners() ->
60 82 Ls = mongoose_config:get_opt(listen),
61 82 Ls2 = lists:map(
62 fun(Listener) ->
63 957 case start_listener(Listener) of
64 957 {ok, _Pid} = R -> R;
65 {error, Error} ->
66
:-(
throw(Error)
67 end
68 end, Ls),
69 82 {ok, {{one_for_one, 10, 1}, Ls2}}.
70
71 -spec start_listener(mongoose_listenet_config:listener()) -> {'error', pid()} | {'ok', _}.
72 start_listener(Opts = #{module := Module}) ->
73 1006 try
74 %% It is only required to start the supervisor in some cases.
75 %% But it doesn't hurt to attempt to start it for any listener.
76 %% So, it's normal (and harmless) that in most cases this call returns:
77 %% {error, {already_started, pid()}}
78 1006 start_module_sup(Module),
79 1006 start_listener_sup(Module, Opts)
80 of
81 1006 {ok, _Pid} = R -> R;
82 {error, {already_started, Pid}} ->
83
:-(
{ok, Pid};
84 {error, Reason} = R ->
85
:-(
?LOG_CRITICAL(#{what => listener_failed_to_start, reason => Reason,
86 text => <<"Failed to start a listener">>,
87
:-(
module => Module, opts => Opts}),
88
:-(
R
89 catch Class:Reason:Stacktrace ->
90
:-(
?LOG_CRITICAL(#{what => listener_failed_to_start,
91 text => <<"Failed to start a listener">>,
92 module => Module, opts => Opts,
93
:-(
class => Class, reason => Reason, stacktrace => Stacktrace}),
94
:-(
{error, Reason}
95 end.
96
97 -spec start_module_sup(Module :: module())
98 -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}.
99 start_module_sup(Module) ->
100 1006 Proc = gen_mod:get_module_proc("sup", Module),
101 1006 ChildSpec =
102 {Proc,
103 {ejabberd_tmp_sup, start_link, [Proc, Module]},
104 permanent,
105 infinity,
106 supervisor,
107 [ejabberd_tmp_sup]},
108 %% TODO Rewrite using ejabberd_sup:start_child
109 %% This function is called more than once
110 1006 supervisor:start_child(ejabberd_sup, ChildSpec).
111
112 -spec start_listener_sup(module(), mongoose_listener_config:listener())
113 -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}.
114 start_listener_sup(Module, Listener = #{ip_address := IPAddr, port := Port, proto := Proto}) ->
115 1006 ListenerId = mongoose_listener_config:listener_id(Listener),
116 1006 Opts = mongoose_listener_config:prepare_opts(Listener),
117 1006 case Module:socket_type() of
118 independent ->
119 578 Module:start_listener(ListenerId, Opts);
120 _ ->
121 428 SockOpts = mongoose_listener_config:filter_socket_opts(Opts),
122 428 {SupModule, Kill, Type} =
123 case Proto of
124
:-(
udp -> {mongoose_udp_listener, brutal_kill, worker};
125 428 _ -> {mongoose_tcp_listener, 1000, supervisor}
126 end,
127 428 ChildSpec = {ListenerId,
128 {SupModule, start_link,
129 [ListenerId, Module, Opts, SockOpts, Port, IPAddr]},
130 transient, Kill, Type, [SupModule]},
131 428 supervisor:start_child(ejabberd_listeners, ChildSpec)
132 end.
133
134 -spec stop_listeners() -> 'ok'.
135 stop_listeners() ->
136 82 Listeners = mongoose_config:get_opt(listen),
137 82 lists:foreach(fun stop_listener/1, Listeners).
138
139 -spec stop_listener(mongoose_listener_config:listener())
140 -> 'ok' | {'error', 'not_found' | 'restarting' | 'running' | 'simple_one_for_one'}.
141 stop_listener(Listener) ->
142 1006 ListenerId = mongoose_listener_config:listener_id(Listener),
143 1006 supervisor:terminate_child(ejabberd_listeners, ListenerId),
144 1006 supervisor:delete_child(ejabberd_listeners, ListenerId).
145
146 %%%
147 %%% Check options
148 %%%
149
150 socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) ->
151
:-(
ReasonT = case Reason of
152 eaddrnotavail ->
153
:-(
"IP address not available: " ++ IPS;
154 eaddrinuse ->
155 "IP address and port number already used: "
156
:-(
++IPS++" "++integer_to_list(Port);
157 _ ->
158
:-(
format_error(Reason)
159 end,
160
:-(
?LOG_ERROR(#{what => failed_to_open_socket, reason => ReasonT,
161
:-(
port => Port, module => Module, socket_option => SockOpts}),
162
:-(
throw({Reason, PortIP}).
163
164 -spec format_error(atom()) -> string().
165 format_error(Reason) ->
166
:-(
case inet:format_error(Reason) of
167 "unknown POSIX error" ->
168
:-(
atom_to_list(Reason);
169 ReasonStr ->
170
:-(
ReasonStr
171 end.
Line Hits Source