diff --git a/lib/ssh/internal_doc/ssh_notes.md b/lib/ssh/internal_doc/ssh_notes.md
index 2d3b8c25ebd4..ef8705fa153d 100644
--- a/lib/ssh/internal_doc/ssh_notes.md
+++ b/lib/ssh/internal_doc/ssh_notes.md
@@ -1,34 +1,73 @@
-# SSH supervision tree (prototype)
+# SSH supervision tree (server side update >= OTP-28)
```mermaid
---
-title: SSH supervision tree (prototype)
+title: SSH supervision tree
---
flowchart RL
- d_sup --> sup[["ssh_sup\n(ssh_app.erl)\n[o4o]"]]
+ d_sup --> sup[["ssh_sup
(ssh_app.erl)
[o4o]"]]
c_sup --> sup
subgraph client
- connection_sup --> c_sup[["sshc_sup\n(ssh_app.erl)\n[o4o]\nauto_shutdown=never"]]
+ connection_sup --> c_sup[["sshc_sup
(ssh_app.erl)
[o4o]
auto_shutdown=never"]]
subgraph connection_c
- connection_handler["ssh_connection_handler\nSIGNIFICANT"] --> connection_sup[["ssh_connection_sup\n[o4a]\nauto_shutdown=any_significant"]]
- channel_sup[["ssh_channel_sup\n[o4o]"]] --> connection_sup
+ connection_handler["ssh_connection_handler
SIGNIFICANT"] --> connection_sup[["ssh_connection_sup
[o4a]
auto_shutdown=any_significant"]]
+ channel_sup[["ssh_channel_sup
[o4o]"]] --> connection_sup
sftp["ssh_sftp"] --> channel_sup
- tcpip_forward_acceptor_sup[["ssh_tcpip_forward_acceptor_sup\n[o4o]"]] --> connection_sup
+ tcpip_forward_acceptor_sup[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> connection_sup
+ ssh_tcpip_forward_acceptor["ssh_tcpip_forward_acceptor"] --> tcpip_forward_acceptor_sup
+ end
+ end
+
+ subgraph server
+ lsocket_sup[["ssh_lsocket_sup
[simple_one_for_one]"]] --> d_sup
+ ssh_lsocket_provider --> lsocket_sup
+ system_sup_s --> d_sup[["sshd_sup
(ssh_app.erl)
[o4o]"]]
+ acceptor_sup --> system_sup_s[["ssh_system_sup
[o4o]
auto_shutdown=all_significant"]]
+ acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup
[o4o?]
SIGNIFICANT"]]
+
+ connection_sup_s --> system_sup_s
+
+ subgraph connection_s
+ connection_handler_s["ssh_connection_handler
SIGNIFICANT"] --> connection_sup_s[["ssh_connection_sup
[o4a]
auto_shutdown=any_significant
SIGNIFICANT"]]
+ channel_sup_s[["ssh_channel_sup
[o4o]"]] --> connection_sup_s
+ tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> connection_sup_s
+ ssh_tcpip_forward_acceptor_s["ssh_tcpip_forward_acceptor"] --> tcpip_forward_acceptor_sup_s
+ sftd1["ssh_sftpd"] --> channel_sup_s
+ end
+ end
+```
+
+# SSH supervision tree (client side update since ssh-5.2.3, ssh-5.1.4.3, ssh-4.15.3.7)
+```mermaid
+---
+title: SSH supervision tree
+---
+flowchart RL
+ d_sup --> sup[["ssh_sup
(ssh_app.erl)
[o4o]"]]
+ c_sup --> sup
+
+ subgraph client
+ connection_sup --> c_sup[["sshc_sup
(ssh_app.erl)
[o4o]
auto_shutdown=never"]]
+ subgraph connection_c
+ connection_handler["ssh_connection_handler
SIGNIFICANT"] --> connection_sup[["ssh_connection_sup
[o4a]
auto_shutdown=any_significant"]]
+ channel_sup[["ssh_channel_sup
[o4o]"]] --> connection_sup
+ sftp["ssh_sftp"] --> channel_sup
+ tcpip_forward_acceptor_sup[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> connection_sup
ssh_tcpip_forward_acceptor["ssh_tcpip_forward_acceptor"] --> tcpip_forward_acceptor_sup
end
end
subgraph server
- system_sup_s --> d_sup[["sshd_sup\n(ssh_app.erl)\n[o4o]"]]
- acceptor_sup --> system_sup_s[["ssh_system_sup\n[o4o]\nauto_shutdown=all_significant"]]
- acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup\n[o4o]\nSIGNIFICANT"]]
+ system_sup_s --> d_sup[["sshd_sup
(ssh_app.erl)
[o4o]"]]
+ acceptor_sup --> system_sup_s[["ssh_system_sup
[o4o]
auto_shutdown=all_significant"]]
+ acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup
[o4o]
SIGNIFICANT"]]
connection_sup_s --> system_sup_s
subgraph connection_s
- connection_handler_s["ssh_connection_handler\nSIGNIFICANT"] --> connection_sup_s[["ssh_connection_sup\n[o4a]\nauto_shutdown=any_significant\nSIGNIFICANT"]]
- channel_sup_s[["ssh_channel_sup\n[o4o]"]] --> connection_sup_s
- tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup\n[o4o]"]] --> connection_sup_s
+ connection_handler_s["ssh_connection_handler
SIGNIFICANT"] --> connection_sup_s[["ssh_connection_sup
[o4a]
auto_shutdown=any_significant
SIGNIFICANT"]]
+ channel_sup_s[["ssh_channel_sup
[o4o]"]] --> connection_sup_s
+ tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> connection_sup_s
ssh_tcpip_forward_acceptor_s["ssh_tcpip_forward_acceptor"] --> tcpip_forward_acceptor_sup_s
sftd1["ssh_sftpd"] --> channel_sup_s
end
@@ -41,33 +80,32 @@ flowchart RL
title: SSH supervision tree (OTP >= 24)
---
flowchart RL
- d_sup --> sup[["ssh_sup\n(ssh_app.erl)\n[o4o]"]]
+ d_sup --> sup[["ssh_sup
(ssh_app.erl)
[o4o]"]]
c_sup --> sup
subgraph client
- system_sup --> c_sup[["sshc_sup\n(ssh_app.erl)\n[o4o]\nauto_shutdown=never"]]
+ system_sup --> c_sup[["sshc_sup
(ssh_app.erl)
[o4o]
auto_shutdown=never"]]
subgraph connection_c
- subsystem_sup --> system_sup[["ssh_system_sup\n[o4o]\nauto_shutdown=all_significant"]]
- connection_handler["ssh_connection_handler\nSIGNIFICANT"] --> subsystem_sup[["ssh_subsystem_sup\n[o4a]\nauto_shutdown=any_significant\nSIGNIFICANT"]]
- channel_sup[["ssh_channel_sup\n[o4o]"]] --> subsystem_sup
+ subsystem_sup --> system_sup[["ssh_system_sup
[o4o]
auto_shutdown=all_significant"]]
+ connection_handler["ssh_connection_handler
SIGNIFICANT"] --> subsystem_sup[["ssh_subsystem_sup
[o4a]
auto_shutdown=any_significant
SIGNIFICANT"]]
+ channel_sup[["ssh_channel_sup
[o4o]"]] --> subsystem_sup
sftp["ssh_sftp"] --> channel_sup
ssh_tcpip_forward_client --> channel_sup
- tcpip_forward_acceptor_sup[["ssh_tcpip_forward_acceptor_sup\n[o4o]"]] --> subsystem_sup
- ssh_tcpip_forward_acceptor["ssh_tcpip_forward_acceptor"] --> tcpip_forward_acceptor_sup
+ tcpip_forward_acceptor_sup[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> subsystem_sup
end
end
subgraph server
- system_sup_s --> d_sup[["sshd_sup\n(ssh_app.erl)\n[o4o]"]]
- acceptor_sup --> system_sup_s[["ssh_system_sup\n[o4o]\nauto_shutdown=all_significant"]]
- acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup\n[o4o]\nSIGNIFICANT"]]
-
+ system_sup_s --> d_sup[["sshd_sup
(ssh_app.erl)
[o4o]"]]
+ acceptor_sup --> system_sup_s[["ssh_system_sup
[o4o]
auto_shutdown=all_significant"]]
+ acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup
[o4o]
SIGNIFICANT"]]
+ acceptor_worker["acceptor
(parallel_login)"] o-. link .-o acceptor
subsystem_sup_s --> system_sup_s
subgraph connection_s
- connection_handler_s["ssh_connection_handler\nSIGNIFICANT"] --> subsystem_sup_s[["ssh_subsystem_sup\n[o4a]\nauto_shutdown=any_significant\nSIGNIFICANT"]]
- channel_sup_s[["ssh_channel_sup\n[o4o]"]] --> subsystem_sup_s
- tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup\n[o4o]"]] --> subsystem_sup_s
+ connection_handler_s["ssh_connection_handler
SIGNIFICANT"] --> subsystem_sup_s[["ssh_subsystem_sup
[o4a]
auto_shutdown=any_significant
SIGNIFICANT"]]
+ channel_sup_s[["ssh_channel_sup
[o4o]"]] --> subsystem_sup_s
+ tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> subsystem_sup_s
ssh_tcpip_forward_acceptor_s["ssh_tcpip_forward_acceptor"] --> tcpip_forward_acceptor_sup_s
sftd1["ssh_sftpd"] --> channel_sup_s
ssh_tcpip_forward_srv --> channel_sup_s
@@ -83,24 +121,24 @@ flowchart RL
title: SSH supervision tree (OTP-22)
---
flowchart RL
- d_sup --> sup[["ssh_sup\n(ssh_app.erl)\n[o4o]"]]
+ d_sup --> sup[["ssh_sup
(ssh_app.erl)
[o4o]"]]
c_sup --> sup
subgraph client
- connection_handler["ssh_connection_handler\nSIGNIFICANT?"] --> c_sup
+ connection_handler["ssh_connection_handler
SIGNIFICANT?"] --> c_sup
end
subgraph server
- system_sup_s --> d_sup[["sshd_sup\n(ssh_app.erl)\n[o4o]"]]
- acceptor_sup --> system_sup_s[["ssh_system_sup\n[o4o]\nauto_shutdown=all_significant"]]
- acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup\n[o4o]\nSIGNIFICANT"]]
+ system_sup_s --> d_sup[["sshd_sup
(ssh_app.erl)
[o4o]"]]
+ acceptor_sup --> system_sup_s[["ssh_system_sup
[o4o]
auto_shutdown=all_significant"]]
+ acceptor["ssh_acceptor"] --> acceptor_sup[["ssh_acceptor_sup
[o4o]
SIGNIFICANT"]]
subsystem_sup_s --> system_sup_s
subgraph connection_s
- connection_handler_s["ssh_connection_handler\nSIGNIFICANT"] --> subsystem_sup_s[["ssh_subsystem_sup\n[o4a]\nauto_shutdown=any_significant\nSIGNIFICANT"]]
- channel_sup_s[["ssh_channel_sup\n[o4o]"]] --> subsystem_sup_s
- tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup\n[o4o]"]] --> subsystem_sup_s
+ connection_handler_s["ssh_connection_handler
SIGNIFICANT"] --> subsystem_sup_s[["ssh_subsystem_sup
[o4a]
auto_shutdown=any_significant
SIGNIFICANT"]]
+ channel_sup_s[["ssh_channel_sup
[o4o]"]] --> subsystem_sup_s
+ tcpip_forward_acceptor_sup_s[["ssh_tcpip_forward_acceptor_sup
[o4o]"]] --> subsystem_sup_s
sftd1["ssh_sftpd"] --> channel_sup_s
end
end
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index a8a088eb7daf..f59c997a1684 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 6cbbc168ac22..a59fd58ad956 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 4b078a7b8901..c651d336fbc0 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]).
@@ -552,13 +553,10 @@ daemon(Socket, UserOptions) ->
{error,SockError} ->
{error,SockError}
end;
-
{error,OptionError} ->
{error,OptionError}
end.
-
-
-doc """
daemon(HostAddress, Port, Options) -> Result
@@ -589,74 +587,60 @@ The rules for handling the two address passing options are:
is set to the value of the 'ip'-option
""".
-spec daemon(any | inet:ip_address(), inet:port_number(), daemon_options()) -> {ok,daemon_ref()} | {error,term()}
- ;(socket, open_socket(), daemon_options()) -> {ok,daemon_ref()} | {error,term()}
- .
+ ;(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}}
- end;
+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, LSocket}} ->
+ {Host, Port, Options1} =
+ update_lsocket(LSocket, LSocketProvider, Options0),
+ try
+ %% Host,Port is what to use for the system
+ %% supervisor to register its name (see
+ %% #address record); LSocket is owned by
+ %% LSocketProvider process. Ownership will be
+ %% transferred once ssh_acceptor_sup is
+ %% started.
+ %% 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, _}} -> % ssh_system_sup with #address already register
+ close_listen_socket(LSocket, Options1),
+ {error, eaddrinuse};
+ {error, Error} ->
+ close_listen_socket(LSocket, Options1),
+ {error, Error}
+ catch
+ error:{shutdown, Err} -> % no suitable host key
+ close_listen_socket(LSocket, Options1),
+ {error, Err};
+ exit:{noproc, _} -> % ssh application not started
+ close_listen_socket(LSocket, Options1),
+ {error, ssh_not_started};
+ error:Error ->
+ close_listen_socket(LSocket, Options1),
+ {error, Error};
+ _C:_E ->
+ {error,{cannot_start_daemon,_C,_E}}
+ end;
+ {error, {_, LSocketError}} ->
+ {error, LSocketError}
+ end;
+ OptionError = {error, _} ->
+ OptionError
+ end;
daemon(_, _, _) ->
{error, badarg}.
@@ -682,9 +666,9 @@ chapters [Configuration in SSH](configurations.md) and
NewUserOptions :: daemon_options().
daemon_replace_options(DaemonRef, NewUserOptions) ->
- {ok,Os0} = ssh_system_sup:get_acceptor_options(DaemonRef),
- Os1 = ssh_options:merge_options(server, NewUserOptions, Os0),
- ssh_system_sup:replace_acceptor_options(DaemonRef, Os1).
+ {ok, Options0} = ssh_system_sup:get_acceptor_options(DaemonRef),
+ Options = ssh_options:merge_options(server, NewUserOptions, Options0),
+ ssh_system_sup:restart_acceptor(DaemonRef, Options).
%%--------------------------------------------------------------------
-doc """
@@ -1239,13 +1223,11 @@ fp_fmt(b64, Bin) ->
%%--------------------------------------------------------------------
%% The handle_daemon_args/2 function basically only sets the ip-option in Opts
%% so that it is correctly set when opening the listening socket.
-
handle_daemon_args(any, Opts) ->
case proplists:get_value(ip, Opts) of
undefined -> {any, Opts};
IP -> {IP, Opts}
end;
-
handle_daemon_args(IPaddr, Opts) when is_tuple(IPaddr) ; IPaddr == loopback ->
case proplists:get_value(ip, Opts) of
undefined -> {IPaddr, [{ip,IPaddr}|Opts]};
@@ -1253,7 +1235,6 @@ handle_daemon_args(IPaddr, Opts) when is_tuple(IPaddr) ; IPaddr == loopback ->
IP -> {IPaddr, [{ip,IPaddr}|Opts--[{ip,IP}]]} %% Backward compatibility
end.
-%%%----------------------------------------------------------------
valid_socket_to_use(Socket, {tcp,_,_}) ->
%% Is this tcp-socket a valid socket?
try {is_tcp_socket(Socket),
@@ -1277,29 +1258,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,22 +1266,16 @@ 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)).
-
-%%%----------------------------------------------------------------
+
-doc false.
is_host(X, Opts) ->
try is_host1(mangle_connect_address(X, Opts))
catch
_:_ -> false
end.
-
is_host1(L) when is_list(L) -> true; %% "string()"
is_host1(T) when tuple_size(T)==4 -> lists:all(fun(I) -> 0= lists:all(fun(I) -> 0= true.
-%%%----------------------------------------------------------------
mangle_connect_address(A, #{socket_options := SockOpts}) ->
mangle_connect_address(A, SockOpts);
mangle_connect_address(A, SockOpts) ->
@@ -1353,7 +1304,6 @@ mangle_connect_address1(A, _) ->
_ -> A
end.
-%%%----------------------------------------------------------------
mangle_tunnel_address(any) -> <<"">>;
mangle_tunnel_address(loopback) -> <<"localhost">>;
mangle_tunnel_address({0,0,0,0}) -> <<"">>;
@@ -1365,7 +1315,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 +1328,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 a558a888d2c2..62528d2e710a 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
+%% 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
- 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 1661134285ec..9b710dc44c13 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -20,7 +20,7 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: The acceptor supervisor for ssh servers hangs under
+%% Purpose: The acceptor supervisor for ssh servers hangs under
%% ssh_system_sup.
%%----------------------------------------------------------------------
@@ -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 5f0c01e7b815..91c688f64d7c 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_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 6502fed79849..8746c2bbd4e8 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -2154,16 +2154,20 @@ ssh_dbg_format(connections, {call, {?MODULE,init, [[Role, Sock, Opts]]}}) ->
end
end,
Opts),
- {ok, {IPp,Portp}} = inet:peername(Sock),
- {ok, {IPs,Ports}} = inet:sockname(Sock),
+ Addresses =
+ case {inet:peername(Sock), inet:sockname(Sock)} of
+ {{ok, {IPp,Portp}}, {ok, {IPs,Ports}}} ->
+ io_lib:format("Socket = ~p, Peer = ~s, Local = ~s,~n"
+ "Non-default options:~n~p",
+ [Sock, ssh_lib:format_address_port(IPp,Portp),
+ ssh_lib:format_address_port(IPs,Ports), NonDefaultOpts]);
+ {E1, E2} ->
+ io_lib:format("Socket = ~p, Peer = ~p, Local = ~p,~n"
+ "Non-default options:~n~p",
+ [Sock, E1, E2, NonDefaultOpts])
+ end,
[io_lib:format("Starting ~p connection:\n",[Role]),
- io_lib:format("Socket = ~p, Peer = ~s, Local = ~s,~n"
- "Non-default options:~n~p",
- [Sock,
- ssh_lib:format_address_port(IPp,Portp),
- ssh_lib:format_address_port(IPs,Ports),
- NonDefaultOpts])
- ];
+ Addresses];
ssh_dbg_format(connections, F) ->
ssh_dbg_format(terminate, F);
diff --git a/lib/ssh/src/ssh_lsocket.erl b/lib/ssh/src/ssh_lsocket.erl
new file mode 100644
index 000000000000..e92d88bac157
--- /dev/null
+++ b/lib/ssh/src/ssh_lsocket.erl
@@ -0,0 +1,175 @@
+%%
+%% %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/4, 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) ->
+ try
+ supervisor:start_child(ssh_lsocket_sup, [self(), Host, Port, Options])
+ of
+ {ok, LSocketProvider} ->
+ receive
+ Result = {_, {LSocketProvider, _}} ->
+ Result
+ after
+ ?DEFAULT_TIMEOUT ->
+ {error, LSocketProvider, no_response_from_lsocket_provider}
+ end
+ catch
+ exit:{noproc, _} ->
+ {error, {no_provider_pid, ssh_not_started}}
+ end.
+
+start_link(Caller, Host, Port, Options) ->
+ {ok, proc_lib:spawn_link(?MODULE, provide_lsocket,
+ [Caller, Host, Port, Options])}.
+
+provide_lsocket(Caller, _Host1, Port0, Options) ->
+ ListenResult =
+ try
+ try_listen(Port0, Options, 4)
+ of
+ {ok, LSocket} ->
+ {ok, {self(), LSocket}};
+ {error, Details} ->
+ {error, {self(), Details}}
+ catch
+ _Class:Exception ->
+ {error, {self(), Exception}}
+ end,
+ case ListenResult of
+ {ok, {_, LSocket1}} ->
+ Caller ! ListenResult,
+ wait_for_acceptor_sup(LSocket1, Options),
+ ok;
+ {error, {_, _}} ->
+ Caller ! ListenResult
+ 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
+ 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, 4, 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, 4),
+ 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 000000000000..1922268c3734
--- /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_options.erl b/lib/ssh/src/ssh_options.erl
index 7aaeb4603dee..6a55954bd0ac 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -229,7 +229,6 @@ handle_options(Role, OptsList0, Opts0) when is_map(Opts0),
%% Enter the user's values into the map; unknown keys are
%% treated as socket options
check_and_save(OptsList2, OptionDefinitions, InitialMap)
-
catch
error:{EO, KV, Reason} when EO == eoptions ; EO == eerl_env ->
if
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index bc1d79d0f49a..f6b78d4e738f 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,Error}
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, LSocket}} ->
+ {_Host, _Port, Options} =
+ ssh:update_lsocket(LSocket, LSocketProvider, Options0),
+ Options;
+ 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 bcf4c7f859e1..c41a200f3087 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,
@@ -138,32 +136,25 @@ groups() ->
known_hosts,
ssh_file_is_host_key,
ssh_file_is_host_key_misc,
- ssh_file_is_auth_key
- ]},
-
+ ssh_file_is_auth_key]},
{key_cb, [?PARALLEL], [key_callback, key_callback_options]},
-
{internal_error, [?PARALLEL], [internal_error]},
-
{login_bad_pwd_no_retry, [?PARALLEL], [login_bad_pwd_no_retry1,
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 +1029,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])
@@ -1502,7 +1516,6 @@ check_error(Error) -> ct:fail(Error).
basic_test(Config) ->
ClientOpts = proplists:get_value(client_opts, Config),
ServerOpts = proplists:get_value(server_opts, Config),
-
{Pid, Host, Port} = ssh_test_lib:daemon(ServerOpts),
CM = ssh_test_lib:connect(Host, Port, ClientOpts),
ok = ssh:close(CM),
diff --git a/lib/ssh/test/ssh_dbg_SUITE.erl b/lib/ssh/test/ssh_dbg_SUITE.erl
index adeeb07b7d18..fd55c4211c37 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 7b8d4e9767be..58d1b7c310bf 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -94,16 +94,9 @@
[{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)).
-
-
%%--------------------------------------------------------------------
%% 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}
]},
@@ -1010,16 +1002,15 @@ client_close_after_hello(Config0) ->
]},
{send, hello}
]) || _ <- lists:seq(1,MaxSessions+100)],
-
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 +1021,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 +1135,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 +1146,24 @@ 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 +1190,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 +1223,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 e6245d65f1ad..3c2541aca4b5 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 53e916df8081..5f9fe2f5405f 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 6f782367ae5a..05ba19a1a647 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, 27000).
+-define(v(Key, Config), proplists:get_value(Key, Config)).
+-define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)).
%%-------------------------------------------------------------------------
%% Check for usable crypto