diff options
Diffstat (limited to 'src/ejabberd_router.erl')
-rw-r--r-- | src/ejabberd_router.erl | 268 |
1 files changed, 168 insertions, 100 deletions
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 4fd85f2c9..20f0c998c 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -10,6 +10,9 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). +-behaviour(gen_server). + +%% API -export([route/3, register_route/1, register_route/2, @@ -20,114 +23,28 @@ dirty_get_all_domains/0 ]). --export([start_link/0, init/0]). +-export([start_link/0]). + +%% 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"). -record(route, {domain, pid, local_hint}). +-record(state, {}). - +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- start_link() -> - update_tables(), - mnesia:create_table(route, - [{ram_copies, [node()]}, - {type, bag}, - {attributes, - record_info(fields, route)}]), - mnesia:add_table_copy(route, node(), ram_copies), - Pid = proc_lib:spawn_link(ejabberd_router, init, []), - register(ejabberd_router, Pid), - {ok, Pid}. + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -init() -> - mnesia:subscribe({table, route, simple}), - lists:foreach( - fun(Pid) -> - erlang:monitor(process, Pid) - end, - mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])), - loop(). - -loop() -> - receive - {route, From, To, Packet} -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> - ok - end, - loop(); - {mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}} -> - erlang:monitor(process, Pid), - loop(); - {'DOWN', _Ref, _Type, Pid, _Info} -> - F = fun() -> - Es = mnesia:select( - route, - [{#route{pid = Pid, _ = '_'}, - [], - ['$_']}]), - lists:foreach(fun(E) -> - mnesia:delete_object(E) - end, Es) - end, - mnesia:transaction(F), - loop(); - _ -> - loop() - end. - -do_route(OrigFrom, OrigTo, OrigPacket) -> - ?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", - [OrigFrom, OrigTo, OrigPacket]), - LOrigDstDomain = OrigTo#jid.lserver, - case ejabberd_hooks:run_fold(filter_packet, LOrigDstDomain, - {OrigFrom, OrigTo, OrigPacket}, []) of - {From, To, Packet} -> - LDstDomain = To#jid.lserver, - case mnesia:dirty_read(route, LDstDomain) of - [] -> - ejabberd_s2s:route(From, To, Packet); - [R] -> - Pid = R#route.pid, - if - node(Pid) == node() -> - case R#route.local_hint of - {apply, Module, Function} -> - Module:Function(From, To, Packet); - _ -> - Pid ! {route, From, To, Packet} - end; - true -> - Pid ! {route, From, To, Packet} - end; - Rs -> - case [R || R <- Rs, node(R#route.pid) == node()] of - [] -> - R = lists:nth(erlang:phash(now(), length(Rs)), Rs), - Pid = R#route.pid, - Pid ! {route, From, To, Packet}; - LRs -> - LRs, - R = lists:nth(erlang:phash(now(), length(LRs)), LRs), - Pid = R#route.pid, - case R#route.local_hint of - {apply, Module, Function} -> - Module:Function(From, To, Packet); - _ -> - Pid ! {route, From, To, Packet} - end - end - end; - drop -> - ok - end. - -%route(From, To, Packet) -> -% ejabberd_router ! {route, From, To, Packet}. route(From, To, Packet) -> case catch do_route(From, To, Packet) of @@ -196,6 +113,157 @@ dirty_get_all_domains() -> lists:usort(mnesia:dirty_all_keys(route)). +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([]) -> + update_tables(), + mnesia:create_table(route, + [{ram_copies, [node()]}, + {type, bag}, + {attributes, + record_info(fields, route)}]), + mnesia:add_table_copy(route, node(), ram_copies), + mnesia:subscribe({table, route, simple}), + lists:foreach( + fun(Pid) -> + erlang:monitor(process, Pid) + end, + mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])), + {ok, #state{}}. + +%%-------------------------------------------------------------------- +%% 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(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling cast messages +%%-------------------------------------------------------------------- +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({route, From, To, Packet}, State) -> + case catch do_route(From, To, Packet) of + {'EXIT', Reason} -> + ?ERROR_MSG("~p~nwhen processing: ~p", + [Reason, {From, To, Packet}]); + _ -> + ok + end, + {noreply, State}; +handle_info({mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}}, + State) -> + erlang:monitor(process, Pid), + {noreply, State}; +handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> + F = fun() -> + Es = mnesia:select( + route, + [{#route{pid = Pid, _ = '_'}, + [], + ['$_']}]), + lists:foreach(fun(E) -> + mnesia:delete_object(E) + end, Es) + end, + mnesia:transaction(F), + {noreply, State}; +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 +%%-------------------------------------------------------------------- +do_route(OrigFrom, OrigTo, OrigPacket) -> + ?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", + [OrigFrom, OrigTo, OrigPacket]), + LOrigDstDomain = OrigTo#jid.lserver, + case ejabberd_hooks:run_fold(filter_packet, LOrigDstDomain, + {OrigFrom, OrigTo, OrigPacket}, []) of + {From, To, Packet} -> + LDstDomain = To#jid.lserver, + case mnesia:dirty_read(route, LDstDomain) of + [] -> + ejabberd_s2s:route(From, To, Packet); + [R] -> + Pid = R#route.pid, + if + node(Pid) == node() -> + case R#route.local_hint of + {apply, Module, Function} -> + Module:Function(From, To, Packet); + _ -> + Pid ! {route, From, To, Packet} + end; + true -> + Pid ! {route, From, To, Packet} + end; + Rs -> + case [R || R <- Rs, node(R#route.pid) == node()] of + [] -> + R = lists:nth(erlang:phash(now(), length(Rs)), Rs), + Pid = R#route.pid, + Pid ! {route, From, To, Packet}; + LRs -> + LRs, + R = lists:nth(erlang:phash(now(), length(LRs)), LRs), + Pid = R#route.pid, + case R#route.local_hint of + {apply, Module, Function} -> + Module:Function(From, To, Packet); + _ -> + Pid ! {route, From, To, Packet} + end + end + end; + drop -> + ok + end. + + update_tables() -> case catch mnesia:table_info(route, attributes) of |