Packages
relx
4.8.0
4.10.0
4.9.0
4.8.0
4.7.0
4.6.0
4.5.0
4.4.0
4.3.0
4.2.0
4.1.0
4.0.2
4.0.1
4.0.0
4.0.0-rc2
3.33.0
3.32.1
3.32.0
3.31.0
3.30.0
3.29.0
3.28.0
3.27.0
3.26.0
3.25.0
3.24.4
3.24.3
3.24.2
3.24.1
3.23.1
3.23.0
3.22.4
3.22.3
retired
3.22.2
3.22.1
retired
3.22.0
3.21.1
3.21.0
3.20.0
3.19.0
3.18.0
3.17.0
3.16.0
3.15.0
3.14.0
3.13.0
3.12.0
3.11.0
3.10.0
3.9.0
3.8.0
3.7.1
3.7.0
3.6.0
3.5.0
3.4.0
3.3.2
3.3.1
3.3.0
3.2.0
3.1.0
3.0.0
2.1.1
2.1.0
Release assembler for Erlang/OTP Releases
Current section
Files
Jump to
Current section
Files
priv/templates/nodetool
#!/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
%%
%% On releases running with OTP-23+ this escript is not used. Instead,
%% the `erl_call' command is used. This escript will be removed from
%% relx when OTP-23 becomes the earliest support version of OTP.
%%
%% -------------------------------------------------------------------
main(Args) ->
%% Extract the args
{RestArgs, TargetNode, StartEpmd} = process_args(Args, [], undefined, true),
ok = start_epmd(StartEpmd),
%% 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,
Timeout =
case os:getenv("RELX_RPC_TIMEOUT") of
false ->
60000;
StrVal ->
try
%% RELX_RPC_TIMEOUT is in seconds
list_to_integer(StrVal) * 1000
catch _:_ ->
60000
end
end,
case RestArgs of
["rpc", Module, Function | ArgsAsString] ->
case consult(lists:flatten(ArgsAsString)) of
{error, Reason} ->
io:format("Error parsing arguments `~s`, failed with reason: ~s~n", [ArgsAsString, Reason]);
{ok, ParsedArgs} when is_list(ParsedArgs) ->
case rpc:call(TargetNode, list_to_atom(Module),
list_to_atom(Function), ParsedArgs, Timeout) of
{badrpc, Reason} ->
io:format("RPC to ~p failed: ~p~n", [TargetNode, Reason]),
halt(1);
rpc_ok ->
%% code doesn't want any output
ok;
{rpc_error, Code} when is_integer(Code) ->
%% code failed, halt with a supplied code
halt(Code);
{rpc_error, Code, Output} when is_integer(Code) ->
%% code failed, halt with a supplied code
%% and log output to stderr
io:format(standard_error, "~p~n", [Output]),
halt(Code);
Other ->
io:format("~p~n", [Other])
end;
{ok, ParsedArgs} ->
io:format("Error: args `~p` not a list.~n"
"Example to call `foo:bar(1,2,3)` pass `foo bar \"[1, 2, 3].\"`~n", [ParsedArgs])
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(
join(ListOfArgs," ")
)
),
% then scan and parse the string
{ok, Scanned, _} = erl_scan:string(String),
{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, StartEpmd) ->
{lists:reverse(Acc), TargetNode, StartEpmd};
process_args(["-setcookie", Cookie | Rest], Acc, TargetNode, StartEpmd) ->
erlang:set_cookie(node(), list_to_atom(Cookie)),
process_args(Rest, Acc, TargetNode, StartEpmd);
process_args(["-start_epmd", StartEpmd | Rest], Acc, TargetNode, _StartEpmd) ->
process_args(Rest, Acc, TargetNode, list_to_atom(StartEpmd));
process_args(["-name", TargetName | Rest], Acc, _, StartEpmd) ->
maybe_start_node(TargetName, longnames),
process_args(Rest, Acc, nodename(TargetName), StartEpmd);
process_args(["-sname", TargetName | Rest], Acc, _, StartEpmd) ->
maybe_start_node(TargetName, shortnames),
process_args(Rest, Acc, nodename(TargetName), StartEpmd);
process_args([Arg | Rest], Acc, Opts, StartEpmd) ->
process_args(Rest, [Arg | Acc], Opts, StartEpmd).
maybe_start_node(TargetName, Names) ->
case erlang:node() of
'nonode@nohost' ->
ThisNode = append_node_suffix(TargetName, "_maint_"),
{ok, _} = net_kernel:start([ThisNode, Names]);
_ ->
ok
end.
start_epmd(true) ->
[] = os:cmd("\"" ++ epmd_path() ++ "\" -daemon"),
ok;
start_epmd(_) ->
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 re:split(Name, "@", [unicode, {return, list}]) of
[_Node, _Host] ->
list_to_atom(Name);
[Node] ->
[_, Host] = re:split(atom_to_list(node()), "@", [{return, list}, unicode]),
list_to_atom(lists:concat([Node, "@", Host]))
end.
append_node_suffix(Name, Suffix) ->
rand:seed(exsss, os:timestamp()),
case re:split(Name, "@", [{return, list}, unicode]) of
[Node, Host] ->
list_to_atom(lists:concat([Node, Suffix, rand:uniform(1000), "@", Host]));
[Node] ->
list_to_atom(lists:concat([Node, Suffix, rand:uniform(1000)]))
end.
%% convert string to erlang term
consult([]) ->
{ok, []};
consult(Bin) when is_binary(Bin)->
consult(binary_to_list(Bin));
consult(Str) when is_list(Str) ->
%% add '.' if the string doesn't end in one
Normalized =
case lists:reverse(Str) of
[$. | _] -> Str;
R -> lists:reverse([$. | R])
end,
case erl_scan:string(Normalized) of
{ok, Tokens, _} ->
case erl_parse:parse_term(Tokens) of
{ok, Args} ->
{ok, Args};
{error, {_, _, ErrorDescriptor}} ->
{error, erl_parse:format_error(ErrorDescriptor)}
end;
{error, {_, _, ErrorDescriptor}} ->
{error, erl_scan:format_error(ErrorDescriptor)}
end.
%% string:join/2 copy; string:join/2 is getting obsoleted
%% and replaced by lists:join/2, but lists:join/2 is too new
%% for version support (only appeared in 19.0) so it cannot be
%% used. Instead we just adopt join/2 locally and hope it works
%% for most unicode use cases anyway.
join([], Sep) when is_list(Sep) ->
[];
join([H|T], Sep) ->
H ++ lists:append([Sep ++ X || X <- T]).