diff options
Diffstat (limited to 'src/ejabberd_s2s.erl')
-rw-r--r-- | src/ejabberd_s2s.erl | 545 |
1 files changed, 284 insertions, 261 deletions
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index b06a7ab6c..0832d1dfd 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -25,49 +25,52 @@ %%%---------------------------------------------------------------------- -module(ejabberd_s2s). + -author('alexey@process-one.net'). -behaviour(gen_server). %% API --export([start_link/0, - route/3, - have_connection/1, - has_key/2, - get_connections_pids/1, - try_register/1, - remove_connection/3, - find_connection/2, - dirty_get_connections/0, - allow_host/2, - incoming_s2s_number/0, - outgoing_s2s_number/0, +-export([start_link/0, route/3, have_connection/1, + has_key/2, get_connections_pids/1, try_register/1, + remove_connection/3, find_connection/2, + dirty_get_connections/0, allow_host/2, + incoming_s2s_number/0, outgoing_s2s_number/0, clean_temporarily_blocked_table/0, list_temporarily_blocked_hosts/0, - external_host_overloaded/1, - is_temporarly_blocked/1 - ]). + external_host_overloaded/1, is_temporarly_blocked/1]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +-export([init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3]). + %% ejabberd API -export([get_info_s2s_connections/1]). -include("ejabberd.hrl"). + -include("jlib.hrl"). + -include("ejabberd_commands.hrl"). -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER, 1). + -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE, 1). -define(S2S_OVERLOAD_BLOCK_PERIOD, 60). + %% once a server is temporarly blocked, it stay blocked for 60 seconds --record(s2s, {fromto, pid, key}). +-record(s2s, {fromto = {<<"">>, <<"">>} :: {binary(), binary()}, + pid = self() :: pid() | '_', + key = <<"">> :: binary() | '_'}). + -record(state, {}). --record(temporarily_blocked, {host, timestamp}). +-record(temporarily_blocked, {host = <<"">> :: binary(), + timestamp = now() :: erlang:timestamp()}). + +-type temporarily_blocked() :: #temporarily_blocked{}. %%==================================================================== %% API @@ -77,57 +80,73 @@ %% Description: Starts the server %%-------------------------------------------------------------------- start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + gen_server:start_link({local, ?MODULE}, ?MODULE, [], + []). + +-spec route(jid(), jid(), xmlel()) -> ok. route(From, To, Packet) -> case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> - ok + {'EXIT', Reason} -> + ?ERROR_MSG("~p~nwhen processing: ~p", + [Reason, {From, To, Packet}]); + _ -> ok end. clean_temporarily_blocked_table() -> - mnesia:clear_table(temporarily_blocked). + mnesia:clear_table(temporarily_blocked). + +-spec list_temporarily_blocked_hosts() -> [temporarily_blocked()]. + list_temporarily_blocked_hosts() -> - ets:tab2list(temporarily_blocked). + ets:tab2list(temporarily_blocked). + +-spec external_host_overloaded(binary()) -> {aborted, any()} | {atomic, ok}. external_host_overloaded(Host) -> - ?INFO_MSG("Disabling connections from ~s for ~p seconds", [Host, ?S2S_OVERLOAD_BLOCK_PERIOD]), - mnesia:transaction( fun() -> - mnesia:write(#temporarily_blocked{host = Host, timestamp = now()}) - end). + ?INFO_MSG("Disabling connections from ~s for ~p " + "seconds", + [Host, ?S2S_OVERLOAD_BLOCK_PERIOD]), + mnesia:transaction(fun () -> + mnesia:write(#temporarily_blocked{host = Host, + timestamp = + now()}) + end). + +-spec is_temporarly_blocked(binary()) -> boolean(). is_temporarly_blocked(Host) -> - case mnesia:dirty_read(temporarily_blocked, Host) of - [] -> false; - [#temporarily_blocked{timestamp = T}=Entry] -> - case timer:now_diff(now(), T) of - N when N > ?S2S_OVERLOAD_BLOCK_PERIOD * 1000 * 1000 -> - mnesia:dirty_delete_object(Entry), - false; - _ -> - true - end - end. + case mnesia:dirty_read(temporarily_blocked, Host) of + [] -> false; + [#temporarily_blocked{timestamp = T} = Entry] -> + case timer:now_diff(now(), T) of + N when N > (?S2S_OVERLOAD_BLOCK_PERIOD) * 1000 * 1000 -> + mnesia:dirty_delete_object(Entry), false; + _ -> true + end + end. +-spec remove_connection({binary(), binary()}, + pid(), binary()) -> {atomic, ok} | + ok | + {aborted, any()}. remove_connection(FromTo, Pid, Key) -> - case catch mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo, - pid = Pid, - _ = '_'}) of - [#s2s{pid = Pid, key = Key}] -> - F = fun() -> - mnesia:delete_object(#s2s{fromto = FromTo, - pid = Pid, - key = Key}) - end, - mnesia:transaction(F); - _ -> - ok + case catch mnesia:dirty_match_object(s2s, + #s2s{fromto = FromTo, pid = Pid, + _ = '_'}) + of + [#s2s{pid = Pid, key = Key}] -> + F = fun () -> + mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid, + key = Key}) + end, + mnesia:transaction(F); + _ -> ok end. +-spec have_connection({binary(), binary()}) -> boolean(). + have_connection(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of [_] -> @@ -136,6 +155,8 @@ have_connection(FromTo) -> false end. +-spec has_key({binary(), binary()}, binary()) -> boolean(). + has_key(FromTo, Key) -> case mnesia:dirty_select(s2s, [{#s2s{fromto = FromTo, key = Key, _ = '_'}, @@ -147,6 +168,8 @@ has_key(FromTo, Key) -> true end. +-spec get_connections_pids({binary(), binary()}) -> [pid()]. + get_connections_pids(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of L when is_list(L) -> @@ -155,33 +178,32 @@ get_connections_pids(FromTo) -> [] end. +-spec try_register({binary(), binary()}) -> {key, binary()} | false. + try_register(FromTo) -> Key = randoms:get_string(), MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo), MaxS2SConnectionsNumberPerNode = max_s2s_connections_number_per_node(FromTo), - F = fun() -> + F = fun () -> L = mnesia:read({s2s, FromTo}), - NeededConnections = needed_connections_number( - L, MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - if - NeededConnections > 0 -> - mnesia:write(#s2s{fromto = FromTo, - pid = self(), - key = Key}), - {key, Key}; - true -> - false + NeededConnections = needed_connections_number(L, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode), + if NeededConnections > 0 -> + mnesia:write(#s2s{fromto = FromTo, pid = self(), + key = Key}), + {key, Key}; + true -> false end end, case mnesia:transaction(F) of - {atomic, Res} -> - Res; - _ -> - false + {atomic, Res} -> Res; + _ -> false end. +-spec dirty_get_connections() -> [{binary(), binary()}]. + dirty_get_connections() -> mnesia:dirty_all_keys(s2s). @@ -239,15 +261,13 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> {noreply, State}; handle_info({route, From, To, Packet}, State) -> case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> - ok + {'EXIT', Reason} -> + ?ERROR_MSG("~p~nwhen processing: ~p", + [Reason, {From, To, Packet}]); + _ -> ok end, {noreply, State}; -handle_info(_Info, State) -> - {noreply, State}. +handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() @@ -284,76 +304,79 @@ clean_table_from_bad_node(Node) -> mnesia:async_dirty(F). do_route(From, To, Packet) -> - ?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", - [From, To, Packet, 8]), + ?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket " + "~P~n", + [From, To, Packet, 8]), case find_connection(From, To) of - {atomic, Pid} when is_pid(Pid) -> - ?DEBUG("sending to process ~p~n", [Pid]), - {xmlelement, Name, Attrs, Els} = Packet, - NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From), - jlib:jid_to_string(To), - Attrs), - #jid{lserver = MyServer} = From, - ejabberd_hooks:run( - s2s_send_packet, - MyServer, - [From, To, Packet]), - send_element(Pid, {xmlelement, Name, NewAttrs, Els}), - ok; - {aborted, _Reason} -> - case xml:get_tag_attr_s("type", Packet) of - "error" -> ok; - "result" -> ok; - _ -> - Err = jlib:make_error_reply( - Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err) - end, - false + {atomic, Pid} when is_pid(Pid) -> + ?DEBUG("sending to process ~p~n", [Pid]), + #xmlel{name = Name, attrs = Attrs, children = Els} = + Packet, + NewAttrs = + jlib:replace_from_to_attrs(jlib:jid_to_string(From), + jlib:jid_to_string(To), Attrs), + #jid{lserver = MyServer} = From, + ejabberd_hooks:run(s2s_send_packet, MyServer, + [From, To, Packet]), + send_element(Pid, + #xmlel{name = Name, attrs = NewAttrs, children = Els}), + ok; + {aborted, _Reason} -> + case xml:get_tag_attr_s(<<"type">>, Packet) of + <<"error">> -> ok; + <<"result">> -> ok; + _ -> + Err = jlib:make_error_reply(Packet, + ?ERR_SERVICE_UNAVAILABLE), + ejabberd_router:route(To, From, Err) + end, + false end. +-spec find_connection(jid(), jid()) -> {aborted, any()} | {atomic, pid()}. + find_connection(From, To) -> #jid{lserver = MyServer} = From, #jid{lserver = Server} = To, FromTo = {MyServer, Server}, - MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo), + MaxS2SConnectionsNumber = + max_s2s_connections_number(FromTo), MaxS2SConnectionsNumberPerNode = max_s2s_connections_number_per_node(FromTo), ?DEBUG("Finding connection for ~p~n", [FromTo]), case catch mnesia:dirty_read(s2s, FromTo) of - {'EXIT', Reason} -> - {aborted, Reason}; - [] -> - %% We try to establish all the connections if the host is not a - %% service and if the s2s host is not blacklisted or - %% is in whitelist: - case not is_service(From, To) andalso allow_host(MyServer, Server) of - true -> - NeededConnections = needed_connections_number( - [], MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - open_several_connections( - NeededConnections, MyServer, - Server, From, FromTo, - MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode); - false -> - {aborted, error} - end; - L when is_list(L) -> - NeededConnections = needed_connections_number( - L, MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - if - NeededConnections > 0 -> - %% We establish the missing connections for this pair. - open_several_connections( - NeededConnections, MyServer, - Server, From, FromTo, - MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode); - true -> - %% We choose a connexion from the pool of opened ones. - {atomic, choose_connection(From, L)} - end + {'EXIT', Reason} -> {aborted, Reason}; + [] -> + %% We try to establish all the connections if the host is not a + %% service and if the s2s host is not blacklisted or + %% is in whitelist: + case not is_service(From, To) andalso + allow_host(MyServer, Server) + of + true -> + NeededConnections = needed_connections_number([], + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode), + open_several_connections(NeededConnections, MyServer, + Server, From, FromTo, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode); + false -> {aborted, error} + end; + L when is_list(L) -> + NeededConnections = needed_connections_number(L, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode), + if NeededConnections > 0 -> + %% We establish the missing connections for this pair. + open_several_connections(NeededConnections, MyServer, + Server, From, FromTo, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode); + true -> + %% We choose a connexion from the pool of opened ones. + {atomic, choose_connection(From, L)} + end end. choose_connection(From, Connections) -> @@ -361,29 +384,26 @@ choose_connection(From, Connections) -> choose_pid(From, Pids) -> Pids1 = case [P || P <- Pids, node(P) == node()] of - [] -> Pids; - Ps -> Ps + [] -> Pids; + Ps -> Ps end, - % Use sticky connections based on the JID of the sender (whithout - % the resource to ensure that a muc room always uses the same - % connection) - Pid = lists:nth(erlang:phash(jlib:jid_remove_resource(From), length(Pids1)), - Pids1), + Pid = + lists:nth(erlang:phash(jlib:jid_remove_resource(From), + length(Pids1)), + Pids1), ?DEBUG("Using ejabberd_s2s_out ~p~n", [Pid]), Pid. -open_several_connections(N, MyServer, Server, From, FromTo, - MaxS2SConnectionsNumber, +open_several_connections(N, MyServer, Server, From, + FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) -> - ConnectionsResult = - [new_connection(MyServer, Server, From, FromTo, - MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) - || _N <- lists:seq(1, N)], + ConnectionsResult = [new_connection(MyServer, Server, + From, FromTo, MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode) + || _N <- lists:seq(1, N)], case [PID || {atomic, PID} <- ConnectionsResult] of - [] -> - hd(ConnectionsResult); - PIDs -> - {atomic, choose_pid(From, PIDs)} + [] -> hd(ConnectionsResult); + PIDs -> {atomic, choose_pid(From, PIDs)} end. new_connection(MyServer, Server, From, FromTo, @@ -393,41 +413,38 @@ new_connection(MyServer, Server, From, FromTo, MyServer, Server, {new, Key}), F = fun() -> L = mnesia:read({s2s, FromTo}), - NeededConnections = needed_connections_number( - L, MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - if - NeededConnections > 0 -> - mnesia:write(#s2s{fromto = FromTo, - pid = Pid, - key = Key}), - ?INFO_MSG("New s2s connection started ~p", [Pid]), - Pid; - true -> - choose_connection(From, L) + NeededConnections = needed_connections_number(L, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode), + if NeededConnections > 0 -> + mnesia:write(#s2s{fromto = FromTo, pid = Pid, + key = Key}), + ?INFO_MSG("New s2s connection started ~p", [Pid]), + Pid; + true -> choose_connection(From, L) end end, TRes = mnesia:transaction(F), case TRes of - {atomic, Pid} -> - ejabberd_s2s_out:start_connection(Pid); - _ -> - ejabberd_s2s_out:stop_connection(Pid) + {atomic, Pid} -> ejabberd_s2s_out:start_connection(Pid); + _ -> ejabberd_s2s_out:stop_connection(Pid) end, TRes. max_s2s_connections_number({From, To}) -> - case acl:match_rule( - From, max_s2s_connections, jlib:make_jid("", To, "")) of - Max when is_integer(Max) -> Max; - _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER + case acl:match_rule(From, max_s2s_connections, + jlib:make_jid(<<"">>, To, <<"">>)) + of + Max when is_integer(Max) -> Max; + _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER end. max_s2s_connections_number_per_node({From, To}) -> - case acl:match_rule( - From, max_s2s_connections_per_node, jlib:make_jid("", To, "")) of - Max when is_integer(Max) -> Max; - _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE + case acl:match_rule(From, max_s2s_connections_per_node, + jlib:make_jid(<<"">>, To, <<"">>)) + of + Max when is_integer(Max) -> Max; + _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE end. needed_connections_number(Ls, MaxS2SConnectionsNumber, @@ -443,45 +460,46 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber, %% -------------------------------------------------------------------- is_service(From, To) -> LFromDomain = From#jid.lserver, - case ejabberd_config:get_local_option({route_subdomains, LFromDomain}) of - s2s -> % bypass RFC 3920 10.3 - false; - _ -> - Hosts = ?MYHOSTS, - P = fun(ParentDomain) -> lists:member(ParentDomain, Hosts) end, - lists:any(P, parent_domains(To#jid.lserver)) + case ejabberd_config:get_local_option( + {route_subdomains, LFromDomain}, + fun(s2s) -> s2s end) of + s2s -> % bypass RFC 3920 10.3 + false; + undefined -> + Hosts = (?MYHOSTS), + P = fun (ParentDomain) -> + lists:member(ParentDomain, Hosts) + end, + lists:any(P, parent_domains(To#jid.lserver)) end. parent_domains(Domain) -> - lists:foldl( - fun(Label, []) -> - [Label]; - (Label, [Head | Tail]) -> - [Label ++ "." ++ Head, Head | Tail] - end, [], lists:reverse(string:tokens(Domain, "."))). - -send_element(Pid, El) -> - Pid ! {send_element, El}. + lists:foldl(fun (Label, []) -> [Label]; + (Label, [Head | Tail]) -> + [<<Label/binary, ".", Head/binary>>, Head | Tail] + end, + [], lists:reverse(str:tokens(Domain, <<".">>))). +send_element(Pid, El) -> Pid ! {send_element, El}. %%%---------------------------------------------------------------------- %%% ejabberd commands commands() -> - [ - #ejabberd_commands{name = incoming_s2s_number, - tags = [stats, s2s], - desc = "Number of incoming s2s connections on the node", - module = ?MODULE, function = incoming_s2s_number, - args = [], - result = {s2s_incoming, integer}}, + [#ejabberd_commands{name = incoming_s2s_number, + tags = [stats, s2s], + desc = + "Number of incoming s2s connections on " + "the node", + module = ?MODULE, function = incoming_s2s_number, + args = [], result = {s2s_incoming, integer}}, #ejabberd_commands{name = outgoing_s2s_number, - tags = [stats, s2s], - desc = "Number of outgoing s2s connections on the node", - module = ?MODULE, function = outgoing_s2s_number, - args = [], - result = {s2s_outgoing, integer}} - ]. + tags = [stats, s2s], + desc = + "Number of outgoing s2s connections on " + "the node", + module = ?MODULE, function = outgoing_s2s_number, + args = [], result = {s2s_outgoing, integer}}]. incoming_s2s_number() -> length(supervisor:which_children(ejabberd_s2s_in_sup)). @@ -489,28 +507,21 @@ incoming_s2s_number() -> outgoing_s2s_number() -> length(supervisor:which_children(ejabberd_s2s_out_sup)). - %%%---------------------------------------------------------------------- %%% Update Mnesia tables update_tables() -> case catch mnesia:table_info(s2s, type) of - bag -> - ok; - {'EXIT', _} -> - ok; - _ -> - % XXX TODO convert it ? - mnesia:delete_table(s2s) + bag -> ok; + {'EXIT', _} -> ok; + _ -> mnesia:delete_table(s2s) end, case catch mnesia:table_info(s2s, attributes) of - [fromto, node, key] -> - mnesia:transform_table(s2s, ignore, [fromto, pid, key]), - mnesia:clear_table(s2s); - [fromto, pid, key] -> - ok; - {'EXIT', _} -> - ok + [fromto, node, key] -> + mnesia:transform_table(s2s, ignore, [fromto, pid, key]), + mnesia:clear_table(s2s); + [fromto, pid, key] -> ok; + {'EXIT', _} -> ok end, case lists:member(local_s2s, mnesia:system_info(tables)) of true -> @@ -521,62 +532,74 @@ update_tables() -> %% Check if host is in blacklist or white list allow_host(MyServer, S2SHost) -> - allow_host2(MyServer, S2SHost) andalso (not is_temporarly_blocked(S2SHost)). + allow_host2(MyServer, S2SHost) andalso + not is_temporarly_blocked(S2SHost). allow_host2(MyServer, S2SHost) -> - Hosts = ?MYHOSTS, - case lists:dropwhile( - fun(ParentDomain) -> - not lists:member(ParentDomain, Hosts) - end, parent_domains(MyServer)) of - [MyHost|_] -> - allow_host1(MyHost, S2SHost); - [] -> - allow_host1(MyServer, S2SHost) + Hosts = (?MYHOSTS), + case lists:dropwhile(fun (ParentDomain) -> + not lists:member(ParentDomain, Hosts) + end, + parent_domains(MyServer)) + of + [MyHost | _] -> allow_host1(MyHost, S2SHost); + [] -> allow_host1(MyServer, S2SHost) end. allow_host1(MyHost, S2SHost) -> - case ejabberd_config:get_local_option({{s2s_host, S2SHost}, MyHost}) of - deny -> false; - allow -> true; - _ -> - case ejabberd_config:get_local_option({s2s_default_policy, MyHost}) of - deny -> false; - _ -> - case ejabberd_hooks:run_fold(s2s_allow_host, MyHost, - allow, [MyHost, S2SHost]) of - deny -> false; - allow -> true; - _ -> true - end - end + case ejabberd_config:get_local_option( + {{s2s_host, S2SHost}, MyHost}, + fun(deny) -> deny; (allow) -> allow end) + of + deny -> false; + allow -> true; + undefined -> + case ejabberd_config:get_local_option( + {s2s_default_policy, MyHost}, + fun(deny) -> deny; (allow) -> allow end) + of + deny -> false; + _ -> + case ejabberd_hooks:run_fold(s2s_allow_host, MyHost, + allow, [MyHost, S2SHost]) + of + deny -> false; + allow -> true; + _ -> true + end + end end. %% Get information about S2S connections of the specified type. %% @spec (Type) -> [Info] %% where Type = in | out %% Info = [{InfoName::atom(), InfoValue::any()}] + get_info_s2s_connections(Type) -> ChildType = case Type of - in -> ejabberd_s2s_in_sup; - out -> ejabberd_s2s_out_sup + in -> ejabberd_s2s_in_sup; + out -> ejabberd_s2s_out_sup end, Connections = supervisor:which_children(ChildType), - get_s2s_info(Connections,Type). - -get_s2s_info(Connections,Type)-> - complete_s2s_info(Connections,Type,[]). -complete_s2s_info([],_,Result)-> - Result; -complete_s2s_info([Connection|T],Type,Result)-> - {_,PID,_,_}=Connection, + get_s2s_info(Connections, Type). + +get_s2s_info(Connections, Type) -> + complete_s2s_info(Connections, Type, []). + +complete_s2s_info([], _, Result) -> Result; +complete_s2s_info([Connection | T], Type, Result) -> + {_, PID, _, _} = Connection, State = get_s2s_state(PID), - complete_s2s_info(T,Type,[State|Result]). + complete_s2s_info(T, Type, [State | Result]). + +-spec get_s2s_state(pid()) -> [{status, open | closed | error} | {s2s_pid, pid()}]. -get_s2s_state(S2sPid)-> - Infos = case gen_fsm:sync_send_all_state_event(S2sPid,get_state_infos) of - {state_infos, Is} -> [{status, open} | Is]; - {noproc,_} -> [{status, closed}]; %% Connection closed - {badrpc,_} -> [{status, error}] +get_s2s_state(S2sPid) -> + Infos = case gen_fsm:sync_send_all_state_event(S2sPid, + get_state_infos) + of + {state_infos, Is} -> [{status, open} | Is]; + {noproc, _} -> [{status, closed}]; %% Connection closed + {badrpc, _} -> [{status, error}] end, [{s2s_pid, S2sPid} | Infos]. |