mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-24 17:29:28 +01:00
added antiflood and filter modules from TeamLeader
This commit is contained in:
parent
db2baa8f84
commit
b6dcd41225
205
src/floodcheck.erl
Normal file
205
src/floodcheck.erl
Normal file
@ -0,0 +1,205 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : floodcheck.erl
|
||||
%%% Author : Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% Description :
|
||||
%%%
|
||||
%%% Created : 11 Sep 2008 by Christophe Romain <christophe.romain@process-one.net>
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(floodcheck).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0, stop/0]).
|
||||
-export([monitor/5, demonitor/1, interval/1, check/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
||||
|
||||
-define(DEFAULT_INTERVAL, 300). %% check every 5mn
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {timer, interval, monitors}).
|
||||
-record(monitor, {id, pid, ref, info, rule, value, handler}).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
case whereis(?SERVER) of
|
||||
undefined -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []);
|
||||
Pid -> {ok, Pid}
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
gen_server:call(?SERVER, stop).
|
||||
|
||||
monitor(Id, Pid, Info, Spec, {Mod, Fun}) ->
|
||||
gen_server:cast(?SERVER, {monitor, Id, Pid, Info, Spec, {Mod, Fun}}).
|
||||
|
||||
demonitor(Id) ->
|
||||
gen_server:cast(?SERVER, {demonitor, Id}).
|
||||
|
||||
interval(Value) ->
|
||||
gen_server:cast(?SERVER, {interval, Value}).
|
||||
|
||||
check() ->
|
||||
gen_server:call(?SERVER, check).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
Timer = erlang:send_after(?DEFAULT_INTERVAL*1000, ?SERVER, monitor),
|
||||
{ok, #state{timer=Timer, interval=?DEFAULT_INTERVAL, monitors=[]}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(check, _From, State) ->
|
||||
Reply = lists:map(fun(#monitor{id=Id}=M) ->
|
||||
{Id, check(M)}
|
||||
end, State#state.monitors),
|
||||
{reply, Reply, State};
|
||||
handle_call(stop, _From, State) ->
|
||||
erlang:cancel_timer(State#state.timer),
|
||||
{stop, normal, ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast({monitor, Id, Pid, Info, Spec, Handler}, State) ->
|
||||
Monitors = State#state.monitors,
|
||||
Ref = erlang:monitor(process, Pid),
|
||||
{Rule, Value} = case Spec of
|
||||
{Op, V} -> {Op, V};
|
||||
V -> {'>', V}
|
||||
end,
|
||||
Monitor = #monitor{id=Id, pid=Pid, ref=Ref, info=Info, rule=Rule, value=Value, handler=Handler},
|
||||
New = case lists:keysearch(Id, #monitor.id, Monitors) of
|
||||
{value, #monitor{ref=OldRef}} ->
|
||||
erlang:demonitor(OldRef),
|
||||
lists:keyreplace(Id, #monitor.id, Monitors, Monitor);
|
||||
_ ->
|
||||
[Monitor|Monitors]
|
||||
end,
|
||||
{noreply, State#state{monitors=New}};
|
||||
handle_cast({demonitor, Id}, State) ->
|
||||
Monitors = State#state.monitors,
|
||||
New = case lists:keysearch(Id, #monitor.id, Monitors) of
|
||||
{value, #monitor{ref=Ref}} ->
|
||||
erlang:demonitor(Ref),
|
||||
lists:keydelete(Id, #monitor.id, Monitors);
|
||||
_ ->
|
||||
Monitors
|
||||
end,
|
||||
{noreply, State#state{monitors=New}};
|
||||
handle_cast({interval, Value}, State) ->
|
||||
erlang:cancel_timer(State#state.timer),
|
||||
Timer = erlang:send_after(Value*1000, ?SERVER, monitor),
|
||||
{noreply, State#state{timer=Timer, interval=Value}};
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({'DOWN', Ref, _Type, _Pid, _Info}, State) ->
|
||||
Monitors = State#state.monitors,
|
||||
New = lists:keydelete(Ref, #monitor.ref, Monitors),
|
||||
{noreply, State#state{monitors=New}};
|
||||
handle_info(monitor, State) ->
|
||||
lists:foreach(fun(#monitor{id=Id, pid=Pid, info=Info, handler={Mod, Fun}}=M) ->
|
||||
case check(M) of
|
||||
ok -> ok;
|
||||
Value -> spawn(Mod, Fun, [Id, Pid, Info, Value])
|
||||
end
|
||||
end, State#state.monitors),
|
||||
Timer = erlang:send_after(State#state.interval*1000, ?SERVER, monitor),
|
||||
{noreply, State#state{timer=Timer}};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
check(#monitor{pid=Pid, info=Info, rule=Rule, value=Value}) ->
|
||||
case catch process_info(Pid, Info) of
|
||||
{Info, Actual} ->
|
||||
Check = case Info of
|
||||
messages -> byte_size(term_to_binary(Actual));
|
||||
dictionary -> byte_size(term_to_binary(Actual));
|
||||
_ -> Actual
|
||||
end,
|
||||
case Rule of
|
||||
'>' ->
|
||||
if Check > Value -> Value;
|
||||
true -> ok
|
||||
end;
|
||||
'<' ->
|
||||
if Check < Value -> Value;
|
||||
true -> ok
|
||||
end;
|
||||
'=' ->
|
||||
if Check == Value -> Value;
|
||||
true -> ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%%% Documentation
|
||||
%%% authorized Info
|
||||
%%% message_queue_len: number of messages
|
||||
%%% messages: messages queue size in bytes
|
||||
%%% dictionary: dictionary size in bytes
|
||||
%%% total_heap_size: total size in words of all heap fragments
|
||||
%%% heap_size: size in words of youngest heap generation
|
||||
%%% stack_size: stack size in words
|
||||
%%% reductions: number of reductions executed by the process
|
||||
%%% memory: process size in bytes
|
186
src/mod_antiflood.erl
Normal file
186
src/mod_antiflood.erl
Normal file
@ -0,0 +1,186 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_antiflood.erl
|
||||
%%% Author : Christophe Romain <cromain@process-one.net>
|
||||
%%% Description :
|
||||
%%% Created : 12 Sep 2008 by Christophe Romain <cromain@process-one.net>
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(mod_antiflood).
|
||||
-author('cromain@process-one.net').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% API
|
||||
-export([start/2, stop/1]).
|
||||
-export([handler/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
start(Host, Opts) ->
|
||||
floodcheck:start_link(),
|
||||
lists:foreach(fun
|
||||
({listeners, Info, Op, Val}) ->
|
||||
lists:foreach(fun({Name, Pid}) ->
|
||||
Id = {Name, Info},
|
||||
floodcheck:monitor(Id, Pid, Info, {Op, Val}, {?MODULE, handler})
|
||||
end, supervised_processes(listeners));
|
||||
({Module, Info, Op, Val}) ->
|
||||
case module_pid(Host, Module) of
|
||||
Pid when is_pid(Pid) ->
|
||||
Id = {Host, Module, Info},
|
||||
floodcheck:monitor(Id, Pid, Info, {Op, Val}, {?MODULE, handler});
|
||||
Error ->
|
||||
?INFO_MSG("can not monitor ~s (~p)", [Module, Error])
|
||||
end;
|
||||
(Arg) ->
|
||||
?INFO_MSG("invalid argument: ~p", [Arg])
|
||||
end, Opts).
|
||||
|
||||
stop(Host) ->
|
||||
MList = gen_mod:loaded_modules_with_opts(Host),
|
||||
case lists:keysearch(?MODULE, 1, MList) of
|
||||
{value, {?MODULE, Opts}} ->
|
||||
lists:foreach(fun
|
||||
({Type, Info, _, _}) ->
|
||||
case supervised_processes(Type) of
|
||||
[] ->
|
||||
Id = {Host, Type, Info},
|
||||
floodcheck:demonitor(Id);
|
||||
Childs ->
|
||||
lists:foreach(fun({Name, _}) ->
|
||||
Id = {Name, Info},
|
||||
floodcheck:demonitor(Id)
|
||||
end, Childs)
|
||||
end;
|
||||
(_) ->
|
||||
ok
|
||||
end, Opts);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
case floodcheck:check() of
|
||||
[] -> floodcheck:stop(), ok;
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
handler({Host, Module, Info}, Pid, Info, Value) ->
|
||||
?WARNING_MSG("Flood alert on Process ~p (~s on ~s): ~s=~p", [Pid, Module, Host, Info, Value]),
|
||||
restart_module(Host, Module),
|
||||
remonitor({Host, Module, Info});
|
||||
handler({Name, Info}, Pid, Info, Value) ->
|
||||
?WARNING_MSG("Flood alert on Process ~p (~s): ~s=~p", [Pid, Name, Info, Value]),
|
||||
kill_process(Name, Pid),
|
||||
remonitor({Name, Info});
|
||||
handler(Id, Pid, Info, Value) ->
|
||||
?WARNING_MSG("Flood alert on Process ~p (~s): ~s=~p~nUnknown id, alert ignored", [Pid, Id, Info, Value]).
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
|
||||
process_pid(Name) -> whereis(Name).
|
||||
server_pid(Host, Name) -> process_pid(gen_mod:get_module_proc(Host, Name)).
|
||||
|
||||
module_pid(Host, mod_caps) -> server_pid(Host, ejabberd_mod_caps);
|
||||
module_pid(Host, mod_ip_blacklist) -> server_pid(Host, mod_ip_blacklist);
|
||||
module_pid(Host, mod_offline) -> server_pid(Host, ejabberd_offline);
|
||||
module_pid(Host, mod_offline_odbc) -> server_pid(Host, ejabberd_offline);
|
||||
module_pid(Host, mod_vcard) -> server_pid(Host, ejabberd_mod_vcard);
|
||||
module_pid(Host, mod_vcard_odbc) -> server_pid(Host, ejabberd_mod_vcard);
|
||||
module_pid(Host, mod_vcard_ldap) -> server_pid(Host, ejabberd_mod_vcard_ldap);
|
||||
module_pid(Host, mod_irc) -> server_pid(Host, ejabberd_mod_irc);
|
||||
module_pid(Host, mod_muc) -> server_pid(Host, ejabberd_mod_muc);
|
||||
module_pid(Host, mod_muc_log) -> server_pid(Host, ejabberd_mod_muc_log);
|
||||
module_pid(Host, mod_proxy65) -> server_pid(Host, ejabberd_mod_proxy65);
|
||||
module_pid(Host, mod_proxy65_service) -> server_pid(Host, ejabberd_mod_proxy65_service);
|
||||
module_pid(Host, mod_proxy65_sm) -> server_pid(Host, ejabberd_mod_proxy65_sm);
|
||||
module_pid(Host, mod_pubsub) -> server_pid(Host, ejabberd_mod_pubsub);
|
||||
module_pid(_, _) -> unsupported.
|
||||
|
||||
supervised_processes(listeners) ->
|
||||
{links, Links} = process_info(whereis(ejabberd_listeners), links),
|
||||
lists:map(fun(Pid) ->
|
||||
{dictionary, Dict} = process_info(Pid, dictionary),
|
||||
{_, _, [Port|_]} = proplists:get_value('$initial_call', Dict),
|
||||
Name = list_to_atom("listener_"++integer_to_list(Port)),
|
||||
{Name, Pid}
|
||||
end, Links);
|
||||
supervised_processes(_) -> [].
|
||||
|
||||
remonitor({Host, Module, Info}) ->
|
||||
MList = gen_mod:loaded_modules_with_opts(Host),
|
||||
case lists:keysearch(?MODULE, 1, MList) of
|
||||
{value, {?MODULE, Opts}} ->
|
||||
lists:foreach(fun
|
||||
({M, I, Op, Val}) when M =:= Module, I =:= Info ->
|
||||
case module_pid(Host, Module) of
|
||||
Pid when is_pid(Pid) ->
|
||||
Id = {Host, Module, Info},
|
||||
floodcheck:monitor(Id, Pid, Info, {Op, Val}, {?MODULE, handler});
|
||||
Error ->
|
||||
?INFO_MSG("can not monitor ~s (~p)", [Module, Error])
|
||||
end;
|
||||
(_) ->
|
||||
ok
|
||||
end, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
remonitor({Name, Info}) ->
|
||||
[Host|_] = ejabberd_config:get_global_option(hosts),
|
||||
MList = gen_mod:loaded_modules_with_opts(Host),
|
||||
case lists:keysearch(?MODULE, 1, MList) of
|
||||
{value, {?MODULE, Opts}} ->
|
||||
lists:foreach(fun
|
||||
({Type, I, Op, Val}) when I =:= Info ->
|
||||
lists:foreach(fun
|
||||
({N, Pid}) when N =:= Name ->
|
||||
Id = {Name, Info},
|
||||
floodcheck:monitor(Id, Pid, Info, {Op, Val}, {?MODULE, handler});
|
||||
(_) ->
|
||||
ok
|
||||
end, supervised_processes(Type));
|
||||
(_) ->
|
||||
ok
|
||||
end, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
remonitor(Id) ->
|
||||
?INFO_MSG("can not monitor ~s", [Id]).
|
||||
|
||||
restart_module(Host, Module) ->
|
||||
MList = gen_mod:loaded_modules_with_opts(Host),
|
||||
case lists:keysearch(Module, 1, MList) of
|
||||
{value, {Module, Opts}} ->
|
||||
?WARNING_MSG("restarting module ~s on ~s", [Module, Host]),
|
||||
gen_mod:stop_module(Host, Module),
|
||||
gen_mod:start_module(Host, Module, Opts);
|
||||
_ ->
|
||||
not_running
|
||||
end.
|
||||
|
||||
kill_process(Name, Pid) ->
|
||||
?WARNING_MSG("killing process ~s(~p)", [Name, Pid]),
|
||||
exit(Pid, kill).
|
350
src/mod_filter.erl
Normal file
350
src/mod_filter.erl
Normal file
@ -0,0 +1,350 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2006-2009, ProcessOne.
|
||||
%%%
|
||||
%%% mod_filter
|
||||
%%% allow message filtering using regexp on message body
|
||||
%%% THIS MODULE NEEDS A PATCHED ERLANG VM AGAINST
|
||||
%%% THE PCRE PATCH AND NEEDS LIBPCRE INSTALLED
|
||||
%%% ejabberd MUST USE THAT PATCHED ERLANG VM
|
||||
%%% BUT, if patch is not available, mod_filter uses re.beam module
|
||||
%%% instead, with speed degradation.
|
||||
%%%
|
||||
%%% @copyright 2006-2009 ProcessOne
|
||||
%%% @author Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
-module(mod_filter).
|
||||
-author('christophe.romain@process-one.net').
|
||||
-vsn('$Id: mod_filter.erl 767 2009-04-20 16:31:40Z cromain $').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
% module functions
|
||||
-export([start/2,stop/1,init/2,update/2,is_loaded/0,loop/5]).
|
||||
-export([add_regexp/4,add_regexp/3,del_regexp/3,del_regexp/2]).
|
||||
-export([purge_logs/0,purge_regexps/1,reload/1]).
|
||||
-export([logged/0,logged/1,rules/0]).
|
||||
-export([process_local_iq/3]).
|
||||
|
||||
% handled ejabberd hooks
|
||||
-export([filter_packet/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("licence.hrl").
|
||||
|
||||
-record(filter_rule, {id, type="filter", regexp, binre}).
|
||||
-record(filter_log, {date, from, to, message}).
|
||||
|
||||
-define(TIMEOUT, 5000). % deliver message anyway if filter does not respond after 5s
|
||||
-define(PROCNAME(VH), list_to_atom(VH++"_message_filter")).
|
||||
-define(NS_FILTER, "p1:iq:filter").
|
||||
|
||||
-define(ALLHOSTS, "all hosts"). %% must be sync with filter.erl
|
||||
|
||||
start(Host, Opts) ->
|
||||
case ?IS_VALID of
|
||||
true ->
|
||||
mnesia:create_table(filter_rule, [
|
||||
{disc_copies, [node()]}, {type, set},
|
||||
{attributes, record_info(fields, filter_rule)} ]),
|
||||
mnesia:create_table(filter_log, [
|
||||
{disc_only_copies, [node()]}, {type, bag},
|
||||
{attributes, record_info(fields, filter_log)} ]),
|
||||
%% this force the last code to be used
|
||||
case whereis(?PROCNAME(Host)) of
|
||||
undefined ->
|
||||
ok;
|
||||
_ ->
|
||||
ejabberd_hooks:delete(filter_packet, ?MODULE, filter_packet, 10),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_FILTER),
|
||||
?PROCNAME(Host) ! quit
|
||||
end,
|
||||
ejabberd_hooks:add(filter_packet, ?MODULE, filter_packet, 10),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_FILTER,
|
||||
?MODULE, process_local_iq, one_queue),
|
||||
case whereis(?PROCNAME(Host)) of
|
||||
undefined -> register(?PROCNAME(Host), spawn(?MODULE, init, [Host, Opts]));
|
||||
_ -> ok
|
||||
end,
|
||||
%% start the all_alias handler
|
||||
case whereis(?PROCNAME(?ALLHOSTS)) of
|
||||
undefined -> init_all_hosts_handler();
|
||||
_ -> ok
|
||||
end,
|
||||
start;
|
||||
false ->
|
||||
not_started
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(filter_packet, ?MODULE, filter_packet, 10),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_FILTER),
|
||||
exit(whereis(?PROCNAME(Host)), kill),
|
||||
{wait, ?PROCNAME(Host)}.
|
||||
|
||||
% this is used by team_leader to check code presence
|
||||
is_loaded() ->
|
||||
ok.
|
||||
|
||||
% this loads rules and return {Types, BinRegExps}
|
||||
% RegExps are text regexp list
|
||||
% Types are regexp type list
|
||||
% both list share the same ordered
|
||||
load_rules(Host) ->
|
||||
Rules = mnesia:dirty_match_object(#filter_rule{id={'_', Host}, _ = '_'})
|
||||
++ mnesia:dirty_match_object(#filter_rule{id={'_', ?ALLHOSTS}, _ = '_'}),
|
||||
lists:map(fun({filter_rule, _, Type, _, BinRegExp}) -> {Type, BinRegExp} end, Rules).
|
||||
%lists:unzip(Config).
|
||||
|
||||
% this call init or reset local rules reading database
|
||||
init(Host, Opts) ->
|
||||
Rules = load_rules(Host),
|
||||
Scope = gen_mod:get_opt(scope, Opts, message),
|
||||
Pattern = gen_mod:get_opt(pattern, Opts, ""),
|
||||
?MODULE:loop(Host, Opts, Rules, Scope, Pattern).
|
||||
|
||||
init_all_hosts_handler() ->
|
||||
register(?PROCNAME(?ALLHOSTS), spawn(?MODULE, loop, [?ALLHOSTS, [], [], none, []])).
|
||||
|
||||
% this call reset local rules reading database
|
||||
% and tell other nodes to reset rules as well
|
||||
update(?ALLHOSTS, _Opts) ->
|
||||
lists:foreach(fun(Host) ->
|
||||
lists:foreach(fun(Node) ->
|
||||
catch rpc:call(Node,mod_filter,reload,[Host])
|
||||
end, mnesia:system_info(running_db_nodes))
|
||||
end, ejabberd_config:get_global_option(hosts)),
|
||||
?MODULE:loop(?ALLHOSTS, [], [], none, []);
|
||||
update(Host, Opts) ->
|
||||
% tell other nodes to update filter
|
||||
lists:foreach(fun(Node) ->
|
||||
catch rpc:call(Node,mod_filter,reload,[Host])
|
||||
end, mnesia:system_info(running_db_nodes)--[node()]),
|
||||
% update rules
|
||||
init(Host, Opts).
|
||||
|
||||
loop(Host, Opts, Rules, Scope, Pattern) ->
|
||||
receive
|
||||
{add, Id, RegExp} ->
|
||||
[BinRegExp] = tlre:compile([RegExp]),
|
||||
mnesia:dirty_write(#filter_rule{id={Id, Host}, regexp=RegExp, binre=BinRegExp}),
|
||||
?MODULE:update(Host, Opts);
|
||||
{add, Id, RegExp, Type} ->
|
||||
[BinRegExp] = tlre:compile([RegExp]),
|
||||
mnesia:dirty_write(#filter_rule{id={Id, Host}, regexp=RegExp, binre=BinRegExp, type=Type}),
|
||||
?MODULE:update(Host, Opts);
|
||||
{del, Id} ->
|
||||
mnesia:dirty_delete_object(#filter_rule{id={Id, Host}, _='_'}),
|
||||
?MODULE:update(Host, Opts);
|
||||
{del, Id, RegExp} ->
|
||||
mnesia:dirty_delete_object(#filter_rule{id={Id, Host}, regexp=RegExp, _='_'}),
|
||||
?MODULE:update(Host, Opts);
|
||||
{match, From, String} ->
|
||||
From ! {match, string_filter(String, Rules, Scope, Pattern)},
|
||||
?MODULE:loop(Host, Opts, Rules, Scope, Pattern);
|
||||
reload ->
|
||||
?MODULE:init(Host, Opts);
|
||||
quit ->
|
||||
unregister(?PROCNAME(Host)),
|
||||
ok
|
||||
end.
|
||||
|
||||
string_filter(String, Rules, Scope, Pattern) ->
|
||||
lists:foldl(fun
|
||||
(_, {Pass, []}) -> {Pass, []};
|
||||
({Type, RegExp}, {Pass, NewString}) -> string_filter(NewString, Pass, RegExp, Type, Scope, Pattern)
|
||||
end, {"pass", String}, Rules).
|
||||
string_filter(String, Pass, RegExp, Type, Scope, Pattern) ->
|
||||
case tlre:grep(String, [RegExp]) of
|
||||
[no_match] ->
|
||||
{Pass, String};
|
||||
[{S1, S2, _}] ->
|
||||
case Scope of
|
||||
word ->
|
||||
Start = string:sub_string(String, 1, S1),
|
||||
StringTail = string:sub_string(String, S2+1, length(String)),
|
||||
NewPass = pass_rule(Pass, Type),
|
||||
{LastPass, End} = string_filter(StringTail, NewPass, RegExp, Type, Scope, Pattern),
|
||||
NewString = case Type of
|
||||
"log" -> lists:append([string:sub_string(String, 1, S2), End]);
|
||||
_ -> lists:append([Start, Pattern, End])
|
||||
end,
|
||||
{LastPass, NewString};
|
||||
_ ->
|
||||
NewString = case Type of
|
||||
"log" -> String;
|
||||
_ -> []
|
||||
end,
|
||||
{pass_rule(Pass, Type), NewString}
|
||||
end
|
||||
end.
|
||||
|
||||
pass_rule("pass", New) -> New;
|
||||
pass_rule("log", "log") -> "log";
|
||||
pass_rule("log", "log and filter") -> "log and filter";
|
||||
pass_rule("log", "filter") -> "log and filter";
|
||||
pass_rule("filter", "log") -> "log and filter";
|
||||
pass_rule("filter", "log and filter") -> "log and filter";
|
||||
pass_rule("filter", "filter") -> "filter";
|
||||
pass_rule("log and filter", _) -> "log and filter".
|
||||
|
||||
add_regexp(VH, Id, RegExp) ->
|
||||
?PROCNAME(VH) ! {add, Id, RegExp},
|
||||
ok.
|
||||
|
||||
add_regexp(VH, Id, RegExp, Type) ->
|
||||
?PROCNAME(VH) ! {add, Id, RegExp, Type},
|
||||
ok.
|
||||
|
||||
del_regexp(VH, Id) ->
|
||||
?PROCNAME(VH) ! {del, Id},
|
||||
ok.
|
||||
|
||||
del_regexp(VH, Id, RegExp) ->
|
||||
?PROCNAME(VH) ! {del, Id, RegExp},
|
||||
ok.
|
||||
|
||||
reload(VH) ->
|
||||
?PROCNAME(VH) ! reload,
|
||||
ok.
|
||||
|
||||
purge_logs() ->
|
||||
mnesia:dirty_delete_object(#filter_log{_='_'}).
|
||||
|
||||
%purge_regexps() ->
|
||||
% mnesia:dirty_delete_object(#filter_rule{_='_'}),
|
||||
% reload().
|
||||
|
||||
purge_regexps(VH) ->
|
||||
mnesia:dirty_delete_object(#filter_rule{id={'_', VH}, _='_'}),
|
||||
reload(VH).
|
||||
|
||||
rules() ->
|
||||
lists:map(fun(#filter_rule{id={Label, VH}, type=Type, regexp=Regexp}) ->
|
||||
{VH, Label, Type, Regexp}
|
||||
end, mnesia:dirty_match_object(#filter_rule{_='_'})).
|
||||
|
||||
logged() ->
|
||||
lists:reverse(lists:map(fun(#filter_log{date=Date, from=From, to=To, message=Msg}) ->
|
||||
{Date, jlib:jid_to_string(From), jlib:jid_to_string(To), Msg}
|
||||
end, mnesia:dirty_match_object(#filter_log{_='_'}))).
|
||||
|
||||
logged(Limit) ->
|
||||
List = mnesia:dirty_match_object(#filter_log{_='_'}),
|
||||
Len = length(List),
|
||||
FinalList = if
|
||||
Len < Limit -> List;
|
||||
true -> lists:nthtail(Len-Limit, List)
|
||||
end,
|
||||
lists:reverse(lists:map(fun(#filter_log{date=Date, from=From, to=To, message=Msg}) ->
|
||||
{Date, jlib:jid_to_string(From), jlib:jid_to_string(To), Msg}
|
||||
end, FinalList)).
|
||||
|
||||
%% filter_packet can receive drop if a previous filter already dropped
|
||||
%% the packet
|
||||
filter_packet(drop) -> drop;
|
||||
filter_packet({From, To, Packet}) ->
|
||||
case Packet of
|
||||
{xmlelement, "message", MsgAttrs, Els} ->
|
||||
case lists:keysearch("body", 2, Els) of
|
||||
{value, {xmlelement, "body", BodyAttrs, Data}} ->
|
||||
NewData = lists:foldl(fun
|
||||
({xmlcdata, CData}, DataAcc) when is_binary(CData) ->
|
||||
#jid{lserver = Host} = To,
|
||||
case lists:member(Host, ejabberd_config:get_global_option(hosts)) of
|
||||
true ->
|
||||
Msg = binary_to_list(CData),
|
||||
?PROCNAME(Host) ! {match, self(), Msg},
|
||||
receive
|
||||
{match, {"pass", _}} ->
|
||||
[{xmlcdata, CData}|DataAcc];
|
||||
{match, {"log", FinalString}} ->
|
||||
mnesia:dirty_write(#filter_log{
|
||||
date=erlang:localtime(),
|
||||
from=From, to=To, message=Msg}),
|
||||
[{xmlcdata, CData}|DataAcc];
|
||||
{match, {"log and filter", FinalString}} ->
|
||||
mnesia:dirty_write(#filter_log{
|
||||
date=erlang:localtime(),
|
||||
from=From, to=To, message=Msg}),
|
||||
case FinalString of
|
||||
[] -> % entire message is dropped
|
||||
DataAcc;
|
||||
S -> % message must be regenerated
|
||||
[{xmlcdata, list_to_binary(S)}|DataAcc]
|
||||
end;
|
||||
{match, {"filter", FinalString}} ->
|
||||
case FinalString of
|
||||
[] -> % entire message is dropped
|
||||
DataAcc;
|
||||
S -> % message must be regenerated
|
||||
[{xmlcdata, list_to_binary(S)}|DataAcc]
|
||||
end
|
||||
after ?TIMEOUT ->
|
||||
[{xmlcdata, CData}|DataAcc]
|
||||
end;
|
||||
false ->
|
||||
[{xmlcdata, CData}|DataAcc]
|
||||
end;
|
||||
(Item, DataAcc) -> %% to not filter internal messages
|
||||
[Item|DataAcc]
|
||||
end, [], Data),
|
||||
case NewData of
|
||||
[] ->
|
||||
drop;
|
||||
D ->
|
||||
NewEls = lists:keyreplace("body", 2, Els, {xmlelement, "body", BodyAttrs, lists:reverse(D)}),
|
||||
{From, To, {xmlelement, "message", MsgAttrs, NewEls}}
|
||||
end;
|
||||
_ ->
|
||||
{From, To, Packet}
|
||||
end;
|
||||
_ ->
|
||||
{From, To, Packet}
|
||||
end.
|
||||
|
||||
process_local_iq(From, #jid{lserver=VH} = _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
get ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
set ->
|
||||
#jid{luser = User, lserver = Server, lresource = Resource} = From,
|
||||
case acl:match_rule(global, configure, {User, Server, Resource}) of
|
||||
allow ->
|
||||
case xml:get_subtag(SubEl, "add") of
|
||||
{xmlelement, "add", AddAttrs, _} ->
|
||||
AID = xml:get_attr_s("id", AddAttrs),
|
||||
ARE = xml:get_attr_s("re", AddAttrs),
|
||||
case xml:get_attr_s("type", AddAttrs) of
|
||||
"" -> add_regexp(VH, AID, ARE);
|
||||
ATP -> add_regexp(VH, AID, ARE, ATP)
|
||||
end;
|
||||
_ -> ok
|
||||
end,
|
||||
case xml:get_subtag(SubEl, "del") of
|
||||
{xmlelement, "del", DelAttrs, _} ->
|
||||
DID = xml:get_attr_s("id", DelAttrs),
|
||||
case xml:get_attr_s("re", DelAttrs) of
|
||||
"" -> del_regexp(VH, DID);
|
||||
DRE -> del_regexp(VH, DID, DRE)
|
||||
end;
|
||||
_ -> ok
|
||||
end,
|
||||
case xml:get_subtag(SubEl, "dellogs") of
|
||||
{xmlelement, "dellogs", _, _} -> purge_logs();
|
||||
_ -> ok
|
||||
end,
|
||||
case xml:get_subtag(SubEl, "delrules") of
|
||||
{xmlelement, "delrules", _, _} -> purge_regexps(VH);
|
||||
_ -> ok
|
||||
end,
|
||||
IQ#iq{type = result, sub_el = []};
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end
|
||||
end.
|
||||
|
261
src/mod_support.erl
Normal file
261
src/mod_support.erl
Normal file
@ -0,0 +1,261 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2006-2009, ProcessOne.
|
||||
%%%
|
||||
%%% mod_support
|
||||
%%% allow automatic build of support archive to be sent to Process-One
|
||||
%%%
|
||||
%%% @copyright 2006-2009 ProcessOne
|
||||
%%% @author Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
|
||||
-module(mod_support).
|
||||
-author('christophe.romain@process-one.net').
|
||||
-vsn('$Id: mod_support.erl 856 2009-09-21 18:46:38Z jpcarlino $').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
%-behaviour(gen_server).
|
||||
|
||||
% module functions
|
||||
-export([start/2,stop/1,is_loaded/0,loop/1,dump/0]).
|
||||
-compile(export_all).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("licence.hrl").
|
||||
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
-define(LOG_FETCH_SIZE, 1000000).
|
||||
-define(RPC_TIMEOUT, 10000). % 10
|
||||
-define(MAX_FILE_SIZE, 2147483648). %%2Gb
|
||||
|
||||
start(Host, Opts) ->
|
||||
case ?IS_VALID of
|
||||
true ->
|
||||
case gen_mod:get_opt(dump_freq, Opts, 0) of
|
||||
0 -> no_dump;
|
||||
Freq -> spawn(?MODULE, loop, [Freq*60000])
|
||||
end,
|
||||
ok;
|
||||
false ->
|
||||
not_started
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
ok.
|
||||
|
||||
is_loaded() ->
|
||||
ok.
|
||||
|
||||
loop(Timeout) ->
|
||||
receive
|
||||
quit -> ok
|
||||
after Timeout ->
|
||||
Dump = dump(),
|
||||
BaseName = get_base_name(),
|
||||
%%{Data,EjabberdLog,SaslLog,ECrash} = Dump,
|
||||
write_logs(tuple_to_list(Dump),BaseName,["_memory.bin",
|
||||
"_ejabberd.log.gz",
|
||||
"_sasl.log.gz",
|
||||
"_erlang_crash_dump.log.gz"]),
|
||||
loop(Timeout)
|
||||
end.
|
||||
|
||||
get_base_name() ->
|
||||
{{Y,M,D},{Hr,Mn,_Sc}} = calendar:local_time(),
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false ->
|
||||
filename:join(filename:dirname(filename:absname("")),
|
||||
lists:flatten(io_lib:format("~b~b~b~b~b",[Y,M,D,Hr,Mn])));
|
||||
Path ->
|
||||
filename:join(filename:dirname(Path),
|
||||
lists:flatten(io_lib:format("~b~b~b~b~b",[Y,M,D,Hr,Mn])))
|
||||
end.
|
||||
|
||||
write_logs([BinaryData|T],BaseName,[Filename|Filenames]) ->
|
||||
Log = BaseName++Filename,
|
||||
file:write_file(Log, BinaryData),
|
||||
write_logs(T,BaseName,Filenames);
|
||||
|
||||
write_logs([],BaseName,_)-> ok.
|
||||
|
||||
dump() ->
|
||||
Dump = lists:map(fun(LogFile) ->
|
||||
Content = case file:open(LogFile,[read,raw]) of
|
||||
{ok, IO} ->
|
||||
Size = case file:read_file_info(LogFile) of
|
||||
{ok, FileInfo} -> FileInfo#file_info.size;
|
||||
_ -> ?LOG_FETCH_SIZE
|
||||
end,
|
||||
case Size>?MAX_FILE_SIZE of
|
||||
true -> io_lib:format("File ~s is too big: ~p bytes.",[LogFile, Size]);
|
||||
false ->
|
||||
if Size>?LOG_FETCH_SIZE ->
|
||||
file:position(IO, Size-?LOG_FETCH_SIZE),
|
||||
case file:read(IO, ?LOG_FETCH_SIZE) of
|
||||
{ok, Data1} -> Data1;
|
||||
Error1 -> io_lib:format("can not read log file (~s): ~p",[LogFile, Error1])
|
||||
end;
|
||||
true ->
|
||||
case file:read(IO, Size) of
|
||||
{ok, Data2} -> Data2;
|
||||
Error2 -> io_lib:format("can not read log file (~s): ~p",[LogFile, Error2])
|
||||
end
|
||||
end
|
||||
end;
|
||||
{error, Reason} ->
|
||||
io_lib:format("can not open log file (~s): ~p",[LogFile, Reason])
|
||||
end,
|
||||
zlib:gzip(list_to_binary(Content))
|
||||
end, [ejabberd_logs(), sasl_logs(), erl_crash()]),
|
||||
NodeState = get_node_state(),
|
||||
list_to_tuple([NodeState|Dump]).
|
||||
|
||||
ejabberd_logs() ->
|
||||
LogPath = case application:get_env(log_path) of
|
||||
{ok, Path} ->
|
||||
Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false -> ?LOG_PATH;
|
||||
Path -> Path
|
||||
end
|
||||
end.
|
||||
|
||||
sasl_logs() ->
|
||||
case os:getenv("SASL_LOG_PATH") of
|
||||
false -> filename:join([filename:dirname(ejabberd_logs()),"sasl.log"]);
|
||||
Path -> Path
|
||||
end.
|
||||
|
||||
erl_crash() ->
|
||||
LogsDir = filename:dirname(ejabberd_logs()),
|
||||
CrashDumpWildcard = filename:join([LogsDir,"erl_crash*dump"]),
|
||||
FileName = case filelib:wildcard(CrashDumpWildcard) of
|
||||
[Files] -> [LastFile|T] = lists:reverse([Files]),
|
||||
LastFile;
|
||||
_ -> case os:getenv("ERL_CRASH_DUMP") of
|
||||
false -> "erl_crash.dump";
|
||||
Path -> Path
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
proc_info(Pid) ->
|
||||
Info = process_info(Pid),
|
||||
lists:map(fun(Elem) ->
|
||||
List = proplists:get_value(Elem, Info),
|
||||
{Elem, size(term_to_binary(List))}
|
||||
end, [messages, dictionary])
|
||||
++ [X || X <- Info,
|
||||
lists:member(element(1,X),
|
||||
[heap_size,stack_size,reductions,links,status,initial_call,current_function])].
|
||||
|
||||
environment() ->
|
||||
{ok, KE} = application:get_key(kernel,env),
|
||||
{ok, EE} = application:get_key(ejabberd,env),
|
||||
Env = [{inetrc, os:getenv("ERL_INETRC")},
|
||||
{sopath, os:getenv("EJABBERD_SO_PATH")},
|
||||
{maxports, os:getenv("ERL_MAX_PORTS")},
|
||||
{maxtables, os:getenv("ERL_MAX_ETS_TABLES")},
|
||||
{crashdump, os:getenv("ERL_CRASH_DUMP")},
|
||||
{archdir, os:getenv("ARCHDIR")},
|
||||
{mnesia, mnesia:system_info(all)}],
|
||||
Args = [{args, init:get_arguments()}, {plain, init:get_plain_arguments()}],
|
||||
KE++EE++Env++Args.
|
||||
|
||||
memtop(N) ->
|
||||
E = lists:sublist(lists:reverse(lists:keysort(2,lists:map(fun(Tab) -> {Tab, ets:info(Tab,memory)} end, ets:all()))),N),
|
||||
M = lists:sublist(lists:reverse(lists:keysort(2,lists:map(fun(Tab) -> {Tab, mnesia:table_info(Tab,memory)} end, mnesia:system_info(tables)))),N),
|
||||
E++M.
|
||||
|
||||
maxmsgqueue() ->
|
||||
lists:max(lists:map(fun(Pid) -> proplists:get_value(message_queue_len,process_info(Pid)) end, erlang:processes())).
|
||||
|
||||
msgqueue(N) ->
|
||||
lists:filter(fun(L) -> proplists:get_value(message_queue_len, L) > N
|
||||
end, lists:map(fun(Pid) -> process_info(Pid) end, erlang:processes())).
|
||||
|
||||
%lists:sublist(lists:reverse(lists:keysort(2,lists:map(fun(Pid) -> {E,L} = process_info(Pid, dictionary), {E,length(L)} end, erlang:processes()))), 10)
|
||||
|
||||
%%Entry point to invoke mod_support via command line.
|
||||
%%Example: erl -sname debug@localhost -s mod_support report ejabberd@localhost
|
||||
%%See issue #TECH-286.
|
||||
report(Node) ->
|
||||
[NodeId|T]=Node,
|
||||
UploadResult = force_load_code_into_node(NodeId, ?MODULE),
|
||||
case UploadResult of
|
||||
ok -> NodeState = rpc:call(NodeId,mod_support,get_node_state,[],?RPC_TIMEOUT),
|
||||
Dump = rpc:call(NodeId,mod_support,dump,[],?RPC_TIMEOUT),
|
||||
BaseName = get_base_name(),
|
||||
%%{Data,EjabberdLog,SaslLog,ECrash} = Dump,
|
||||
write_logs(tuple_to_list(Dump),BaseName,["_memory.bin",
|
||||
"_ejabberd.log.gz",
|
||||
"_sasl.log.gz",
|
||||
"_erlang_crash_dump.log.gz"]),
|
||||
error_logger:info_msg("State in node ~p was written to log~n",[NodeId]),
|
||||
error_logger:info_msg("Unloading module ~s from node ~p. ",[?MODULE,NodeId]),
|
||||
force_unload_code_from_node(NodeId, ?MODULE);
|
||||
_ -> error_logger:info_msg("Error uploading module ~s from node ~p~n",[?MODULE,NodeId])
|
||||
end.
|
||||
|
||||
%%Load Module into the ejabberd Node specified.
|
||||
force_load_code_into_node(Node, Module) ->
|
||||
CodeFile = code:where_is_file(atom_to_list(Module)++".beam"),
|
||||
case file:read_file(CodeFile) of
|
||||
{ok, Code} ->
|
||||
rpc:call(Node, code, purge, [Module], ?RPC_TIMEOUT),
|
||||
rpc:call(Node, code, delete, [Module], ?RPC_TIMEOUT),
|
||||
case rpc:call(Node, code, load_binary, [Module, CodeFile, Code], ?RPC_TIMEOUT) of
|
||||
{module, _} ->
|
||||
error_logger:info_msg("Loading ~s module into ~p : success ~n", [Module,Node]),
|
||||
rpc:block_call(Node, Module, is_loaded, [], ?RPC_TIMEOUT);
|
||||
{error, badfile} ->
|
||||
error_logger:info_msg("Loading ~s module into ~p : incorrect format ~n", [Module,Node]),
|
||||
{error, badfile};
|
||||
{error, not_purged} ->
|
||||
% this should never happen anyway..
|
||||
error_logger:info_msg("Loading ~s module into ~p : old code already exists ~n", [Module,Node]),
|
||||
{error, not_purged};
|
||||
{badrpc, Reason} ->
|
||||
error_logger:info_msg("Loading ~s module into ~p: badrpc ~p ~n", [Module,Node,Reason]),
|
||||
{badrpc, Reason}
|
||||
end;
|
||||
Error ->
|
||||
error_logger:error_msg("Cannot read module file ~s ~p : ~p ~n", [Module, CodeFile, Error]),
|
||||
Error
|
||||
end.
|
||||
|
||||
%%Unload erlang Module from the Node specified. Used to ensure cleanup after rpc calls.
|
||||
force_unload_code_from_node(Node, Module) ->
|
||||
rpc:call(Node, code, purge, [Module], ?RPC_TIMEOUT),
|
||||
rpc:call(Node, code, delete, [Module], ?RPC_TIMEOUT).
|
||||
|
||||
%%Retrieve system state and pack it into Data
|
||||
%%TODO enhance state info. See #TECH-286.
|
||||
get_node_state() ->
|
||||
Mem = erlang:memory(),
|
||||
Ets = lists:map(fun(Tab) -> ets:info(Tab) end, ets:all()),
|
||||
Mnesia = lists:map(fun(Tab) -> mnesia:table_info(Tab,all) end, mnesia:system_info(tables)),
|
||||
Procs = lists:map(fun(Pid) -> proc_info(Pid) end, erlang:processes()),
|
||||
Data = term_to_binary({Mem, Ets, Mnesia, Procs}).
|
||||
|
||||
crash_dump() ->
|
||||
SystemInfo = [erlang:system_info(X) || X<-[info,loaded,procs]],
|
||||
[zlib:gzip(list_to_binary(lists:flatten(SystemInfo)))].
|
||||
|
||||
crash_dump(Node) ->
|
||||
[NodeId|T]=Node,
|
||||
UploadResult = force_load_code_into_node(NodeId, ?MODULE),
|
||||
case UploadResult of
|
||||
ok -> Dump = rpc:call(NodeId,mod_support,crash_dump,[],?RPC_TIMEOUT),
|
||||
BaseName = get_base_name(),
|
||||
write_logs(Dump,BaseName,["_realtime_crash_dump.gz"]),
|
||||
error_logger:info_msg("Unloading module ~s from node ~p. ",[?MODULE,NodeId]),
|
||||
force_unload_code_from_node(NodeId, ?MODULE);
|
||||
_ -> error_logger:info_msg("Error uploading module ~s from node ~p~n",[?MODULE,NodeId])
|
||||
end.
|
Loading…
Reference in New Issue
Block a user