diff options
author | Hans Nilsson <hans@erlang.org> | 2021-03-16 16:49:25 +0100 |
---|---|---|
committer | Hans Nilsson <hans@erlang.org> | 2021-04-15 16:28:24 +0200 |
commit | bc26a8a8c501cec95b1c19a08ec926b26ebc66da (patch) | |
tree | ed8e1299d369d272665192e1562e408ba8a023e9 | |
parent | 2f4b2f0925fa1cd00ee2e7ef5eae779a875d0c5a (diff) | |
download | erlang-bc26a8a8c501cec95b1c19a08ec926b26ebc66da.tar.gz |
ssh: Re-write supervisor tree to use PR-4368
26 files changed, 669 insertions, 1120 deletions
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 29d77fa6f5..517a6611c4 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -66,8 +66,6 @@ MODULES= \ ssh_cli \ ssh_connection \ ssh_connection_handler \ - ssh_connection_sup \ - ssh_controller \ ssh_file \ ssh_fsm_kexinit \ ssh_fsm_userauth_client \ @@ -83,16 +81,13 @@ MODULES= \ ssh_sftpd_file\ ssh_shell \ ssh_subsystem_sup \ - ssh_sup \ ssh_system_sup \ ssh_tcpip_forward_srv \ ssh_tcpip_forward_client \ ssh_tcpip_forward_acceptor_sup \ ssh_tcpip_forward_acceptor \ ssh_transport \ - ssh_xfer \ - sshc_sup \ - sshd_sup + ssh_xfer HRL_FILES = diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 085bb14383..57465b9fe8 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -22,14 +22,10 @@ ssh_fsm_kexinit, ssh_fsm_userauth_client, ssh_fsm_userauth_server, - ssh_connection_sup, - ssh_controller, ssh_daemon_channel, ssh_dbg, ssh_lib, ssh_shell, - sshc_sup, - sshd_sup, ssh_io, ssh_info, ssh_file, @@ -41,7 +37,6 @@ ssh_sftpd_file, ssh_sftpd_file_api, ssh_subsystem_sup, - ssh_sup, ssh_tcpip_forward_client, ssh_tcpip_forward_srv, ssh_tcpip_forward_acceptor_sup, diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 22fc8f40be..867e795c11 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -144,7 +144,7 @@ connect(Socket, UserOptions, NegotiationTimeout) when is_list(UserOptions) -> Options = #{} -> case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of ok -> - ssh_connection_handler:start_link(client, Socket, Options, NegotiationTimeout); + continue_connect(Socket, Options, NegotiationTimeout); {error,SockError} -> {error,SockError} end @@ -165,10 +165,34 @@ connect(Host0, Port, UserOptions, NegotiationTimeout) when is_integer(Port), {error, Reason}; Options -> + SocketOpts = [{active,false} | ?GET_OPT(socket_options,Options)], Host = mangle_connect_address(Host0, Options), - ssh_connection_handler:start_link(client, Host, Port, Options, NegotiationTimeout) + try + {_, Callback, _} = ?GET_OPT(transport, Options), + Callback:connect(Host, Port, SocketOpts, ?GET_OPT(connect_timeout,Options)) + of + {ok, Socket} -> + continue_connect(Socket, Options, NegotiationTimeout); + {error, Reason} -> + {error, Reason} + catch + _:badarg -> {error, {options,?GET_OPT(socket_options,Options)}}; + _:{error,Reason} -> {error,Reason}; + error:Error -> {error,Error}; + Class:Error -> {error, {Class,Error}} + end end. +%%%---------------- +continue_connect(Socket, Options0, NegTimeout) -> + {ok, {SockHost,SockPort}} = inet:sockname(Socket), + Options = ?PUT_INTERNAL_OPT([{negotiation_timeout,NegTimeout}], Options0), + Address = #address{address = SockHost, + port = SockPort, + profile = ?GET_OPT(profile,Options) + }, + ssh_system_sup:start_subsystem(client, Address, Socket, Options). + %%-------------------------------------------------------------------- -spec close(ConnectionRef) -> ok | {error,term()} when ConnectionRef :: connection_ref() . @@ -254,20 +278,17 @@ daemon(Socket, UserOptions) -> case valid_socket_to_use(Socket, ?GET_OPT(transport,Options0)) of ok -> try - Options = ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options0), %% throws error:Error if no usable hostkey is found - ssh_connection_handler:available_hkey_algorithms(server, Options), - {ok, {IP,Port}} = inet:sockname(Socket), - case sshd_sup:start_child(IP, Port, Options) of - Result = {ok,_} -> - case ssh_connection_handler:start_link(server, Socket, Options, - ?GET_OPT(negotiation_timeout,Options)) - of - {error,Error} -> - {error,Error}; - _ -> - Result - end; + ssh_connection_handler:available_hkey_algorithms(server, Options0), + {ok, {SockHost,SockPort}} = inet:sockname(Socket), + Address = #address{address = SockHost, + port = SockPort, + profile = ?GET_OPT(profile,Options0) + }, + Options = ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options0), + case ssh_system_sup:start_subsystem(server, Address, Socket, Options) of + {ok,Pid} -> + {ok,Pid}; {error, {already_started, _}} -> {error, eaddrinuse}; {error, Error} -> @@ -288,7 +309,7 @@ daemon(Socket, UserOptions) -> {error,OptionError} -> {error,OptionError} - end. + end. @@ -301,26 +322,35 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535, try {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0), #{} = Options0 = ssh_options:handle_options(server, UserOptions), - open_listen_socket(Host1, Port0, Options0) + %% 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 is for listening on connections. But it is still owned - %% by self()... + %% 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), - sshd_sup:start_child(Host, Port, Options1) + ssh_system_sup:start_system(server, + #address{address = Host, + port = Port, + profile = ?GET_OPT(profile,Options1)}, + Options1) of - Result = {ok,_} -> + {ok,DaemonRef} when ListenSocket == undefined -> + {ok,DaemonRef}; + {ok,DaemonRef} -> receive {request_control, ListenSocket, ReqPid} -> {_, Callback, _} = ?GET_OPT(transport, Options1), ok = Callback:controlling_process(ListenSocket, ReqPid), ReqPid ! {its_yours,ListenSocket} end, - Result; + {ok,DaemonRef}; {error, {already_started, _}} -> close_listen_socket(ListenSocket, Options1), {error, eaddrinuse}; @@ -370,20 +400,16 @@ daemon(_, _, _) -> InfoTuple :: daemon_info_tuple(). daemon_info(DaemonRef) -> - case catch ssh_system_sup:acceptor_supervisor(DaemonRef) of - AsupPid when is_pid(AsupPid) -> - [{Host,Port,Profile}] = - [{Hst,Prt,Prf} - || {{ssh_acceptor_sup,Hst,Prt,Prf},_Pid,worker,[ssh_acceptor]} - <- supervisor:which_children(AsupPid)], - IP = - case inet:parse_strict_address(Host) of - {ok,IP0} -> IP0; - _ -> Host + case ssh_system_sup:get_daemon_listen_address(DaemonRef) of + {ok,A} -> + Address = + case inet:parse_strict_address(A#address.address) of + {ok,IP} -> A#address{address=IP}; + _ -> A end, - Opts = - case ssh_system_sup:get_options(DaemonRef, Host, Port, Profile) of + %% Pick a subset of the Options to present: + case ssh_system_sup:get_options(DaemonRef, Address) of {ok, OptMap} -> lists:sort( maps:to_list( @@ -394,11 +420,12 @@ daemon_info(DaemonRef) -> [] end, - {ok, [{port,Port}, - {ip,IP}, - {profile,Profile}, - {options,Opts} + {ok, [{port, Address#address.port}, + {ip, Address#address.address}, + {profile, Address#address.profile}, + {options, Opts} ]}; + _ -> {error,bad_daemon_ref} end. @@ -442,14 +469,14 @@ stop_listener(Address, Port) -> -spec stop_listener(any|inet:ip_address(), inet:port_number(), term()) -> ok. -stop_listener(any, Port, Profile) -> - map_ip(fun(IP) -> - ssh_system_sup:stop_listener(IP, Port, Profile) - end, [{0,0,0,0},{0,0,0,0,0,0,0,0}]); stop_listener(Address, Port, Profile) -> - map_ip(fun(IP) -> - ssh_system_sup:stop_listener(IP, Port, Profile) - end, {address,Address}). + lists:foreach(fun({Sup,_Addr}) -> + stop_listener(Sup) + end, + ssh_system_sup:addresses(server, + #address{address=Address, + port=Port, + profile=Profile})). %%-------------------------------------------------------------------- %% Description: Stops the listener and all connections started by @@ -469,14 +496,14 @@ stop_daemon(Address, Port) -> -spec stop_daemon(any|inet:ip_address(), inet:port_number(), atom()) -> ok. -stop_daemon(any, Port, Profile) -> - map_ip(fun(IP) -> - ssh_system_sup:stop_system(server, IP, Port, Profile) - end, [{0,0,0,0},{0,0,0,0,0,0,0,0}]); stop_daemon(Address, Port, Profile) -> - map_ip(fun(IP) -> - ssh_system_sup:stop_system(server, IP, Port, Profile) - end, {address,Address}). + lists:foreach(fun({Sup,_Addr}) -> + stop_daemon(Sup) + end, + ssh_system_sup:addresses(server, + #address{address=Address, + port=Port, + profile=Profile})). %%-------------------------------------------------------------------- %% Description: Starts an interactive shell to an SSH server on the @@ -752,17 +779,26 @@ is_tcp_socket(Socket) -> end. %%%---------------------------------------------------------------- -open_listen_socket(_Host0, Port0, Options0) -> - {ok,LSock} = - case ?GET_SOCKET_OPT(fd, Options0) of - undefined -> - ssh_acceptor:listen(Port0, Options0); +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, Options0) + ssh_acceptor:listen(0, Options); + undefined -> + open_later end, - {ok,{LHost,LPort}} = inet:sockname(LSock), - {LHost, LPort, LSock, ?PUT_INTERNAL_OPT({lsocket,{LSock,self()}}, Options0)}. + 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) -> @@ -774,19 +810,6 @@ close_listen_socket(ListenSocket, Options) -> end. %%%---------------------------------------------------------------- -map_ip(Fun, {address,IP}) when is_tuple(IP) -> - Fun(IP); -map_ip(Fun, {address,Address}) -> - IPs = try {ok,#hostent{h_addr_list=IP0s}} = inet:gethostbyname(Address), - IP0s - catch - _:_ -> [] - end, - map_ip(Fun, IPs); -map_ip(Fun, IPs) -> - lists:map(Fun, IPs). - -%%%---------------------------------------------------------------- is_host(X, Opts) -> try is_host1(mangle_connect_address(X, Opts)) catch diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 33f6833830..848513e6a5 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -36,6 +36,8 @@ -define(DEFAULT_SHELL, {shell, start, []} ). +-define(DEFAULT_TIMEOUT, 5000). + -define(MAX_RND_PADDING_LEN, 15). -define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password"). @@ -410,6 +412,11 @@ %% Records +-record(address, {address, + port, + profile + }). + -record(ssh, { role :: client | role(), diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index 9899fc7098..3ea066f98d 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -25,12 +25,12 @@ -include("ssh.hrl"). %% Internal application API --export([start_link/4, +-export([start_link/3, number_of_connections/1, listen/2]). %% spawn export --export([acceptor_init/5, acceptor_loop/6]). +-export([acceptor_init/4, acceptor_loop/7]). -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]). @@ -40,61 +40,9 @@ %%==================================================================== %% Internal application API %%==================================================================== -start_link(Port, Address, Options, AcceptTimeout) -> - Args = [self(), Port, Address, Options, AcceptTimeout], - proc_lib:start_link(?MODULE, acceptor_init, Args). - -%%%---------------------------------------------------------------- -number_of_connections(SysSup) -> - length([S || S <- supervisor:which_children(SysSup), - has_worker(SysSup,S)]). - - -has_worker(SysSup, {R,SubSysSup,supervisor,[ssh_subsystem_sup]}) when is_reference(R), - is_pid(SubSysSup) -> - try - {{server, ssh_connection_sup, _, _}, Pid, supervisor, [ssh_connection_sup]} = - lists:keyfind([ssh_connection_sup], 4, supervisor:which_children(SubSysSup)), - {Pid, supervisor:which_children(Pid)} - of - {ConnSup,[]} -> - %% Strange. Since the connection supervisor exists, there should have been - %% a connection here. - %% It might be that the connection_handler worker has "just died", maybe - %% due to a exit(_,kill). It might also be so that the worker is starting. - %% Spawn a killer that redo the test and kills it if the problem persists. - %% TODO: Fix this better in the supervisor tree.... - spawn(fun() -> - timer:sleep(10), - try supervisor:which_children(ConnSup) - of - [] -> - %% we are on the server-side: - ssh_system_sup:stop_subsystem(SysSup, SubSysSup); - [_] -> - %% is ok now - ok; - _ -> - %% What?? - error - catch _:_ -> - %% What?? - error - end - end), - false; - {_ConnSup,[_]}-> - true; - _ -> - %% What?? - false - catch _:_ -> - %% What?? - false - end; - -has_worker(_,_) -> - false. +%% Supposed to be called in a child-spec of the ssh_acceptor_sup +start_link(SystemSup, Address, Options) -> + proc_lib:start_link(?MODULE, acceptor_init, [self(),SystemSup,Address,Options]). %%%---------------------------------------------------------------- listen(Port, Options) -> @@ -114,29 +62,43 @@ listen(Port, Options) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -acceptor_init(Parent, Port, Address, Opts, AcceptTimeout) -> - try - ?GET_INTERNAL_OPT(lsocket, Opts) - of +acceptor_init(Parent, SystemSup, + #address{address=Address, port=Port, profile=_Profile}, + Opts) -> + AcceptTimeout = ?GET_INTERNAL_OPT(timeout, Opts, ?DEFAULT_TIMEOUT), + {_, Callback, _} = ?GET_OPT(transport, Opts), + 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 + {ok,{_,Port}} -> + %% A usable, open LSock proc_lib:init_ack(Parent, {ok, self()}), request_ownership(LSock, SockOwner), - {_, Callback, _} = ?GET_OPT(transport, Opts), - acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout); + acceptor_loop(Callback, 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(Callback, Port, Address, Opts1, NewLSock, AcceptTimeout, SystemSup); + {error,Error} -> + proc_lib:init_ack(Parent, {error,Error}) + end + end; - {error,_} -> % Not open, a restart - %% Allow gen_tcp:listen to fail 4 times if eaddrinuse: - {ok,NewLSock} = try_listen(Port, Opts, 4), + undefined -> + %% No listening socket (nor fd option) was provided; open a listening socket: + case listen(Port, Opts) of + {ok,LSock} -> proc_lib:init_ack(Parent, {ok, self()}), - Opts1 = ?DELETE_INTERNAL_OPT(lsocket, Opts), - {_, Callback, _} = ?GET_OPT(transport, Opts1), - acceptor_loop(Callback, Port, Address, Opts1, NewLSock, AcceptTimeout) + acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout, SystemSup); + {error,Error} -> + proc_lib:init_ack(Parent, {error,Error}) end - catch - _:_ -> - {error,use_existing_socket_failed} end. @@ -160,11 +122,11 @@ request_ownership(LSock, SockOwner) -> end. %%%---------------------------------------------------------------- -acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) -> +acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout, SystemSup) -> case Callback:accept(ListenSocket, AcceptTimeout) of {ok,Socket} -> {ok, {FromIP,FromPort}} = inet:peername(Socket), % Just in case of error in next line: - case handle_connection(Address, Port, Opts, Socket) of + case handle_connection(SystemSup, Address, Port, Opts, Socket) of {error,Error} -> catch Callback:close(Socket), handle_error(Error, Address, Port, FromIP, FromPort); @@ -174,18 +136,22 @@ acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) -> {error,Error} -> handle_error(Error, Address, Port) end, - ?MODULE:acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout). + ?MODULE:acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout, SystemSup). %%%---------------------------------------------------------------- -handle_connection(Address, Port, Options, Socket) -> - Profile = ?GET_OPT(profile, Options), - SystemSup = ssh_system_sup:system_supervisor(Address, Port, Profile), - - MaxSessions = ?GET_OPT(max_sessions, Options), +handle_connection(SystemSup, Address, Port, Options0, Socket) -> + MaxSessions = ?GET_OPT(max_sessions, Options0), case number_of_connections(SystemSup) < MaxSessions of true -> - NegTimeout = ?GET_OPT(negotiation_timeout, Options), - ssh_connection_handler:start_link(server, Address, Port, Socket, Options, NegTimeout); + Options = ?PUT_INTERNAL_OPT([{user_pid, self()} + ], Options0), + ssh_system_sup:start_subsystem(server, + #address{address = Address, + port = Port, + profile = ?GET_OPT(profile,Options) + }, + Socket, + Options); false -> {error,{max_sessions,MaxSessions}} end. @@ -233,6 +199,12 @@ handle_error(Reason, ToAddress, ToPort, FromAddress, FromPort) -> io_lib:format(": ~p", [Error])]) end. +%%%---------------------------------------------------------------- +number_of_connections(SysSupPid) -> + lists:foldl(fun({_Ref,_Pid,supervisor,[ssh_subsystem_sup]}, N) -> N+1; + (_, N) -> N + end, 0, supervisor:which_children(SysSupPid)). + %%%################################################################ %%%# %%%# Tracing @@ -242,17 +214,16 @@ ssh_dbg_trace_points() -> [connections]. ssh_dbg_flags(connections) -> [c]. -ssh_dbg_on(connections) -> dbg:tp(?MODULE, acceptor_init, 5, x), +ssh_dbg_on(connections) -> dbg:tp(?MODULE, acceptor_init, 4, x), dbg:tpl(?MODULE, handle_connection, 4, x). -ssh_dbg_off(connections) -> dbg:ctp(?MODULE, acceptor_init, 5), +ssh_dbg_off(connections) -> dbg:ctp(?MODULE, acceptor_init, 4), dbg:ctp(?MODULE, handle_connection, 4). -ssh_dbg_format(connections, {call, {?MODULE,acceptor_init, - [_Parent, Port, Address, _Opts, _AcceptTimeout]}}) -> - [io_lib:format("Starting LISTENER on ~s:~p\n", [ssh_lib:format_address(Address),Port]) +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,5}, _Ret}) -> +ssh_dbg_format(connections, {return_from, {?MODULE,acceptor_init,4}, _Ret}) -> skip; ssh_dbg_format(connections, {call, {?MODULE,handle_connection,[_,_,_,_]}}) -> diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl index 15a2238dd3..749de43776 100644 --- a/lib/ssh/src/ssh_acceptor_sup.erl +++ b/lib/ssh/src/ssh_acceptor_sup.erl @@ -29,64 +29,43 @@ -include("ssh.hrl"). --export([start_link/4, start_child/5, stop_child/4]). +-export([start_link/3, + restart_child/2 + ]). %% Supervisor callback -export([init/1]). --define(DEFAULT_TIMEOUT, 50000). - %%%========================================================================= %%% API %%%========================================================================= -start_link(Address, Port, Profile, Options) -> - supervisor:start_link(?MODULE, [Address, Port, Profile, Options]). - -start_child(AccSup, Address, Port, Profile, Options) -> - Spec = child_spec(Address, Port, Profile, Options), - case supervisor:start_child(AccSup, Spec) of - {error, already_present} -> - %% Is this ever called? - stop_child(AccSup, Address, Port, Profile), - supervisor:start_child(AccSup, Spec); - Reply -> - %% Reply = {ok,SystemSupPid} when the user calls ssh:daemon - %% after having called ssh:stop_listening - Reply +start_link(SystemSup, Address, Options) -> + case supervisor:start_link(?MODULE, [SystemSup, Address, Options]) of + {error, {shutdown, {failed_to_start_child, _, Error}}} -> + {error,Error}; + Other -> + Other end. -stop_child(AccSup, Address, Port, Profile) -> - Name = id(Address, Port, Profile), - case supervisor:terminate_child(AccSup, Name) of - ok -> - supervisor:delete_child(AccSup, Name); - Error -> - Error - end. +restart_child(AccSup, Address) -> + supervisor:restart_child(AccSup, {?MODULE,Address}). %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init([Address, Port, Profile, Options]) -> - %% Initial start of ssh_acceptor_sup for this port or new start after - %% ssh:stop_daemon +init([SystemSup, Address, Options]) -> + %% Initial start of ssh_acceptor_sup for this port SupFlags = #{strategy => one_for_one, intensity => 10, period => 3600 }, - ChildSpecs = [child_spec(Address, Port, Profile, Options)], + ChildSpecs = [#{id => {?MODULE,Address}, + start => {ssh_acceptor, start_link, [SystemSup, Address, Options]}, + restart => transient % because a crashed listener could be replaced by a new one + } + ], {ok, {SupFlags,ChildSpecs}}. %%%========================================================================= %%% Internal functions %%%========================================================================= -child_spec(Address, Port, Profile, Options) -> - Timeout = ?GET_INTERNAL_OPT(timeout, Options, ?DEFAULT_TIMEOUT), - #{id => id(Address, Port, Profile), - start => {ssh_acceptor, start_link, [Port, Address, Options, Timeout]}, - restart => transient % because a crashed listener could be replaced by a new one - }. - -id(Address, Port, Profile) -> - {ssh_acceptor_sup, Address, Port, Profile}. - diff --git a/lib/ssh/src/ssh_app.erl b/lib/ssh/src/ssh_app.erl index d3680e20da..2e8b671362 100644 --- a/lib/ssh/src/ssh_app.erl +++ b/lib/ssh/src/ssh_app.erl @@ -20,16 +20,62 @@ %% -%% Purpose : Application master for SSH. +%%%========================================================================= +%%% Purpose : Application master and top supervisors for SSH. +%%% +%%% -----> ssh_sup -----+-----> sshc_sup --+--> "system sup" (etc) +%%% | | +%%% | +--> "system sup" (etc) +%%% | : +%%% | +--> "system sup" (etc) +%%% | +%%% +-----> sshc_sup --+--> "system sup" (etc) +%%% | +%%% +--> "system sup" (etc) +%%% : +%%% +--> "system sup" (etc) -module(ssh_app). -behaviour(application). +-behaviour(supervisor). +%% 'application' export: -export([start/2, stop/1]). +%% 'supervisor' export: +-export([init/1]). + + +%%%========================================================================= +%%% Application callback +%%%========================================================================= start(_Type, _State) -> - supervisor:start_link({local, ssh_sup}, ssh_sup, []). + supervisor:start_link({local,ssh_sup}, ?MODULE, [ssh_sup]). stop(_State) -> ok. + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= +init([ssh_sup]) -> + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + ChildSpecs = [#{id => SupName, + start => {supervisor, start_link, + [{local,SupName}, ?MODULE, [sshX_sup]]}, + type => supervisor} + || SupName <- [sshd_sup, sshc_sup] + ], + {ok, {SupFlags,ChildSpecs}}; + +init([sshX_sup]) -> + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + ChildSpecs = [], + {ok, {SupFlags,ChildSpecs}}. diff --git a/lib/ssh/src/ssh_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl index 4b36c8a5a0..bdecfe8c21 100644 --- a/lib/ssh/src/ssh_channel_sup.erl +++ b/lib/ssh/src/ssh_channel_sup.erl @@ -40,35 +40,17 @@ start_link(Args) -> start_child(client, ChannelSup, ConnRef, Callback, Id, Args, Exec, _Opts) when is_pid(ConnRef) -> - start_the_child(ssh_client_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec); + start_the_channel(ssh_client_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec); + start_child(server, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts) when is_pid(ConnRef) -> case max_num_channels_not_exceeded(ChannelSup, Opts) of true -> - start_the_child(ssh_server_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec); + start_the_channel(ssh_server_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec); false -> {error, max_num_channels_exceeded} end. -start_the_child(ChanMod, ChannelSup, ConnRef, Callback, Id, Args, Exec) -> - ChildSpec = - #{id => make_ref(), - start => {ChanMod, start_link, [ConnRef, Id, Callback, Args, Exec]}, - restart => temporary, - type => worker, - modules => [ChanMod] - }, - case supervisor:start_child(ChannelSup, ChildSpec) of - {ok, Pid} -> - {ok, Pid}; - {ok, Pid, _Info} -> - {ok,Pid}; - {error, {Error,_Info}} -> - {error, Error}; - {error, Error} -> - {error, Error} - end. - %%%========================================================================= %%% Supervisor callback %%%========================================================================= @@ -88,3 +70,20 @@ max_num_channels_not_exceeded(ChannelSup, Opts) -> supervisor:which_children(ChannelSup)]), %% Note that NumChannels is BEFORE starting a new one NumChannels < MaxNumChannels. + + +start_the_channel(ChanMod, ChannelSup, ConnRef, Callback, Id, Args, Exec) -> + ChildSpec = + #{id => make_ref(), + start => {ChanMod, start_link, [ConnRef, Id, Callback, Args, Exec]}, + restart => temporary, + type => worker, + modules => [ChanMod] + }, + case supervisor:start_child(ChannelSup, ChildSpec) of + {ok, Pid} -> {ok, Pid}; + {ok, Pid, _Info} -> {ok, Pid}; + {error, {Error,_Info}} -> {error, Error}; + {error, Error} -> {error, Error} + end. + diff --git a/lib/ssh/src/ssh_client_channel.erl b/lib/ssh/src/ssh_client_channel.erl index 3cfffc25b4..455cb72d2f 100644 --- a/lib/ssh/src/ssh_client_channel.erl +++ b/lib/ssh/src/ssh_client_channel.erl @@ -70,7 +70,8 @@ -export([cache_create/0, cache_lookup/2, cache_update/2, cache_delete/1, cache_delete/2, cache_foldl/3, cache_info/2, cache_find/2, - get_print_info/1]). + get_print_info/1, get_print_info/2 + ]). -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]). @@ -212,6 +213,9 @@ handle_call(get_print_info, _From, State) -> }, {reply, Reply, State}; +handle_call({get_print_info,channel_cb}, _From, State) -> + {reply, State#state.channel_cb, State}; + handle_call(Request, From, #state{channel_cb = Module, channel_state = ChannelState} = State) -> try Module:handle_call(Request, From, ChannelState) of @@ -365,6 +369,9 @@ cache_find(ChannelPid, Cache) -> get_print_info(Pid) -> call(Pid, get_print_info, 1000). +get_print_info(Pid, Arg) -> + call(Pid, {get_print_info,Arg}, 1000). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl index e00e78d6b7..38e2b2de0b 100644 --- a/lib/ssh/src/ssh_connect.hrl +++ b/lib/ssh/src/ssh_connect.hrl @@ -25,7 +25,6 @@ -define(DEFAULT_PACKET_SIZE, 65536). -define(DEFAULT_WINDOW_SIZE, 10*?DEFAULT_PACKET_SIZE). --define(DEFAULT_TIMEOUT, 5000). -define(MAX_PROTO_VERSION, 255). % Max length of the hello string %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -268,7 +267,5 @@ cli_spec, options, exec, - system_supervisor, - sub_system_supervisor, - connection_supervisor + sub_system_supervisor }). diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index a966f7bbf1..b8dd091c94 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -918,8 +918,7 @@ handle_msg(#ssh_msg_global_request{name = <<"tcpip-forward">>, {[{connection_reply, request_failure_msg()}], Connection}; true -> - Sups = ?GET_INTERNAL_OPT(supervisors, Opts), - SubSysSup = proplists:get_value(subsystem_sup, Sups), + SubSysSup = ?GET_INTERNAL_OPT(subsystem_sup, Opts), FwdSup = ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup), ConnPid = self(), case ssh_tcpip_forward_acceptor:supervised_start(FwdSup, diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 1bcfeb1146..13790157c7 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -42,7 +42,8 @@ %%==================================================================== %%% Start and stop --export([start_link/4, start_link/5, start_link/6, +-export([start_link_ng/4, start_link_ng/5, + takeover/4, stop/1 ]). @@ -95,74 +96,44 @@ %% Start / stop %%==================================================================== -start_link(client, Host, Port, Options, NegotiationTimeout) -> - {_, Callback, _} = ?GET_OPT(transport, Options), - SocketOpts = [{active,false} | ?GET_OPT(socket_options,Options)], - try Callback:connect(Host, Port, SocketOpts, ?GET_OPT(connect_timeout,Options)) of - {ok, Socket} -> - start_link(client, Socket, Options, NegotiationTimeout); - {error, Reason} -> - {error, Reason} - catch - _:badarg -> {error, {options,?GET_OPT(socket_options,Options)}}; - _:{error,Reason} -> {error,Reason}; - error:Error -> {error,Error}; - Class:Error -> {error, {Class,Error}} +start_link_ng(Role, Address, Socket, Options) -> + start_link_ng(Role, Address, undefined, Socket, Options). + +start_link_ng(Role, _Address=#address{}, Id, Socket, Options) -> + case gen_statem:start_link(?MODULE, + [Role, Socket, Options], + [{spawn_opt, [{message_queue_data,off_heap}]}]) of + + {ok, Pid} when Id =/= undefined -> + %% Announce the ConnectionRef to the system supervisor so it could + %% 1) initiate the socket handover, and + %% 2) be returned to whoever called for example ssh:connect; the Pid + %% returned from this function is "consumed" by the subsystem + %% supervisor. + ?GET_INTERNAL_OPT(user_pid,Options) ! {new_connection_ref, Id, Pid}, + {ok, Pid}; + + Others -> + Others end. -start_link(Role, Socket, Options, NegotiationTimeout) -> - {ok, {Host,Port}} = inet:sockname(Socket), - start_link(Role, Host, Port, Socket, Options, NegotiationTimeout). -start_link(Role, Host, Port, Socket, Options0, NegotiationTimeout) -> - try - Options1 = ?PUT_INTERNAL_OPT([{user_pid, self()} - ], Options0), - Profile = ?GET_OPT(profile, Options1), - Sup = case Role of - client -> sshc_sup; - server -> sshd_sup - end, - {ok, {SystemSup, SubSysSup}} = - Sup:start_system_subsystem(Host, Port, Profile, Options1), - ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup), - Options = ?PUT_INTERNAL_OPT([{supervisors, [{system_sup, SystemSup}, - {subsystem_sup, SubSysSup}, - {connection_sup, ConnectionSup}]} - ], Options1), - - %% This is essentially gen_statem:start_link/3 : - case ssh_connection_sup:start_child(ConnectionSup, - [?MODULE, - [Role, Socket, Options], - [{spawn_opt, [{message_queue_data,off_heap}]}]] - ) of - {ok, Pid} -> - %% Now the connection_handler process is started - %% First set the group leader if it is a client: - case Role of - client -> - group_leader(group_leader(), Pid); - _ -> - ok - end, - %% No message handling yet. It begins when the socket_control/3 is called - case socket_control(Socket, Pid, Options) of - ok -> - %% handshake returns {ok,Pid} after a successful connection setup. - %% or {error,Reason} after an unsuccesful one: - 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}; - error:Error -> {error,Error}; - Class:Error -> {error, {Class,Error}} +takeover(ConnPid, client, Socket, Options) -> + group_leader(group_leader(), ConnPid), + takeover(ConnPid, common, Socket, Options); + +takeover(ConnPid, _, Socket, Options) -> + {_, Callback, _} = ?GET_OPT(transport, Options), + case Callback:controlling_process(Socket, ConnPid) of + ok -> + gen_statem:cast(ConnPid, socket_control), + NegTimeout = ?GET_INTERNAL_OPT(negotiation_timeout, + Options, + ?GET_OPT(negotiation_timeout, Options) + ), + handshake(ConnPid, erlang:monitor(process,ConnPid), NegTimeout); + {error, Reason} -> + {error, Reason} end. %%-------------------------------------------------------------------- @@ -455,14 +426,11 @@ init([Role, Socket, Opts]) when Role==client ; Role==server -> %%% 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) + sub_system_supervisor = ?GET_INTERNAL_OPT(subsystem_sup, Opts) }, case Role of server -> @@ -534,29 +502,20 @@ 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 {Pid, ssh_connected} -> - erlang:demonitor(Ref), + erlang:demonitor(Ref, [flush]), {ok, Pid}; {Pid, {not_connected, Reason}} -> - erlang:demonitor(Ref), + erlang:demonitor(Ref, [flush]), {error, Reason}; {'DOWN', Ref, process, Pid, {shutdown, Reason}} -> {error, Reason}; {'DOWN', Ref, process, Pid, Reason} -> {error, Reason} after Timeout -> + erlang:demonitor(Ref, [flush]), ssh_connection_handler:stop(Pid), {error, timeout} end. @@ -1037,8 +996,7 @@ handle_event({call,From}, {eof, ChannelId}, StateName, D0) handle_event({call,From}, get_misc, StateName, #data{connection_state = #connection{options = Opts}} = D) when ?CONNECTED(StateName) -> - Sups = ?GET_INTERNAL_OPT(supervisors, Opts), - SubSysSup = proplists:get_value(subsystem_sup, Sups), + SubSysSup = ?GET_INTERNAL_OPT(subsystem_sup, Opts), Reply = {ok, {SubSysSup, ?role(StateName), Opts}}, {keep_state, D, [{reply,From,Reply}]}; @@ -1384,23 +1342,10 @@ handle_event(Type, Ev, StateName, D0) -> %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . terminate(normal, _StateName, D) -> - stop_subsystem(D), - close_transport(D); - -terminate({shutdown,"Connection closed"}, _StateName, D) -> - %% Normal: terminated by a sent by peer - stop_subsystem(D), - close_transport(D); - -terminate({shutdown,{init,Reason}}, StateName, D) -> - %% Error in initiation. "This error should not occur". - log(error, D, "Shutdown in init (StateName=~p): ~p~n", [StateName,Reason]), - stop_subsystem(D), close_transport(D); terminate({shutdown,_R}, _StateName, D) -> %% Internal termination, usually already reported via ?send_disconnect resulting in a log entry - stop_subsystem(D), close_transport(D); terminate(shutdown, _StateName, D0) -> @@ -1411,11 +1356,6 @@ terminate(shutdown, _StateName, D0) -> D0), close_transport(D); -terminate(killed, _StateName, D) -> - %% Got a killed signal - stop_subsystem(D), - close_transport(D); - terminate(Reason, StateName, D0) -> %% Others, e.g undef, {badmatch,_}, ... log(error, D0, Reason), @@ -1423,7 +1363,6 @@ terminate(Reason, StateName, D0) -> "Internal error", io_lib:format("Reason: ~p",[Reason]), StateName, D0), - stop_subsystem(D), close_transport(D). %%-------------------------------------------------------------------- @@ -1501,51 +1440,10 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Starting - -%%-------------------------------------------------------------------- -%% Stopping - -stop_subsystem(#data{ssh_params = - #ssh{role = Role}, - connection_state = - #connection{system_supervisor = SysSup, - sub_system_supervisor = SubSysSup} - }) when is_pid(SysSup) andalso is_pid(SubSysSup) -> - C = self(), - spawn(fun() -> - wait_until_dead(C, 10000), - case Role of - server -> - ssh_system_sup:stop_subsystem(SysSup, SubSysSup); - client -> - ssh_system_sup:stop_subsystem(SysSup, SubSysSup), - wait_until_dead(SubSysSup, 1000), - sshc_sup:stop_system(SysSup) - end - end); -stop_subsystem(_) -> - ok. - - -wait_until_dead(Pid, Timeout) -> - Mref = erlang:monitor(process, Pid), - receive - {'DOWN', Mref, process, Pid, _Info} -> ok - after - Timeout -> ok - end. - - close_transport(#data{transport_cb = Transport, socket = Socket}) -> - try - Transport:close(Socket) - of - _ -> ok - catch - _:_ -> ok - end. + catch Transport:close(Socket), + ok. %%-------------------------------------------------------------------- available_hkey_algorithms(client, Options) -> diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl deleted file mode 100644 index 192ce6f65d..0000000000 --- a/lib/ssh/src/ssh_connection_sup.erl +++ /dev/null @@ -1,58 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2018. 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: Ssh connection supervisor. -%%---------------------------------------------------------------------- - --module(ssh_connection_sup). - --behaviour(supervisor). - -%% API --export([start_link/1]). --export([start_child/2]). - -%% Supervisor callback --export([init/1]). - -%%%========================================================================= -%%% API -%%%========================================================================= -start_link(Args) -> - supervisor:start_link(?MODULE, [Args]). - -start_child(Sup, Args) -> - supervisor:start_child(Sup, Args). - -%%%========================================================================= -%%% Supervisor callback -%%%========================================================================= -init(_) -> - SupFlags = #{strategy => simple_one_for_one, - intensity => 0, - period => 3600 - }, - ChildSpecs = [#{id => undefined, % As simple_one_for_one is used. - start => {gen_statem, start_link, []}, - restart => temporary % because there is no way to restart a crashed connection - } - ], - {ok, {SupFlags,ChildSpecs}}. diff --git a/lib/ssh/src/ssh_controller.erl b/lib/ssh/src/ssh_controller.erl deleted file mode 100644 index d8fbc8d0f2..0000000000 --- a/lib/ssh/src/ssh_controller.erl +++ /dev/null @@ -1,125 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2020. 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: Handles an ssh connection, e.i. both the -%% setup SSH Transport Layer Protocol (RFC 4253), Authentication -%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255) -%% Details of the different protocols are -%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl -%% ---------------------------------------------------------------------- - --module(ssh_controller). - --behaviour(gen_server). - - -%%==================================================================== -%%% Exports -%%==================================================================== - -%%% API --export([start_system_subsystem/7, - stop_system/2 - ]). - -%%% Start and stop --export([start_link/2 - ]). - --export([init/1, - handle_call/3, - handle_cast/2 - ]). - -%%==================================================================== -%% Start / stop -%%==================================================================== - -start_link(Role, RegName) -> - gen_server:start_link({local,RegName}, ?MODULE, [Role], []). - -%%==================================================================== -%% Internal application API -%%==================================================================== - --define(TIMEOUT, 30000). - -start_system_subsystem(Controller, Sup, Host, Port, Profile, Options, ChildSpec) -> - gen_server:call(Controller, {start_system_subsystem, Sup, Host, Port, Profile, Options, ChildSpec}, ?TIMEOUT). - -stop_system(Controller, SysSup) -> - gen_server:call(Controller, {stop_system,SysSup}, ?TIMEOUT). - -%%==================================================================== -%% Internal process state -%%==================================================================== --record(data, { - role - }). - -%%==================================================================== -%% Intitialisation -%%==================================================================== -init([Role]=_Args) -> - {ok, #data{role=Role}}. - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== -handle_call({start_system_subsystem, Sup, Address, Port, Profile, Options, ChildSpec}, _From, D) -> - try - {ok,SystemSup0} = - case ssh_system_sup:system_supervisor(Address, Port, Profile) of - undefined -> - supervisor:start_child(Sup, ChildSpec); - Pid -> - {ok,Pid} - end, - {SystemSup0, ssh_system_sup:start_subsystem(SystemSup0, D#data.role, Address, Port, Profile, Options)} - of - {SystemSup, {ok,SubSysSup}} -> - {reply, {ok,{SystemSup,SubSysSup}}, D} - catch - C:E:S -> - {reply, {error,{failed,C,E,S}}, D} - end; - - -handle_call({stop_system,SysSup}, _From, D) -> - try - case supervisor:which_children(SysSup) of - [] -> - ssh_system_sup:stop_system(D#data.role, SysSup); - _X -> - ok - end - catch - _:_ -> - %% Already stopped (?) - skip - end, - {reply, ok, D}. - - - - -handle_cast(_Request, D) -> - {noreply, D}. diff --git a/lib/ssh/src/ssh_lib.erl b/lib/ssh/src/ssh_lib.erl index c1553896fa..edfd1bb8a5 100644 --- a/lib/ssh/src/ssh_lib.erl +++ b/lib/ssh/src/ssh_lib.erl @@ -30,6 +30,8 @@ format_time_ms/1 ]). +-include("ssh.hrl"). + %%%---------------------------------------------------------------- format_address_port({IP,Port}) when is_integer(Port) -> format_address_port(IP, Port); @@ -44,6 +46,8 @@ format_address_port(Address, Port) -> end. %%%---------------------------------------------------------------- +format_address(#address{address=A, port=P}) -> + format_address_port(A,P); format_address(A) -> try inet:ntoa(A) catch diff --git a/lib/ssh/src/ssh_server_channel.erl b/lib/ssh/src/ssh_server_channel.erl index a7489847ff..11cdfbdf17 100644 --- a/lib/ssh/src/ssh_server_channel.erl +++ b/lib/ssh/src/ssh_server_channel.erl @@ -44,7 +44,7 @@ %%% Internal API -export([start_link/5, - get_print_info/1 + get_print_info/1, get_print_info/2 ]). start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) -> @@ -53,3 +53,6 @@ start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) -> get_print_info(Pid) -> ssh_client_channel:get_print_info(Pid). + +get_print_info(Pid, Arg) -> + ssh_client_channel:get_print_info(Pid, Arg). diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index f16db5af6d..22997d863a 100644 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.erl @@ -234,8 +234,8 @@ stop_channel(Pid) -> receive {'DOWN',MonRef,_,_,_} -> ok after 1000 -> - exit(Pid, kill), erlang:demonitor(MonRef, [flush]), + exit(Pid, kill), ok end; false -> diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl index 140b219b32..28a23d434b 100644 --- a/lib/ssh/src/ssh_subsystem_sup.erl +++ b/lib/ssh/src/ssh_subsystem_sup.erl @@ -29,10 +29,8 @@ -include("ssh.hrl"). -export([start_link/5, - connection_supervisor/1, - channel_supervisor/1, - tcpip_fwd_supervisor/1, - start_channel/8 + start_channel/8, + tcpip_fwd_supervisor/1 ]). %% Supervisor callback @@ -41,81 +39,72 @@ %%%========================================================================= %%% API %%%========================================================================= -start_link(Role, Address, Port, Profile, Options) -> - supervisor:start_link(?MODULE, [Role, Address, Port, Profile, Options]). - -connection_supervisor(SupPid) -> - Children = supervisor:which_children(SupPid), - ssh_connection_sup(Children). - -channel_supervisor(SupPid) when is_pid(SupPid) -> - Children = supervisor:which_children(SupPid), - ssh_channel_sup(Children). - -tcpip_fwd_supervisor(SupPid) when is_pid(SupPid) -> - Children = supervisor:which_children(SupPid), - tcpip_fwd_sup(Children). +start_link(Role, Address=#address{}, Id, Socket, Options) -> + case supervisor:start_link(?MODULE, [Role, Address, Id, Socket, Options]) of + {error, {shutdown, {failed_to_start_child, _, Error}}} -> + {error,Error}; + Other -> + Other + end. start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) -> ChannelSup = channel_supervisor(SupPid), ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts). +tcpip_fwd_supervisor(SubSysSup) -> + find_child(tcpip_forward_acceptor_sup, SubSysSup). + + %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init([Role, Address, Port, Profile, Options]) -> - SupFlags = #{strategy => one_for_all, - intensity => 0, - period => 3600 +init([Role, Address, Id, Socket, Options]) -> + SubSysSup = self(), + SupFlags = #{strategy => one_for_all, + auto_shutdown => any_significant, + intensity => 0, + period => 3600 }, - ChildSpecs = child_specs(Role, Address, Port, Profile, Options), + ChildSpecs = [#{id => connection, + restart => temporary, + type => worker, + significant => true, + start => {ssh_connection_handler, + start_link_ng, + [Role, Address, Id, Socket, + ?PUT_INTERNAL_OPT([ + {subsystem_sup, SubSysSup} + ], Options) + ] + } + }, + #{id => channel_sup, + restart => temporary, + type => supervisor, + start => {ssh_channel_sup, start_link, [Options]} + }, + + #{id => tcpip_forward_acceptor_sup, + restart => temporary, + type => supervisor, + start => {ssh_tcpip_forward_acceptor_sup, start_link, []} + } + ], {ok, {SupFlags,ChildSpecs}}. %%%========================================================================= %%% Internal functions %%%========================================================================= -child_specs(Role, Address, Port, Profile, Options) -> - [ssh_channel_child_spec(Role, Address, Port, Profile, Options), - ssh_connection_child_spec(Role, Address, Port, Profile, Options), - ssh_tcpip_forward_acceptor_child_spec()]. - -ssh_connection_child_spec(Role, Address, Port, _Profile, Options) -> - #{id => id(Role, ssh_connection_sup, Address, Port), - start => {ssh_connection_sup, start_link, [Options]}, - restart => temporary, - type => supervisor - }. - -ssh_channel_child_spec(Role, Address, Port, _Profile, Options) -> - #{id => id(Role, ssh_channel_sup, Address, Port), - start => {ssh_channel_sup, start_link, [Options]}, - restart => temporary, - type => supervisor - }. - -ssh_tcpip_forward_acceptor_child_spec() -> - #{id => make_ref(), - start => {ssh_tcpip_forward_acceptor_sup, start_link, []}, - restart => temporary, - type => supervisor - }. - - -id(Role, Sup, Address, Port) -> - {Role, Sup, Address, Port}. - -ssh_connection_sup([{_, Child, _, [ssh_connection_sup]} | _]) -> - Child; -ssh_connection_sup([_ | Rest]) -> - ssh_connection_sup(Rest). - -ssh_channel_sup([{_, Child, _, [ssh_channel_sup]} | _]) -> - Child; -ssh_channel_sup([_ | Rest]) -> - ssh_channel_sup(Rest). - -tcpip_fwd_sup([{_, Child, _, [ssh_tcpip_forward_acceptor_sup]} | _]) -> - Child; -tcpip_fwd_sup([_ | Rest]) -> - tcpip_fwd_sup(Rest). +channel_supervisor(SubSysSup) -> find_child(channel_sup, SubSysSup). + +find_child(Id, Sup) when is_pid(Sup) -> + try + {Id, Pid, _, _} = lists:keyfind(Id, 1, supervisor:which_children(Sup)), + Pid + catch + exit:{no_proc,_} -> + {error, no_proc}; + _:_ -> + {error, {id_not_found,?MODULE,Id}} + end. diff --git a/lib/ssh/src/ssh_sup.erl b/lib/ssh/src/ssh_sup.erl deleted file mode 100644 index 61afbcd2ed..0000000000 --- a/lib/ssh/src/ssh_sup.erl +++ /dev/null @@ -1,49 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2018. 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 top supervisor for the ssh application. -%%---------------------------------------------------------------------- --module(ssh_sup). - --behaviour(supervisor). - --export([init/1]). - -%%%========================================================================= -%%% Supervisor callback -%%%========================================================================= -init(_) -> - SupFlags = #{strategy => one_for_one, - intensity => 10, - period => 3600 - }, - ChildSpecs = [#{id => sshd_sup, - start => {sshd_sup, start_link, []}, - type => supervisor - }, - #{id => sshc_sup, - start => {sshc_sup, start_link, []}, - type => supervisor - } - ], - {ok, {SupFlags,ChildSpecs}}. - diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl index d80fe3e296..7c9a81fe37 100644 --- a/lib/ssh/src/ssh_system_sup.erl +++ b/lib/ssh/src/ssh_system_sup.erl @@ -31,153 +31,216 @@ -include("ssh.hrl"). --export([start_link/5, stop_listener/1, - stop_listener/3, stop_system/2, - stop_system/4, system_supervisor/3, - subsystem_supervisor/1, channel_supervisor/1, - connection_supervisor/1, - acceptor_supervisor/1, start_subsystem/6, - stop_subsystem/2, - get_options/4 +-export([start_link/3, + stop_listener/1, + stop_system/2, + start_system/3, + start_subsystem/4, + get_daemon_listen_address/1, + addresses/1, + addresses/2, + get_options/2 ]). %% Supervisor callback -export([init/1]). --define(START(Address, Port, Profile, Options), - {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]}). - %%%========================================================================= %%% API %%%========================================================================= -start_link(Role, Address, Port, Profile, Options) -> - Name = make_name(Address, Port, Profile), - supervisor:start_link({local, Name}, ?MODULE, [Role, Address, Port, Profile, Options]). + +start_system(Role, Address0, Options) -> + case find_system_sup(Role, Address0) of + {ok,{SysPid,Address}} -> + restart_acceptor(SysPid, Address, Options); + {error,not_found} -> + supervisor:start_child(sup(Role), + #{id => {?MODULE,Address0}, + start => {?MODULE, start_link, [Role, Address0, Options]}, + restart => temporary, + type => supervisor + }) + end. + +%%%---------------------------------------------------------------- +stop_system(Role, SysSup) when is_pid(SysSup) -> + case lists:keyfind(SysSup, 2, supervisor:which_children(sup(Role))) of + {{?MODULE,Name}, SysSup, _, _} -> stop_system(Role, Name); + false -> undefind + end; +stop_system(Role, Name) -> + supervisor:terminate_child(sup(Role), {?MODULE,Name}). + + +%%%---------------------------------------------------------------- +stop_listener(SystemSup) when is_pid(SystemSup) -> + {Name, _, _, _} = lookup(ssh_acceptor_sup, SystemSup), + supervisor:terminate_child(SystemSup, Name), + supervisor:delete_child(SystemSup, Name). + +%%%---------------------------------------------------------------- +get_daemon_listen_address(SystemSup) -> + try lookup(ssh_acceptor_sup, SystemSup) + of + {{ssh_acceptor_sup,Address}, _, _, _} -> + {ok, Address}; + _ -> + {error, not_found} + catch + _:_ -> + {error, not_found} + end. + +%%%---------------------------------------------------------------- +%%% Start the subsystem child. It is a child of the system supervisor (callback = this module) +start_subsystem(Role, Address=#address{}, Socket, Options0) -> + Options = ?PUT_INTERNAL_OPT([{user_pid, self()}], Options0), + Id = make_ref(), + case get_system_sup(Role, Address, Options) of + {ok,SysPid} -> + case supervisor:start_child(SysPid, + #{id => Id, + start => {ssh_subsystem_sup, start_link, + [Role,Address,Id,Socket,Options] + }, + restart => temporary, + significant => true, + type => supervisor + }) + of + {ok,_SubSysPid} -> + try + receive + {new_connection_ref, Id, ConnPid} -> + ssh_connection_handler:takeover(ConnPid, Role, Socket, Options) + after 10000 -> + + error(timeout) + end + catch + error:{badmatch,{error,Error}} -> + {error,Error}; + error:timeout -> + %% The connection was started, but the takover procedure timed out, + %% therefor it exists a subtree, but it is not quite ready and + %% must be removed (by the supervisor above): + supervisor:terminate_child(SysPid, Id), + {error, connection_start_timeout} + end; + Others -> + Others + end; + Others -> + Others + end. + + +%%%---------------------------------------------------------------- +start_link(Role, Address, Options) -> + supervisor:start_link(?MODULE, [Role, Address, Options]). + + +%%%---------------------------------------------------------------- +addresses(Role) -> + addresses(Role, #address{address=any, port=any, profile=any}). + +addresses(Role, #address{address=Address, port=Port, profile=Profile}) -> + [{SysSup,A} || {{ssh_system_sup,A},SysSup,supervisor,_} <- + supervisor:which_children(sup(Role)), + Address == any orelse A#address.address == Address, + Port == any orelse A#address.port == Port, + Profile == any orelse A#address.profile == Profile]. %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init([server, Address, Port, Profile, Options]) -> - SupFlags = #{strategy => one_for_one, +init([Role, Address, Options]) -> + SupFlags = #{strategy => one_for_one, + auto_shutdown => all_significant, intensity => 0, period => 3600 }, ChildSpecs = - case ?GET_INTERNAL_OPT(connected_socket,Options,undefined) of - undefined -> - [#{id => id(ssh_acceptor_sup, Address, Port, Profile), - start => ?START(Address,Port,Profile,Options), - restart => transient, - type => supervisor - }]; + case {Role, is_socket_server(Options)} of + {server, false} -> + [acceptor_sup_child_spec(_SysSup=self(), Address, Options)]; _ -> [] end, - {ok, {SupFlags,ChildSpecs}}; - -init([client, _Address, _Port, _Profile, _Options]) -> - SupFlags = #{strategy => one_for_one, - intensity => 0, - period => 3600 - }, - ChildSpecs = [], {ok, {SupFlags,ChildSpecs}}. %%%========================================================================= %%% Service API %%%========================================================================= -stop_listener(SystemSup) -> - {Name, AcceptorSup, _, _} = lookup(ssh_acceptor_sup, SystemSup), - case supervisor:terminate_child(AcceptorSup, Name) of - ok -> - supervisor:delete_child(AcceptorSup, Name); - Error -> - Error - end. - -stop_listener(Address, Port, Profile) -> - stop_listener( - system_supervisor(Address, Port, Profile)). - -stop_system(server, SysSup) -> catch sshd_sup:stop_child(SysSup), ok; -stop_system(client, SysSup) -> catch sshc_sup:stop_child(SysSup), ok. +%% A macro to keep get_options/2 and acceptor_sup_child_spec/3 synchronized +-define(accsup_start(SysSup,Addr,Opts), + {ssh_acceptor_sup, start_link, [SysSup,Addr,Opts]} + ). -stop_system(server, Address, Port, Profile) -> - catch sshd_sup:stop_child(Address, Port, Profile), - ok. - - -get_options(Sup, Address, Port, Profile) -> +get_options(Sup, Address = #address{}) -> + %% Lookup the Option parameter in the running ssh_acceptor_sup: try - {ok, #{start:=?START(Address,Port,Profile,Options)}} = - supervisor:get_childspec(Sup, id(ssh_acceptor_sup,Address,Port,Profile)), - {ok, Options} + {ok, #{start:=?accsup_start(_, _, Options)}} = + supervisor:get_childspec(Sup, {ssh_acceptor_sup,Address}), + {ok, Options} catch _:_ -> {error,not_found} end. -system_supervisor(Address, Port, Profile) -> - Name = make_name(Address, Port, Profile), - whereis(Name). - -subsystem_supervisor(SystemSup) -> - {_, Child, _, _} = lookup(ssh_subsystem_sup, SystemSup), - Child. - -channel_supervisor(SystemSup) -> - ssh_subsystem_sup:channel_supervisor( - subsystem_supervisor(SystemSup)). - -connection_supervisor(SystemSup) -> - ssh_subsystem_sup:connection_supervisor( - subsystem_supervisor(SystemSup)). - -acceptor_supervisor(SystemSup) -> - {_, Child, _, _} = lookup(ssh_acceptor_sup, SystemSup), - Child. - - -start_subsystem(SystemSup, Role, Address, Port, Profile, Options) -> - SubsystemSpec = - #{id => make_ref(), - start => {ssh_subsystem_sup, start_link, [Role, Address, Port, Profile, Options]}, - restart => temporary, - type => supervisor - }, - supervisor:start_child(SystemSup, SubsystemSpec). - -stop_subsystem(SystemSup, SubSys) -> - case catch lists:keyfind(SubSys, 2, supervisor:which_children(SystemSup)) of - false -> - {error, not_found}; - {Id, _, _, _} -> - spawn(fun() -> supervisor:terminate_child(SystemSup, Id), - supervisor:delete_child(SystemSup, Id) end), - ok; - {'EXIT', {noproc, _}} -> - %% Already terminated; probably shutting down. - ok; - {'EXIT', {shutdown, _}} -> - %% Already shutting down. - ok - end. - %%%========================================================================= %%% Internal functions %%%========================================================================= -id(Sup, Address, Port, Profile) -> - {Sup, Address, Port, Profile}. - -make_name(Address, Port, Profile) -> - list_to_atom(lists:flatten(io_lib:format("ssh_system_~s_~p_~p_sup", [fmt_host(Address), Port, Profile]))). - -fmt_host(IP) when is_tuple(IP) -> inet:ntoa(IP); -fmt_host(A) when is_atom(A) -> A; -fmt_host(S) when is_list(S) -> S. +%% A separate function because this spec is need in >1 places +acceptor_sup_child_spec(SysSup, Address, Options) -> + #{id => {ssh_acceptor_sup,Address}, + start => ?accsup_start(SysSup, Address, Options), + restart => transient, + significant => true, + type => supervisor + }. lookup(SupModule, SystemSup) -> lists:keyfind([SupModule], 4, supervisor:which_children(SystemSup)). +get_system_sup(Role, Address0, Options) -> + case find_system_sup(Role, Address0) of + {ok,{SysPid,_Address}} -> + {ok,SysPid}; + {error,not_found} -> + start_system(Role, Address0, Options); + {error,Error} -> + {error,Error} + end. + +find_system_sup(Role, Address0) -> + case addresses(Role, Address0) of + [{SysSupPid,Address}] -> {ok,{SysSupPid,Address}}; + [] -> {error,not_found}; + [_,_|_] -> {error,ambigous} + end. + +sup(client) -> sshc_sup; +sup(server) -> sshd_sup. + + +is_socket_server(Options) -> + undefined =/= ?GET_INTERNAL_OPT(connected_socket,Options,undefined). + +restart_acceptor(SysPid, Address, Options) -> + case lookup(ssh_acceptor_sup, SysPid) of + {_,_,supervisor,_} -> + {error, eaddrinuse}; + false -> + ChildSpec = acceptor_sup_child_spec(SysPid, Address, Options), + case supervisor:start_child(SysPid, ChildSpec) of + {ok,_ChildPid} -> + {ok,SysPid}; % sic! + {ok,_ChildPid,_Info} -> + {ok,SysPid}; % sic! + {error,Error} -> + {error,Error} + end + end. diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl deleted file mode 100644 index bbf3d16440..0000000000 --- a/lib/ssh/src/sshc_sup.erl +++ /dev/null @@ -1,109 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2018. 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 client top supervisor -%%---------------------------------------------------------------------- - --module(sshc_sup). - --behaviour(supervisor). - --include("ssh.hrl"). - --export([start_link/0, - start_child/4, - start_system_subsystem/4, - stop_child/1, - stop_system/1 - ]). - -%% Supervisor callback --export([init/1]). - --define(SSHC_SUP, ?MODULE). - -%%%========================================================================= -%%% API -%%%========================================================================= -start_link() -> - supervisor:start_link({local,?SSHC_SUP}, ?MODULE, []). - -start_child(Address, Port, Profile, Options) -> - case ssh_system_sup:system_supervisor(Address, Port, Profile) of - undefined -> - %% Here we a new connction on a new Host/Port/Profile - Spec = child_spec(Address, Port, Profile, Options), - supervisor:start_child(?MODULE, Spec); - Pid -> - {ok,Pid} - end. - - -start_system_subsystem(Host, Port, Profile, Options) -> - ssh_controller:start_system_subsystem(client_controller, ?MODULE, Host, Port, Profile, Options, - child_spec(Host, Port, Profile, Options) - ). - -stop_child(ChildId) when is_tuple(ChildId) -> - supervisor:terminate_child(?SSHC_SUP, ChildId); -stop_child(ChildPid) when is_pid(ChildPid)-> - stop_child(system_name(ChildPid)). - -stop_system(SysSup) -> - ssh_controller:stop_system(client_controller, SysSup). - - -%%%========================================================================= -%%% Supervisor callback -%%%========================================================================= -init(_) -> - SupFlags = #{strategy => one_for_one, - intensity => 0, - period => 3600 - }, - ChildSpecs = [#{id => client_controller, - start => {ssh_controller, start_link, [client, client_controller]}, - restart => permanent, - type => worker - } - ], - {ok, {SupFlags,ChildSpecs}}. - -%%%========================================================================= -%%% Internal functions -%%%========================================================================= -child_spec(Address, Port, Profile, Options) -> - #{id => id(Address, Port, Profile), - start => {ssh_system_sup, start_link, [client, Address, Port, Profile, Options]}, - restart => temporary, - type => supervisor - }. - -id(Address, Port, Profile) -> - {client, ssh_system_sup, Address, Port, Profile}. - -system_name(SysSup) -> - case lists:keyfind(SysSup, 2, supervisor:which_children(?SSHC_SUP)) of - {Name, SysSup, _, _} -> Name; - false -> undefind - end. - diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl deleted file mode 100644 index 6fd36bf97a..0000000000 --- a/lib/ssh/src/sshd_sup.erl +++ /dev/null @@ -1,114 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2018. 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 daemon top supervisor -%%---------------------------------------------------------------------- - --module(sshd_sup). - --behaviour(supervisor). - --include("ssh.hrl"). - --export([start_link/0, - start_child/3, - start_system_subsystem/4, - stop_child/1, - stop_child/3 - ]). - -%% Supervisor callback --export([init/1]). - --define(SSHD_SUP, ?MODULE). - -%%%========================================================================= -%%% API -%%%========================================================================= -start_link() -> - supervisor:start_link({local,?SSHD_SUP}, ?MODULE, []). - -start_child(Address, Port, Options) -> - Profile = ?GET_OPT(profile,Options), - case ssh_system_sup:system_supervisor(Address, Port, Profile) of - undefined -> - %% Here we start listening on a new Host/Port/Profile - Spec = child_spec(Address, Port, Profile, Options), - supervisor:start_child(?SSHD_SUP, Spec); - Pid -> - %% Here we resume listening on a new Host/Port/Profile after - %% haveing stopped listening to he same with ssh:stop_listen(Pid) - AccPid = ssh_system_sup:acceptor_supervisor(Pid), - ssh_acceptor_sup:start_child(AccPid, 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)-> - stop_child(system_name(ChildPid)). - - -stop_child(Address, Port, Profile) -> - Id = id(Address, Port, Profile), - stop_child(Id). - -%%%========================================================================= -%%% Supervisor callback -%%%========================================================================= -init(_) -> - SupFlags = #{strategy => one_for_one, - intensity => 10, - period => 3600 - }, - ChildSpecs = [#{id => daemon_controller, - start => {ssh_controller, start_link, [server, daemon_controller]}, - restart => permanent, - type => worker - } - ], - {ok, {SupFlags,ChildSpecs}}. - -%%%========================================================================= -%%% Internal functions -%%%========================================================================= -child_spec(Address, Port, Profile, Options) -> - #{id => id(Address, Port, Profile), - start => {ssh_system_sup, start_link, [server, Address, Port, Profile, Options]}, - restart => temporary, - type => supervisor - }. - -id(Address, Port, Profile) -> - {server, ssh_system_sup, Address, Port, Profile}. - -system_name(SysSup) -> - case lists:keyfind(SysSup, 2, supervisor:which_children(?SSHD_SUP)) of - {Name, SysSup, _, _} -> Name; - false -> undefind - end. - diff --git a/lib/ssh/test/ssh.cover b/lib/ssh/test/ssh.cover index aa0da36c75..daf6c723b9 100644 --- a/lib/ssh/test/ssh.cover +++ b/lib/ssh/test/ssh.cover @@ -6,7 +6,7 @@ ssh_app, %% %% Supervisors - %% ssh_acceptor_sup, ssh_channel_sup, ssh_connection_sup, + %% ssh_acceptor_sup, ssh_channel_sup, %% sshc_sup, sshd_sup, ssh_subsystem_sup, ssh_sup, %% ssh_system_sup, ssh_tcpip_forward_acceptor_sup, diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index 49168d38bb..9daa887f6c 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -1179,7 +1179,7 @@ stop_listener(Config) when is_list(Config) -> ConnectionRef0 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, {password, "morot"}, - {user_interaction, true}, + {user_interaction, false}, {user_dir, UserDir}]), {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef0, infinity), diff --git a/lib/ssh/test/ssh_limited.cover b/lib/ssh/test/ssh_limited.cover index 2b0fcf5bf6..29c0121ae1 100644 --- a/lib/ssh/test/ssh_limited.cover +++ b/lib/ssh/test/ssh_limited.cover @@ -8,7 +8,7 @@ ssh_app, %% Supervisors - ssh_acceptor_sup, ssh_channel_sup, ssh_connection_sup, + ssh_acceptor_sup, ssh_channel_sup, sshc_sup, sshd_sup, ssh_subsystem_sup, ssh_sup, ssh_system_sup, ssh_tcpip_forward_acceptor_sup, diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl index 9e0e10c3bb..485bbe465f 100644 --- a/lib/ssh/test/ssh_sup_SUITE.erl +++ b/lib/ssh/test/ssh_sup_SUITE.erl @@ -50,6 +50,9 @@ -define(WAIT_FOR_SHUTDOWN, 500). +-define(SSHC_SUP(Pid), {sshc_sup, Pid, supervisor,[sshc_sup]}). +-define(SSHD_SUP(Pid), {sshd_sup, Pid, supervisor,[sshd_sup]}). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -114,23 +117,22 @@ default_tree(Config) when is_list(Config) -> lists:keysearch(sshc_sup, 1, TopSupChildren), {value, {sshd_sup, _,supervisor,[sshd_sup]}} = lists:keysearch(sshd_sup, 1, TopSupChildren), - ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)), - ?wait_match([{daemon_controller,_,worker,_}], supervisor:which_children(sshd_sup)). + ?wait_match([], supervisor:which_children(sshc_sup)), + ?wait_match([], supervisor:which_children(sshd_sup)). %%------------------------------------------------------------------------- sshc_subtree(Config) when is_list(Config) -> {_Pid, Host, Port} = proplists:get_value(server, Config), UserDir = proplists:get_value(userdir, Config), - ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)), + ?wait_match([], supervisor:which_children(sshc_sup)), {ok, Pid1} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, {user_interaction, false}, {user, ?USER}, {password, ?PASSWD},{user_dir, UserDir}]), - ?wait_match([{{client,ssh_system_sup, LocalIP, LocalPort, ?DEFAULT_PROFILE}, - SysSup, supervisor,[ssh_system_sup]}, - {client_controller,_,worker,_} + ?wait_match([{{ssh_system_sup, {address,LocalIP, LocalPort, ?DEFAULT_PROFILE}}, + SysSup, supervisor,[ssh_system_sup]} ], supervisor:which_children(sshc_sup), [SysSup, LocalIP, LocalPort]), @@ -140,18 +142,16 @@ sshc_subtree(Config) when is_list(Config) -> {user_interaction, false}, {user, ?USER}, {password, ?PASSWD}, {user_dir, UserDir}]), ?wait_match([{_, _,supervisor,[ssh_system_sup]}, - {_, _,supervisor,[ssh_system_sup]}, - {client_controller,_,worker,_} + {_, _,supervisor,[ssh_system_sup]} ], supervisor:which_children(sshc_sup)), ssh:close(Pid1), - ?wait_match([{_, _,supervisor,[ssh_system_sup]}, - {client_controller,_,worker,_} + ?wait_match([{_, _,supervisor,[ssh_system_sup]} ], supervisor:which_children(sshc_sup)), ssh:close(Pid2), - ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)). + ?wait_match([], supervisor:which_children(sshc_sup)). %%------------------------------------------------------------------------- sshd_subtree(Config) when is_list(Config) -> @@ -162,10 +162,10 @@ sshd_subtree(Config) when is_list(Config) -> [{?USER, ?PASSWD}]}]), ct:log("Expect HostIP=~p, Port=~p, Daemon=~p",[HostIP,Port,Daemon]), - ?wait_match([{{server,ssh_system_sup, ListenIP, Port, ?DEFAULT_PROFILE}, + ?wait_match([{{ssh_system_sup, {address,ListenIP, Port, ?DEFAULT_PROFILE}}, Daemon, supervisor, - [ssh_system_sup]}, - {daemon_controller,_,worker,_} + [ssh_system_sup] + } ], supervisor:which_children(sshd_sup), [ListenIP,Daemon]), @@ -173,7 +173,7 @@ sshd_subtree(Config) when is_list(Config) -> check_sshd_system_tree(Daemon, HostIP, Port, Config), ssh:stop_daemon(HostIP, Port), ct:sleep(?WAIT_FOR_SHUTDOWN), - ?wait_match([{daemon_controller,_,worker,_}], supervisor:which_children(sshd_sup)). + ?wait_match([], supervisor:which_children(sshd_sup)). %%------------------------------------------------------------------------- sshd_subtree_profile(Config) when is_list(Config) -> @@ -186,17 +186,18 @@ sshd_subtree_profile(Config) when is_list(Config) -> [{?USER, ?PASSWD}]}, {profile, Profile}]), ct:log("Expect HostIP=~p, Port=~p, Profile=~p, Daemon=~p",[HostIP,Port,Profile,Daemon]), - ?wait_match([{{server,ssh_system_sup, ListenIP,Port,Profile}, + ?wait_match([{{ssh_system_sup, {address,ListenIP,Port,Profile}}, Daemon, supervisor, - [ssh_system_sup]}, - {daemon_controller,_,worker,_}], + [ssh_system_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([{daemon_controller,_,worker,_}], supervisor:which_children(sshd_sup)). + ?wait_match([], supervisor:which_children(sshd_sup)). %%------------------------------------------------------------------------- killed_acceptor_restarts(Config) -> @@ -304,17 +305,27 @@ shell_channel_tree(Config) -> {user_interaction, true}, {user_dir, UserDir}]), - [ChannelSup|_] = Sups0 = chk_empty_con_daemon(Daemon), + [SubSysSup,ChPid|_] = Sups0 = chk_empty_con_daemon(Daemon), {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), ok = ssh_connection:shell(ConnectionRef,ChannelId0), success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId0, [{pty_opts,[{onlcr,1}]}]), - ?wait_match([{_, GroupPid,worker,[ssh_server_channel]}], - supervisor:which_children(ChannelSup), - [GroupPid]), + ?wait_match([{connection,_,worker,[ssh_connection_handler]}, + {_,_, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor,[ssh_channel_sup]} + ], + supervisor:which_children(SubSysSup), + [ChSup]), + ?wait_match([{_,GroupPid,worker,[ssh_server_channel]} + ], + supervisor:which_children(ChSup), + [GroupPid]), + + {links,GroupLinks} = erlang:process_info(GroupPid, links), - [ShellPid] = GroupLinks--[ChannelSup], + ct:log("GroupPid = ~p, GroupLinks = ~p Sups0 = ~p",[GroupPid,GroupLinks,Sups0]), + [ShellPid] = GroupLinks--[ChSup], ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]), receive @@ -355,110 +366,131 @@ shell_channel_tree(Config) -> chk_empty_con_daemon(Daemon) -> ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]}, - {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}], + {{ssh_acceptor_sup,_}, AccSup, supervisor,[ssh_acceptor_sup]}], supervisor:which_children(Daemon), [SubSysSup,AccSup]), - ?wait_match([{_,_, supervisor, - [ssh_tcpip_forward_acceptor_sup]}, - {{server,ssh_connection_sup, _,_}, - ConnectionSup, supervisor, - [ssh_connection_sup]}, - {{server,ssh_channel_sup,_ ,_}, - ChannelSup,supervisor, - [ssh_channel_sup]}], + ?wait_match([{connection,ServerConnPid,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor,[ssh_channel_sup]} + ], supervisor:which_children(SubSysSup), - [ConnectionSup,ChannelSup]), - ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}], - supervisor:which_children(AccSup)), - ?wait_match([{_, _, worker,[gen_statem]}], - supervisor:which_children(ConnectionSup)), - ?wait_match([], supervisor:which_children(ChannelSup)), - [ChannelSup, ConnectionSup, SubSysSup, AccSup]. + [ChSup,FwdAccSup,ServerConnPid]), + ?wait_match([], supervisor:which_children(FwdAccSup)), + ?wait_match([], supervisor:which_children(ChSup)), + ?wait_match([{{ssh_acceptor_sup,_},_,worker,[ssh_acceptor]}], + supervisor:which_children(AccSup), + []), + [SubSysSup, ChSup, ServerConnPid, AccSup, FwdAccSup]. %%------------------------------------------------------------------------- %% Help functions %%------------------------------------------------------------------------- check_sshd_system_tree(Daemon, Host, Port, Config) -> UserDir = proplists:get_value(userdir, Config), - {ok, Client} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, - {user_interaction, false}, - {user, ?USER}, - {password, ?PASSWD}, - {user_dir, UserDir}]), + {ok, ClientConn} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user_interaction, false}, + {user, ?USER}, + {password, ?PASSWD}, + {user_dir, UserDir}]), ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]}, - {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}], + {{ssh_acceptor_sup,_}, AccSup, supervisor,[ssh_acceptor_sup]}], supervisor:which_children(Daemon), [SubSysSup,AccSup]), - ?wait_match([{_, - _AcceptorSup, supervisor, - [ssh_tcpip_forward_acceptor_sup]}, - {{server,ssh_connection_sup, _,_}, - ConnectionSup, supervisor, - [ssh_connection_sup]}, - {{server,ssh_channel_sup,_ ,_}, - ChannelSup,supervisor, - [ssh_channel_sup]}], + ?wait_match([{connection,ServerConn,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,_,supervisor,[ssh_channel_sup]} + ], supervisor:which_children(SubSysSup), - [ConnectionSup,ChannelSup]), - - ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}], + [FwdAccSup,ServerConn]), + ?wait_match([], supervisor:which_children(FwdAccSup)), + + ?wait_match([{{ssh_acceptor_sup,_},_,worker,[ssh_acceptor]}], supervisor:which_children(AccSup)), - ?wait_match([{_, _, worker,[gen_statem]}], - supervisor:which_children(ConnectionSup)), - - ?wait_match([], supervisor:which_children(ChannelSup)), - {ok,PidC} = ssh_sftp:start_channel(Client), + {ok,PidC} = ssh_sftp:start_channel(ClientConn), + ?wait_match([{connection,ServerConn,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor,[ssh_channel_sup]} + ], + supervisor:which_children(SubSysSup), + [ChSup,ServerConn]), - ?wait_match([{_, PidS,worker,[ssh_server_channel]}], - supervisor:which_children(ChannelSup), + ?wait_match([{_,PidS,worker,[ssh_server_channel]}], + supervisor:which_children(ChSup), [PidS]), true = (PidS =/= PidC), + ?wait_match([], supervisor:which_children(FwdAccSup)), - ssh:close(Client). + ssh:close(ClientConn). -check_sshc_system_tree(SysSup, Connection, LocalIP, LocalPort, _Config) -> +check_sshc_system_tree(SysSup, Connection, _LocalIP, _LocalPort, _Config) -> ?wait_match([{_,SubSysSup,supervisor,[ssh_subsystem_sup]}], supervisor:which_children(SysSup), [SubSysSup]), - ?wait_match([{_,FwdAccSup, supervisor, - [ssh_tcpip_forward_acceptor_sup]}, - {{client,ssh_connection_sup, LocalIP, LocalPort}, - ConnectionSup, supervisor, - [ssh_connection_sup]}, - {{client,ssh_channel_sup, LocalIP, LocalPort}, - ChannelSup,supervisor, - [ssh_channel_sup]}], + ?wait_match([{connection,Connection,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,_,supervisor,[ssh_channel_sup]} + ], supervisor:which_children(SubSysSup), - [ConnectionSup,ChannelSup,FwdAccSup]), - ?wait_match([{_, Connection, worker,[gen_statem]}], - supervisor:which_children(ConnectionSup)), - ?wait_match([], supervisor:which_children(ChannelSup)), + [FwdAccSup]), ?wait_match([], supervisor:which_children(FwdAccSup)), {ok,ChPid1} = ssh_sftp:start_channel(Connection), - ?wait_match([{_,ChPid1,worker,[ssh_client_channel]}], - supervisor:which_children(ChannelSup)), + ?wait_match([{connection,Connection,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor, [ssh_channel_sup]} + ], + supervisor:which_children(SubSysSup), + [ChSup,FwdAccSup]), + + ?wait_match([{_,ChPid1,worker,[ssh_client_channel]} + ], + supervisor:which_children(ChSup), + [ChPid1]), {ok,ChPid2} = ssh_sftp:start_channel(Connection), - ?wait_match([{_,ChPidA,worker,[ssh_client_channel]}, - {_,ChPidB,worker,[ssh_client_channel]}], - supervisor:which_children(ChannelSup), - [ChPidA, ChPidB]), - true = (lists:sort([ChPidA, ChPidB]) == lists:sort([ChPid1, ChPid2])), + ?wait_match([{connection,Connection,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor, [ssh_channel_sup]} + ], + supervisor:which_children(SubSysSup), + [ChSup,FwdAccSup]), + + ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}, + {_,ChPid1,worker,[ssh_client_channel]} + ], + supervisor:which_children(ChSup), + [ChPid1,ChPid2]), ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid1]), exit(ChPid1, kill), - ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}], - supervisor:which_children(ChannelSup)), + ?wait_match([{connection,Connection,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor, [ssh_channel_sup]} + ], + supervisor:which_children(SubSysSup), + [ChSup,FwdAccSup]), + + ?wait_match([{_,ChPid2,worker,[ssh_client_channel]} + ], + supervisor:which_children(ChSup), + [ChPid2]), ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid2]), exit(ChPid2, kill), - ?wait_match([], supervisor:which_children(ChannelSup)), + ?wait_match([{connection,Connection,worker,[ssh_connection_handler]}, + {_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]}, + {_,ChSup,supervisor, [ssh_channel_sup]} + ], + supervisor:which_children(SubSysSup), + [ChSup,FwdAccSup]), + + ?wait_match([], supervisor:which_children(ChSup)), + ct:pal("... now there should not be any SUPERVISOR REPORT.~n", []). @@ -469,19 +501,16 @@ acceptor_pid(DaemonPid) -> Parent ! {self(), supsearch, [{AccPid,ListenAddr,Port} - || {{server,ssh_system_sup,ListenAddr,Port,NS}, + || {{ssh_system_sup,{address,ListenAddr,Port,NS}}, DPid,supervisor, [ssh_system_sup]} <- supervisor:which_children(sshd_sup), DPid == DaemonPid, - {{ssh_acceptor_sup,L1,P1,NS1}, + {{ssh_acceptor_sup,_}, AccSupPid,supervisor, [ssh_acceptor_sup]} <- supervisor:which_children(DaemonPid), - L1 == ListenAddr, - P1 == Port, - NS1 == NS1, - {{ssh_acceptor_sup,L2,P2,NS2}, + {{ssh_acceptor_sup,{address,L2,P2,NS2}}, AccPid,worker, [ssh_acceptor]} <- supervisor:which_children(AccSupPid), L2 == ListenAddr, |