%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2008-2016. 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 server instance supervisor, an instans of this supervisor
%% exists for every ip-address and port combination, hangs under
%% sshd_sup.
%%----------------------------------------------------------------------
-module(ssh_system_sup).
-behaviour(supervisor).
-include("ssh.hrl").
-export([start_link/1, stop_listener/1,
stop_listener/3, stop_system/1,
stop_system/3, system_supervisor/3,
subsystem_supervisor/1, channel_supervisor/1,
connection_supervisor/1,
acceptor_supervisor/1, start_subsystem/2, restart_subsystem/3,
restart_acceptor/3, stop_subsystem/2]).
%% Supervisor callback
-export([init/1]).
%%%=========================================================================
%%% Internal API
%%%=========================================================================
start_link(ServerOpts) ->
Address = proplists:get_value(address, ServerOpts),
Port = proplists:get_value(port, ServerOpts),
Profile = proplists:get_value(profile, proplists:get_value(ssh_opts, ServerOpts), ?DEFAULT_PROFILE),
Name = make_name(Address, Port, Profile),
supervisor:start_link({local, Name}, ?MODULE, [ServerOpts]).
stop_listener(SysSup) ->
stop_acceptor(SysSup).
stop_listener(Address, Port, Profile) ->
Name = make_name(Address, Port, Profile),
stop_acceptor(whereis(Name)).
stop_system(SysSup) ->
Name = sshd_sup:system_name(SysSup),
spawn(fun() -> sshd_sup:stop_child(Name) end),
ok.
stop_system(Address, Port, Profile) ->
spawn(fun() -> sshd_sup:stop_child(Address, Port, Profile) end),
ok.
system_supervisor(Address, Port, Profile) ->
Name = make_name(Address, Port, Profile),
whereis(Name).
subsystem_supervisor(SystemSup) ->
ssh_subsystem_sup(supervisor:which_children(SystemSup)).
channel_supervisor(SystemSup) ->
SubSysSup = ssh_subsystem_sup(supervisor:which_children(SystemSup)),
ssh_subsystem_sup:channel_supervisor(SubSysSup).
connection_supervisor(SystemSup) ->
SubSysSup = ssh_subsystem_sup(supervisor:which_children(SystemSup)),
ssh_subsystem_sup:connection_supervisor(SubSysSup).
acceptor_supervisor(SystemSup) ->
ssh_acceptor_sup(supervisor:which_children(SystemSup)).
start_subsystem(SystemSup, Options) ->
Spec = ssh_subsystem_child_spec(Options),
supervisor:start_child(SystemSup, Spec).
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.
restart_subsystem(Address, Port, Profile) ->
SysSupName = make_name(Address, Port, Profile),
SubSysName = id(ssh_subsystem_sup, Address, Port, Profile),
case supervisor:terminate_child(SysSupName, SubSysName) of
ok ->
supervisor:restart_child(SysSupName, SubSysName);
Error ->
Error
end.
restart_acceptor(Address, Port, Profile) ->
SysSupName = make_name(Address, Port, Profile),
AcceptorName = id(ssh_acceptor_sup, Address, Port, Profile),
supervisor:restart_child(SysSupName, AcceptorName).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-spec init( [term()] ) -> {ok,{supervisor:sup_flags(),[supervisor:child_spec()]}} | ignore .
init([ServerOpts]) ->
RestartStrategy = one_for_one,
MaxR = 0,
MaxT = 3600,
Children = child_specs(ServerOpts),
{ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
child_specs(ServerOpts) ->
[ssh_acceptor_child_spec(ServerOpts)].
ssh_acceptor_child_spec(ServerOpts) ->
Address = proplists:get_value(address, ServerOpts),
Port = proplists:get_value(port, ServerOpts),
Profile = proplists:get_value(profile, proplists:get_value(ssh_opts, ServerOpts), ?DEFAULT_PROFILE),
Name = id(ssh_acceptor_sup, Address, Port, Profile),
StartFunc = {ssh_acceptor_sup, start_link, [ServerOpts]},
Restart = transient,
Shutdown = infinity,
Modules = [ssh_acceptor_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
ssh_subsystem_child_spec(ServerOpts) ->
Name = make_ref(),
StartFunc = {ssh_subsystem_sup, start_link, [ServerOpts]},
Restart = temporary,
Shutdown = infinity,
Modules = [ssh_subsystem_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
id(Sup, Address, Port, Profile) ->
case is_list(Address) of
true ->
{Sup, any, Port, Profile};
false ->
{Sup, Address, Port, Profile}
end.
make_name(Address, Port, Profile) ->
case is_list(Address) of
true ->
list_to_atom(lists:flatten(io_lib:format("ssh_system_~p_~p_~p_sup",
[any, Port, Profile])));
false ->
list_to_atom(lists:flatten(io_lib:format("ssh_system_~p_~p_~p_sup",
[Address, Port, Profile])))
end.
ssh_subsystem_sup([{_, Child, _, [ssh_subsystem_sup]} | _]) ->
Child;
ssh_subsystem_sup([_ | Rest]) ->
ssh_subsystem_sup(Rest).
ssh_acceptor_sup([{_, Child, _, [ssh_acceptor_sup]} | _]) ->
Child;
ssh_acceptor_sup([_ | Rest]) ->
ssh_acceptor_sup(Rest).
stop_acceptor(Sup) ->
[{Name, AcceptorSup}] =
[{SupName, ASup} || {SupName, ASup, _, [ssh_acceptor_sup]} <-
supervisor:which_children(Sup)],
case supervisor:terminate_child(AcceptorSup, Name) of
ok ->
supervisor:delete_child(AcceptorSup, Name);
Error ->
Error
end.