summaryrefslogtreecommitdiff
path: root/src/ejabberd_ctl.erl
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2016-03-31 14:53:31 +0300
committerAlexey Shchepin <alexey@process-one.net>2016-03-31 14:53:31 +0300
commit3dc55c6d47e3093a6147ce275c7269a7d08ffc45 (patch)
tree1ff7eb63244a18f9c91dc26dd6e6845499f9f5b5 /src/ejabberd_ctl.erl
parentmix version updated for 16.03 release (diff)
Commands refactor, first pass.
- add API versionning - changed error handling, based on exception - commands moved/merged from mod_admin_p1 to mod_admin_extra - command bufixes - add some elixir unit test cases Squashed commit of the following: commit dd59855b3486f78a9349756e4f102e79b3accff8 Merge: 14e8ffc 506e08e Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 30 11:43:18 2015 +0100 Merge branch '3.2.x' into api commit 14e8ffce78cbea6c8605371d1fc50a0c1d1e012c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:35:17 2015 +0100 Added OAuth tests to ejabberd_commands commit f81c550c14628edfe4861c228576cb767924366a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:34:55 2015 +0100 Added some mod_http_api tests commit 6a64578d5b2ba532a2feb6503ed98561e56d5d53 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Oct 26 15:29:36 2015 +0100 Fix get_last command test Previous version won't work with dst. commit 27e0cde9e9c1f001effe68f8424a365ad947c068 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 17:59:34 2015 +0200 Add tests on admin command policy commit 19dad8d54f54c9fabd454280483cccfb06c8e78a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:36 2015 +0200 Added command related tests (http api & user policy) commit e0e596ab4a3f3a70aba5f374f028939ab794de33 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:16 2015 +0200 Fix command call. commit 128cd7d1ede3c47a34f8ec3a750c980ccad2c61d Merge: 60c4c4c 447313c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:48:39 2015 +0200 Merge branch '3.2.x' into api commit 60c4c4c0751302524c14219c6bc8c56a6069a689 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:45:57 2015 +0200 Fix ejabberd_commands spec. commit 8e145c28c5da762c2b93ee32327eff1db94ebfed Merge: 397273a f13dc94 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 18:26:07 2015 +0200 Merge branch '3.2.x' into api commit 397273a23ed415feac87aed33da6452229793387 Merge: c30e89b f289e27 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 15:27:45 2015 +0200 Merge branch '3.2.x' into api commit c30e89bb8a0013bff37e61e4c6953350c9c1f313 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 12:47:02 2015 +0200 Merge mod_http_api commit 7b0db22b4acd48ff6fabce41c1b2525e6580a3c5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 16 11:55:48 2015 +0200 Fix exunit tests to run with common_test suites commit d8b1a89800ac7379a57a7eb4a09c3c93c3e1e5eb Merge: 2879ae8 63455b3 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 15 11:39:45 2015 +0200 Merge branch '3.2.x' into api commit 2879ae87ff3eee369ef3d780136b96ecff5285d1 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 14 14:53:44 2015 +0200 Fix update_roster command. commit a1d453dd7a3afda9861a8d747494a45057ad574b Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 13 16:14:28 2015 +0200 API commands refactor Moving and/or merging commands from mod_admin_p1 to mod_admin_extra commit b709ed26b0fc0ca4f3bdd5a59fa58ec7e3db97fa Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 7 15:10:01 2015 +0200 Add tests on commands commit 6711687bee9c672cb3d5aed0744e13420ecf6dbd Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 29 15:58:16 2015 +0200 Add ejabberd_commands tests commit df8682f419cf3877e77e36a19bca0fc55dc991f8 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 28 14:54:39 2015 +0200 Added API versioning for ejabberdctl and rest commands commit cd017b0e3aac431bc3ee807ceb7f8641e1523ef5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Sep 18 11:21:45 2015 +0200 Better error handling of HTTP API commands. commit ca5cb6acd8e4643f9d6c484d2277b0d7e88471e5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:03:05 2015 +0200 add commands to mod_admin_extra: - get_offline_count - get_presence - change_password commit 7f583fa099e30ac2b0915669fd8f102ac565b833 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:02:16 2015 +0200 Improve REST API error handling commit 14753b1c02cdce434a786b7f80f6c09f0d210075 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 14 10:51:17 2015 +0200 Change REST API return codes for integer type.
Diffstat (limited to 'src/ejabberd_ctl.erl')
-rw-r--r--src/ejabberd_ctl.erl141
1 files changed, 84 insertions, 57 deletions
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index bf4e4675..edec5a07 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -48,7 +48,7 @@
-behaviour(ejabberd_config).
-author('alexey@process-one.net').
--export([start/0, init/0, process/1, process2/2,
+-export([start/0, init/0, process/1,
register_commands/3, unregister_commands/3,
opt_type/1]).
@@ -57,6 +57,8 @@
-include("ejabberd.hrl").
-include("logger.hrl").
+-define(DEFAULT_VERSION, 1000000).
+
%%-----------------------------
%% Module
@@ -69,7 +71,7 @@ start() ->
[SNode3 | Args3] ->
[SNode3, 60000, Args3];
_ ->
- print_usage(),
+ print_usage(?DEFAULT_VERSION),
halt(?STATUS_USAGE)
end,
SNode1 = case string:tokens(SNode, "@") of
@@ -93,6 +95,9 @@ start() ->
[Node, Reason]),
%% TODO: show minimal start help
?STATUS_BADRPC;
+ {invalid_version, V} ->
+ print("Invalid API version number: ~p~n", [V]),
+ ?STATUS_ERROR;
S ->
S
end,
@@ -126,11 +131,17 @@ unregister_commands(CmdDescs, Module, Function) ->
%% Process
%%-----------------------------
+
-spec process([string()]) -> non_neg_integer().
+process(Args) ->
+ process(Args, ?DEFAULT_VERSION).
+
+
+-spec process([string()], non_neg_integer()) -> non_neg_integer().
%% The commands status, stop and restart are defined here to ensure
%% they are usable even if ejabberd is completely stopped.
-process(["status"]) ->
+process(["status"], _Version) ->
{InternalStatus, ProvidedStatus} = init:get_status(),
print("The node ~p is ~p with status: ~p~n",
[node(), InternalStatus, ProvidedStatus]),
@@ -146,24 +157,24 @@ process(["status"]) ->
?STATUS_SUCCESS
end;
-process(["stop"]) ->
+process(["stop"], _Version) ->
%%ejabberd_cover:stop(),
init:stop(),
?STATUS_SUCCESS;
-process(["restart"]) ->
+process(["restart"], _Version) ->
init:restart(),
?STATUS_SUCCESS;
-process(["mnesia"]) ->
+process(["mnesia"], _Version) ->
print("~p~n", [mnesia:system_info(all)]),
?STATUS_SUCCESS;
-process(["mnesia", "info"]) ->
+process(["mnesia", "info"], _Version) ->
mnesia:info(),
?STATUS_SUCCESS;
-process(["mnesia", Arg]) ->
+process(["mnesia", Arg], _Version) ->
case catch mnesia:system_info(list_to_atom(Arg)) of
{'EXIT', Error} -> print("Error: ~p~n", [Error]);
Return -> print("~p~n", [Return])
@@ -172,23 +183,23 @@ process(["mnesia", Arg]) ->
%% The arguments --long and --dual are not documented because they are
%% automatically selected depending in the number of columns of the shell
-process(["help" | Mode]) ->
+process(["help" | Mode], Version) ->
{MaxC, ShCode} = get_shell_info(),
case Mode of
[] ->
- print_usage(dual, MaxC, ShCode),
+ print_usage(dual, MaxC, ShCode, Version),
?STATUS_USAGE;
["--dual"] ->
- print_usage(dual, MaxC, ShCode),
+ print_usage(dual, MaxC, ShCode, Version),
?STATUS_USAGE;
["--long"] ->
- print_usage(long, MaxC, ShCode),
+ print_usage(long, MaxC, ShCode, Version),
?STATUS_USAGE;
["--tags"] ->
- print_usage_tags(MaxC, ShCode),
+ print_usage_tags(MaxC, ShCode, Version),
?STATUS_SUCCESS;
["--tags", Tag] ->
- print_usage_tags(Tag, MaxC, ShCode),
+ print_usage_tags(Tag, MaxC, ShCode, Version),
?STATUS_SUCCESS;
["help"] ->
print_usage_help(MaxC, ShCode),
@@ -196,13 +207,22 @@ process(["help" | Mode]) ->
[CmdString | _] ->
CmdStringU = ejabberd_regexp:greplace(
list_to_binary(CmdString), <<"-">>, <<"_">>),
- print_usage_commands(binary_to_list(CmdStringU), MaxC, ShCode),
+ print_usage_commands2(binary_to_list(CmdStringU), MaxC, ShCode, Version),
?STATUS_SUCCESS
end;
-process(Args) ->
+process(["--version", Arg | Args], _) ->
+ Version =
+ try
+ list_to_integer(Arg)
+ catch _:_ ->
+ throw({invalid_version, Arg})
+ end,
+ process(Args, Version);
+
+process(Args, Version) ->
AccessCommands = get_accesscommands(),
- {String, Code} = process2(Args, AccessCommands),
+ {String, Code} = process2(Args, AccessCommands, Version),
case String of
[] -> ok;
_ ->
@@ -211,18 +231,21 @@ process(Args) ->
Code.
%% @spec (Args::[string()], AccessCommands) -> {String::string(), Code::integer()}
-process2(["--auth", User, Server, Pass | Args], AccessCommands) ->
- process2(Args, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass), true}, AccessCommands);
-process2(Args, AccessCommands) ->
- process2(Args, noauth, AccessCommands).
+process2(["--auth", User, Server, Pass | Args], AccessCommands, Version) ->
+ process2(Args, AccessCommands, {list_to_binary(User), list_to_binary(Server),
+ list_to_binary(Pass), true}, Version);
+process2(Args, AccessCommands, Version) ->
+ process2(Args, AccessCommands, admin, Version).
+
+
-process2(Args, Auth, AccessCommands) ->
- case try_run_ctp(Args, Auth, AccessCommands) of
+process2(Args, AccessCommands, Auth, Version) ->
+ case try_run_ctp(Args, Auth, AccessCommands, Version) of
{String, wrong_command_arguments}
when is_list(String) ->
io:format(lists:flatten(["\n" | String]++["\n"])),
[CommandString | _] = Args,
- process(["help" | [CommandString]]),
+ process(["help" | [CommandString]], Version),
{lists:flatten(String), ?STATUS_ERROR};
{String, Code}
when is_list(String) and is_integer(Code) ->
@@ -246,29 +269,29 @@ get_accesscommands() ->
%%-----------------------------
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
-try_run_ctp(Args, Auth, AccessCommands) ->
+try_run_ctp(Args, Auth, AccessCommands, Version) ->
try ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
false when Args /= [] ->
- try_call_command(Args, Auth, AccessCommands);
+ try_call_command(Args, Auth, AccessCommands, Version);
false ->
- print_usage(),
+ print_usage(Version),
{"", ?STATUS_USAGE};
Status ->
{"", Status}
catch
exit:Why ->
- print_usage(),
+ print_usage(Version),
{io_lib:format("Error in ejabberd ctl process: ~p", [Why]), ?STATUS_USAGE};
Error:Why ->
%% In this case probably ejabberd is not started, so let's show Status
- process(["status"]),
+ process(["status"], Version),
print("~n", []),
{io_lib:format("Error in ejabberd ctl process: '~p' ~p", [Error, Why]), ?STATUS_USAGE}
end.
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
-try_call_command(Args, Auth, AccessCommands) ->
- try call_command(Args, Auth, AccessCommands) of
+try_call_command(Args, Auth, AccessCommands, Version) ->
+ try call_command(Args, Auth, AccessCommands, Version) of
{error, command_unknown} ->
{io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR};
{error, wrong_command_arguments} ->
@@ -276,24 +299,28 @@ try_call_command(Args, Auth, AccessCommands) ->
Res ->
Res
catch
+ throw:Error ->
+ {io_lib:format("~p", [Error]), ?STATUS_ERROR};
A:Why ->
Stack = erlang:get_stacktrace(),
{io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR}
end.
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, ErrorType}
-call_command([CmdString | Args], Auth, AccessCommands) ->
+call_command([CmdString | Args], Auth, AccessCommands, Version) ->
CmdStringU = ejabberd_regexp:greplace(
list_to_binary(CmdString), <<"-">>, <<"_">>),
Command = list_to_atom(binary_to_list(CmdStringU)),
- case ejabberd_commands:get_command_format(Command, Auth) of
+ case ejabberd_commands:get_command_format(Command, Auth, Version) of
{error, command_unknown} ->
{error, command_unknown};
{ArgsFormat, ResultFormat} ->
case (catch format_args(Args, ArgsFormat)) of
ArgsFormatted when is_list(ArgsFormatted) ->
- Result = ejabberd_commands:execute_command(AccessCommands, Auth, Command,
- ArgsFormatted),
+ Result = ejabberd_commands:execute_command(AccessCommands,
+ Auth, Command,
+ ArgsFormatted,
+ Version),
format_result(Result, ResultFormat);
{'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} ->
{NumCompa, TextCompa} =
@@ -404,8 +431,8 @@ make_status(ok) -> ?STATUS_SUCCESS;
make_status(true) -> ?STATUS_SUCCESS;
make_status(_Error) -> ?STATUS_ERROR.
-get_list_commands() ->
- try ejabberd_commands:list_commands() of
+get_list_commands(Version) ->
+ try ejabberd_commands:list_commands(Version) of
Commands ->
[tuple_command_help(Command)
|| {N,_,_}=Command <- Commands,
@@ -458,10 +485,10 @@ get_list_ctls() ->
-define(U2, "\e[24m").
-define(U(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end).
-print_usage() ->
+print_usage(Version) ->
{MaxC, ShCode} = get_shell_info(),
- print_usage(dual, MaxC, ShCode).
-print_usage(HelpMode, MaxC, ShCode) ->
+ print_usage(dual, MaxC, ShCode, Version).
+print_usage(HelpMode, MaxC, ShCode, Version) ->
AllCommands =
[
{"status", [], "Get ejabberd status"},
@@ -469,11 +496,11 @@ print_usage(HelpMode, MaxC, ShCode) ->
{"restart", [], "Restart ejabberd"},
{"help", ["[--tags [tag] | com?*]"], "Show help (try: ejabberdctl help help)"},
{"mnesia", ["[info]"], "show information of Mnesia system"}] ++
- get_list_commands() ++
+ get_list_commands(Version) ++
get_list_ctls(),
print(
- ["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--auth ",
+ ["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--version ", ?U("api_version"), "] [--auth ",
?U("user"), " ", ?U("host"), " ", ?U("password"), "] ",
?U("command"), " [", ?U("options"), "]\n"
"\n"
@@ -598,9 +625,9 @@ format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) ->
%% Print Tags
%%-----------------------------
-print_usage_tags(MaxC, ShCode) ->
+print_usage_tags(MaxC, ShCode, Version) ->
print("Available tags and commands:", []),
- TagsCommands = ejabberd_commands:get_tags_commands(),
+ TagsCommands = ejabberd_commands:get_tags_commands(Version),
lists:foreach(
fun({Tag, Commands} = _TagCommands) ->
print(["\n\n ", ?B(Tag), "\n "], []),
@@ -611,10 +638,10 @@ print_usage_tags(MaxC, ShCode) ->
TagsCommands),
print("\n\n", []).
-print_usage_tags(Tag, MaxC, ShCode) ->
+print_usage_tags(Tag, MaxC, ShCode, Version) ->
print(["Available commands with tag ", ?B(Tag), ":", "\n"], []),
HelpMode = long,
- TagsCommands = ejabberd_commands:get_tags_commands(),
+ TagsCommands = ejabberd_commands:get_tags_commands(Version),
CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of
{value, {Tag, CNs}} -> CNs;
false -> []
@@ -622,7 +649,7 @@ print_usage_tags(Tag, MaxC, ShCode) ->
CommandsList = lists:map(
fun(NameString) ->
C = ejabberd_commands:get_command_definition(
- list_to_atom(NameString)),
+ list_to_atom(NameString), Version),
#ejabberd_commands{name = Name,
args = Args,
desc = Desc} = C,
@@ -673,20 +700,20 @@ print_usage_help(MaxC, ShCode) ->
%%-----------------------------
%% @spec (CmdSubString::string(), MaxC::integer(), ShCode::boolean()) -> ok
-print_usage_commands(CmdSubString, MaxC, ShCode) ->
+print_usage_commands2(CmdSubString, MaxC, ShCode, Version) ->
%% Get which command names match this substring
- AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands()],
+ AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)],
Cmds = filter_commands(AllCommandsNames, CmdSubString),
case Cmds of
- [] -> io:format("Error: not command found that match: ~p~n", [CmdSubString]);
- _ -> print_usage_commands2(lists:sort(Cmds), MaxC, ShCode)
+ [] -> io:format("Error: no command found that match: ~p~n", [CmdSubString]);
+ _ -> print_usage_commands3(lists:sort(Cmds), MaxC, ShCode, Version)
end.
-print_usage_commands2(Cmds, MaxC, ShCode) ->
+print_usage_commands3(Cmds, MaxC, ShCode, Version) ->
%% Then for each one print it
lists:mapfoldl(
fun(Cmd, Remaining) ->
- print_usage_command(Cmd, MaxC, ShCode),
+ print_usage_command(Cmd, MaxC, ShCode, Version),
case Remaining > 1 of
true -> print([" ", lists:duplicate(MaxC, 126), " \n"], []);
false -> ok
@@ -716,16 +743,16 @@ filter_commands_regexp(All, Glob) ->
All).
%% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok
-print_usage_command(Cmd, MaxC, ShCode) ->
+print_usage_command(Cmd, MaxC, ShCode, Version) ->
Name = list_to_atom(Cmd),
- case ejabberd_commands:get_command_definition(Name) of
+ case ejabberd_commands:get_command_definition(Name, Version) of
command_not_found ->
io:format("Error: command ~p not known.~n", [Cmd]);
C ->
- print_usage_command(Cmd, C, MaxC, ShCode)
+ print_usage_command2(Cmd, C, MaxC, ShCode)
end.
-print_usage_command(Cmd, C, MaxC, ShCode) ->
+print_usage_command2(Cmd, C, MaxC, ShCode) ->
#ejabberd_commands{
tags = TagsAtoms,
desc = Desc,