File 3821-ssh-New-connection-start-procedure.patch of Package erlang

From f32fc320cbffa31554b5468d7b491e63bfc05d68 Mon Sep 17 00:00:00 2001
From: Hans Nilsson <hans@erlang.org>
Date: Thu, 3 Dec 2020 09:55:53 +0100
Subject: [PATCH 1/2] ssh: New connection start procedure

---
 lib/ssh/src/ssh.erl                    |  72 ++----
 lib/ssh/src/ssh_acceptor.erl           |  62 ++---
 lib/ssh/src/ssh_connection_handler.erl | 330 +++++++++++--------------
 lib/ssh/src/sshc_sup.erl               |   9 +-
 lib/ssh/src/sshd_sup.erl               |  22 +-
 lib/ssh/test/ssh_sup_SUITE.erl         |  13 +-
 6 files changed, 222 insertions(+), 286 deletions(-)

diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 023f6d884d..9abb0fdebb 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -140,16 +140,15 @@ connect(Socket, UserOptions, NegotiationTimeout) when is_list(UserOptions) ->
     case ssh_options:handle_options(client, UserOptions) of
 	{error, Error} ->
 	    {error, Error};
-	Options ->
-           case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
-               ok ->
-                   connect_socket(Socket,
-                                  ?PUT_INTERNAL_OPT({connected_socket,Socket}, Options),
-                                  NegotiationTimeout);
-               {error,SockError} ->
-                   {error,SockError}
-           end
-        end.
+
+	Options = #{} ->
+            case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
+                ok ->
+                    ssh_connection_handler:start_connection(client, Socket, Options, NegotiationTimeout);
+                {error,SockError} ->
+                    {error,SockError}
+            end
+    end.
 
 
 -spec connect(Host, Port, Options, NegotiationTimeout) -> {ok,connection_ref()} | {error,term()} when
@@ -162,44 +161,25 @@ connect(Host0, Port, UserOptions, NegotiationTimeout) when is_integer(Port),
                                                            Port>0,
                                                            is_list(UserOptions) ->
     case ssh_options:handle_options(client, UserOptions) of
-	{error, _Reason} = Error ->
-	    Error;
+	{error, Reason} ->
+            {error, Reason};
+        
         Options ->
-	    {_, Transport, _} = TransportOpts = ?GET_OPT(transport, Options),
-	    ConnectionTimeout = ?GET_OPT(connect_timeout, Options),
-            SocketOpts = [{active,false} | ?GET_OPT(socket_options,Options)],
-            Host = mangle_connect_address(Host0, SocketOpts),
-	    try Transport:connect(Host, Port, SocketOpts, ConnectionTimeout) of
-		{ok, Socket} ->
-                    connect_socket(Socket,
-                                   ?PUT_INTERNAL_OPT({host,Host}, Options),
-                                   NegotiationTimeout);
-		{error, Reason} ->
-		    {error, Reason}
-	    catch
-		exit:{function_clause, _F} ->
-		    {error, {options, {transport, TransportOpts}}};
-		exit:badarg ->
-		    {error, {options, {socket_options, SocketOpts}}}
-	    end
+            SockOpts = ?GET_OPT(socket_options, Options),
+            Host = mangle_connect_address(Host0, SockOpts),
+            {_, Callback, _} = ?GET_OPT(transport, Options),
+            SocketOpts = [{active,false} | SockOpts],
+            ConnectionTimeout = ?GET_OPT(connect_timeout, Options),
+            try Callback:connect(Host, Port, SocketOpts, ConnectionTimeout) of
+                {ok, Socket} ->
+                    ssh_connection_handler:start_connection(client, Socket, Options, NegotiationTimeout);
+                {error, Reason} ->
+                    {error, Reason}
+            catch
+                _:Error -> {error, Error}
+            end
     end.
 
-
-connect_socket(Socket, Options0, NegotiationTimeout) ->
-    {ok, {Host,Port}} = inet:sockname(Socket),
-    Profile = ?GET_OPT(profile, Options0),
-
-    {ok, {SystemSup, SubSysSup}} = sshc_sup:start_system_subsystem(Host, Port, Profile, Options0),
-
-    ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
-    Opts = ?PUT_INTERNAL_OPT([{user_pid,self()},
-                              {supervisors, [{system_sup, SystemSup},
-                                             {subsystem_sup, SubSysSup},
-                                             {connection_sup, ConnectionSup}]}
-                             ], Options0),
-    ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout).
-
-
 %%--------------------------------------------------------------------
 -spec close(ConnectionRef) -> ok | {error,term()} when
       ConnectionRef :: connection_ref() .
@@ -841,6 +821,8 @@ is_host1(T) when is_tuple(T), size(T)==16 -> lists:all(fun(I) -> 0=<I andalso I=
 is_host1(loopback) -> true.
 
 %%%----------------------------------------------------------------
+mangle_connect_address(A,  #{socket_options := SockOpts}) ->
+    mangle_connect_address(A, SockOpts);
 mangle_connect_address(A, SockOpts) ->
     mangle_connect_address1(A, proplists:get_value(inet6,SockOpts,false)).
 
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 7874f93fa2..773d8025cb 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -167,20 +167,20 @@ request_ownership(LSock, SockOwner) ->
     
 %%%----------------------------------------------------------------    
 acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
-    case (catch Callback:accept(ListenSocket, AcceptTimeout)) of
-	{ok, Socket} ->
-	    handle_connection(Callback, Address, Port, Opts, Socket),
-	    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
-				  ListenSocket, AcceptTimeout);
-	{error, Reason} ->
-	    handle_error(Reason),
-	    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
-				  ListenSocket, AcceptTimeout);
-	{'EXIT', Reason} ->
-	    handle_error(Reason),
-	    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
-				  ListenSocket, AcceptTimeout)
-    end.
+    case Callback:accept(ListenSocket, AcceptTimeout) of
+        {ok,Socket} ->
+            case handle_connection(Callback, Address, Port, Opts, Socket) of
+                {error,Error} ->
+                    catch Callback:close(Socket),
+                    handle_error(Error);
+                _ ->
+                    ok
+            end;
+        {error,Error} ->
+            handle_error(Error)
+    end,
+    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
+                          ListenSocket, AcceptTimeout).
 
 %%%----------------------------------------------------------------
 handle_connection(Callback, Address, Port, Options, Socket) ->
@@ -190,16 +190,8 @@ handle_connection(Callback, Address, Port, Options, Socket) ->
     MaxSessions = ?GET_OPT(max_sessions, Options),
     case number_of_connections(SystemSup) < MaxSessions of
 	true ->
-	    {ok, SubSysSup} = 
-                ssh_system_sup:start_subsystem(SystemSup, server, Address, Port, Profile, Options),
-	    ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
 	    NegTimeout = ?GET_OPT(negotiation_timeout, Options),
-	    ssh_connection_handler:start_connection(server, Socket,
-                                                    ?PUT_INTERNAL_OPT(
-                                                       {supervisors, [{system_sup, SystemSup},
-                                                                      {subsystem_sup, SubSysSup},
-                                                                      {connection_sup, ConnectionSup}]},
-                                                       Options), NegTimeout);
+            ssh_connection_handler:start_connection(server, {Address,Port}, Socket, Options, NegTimeout);
 	false ->
 	    Callback:close(Socket),
 	    IPstr = if is_tuple(Address) -> inet:ntoa(Address);
@@ -218,29 +210,23 @@ handle_connection(Callback, Address, Port, Options, Socket) ->
 %%%----------------------------------------------------------------
 handle_error(timeout) ->
     ok;
-
 handle_error(enfile) ->
     %% Out of sockets...
     timer:sleep(?SLEEP_TIME);
-
 handle_error(emfile) ->
     %% Too many open files -> Out of sockets...
     timer:sleep(?SLEEP_TIME);
-
 handle_error(closed) ->
     error_logger:info_report("The ssh accept socket was closed by " 
-			     "a third party. "
-			     "This will not have an impact on ssh "
-			     "that will open a new accept socket and " 
-			     "go on as nothing happened. It does however "
-			     "indicate that some other software is behaving "
-			     "badly."),
-    exit(normal);
-
-handle_error(Reason) ->
-    String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
-    error_logger:error_report(String),
-    exit({accept_failed, String}).    
+                             "a third party. "
+                             "This will not have an impact on ssh "
+                             "that will open a new accept socket and " 
+                             "go on as nothing happened. It does however "
+                             "indicate that some other software is behaving "
+                             "badly.");
+handle_error(Error) ->
+    String = lists:flatten(io_lib:format("Accept error: ~p", [Error])),
+    error_logger:error_report(String).
 
 %%%################################################################
 %%%#
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index a198a95937..070039b309 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -41,12 +41,13 @@
 
 %%% Start and stop
 -export([start_link/3,
+         start_connection/4, start_connection/5,
+         socket_control/3,
 	 stop/1
 	]).
 
 %%% Internal application API
--export([start_connection/4,
-         available_hkey_algorithms/2,
+-export([available_hkey_algorithms/2,
 	 open_channel/6,
          start_channel/5,
          handle_direct_tcpip/6,
@@ -75,8 +76,7 @@
 	 format_status/2, code_change/4]).
 
 %%% Exports not intended to be used :). They are used for spawning and tests
--export([init_connection_handler/3,	   % proc_lib:spawn needs this
-	 init_ssh_record/3,		   % Export of this internal function
+-export([init_ssh_record/3,		   % Export of this internal function
 					   % intended for low-level protocol test suites
 	 renegotiate/1, alg/1 % Export intended for test cases
 	]).
@@ -97,18 +97,54 @@
 %%====================================================================
 %% Start / stop
 %%====================================================================
+
+%%--------------------------------------------------------------------
+start_connection(Role, Socket, Options, NegotiationTimeout) ->
+    {ok, {Host,Port}} = inet:sockname(Socket),
+    start_connection(Role, {Host,Port}, Socket, Options, NegotiationTimeout).
+
+start_connection(Role, {Host,Port}, Socket, Options0, NegotiationTimeout) ->
+    try
+        Options1 = ?PUT_INTERNAL_OPT([{user_pid, self()}
+                                     ], Options0),
+        Profile = ?GET_OPT(profile, Options1),
+        {ok, {SystemSup, SubSysSup}} =
+            case Role of
+                client -> sshc_sup:start_system_subsystem(Host, Port, Profile, Options1);
+                server -> sshd_sup:start_system_subsystem(Host, Port, Profile, Options1)
+            end,
+        ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
+        Options = ?PUT_INTERNAL_OPT([{supervisors, [{system_sup, SystemSup},
+                                                    {subsystem_sup, SubSysSup},
+                                                    {connection_sup, ConnectionSup}]}
+                                    ], Options1),
+        case ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Options]) of
+            {ok, Pid} ->
+                case socket_control(Socket, Pid, Options) of
+                    ok ->
+                        handshake(Pid, erlang:monitor(process,Pid), NegotiationTimeout);
+                    {error, Reason} ->
+                        {error, Reason}
+                end;
+            {error, Reason} ->
+                {error, Reason}
+        end
+    catch
+        exit:{noproc,{gen_server,call,_}} -> {error, ssh_not_started};
+        error:Error ->  {error, Error};
+        Class:Error ->  {error, {Class,Error}}
+    end.
+
 %%--------------------------------------------------------------------
 -spec start_link(role(),
 		 gen_tcp:socket(),
                  internal_options()
-		) -> {ok, pid()}.
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-start_link(Role, Socket, Options) ->
-    {ok, proc_lib:spawn_opt(?MODULE, 
-                            init_connection_handler, 
-                            [Role, Socket, Options],
-                            [link, {message_queue_data,off_heap}]
-                           )}.
+		) -> {ok, pid()} | ignore | {error, term()} .
+
+start_link(Role, Socket, Options) when Role==client ; Role==server ->
+    gen_statem:start_link(?MODULE, [Role, Socket, Options],
+                          [{spawn_opt, [{message_queue_data,off_heap}]}
+                          ]).
 
 
 %%--------------------------------------------------------------------
@@ -127,46 +163,6 @@ stop(ConnectionHandler)->
 %% Internal application API
 %%====================================================================
 
-%%--------------------------------------------------------------------
--spec start_connection(role(),
-		       gen_tcp:socket(),
-                       internal_options(),
-		       timeout()
-		      ) -> {ok, connection_ref()} | {error, term()}.
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-start_connection(Role, Socket, Options, Timeout) ->
-    try
-        case Role of
-            client ->
-                ChildPid = start_the_connection_child(self(), Role, Socket, Options),
-                handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout);
-            server ->
-                case ?GET_OPT(parallel_login, Options) of
-                    true ->
-                        HandshakerPid =
-                            spawn_link(fun() ->
-                                               process_flag(trap_exit, true),
-                                               receive
-                                                   {do_handshake, Pid} ->
-                                                       handshake(Pid, erlang:monitor(process,Pid), Timeout)
-                                               after Timeout ->
-                                                       {error, timeout2}
-                                               end
-                                       end),
-                        ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
-                        HandshakerPid ! {do_handshake, ChildPid};
-                    false ->
-                        ChildPid = start_the_connection_child(self(), Role, Socket, Options),
-                        handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
-                end
-        end
-    catch
-	exit:{noproc, _} ->
-	    {error, ssh_not_started};
-	_:Error ->
-	    {error, Error}
-    end.
-
 %%--------------------------------------------------------------------
 %%% Some other module has decided to disconnect.
 
@@ -446,92 +445,54 @@ alg(ConnectionHandler) ->
 %%====================================================================
 %% Intitialisation
 %%====================================================================
-%%--------------------------------------------------------------------
--spec init_connection_handler(role(),
-			      gen_tcp:socket(),
-			      internal_options()
-			     ) -> no_return().
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-init_connection_handler(Role, Socket, Opts) ->
-    case init([Role, Socket, Opts]) of
-        {ok, StartState, D} when Role == server ->
-            process_flag(trap_exit, true),
-            gen_statem:enter_loop(?MODULE,
-                                  [], %%[{debug,[trace,log,statistics,debug]} ], %% []
-                                  StartState,
-                                  D);
-
-        {ok, StartState, D0=#data{connection_state=C}} when Role == client ->
-            process_flag(trap_exit, true),
-            Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-            D = D0#data{connection_state = 
-                            C#connection{system_supervisor =     proplists:get_value(system_sup,     Sups),
-                                         sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
-                                         connection_supervisor = proplists:get_value(connection_sup, Sups)
-                                        }},
-            gen_statem:enter_loop(?MODULE,
-                                  [], %%[{debug,[trace,log,statistics,debug]} ], %% []
-                                  StartState,
-                                  D);
-
-        {stop, Error} ->
-            D = try
-                    %% Only servers have supervisorts defined in Opts
-                    Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-                    #connection{system_supervisor =     proplists:get_value(system_sup,     Sups),
-                                sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
-                                connection_supervisor = proplists:get_value(connection_sup, Sups)
-                               }
-                of
-                    C ->
-                        #data{connection_state=C}
-                catch
-                    _:_ ->
-                        #data{connection_state=#connection{}}
-                end,
-            gen_statem:enter_loop(?MODULE,
-                                  [],
-                                  {init_error,Error},
-                                  D#data{socket=Socket})
-    end.
-
-
 
-init([Role,Socket,Opts]) ->
+init([Role, Socket, Opts]) when Role==client ; Role==server ->
     case inet:peername(Socket) of
         {ok, PeerAddr} ->
-            {Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
-            C = #connection{channel_cache = ssh_client_channel:cache_create(),
-                            channel_id_seed = 0,
-                            requests = [],
-                            options = Opts},
-            D0 = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
-                       connection_state = C,
-                       socket = Socket,
-                       transport_protocol = Protocol,
-                       transport_cb = Callback,
-                       transport_close_tag = CloseTag,
-                       ssh_params = init_ssh_record(Role, Socket, PeerAddr, Opts)
-              },
-            D = case Role of
-                    client ->
-                        D0;
-                    server ->
-                        Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-                        D0#data{connection_state = 
-                                    C#connection{cli_spec = ?GET_OPT(ssh_cli, Opts, {ssh_cli,[?GET_OPT(shell, Opts)]}),
-                                                 exec =     ?GET_OPT(exec,    Opts),
-                                                 system_supervisor =     proplists:get_value(system_sup,     Sups),
-                                                 sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
-                                                 connection_supervisor = proplists:get_value(connection_sup, Sups)
-                                                }}
-                end,
-            {ok, {hello,Role}, D};
-        
+            try
+                {Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
+                D = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
+                          socket = Socket,
+                          transport_protocol = Protocol,
+                          transport_cb = Callback,
+                          transport_close_tag = CloseTag,
+                          ssh_params = init_ssh_record(Role, Socket, PeerAddr, Opts),
+                          connection_state = init_connection_record(Role, Opts)
+                         },
+                process_flag(trap_exit, true),
+                {ok, {hello,Role}, D}
+            catch
+                _:Error ->
+                    {stop, Error}
+            end;
+
         {error,Error} ->
             {stop, Error}
     end.
 
+%%%----------------------------------------------------------------
+%%% Connection start and initalization helpers
+
+init_connection_record(Role, Opts) ->
+    Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+    C = #connection{channel_cache = ssh_client_channel:cache_create(),
+                    channel_id_seed = 0,
+                    requests = [],
+                    options = Opts,
+                    system_supervisor =     proplists:get_value(system_sup,     Sups),
+                    sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
+                    connection_supervisor = proplists:get_value(connection_sup, Sups)
+                   },
+    case Role of
+        server ->
+            C#connection{cli_spec =
+                             ?GET_OPT(ssh_cli, Opts, {ssh_cli,[?GET_OPT(shell, Opts)]}),
+                         exec =
+                             ?GET_OPT(exec, Opts)};
+        client ->
+            C
+    end.
+                         
 
 
 init_ssh_record(Role, Socket, Opts) ->
@@ -592,6 +553,42 @@ init_ssh_record(Role, Socket, PeerAddr, Opts) ->
     end.
 
 
+socket_control(Socket, Pid, Options) ->
+    {_, Callback, _} =	?GET_OPT(transport, Options),
+    case Callback:controlling_process(Socket, Pid) of
+	ok ->
+	    gen_statem:cast(Pid, socket_control);
+	{error, Reason}	->
+	    {error, Reason}
+    end.
+
+
+handshake(Pid, Ref, Timeout) ->
+    receive
+	ssh_connected ->
+	    erlang:demonitor(Ref),
+	    {ok, Pid};
+	{Pid, not_connected, Reason} ->
+	    {error, Reason};
+	{Pid, user_password} ->
+	    Pass = io:get_password(),
+	    Pid ! Pass,
+	    handshake(Pid, Ref, Timeout);
+	{Pid, question} ->
+	    Answer = io:get_line(""),
+	    Pid ! Answer,
+	    handshake(Pid, Ref, Timeout);
+	{'DOWN', _, process, Pid, {shutdown, Reason}} ->
+	    {error, Reason};
+	{'DOWN', _, process, Pid, Reason} ->
+	    {error, Reason};
+        {'EXIT',_,Reason} ->
+            stop(Pid),
+            {error, {exit,Reason}}
+    after Timeout ->
+	    ssh_connection_handler:stop(Pid),
+	    {error, timeout}
+    end.
 
 %%====================================================================
 %% gen_statem callbacks
@@ -1805,9 +1799,15 @@ terminate(Reason, StateName, D0) ->
 
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
-format_status(normal, [_, _StateName, D]) ->
+format_status(A, B) ->
+    try format_status0(A, B)
+    catch
+        _:_ -> "????"
+    end.
+        
+format_status0(normal, [_PDict, _StateName, D]) ->
     [{data, [{"State", D}]}];
-format_status(terminate, [_, _StateName, D]) ->
+format_status0(terminate, [_, _StateName, D]) ->
     [{data, [{"State", clean(D)}]}].
 
 
@@ -1882,15 +1882,6 @@ code_change(_OldVsn, StateName, State, _Extra) ->
 %%--------------------------------------------------------------------
 %% Starting
 
-start_the_connection_child(UserPid, Role, Socket, Options0) ->
-    Sups = ?GET_INTERNAL_OPT(supervisors, Options0),
-    ConnectionSup = proplists:get_value(connection_sup, Sups),
-    Options = ?PUT_INTERNAL_OPT({user_pid,UserPid}, Options0),
-    InitArgs = [Role, Socket, Options],
-    {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, InitArgs),
-    ok = socket_control(Socket, Pid, Options), % transfer the Socket ownership in a controlled way.
-    Pid.
-
 %%--------------------------------------------------------------------
 %% Stopping
 
@@ -1898,20 +1889,15 @@ stop_subsystem(#data{ssh_params =
                          #ssh{role = Role},
                      connection_state =
                          #connection{system_supervisor = SysSup,
-                                     sub_system_supervisor = SubSysSup,
-                                     options = Opts}
+                                     sub_system_supervisor = SubSysSup}
                     }) when is_pid(SysSup) andalso is_pid(SubSysSup)  ->
     C = self(),
     spawn(fun() ->
                   wait_until_dead(C, 10000),
-                  case {Role, ?GET_INTERNAL_OPT(connected_socket,Opts,non_socket_started)} of
-                      {server, non_socket_started} ->
+                  case Role of
+                      server ->
                           ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
-                      {client, non_socket_started} ->
-                          ssh_system_sup:stop_system(Role, SysSup);
-                      {server, _Socket} ->
-                          ssh_system_sup:stop_system(Role, SysSup);
-                      {client, _Socket} ->
+                      client ->
                           ssh_system_sup:stop_subsystem(SysSup, SubSysSup),
                           wait_until_dead(SubSysSup, 1000),
                           sshc_sup:stop_system(SysSup)
@@ -2496,44 +2482,6 @@ start_channel_request_timer(Channel, From, Time) ->
     erlang:send_after(Time, self(), {timeout, {Channel, From}}).
 
 %%%----------------------------------------------------------------
-%%% Connection start and initialization helpers
-
-socket_control(Socket, Pid, Options) ->
-    {_, Callback, _} =	?GET_OPT(transport, Options),
-    case Callback:controlling_process(Socket, Pid) of
-	ok ->
-	    gen_statem:cast(Pid, socket_control);
-	{error, Reason}	->
-	    {error, Reason}
-    end.
-
-handshake(Pid, Ref, Timeout) ->
-    receive
-	ssh_connected ->
-	    erlang:demonitor(Ref),
-	    {ok, Pid};
-	{Pid, not_connected, Reason} ->
-	    {error, Reason};
-	{Pid, user_password} ->
-	    Pass = io:get_password(),
-	    Pid ! Pass,
-	    handshake(Pid, Ref, Timeout);
-	{Pid, question} ->
-	    Answer = io:get_line(""),
-	    Pid ! Answer,
-	    handshake(Pid, Ref, Timeout);
-	{'DOWN', _, process, Pid, {shutdown, Reason}} ->
-	    {error, Reason};
-	{'DOWN', _, process, Pid, Reason} ->
-	    {error, Reason};
-        {'EXIT',_,Reason} ->
-            stop(Pid),
-            {error, {exit,Reason}}
-    after Timeout ->
-	    stop(Pid),
-	    {error, timeout}
-    end.
-
 update_inet_buffers(Socket) ->
     try
         {ok, BufSzs0} = inet:getopts(Socket, [sndbuf,recbuf]),
@@ -2557,7 +2508,7 @@ ssh_dbg_flags(connection_events) -> [c];
 ssh_dbg_flags(terminate) -> [c];
 ssh_dbg_flags(disconnect) -> [c].
 
-ssh_dbg_on(connections) -> dbg:tp(?MODULE,  init_connection_handler, 3, x),
+ssh_dbg_on(connections) -> dbg:tp(?MODULE,  init, 1, x),
                            ssh_dbg_on(terminate);
 ssh_dbg_on(connection_events) -> dbg:tp(?MODULE,   handle_event, 4, x);
 ssh_dbg_on(connection_handshake) -> dbg:tpl(?MODULE, handshake, 3, x);
@@ -2577,11 +2528,11 @@ ssh_dbg_off(renegotiation) -> dbg:ctpl(?MODULE,   init_renegotiate_timers, 3),
                               dbg:ctpg(?MODULE,   renegotiate, 1);
 ssh_dbg_off(connection_events) -> dbg:ctpg(?MODULE, handle_event, 4);
 ssh_dbg_off(connection_handshake) -> dbg:ctpl(?MODULE, handshake, 3);
-ssh_dbg_off(connections) -> dbg:ctpg(?MODULE, init_connection_handler, 3),
+ssh_dbg_off(connections) -> dbg:ctpg(?MODULE, init, 1),
                             ssh_dbg_off(terminate).
 
 
-ssh_dbg_format(connections, {call, {?MODULE,init_connection_handler, [Role, Sock, Opts]}}) ->
+ssh_dbg_format(connections, {call, {?MODULE,init, [[Role, Sock, Opts]]}}) ->
     DefaultOpts = ssh_options:handle_options(Role,[]),
     ExcludedKeys = [internal_options, user_options],
     NonDefaultOpts =
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index e582733de8..bbf3d16440 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -20,13 +20,15 @@
 
 %%
 %%----------------------------------------------------------------------
-%% Purpose: The ssh client subsystem supervisor 
+%% Purpose: The ssh client top supervisor
 %%----------------------------------------------------------------------
 
 -module(sshc_sup).
 
 -behaviour(supervisor).
 
+-include("ssh.hrl").
+
 -export([start_link/0,
          start_child/4,
          start_system_subsystem/4,
@@ -43,7 +45,7 @@
 %%%  API
 %%%=========================================================================
 start_link() ->
-    supervisor:start_link({local,?MODULE}, ?MODULE, []).
+    supervisor:start_link({local,?SSHC_SUP}, ?MODULE, []).
 
 start_child(Address, Port, Profile, Options) ->
     case ssh_system_sup:system_supervisor(Address, Port, Profile) of
@@ -82,7 +84,8 @@ init(_) ->
                     start    => {ssh_controller, start_link, [client, client_controller]},
                     restart  => permanent,
                     type     => worker
-                   }],
+                   }
+                 ],
     {ok, {SupFlags,ChildSpecs}}.
 
 %%%=========================================================================
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index b716b66ec7..bda156223e 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -17,10 +17,10 @@
 %%
 %% %CopyrightEnd%
 %%
+
 %%
 %%----------------------------------------------------------------------
-%% Purpose: The top supervisor for ssh servers hangs under
-%%          ssh_sup.
+%% Purpose: The ssh daemon top supervisor
 %%----------------------------------------------------------------------
 
 -module(sshd_sup).
@@ -30,10 +30,11 @@
 -include("ssh.hrl").
 
 -export([start_link/0,
-         start_child/4,
+         start_child/4, 
+         start_system_subsystem/4,
          stop_child/1,
 	 stop_child/3
-]).
+        ]).
 
 %% Supervisor callback
 -export([init/1]).
@@ -44,8 +45,6 @@
 %%%  API
 %%%=========================================================================
 start_link() ->
-    %% No children are start now. We wait until the user calls ssh:daemon
-    %% and uses start_child/4 to create the children
     supervisor:start_link({local,?SSHD_SUP}, ?MODULE, []).
 
 start_child(Address, Port, Profile, Options) ->
@@ -62,6 +61,11 @@ start_child(Address, Port, Profile, Options) ->
             {ok,Pid}
     end.
 
+start_system_subsystem(Host, Port, Profile, Options) ->
+    ssh_controller:start_system_subsystem(daemon_controller, ?MODULE, Host, Port, Profile, Options,
+                                          child_spec(Host, Port, Profile, Options)
+                                         ).
+
 stop_child(ChildId) when is_tuple(ChildId) ->
     supervisor:terminate_child(?SSHD_SUP, ChildId);
 stop_child(ChildPid) when is_pid(ChildPid)->
@@ -80,7 +84,11 @@ init(_) ->
                  intensity =>   10,
                  period    => 3600
                 },
-    ChildSpecs = [
+    ChildSpecs = [#{id       => daemon_controller,
+                    start    => {ssh_controller, start_link, [server, daemon_controller]},
+                    restart  => permanent,
+                    type     => worker
+                   }
                  ],
     {ok, {SupFlags,ChildSpecs}}.
 
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 8ad405b1d7..fa9dbe1c7d 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -115,7 +115,7 @@ default_tree(Config) when is_list(Config) ->
     {value, {sshd_sup, _,supervisor,[sshd_sup]}} = 
 	lists:keysearch(sshd_sup, 1, TopSupChildren),
     ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)),
-    ?wait_match([], supervisor:which_children(sshd_sup)).
+    ?wait_match([{daemon_controller,_,worker,_}], supervisor:which_children(sshd_sup)).
 
 %%-------------------------------------------------------------------------
 sshc_subtree(Config) when is_list(Config) ->
@@ -164,14 +164,16 @@ sshd_subtree(Config) when is_list(Config) ->
     ct:log("Expect HostIP=~p, Port=~p, Daemon=~p",[HostIP,Port,Daemon]),
     ?wait_match([{{server,ssh_system_sup, ListenIP, Port, ?DEFAULT_PROFILE},
 		  Daemon, supervisor,
-		  [ssh_system_sup]}],
+		  [ssh_system_sup]},
+                 {daemon_controller,_,worker,_}
+                ],
 		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([{daemon_controller,_,worker,_}], supervisor:which_children(sshd_sup)).
 
 %%-------------------------------------------------------------------------
 sshd_subtree_profile(Config) when is_list(Config) ->
@@ -186,14 +188,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([{{server,ssh_system_sup, ListenIP,Port,Profile},
 		  Daemon, supervisor,
-		  [ssh_system_sup]}],
+		  [ssh_system_sup]},
+                 {daemon_controller,_,worker,_}],
 		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([{daemon_controller,_,worker,_}], supervisor:which_children(sshd_sup)).
 
 %%-------------------------------------------------------------------------
 killed_acceptor_restarts(Config) ->
-- 
2.26.2

openSUSE Build Service is sponsored by