1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: The ssh server instance supervisor, an instans of this supervisor 24%% exists for every ip-address and port combination, hangs under 25%% sshd_sup. 26%%---------------------------------------------------------------------- 27 28-module(ssh_system_sup). 29 30-behaviour(supervisor). 31 32-include("ssh.hrl"). 33 34-export([start_link/3, 35 stop_listener/1, 36 stop_system/2, 37 start_system/3, 38 start_subsystem/4, 39 get_daemon_listen_address/1, 40 addresses/1, 41 addresses/2, 42 get_options/2 43 ]). 44 45%% Supervisor callback 46-export([init/1]). 47 48%%%========================================================================= 49%%% API 50%%%========================================================================= 51 52start_system(Role, Address0, Options) -> 53 case find_system_sup(Role, Address0) of 54 {ok,{SysPid,Address}} -> 55 restart_acceptor(SysPid, Address, Options); 56 {error,not_found} -> 57 supervisor:start_child(sup(Role), 58 #{id => {?MODULE,Address0}, 59 start => {?MODULE, start_link, [Role, Address0, Options]}, 60 restart => temporary, 61 type => supervisor 62 }) 63 end. 64 65%%%---------------------------------------------------------------- 66stop_system(Role, SysSup) when is_pid(SysSup) -> 67 case lists:keyfind(SysSup, 2, supervisor:which_children(sup(Role))) of 68 {{?MODULE,Name}, SysSup, _, _} -> stop_system(Role, Name); 69 false -> undefind 70 end; 71stop_system(Role, Name) -> 72 supervisor:terminate_child(sup(Role), {?MODULE,Name}). 73 74 75%%%---------------------------------------------------------------- 76stop_listener(SystemSup) when is_pid(SystemSup) -> 77 {Name, _, _, _} = lookup(ssh_acceptor_sup, SystemSup), 78 supervisor:terminate_child(SystemSup, Name), 79 supervisor:delete_child(SystemSup, Name). 80 81%%%---------------------------------------------------------------- 82get_daemon_listen_address(SystemSup) -> 83 try lookup(ssh_acceptor_sup, SystemSup) 84 of 85 {{ssh_acceptor_sup,Address}, _, _, _} -> 86 {ok, Address}; 87 _ -> 88 {error, not_found} 89 catch 90 _:_ -> 91 {error, not_found} 92 end. 93 94%%%---------------------------------------------------------------- 95%%% Start the subsystem child. It is a child of the system supervisor (callback = this module) 96start_subsystem(Role, Address=#address{}, Socket, Options0) -> 97 Options = ?PUT_INTERNAL_OPT([{user_pid, self()}], Options0), 98 Id = make_ref(), 99 case get_system_sup(Role, Address, Options) of 100 {ok,SysPid} -> 101 case supervisor:start_child(SysPid, 102 #{id => Id, 103 start => {ssh_subsystem_sup, start_link, 104 [Role,Address,Id,Socket,Options] 105 }, 106 restart => temporary, 107 significant => true, 108 type => supervisor 109 }) 110 of 111 {ok,_SubSysPid} -> 112 try 113 receive 114 {new_connection_ref, Id, ConnPid} -> 115 ssh_connection_handler:takeover(ConnPid, Role, Socket, Options) 116 after 10000 -> 117 118 error(timeout) 119 end 120 catch 121 error:{badmatch,{error,Error}} -> 122 {error,Error}; 123 error:timeout -> 124 %% The connection was started, but the takover procedure timed out, 125 %% therefor it exists a subtree, but it is not quite ready and 126 %% must be removed (by the supervisor above): 127 supervisor:terminate_child(SysPid, Id), 128 {error, connection_start_timeout} 129 end; 130 Others -> 131 Others 132 end; 133 Others -> 134 Others 135 end. 136 137 138%%%---------------------------------------------------------------- 139start_link(Role, Address, Options) -> 140 supervisor:start_link(?MODULE, [Role, Address, Options]). 141 142 143%%%---------------------------------------------------------------- 144addresses(Role) -> 145 addresses(Role, #address{address=any, port=any, profile=any}). 146 147addresses(Role, #address{address=Address, port=Port, profile=Profile}) -> 148 [{SysSup,A} || {{ssh_system_sup,A},SysSup,supervisor,_} <- 149 supervisor:which_children(sup(Role)), 150 Address == any orelse A#address.address == Address, 151 Port == any orelse A#address.port == Port, 152 Profile == any orelse A#address.profile == Profile]. 153 154%%%========================================================================= 155%%% Supervisor callback 156%%%========================================================================= 157init([Role, Address, Options]) -> 158 SupFlags = #{strategy => one_for_one, 159 auto_shutdown => all_significant, 160 intensity => 0, 161 period => 3600 162 }, 163 ChildSpecs = 164 case {Role, is_socket_server(Options)} of 165 {server, false} -> 166 [acceptor_sup_child_spec(_SysSup=self(), Address, Options)]; 167 _ -> 168 [] 169 end, 170 {ok, {SupFlags,ChildSpecs}}. 171 172%%%========================================================================= 173%%% Service API 174%%%========================================================================= 175 176%% A macro to keep get_options/2 and acceptor_sup_child_spec/3 synchronized 177-define(accsup_start(SysSup,Addr,Opts), 178 {ssh_acceptor_sup, start_link, [SysSup,Addr,Opts]} 179 ). 180 181get_options(Sup, Address = #address{}) -> 182 %% Lookup the Option parameter in the running ssh_acceptor_sup: 183 try 184 {ok, #{start:=?accsup_start(_, _, Options)}} = 185 supervisor:get_childspec(Sup, {ssh_acceptor_sup,Address}), 186 {ok, Options} 187 catch 188 _:_ -> {error,not_found} 189 end. 190 191%%%========================================================================= 192%%% Internal functions 193%%%========================================================================= 194 195%% A separate function because this spec is need in >1 places 196acceptor_sup_child_spec(SysSup, Address, Options) -> 197 #{id => {ssh_acceptor_sup,Address}, 198 start => ?accsup_start(SysSup, Address, Options), 199 restart => transient, 200 significant => true, 201 type => supervisor 202 }. 203 204lookup(SupModule, SystemSup) -> 205 lists:keyfind([SupModule], 4, 206 supervisor:which_children(SystemSup)). 207 208get_system_sup(Role, Address0, Options) -> 209 case find_system_sup(Role, Address0) of 210 {ok,{SysPid,_Address}} -> 211 {ok,SysPid}; 212 {error,not_found} -> 213 start_system(Role, Address0, Options); 214 {error,Error} -> 215 {error,Error} 216 end. 217 218find_system_sup(Role, Address0) -> 219 case addresses(Role, Address0) of 220 [{SysSupPid,Address}] -> {ok,{SysSupPid,Address}}; 221 [] -> {error,not_found}; 222 [_,_|_] -> {error,ambigous} 223 end. 224 225sup(client) -> sshc_sup; 226sup(server) -> sshd_sup. 227 228 229is_socket_server(Options) -> 230 undefined =/= ?GET_INTERNAL_OPT(connected_socket,Options,undefined). 231 232restart_acceptor(SysPid, Address, Options) -> 233 case lookup(ssh_acceptor_sup, SysPid) of 234 {_,_,supervisor,_} -> 235 {error, eaddrinuse}; 236 false -> 237 ChildSpec = acceptor_sup_child_spec(SysPid, Address, Options), 238 case supervisor:start_child(SysPid, ChildSpec) of 239 {ok,_ChildPid} -> 240 {ok,SysPid}; % sic! 241 {ok,_ChildPid,_Info} -> 242 {ok,SysPid}; % sic! 243 {error,Error} -> 244 {error,Error} 245 end 246 end. 247