#!/usr/bin/env escript %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% nodetool: Helper Script for interacting with live nodes %% %% ------------------------------------------------------------------- main(Args) -> ok = start_epmd(), %% Extract the args {RestArgs, TargetNode} = process_args(Args, [], undefined), %% See if the node is currently running -- if it's not, we'll bail case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of {true, pong} -> ok; {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of ["ping"] -> %% If we got this far, the node already responsed to a ping, so just dump %% a "pong" io:format("pong\n"); ["stop"] -> io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]); ["restart"] -> io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]); ["reboot"] -> io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); ["rpc", Module, Function | RpcArgs] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), [RpcArgs], 60000) of ok -> ok; {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); _ -> halt(1) end; ["rpcterms", Module, Function | ArgsAsString] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), consult(lists:flatten(ArgsAsString)), 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); Other -> io:format("~p\n", [Other]) end; ["eval" | ListOfArgs] -> % shells may process args into more than one, and end up stripping % spaces, so this converts all of that to a single string to parse String = binary_to_list( list_to_binary( string:join(ListOfArgs," ") ) ), % then just as a convenience to users, if they forgot a trailing % '.' add it for them. Normalized = case lists:reverse(String) of [$. | _] -> String; R -> lists:reverse([$. | R]) end, % then scan and parse the string {ok, Scanned, _} = erl_scan:string(Normalized), {ok, Parsed } = erl_parse:parse_exprs(Scanned), % and evaluate it on the remote node case rpc:call(TargetNode, erl_eval, exprs, [Parsed, [] ]) of {value, Value, _} -> io:format ("~p\n",[Value]); {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1) end; Other -> io:format("Other: ~p\n", [Other]), io:format("Usage: nodetool {ping|stop|restart|reboot|rpc|rpcterms|eval [Terms]} [RPC]\n") end, net_kernel:stop(). process_args([], Acc, TargetNode) -> {lists:reverse(Acc), TargetNode}; process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) -> erlang:set_cookie(node(), list_to_atom(Cookie)), process_args(Rest, Acc, TargetNode); process_args(["-name", TargetName | Rest], Acc, _) -> ThisNode = append_node_suffix(TargetName, "_maint_"), {ok, _} = net_kernel:start([ThisNode, longnames]), process_args(Rest, Acc, nodename(TargetName)); process_args(["-sname", TargetName | Rest], Acc, _) -> ThisNode = append_node_suffix(TargetName, "_maint_"), {ok, _} = net_kernel:start([ThisNode, shortnames]), process_args(Rest, Acc, nodename(TargetName)); process_args([Arg | Rest], Acc, Opts) -> process_args(Rest, [Arg | Acc], Opts). start_epmd() -> [] = os:cmd("\"" ++ epmd_path() ++ "\" -daemon"), ok. epmd_path() -> ErtsBinDir = filename:dirname(escript:script_name()), Name = "epmd", case os:find_executable(Name, ErtsBinDir) of false -> case os:find_executable(Name) of false -> io:format("Could not find epmd.~n"), halt(1); GlobalEpmd -> GlobalEpmd end; Epmd -> Epmd end. nodename(Name) -> case string:tokens(Name, "@") of [_Node, _Host] -> list_to_atom(Name); [Node] -> [_, Host] = string:tokens(atom_to_list(node()), "@"), list_to_atom(lists:concat([Node, "@", Host])) end. append_node_suffix(Name, Suffix) -> case string:tokens(Name, "@") of [Node, Host] -> list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host])); [Node] -> list_to_atom(lists:concat([Node, Suffix, os:getpid()])) end. %% %% Given a string or binary, parse it into a list of terms, ala file:consult/0 %% consult(Str) when is_list(Str) -> consult([], Str, []); consult(Bin) when is_binary(Bin)-> consult([], binary_to_list(Bin), []). consult(Cont, Str, Acc) -> case erl_scan:tokens(Cont, Str, 0) of {done, Result, Remaining} -> case Result of {ok, Tokens, _} -> {ok, Term} = erl_parse:parse_term(Tokens), consult([], Remaining, [Term | Acc]); {eof, _Other} -> lists:reverse(Acc); {error, Info, _} -> {error, Info} end; {more, Cont1} -> consult(Cont1, eof, Acc) end.