-module(observer_cli_escriptize). -include("observer_cli.hrl"). -export([main/1]). %% for rpc -export([ensure_set_env/2]). -ifdef(TEST). -export([ required_modules/1, resolve_target_name/1, random_local_node_name/0, application_included/1, application_modules/1, applications/2, all_applications/1, run/3, run/4, remote_load/1 ]). -endif. %% @doc escript main -spec main(list()) -> 'ok'. main([TargetNode]) -> run(TargetNode, undefined, 1500); main([TargetNode, Cookie, Interval]) -> CookieAtom = list_to_atom(Cookie), IntervalInt = list_to_integer(Interval), run(TargetNode, CookieAtom, IntervalInt); main(_Options) -> io:format("Usage: observer_cli TARGETNODE [TARGETCOOKIE REFRESHMS]~n"). run(TargetNode, Cookie, Interval) -> run(TargetNode, Cookie, Interval, fun remote_load/1). run(TargetNode, Cookie, Interval, RemoteLoadFun) -> {TargetNodeAtom, NameOpt} = resolve_target_name(TargetNode), LocalNode = random_local_node_name(), MyName = case NameOpt of shortnames -> list_to_atom(LocalNode); longnames -> list_to_atom(LocalNode ++ "@127.0.0.1") end, case net_kernel:start([MyName, NameOpt]) of {ok, _} -> ok; {error, {already_started, _}} -> ensure_net_kernel_name_mode(NameOpt); {error, Reason} -> erlang:error({net_kernel_start_failed, Reason}) end, Start = fun() -> Options = [{cookie, Cookie}, {interval, Interval}], observer_cli:start(TargetNodeAtom, Options) end, {badrpc, _} = Start(), RemoteLoadFun(TargetNodeAtom), maybe_wait_remote_stop(TargetNodeAtom), io:format("~p~n", [Start()]). remote_load(Node) when Node =:= node() -> ok; remote_load(Node) -> do_remote_load(Node). do_remote_load(Node) -> application:load(observer_cli), Formatter = application:get_env(observer_cli, formatter, ?DEFAULT_FORMATTER), FormatterApp = maps:get(application, Formatter), Apps = lists:usort([observer_cli, FormatterApp]), [recon:remote_load([Node], Mod) || Mod <- required_modules(Apps)], erpc:call(Node, ?MODULE, ensure_set_env, [ observer_cli, application:get_all_env(observer_cli) ]), ok. random_local_node_name() -> {_, {H, M, S}} = calendar:local_time(), lists:flatten(io_lib:format("observer_cli_~2.2.0p_~2.2.0p_~2.2.0p", [H, M, S])). resolve_target_name(TargetNode) -> case string:tokens(TargetNode, "@") of [_Name, Host] -> Node = list_to_atom(TargetNode), case string:tokens(Host, ".") of [Host] -> {Node, shortnames}; [_ | _] -> {Node, longnames} end; [Name] -> %% only a name without host given, assume shortname {ok, Host} = inet:gethostname(), {list_to_atom(Name ++ "@" ++ Host), shortnames} end. ensure_net_kernel_name_mode(ExpectedMode) -> ActualMode = case net_kernel:longnames() of true -> longnames; false -> shortnames end, case ExpectedMode =:= ActualMode of true -> ok; false -> Hint = "use -name for longnames, -sname for shortnames", erlang:error( {net_kernel_start_failed, {name_mode_mismatch, ExpectedMode, ActualMode, Hint}} ) end. %%%=================================================================== %%% application %%%=================================================================== required_modules(AppList) -> required_modules(AppList, sets:new()). required_modules([], Res) -> sets:to_list(Res); required_modules(AppList, Res) -> [H | T] = AppList, required_modules( T ++ all_applications(H), lists:foldl(fun sets:add_element/2, Res, application_modules(H)) ). all_applications(App) -> observer_cli_lib:pipe([], [ fun(ApplicationsAcc) -> applications(ApplicationsAcc, App) end, fun(ApplicationsAcc) -> ApplicationsAcc ++ application_included(App) end, fun(ApplicationsAcc) -> ApplicationsAcc -- [kernel, stdlib] end ]). -spec ensure_set_env(App :: atom(), Env :: [{atom(), term()}]) -> ok | {error, term()}. ensure_set_env(App, Env) -> Result = case application:get_all_env(App) of [] -> application:set_env([{App, Env}]); _EnvLoaded -> ok end, maybe_stop_remote(App), Result. -ifdef(TEST). maybe_stop_remote(App) -> case application:get_env(App, test_stop_remote, false) of true -> spawn(fun init:stop/0), ok; false -> ok end. maybe_wait_remote_stop(Node) -> case application:get_env(observer_cli, test_stop_remote, false) of true -> wait_for_nodedown(Node); false -> ok end. wait_for_nodedown(Node) -> net_kernel:monitor_nodes(true), try case lists:member(Node, nodes()) of false -> ok; true -> receive {nodedown, Node} -> ok; {nodedown, Node, _Reason} -> ok after 5000 -> erlang:error({remote_node_stop_timeout, Node}) end end after net_kernel:monitor_nodes(false) end. -else. maybe_stop_remote(_App) -> ok. maybe_wait_remote_stop(_Node) -> ok. -endif. application_included(Application) -> case application:get_key(Application, included_applications) of {ok, Apps} -> Apps; _ -> [] end. application_modules(Application) -> case application:get_key(Application, modules) of {ok, Modules} -> Modules; _ -> [] end. applications(ApplicationsAcc, App) -> case application:get_key(App, applications) of {ok, Applications} -> ApplicationsAcc ++ Applications; undefined -> ApplicationsAcc end.