summaryrefslogtreecommitdiff
path: root/src/ejabberd_router.erl
blob: 700724eab3b2441ab906b97f59fd3e706c612c76 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
%%%----------------------------------------------------------------------
%%% File    : ejabberd_router.erl
%%% Author  : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Main router
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Id      : $Id$
%%%----------------------------------------------------------------------

-module(ejabberd_router).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').

-export([route/3,
	 register_route/1,
	 unregister_route/1,
	 dirty_get_all_routes/0,
	 dirty_get_all_domains/0
	]).

-export([start_link/0, init/0]).

-include("ejabberd.hrl").
-include("jlib.hrl").

-record(route, {domain, pid}).


start_link() ->
    Pid = proc_lib:spawn_link(ejabberd_router, init, []),
    register(ejabberd_router, Pid),
    {ok, Pid}.

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}),
    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();
	{register_route, Domain, Pid} ->
	    F = fun() ->
			mnesia:write(#route{domain = Domain,
					    pid = Pid})
		end,
	    mnesia:transaction(F),
	    loop();
	{unregister_route, Domain, Pid} ->
	    F = fun() ->
			mnesia:delete_object(#route{domain = Domain,
						    pid = Pid})
		end,
	    mnesia:transaction(F),
	    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(From, To, Packet) ->
    ?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", [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,
	    ?DEBUG("routed to process ~p~n", [Pid]),
	    Pid ! {route, From, To, Packet};
	Rs ->
	    Rs1 = case [R || R <- Rs, node(R#route.pid) == node()] of
		      [] -> Rs;
		      LRs -> LRs
		  end,
	    R = lists:nth(erlang:phash(now(), length(Rs1)), Rs1),
	    Pid = R#route.pid,
	    ?DEBUG("routed to process ~p~n", [Pid]),
	    Pid ! {route, From, To, Packet}
    end.


route(From, To, Packet) ->
    ejabberd_router ! {route, From, To, Packet}.

register_route(Domain) ->
    ejabberd_router ! {register_route, Domain, self()}.

unregister_route(Domain) ->
    ejabberd_router ! {unregister_route, Domain, self()}.


dirty_get_all_routes() ->
    lists:delete(?MYNAME, lists:usort(mnesia:dirty_all_keys(route))).

dirty_get_all_domains() ->
    lists:usort(mnesia:dirty_all_keys(route)).



update_tables() ->
    case catch mnesia:table_info(route, attributes) of
	[domain, node, pid] ->
	    mnesia:delete_table(route);
	[domain, pid] ->
	    ok;
	{'EXIT', _} ->
	    ok
    end,
    case lists:member(local_route, mnesia:system_info(tables)) of
	true ->
	    mnesia:delete_table(local_route);
	false ->
	    ok
    end.