1
0
Fork 0
mirror of https://github.com/processone/ejabberd synced 2025-10-03 09:49:18 +02:00

New ejabberdctl option CTL_OVER_HTTP

This uses an HTTP connection to execute the command,
which is way faster than starting an erlang node
This commit is contained in:
Badlop 2025-01-06 19:05:58 +01:00
parent ab8a39e71f
commit 46a64c0f68
3 changed files with 96 additions and 16 deletions

View file

@ -198,6 +198,17 @@
#
#CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules
#.
#' CTL_OVER_HTTP: Path to ejabberdctl HTTP listener socket
#
# To speedup ejabberdctl execution time for ejabberd commands,
# you can setup an ejabberd_http listener with ejabberd_ctl handling requests,
# listening in a unix domain socket.
#
# Default: disabled
#
#CTL_OVER_HTTP=sockets/ctl_over_http.sock
#.
#'
# vim: foldmarker=#',#. foldmethod=marker:

View file

@ -334,6 +334,47 @@ wait_status()
[ $timeout -gt 0 ]
}
exec_other_command()
{
if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \
|| [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \
|| [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then
exec_erl "$(uid ctl)" -hidden -noinput \
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
-s ejabberd_ctl \
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
result=$?
case $result in
3) help;;
*) :;;
esac
exit $result
else
exec_ctl_over_http_socket "$@"
fi
}
exec_ctl_over_http_socket()
{
CARGS='{"ctl-command-line": "'${*}'"}'
TEMPHEADERS=temp-headers.log
curl \
--unix-socket ${CTL_OVER_HTTP} \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "${CARGS}" \
--dump-header ${TEMPHEADERS} \
--no-progress-meter \
"http://localhost/ctl/${1}"
result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS)
rm ${TEMPHEADERS}
case $result in
2|3) exec_other_command help ${1};;
*) :;;
esac
exit $result
}
# ensure we can change current directory to SPOOL_DIR
[ -d "$SPOOL_DIR" ] || exec_cmd mkdir -p "$SPOOL_DIR"
cd "$SPOOL_DIR" || {
@ -402,15 +443,6 @@ case $1 in
;;
*)
set_dist_client
exec_erl "$(uid ctl)" -hidden -noinput \
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
-s ejabberd_ctl \
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
result=$?
case $result in
2|3) help;;
*) :;;
esac
exit $result
exec_other_command "$@"
;;
esac

View file

@ -28,7 +28,7 @@
-behaviour(gen_server).
-author('alexey@process-one.net').
-export([start/0, start_link/0, process/1, process2/2]).
-export([start/0, start_link/0, process/1, process/2, process2/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@ -37,6 +37,7 @@
-include("ejabberd_ctl.hrl").
-include("ejabberd_commands.hrl").
-include("ejabberd_http.hrl").
-include("logger.hrl").
-include("ejabberd_stacktrace.hrl").
@ -115,15 +116,51 @@ code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%-----------------------------
%% Process
%% Process http
%%-----------------------------
-spec process_http([binary()], tuple()) -> {non_neg_integer(), [{binary(), binary()}], string()}.
process_http([Call], #request{data = Data} = Request) when is_binary(Call) and is_record(Request, request) ->
[{<<"ctl-command-line">>, LineBin}] = extract_args(Data),
LineStrings = string:split(binary_to_list(LineBin), " ", all),
process_http2(LineStrings, ?DEFAULT_VERSION).
process_http2(["--version", Arg | Args], _) ->
Version =
try
list_to_integer(Arg)
catch _:_ ->
throw({invalid_version, Arg})
end,
process_http2(Args, Version);
process_http2(Args, Version) ->
{String, Code} = process2(Args, [], Version),
String2 = case String of
[] -> String;
_ -> [String, "\n"]
end,
{200, [{<<"status-code">>, integer_to_binary(Code)}], String2}.
%% Be tolerant to make API more easily usable from command-line pipe.
extract_args(<<"\n">>) -> [];
extract_args(Data) ->
Maps = misc:json_decode(Data),
maps:to_list(Maps).
%%-----------------------------
%% Process command line
%%-----------------------------
-spec process([string()]) -> non_neg_integer().
process(Args) ->
process(Args, ?DEFAULT_VERSION).
-spec process([string() | binary()], non_neg_integer() | tuple()) -> non_neg_integer().
-spec process([string()], non_neg_integer()) -> non_neg_integer().
process([Call], Request) when is_binary(Call) and is_record(Request, request) ->
process_http([Call], Request);
%% The commands status, stop and restart are defined here to ensure
%% they are usable even if ejabberd is completely stopped.
@ -232,7 +269,7 @@ process2(Args, AccessCommands, Auth, Version) ->
io:format(lists:flatten(["\n" | String]++["\n"])),
[CommandString | _] = Args,
process(["help" | [CommandString]], Version),
{lists:flatten(String), ?STATUS_ERROR};
{lists:flatten(String), ?STATUS_USAGE};
{String, Code}
when is_list(String) and is_integer(Code) ->
{lists:flatten(String), Code};
@ -271,7 +308,7 @@ try_run_ctp(Args, Auth, AccessCommands, Version) ->
try_call_command(Args, Auth, AccessCommands, Version);
false ->
print_usage(Version),
{"", ?STATUS_USAGE};
{"", ?STATUS_BADRPC};
Status ->
{"", Status}
catch
@ -288,7 +325,7 @@ try_run_ctp(Args, Auth, AccessCommands, Version) ->
try_call_command(Args, Auth, AccessCommands, Version) ->
try call_command(Args, Auth, AccessCommands, Version) of
{Reason, wrong_command_arguments} ->
{Reason, ?STATUS_ERROR};
{Reason, ?STATUS_USAGE};
Res ->
Res
catch