aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl/src/si_sasl_supp.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sasl/src/si_sasl_supp.erl')
-rw-r--r--lib/sasl/src/si_sasl_supp.erl373
1 files changed, 373 insertions, 0 deletions
diff --git a/lib/sasl/src/si_sasl_supp.erl b/lib/sasl/src/si_sasl_supp.erl
new file mode 100644
index 0000000000..52dbed2e00
--- /dev/null
+++ b/lib/sasl/src/si_sasl_supp.erl
@@ -0,0 +1,373 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(si_sasl_supp).
+
+-behaviour(gen_server).
+
+%%%---------------------------------------------------------------------------
+%%% Description:
+%%% This module contains the BOS specific parts of the Status Inspection Tool.
+%%%---------------------------------------------------------------------------
+
+
+%% user interface
+-export([h/0, help/0, start_log/1, stop_log/0, abbrevs/0, pi/1, pi/2, pi/3,
+ pi/4, ppi/1, ppi/3, start/0, start/1, stop/0, start_link/1]).
+
+%% intermodule exports
+-export([make_pid/1, make_pid/3, process_abbrevs/0, expand_abbrev/2,
+ status_info/1, valid_opt/1, p/1, do_best_printout/4,
+ si_exec/2, handle_call/3, terminate/2]).
+
+%% exports for use within module
+-export([init/1, start_log_impl/1, pi_impl/2, ppi_impl/1]).
+
+%% other gen_server callbacks (not used)
+-export([handle_cast/2, handle_info/2, code_change/3]).
+
+%%--------------------------------------------------
+%% Table of contents
+%% 1. Interface
+%% 2. SI - Server
+%% 3. Code
+%% 4. Selectors
+%%--------------------------------------------------
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 1. Interface
+%% -----------------------------------------------------
+
+h() -> print_help().
+help() -> print_help().
+
+si_exec(Fun, Args) -> gen_server:call(si_server, {si_exec, Fun, Args}).
+
+start_log(FileName) ->
+ gen_server:call(si_server, {start_log, FileName}).
+
+stop_log() ->
+ gen_server:call(si_server, stop_log).
+
+abbrevs() ->
+ io:format("~p", [process_abbrevs()]).
+
+%%-----------------------------------------------------------------
+%% All functions can be called with an option 'normal' or 'all';
+%% default is 'normal'.
+%%-----------------------------------------------------------------
+%% Process Info that tries to determine processtype (=Module), then
+%% it uses this Module:format_info to format data from status_info/1.
+%%-----------------------------------------------------------------
+pi(XPid) ->
+ si_exec({si_sasl_supp, pi_impl}, [normal, XPid]).
+
+pi(Opt, XPid) ->
+ si_exec({si_sasl_supp, pi_impl}, [valid_opt(Opt), XPid]).
+
+pi(A, B, C) when is_integer(A), is_integer(B), is_integer(C) ->
+ si_exec({si_sasl_supp, pi_impl}, [normal, {A, B, C}]).
+
+pi(Opt, A, B, C) when is_integer(A), is_integer(B), is_integer(C) ->
+ si_exec({si_sasl_supp, pi_impl}, [valid_opt(Opt), {A, B, C}]).
+
+%%-----------------------------------------------------------------
+%% Pretty print Process_Info.
+%%-----------------------------------------------------------------
+ppi(XPid) ->
+ case whereis(si_server) of
+ undefined -> % You can always run ppi.
+ ppi_impl(XPid); % if si_server is down, use standard_io
+ _ ->
+ si_exec({si_sasl_supp, ppi_impl}, [XPid])
+ end.
+ppi(A, B, C) ->
+ case whereis(si_server) of
+ undefined -> % You can always run ppi.
+ ppi_impl({A, B, C}); % if si_server is down, use standard_io
+ _ ->
+ si_exec({si_sasl_supp, ppi_impl}, [{A, B, C}])
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 2. SI - Server
+%%--------------------------------------------------
+-record(state, {}).
+
+start() -> start([]).
+start(Options) ->
+ supervisor:start_child(sasl_sup,
+ {si_server, {si_sasl_supp, start_link, [Options]},
+ temporary, brutal_kill, worker, [si_sasl_supp]}).
+
+start_link(_Options) ->
+ gen_server:start_link({local, si_server}, si_sasl_supp, [], []).
+
+stop() ->
+ gen_server:call(si_server, stop),
+ supervisor:delete_child(sasl_sup, si_server).
+
+
+init(Options) ->
+ process_flag(trap_exit, true),
+ start_log_impl(get_option(Options, start_log, standard_io)),
+ {ok, #state{}}.
+
+%%-----------------------------------------------------------------
+%% If an error occurs and we're logging to file: write the error
+%% to the file.
+%% Always return the error.
+%% The only data held by the si_server is the device in its process dictionary.
+%%-----------------------------------------------------------------
+handle_call({si_exec, Fun, Args}, _From, State) ->
+ case catch apply(Fun, Args) of
+ {'EXIT', Reason} ->
+ print_error(get(device),
+ "SI internal error. Reason: ~w~n",
+ [Reason]),
+ {stop, shutdown, {internal_error, Reason}, State};
+ {error, Reason} ->
+ print_error(get(device), "~nSI error: ~w~n", [Reason]),
+ {reply, {error, Reason}, State};
+ X ->
+ {reply, X, State}
+ end;
+handle_call({start_log, FileName}, _From, State) ->
+ start_log_impl(FileName),
+ {reply, ok, State};
+handle_call(stop_log, _From, State) ->
+ start_log_impl(standard_io),
+ {reply, ok, State};
+handle_call(stop, _From, State) ->
+ start_log_impl(standard_io),
+ {stop, normal, stopped, State}.
+
+terminate(_Reason, _State) ->
+ close_device(get(device)),
+ ok.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+handle_info(_Info, State) ->
+ {noreply, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+close_device(standard_io) -> ok;
+close_device(Fd) -> file:close(Fd).
+
+print_error(standard_io, _, _) -> ok;
+print_error(Device, Format, Args) ->
+ io:format(Device, Format, Args).
+
+get_option(Options, Key, Default) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value, {_Key, Value}} -> Value;
+ _ -> Default
+ end.
+
+open_log_file(undefined, NewFile) ->
+ open_log_file(NewFile);
+open_log_file(standard_io, NewFile) ->
+ open_log_file(NewFile);
+open_log_file(OldFile, NewFile) ->
+ file:close(OldFile),
+ open_log_file(NewFile).
+
+open_log_file(standard_io) -> standard_io;
+open_log_file(FileName) ->
+ case file:open(FileName, [write]) of
+ {ok, Fd} -> Fd;
+ Error ->
+ io:format("si_sasl_supp: Cannot open file '~s' (~w).~n",
+ [FileName, Error]),
+ io:format("si_sasl_supp: Using standard_io~n"),
+ standard_io
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 3. Code
+%%--------------------------------------------------
+
+%%--------------------------------------------------
+%% Makes a Pid of almost anything.
+%% Returns: Pid|{error, Reason}
+%% Fails: Never.
+%%--------------------------------------------------
+make_pid(A,B,C) when is_integer(A), is_integer(B), is_integer(C) ->
+ list_to_pid(lists:concat(["<",A,".",B,".",C,">"])).
+make_pid(P) when is_pid(P) -> P;
+make_pid(undefined) -> undefined;
+make_pid(P) when is_atom(P) ->
+ case whereis(P) of
+ undefined ->
+ case expand_abbrev(P, process_abbrevs()) of
+ {error, Reason} -> {error, Reason};
+ {value, {_Abbrev, FullName}} ->
+ case whereis(FullName) of
+ undefined ->
+ {error, {'process not registered', P}};
+ Pid -> Pid
+ end
+ end;
+ Pid -> Pid
+ end;
+make_pid(P) when is_list(P) -> list_to_pid(P);
+make_pid({A, B, C}) -> make_pid(A, B, C);
+make_pid(X) -> {error, {'can not make a pid of', X}}.
+
+process_abbrevs() ->
+ [{init, init},
+ {fs, file_server}].
+
+%%--------------------------------------------------
+%% Args: Abbrevs is an assoc-list of {Abbrev, RealName}
+%% Returns: {value, {Abbrev, FullName}}|{error, Reason}
+%%--------------------------------------------------
+expand_abbrev(ProcessName, Abbrevs) ->
+ case lists:keysearch(ProcessName, 1, Abbrevs) of
+ {value, {Abbrev, FullName}} ->
+ {value, {Abbrev, FullName}};
+ _ ->
+ case lists:keysearch(ProcessName, 2, Abbrevs) of
+ {value, {Abbrev, FullName}} ->
+ {value, {Abbrev, FullName}};
+ _ ->
+ {error, {'invalid process name', ProcessName}}
+ end
+ end.
+
+%%-----------------------------------------------------------------
+%% This is the function that actually gets the information out
+%% of the agent/server/...
+%% Returns: {status_info, Pid, Type, Data}
+%% | {error, Reason}
+%%-----------------------------------------------------------------
+status_info(Pid) when is_pid(Pid) ->
+ case catch sys:get_status(Pid, 5000) of
+ {status, Pid, Type, Info} ->
+ {status_info, Pid, Type, Info};
+ _ ->
+ {error, {'process does not respond', Pid}}
+ end;
+
+status_info(X) ->
+ {error, {'not a pid', X}}.
+
+%%--------------------------------------------------
+%% Implementation starts here.
+%%--------------------------------------------------
+start_log_impl(FileName) ->
+ put(device, open_log_file(get(device), FileName)).
+
+valid_opt(all) -> all;
+valid_opt(_Opt) -> normal.
+
+
+print_help() ->
+ p("- - - - - - - - PROCESSES - - - - - - - - - "),
+ p("si_sasl_supp:pi([Opt,] Pid) - Formatted information about any process that"),
+ p(" SI recognises."),
+ p("si_sasl_supp:pi([Opt,] A,B,C) - Same as si_sasl_supp:pi({A, B, C})."),
+ p("si_sasl_supp:ppi(Pid) - Pretty formating of process_info."),
+ p(" Works for any process."),
+ p("- - - - - - - - MISC - - - - - - - - - - - "),
+ p("si_sasl_supp:abbrevs() - Lists valid abbreviations."),
+ p("si_sasl_supp:start_log(FileNname)"),
+ p("si_sasl_supp:stop_log()"),
+ p("si_sasl_supp:start() - Starts Status Inspection (the si_server)."),
+ p("si_sasl_supp:start([{start_log, FileName}])"),
+ p("si_sasl_supp:stop() - Shut down SI.").
+
+
+
+%% Convenient shorthand
+p(X) ->
+ io:format(lists:append(X, "~n")).
+
+pi_impl(Opt, XPid) ->
+ case make_pid(XPid) of
+ Pid when is_pid(Pid) ->
+ case status_info(Pid) of
+ {status_info, Pid, {module, Module}, Data} ->
+ do_best_printout(Opt, Pid, Module, Data);
+ {error, Reason} ->
+ ppi_impl(Pid),
+ {error, {"can not get status info from process:",
+ XPid,
+ Reason}}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------
+%% Is there a format_info for this process? In that case, run it!
+%% Return ok|{error, Reason}
+%% Fails: Never.
+%%--------------------------------------------------
+do_best_printout(Opt, Pid, Mod, Data) when is_pid(Pid) ->
+ case print_info(get(device), Pid, {Mod, format_status}, Opt, Data) of
+ ok -> ok;
+ {error, Reason} ->
+ ppi_impl(Pid),
+ {error, Reason}
+ end.
+
+ppi_impl(XPid) ->
+ case make_pid(XPid) of
+ P when is_pid(P) ->
+ case process_info(P) of
+ undefined ->
+ {error, {'dead process', P}};
+ PI ->
+ Device = case get(device) of
+ undefined -> standard_io;
+ X -> X
+ end,
+ io:format(Device, "~nPretty Process Info~n", []),
+ io:format(Device, "-------------------~n", []),
+ io:format(Device, "~p~n", [PI])
+ end;
+ _ -> {error, {no_pid, XPid}}
+ end.
+
+print_info(Device, Pid, {Module, Func}, Opt, Data) ->
+ case erlang:function_exported(Module, Func, 2) of
+ true ->
+ case catch apply({Module, Func}, [Opt, Data]) of
+ Format when is_list(Format) ->
+ format_lib_supp:print_info(Device, 79,
+ add_pid_to_format(Pid, Format)),
+ ok;
+ Other -> {error, {'invalid format', Other}}
+ end;
+ _ ->
+ {error, {no_such_function, Module, Func}}
+ end.
+
+add_pid_to_format(Pid, [{header, H} | T]) ->
+ [{header, H}, {data, [{"Pid", Pid}]} | T];
+add_pid_to_format(Pid, List) ->
+ [{data, [{"Pid", Pid}]} | List].
+
+