2010-07-13 18:40:16 +02:00
|
|
|
%% Usage:
|
|
|
|
%% In config file:
|
|
|
|
%% {mod_mnesia_mgmt, [{logdir, "/tmp/xmpplogs/"}]},
|
|
|
|
%% From Erlang shell:
|
|
|
|
%% mod_mnesia_mgmt:start("localhost", []).
|
|
|
|
%% mod_mnesia_mgmt:stop("localhost").
|
|
|
|
|
|
|
|
-module(mod_mnesia_mngt).
|
2012-09-11 15:45:59 +02:00
|
|
|
|
2010-07-13 18:40:16 +02:00
|
|
|
-author('mremond@process-one.net').
|
|
|
|
|
|
|
|
-behaviour(gen_mod).
|
2012-09-11 15:45:59 +02:00
|
|
|
|
2010-07-13 18:40:16 +02:00
|
|
|
-behavior(gen_server).
|
|
|
|
|
|
|
|
-export([start/2, start_link/2, stop/1]).
|
|
|
|
|
|
|
|
%% gen_server callbacks
|
2012-09-11 15:45:59 +02:00
|
|
|
-export([init/1, handle_call/3, handle_cast/2,
|
|
|
|
handle_info/2, terminate/2, code_change/3]).
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
-include("ejabberd.hrl").
|
2012-09-11 15:45:59 +02:00
|
|
|
|
2010-07-13 18:40:16 +02:00
|
|
|
-include("jlib.hrl").
|
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
-record(modstate, {host = <<"">> :: binary(),
|
|
|
|
logdir = <<"">> :: binary(),
|
|
|
|
iodevice :: file:io_device(),
|
|
|
|
timer :: timer:tref()}).
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
-define(SUPERVISOR, ejabberd_sup).
|
2012-09-11 15:45:59 +02:00
|
|
|
|
2010-07-13 18:40:16 +02:00
|
|
|
-define(PROCNAME, mod_mnesia_mgmt).
|
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
-define(STANDARD_ACCEPT_INTERVAL, 20).
|
|
|
|
|
|
|
|
-define(ACCEPT_INTERVAL, 200).
|
|
|
|
|
|
|
|
-define(RATE_LIMIT_DURATION, 120000).
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
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),
|
2012-09-11 15:45:59 +02:00
|
|
|
gen_server:start_link({local, Proc}, ?MODULE,
|
|
|
|
[Host, Opts], []).
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
init([Host, Opts]) ->
|
|
|
|
?INFO_MSG("Starting mod_mnesia_mgmt for: ~p", [Host]),
|
2012-09-11 15:45:59 +02:00
|
|
|
ejabberd_listener:rate_limit([5222, 5223],
|
|
|
|
?STANDARD_ACCEPT_INTERVAL),
|
|
|
|
MyHost = gen_mod:get_opt_host(Host, Opts,
|
|
|
|
<<"mnesia_mngt.@HOST@">>),
|
|
|
|
Logdir = gen_mod:get_opt(logdir, Opts,
|
|
|
|
fun iolist_to_binary/1,
|
|
|
|
<<"/tmp/xmpplogs/">>),
|
|
|
|
make_dir_rec(binary_to_list(Logdir)),
|
2010-07-13 18:40:16 +02:00
|
|
|
{ok, IOD} = file:open(filename(Logdir), [append]),
|
|
|
|
mnesia:subscribe(system),
|
2012-09-11 15:45:59 +02:00
|
|
|
{ok,
|
|
|
|
#modstate{host = MyHost, logdir = Logdir,
|
|
|
|
iodevice = IOD}}.
|
2010-07-13 18:40:16 +02:00
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
terminate(_Reason,
|
|
|
|
#modstate{host = Host, iodevice = IOD}) ->
|
2010-07-13 18:40:16 +02:00
|
|
|
?INFO_MSG("Stopping mod_mnesia_mgmt for: ~s", [Host]),
|
|
|
|
mnesia:unsubscribe(system),
|
|
|
|
file:close(IOD).
|
|
|
|
|
|
|
|
handle_call(stop, _From, State) ->
|
|
|
|
{stop, normal, ok, State};
|
|
|
|
handle_call(_Req, _From, State) ->
|
|
|
|
{reply, {error, badarg}, State}.
|
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
handle_cast(_Msg, State) -> {noreply, State}.
|
2010-07-13 18:40:16 +02:00
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
handle_info({mnesia_system_event,
|
|
|
|
{mnesia_overload,
|
|
|
|
{mnesia_tm, message_queue_len, Values}}},
|
2010-07-13 18:40:16 +02:00
|
|
|
#modstate{iodevice = IOD, timer = Timer} = State) ->
|
2012-09-11 15:45:59 +02:00
|
|
|
Line =
|
|
|
|
io_lib:format("~s - Mnesia overload due to message "
|
|
|
|
"queue length (~p)",
|
|
|
|
[timestamp(), Values]),
|
2010-07-13 18:40:16 +02:00
|
|
|
file:write(IOD, Line),
|
|
|
|
reset_timer(Timer),
|
2012-09-11 15:45:59 +02:00
|
|
|
{messages, Messages} = process_info(whereis(mnesia_tm),
|
|
|
|
messages),
|
2010-07-13 18:40:16 +02:00
|
|
|
log_messages(IOD, Messages, 20),
|
|
|
|
{noreply, State#modstate{timer = undefined}};
|
2012-09-11 15:45:59 +02:00
|
|
|
handle_info({mnesia_system_event,
|
|
|
|
{mnesia_overload, Details}},
|
2010-07-13 18:40:16 +02:00
|
|
|
#modstate{iodevice = IOD, timer = Timer} = State) ->
|
|
|
|
Line = io_lib:format("~s - Mnesia overload: ~p",
|
|
|
|
[timestamp(), Details]),
|
|
|
|
file:write(IOD, Line),
|
|
|
|
reset_timer(Timer),
|
|
|
|
{noreply, State};
|
|
|
|
handle_info({mnesia_system_event, _Event}, State) ->
|
|
|
|
{noreply, State};
|
2012-09-11 15:45:59 +02:00
|
|
|
handle_info(_Info, State) -> {noreply, State}.
|
2010-07-13 18:40:16 +02:00
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
filename(LogDir) ->
|
|
|
|
Filename = lists:flatten(timestamp()) ++ "-mnesia.log",
|
|
|
|
filename:join([LogDir, Filename]).
|
|
|
|
|
|
|
|
timestamp() ->
|
2012-09-11 15:45:59 +02:00
|
|
|
{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]).
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
make_dir_rec(Dir) ->
|
|
|
|
case file:read_file_info(Dir) of
|
2012-09-11 15:45:59 +02:00
|
|
|
{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)
|
2010-07-13 18:40:16 +02:00
|
|
|
end.
|
|
|
|
|
2012-09-11 15:45:59 +02:00
|
|
|
log_messages(_IOD, _Messages, 0) -> ok;
|
|
|
|
log_messages(_IOD, [], _N) -> ok;
|
|
|
|
log_messages(IOD, [Message | Messages], N) ->
|
|
|
|
Line = io_lib:format("** ~w", [Message]),
|
2010-07-13 18:40:16 +02:00
|
|
|
file:write(IOD, Line),
|
2012-09-11 15:45:59 +02:00
|
|
|
log_messages(IOD, Messages, N - 1).
|
2010-07-13 18:40:16 +02:00
|
|
|
|
|
|
|
reset_timer(Timer) ->
|
|
|
|
cancel_timer(Timer),
|
2012-09-11 15:45:59 +02:00
|
|
|
ejabberd_listener:rate_limit([5222, 5223],
|
|
|
|
?ACCEPT_INTERVAL),
|
|
|
|
timer:apply_after(?RATE_LIMIT_DURATION,
|
|
|
|
ejabberd_listener, rate_limit,
|
|
|
|
[[5222, 5223], ?STANDARD_ACCEPT_INTERVAL]).
|
|
|
|
|
|
|
|
cancel_timer(undefined) -> ok;
|
|
|
|
cancel_timer(Timer) -> timer:cancel(Timer).
|