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