./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
42 -include("mongoose.hrl").
43
44 -spec start_link() -> 'ignore' | {'error', _} | {'ok', pid()}.
45 start_link() ->
46 80 supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []).
47
48
49 init(_) ->
50 %bind_tcp_ports(),
51 80 {ok, {{one_for_one, 10, 1}, []}}.
52
53
54 -spec start_listeners() -> {'ok', {{_, _, _}, [any()]}}.
55 start_listeners() ->
56 80 Ls = mongoose_config:get_opt(listen),
57 80 Ls2 = lists:map(
58 fun(Listener) ->
59 931 case start_listener(Listener) of
60 931 {ok, _Pid} = R -> R;
61 {error, Error} ->
62
:-(
throw(Error)
63 end
64 end, Ls),
65 80 {ok, {{one_for_one, 10, 1}, Ls2}}.
66
67 -spec start_listener(mongoose_listenet_config:listener()) -> {'error', pid()} | {'ok', _}.
68 start_listener(Opts = #{module := Module}) ->
69 980 try
70 %% It is only required to start the supervisor in some cases.
71 %% But it doesn't hurt to attempt to start it for any listener.
72 %% So, it's normal (and harmless) that in most cases this call returns:
73 %% {error, {already_started, pid()}}
74 980 start_module_sup(Module),
75 980 start_listener_sup(Module, Opts)
76 of
77 980 {ok, _Pid} = R -> R;
78 {error, {already_started, Pid}} ->
79
:-(
{ok, Pid};
80 {error, Reason} = R ->
81
:-(
?LOG_CRITICAL(#{what => listener_failed_to_start, reason => Reason,
82 text => <<"Failed to start a listener">>,
83
:-(
module => Module, opts => Opts}),
84
:-(
R
85 catch Class:Reason:Stacktrace ->
86
:-(
?LOG_CRITICAL(#{what => listener_failed_to_start,
87 text => <<"Failed to start a listener">>,
88 module => Module, opts => Opts,
89
:-(
class => Class, reason => Reason, stacktrace => Stacktrace}),
90
:-(
{error, Reason}
91 end.
92
93 -spec start_module_sup(Module :: module())
94 -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}.
95 start_module_sup(Module) ->
96 980 Proc = gen_mod:get_module_proc("sup", Module),
97 980 ChildSpec =
98 {Proc,
99 {ejabberd_tmp_sup, start_link, [Proc, Module]},
100 permanent,
101 infinity,
102 supervisor,
103 [ejabberd_tmp_sup]},
104 %% TODO Rewrite using ejabberd_sup:start_child
105 %% This function is called more than once
106 980 supervisor:start_child(ejabberd_sup, ChildSpec).
107
108 -spec start_listener_sup(module(), mongoose_listener_config:listener())
109 -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}.
110 start_listener_sup(Module, Listener = #{ip_address := IPAddr, port := Port, proto := Proto}) ->
111 980 ListenerId = mongoose_listener_config:listener_id(Listener),
112 980 Opts = mongoose_listener_config:prepare_opts(Listener),
113 980 case Module:socket_type() of
114 independent ->
115 564 Module:start_listener(ListenerId, Opts);
116 _ ->
117 416 SockOpts = mongoose_listener_config:filter_socket_opts(Opts),
118 416 {SupModule, Kill, Type} =
119 case Proto of
120
:-(
udp -> {mongoose_udp_listener, brutal_kill, worker};
121 416 _ -> {mongoose_tcp_listener, 1000, supervisor}
122 end,
123 416 ChildSpec = {ListenerId,
124 {SupModule, start_link,
125 [ListenerId, Module, Opts, SockOpts, Port, IPAddr]},
126 transient, Kill, Type, [SupModule]},
127 416 supervisor:start_child(ejabberd_listeners, ChildSpec)
128 end.
129
130 -spec stop_listeners() -> 'ok'.
131 stop_listeners() ->
132 80 Listeners = mongoose_config:get_opt(listen),
133 80 lists:foreach(fun stop_listener/1, Listeners).
134
135 -spec stop_listener(mongoose_listener_config:listener())
136 -> 'ok' | {'error', 'not_found' | 'restarting' | 'running' | 'simple_one_for_one'}.
137 stop_listener(Listener) ->
138 980 ListenerId = mongoose_listener_config:listener_id(Listener),
139 980 supervisor:terminate_child(ejabberd_listeners, ListenerId),
140 980 supervisor:delete_child(ejabberd_listeners, ListenerId).
141
142 %%%
143 %%% Check options
144 %%%
145
146 socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) ->
147
:-(
ReasonT = case Reason of
148 eaddrnotavail ->
149
:-(
"IP address not available: " ++ IPS;
150 eaddrinuse ->
151 "IP address and port number already used: "
152
:-(
++IPS++" "++integer_to_list(Port);
153 _ ->
154
:-(
format_error(Reason)
155 end,
156
:-(
?LOG_ERROR(#{what => failed_to_open_socket, reason => ReasonT,
157
:-(
port => Port, module => Module, socket_option => SockOpts}),
158
:-(
throw({Reason, PortIP}).
159
160 -spec format_error(atom()) -> string().
161 format_error(Reason) ->
162
:-(
case inet:format_error(Reason) of
163 "unknown POSIX error" ->
164
:-(
atom_to_list(Reason);
165 ReasonStr ->
166
:-(
ReasonStr
167 end.
Line Hits Source