diff options
Diffstat (limited to 'lib/inets/src/inets_app/inets.erl')
-rw-r--r-- | lib/inets/src/inets_app/inets.erl | 740 |
1 files changed, 740 insertions, 0 deletions
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl new file mode 100644 index 0000000000..7977a3dc2a --- /dev/null +++ b/lib/inets/src/inets_app/inets.erl @@ -0,0 +1,740 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-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% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: The main interface module of the inets application +%%---------------------------------------------------------------------- + +-module(inets). + +%% API +-export([start/0, start/1, start/2, start/3, + stop/0, stop/2, + services/0, services_info/0, + service_names/0]). +-export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, + report_event/4]). +-export([versions/0, + print_version_info/0, print_version_info/1]). + + +%%==================================================================== +%% API +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: start([, Type]) -> ok +%% +%% Type = permanent | transient | temporary +%% +%% Description: Starts the inets application. Default type +%% is temporary. see application(3) +%%-------------------------------------------------------------------- +start() -> + application:start(inets). + +start(Type) -> + application:start(inets, Type). + + +%%-------------------------------------------------------------------- +%% Function: start(Service, ServiceConfig [, How]) -> {ok, Pid} | +%% {error, Reason} +%% +%% Service = - ftpc | tftpd | httpc | httpd +%% ServiceConfig = ConfPropList | ConfFile +%% ConfPropList = [{Property, Value}] according to service +%% ConfFile = Path - when service is httpd +%% How = inets | stand_alone +%% +%% Description: Dynamically starts an inets service after the inets +%% application has been started. +%% +%% Note: Dynamically started services will not be handled by +%% application takeover and failover behavior when inets is run as a +%% distributed application. Nor will they be automaticly restarted +%% when the inets application is restarted, but as long as the inets +%% application is up and running they will be supervised and may be +%% soft code upgraded. Services started with the option stand alone, +%% e.i. the service is not started as part of the inets application, +%% will lose all OTP application benefits such as soft upgrade. The +%% stand alone service will be linked to the process that started it. +%% In most cases some of the supervison functionallity will still be +%% in place and in some sense the calling process has now become the +%% top supervisor. +%% -------------------------------------------------------------------- +start(Service, ServiceConfig) -> + Module = service_module(Service), + start_service(Module, ServiceConfig, inets). + +start(Service, ServiceConfig, How) -> + Module = service_module(Service), + start_service(Module, ServiceConfig, How). + +%%-------------------------------------------------------------------- +%% Function: stop() -> ok +%% +%% Description: Stops the inets application. +%%-------------------------------------------------------------------- +stop() -> + application:stop(inets). + + +%%-------------------------------------------------------------------- +%% Function: stop(Service, Pid) -> ok +%% +%% Service - ftp | tftpd | http | httpd | stand_alone +%% +%% Description: Stops a started service of the inets application or takes +%% down a stand alone "service" gracefully. +%%-------------------------------------------------------------------- +stop(stand_alone, Pid) -> + true = exit(Pid, shutdown), + ok; + +stop(Service, Pid) -> + Module = service_module(Service), + call_service(Module, stop_service, Pid). + + +%%-------------------------------------------------------------------- +%% Function: services() -> [{Service, Pid}] +%% +%% Description: Returns a list of currently running services. +%% Note: Services started with the stand alone option will not be listed +%%-------------------------------------------------------------------- +services() -> + Modules = [service_module(Service) || Service <- + service_names()], + try lists:flatten(lists:map(fun(Module) -> + Module:services() + end, Modules)) of + Result -> + Result + catch + exit:{noproc, _} -> + {error, inets_not_started} + end. + + +%%-------------------------------------------------------------------- +%% Function: services_info() -> [{Service, Pid, Info}] +%% +%% Description: Returns a list of currently running services where +%% each service is described by a [{Property, Value}] list. +%%-------------------------------------------------------------------- +services_info() -> + case services() of + {error, inets_not_started} -> + {error, inets_not_started}; + Services -> + Fun = fun({Service, Pid}) -> + Module = service_module(Service), + Info = + case Module:service_info(Pid) of + {ok, PropList} -> + PropList; + {error, Reason} -> + Reason + end, + {Service, Pid, Info} + end, + lists:flatten(lists:map(Fun, Services)) + end. + + + +%%-------------------------------------------------------------------- +%% Function: print_version_info() +%% +%% Description: Simple utility function to print information +%% about versions (system, OS and modules). +%%-------------------------------------------------------------------- + +print_version_info() -> + {ok, Versions} = inets:versions(), + print_version_info(Versions). + +print_version_info(Versions) when is_list(Versions) -> + print_sys_info(Versions), + print_os_info(Versions), + print_mods_info(Versions). + +print_sys_info(Versions) -> + case key1search(sys_info, Versions) of + {value, SysInfo} when is_list(SysInfo) -> + {value, Arch} = key1search(arch, SysInfo, "Not found"), + {value, Ver} = key1search(ver, SysInfo, "Not found"), + io:format("System info: " + "~n Arch: ~s" + "~n Ver: ~s" + "~n", [Arch, Ver]), + ok; + _ -> + io:format("System info: Not found~n", []), + not_found + end. + +print_os_info(Versions) -> + case key1search(os_info, Versions) of + {value, OsInfo} when is_list(OsInfo) -> + Fam = + case key1search(fam, OsInfo, "Not found") of + {value, F} when is_atom(F) -> + atom_to_list(F); + {value, LF} when is_list(LF) -> + LF; + {value, XF} -> + lists:flatten(io_lib:format("~p", [XF])) + end, + Name = + case key1search(name, OsInfo) of + {value, N} when is_atom(N) -> + "[" ++ atom_to_list(N) ++ "]"; + {value, LN} when is_list(LN) -> + "[" ++ LN ++ "]"; + not_found -> + "" + end, + Ver = + case key1search(ver, OsInfo, "Not found") of + {value, T} when is_tuple(T) -> + tversion(T); + {value, LV} when is_list(LV) -> + LV; + {value, XV} -> + lists:flatten(io_lib:format("~p", [XV])) + end, + io:format("OS info: " + "~n Family: ~s ~s" + "~n Ver: ~s" + "~n", [Fam, Name, Ver]), + ok; + _ -> + io:format("OS info: Not found~n", []), + not_found + end. + +versions() -> + App = inets, + LibDir = code:lib_dir(App), + File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]), + case file:consult(File) of + {ok, [{application, App, AppFile}]} -> + case lists:keysearch(modules, 1, AppFile) of + {value, {modules, Mods}} -> + {ok, version_info(Mods)}; + _ -> + {error, {invalid_format, modules}} + end; + Error -> + {error, {invalid_format, Error}} + end. + +version_info(Mods) -> + SysInfo = sys_info(), + OsInfo = os_info(), + ModInfo = [mod_version_info(Mod) || Mod <- Mods], + [{sys_info, SysInfo}, {os_info, OsInfo}, {mod_info, ModInfo}]. + +mod_version_info(Mod) -> + Info = Mod:module_info(), + {value, {attributes, Attr}} = lists:keysearch(attributes, 1, Info), + {value, {vsn, [Vsn]}} = lists:keysearch(vsn, 1, Attr), + {value, {app_vsn, AppVsn}} = lists:keysearch(app_vsn, 1, Attr), + {value, {compile, Comp}} = lists:keysearch(compile, 1, Info), + {value, {version, Ver}} = lists:keysearch(version, 1, Comp), + {value, {time, Time}} = lists:keysearch(time, 1, Comp), + {Mod, [{vsn, Vsn}, + {app_vsn, AppVsn}, + {compiler_version, Ver}, + {compile_time, Time}]}. + +sys_info() -> + SysArch = string:strip(erlang:system_info(system_architecture),right,$\n), + SysVer = string:strip(erlang:system_info(system_version),right,$\n), + [{arch, SysArch}, {ver, SysVer}]. + +os_info() -> + V = os:version(), + case os:type() of + {OsFam, OsName} -> + [{fam, OsFam}, {name, OsName}, {ver, V}]; + OsFam -> + [{fam, OsFam}, {ver, V}] + end. + + +print_mods_info(Versions) -> + case key1search(mod_info, Versions) of + {value, ModsInfo} when is_list(ModsInfo) -> + io:format("Module info: ~n", []), + lists:foreach(fun print_mod_info/1, ModsInfo); + _ -> + io:format("Module info: Not found~n", []), + not_found + end. + +tversion(T) -> + L = tuple_to_list(T), + lversion(L). + +lversion([]) -> + ""; +lversion([A]) -> + integer_to_list(A); +lversion([A|R]) -> + integer_to_list(A) ++ "." ++ lversion(R). + +print_mod_info({Module, Info}) -> + % Maybe a asn1 generated module + Asn1Vsn = + case (catch Module:info()) of + AI when is_list(AI) -> + case (catch key1search(vsn, AI)) of + {value, V} when is_atom(V) -> + atom_to_list(V); + _ -> + "-" + end; + _ -> + "-" + end, + Vsn = + case key1search(vsn, Info) of + {value, I} when is_integer(I) -> + integer_to_list(I); + _ -> + "Not found" + end, + AppVsn = + case key1search(app_vsn, Info) of + {value, S1} when is_list(S1) -> + S1; + _ -> + "Not found" + end, + CompVer = + case key1search(compiler_version, Info) of + {value, S2} when is_list(S2) -> + S2; + _ -> + "Not found" + end, + CompDate = + case key1search(compile_time, Info) of + {value, {Year, Month, Day, Hour, Min, Sec}} -> + lists:flatten( + io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Min, Sec])); + _ -> + "Not found" + end, + io:format(" ~w:~n" + " Vsn: ~s~n" + " App vsn: ~s~n" + " ASN.1 vsn: ~s~n" + " Compiler ver: ~s~n" + " Compile time: ~s~n", + [Module, Vsn, AppVsn, Asn1Vsn, CompVer, CompDate]), + ok. + + +key1search(Key, Vals) -> + case lists:keysearch(Key, 1, Vals) of + {value, {Key, Val}} -> + {value, Val}; + false -> + not_found + end. + +key1search(Key, Vals, Def) -> + case key1search(Key, Vals) of + not_found -> + {value, Def}; + Value -> + Value + end. + + +%%-------------------------------------------------------------------- +%% Function: service_names() -> [ServiceName] +%% +%% ServiceName = atom() +%% +%% Description: Returns a list of supported services +%%------------------------------------------------------------------- +service_names() -> + [ftpc, tftpd, httpc, httpd]. + + +%%----------------------------------------------------------------- +%% enable_trace(Level, Destination) -> void() +%% enable_trace(Level, Destination, Service) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% Destination -> File | Port | io | HandlerSpec +%% Service -> httpc | httpd | ftpc | tftp | all +%% File -> string() +%% Port -> integer() +%% Verbosity -> true | false +%% HandlerSpec = {function(), Data} +%% Data = term() +%% +%% Description: +%% This function is used to start tracing at level Level and send +%% the result either to the file File, the port Port or to a +%% trace handler. +%% Note that it starts a tracer server. +%% When Destination is the atom io (or the tuple {io, Verbosity}), +%% all (printable) inets trace events (trace_ts events which has +%% Severity withing Limit) will be written to stdout using io:format. +%% +%%----------------------------------------------------------------- +enable_trace(Level, Dest) -> + enable_trace(Level, Dest, all). + +enable_trace(Level, Dest, Service) -> + case valid_trace_service(Service) of + true -> + enable_trace2(Level, Dest, Service); + false -> + {error, {invalid_service, Service}} + end. + +enable_trace2(Level, File, Service) + when is_list(File) -> + case file:open(File, [write]) of + {ok, Fd} -> + HandleSpec = {fun handle_trace/2, {Service, Fd}}, + do_enable_trace(Level, process, HandleSpec); + Err -> + Err + end; +enable_trace2(Level, Port, _) when is_integer(Port) -> + do_enable_trace(Level, port, dbg:trace_port(ip, Port)); +enable_trace2(Level, io, Service) -> + HandleSpec = {fun handle_trace/2, {Service, standard_io}}, + do_enable_trace(Level, process, HandleSpec); +enable_trace2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> + do_enable_trace(Level, process, HandleSpec). + +do_enable_trace(Level, Type, HandleSpec) -> + case dbg:tracer(Type, HandleSpec) of + {ok, _} -> + set_trace(Level), + ok; + Error -> + Error + end. + +valid_trace_service(all) -> + true; +valid_trace_service(Service) -> + lists:member(Service, [httpc, httpd, ftpc, tftp]). + + +%%----------------------------------------------------------------- +%% disable_trace() -> void() +%% +%% Description: +%% This function is used to stop tracing. +%%----------------------------------------------------------------- +disable_trace() -> + %% This is to make handle_trace/2 close the output file (if the + %% event gets there before dbg closes) + inets:report_event(100, "stop trace", stop_trace, [stop_trace]), + dbg:stop(). + + + +%%----------------------------------------------------------------- +%% set_trace(Level) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% +%% Description: +%% This function is used to change the trace level when tracing has +%% already been started. +%%----------------------------------------------------------------- +set_trace(Level) -> + set_trace(Level, all). + +set_trace(Level, Service) -> + Pat = make_pattern(?MODULE, Service, Level), + change_pattern(Pat). + +make_pattern(Mod, Service, Level) + when is_atom(Mod) andalso is_atom(Service) -> + case Level of + min -> + {Mod, Service, []}; + max -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [], + {Mod, Service, [{Head, Cond, Body}]}; + DetailLevel when is_integer(DetailLevel) -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [{ '=<', '$1', DetailLevel}], + {Mod, Service, [{Head, Cond, Body}]}; + _ -> + exit({bad_level, Level}) + end. + +change_pattern({Mod, Service, Pattern}) + when is_atom(Mod) andalso is_atom(Service) -> + MFA = {Mod, report_event, 4}, + case Pattern of + [] -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(p, dbg:p(all, clear)) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end; + List when is_list(List) -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(tp, dbg:tp(MFA, Pattern)), + error_to_exit(p, dbg:p(all, [call, timestamp])) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end; + _ -> + exit({bad_pattern, Pattern}) + end, + ok. + +error_to_exit(_Where, {ok, _} = OK) -> + OK; +error_to_exit(Where, {error, Reason}) -> + exit({Where, Reason}). + + +%%----------------------------------------------------------------- +%% report_event(Serverity, Label, Service, Content) +%% +%% Parameters: +%% Severity -> 0 =< integer() =< 100 +%% Label -> string() +%% Service -> httpd | httpc | ftp | tftp +%% Content -> [{tag, term()}] +%% +%% Description: +%% This function is used to generate trace events, that is, +%% put trace on this function. +%%----------------------------------------------------------------- + +report_event(Severity, Label, Service, Content) + when (is_integer(Severity) andalso + (Severity >= 0) andalso (100 >= Severity)) andalso + is_list(Label) andalso + is_atom(Service) andalso + is_list(Content) -> + hopefully_traced. + + +%% ---------------------------------------------------------------------- +%% handle_trace(Event, Fd) -> Verbosity +%% +%% Parameters: +%% Event -> The trace event (only megaco 'trace_ts' events are printed) +%% Fd -> standard_io | file_descriptor() | trace_port() +%% +%% Description: +%% This function is used to "receive" and print the trace events. +%% Events are printed if: +%% - Verbosity is max +%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40) +%% Events are not printed if: +%% - Verbosity is min +%% - Severity is > Verbosity +%%----------------------------------------------------------------- + +handle_trace(_, closed_file = Fd) -> + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {standard_io, _} = Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + standard_io = Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_Service, Fd}) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + (catch file:close(Fd)), + closed_file; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + (catch file:close(Fd)), + closed_file; +handle_trace({trace_ts, Who, call, + {?MODULE, report_event, + [Sev, Label, Service, Content]}, Timestamp}, + Fd) -> + (catch print_inets_trace(Fd, Sev, Timestamp, Who, + Label, Service, Content)), + Fd; +handle_trace(Event, Fd) -> + (catch print_trace(Fd, Event)), + Fd. + + +print_inets_trace({Service, Fd}, + Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); +print_inets_trace({ServiceA, Fd}, + Sev, Timestamp, Who, Label, ServiceB, Content) + when (ServiceA =:= all) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); +print_inets_trace({ServiceA, _Fd}, + _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) + when ServiceA =/= ServiceB -> + ok; +print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). + +do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + Ts = format_timestamp(Timestamp), + io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " + "~n Content: ~p" + "~n", + [Service, Sev, Who, Ts, Label, Content]). + +print_trace({_, Fd}, Event) -> + do_print_trace(Fd, Event); +print_trace(Fd, Event) -> + do_print_trace(Fd, Event). + +do_print_trace(Fd, {trace, Who, What, Where}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Who, What, Where]); + +do_print_trace(Fd, {trace, Who, What, Where, Extra}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n Extra: ~p" + "~n", [Who, What, Where, Extra]); + +do_print_trace(Fd, {trace_ts, Who, What, Where, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[trace ~s]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Ts, Who, What, Where]); + +do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[trace ~s]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n Extra: ~p" + "~n", [Ts, Who, What, Where, Extra]); + +do_print_trace(Fd, {seq_trace, What, Where}) -> + io:format(Fd, "[seq trace]" + "~n What: ~p" + "~n Where: ~p" + "~n", [What, Where]); + +do_print_trace(Fd, {seq_trace, What, Where, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[seq trace ~s]" + "~n What: ~p" + "~n Where: ~p" + "~n", [Ts, What, Where]); + +do_print_trace(Fd, {drop, Num}) -> + io:format(Fd, "[drop trace] ~p~n", [Num]); + +do_print_trace(Fd, Trace) -> + io:format(Fd, "[trace] " + "~n ~p" + "~n", [Trace]). + + +format_timestamp({_N1, _N2, N3} = Now) -> + {Date, Time} = calendar:now_to_datetime(Now), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", + [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + lists:flatten(FormatDate). + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +start_service(Service, Args, stand_alone) -> + Service:start_standalone(Args); +start_service(Service, Args, inets) -> + call_service(Service, start_service, Args). + +call_service(Service, Call, Args) -> + try Service:Call(Args) of + Result -> + Result + catch + exit:{noproc, _} -> + {error, inets_not_started} + end. + +service_module(tftpd) -> + tftp; +service_module(httpc) -> + http; +service_module(ftpc) -> + ftp; +service_module(Service) -> + Service. + + + + + + |