|
|
@@ -18,11 +18,12 @@
|
|
|
|
|
|
-behaviour(gen_server).
|
|
|
|
|
|
+-include("types.hrl").
|
|
|
-include("logger.hrl").
|
|
|
|
|
|
-logger_header("[Ctl]").
|
|
|
|
|
|
--export([start_link/0]).
|
|
|
+-export([start_link/0, stop/0]).
|
|
|
|
|
|
-export([ register_command/2
|
|
|
, register_command/3
|
|
|
@@ -32,6 +33,7 @@
|
|
|
-export([ run_command/1
|
|
|
, run_command/2
|
|
|
, lookup_command/1
|
|
|
+ , get_commands/0
|
|
|
]).
|
|
|
|
|
|
-export([ print/1
|
|
|
@@ -40,7 +42,7 @@
|
|
|
, usage/2
|
|
|
]).
|
|
|
|
|
|
-%% format/1,2 and format_usage/1,2 are exported mainly for test cases
|
|
|
+%% Exports mainly for test cases
|
|
|
-export([ format/1
|
|
|
, format/2
|
|
|
, format_usage/1
|
|
|
@@ -63,34 +65,39 @@
|
|
|
-type(cmd_usage() :: {cmd(), cmd_descr()}).
|
|
|
|
|
|
-define(SERVER, ?MODULE).
|
|
|
--define(TAB, emqx_command).
|
|
|
+-define(CMD_TAB, emqx_command).
|
|
|
|
|
|
+-spec(start_link() -> startlink_ret()).
|
|
|
start_link() ->
|
|
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
|
|
|
|
|
+-spec(stop() -> ok).
|
|
|
+stop() -> gen_server:stop(?SERVER).
|
|
|
+
|
|
|
-spec(register_command(cmd(), {module(), atom()}) -> ok).
|
|
|
register_command(Cmd, MF) when is_atom(Cmd) ->
|
|
|
register_command(Cmd, MF, []).
|
|
|
|
|
|
-spec(register_command(cmd(), {module(), atom()}, list()) -> ok).
|
|
|
register_command(Cmd, MF, Opts) when is_atom(Cmd) ->
|
|
|
- cast({register_command, Cmd, MF, Opts}).
|
|
|
+ call({register_command, Cmd, MF, Opts}).
|
|
|
|
|
|
-spec(unregister_command(cmd()) -> ok).
|
|
|
unregister_command(Cmd) when is_atom(Cmd) ->
|
|
|
cast({unregister_command, Cmd}).
|
|
|
|
|
|
-cast(Msg) ->
|
|
|
- gen_server:cast(?SERVER, Msg).
|
|
|
+call(Req) -> gen_server:call(?SERVER, Req).
|
|
|
+
|
|
|
+cast(Msg) -> gen_server:cast(?SERVER, Msg).
|
|
|
|
|
|
+-spec(run_command(list(string())) -> ok | {error, term()}).
|
|
|
run_command([]) ->
|
|
|
run_command(help, []);
|
|
|
run_command([Cmd | Args]) ->
|
|
|
run_command(list_to_atom(Cmd), Args).
|
|
|
|
|
|
--spec(run_command(cmd(), [string()]) -> ok | {error, term()}).
|
|
|
-run_command(help, []) ->
|
|
|
- help();
|
|
|
+-spec(run_command(cmd(), list(string())) -> ok | {error, term()}).
|
|
|
+run_command(help, []) -> help();
|
|
|
run_command(Cmd, Args) when is_atom(Cmd) ->
|
|
|
case lookup_command(Cmd) of
|
|
|
[{Mod, Fun}] ->
|
|
|
@@ -107,15 +114,19 @@ run_command(Cmd, Args) when is_atom(Cmd) ->
|
|
|
|
|
|
-spec(lookup_command(cmd()) -> [{module(), atom()}]).
|
|
|
lookup_command(Cmd) when is_atom(Cmd) ->
|
|
|
- case ets:match(?TAB, {{'_', Cmd}, '$1', '_'}) of
|
|
|
+ case ets:match(?CMD_TAB, {{'_', Cmd}, '$1', '_'}) of
|
|
|
[El] -> El;
|
|
|
[] -> []
|
|
|
end.
|
|
|
|
|
|
+-spec(get_commands() -> list({cmd(), module(), atom()})).
|
|
|
+get_commands() ->
|
|
|
+ [{Cmd, M, F} || {{_Seq, Cmd}, {M, F}, _Opts} <- ets:tab2list(?CMD_TAB)].
|
|
|
+
|
|
|
help() ->
|
|
|
print("Usage: ~s~n", [?MODULE]),
|
|
|
[begin print("~80..-s~n", [""]), Mod:Cmd(usage) end
|
|
|
- || {_, {Mod, Cmd}, _} <- ets:tab2list(?TAB)].
|
|
|
+ || {_, {Mod, Cmd}, _} <- ets:tab2list(?CMD_TAB)].
|
|
|
|
|
|
-spec(print(io:format()) -> ok).
|
|
|
print(Msg) ->
|
|
|
@@ -152,34 +163,33 @@ format_usage(UsageList) ->
|
|
|
format_usage(Cmd, Desc) ->
|
|
|
CmdLines = split_cmd(Cmd),
|
|
|
DescLines = split_cmd(Desc),
|
|
|
- lists:foldl(
|
|
|
- fun({CmdStr, DescStr}, Usage) ->
|
|
|
- Usage ++ format("~-48s# ~s~n", [CmdStr, DescStr])
|
|
|
- end, "", zip_cmd(CmdLines, DescLines)).
|
|
|
+ lists:foldl(fun({CmdStr, DescStr}, Usage) ->
|
|
|
+ Usage ++ format("~-48s# ~s~n", [CmdStr, DescStr])
|
|
|
+ end, "", zip_cmd(CmdLines, DescLines)).
|
|
|
|
|
|
-%%------------------------------------------------------------------------------
|
|
|
+%%--------------------------------------------------------------------
|
|
|
%% gen_server callbacks
|
|
|
-%%------------------------------------------------------------------------------
|
|
|
+%%--------------------------------------------------------------------
|
|
|
|
|
|
init([]) ->
|
|
|
- ok = emqx_tables:new(?TAB, [protected, ordered_set]),
|
|
|
+ ok = emqx_tables:new(?CMD_TAB, [protected, ordered_set]),
|
|
|
{ok, #state{seq = 0}}.
|
|
|
|
|
|
-handle_call(Req, _From, State) ->
|
|
|
- ?LOG(error, "Unexpected call: ~p", [Req]),
|
|
|
- {reply, ignored, State}.
|
|
|
-
|
|
|
-handle_cast({register_command, Cmd, MF, Opts}, State = #state{seq = Seq}) ->
|
|
|
- case ets:match(?TAB, {{'$1', Cmd}, '_', '_'}) of
|
|
|
- [] -> ets:insert(?TAB, {{Seq, Cmd}, MF, Opts});
|
|
|
+handle_call({register_command, Cmd, MF, Opts}, _From, State = #state{seq = Seq}) ->
|
|
|
+ case ets:match(?CMD_TAB, {{'$1', Cmd}, '_', '_'}) of
|
|
|
+ [] -> ets:insert(?CMD_TAB, {{Seq, Cmd}, MF, Opts});
|
|
|
[[OriginSeq] | _] ->
|
|
|
?LOG(warning, "CMD ~s is overidden by ~p", [Cmd, MF]),
|
|
|
- ets:insert(?TAB, {{OriginSeq, Cmd}, MF, Opts})
|
|
|
+ true = ets:insert(?CMD_TAB, {{OriginSeq, Cmd}, MF, Opts})
|
|
|
end,
|
|
|
- noreply(next_seq(State));
|
|
|
+ {reply, ok, next_seq(State)};
|
|
|
+
|
|
|
+handle_call(Req, _From, State) ->
|
|
|
+ ?LOG(error, "Unexpected call: ~p", [Req]),
|
|
|
+ {reply, ignored, State}.
|
|
|
|
|
|
handle_cast({unregister_command, Cmd}, State) ->
|
|
|
- ets:match_delete(?TAB, {{'_', Cmd}, '_', '_'}),
|
|
|
+ ets:match_delete(?CMD_TAB, {{'_', Cmd}, '_', '_'}),
|
|
|
noreply(State);
|
|
|
|
|
|
handle_cast(Msg, State) ->
|
|
|
@@ -214,3 +224,4 @@ zip_cmd([X | Xs], [Y | Ys]) -> [{X, Y} | zip_cmd(Xs, Ys)];
|
|
|
zip_cmd([X | Xs], []) -> [{X, ""} | zip_cmd(Xs, [])];
|
|
|
zip_cmd([], [Y | Ys]) -> [{"", Y} | zip_cmd([], Ys)];
|
|
|
zip_cmd([], []) -> [].
|
|
|
+
|