%% %% %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: Print some info of a running ssh aplication. %%---------------------------------------------------------------------- -module(ssh_info). -export([print/0, print/1, string/0, collect_pids/0 ]). -include("ssh_connect.hrl"). print() -> io:format("~s", [string()]). print(File) when is_list(File) -> {ok,D} = file:open(File, [write]), print(D), file:close(D); print(D) -> io:format(D, "~s", [string()]). string() -> try supervisor:which_children(ssh_sup) of _ -> [io_lib:nl(), print_general(), io_lib:nl(), underline("Client part", $=), print_clients(), io_lib:nl(), underline("Server part", $=), print_servers(), io_lib:nl(), underline("Supervisors", $=), walk_sups(ssh_sup), io_lib:nl()] catch _:_ -> io_lib:format("Ssh not found~n",[]) end. %%%================================================================ -define(INDENT, " "). print_general() -> {_Name, Slogan, Ver} = lists:keyfind(ssh,1,application:which_applications()), [underline(io_lib:format("~s ~s", [Slogan, Ver]), $=), io_lib:format('This printout is generated ~s. ~n',[datetime()]) ]. print_clients() -> try lists:map(fun print_client/1, supervisor:which_children(sshc_sup)) catch C:E -> io_lib:format('***print_clients FAILED: ~p:~p~n',[C,E]) end. print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) -> {{Local,Remote},_Str} = ssh_connection_handler:get_print_info(Pid), [io_lib:format(?INDENT"Local: ~s Remote: ~s ConnectionRef = ~p~n", [fmt_host_port(Local), fmt_host_port(Remote), Pid]), case channels(Pid) of {ok,Channels=[_|_]} -> [print_ch(ChPid) || #channel{user=ChPid} <- Channels]; _ -> io_lib:format(?INDENT?INDENT?INDENT"No channels~n",[]) end]; print_client(Other) -> io_lib:format(" [[Other 1: ~p]]~n",[Other]). %%%================================================================ print_servers() -> try lists:map(fun print_server/1, supervisor:which_children(sshd_sup)) catch C:E -> io_lib:format('***print_servers FAILED: ~p:~p~n',[C,E]) end. print_server({{server,ssh_system_sup,LocalHost,LocalPort,Profile},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) -> Children = supervisor:which_children(Pid), [io_lib:format(?INDENT"Listen: ~s (~p children) Profile ~p",[fmt_host_port({LocalHost,LocalPort}), ssh_acceptor:number_of_connections(Pid), Profile]), case [AccPid || {{ssh_acceptor_sup,_LocalHost,_LocalPort,_Profile}, AccPid, supervisor, [ssh_acceptor_sup]} <- Children] of AcceptorPids = [_|_] -> [io_lib:format(" [Acceptor Pid", []), [io_lib:format(" ~p",[AccPid]) || AccPid <- AcceptorPids], io_lib:format("]~n", []) ]; [] -> io_lib:nl() end, lists:map(fun print_system_sup/1, supervisor:which_children(Pid)) ]. print_system_sup({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref), is_pid(Pid) -> lists:map(fun print_channels/1, supervisor:which_children(Pid)); print_system_sup({{ssh_acceptor_sup,_LocalHost,_LocalPort,_Profile}, Pid, supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) -> []. print_channels({{server,ssh_server_channel_sup,_,_},Pid,supervisor,[ssh_server_channel_sup]}) when is_pid(Pid) -> Children = supervisor:which_children(Pid), ChannelPids = [P || {R,P,worker,[ssh_server_channel]} <- Children, is_pid(P), is_reference(R)], case ChannelPids of [] -> io_lib:format(?INDENT?INDENT"No channels~n",[]); [Ch1Pid|_] -> {{ConnManager,_}, _Str} = ssh_server_channel:get_print_info(Ch1Pid), {{_,Remote},_} = ssh_connection_handler:get_print_info(ConnManager), [io_lib:format(?INDENT?INDENT"Remote: ~s ConnectionRef = ~p~n",[fmt_host_port(Remote),ConnManager]), lists:map(fun print_ch/1, ChannelPids) ] end; print_channels({{server,ssh_connection_sup,_,_},Pid,supervisor,[ssh_connection_sup]}) when is_pid(Pid) -> []. % The supervisor of the connections socket owning process print_ch(Pid) -> try {{ConnManager,ChannelID}, Str} = ssh_server_channel:get_print_info(Pid), {_LocalRemote,StrM} = ssh_connection_handler:get_print_info(ConnManager), io_lib:format(?INDENT?INDENT?INDENT"ch ~p ~p: ~s ~s~n",[ChannelID, Pid, StrM, Str]) catch C:E -> io_lib:format('****print_ch FAILED for ChanPid ~p: ~p:~p~n',[Pid, C, E]) end. %%%================================================================ -define(inc(N), (N+4)). walk_sups(StartPid) -> io_lib:format("Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]), walk_sups(children(StartPid), _Indent=?inc(0)). walk_sups([H={_,Pid,_,_}|T], Indent) -> [indent(Indent), io_lib:format('~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]), case H of {_,_,supervisor,[ssh_connection_handler]} -> ""; {_,Pid,supervisor,_} -> walk_sups(children(Pid), ?inc(Indent)); _ -> "" end, walk_sups(T, Indent) ]; walk_sups([], _) -> "". dead_or_alive(Name) when is_atom(Name) -> case whereis(Name) of undefined -> "**UNDEFINED**"; Pid -> dead_or_alive(Pid) end; dead_or_alive(Pid) when is_pid(Pid) -> case process_info(Pid) of undefined -> "**DEAD**"; _ -> "alive" end. indent(I) -> io_lib:format('~*c',[I,$ ]). children(Pid) -> Parent = self(), Helper = spawn(fun() -> Parent ! {self(),supervisor:which_children(Pid)} end), receive {Helper,L} when is_list(L) -> L after 2000 -> catch exit(Helper, kill), [] end. is_connection_handler(Pid) -> try {ssh_connection_handler,init,_} = proplists:get_value( '$initial_call', proplists:get_value( dictionary, process_info(Pid, [dictionary]))) of _ -> true catch _:_ -> false end. channels(Pid) -> case is_connection_handler(Pid) of true -> ssh_connection_handler:info(Pid,all); false -> false end. %%%================================================================ underline(Str, LineChar) -> io_lib:format('~s~n~*c~n',[Str, lists:flatlength(Str), LineChar]). datetime() -> {{YYYY,MM,DD}, {H,M,S}} = calendar:now_to_universal_time(erlang:timestamp()), lists:flatten(io_lib:format('~4w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w UTC',[YYYY,MM,DD, H,M,S])). fmt_host_port({{A,B,C,D},Port}) -> io_lib:format('~p.~p.~p.~p:~p',[A,B,C,D,Port]); fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]). %%%################################################################ collect_pids() -> collect_pids(ssh_sup). collect_pids(P) -> Collector = pcollect_pids(P, spawn(fun init_collector/0)), Collector ! {get_values,self()}, receive {values,Values} -> Values end. %%%---------------- pcollect_pids(undefined, Collector) -> Collector; pcollect_pids(A, Collector) when is_atom(A) -> pcollect_pids(whereis(A), Collector); pcollect_pids(Pid, Collector) when is_pid(Pid) -> Collector ! {expect,Pid}, spawn(fun() -> lists:foreach( fun(P2) -> pcollect_pids(P2,Collector) end, children(Pid)), Collector ! {value,Pid,Pid} end), Collector; pcollect_pids({Ref,Pid,supervisor,_}, Collector) when is_pid(Pid), is_reference(Ref) -> pcollect_pids(Pid, Collector); pcollect_pids({sshc_sup,Pid,supervisor,_}, Collector) when is_pid(Pid) -> pcollect_pids(Pid, Collector); pcollect_pids({sshd_sup,Pid,supervisor,_}, Collector) when is_pid(Pid) -> pcollect_pids(Pid, Collector); pcollect_pids({{ssh_acceptor_sup,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) -> pcollect_pids(Pid, Collector); pcollect_pids({{server,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) -> pcollect_pids(Pid, Collector); pcollect_pids({{server,_,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) -> pcollect_pids(Pid, Collector); pcollect_pids({undefined,Pid,supervisor,[ssh_connection_handler]}, Collector) -> Collector ! {value,Pid,Pid}, case channels(Pid) of {ok,L} -> [Collector!{value,P,P} || #channel{user=P} <- L]; _ -> ok end, Collector; pcollect_pids({_,Pid,_,_}, Collector) when is_pid(Pid) -> Collector ! {value,Pid,Pid}, Collector; pcollect_pids(_, Collector) -> Collector. %%%---------------- init_collector() -> loop_collector([],[]). loop_collector(Expects, Values) -> receive {expect, Ref} -> loop_collector([Ref|Expects], Values); {value, Ref, Val} -> loop_collector(Expects--[Ref], [Val|Values]); {get_values, From} when Expects==[] -> %% Values=/=[] -> From ! {values,Values} end.