1%% This Source Code Form is subject to the terms of the Mozilla Public
2%% License, v. 2.0. If a copy of the MPL was not distributed with this
3%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4%%
5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(tcp_listener).
9
10%% Represents a running TCP listener (a process that listens for inbound
11%% TCP or TLS connections). Every protocol supported typically has one
12%% or two listeners, plain TCP and (optionally) TLS, but there can
13%% be more, e.g. when multiple network interfaces are involved.
14%%
15%% A listener has 6 properties (is a tuple of 6):
16%%
17%%  * IP address
18%%  * Port
19%%  * Node
20%%  * Label (human-friendly name, e.g. AMQP 0-9-1)
21%%  * Startup callback
22%%  * Shutdown callback
23%%
24%% Listeners use Ranch in embedded mode to accept and "bridge" client
25%% connections with protocol entry points such as rabbit_reader.
26%%
27%% Listeners are tracked in a Mnesia table so that they can be
28%%
29%%  * Shut down
30%%  * Listed (e.g. in the management UI)
31%%
32%% Every tcp_listener process has callbacks that are executed on start
33%% and termination. Those must take care of listener registration
34%% among other things.
35%%
36%% Listeners are supervised by tcp_listener_sup (one supervisor per protocol).
37%%
38%% See also rabbit_networking and tcp_listener_sup.
39
40-behaviour(gen_server).
41
42-export([start_link/5]).
43
44-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
45         terminate/2, code_change/3]).
46
47-record(state, {on_shutdown, label, ip, port}).
48
49%%----------------------------------------------------------------------------
50
51-type mfargs() :: {atom(), atom(), [any()]}.
52
53-spec start_link
54        (inet:ip_address(), inet:port_number(),
55         mfargs(), mfargs(), string()) ->
56                           rabbit_types:ok_pid_or_error().
57
58start_link(IPAddress, Port,
59           OnStartup, OnShutdown, Label) ->
60    gen_server:start_link(
61      ?MODULE, {IPAddress, Port,
62                OnStartup, OnShutdown, Label}, []).
63
64%%--------------------------------------------------------------------
65
66init({IPAddress, Port, {M, F, A}, OnShutdown, Label}) ->
67    process_flag(trap_exit, true),
68    logger:info("started ~s on ~s:~p", [Label, rabbit_misc:ntoab(IPAddress), Port]),
69    apply(M, F, A ++ [IPAddress, Port]),
70    State0 = #state{
71        on_shutdown = OnShutdown,
72        label = Label,
73        ip = IPAddress,
74        port = Port
75    },
76    {ok, obfuscate_state(State0)}.
77
78handle_call(_Request, _From, State) ->
79    {noreply, State}.
80
81handle_cast(_Msg, State) ->
82    {noreply, State}.
83
84handle_info(_Info, State) ->
85    {noreply, State}.
86
87terminate(_Reason, #state{on_shutdown = OnShutdown, label = Label, ip = IPAddress, port = Port}) ->
88    logger:info("stopped ~s on ~s:~p", [Label, rabbit_misc:ntoab(IPAddress), Port]),
89    try
90        OnShutdown(IPAddress, Port)
91    catch _:Error ->
92        logger:error("Failed to stop ~s on ~s:~p: ~p",
93                     [Label, rabbit_misc:ntoab(IPAddress), Port, Error])
94    end.
95
96code_change(_OldVsn, State, _Extra) ->
97    {ok, State}.
98
99obfuscate_state(#state{on_shutdown = OnShutdown} = State) ->
100    {M, F, A} = OnShutdown,
101    State#state{
102        %% avoids arguments from being logged in case of an exception
103        on_shutdown = fun(IPAddress, Port) ->
104          apply(M, F, A ++ [IPAddress, Port])
105        end
106    }.