aboutsummaryrefslogblamecommitdiffstats
path: root/priv/templates/nodetool
blob: 9e24f32398b4f4143da6b5b9577ac936784a97b3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                      








                                                                      
                       


                                                                                





                                                                                  
                                                                          



                    
                   
                                                                                     








                                                                                




                                                                                   
                                                                              



                            
                                                        
                                                                                   
                                                                         
                                   
                                                                              

                            
                                              
                




                                                                              
                                            






















                                                                              
                
                                              
                                                                                                     


                      


                                                                          
                                                    



                                                                               
                                            

                                                                 
                                             


                                                             
 








                                                                 
                   
                                                     

                



                                                         
                  



                                                
                                                        









                              
                                                          


                               
                                                                                       
                                                         


                                   
                                                          
                       
                                                                               


























                                                                              









                                                               
#!/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) ->
    %% 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,

    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(
                        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, 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, "@", [{return, list}, unicode]) 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) ->
    case re:split(Name, "@", [{return, list}, unicode]) 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.

%% 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]).