File 1701-ssh-lsocket_sup-and-lsocket-added.patch of Package erlang
From 3d404e42ed34a9a20915fec0c214d392678986c6 Mon Sep 17 00:00:00 2001
From: Jakub Witczak <kuba@erlang.org>
Date: Mon, 21 Oct 2024 11:27:36 +0200
Subject: [PATCH 1/3] ssh: lsocket_sup and lsocket added
- ssh server side supervision tree update
- listen socket is always 1st step when daemon is started
- listen socket is provided by temporary ssh_lsocket process
- supervised by ssh_lsocket_sup
---
lib/ssh/src/Makefile | 2 +
lib/ssh/src/ssh.app.src | 2 +
lib/ssh/src/ssh.erl | 203 ++++++++++++----------------
lib/ssh/src/ssh_acceptor.erl | 125 ++++-------------
lib/ssh/src/ssh_acceptor_sup.erl | 53 +++++++-
lib/ssh/src/ssh_app.erl | 23 ++--
lib/ssh/src/ssh_lsocket.erl | 169 +++++++++++++++++++++++
lib/ssh/src/ssh_lsocket_sup.erl | 86 ++++++++++++
lib/ssh/src/ssh_system_sup.erl | 62 +++++++--
lib/ssh/test/ssh_basic_SUITE.erl | 44 ++++--
lib/ssh/test/ssh_dbg_SUITE.erl | 4 +-
lib/ssh/test/ssh_protocol_SUITE.erl | 100 ++++----------
lib/ssh/test/ssh_sup_SUITE.erl | 14 +-
lib/ssh/test/ssh_test_lib.erl | 46 ++++++-
lib/ssh/test/ssh_test_lib.hrl | 2 +
15 files changed, 607 insertions(+), 328 deletions(-)
create mode 100644 lib/ssh/src/ssh_lsocket.erl
create mode 100644 lib/ssh/src/ssh_lsocket_sup.erl
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index a8a088eb7d..f59c997a16 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -73,6 +73,8 @@ MODULES= \
ssh_info \
ssh_io \
ssh_lib \
+ ssh_lsocket \
+ ssh_lsocket_sup \
ssh_message \
ssh_no_io \
ssh_options \
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 93f4c2b305..48df1276a2 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -25,6 +25,8 @@
ssh_daemon_channel,
ssh_dbg,
ssh_lib,
+ ssh_lsocket_sup,
+ ssh_lsocket,
ssh_shell,
ssh_io,
ssh_info,
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 4b078a7b89..8efbe54342 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -167,10 +167,11 @@ The directory could be changed with the option
%%% Internal export
--export([is_host/2]).
+-export([is_host/2, update_lsocket/3]).
-behaviour(ssh_dbg).
--export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2, ssh_dbg_format/3]).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1,
+ ssh_dbg_format/2, ssh_dbg_format/3]).
%%% "Deprecated" types export:
-export_type([ssh_daemon_ref/0, ssh_connection_ref/0, ssh_channel_id/0]).
@@ -592,69 +593,54 @@ The rules for handling the two address passing options are:
;(socket, open_socket(), daemon_options()) -> {ok,daemon_ref()} | {error,term()}
.
-daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535,
- Host0 == any ; Host0 == loopback ; is_tuple(Host0) ->
- try
- {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0),
- #{} = Options0 = ssh_options:handle_options(server, UserOptions),
- %% We need to open the listen socket here before start of the system supervisor. That
- %% is because Port0 might be 0, or if an FD is provided in the Options0, in which case
- %% the real listening port will be known only after the gen_tcp:listen call.
- maybe_open_listen_socket(Host1, Port0, Options0)
- of
- {Host, Port, ListenSocket, Options1} ->
- try
- %% Now Host,Port is what to use for the supervisor to register its name,
- %% and ListenSocket, if provided, is for listening on connections. But
- %% it is still owned by self()...
-
- %% throws error:Error if no usable hostkey is found
- ssh_connection_handler:available_hkey_algorithms(server, Options1),
- ssh_system_sup:start_system(#address{address = Host,
- port = Port,
- profile = ?GET_OPT(profile,Options1)},
- Options1)
- of
- {ok,DaemonRef} when ListenSocket == undefined ->
- {ok,DaemonRef};
- {ok,DaemonRef} ->
- receive
- {request_control, ListenSocket, ReqPid} ->
- ok = controlling_process(ListenSocket, ReqPid, Options1),
- ReqPid ! {its_yours,ListenSocket}
- end,
- {ok,DaemonRef};
- {error, {already_started, _}} ->
- close_listen_socket(ListenSocket, Options1),
- {error, eaddrinuse};
- {error, Error} ->
- close_listen_socket(ListenSocket, Options1),
- {error, Error}
- catch
- error:{shutdown,Err} ->
- close_listen_socket(ListenSocket, Options1),
- {error,Err};
- exit:{noproc, _} ->
- close_listen_socket(ListenSocket, Options1),
- {error, ssh_not_started};
- error:Error ->
- close_listen_socket(ListenSocket, Options1),
- error(Error);
- exit:Exit ->
- close_listen_socket(ListenSocket, Options1),
- exit(Exit)
- end
- catch
- throw:bad_fd ->
- {error,bad_fd};
- throw:bad_socket ->
- {error,bad_socket};
- error:{badmatch,{error,Error}} ->
- {error,Error};
- error:Error ->
- {error,Error};
- _C:_E ->
- {error,{cannot_start_daemon,_C,_E}}
+daemon(Host0, Port0, UserOptions0)
+ when 0 =< Port0, Port0 =< 65535, Host0 == any ;
+ Host0 == loopback ; is_tuple(Host0) ->
+ {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0),
+ case ssh_options:handle_options(server, UserOptions) of
+ #{} = Options0 ->
+ case ssh_lsocket:get_lsocket(Host1, Port0, Options0) of
+ {ok, LSocketProvider, {ok, LSocket}} ->
+ {Host, Port, Options1} = update_lsocket(LSocket, LSocketProvider, Options0),
+ try
+ %% Now Host,Port is what to use for the supervisor to register its name,
+ %% and ListenSocket, if provided, is for listening on connections. But
+ %% it is still owned by self()...
+
+ %% throws error:Error if no usable hostkey is found
+ ssh_connection_handler:available_hkey_algorithms(server, Options1),
+ ssh_system_sup:start_system(#address{address = Host,
+ port = Port,
+ profile = ?GET_OPT(profile,Options1)},
+ Options1)
+ of
+ {ok,DaemonRef} ->
+ {ok,DaemonRef};
+ {error, {already_started, _}} ->
+ close_listen_socket(LSocket, Options1),
+ {error, eaddrinuse};
+ {error, Error} ->
+ close_listen_socket(LSocket, Options1),
+ {error, Error}
+ catch
+ error:{shutdown,Err} ->
+ close_listen_socket(LSocket, Options1),
+ {error,Err};
+ exit:{noproc, _} ->
+ close_listen_socket(LSocket, Options1),
+ {error, ssh_not_started};
+ error:Error ->
+ close_listen_socket(LSocket, Options1),
+ error(Error);
+ exit:Exit ->
+ close_listen_socket(LSocket, Options1),
+ exit(Exit)
+ end;
+ Error = {error, _} ->
+ Error
+ end;
+ OptionError = {error,_} ->
+ OptionError
end;
daemon(_, _, _) ->
@@ -682,9 +668,9 @@ chapters [Configuration in SSH](configurations.md) and
daemon_replace_options(DaemonRef, NewUserOptions) ->
case ssh_system_sup:get_acceptor_options(DaemonRef) of
- {ok, Os0} ->
- Os1 = ssh_options:merge_options(server, NewUserOptions, Os0),
- ssh_system_sup:replace_acceptor_options(DaemonRef, Os1);
+ {ok, Options0} ->
+ Options = ssh_options:merge_options(server, NewUserOptions, Options0),
+ ssh_system_sup:restart_acceptor(DaemonRef, Options);
{error, _Reason} = Error ->
Error
end.
@@ -1277,29 +1263,6 @@ is_tcp_socket(Socket) ->
_ -> false
end.
-%%%----------------------------------------------------------------
-maybe_open_listen_socket(Host, Port, Options) ->
- Opened =
- case ?GET_SOCKET_OPT(fd, Options) of
- undefined when Port == 0 ->
- ssh_acceptor:listen(0, Options);
- Fd when is_integer(Fd) ->
- %% Do gen_tcp:listen with the option {fd,Fd}:
- ssh_acceptor:listen(0, Options);
- undefined ->
- open_later
- end,
- case Opened of
- {ok,LSock} ->
- {ok,{LHost,LPort}} = inet:sockname(LSock),
- {LHost, LPort, LSock, ?PUT_INTERNAL_OPT({lsocket,{LSock,self()}}, Options)};
- open_later ->
- {Host, Port, undefined, Options};
- Others ->
- Others
- end.
-
-%%%----------------------------------------------------------------
close_listen_socket(ListenSocket, Options) ->
try
{_, Callback, _} = ?GET_OPT(transport, Options),
@@ -1308,10 +1271,6 @@ close_listen_socket(ListenSocket, Options) ->
_C:_E -> ok
end.
-controlling_process(ListenSocket, ReqPid, Options) ->
- {_, Callback, _} = ?GET_OPT(transport, Options),
- Callback:controlling_process(ListenSocket, ReqPid).
-
transport_connect(Host, Port, SocketOpts, Options) ->
{_, Callback, _} = ?GET_OPT(transport, Options),
Callback:connect(Host, Port, SocketOpts, ?GET_OPT(connect_timeout,Options)).
@@ -1365,7 +1324,12 @@ mangle_tunnel_address(X) when is_list(X) -> case catch inet:parse_address(X) of
{ok, {0,0,0,0,0,0,0,0}} -> <<"">>;
_ -> list_to_binary(X)
end.
-
+-doc false.
+update_lsocket(LSocket, LSocketProvider, Options0) ->
+ {ok, {LHost, LPort}} = inet:sockname(LSocket),
+ Options = ?PUT_INTERNAL_OPT({lsocket,
+ {LSocket, LHost, LPort, LSocketProvider}}, Options0),
+ {LHost, LPort, Options}.
%%%################################################################
%%%#
@@ -1373,37 +1337,46 @@ mangle_tunnel_address(X) when is_list(X) -> case catch inet:parse_address(X) of
%%%#
-doc false.
-ssh_dbg_trace_points() -> [tcp].
+ssh_dbg_trace_points() -> [tcp, connections].
-doc false.
-ssh_dbg_flags(tcp) -> [c].
+ssh_dbg_flags(tcp) -> [c];
+ssh_dbg_flags(connections) -> [c].
-doc false.
-ssh_dbg_on(tcp) -> dbg:tpl(?MODULE, controlling_process, 3, x),
- dbg:tpl(?MODULE, transport_connect, 4, x),
- dbg:tpl(?MODULE, close_listen_socket, 2, x).
-
--doc false.
-ssh_dbg_off(tcp) ->dbg:ctpl(?MODULE, controlling_process, 3),
- dbg:ctpl(?MODULE, transport_connect, 4),
- dbg:ctpl(?MODULE, close_listen_socket, 2).
+ssh_dbg_on(tcp) ->
+ dbg:tpl(?MODULE, transport_connect, 4, x),
+ dbg:tpl(?MODULE, close_listen_socket, 2, x);
+ssh_dbg_on(connections) ->
+ dbg:tpl(?MODULE, update_lsocket, 3, x).
-doc false.
-ssh_dbg_format(tcp, {call, {?MODULE,controlling_process, [ListenSocket, ReqPid, _Opts]}}) ->
- ["TCP socket transferred to\n",
- io_lib:format("Sock: ~p~n"
- "ToPid: ~p~n", [ListenSocket, ReqPid])
- ];
-ssh_dbg_format(tcp, {return_from, {?MODULE,controlling_process,3}, _Result}) ->
- skip;
+ssh_dbg_off(tcp) ->
+ dbg:ctpl(?MODULE, transport_connect, 4),
+ dbg:ctpl(?MODULE, close_listen_socket, 2);
+ssh_dbg_off(connections) ->
+ dbg:ctpl(?MODULE, update_lsocket, 3).
+-doc false.
ssh_dbg_format(tcp, {call, {?MODULE,close_listen_socket, [ListenSocket, _Opts]}}) ->
["TCP socket listening closed\n",
io_lib:format("Sock: ~p~n", [ListenSocket])
];
ssh_dbg_format(tcp, {return_from, {?MODULE,close_listen_socket,2}, _Result}) ->
- skip.
-
+ skip;
+ssh_dbg_format(Tracepoint , Event = {call, {?MODULE, Function, Args}}) ->
+ [io_lib:format("~w:~w/~w> ~s", [?MODULE, Function, length(Args)] ++
+ ssh_dbg_comment(Tracepoint, Event))];
+ssh_dbg_format(Tracepoint, Event = {return_from, {?MODULE,Function,Arity}, Ret}) ->
+ [io_lib:format("~w:~w/~w returned ~W> ~s", [?MODULE, Function, Arity, Ret, 3] ++
+ ssh_dbg_comment(Tracepoint, Event))].
+
+ssh_dbg_comment(connections, {call, {?MODULE, update_lsocket, [LSocket, LSocketProvider, _]}}) ->
+ [io_lib:format("LSocket = ~p, LSocketProvider = ~p", [LSocket, LSocketProvider])];
+ssh_dbg_comment(connections, {return_from, {?MODULE, update_lsocket,3}, {LHost, LPort, _}}) ->
+ [io_lib:format("LHost = ~p, LPort = ~p", [LHost, LPort])];
+ssh_dbg_comment(_, _) ->
+ [""].
-doc false.
ssh_dbg_format(tcp, {call, {?MODULE,transport_connect, [Host,Port,SockOpts,_Opts]}}, Stack) ->
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index a558a888d2..60a0f52c46 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -27,14 +27,14 @@
%% Internal application API
-export([start_link/3,
- number_of_connections/1,
- listen/2]).
+ number_of_connections/1]).
%% spawn export
-export([acceptor_init/4, acceptor_loop/6]).
-behaviour(ssh_dbg).
--export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2, ssh_dbg_format/3]).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1,
+ ssh_dbg_format/2, ssh_dbg_format/3]).
-define(SLEEP_TIME, 200).
@@ -45,20 +45,6 @@
start_link(SystemSup, Address, Options) ->
proc_lib:start_link(?MODULE, acceptor_init, [self(),SystemSup,Address,Options]).
-%%%----------------------------------------------------------------
-listen(Port, Options) ->
- {_, Callback, _} = ?GET_OPT(transport, Options),
- SockOpts = ?GET_OPT(socket_options, Options) ++ [{active, false}, {reuseaddr,true}],
- case Callback:listen(Port, SockOpts) of
- {error, nxdomain} ->
- Callback:listen(Port, lists:delete(inet6, SockOpts));
- {error, enetunreach} ->
- Callback:listen(Port, lists:delete(inet6, SockOpts));
- {error, eafnosupport} ->
- Callback:listen(Port, lists:delete(inet6, SockOpts));
- Other ->
- Other
- end.
accept(ListenSocket, AcceptTimeout, Options) ->
{_, Callback, _} = ?GET_OPT(transport, Options),
@@ -67,7 +53,7 @@ accept(ListenSocket, AcceptTimeout, Options) ->
close(Socket, Options) ->
{_, Callback, _} = ?GET_OPT(transport, Options),
Callback:close(Socket).
-
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -78,57 +64,12 @@ acceptor_init(Parent, SystemSup,
{acceptor,
list_to_binary(ssh_lib:format_address_port(Address, Port))}),
AcceptTimeout = ?GET_INTERNAL_OPT(timeout, Opts, ?DEFAULT_TIMEOUT),
- case ?GET_INTERNAL_OPT(lsocket, Opts, undefined) of
- {LSock, SockOwner} ->
- %% A listening socket (or fd option) was provided in the ssh:daemon call
- case inet:sockname(LSock) of
- {ok,{_,Port}} ->
- %% A usable, open LSock
- proc_lib:init_ack(Parent, {ok, self()}),
- request_ownership(LSock, SockOwner),
- acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup);
- {error,_Error} ->
- %% Not open, a restart
- %% Allow gen_tcp:listen to fail 4 times if eaddrinuse (It is a bug fix):
- case try_listen(Port, Opts, 4) of
- {ok,NewLSock} ->
- proc_lib:init_ack(Parent, {ok, self()}),
- Opts1 = ?DELETE_INTERNAL_OPT(lsocket, Opts),
- acceptor_loop(Port, Address, Opts1, NewLSock, AcceptTimeout, SystemSup);
- {error,Error} ->
- proc_lib:init_fail(Parent, {error,Error}, {exit, normal})
- end
- end;
- undefined ->
- %% No listening socket (nor fd option) was provided; open a listening socket:
- case try_listen(Port, Opts, 4) of
- {ok,LSock} ->
- proc_lib:init_ack(Parent, {ok, self()}),
- acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup);
- {error,Error} ->
- proc_lib:init_fail(Parent, {error,Error}, {exit, normal})
- end
- end.
+ {LSock, _LHost, _LPort, _SockOwner} =
+ ?GET_INTERNAL_OPT(lsocket, Opts, undefined),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup).
-try_listen(Port, Opts, NtriesLeft) ->
- try_listen(Port, Opts, 1, NtriesLeft).
-
-try_listen(Port, Opts, N, Nmax) ->
- case listen(Port, Opts) of
- {error,eaddrinuse} when N<Nmax ->
- timer:sleep(10*N), % Sleep 10, 20, 30,... ms
- try_listen(Port, Opts, N+1, Nmax);
- Other ->
- Other
- end.
-
-request_ownership(LSock, SockOwner) ->
- SockOwner ! {request_control,LSock,self()},
- receive
- {its_yours,LSock} -> ok
- end.
-
-%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
acceptor_loop(Port, Address, Opts, ListenSocket, AcceptTimeout, SystemSup) ->
try
case accept(ListenSocket, AcceptTimeout, Opts) of
@@ -257,35 +198,18 @@ ssh_dbg_trace_points() -> [connections, tcp].
ssh_dbg_flags(tcp) -> [c];
ssh_dbg_flags(connections) -> [c].
-ssh_dbg_on(tcp) -> dbg:tp(?MODULE, listen, 2, x),
- dbg:tpl(?MODULE, accept, 3, x),
+ssh_dbg_on(tcp) -> dbg:tpl(?MODULE, accept, 3, x),
dbg:tpl(?MODULE, close, 2, x);
-
+
ssh_dbg_on(connections) -> dbg:tp(?MODULE, acceptor_init, 4, x),
dbg:tpl(?MODULE, handle_connection, 4, x).
-ssh_dbg_off(tcp) -> dbg:ctpg(?MODULE, listen, 2),
- dbg:ctpl(?MODULE, accept, 3),
+ssh_dbg_off(tcp) -> dbg:ctpl(?MODULE, accept, 3),
dbg:ctpl(?MODULE, close, 2);
ssh_dbg_off(connections) -> dbg:ctp(?MODULE, acceptor_init, 4),
dbg:ctp(?MODULE, handle_connection, 4).
-ssh_dbg_format(tcp, {call, {?MODULE,listen, [Port,_Opts]}}, Stack) ->
- {skip, [{port,Port}|Stack]};
-ssh_dbg_format(tcp, {return_from, {?MODULE,listen,2}, {ok,Sock}}, [{port,Port}|Stack]) ->
- {["TCP listener started\n",
- io_lib:format("Port: ~p~n"
- "ListeningSocket: ~p~n", [Port,Sock])
- ],
- Stack};
-ssh_dbg_format(tcp, {return_from, {?MODULE,listen,2}, Result}, [{port,Port}|Stack]) ->
- {["TCP listener start ERROR\n",
- io_lib:format("Port: ~p~n"
- "Return: ~p~n", [Port,Result])
- ],
- Stack};
-
ssh_dbg_format(tcp, {call, {?MODULE,accept, [ListenSocket, _AcceptTimeout, _Options]}}, Stack) ->
{skip, [{lsock,ListenSocket}|Stack]};
ssh_dbg_format(tcp, {return_from, {?MODULE,accept,3}, {ok,Sock}}, [{lsock,ListenSocket}|Stack]) ->
@@ -301,21 +225,22 @@ ssh_dbg_format(tcp, {return_from, {?MODULE,accept,3}, Return}, [{lsock,ListenSoc
"Return: ~p~n", [ListenSocket,Return])
], Stack}.
-ssh_dbg_format(tcp, {call, {?MODULE,close, [Socket, _Options]}}) ->
- ["TCP close listen socket\n",
- io_lib:format("Socket: ~p~n", [Socket])];
ssh_dbg_format(tcp, {return_from, {?MODULE,close,2}, _Return}) ->
skip;
-
-ssh_dbg_format(connections, {call, {?MODULE,acceptor_init, [_Parent, _SysSup, Address, _Opts]}}) ->
- [io_lib:format("Starting LISTENER on ~s\n", [ssh_lib:format_address(Address)])
- ];
ssh_dbg_format(connections, {return_from, {?MODULE,acceptor_init,4}, _Ret}) ->
skip;
-
ssh_dbg_format(connections, {call, {?MODULE,handle_connection,[_Address,_Port,_Options,_Sock]}}) ->
skip;
-ssh_dbg_format(connections, {return_from, {?MODULE,handle_connection,4}, {error,Error}}) ->
- ["Starting connection to server failed:\n",
- io_lib:format("Error = ~p", [Error])
- ].
+ssh_dbg_format(Tracepoint, Event = {call, {?MODULE, Function, Args}}) ->
+ [io_lib:format("~w:~w/~w> ~s", [?MODULE, Function, length(Args)] ++
+ ssh_dbg_comment(Tracepoint, Event))];
+ssh_dbg_format(Tracepoint, Event = {return_from, {?MODULE,Function,Arity}, Ret}) ->
+ [io_lib:format("~w:~w/~w returned ~W> ~s", [?MODULE, Function, Arity, Ret, 2] ++
+ ssh_dbg_comment(Tracepoint, Event))].
+
+ssh_dbg_comment(tcp, {call, {?MODULE,close, [Socket, _Options]}}) ->
+ [io_lib:format("TCP close listen socket Socket: ~p~n", [Socket])];
+ssh_dbg_comment(connections, {call, {?MODULE,acceptor_init, [_Parent, _SysSup, Address, _Opts]}}) ->
+ [io_lib:format("Starting LISTENER on ~s", [ssh_lib:format_address(Address)])];
+ssh_dbg_comment(connections, {return_from, {?MODULE,handle_connection,4}, {error,Error}}) ->
+ [io_lib:format("Starting connection to server failed: Error = ~p", [Error])].
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index 1661134285..74fb5ed71c 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -37,6 +37,10 @@
%% Supervisor callback
-export([init/1]).
+-behaviour(ssh_dbg).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1,
+ ssh_dbg_format/2]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -57,7 +61,10 @@ restart_child(AccSup, Address) ->
init([SystemSup, Address, Options]) ->
ssh_lib:set_label(server, acceptor_sup),
%% Initial start of ssh_acceptor_sup for this port
- SupFlags = #{strategy => one_for_one,
+ {LSocket, _LHost, _LPort, ProviderPid} =
+ ?GET_INTERNAL_OPT(lsocket, Options, undefined),
+ request_ownership(LSocket, ProviderPid),
+ SupFlags = #{strategy => one_for_one,
intensity => 10,
period => 3600
},
@@ -71,3 +78,47 @@ init([SystemSup, Address, Options]) ->
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
+request_ownership(LSocket, SockProvider) ->
+ SockProvider ! {request_control,LSocket,self()},
+ receive
+ {its_yours,LSocket} ->
+ ok
+ after ?DEFAULT_TIMEOUT ->
+ no_response_from_socket_provider
+ end.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+
+ssh_dbg_trace_points() -> [connections].
+
+ssh_dbg_flags(connections) -> [c].
+
+ssh_dbg_on(connections) ->
+ dbg:tpl(?MODULE, start_link, 3, x),
+ dbg:tpl(?MODULE, restart_child, 2, x),
+ dbg:tpl(?MODULE, request_ownership, 2, x),
+ dbg:tpl(?MODULE, init, 1, x).
+
+ssh_dbg_off(connections) ->
+ dbg:ctpl(?MODULE, start_link, 3),
+ dbg:ctpl(?MODULE, restart_child, 2),
+ dbg:ctpl(?MODULE, request_ownership, 2),
+ dbg:ctpl(?MODULE, init, 1).
+
+ssh_dbg_format(Tracepoint, Event = {call, {?MODULE, Function, Args}}) ->
+ [io_lib:format("~w:~w/~w> ~s", [?MODULE, Function, length(Args)] ++
+ ssh_dbg_comment(Tracepoint, Event))];
+ssh_dbg_format(Tracepoint, Event = {return_from, {?MODULE,Function,Arity}, Ret}) ->
+ [io_lib:format("~w:~w/~w returned ~W> ~s", [?MODULE, Function, Arity, Ret, 2] ++
+ ssh_dbg_comment(Tracepoint, Event))].
+
+ssh_dbg_comment(connections, {call, {?MODULE, init, [[LSocket | _]]}}) ->
+ [io_lib:format("LSocket ~p", [LSocket])];
+ssh_dbg_comment(connections, {call, {?MODULE, request_ownership, [LSocket, SockProvider]}}) ->
+ [io_lib:format("LSocket ~p SockProvider ~p", [LSocket, SockProvider])];
+ssh_dbg_comment(_, _) ->
+ [""].
+
diff --git a/lib/ssh/src/ssh_app.erl b/lib/ssh/src/ssh_app.erl
index 5f0c01e7b8..91c688f64d 100644
--- a/lib/ssh/src/ssh_app.erl
+++ b/lib/ssh/src/ssh_app.erl
@@ -29,7 +29,7 @@
%%% | :
%%% | +--> "connection sup" (etc)
%%% |
-%%% +-----> sshc_sup --+--> "system sup" (etc)
+%%% +-----> sshd_sup --+--> "lsocket sup" (etc)
%%% |
%%% +--> "system sup" (etc)
%%% :
@@ -64,21 +64,26 @@ init([ssh_sup]) ->
add_logger_filter(),
SupFlags = #{strategy => one_for_one,
intensity => 10,
- period => 3600
- },
+ period => 3600},
ChildSpecs = [#{id => SupName,
start => {supervisor, start_link,
- [{local,SupName}, ?MODULE, [sshX_sup]]},
+ [{local,SupName}, ?MODULE, [SupName]]},
type => supervisor}
- || SupName <- [sshd_sup, sshc_sup]
- ],
+ || SupName <- [sshd_sup, sshc_sup]],
{ok, {SupFlags,ChildSpecs}};
-init([sshX_sup]) ->
+init([sshd_sup]) ->
SupFlags = #{strategy => one_for_one,
intensity => 10,
- period => 3600
- },
+ period => 3600},
+ ChildSpecs = [#{id => ssh_lsocket_sup,
+ start => {ssh_lsocket_sup, start_link, []},
+ type => supervisor}],
+ {ok, {SupFlags,ChildSpecs}};
+init([sshc_sup]) ->
+ SupFlags = #{strategy => one_for_one,
+ intensity => 10,
+ period => 3600},
ChildSpecs = [],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_lsocket.erl b/lib/ssh/src/ssh_lsocket.erl
new file mode 100644
index 0000000000..c4d9853c0c
--- /dev/null
+++ b/lib/ssh/src/ssh_lsocket.erl
@@ -0,0 +1,169 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2024. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Produce listen sockets.
+%%----------------------------------------------------------------------
+-module(ssh_lsocket).
+-moduledoc false.
+
+-include("ssh.hrl").
+-export([start_link/4, provide_lsocket/5, get_lsocket/3]).
+
+-behaviour(ssh_dbg).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1,
+ ssh_dbg_format/2, ssh_dbg_format/3]).
+
+get_lsocket(Host, Port, Options) ->
+ supervisor:start_child(ssh_lsocket_sup, [self(), Host, Port, Options]).
+
+start_link(Caller, Host, Port, Options) ->
+ proc_lib:start_link(?MODULE, provide_lsocket,
+ [self(), Caller, Host, Port, Options]).
+
+provide_lsocket(Parent, _Caller, _Host1, Port0, Options) ->
+ OpenResult =
+ try
+ try_listen(Port0, Options, 4)
+ of
+ {ok, LSocket} ->
+ {ok, LSocket};
+ Others ->
+ Others
+ catch
+ throw:bad_fd ->
+ {error,bad_fd};
+ throw:bad_socket ->
+ {error,bad_socket};
+ error:{badmatch, {error,Error}} ->
+ {error,Error};
+ error:Error ->
+ {error,Error};
+ _C:_E ->
+ {error,{cannot_start_daemon,_C,_E}}
+ end,
+ case OpenResult of
+ {ok, LSocket1} ->
+ proc_lib:init_ack(Parent, {ok, self(), OpenResult}),
+ wait_for_acceptor_sup(LSocket1, Options),
+ ok;
+ {error, _} ->
+ proc_lib:init_fail(Parent, OpenResult, {exit, normal})
+ end.
+
+wait_for_acceptor_sup(ListenSocket, Options) ->
+ receive
+ {request_control, ListenSocket, AcceptorSup} ->
+ ok = controlling_process(ListenSocket, AcceptorSup, Options),
+ AcceptorSup ! {its_yours,ListenSocket},
+ ok
+ end.
+
+controlling_process(ListenSocket, ReqPid, Options) ->
+ {_, Callback, _} = ?GET_OPT(transport, Options),
+ Callback:controlling_process(ListenSocket, ReqPid).
+
+try_listen(Port, Opts, NtriesLeft) ->
+ try_listen(Port, Opts, 1, NtriesLeft).
+
+try_listen(Port, Opts, N, Nmax) ->
+ case listen(Port, Opts) of
+ {error,eaddrinuse} when N<Nmax ->
+ timer:sleep(10*N), % Sleep 10, 20, 30,... ms
+ try_listen(Port, Opts, N+1, Nmax);
+ Other ->
+ Other
+ end.
+
+listen(Port, Options) ->
+ {_, Callback, _} = ?GET_OPT(transport, Options),
+ SockOpts = ?GET_OPT(socket_options, Options) ++ [{active, false}, {reuseaddr,true}],
+ case Callback:listen(Port, SockOpts) of
+ {error, nxdomain} ->
+ Callback:listen(Port, lists:delete(inet6, SockOpts));
+ {error, enetunreach} ->
+ Callback:listen(Port, lists:delete(inet6, SockOpts));
+ {error, eafnosupport} ->
+ Callback:listen(Port, lists:delete(inet6, SockOpts));
+ Other ->
+ Other
+ end.
+
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+
+ssh_dbg_trace_points() -> [connections].
+
+ssh_dbg_flags(connections) -> [c].
+
+ssh_dbg_on(connections) ->
+ dbg:tpl(?MODULE, provide_lsocket, 5, x),
+ dbg:tpl(?MODULE, controlling_process, 3, x),
+ dbg:tpl(?MODULE, try_listen, 4, x),
+ dbg:tpl(?MODULE, wait_for_acceptor_sup, 2, x);
+ssh_dbg_on(tcp) -> dbg:tpl(?MODULE, accept, 3, x).
+
+ssh_dbg_off(connections) ->
+ dbg:ctpl(?MODULE, provide_lsocket, 5),
+ dbg:ctpl(?MODULE, controlling_process, 3),
+ dbg:ctpl(?MODULE, try_listen, 4),
+ dbg:ctpl(?MODULE, wait_for_acceptor_sup, 2);
+ssh_dbg_off(tcp) -> dbg:ctpl(?MODULE, accept, 3).
+
+ssh_dbg_format(Tracepoint, Event = {call, {?MODULE, Function, Args}}) ->
+ [io_lib:format("~w:~w/~w> ~s", [?MODULE, Function, length(Args)] ++
+ ssh_dbg_comment(Tracepoint, Event))];
+ssh_dbg_format(Tracepoint, Event = {return_from, {?MODULE,Function,Arity}, Ret}) ->
+ [io_lib:format("~w:~w/~w returned ~W> ~s", [?MODULE, Function, Arity, Ret, 3] ++
+ ssh_dbg_comment(Tracepoint, Event))].
+
+ssh_dbg_comment(connections, {call, {?MODULE, try_listen, [Port, _, N, Nmax]}}) ->
+ [io_lib:format("listen retry on Port:~p [~p/~p]", [Port, N, Nmax])];
+ssh_dbg_comment(connections, {call, {?MODULE, provide_lsocket, [_Parent, _Caller, _Ref, Host, Port, _UserOptions]}}) ->
+ [io_lib:format("providing LSocket for Host: ~p Port: ~p", [Host, Port])];
+ssh_dbg_comment(connections, {call, {?MODULE,controlling_process,[ListenSocket, ReqPid, _Options]}}) ->
+ [io_lib:format("changing owner for ~p to ~p", [ListenSocket, ReqPid])];
+ssh_dbg_comment(connections, {return_from, {?MODULE,controlling_process,3}, _Ret}) ->
+ [io_lib:format("ownership changed", [])];
+ssh_dbg_comment(connections, {call, {?MODULE, wait_for_acceptor_sup, [LSocket, _Options]}}) ->
+ [io_lib:format("waiting for acceptor sup to pickup LSocket ~p", [LSocket])];
+ssh_dbg_comment(connections, {return_from, {?MODULE,wait_for_acceptor_sup,2}, ok}) ->
+ [io_lib:format("LSocket provided. Bye.", [])];
+ssh_dbg_comment(_, _) ->
+ [""].
+
+ssh_dbg_format(tcp, {call, {?MODULE,listen, [Port,_Opts]}}, Stack) ->
+ {skip, [{port,Port}|Stack]};
+ssh_dbg_format(tcp, {return_from, {?MODULE,listen,2}, {ok,Sock}}, [{port,Port}|Stack]) ->
+ {["TCP listener started\n",
+ io_lib:format("Port: ~p~n"
+ "ListeningSocket: ~p~n", [Port,Sock])
+ ],
+ Stack};
+ssh_dbg_format(tcp, {return_from, {?MODULE,listen,2}, Result}, [{port,Port}|Stack]) ->
+ {["TCP listener start ERROR\n",
+ io_lib:format("Port: ~p~n"
+ "Return: ~p~n", [Port,Result])
+ ],
+ Stack}.
diff --git a/lib/ssh/src/ssh_lsocket_sup.erl b/lib/ssh/src/ssh_lsocket_sup.erl
new file mode 100644
index 0000000000..1922268c37
--- /dev/null
+++ b/lib/ssh/src/ssh_lsocket_sup.erl
@@ -0,0 +1,86 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2024. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The ssh_lsocket supervisor, hangs under
+%% sshd_sup.
+%%----------------------------------------------------------------------
+
+-module(ssh_lsocket_sup).
+-moduledoc false.
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+-define(SERVER, ?MODULE).
+
+%%%===================================================================
+%%% API functions
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the supervisor
+%% @end
+%%--------------------------------------------------------------------
+-spec start_link() -> {ok, Pid :: pid()} |
+ {error, {already_started, Pid :: pid()}} |
+ {error, {shutdown, term()}} |
+ {error, term()} |
+ ignore.
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%%===================================================================
+%%% Supervisor callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Whenever a supervisor is started using supervisor:start_link/[2,3],
+%% this function is called by the new process to find out about
+%% restart strategy, maximum restart intensity, and child
+%% specifications.
+%% @end
+%%--------------------------------------------------------------------
+-spec init(Args :: term()) ->
+ {ok, {SupFlags :: supervisor:sup_flags(),
+ [ChildSpec :: supervisor:child_spec()]}} |
+ ignore.
+init([]) ->
+ ssh_lib:set_label(server, lsocket_sup),
+ SupFlags = #{strategy => simple_one_for_one,
+ intensity => 1,
+ period => 5},
+
+ AChild = #{id => ssh_lsocket,
+ start => {ssh_lsocket, start_link, []},
+ restart => temporary,
+ shutdown => 5000,
+ type => worker,
+ modules => [ssh_lsocket]},
+
+ {ok, {SupFlags, [AChild]}}.
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index bc1d79d0f4..676c75f27f 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -41,12 +41,16 @@
addresses/1,
get_options/2,
get_acceptor_options/1,
- replace_acceptor_options/2
+ restart_acceptor/2
]).
%% Supervisor callback
-export([init/1]).
+-behaviour(ssh_dbg).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1,
+ ssh_dbg_format/2]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -54,7 +58,7 @@
start_system(Address0, Options) ->
case find_system_sup(Address0) of
{ok,{SysPid,Address}} ->
- restart_acceptor(SysPid, Address, Options);
+ start_acceptor(SysPid, Address, Options);
{error,not_found} ->
supervisor:start_child(sshd_sup,
#{id => {?MODULE,Address0},
@@ -164,16 +168,19 @@ get_acceptor_options(SysPid) ->
{error,bad_daemon_ref}
end.
-replace_acceptor_options(SysPid, NewOpts) ->
+restart_acceptor(SysPid, Options0) ->
case get_daemon_listen_address(SysPid) of
{ok,Address} ->
- try stop_listener(SysPid)
+ try
+ stop_listener(SysPid)
of
ok ->
- restart_acceptor(SysPid, Address, NewOpts)
+ Options = refresh_lsocket(Options0),
+ start_acceptor(SysPid, Address, Options)
catch
- error:_ ->
- restart_acceptor(SysPid, Address, NewOpts)
+ error:_Error ->
+ Options = refresh_lsocket(Options0),
+ start_acceptor(SysPid, Address, Options)
end;
{error,Error} ->
{error,Error}
@@ -258,7 +265,7 @@ sup(server) -> sshd_sup.
is_socket_server(Options) ->
undefined =/= ?GET_INTERNAL_OPT(connected_socket,Options,undefined).
-restart_acceptor(SysPid, Address, Options) ->
+start_acceptor(SysPid, Address, Options) ->
case lookup(ssh_acceptor_sup, SysPid) of
{_,_,supervisor,_} ->
{error, eaddrinuse};
@@ -273,3 +280,42 @@ restart_acceptor(SysPid, Address, Options) ->
{error,Error}
end
end.
+
+refresh_lsocket(Options0) ->
+ {_OldLSock, LHost, LPort, _SockOwner} =
+ ?GET_INTERNAL_OPT(lsocket, Options0, lsocket_undefined),
+ case ssh_lsocket:get_lsocket(LHost, LPort, Options0) of
+ {ok, LSocketProvider, {ok, LSocket}} ->
+ {_Host, _Port, Options} =
+ ssh:update_lsocket(LSocket, LSocketProvider, Options0),
+ Options;
+ {error, Error} ->
+ {error, Error}
+ end.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+
+ssh_dbg_trace_points() -> [connections].
+
+ssh_dbg_flags(connections) -> [c].
+
+ssh_dbg_on(connections) ->
+ dbg:tpl(?MODULE, stop_listener, 1, x),
+ dbg:tpl(?MODULE, start_acceptor, 3, x).
+
+ssh_dbg_off(connections) ->
+ dbg:ctpl(?MODULE, stop_listener, 1),
+ dbg:ctpl(?MODULE, start_acceptor, 3).
+
+ssh_dbg_format(Tracepoint, Event = {call, {?MODULE, Function, Args}}) ->
+ [io_lib:format("~w:~w/~w> ~s", [?MODULE, Function, length(Args)] ++
+ ssh_dbg_comment(Tracepoint, Event))];
+ssh_dbg_format(Tracepoint, Event = {return_from, {?MODULE,Function,Arity}, Ret}) ->
+ [io_lib:format("~w:~w/~w returned ~W> ~s", [?MODULE, Function, Arity, Ret, 3] ++
+ ssh_dbg_comment(Tracepoint, Event))].
+
+ssh_dbg_comment(_, _) ->
+ [""].
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index bcf4c7f859..d889bcabf0 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -82,6 +82,7 @@
pass_phrase/1,
peername_sockname/1,
send/1,
+ parallel_login/1,
setopts_getopts/1,
shell/1,
shell_exit_status/1,
@@ -113,7 +114,6 @@ all() ->
[{group, all_tests}
].
-%%%-define(PARALLEL, ).
-define(PARALLEL, parallel).
groups() ->
@@ -121,9 +121,7 @@ groups() ->
{group, p_basic},
{group, internal_error},
{group, login_bad_pwd_no_retry},
- {group, key_cb}
- ]},
-
+ {group, key_cb}]},
{sequential, [], [app_test,
appup_test,
daemon_already_started,
@@ -149,21 +147,18 @@ groups() ->
login_bad_pwd_no_retry2,
login_bad_pwd_no_retry3,
login_bad_pwd_no_retry4,
- login_bad_pwd_no_retry5
- ]},
-
- {p_basic, [?PARALLEL], [send, peername_sockname,
- exec, exec_compressed,
+ login_bad_pwd_no_retry5]},
+ {p_basic, [?PARALLEL], [send, parallel_login, peername_sockname,
+ exec, exec_compressed,
exec_with_io_out, exec_with_io_in,
cli, cli_exit_normal, cli_exit_status,
idle_time_client, idle_time_server,
max_initial_idle_time,
openssh_zlib_basic_test,
misc_ssh_options, inet_option, inet6_option,
- shell, shell_socket, shell_ssh_conn, shell_no_unicode, shell_unicode_string,
- close
- ]}
- ].
+ shell, shell_socket, shell_ssh_conn,
+ shell_no_unicode, shell_unicode_string,
+ close]}].
%%--------------------------------------------------------------------
init_per_suite(Config) ->
@@ -1038,6 +1033,29 @@ send(Config) when is_list(Config) ->
ok = ssh_connection:send(ConnectionRef, ChannelId, << >>),
ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
+%%% Test parallel_login
+parallel_login(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
+ UserDir = proplists:get_value(priv_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {preferred_algorithms, ssh_transport:supported_algorithms()},
+ {user_dir, UserDir},
+ {parallel_login, true},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{preferred_algorithms, ssh_transport:supported_algorithms()},
+ {silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"Data">>),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, << >>),
+ ssh_info:print(fun(Fmt, Args) -> io:fwrite(user, Fmt, Args) end),
+ {Parents, Conns, Handshakers} =
+ ssh_test_lib:find_handshake_parent(Port),
+ ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
%%% Test ssh:connection_info([peername, sockname])
diff --git a/lib/ssh/test/ssh_dbg_SUITE.erl b/lib/ssh/test/ssh_dbg_SUITE.erl
index adeeb07b7d..fd55c4211c 100644
--- a/lib/ssh/test/ssh_dbg_SUITE.erl
+++ b/lib/ssh/test/ssh_dbg_SUITE.erl
@@ -192,7 +192,7 @@ dbg_connections(Config) ->
end},
{failfun, fun ssh_test_lib:failfun/2}]),
- ?DBG_RECEIVE("Starting LISTENER on ", Ref, _, Pid),
+ ?DBG_RECEIVE("ssh_acceptor:acceptor_init/4> Starting LISTENER on ", Ref, _, Pid),
C = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
@@ -435,7 +435,7 @@ dbg_channels(Config) ->
},
{failfun, fun ssh_test_lib:failfun/2}]),
- ?DBG_RECEIVE("Starting LISTENER on ", Ref, _, Pid),
+ ?DBG_RECEIVE("ssh_acceptor:acceptor_init/4> Starting LISTENER on ", Ref, _, Pid),
C = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 7b8d4e9767..5e685aaeb4 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -103,8 +103,6 @@
[{client2server,Ciphs}, {server2client,Ciphs}]
end)()
).
--define(v(Key, Config), proplists:get_value(Key, Config)).
--define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)).
-define(HARDCODED_KEXDH_REPLY,
#ssh_msg_kexdh_reply{
public_host_key = {{{'ECPoint',<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
@@ -116,11 +114,9 @@
92,210,130,249,103,2,215,245,7,213,110,235,136,134,11,
124,248,139,79,17,225,77,125,182,204,84,137,167,99,186,
167,42,192,10>>}).
-
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{seconds,40}}].
@@ -365,7 +358,7 @@ no_common_alg_server_disconnects(Config) ->
ssh_trpt_test_lib:exec(
[{set_options, [print_ops, {print_messages,detail}]},
{connect,
- server_host(Config),server_port(Config),
+ ssh_test_lib:server_host(Config),ssh_test_lib:server_port(Config),
[{silently_accept_hosts, true},
{user_dir, user_dir(Config)},
{user_interaction, false},
@@ -473,7 +466,7 @@ do_gex_client_init(Config, {Min,N,Max}, {G,P}) ->
ssh_trpt_test_lib:exec(
[{set_options, [print_ops, print_seqnums, print_messages]},
{connect,
- server_host(Config),server_port(Config),
+ ssh_test_lib:server_host(Config),ssh_test_lib:server_port(Config),
[{silently_accept_hosts, true},
{user_dir, user_dir(Config)},
{user_interaction, false},
@@ -508,7 +501,7 @@ do_gex_client_init_old(Config, N, {G,P}) ->
ssh_trpt_test_lib:exec(
[{set_options, [print_ops, print_seqnums, print_messages]},
{connect,
- server_host(Config),server_port(Config),
+ ssh_test_lib:server_host(Config),ssh_test_lib:server_port(Config),
[{silently_accept_hosts, true},
{user_dir, user_dir(Config)},
{user_interaction, false},
@@ -874,7 +867,7 @@ kex_strict_helper(Config, TestMessages, ExpectedReason) ->
{ok, _AfterKexState} =
ssh_trpt_test_lib:exec(
[{connect,
- server_host(Config),server_port(Config),
+ ssh_test_lib:server_host(Config),ssh_test_lib:server_port(Config),
[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
{cipher,?DEFAULT_CIPHERS}
]},
@@ -992,13 +985,12 @@ client_close_after_hello(Config0) ->
{max_sessions,MaxSessions},
{negotiation_timeout,SleepSec*1000}
]),
-
- {_Parents0, Conns0, []} = find_handshake_parent(server_port(Config)),
-
+ {_Parents0, Conns0, []} =
+ ssh_test_lib:find_handshake_parent(ssh_test_lib:server_port(Config)),
Cs =
[ssh_trpt_test_lib:exec(
[{connect,
- server_host(Config),server_port(Config),
+ ssh_test_lib:server_host(Config),ssh_test_lib:server_port(Config),
[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
{cipher,?DEFAULT_CIPHERS}
]},
@@ -1014,12 +1006,13 @@ client_close_after_hello(Config0) ->
ct:log("=== Tried to start ~p sessions.", [length(Cs)]),
ssh_info:print(fun ct:log/2),
- {Parents, Conns, Handshakers} = find_handshake_parent(server_port(Config)),
+ {Parents, Conns, Handshakers} =
+ ssh_test_lib:find_handshake_parent(ssh_test_lib:server_port(Config)),
ct:log("Found (Port=~p):~n"
" Connections (length ~p): ~p~n"
" Handshakers (length ~p): ~p~n"
" with parents (length ~p): ~p",
- [server_port(Config),
+ [ssh_test_lib:server_port(Config),
length(Conns), Conns,
length(Handshakers), Handshakers,
length(Parents), Parents]),
@@ -1030,12 +1023,13 @@ client_close_after_hello(Config0) ->
timer:sleep((SleepSec+15)*1000),
ct:log("After sleeping", []),
ssh_info:print(fun ct:log/2),
- {Parents2, Conns2, Handshakers2} = find_handshake_parent(server_port(Config)),
+ {Parents2, Conns2, Handshakers2} =
+ ssh_test_lib:find_handshake_parent(ssh_test_lib:server_port(Config)),
ct:log("Found (Port=~p):~n"
" Connections (length ~p): ~p~n"
" Handshakers (length ~p): ~p~n"
" with parents (length ~p): ~p",
- [server_port(Config),
+ [ssh_test_lib:server_port(Config),
length(Conns2), Conns2,
length(Handshakers2), Handshakers2,
length(Parents2), Parents2]),
@@ -1143,8 +1137,10 @@ start_std_daemon(Config, ExtraOpts) ->
stop_std_daemon(Config) ->
- ssh:stop_daemon(server_pid(Config)),
- ct:log("Std server ~p at ~p:~p stopped", [server_pid(Config), server_host(Config), server_port(Config)]),
+ ssh:stop_daemon(ssh_test_lib:server_pid(Config)),
+ ct:log("Std server ~p at ~p:~p stopped",
+ [ssh_test_lib:server_pid(Config), ssh_test_lib:server_host(Config),
+ ssh_test_lib:server_port(Config)]),
lists:keydelete(server, 1, Config).
@@ -1152,28 +1148,25 @@ check_std_daemon_works(Config, Line) ->
case std_connect(Config) of
{ok,C} ->
ct:log("Server ~p:~p ~p is ok at line ~p",
- [server_host(Config), server_port(Config),
- server_pid(Config), Line]),
+ [ssh_test_lib:server_host(Config), ssh_test_lib:server_port(Config),
+ ssh_test_lib:server_pid(Config), Line]),
ok = ssh:close(C),
Config;
Error = {error,_} ->
ct:fail("Standard server ~p:~p ~p is ill at line ~p: ~p",
- [server_host(Config), server_port(Config),
- server_pid(Config), Line, Error])
+ [ssh_test_lib:server_host(Config), ssh_test_lib:server_port(Config),
+ ssh_test_lib:server_pid(Config), Line, Error])
end.
-server_pid(Config) -> element(1,?v(server,Config)).
-server_host(Config) -> element(2,?v(server,Config)).
-server_port(Config) -> element(3,?v(server,Config)).
-
server_user_password(Config) -> server_user_password(1, Config).
server_user_password(N, Config) -> lists:nth(N, ?v(user_passwords,Config)).
-std_connect(Config) ->
- std_connect({server_host(Config), server_port(Config)}, Config).
-
+std_connect(Config) ->
+ std_connect({ssh_test_lib:server_host(Config),
+ ssh_test_lib:server_port(Config)}, Config).
+
std_connect({Host,Port}, Config) ->
std_connect({Host,Port}, Config, []).
@@ -1200,7 +1193,7 @@ connect_and_kex(Config) ->
connect_and_kex(Config, InitialState) ->
ssh_trpt_test_lib:exec(
[{connect,
- server_host(Config),server_port(Config),
+ ssh_test_lib:server_host(Config),ssh_test_lib:server_port(Config),
[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
{cipher,?DEFAULT_CIPHERS}
]},
@@ -1233,44 +1226,3 @@ disconnect(Code) ->
tcp_closed,
{tcp_error,econnaborted}
]}.
-
-%%%----------------------------------------------------------------
-find_handshake_parent(Port) ->
- Acc = {_Parents=[], _Connections=[], _Handshakers=[]},
- find_handshake_parent(supervisor:which_children(sshd_sup), Port, Acc).
-
-
-find_handshake_parent([{{ssh_system_sup,{address,_,Port,_}},
- Pid,supervisor, [ssh_system_sup]}|_],
- Port, Acc) ->
- find_handshake_parent(supervisor:which_children(Pid), Port, Acc);
-
-find_handshake_parent([{{ssh_acceptor_sup,{address,_,Port,_}},
- PidS,supervisor,[ssh_acceptor_sup]}|T],
- Port, {AccP,AccC,AccH}) ->
- ParentHandshakers =
- [{PidW,PidH} ||
- {{ssh_acceptor_sup,{address,_,Port1,_}}, PidW, worker,
- [ssh_acceptor]} <- supervisor:which_children(PidS),
- Port1 == Port,
- PidH <- element(2, process_info(PidW,links)),
- is_pid(PidH),
- process_info(PidH,current_function) ==
- {current_function,
- {ssh_connection_handler,handshake,4}}],
- {Parents,Handshakers} = lists:unzip(ParentHandshakers),
- find_handshake_parent(T, Port, {AccP++Parents, AccC, AccH++Handshakers});
-
-find_handshake_parent([{_Ref,PidS,supervisor,[ssh_connection_sup]}|T],
- Port, {AccP,AccC,AccH}) ->
- Connections =
- [Pid ||
- {connection,Pid,worker,[ssh_connection_handler]} <-
- supervisor:which_children(PidS)],
- find_handshake_parent(T, Port, {AccP, AccC++Connections, AccH});
-
-find_handshake_parent([_|T], Port, Acc) ->
- find_handshake_parent(T, Port, Acc);
-
-find_handshake_parent(_, _, {AccP,AccC,AccH}) ->
- {lists:usort(AccP), lists:usort(AccC), lists:usort(AccH)}.
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index e6245d65f1..3c2541aca4 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -48,6 +48,7 @@
-define(SSHC_SUP(Pid), {sshc_sup, Pid, supervisor, [supervisor]}).
-define(SSHD_SUP(Pid), {sshd_sup, Pid, supervisor, [supervisor]}).
+-define(LSOCKET_SUP(Pid), {ssh_lsocket_sup, Pid, supervisor, [ssh_lsocket_sup]}).
-define(SYSTEM_SUP(Pid,Address),
{{ssh_system_sup, Address}, Pid, supervisor,[ssh_system_sup]}).
-define(CONNECTION_SUP(Pid), {_,Pid, supervisor,[ssh_connection_sup]}).
@@ -116,7 +117,8 @@ default_tree(Config) when is_list(Config) ->
{value, ?SSHC_SUP(_)} = lists:keysearch(sshc_sup, 1, TopSupChildren),
{value, ?SSHD_SUP(_)} = lists:keysearch(sshd_sup, 1, TopSupChildren),
?wait_match([], supervisor:which_children(sshc_sup)),
- ?wait_match([], supervisor:which_children(sshd_sup)).
+ ?wait_match([?LSOCKET_SUP(_)],
+ supervisor:which_children(sshd_sup)).
%%-------------------------------------------------------------------------
sshc_subtree(Config) when is_list(Config) ->
@@ -160,14 +162,15 @@ sshd_subtree(Config) when is_list(Config) ->
ct:log("Expect HostIP=~p, Port=~p, Daemon=~p",[HostIP,Port,Daemon]),
?wait_match([?SYSTEM_SUP(Daemon, #address{address=ListenIP,
port=Port,
- profile=?DEFAULT_PROFILE})],
+ profile=?DEFAULT_PROFILE}),
+ ?LSOCKET_SUP(_)],
supervisor:which_children(sshd_sup),
[ListenIP,Daemon]),
true = ssh_test_lib:match_ip(HostIP, ListenIP),
check_sshd_system_tree(Daemon, HostIP, Port, Config),
ssh:stop_daemon(HostIP, Port),
ct:sleep(?WAIT_FOR_SHUTDOWN),
- ?wait_match([], supervisor:which_children(sshd_sup)).
+ ?wait_match([?LSOCKET_SUP(_)], supervisor:which_children(sshd_sup)).
%%-------------------------------------------------------------------------
sshd_subtree_profile(Config) when is_list(Config) ->
@@ -181,14 +184,15 @@ sshd_subtree_profile(Config) when is_list(Config) ->
ct:log("Expect HostIP=~p, Port=~p, Profile=~p, Daemon=~p",[HostIP,Port,Profile,Daemon]),
?wait_match([?SYSTEM_SUP(Daemon, #address{address=ListenIP,
port=Port,
- profile=Profile})],
+ profile=Profile}),
+ ?LSOCKET_SUP(_)],
supervisor:which_children(sshd_sup),
[ListenIP,Daemon]),
true = ssh_test_lib:match_ip(HostIP, ListenIP),
check_sshd_system_tree(Daemon, HostIP, Port, Config),
ssh:stop_daemon(HostIP, Port, Profile),
ct:sleep(?WAIT_FOR_SHUTDOWN),
- ?wait_match([], supervisor:which_children(sshd_sup)).
+ ?wait_match([?LSOCKET_SUP(_)], supervisor:which_children(sshd_sup)).
%%-------------------------------------------------------------------------
killed_acceptor_restarts(Config) ->
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 53e916df80..5f9fe2f540 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -124,7 +124,11 @@ setup_known_host/3,
get_addr_str/0,
file_base_name/2,
kex_strict_negotiated/2,
-event_logged/3
+event_logged/3,
+server_host/1,
+server_port/1,
+server_pid/1,
+find_handshake_parent/1
]).
%% logger callbacks and related helpers
-export([log/2,
@@ -1372,3 +1376,43 @@ log(LogEvent = #{level:=_Level,msg:=_Msg,meta:=_Meta},
#{test_ref := TestRef, recipient := Recipient}) ->
Recipient ! {TestRef, LogEvent},
ok.
+
+server_pid(Config) -> element(1,?v(server,Config)).
+server_host(Config) -> element(2,?v(server,Config)).
+server_port(Config) -> element(3,?v(server,Config)).
+
+%%%----------------------------------------------------------------
+find_handshake_parent(Port) ->
+ Acc = {_Parents=[], _Connections=[], _Handshakers=[]},
+ find_handshake_parent(supervisor:which_children(sshd_sup), Port, Acc).
+
+find_handshake_parent([{{ssh_system_sup,{address,_,Port,_}},
+ Pid,supervisor, [ssh_system_sup]}|_],
+ Port, Acc) ->
+ find_handshake_parent(supervisor:which_children(Pid), Port, Acc);
+find_handshake_parent([{{ssh_acceptor_sup,{address,_,Port,_}},
+ PidS,supervisor,[ssh_acceptor_sup]}|T],
+ Port, {AccP,AccC,AccH}) ->
+ ParentHandshakers =
+ [{PidW,PidH} ||
+ {{ssh_acceptor_sup,{address,_,Port1,_}}, PidW, worker,
+ [ssh_acceptor]} <- supervisor:which_children(PidS),
+ Port1 == Port,
+ PidH <- element(2, process_info(PidW,links)),
+ is_pid(PidH),
+ process_info(PidH,current_function) ==
+ {current_function,
+ {ssh_connection_handler,handshake,4}}],
+ {Parents,Handshakers} = lists:unzip(ParentHandshakers),
+ find_handshake_parent(T, Port, {AccP++Parents, AccC, AccH++Handshakers});
+find_handshake_parent([{_Ref,PidS,supervisor,[ssh_connection_sup]}|T],
+ Port, {AccP,AccC,AccH}) ->
+ Connections =
+ [Pid ||
+ {connection,Pid,worker,[ssh_connection_handler]} <-
+ supervisor:which_children(PidS)],
+ find_handshake_parent(T, Port, {AccP, AccC++Connections, AccH});
+find_handshake_parent([_|T], Port, Acc) ->
+ find_handshake_parent(T, Port, Acc);
+find_handshake_parent(_, _, {AccP,AccC,AccH}) ->
+ {lists:usort(AccP), lists:usort(AccC), lists:usort(AccH)}.
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index 6f782367ae..05ba19a1a6 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -10,6 +10,8 @@
%% Timeout time in ms
%%-------------------------------------------------------------------------
-define(TIMEOUT, 15000).
+-define(v(Key, Config), proplists:get_value(Key, Config)).
+-define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)).
%%-------------------------------------------------------------------------
%% Check for usable crypto
--
2.43.0