%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2006-2018. 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 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 | tftpc | tftp | 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 - ftpc | ftp | tftpd | tftpc | tftp | httpc | 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(),
{OsFam, OsName} = os:type(),
[{fam, OsFam}, {name, OsName}, {ver, V}].
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, tftp, 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) -> inets_trace:enable(Level, Dest).
enable_trace(Level, Dest, Service) -> inets_trace:enable(Level, Dest, Service).
%%-----------------------------------------------------------------
%% disable_trace() -> void()
%%
%% Description:
%% This function is used to stop tracing.
%%-----------------------------------------------------------------
disable_trace() -> inets_trace:disable().
%%-----------------------------------------------------------------
%% 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) -> inets_trace:set_level(Level).
%%-----------------------------------------------------------------
%% report_event(Severity, 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) ->
inets_trace:report_event(Severity, Label, Service, Content).
%%--------------------------------------------------------------------
%%% 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.
%% Obsolete! Kept for backward compatiblity!
%% TFTP application has been moved out from inets
service_module(tftpd) ->
inets_tftp_wrapper;
service_module(tftpc) ->
inets_tftp_wrapper;
service_module(tftp) ->
inets_tftp_wrapper;
%% Obsolete! Kept for backward compatiblity!
%% FTP application has been moved out from inets
service_module(ftpc) ->
inets_ftp_wrapper;
service_module(Service) ->
Service.