1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
%%
%% Copyright (C) 2014, Jaguar Land Rover
%%
%% This program is licensed under the terms and conditions of the
%% Mozilla Public License, version 2.0. The full text of the
%% Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
%%
%% Setup a listen socket and manage connections to remote parties.
%% Can also retrieve connections by peer address.
-module(listener).
-include_lib("lager/include/log.hrl").
-export([start_link/0,
add_listener/3,
remove_listener/2]).
-export([init/2, handle_call/3, handle_cast/2, handle_info/2]).
-export([terminate/2, sock_opts/0, new_connection/4]).
-behavior(gen_nb_server).
-define(TAB, dlink_tcp_listener_tab).
start_link() ->
create_ets(),
gen_nb_server:start_link({local, ?MODULE}, ?MODULE, []).
create_ets() ->
case ets:info(?TAB, name) of
undefined -> ets:new(?TAB, [public, named_table, set]);
_ -> ?TAB
end.
add_listener(IpAddr, Port, CompSpec) ->
gen_server:call(?MODULE, {add_listener, IpAddr, Port, CompSpec}).
remove_listener(IpAddr, Port) ->
gen_server:call(?MODULE, {remove_listener, IpAddr, Port}).
init([], State) ->
case ets_select(?TAB, [{ '_', [], ['$_'] }]) of
[] ->
{ok, State};
Addrs ->
lists:foldl(
fun({{_, _} = Addr}, Acc) ->
case gen_nb_server:add_listen_socket(Addr, Acc) of
{ok, Acc1} ->
Acc1;
_Error ->
ets_delete(?TAB, Addr),
Acc
end;
({cs, CS}, Acc) ->
gen_nb_server:store_cb_state(CS, Acc)
end, State, Addrs)
end.
handle_call({add_listener, IpAddr, Port, CompSpec}, _From, State) ->
ets_insert(?TAB, {cs, CompSpec}),
case gen_nb_server:add_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
ets_insert(?TAB, {{IpAddr,Port}}),
{reply, ok, gen_nb_server:store_cb_state( CompSpec, State1 )};
Error ->
{reply, Error, gen_nb_server:store_cb_state( CompSpec, State )}
end;
handle_call({remove_listener, IpAddr, Port}, _From, State) ->
case gen_nb_server:remove_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
ets_delete(?TAB, {IpAddr, Port}),
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call(_Msg, _From, State) ->
{reply, ignored, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
sock_opts() ->
[binary, {active, once}, {packet, 0}].
new_connection(IP, Port, Sock, State) ->
?debug("listener:new_connection(): Peer IP: ~p (ignored)", [IP]),
?debug("listener:new_connection(): Peer Port: ~p (ignored)", [Port]),
?debug("listener:new_connection(): Sock: ~p", [Sock]),
%% IP and Port are garbage. We'll grab peername when we get our
%% first data.
%% Provide component spec as extra arg.
{ok, _P} = connection:setup(undefined, 0, Sock,
dlink_tcp_rpc,
handle_socket, gen_nb_server:get_cb_state(State)),
{ok, State}.
ets_insert(Tab, Obj) ->
ets:insert(Tab, Obj).
ets_delete(Tab, Key) ->
ets:delete(Tab, Key).
ets_select(Tab, Pat) ->
ets:select(Tab, Pat).
|