mirror of
https://github.com/processone/ejabberd.git
synced 2024-07-21 00:19:53 +02:00
176 lines
6.1 KiB
Erlang
176 lines
6.1 KiB
Erlang
%% Usage:
|
|
%% In config file:
|
|
%% {mod_c2s_debug, [{logdir, "/tmp/xmpplogs"}]},
|
|
%% From Erlang shell:
|
|
%% mod_c2s_debug:start("localhost", []).
|
|
%% mod_c2s_debug:stop("localhost").
|
|
%%
|
|
%% Warning: Only one module for the debug handler can be defined.
|
|
-module(mod_c2s_debug).
|
|
-author('mremond@process-one.net').
|
|
|
|
-behaviour(gen_mod).
|
|
-behavior(gen_server).
|
|
|
|
-export([start/2, start_link/2, stop/1,
|
|
debug_start/3, debug_stop/2, log_packet/4, log_packet/5]).
|
|
%% gen_server callbacks
|
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
|
terminate/2, code_change/3]).
|
|
|
|
-include("ejabberd.hrl").
|
|
-include("jlib.hrl").
|
|
-include("ejabberd_c2s.hrl").
|
|
|
|
-record(modstate, {host, logdir, pid, iodevice}).
|
|
-record(clientinfo, {pid, jid, auth_module, ip}).
|
|
|
|
-define(SUPERVISOR, ejabberd_sup).
|
|
-define(PROCNAME, c2s_debug).
|
|
|
|
%%====================================================================
|
|
%% gen_mod callbacks
|
|
%%====================================================================
|
|
start(Host, Opts) ->
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
Spec = {Proc, {?MODULE, start_link, [Host, Opts]},
|
|
transient, 2000, worker, [?MODULE]},
|
|
supervisor:start_child(?SUPERVISOR, Spec).
|
|
|
|
stop(Host) ->
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
gen_server:call(Proc, stop),
|
|
supervisor:delete_child(?SUPERVISOR, Proc).
|
|
|
|
start_link(Host, Opts) ->
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
|
|
|
|
%%====================================================================
|
|
%% Hooks
|
|
%%====================================================================
|
|
|
|
%% Debug handled by another module... Do nothing:
|
|
debug_start(_Status, Pid, C2SState) ->
|
|
Host = C2SState#state.server,
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
|
|
JID = jlib:jid_to_string(C2SState#state.jid),
|
|
AuthModule = C2SState#state.auth_module,
|
|
IP = C2SState#state.ip,
|
|
ClientInfo = #clientinfo{pid = Pid, jid = JID, auth_module = AuthModule, ip = IP},
|
|
|
|
gen_server:call(Proc, {debug_start, ClientInfo}).
|
|
|
|
debug_stop(Pid, C2SState) ->
|
|
Host = C2SState#state.server,
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
gen_server:cast(Proc, {debug_stop, Pid}).
|
|
|
|
log_packet(false, _FromJID, _ToJID, _Packet) ->
|
|
ok;
|
|
log_packet(true, FromJID, ToJID, Packet) ->
|
|
Host = FromJID#jid.lserver,
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
gen_server:cast(Proc, {addlog, {"Send", FromJID, ToJID, Packet}}).
|
|
log_packet(false, _JID, _FromJID, _ToJID, _Packet) ->
|
|
ok;
|
|
log_packet(true, JID, FromJID, ToJID, Packet) ->
|
|
Host = JID#jid.lserver,
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
gen_server:cast(Proc, {addlog, {"Receive", FromJID, ToJID, Packet}}).
|
|
|
|
%%====================================================================
|
|
%% gen_server callbacks
|
|
%%====================================================================
|
|
init([Host, Opts]) ->
|
|
?INFO_MSG("Starting c2s debug module for: ~p", [Host]),
|
|
MyHost = gen_mod:get_opt_host(Host, Opts, "c2s_debug.@HOST@"),
|
|
ejabberd_hooks:add(c2s_debug_start_hook, Host,
|
|
?MODULE, debug_start, 50),
|
|
ejabberd_hooks:add(c2s_debug_stop_hook, Host,
|
|
?MODULE, debug_stop, 50),
|
|
ejabberd_hooks:add(user_send_packet, Host, ?MODULE, log_packet, 50),
|
|
ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, log_packet, 50),
|
|
|
|
Logdir = gen_mod:get_opt(logdir, Opts, "/tmp/xmpplogs/"),
|
|
make_dir_rec(Logdir),
|
|
{ok, #modstate{host = MyHost, logdir = Logdir}}.
|
|
|
|
terminate(_Reason, #modstate{host = Host}) ->
|
|
?INFO_MSG("Stopping c2s debug module for: ~s", [Host]),
|
|
ejabberd_hooks:delete(c2s_debug_start_hook, Host,
|
|
?MODULE, debug_start, 50),
|
|
ejabberd_hooks:delete(c2s_debug_stop_hook, Host,
|
|
?MODULE, debug_stop, 50),
|
|
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, log_packet, 50).
|
|
|
|
handle_call({debug_start, ClientInfo}, _From, #modstate{pid=undefined} = State) ->
|
|
Pid = ClientInfo#clientinfo.pid,
|
|
?INFO_MSG("Debug started for PID:~p", [Pid]),
|
|
|
|
JID = ClientInfo#clientinfo.jid,
|
|
AuthModule = ClientInfo#clientinfo.auth_module,
|
|
IP = ClientInfo#clientinfo.ip,
|
|
|
|
{ok, IOD} = file:open(filename(State#modstate.logdir), [append]),
|
|
Line = io_lib:format("~s - Session open~nJID: ~s~nAuthModule: ~p~nIP: ~p~n",
|
|
[timestamp(), JID, AuthModule, IP]),
|
|
file:write(IOD, Line),
|
|
|
|
{reply, true, State#modstate{pid = Pid, iodevice = IOD}};
|
|
handle_call({debug_start, _ClientInfo}, _From, State) ->
|
|
{reply, false, State};
|
|
handle_call(stop, _From, State) ->
|
|
{stop, normal, ok, State};
|
|
handle_call(_Req, _From, State) ->
|
|
{reply, {error, badarg}, State}.
|
|
|
|
handle_cast({addlog, _}, #modstate{iodevice=undefined} = State) ->
|
|
{noreply, State};
|
|
handle_cast({addlog, {Direction, FromJID, ToJID, Packet}}, #modstate{iodevice=IOD} = State) ->
|
|
LogEntry = io_lib:format("=====~n~s - ~s~nFrom: ~s~nTo: ~s~n~s~n", [timestamp(), Direction,
|
|
jlib:jid_to_string(FromJID),
|
|
jlib:jid_to_string(ToJID),
|
|
xml:element_to_string(Packet)]),
|
|
file:write(IOD, LogEntry),
|
|
{noreply, State};
|
|
handle_cast({debug_stop, Pid}, #modstate{pid=Pid, iodevice=IOD} = State) ->
|
|
Line = io_lib:format("=====~n~s - Session closed~n",
|
|
[timestamp()]),
|
|
file:write(IOD, Line),
|
|
|
|
file:close(IOD),
|
|
{noreply, State#modstate{pid = undefined, iodevice=undefined}};
|
|
handle_cast(_Msg, State) ->
|
|
{noreply, State}.
|
|
|
|
handle_info(_Info, State) ->
|
|
{noreply, State}.
|
|
|
|
code_change(_OldVsn, State, _Extra) ->
|
|
{ok, State}.
|
|
|
|
%% Generate filename
|
|
filename(LogDir) ->
|
|
Filename = lists:flatten(timestamp()) ++ "-c2s.log",
|
|
filename:join([LogDir, Filename]).
|
|
|
|
%% Generate timestamp
|
|
timestamp() ->
|
|
{Y,Mo,D} = erlang:date(),
|
|
{H,Mi,S} = erlang:time(),
|
|
io_lib:format("~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,Mo,D,H,Mi,S]).
|
|
|
|
%% Create dir recusively
|
|
make_dir_rec(Dir) ->
|
|
case file:read_file_info(Dir) of
|
|
{ok, _} ->
|
|
ok;
|
|
{error, enoent} ->
|
|
DirS = filename:split(Dir),
|
|
DirR = lists:sublist(DirS, length(DirS)-1),
|
|
make_dir_rec(filename:join(DirR)),
|
|
file:make_dir(Dir)
|
|
end.
|