diff --git a/ChangeLog b/ChangeLog index 08e2d38b4..b28db2ec6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2006-01-29 Alexey Shchepin + + * src/odbc/pg.sql: Fixed syntax error + +2006-01-28 Alexey Shchepin + + * src/ejabberd_router.erl: Updated to use gen_server behaviour + * src/ejabberd_sm.erl: Likewise + * src/ejabberd_s2s.erl: Likewise + * src/gen_iq_handler.erl: Likewise + + * src/ejabberd_sup.erl: Added supervisor for ejabberd_receiver + * src/ejabberd_receiver.erl: Updating + 2006-01-27 Alexey Shchepin * src/ejabberd_update.erl: Support for run-time ejabberd updating diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index a9fa5ee2e..afb5332a7 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -10,7 +10,10 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start_link/0, init/0]). +-behaviour(gen_server). + +%% API +-export([start_link/0]). -export([route/3, register_iq_handler/4, @@ -20,102 +23,26 @@ bounce_resource_packet/3 ]). +%% 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(state, {}). + -define(IQTABLE, local_iqtable). +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- start_link() -> - register(ejabberd_local, - Pid = proc_lib:spawn_link(ejabberd_local, init, [])), - {ok, Pid}. - -init() -> - lists:foreach( - fun(Host) -> - ejabberd_router:register_route(Host, {apply, ?MODULE, route}), - ejabberd_hooks:add(local_send_to_resource_hook, Host, - ?MODULE, bounce_resource_packet, 100) - end, ?MYHOSTS), - catch ets:new(?IQTABLE, [named_table, public]), - 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_iq_handler, Host, XMLNS, Module, Function} -> - ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}), - catch mod_disco:register_feature(Host, XMLNS), - loop(); - {register_iq_handler, Host, XMLNS, Module, Function, Opts} -> - ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}), - catch mod_disco:register_feature(Host, XMLNS), - loop(); - {unregister_iq_handler, Host, XMLNS} -> - case ets:lookup(?IQTABLE, {XMLNS, Host}) of - [{_, Module, Function, Opts}] -> - gen_iq_handler:stop_iq_handler(Module, Function, Opts); - _ -> - ok - end, - ets:delete(?IQTABLE, {XMLNS, Host}), - catch mod_disco:unregister_feature(Host, XMLNS), - loop(); - refresh_iq_handlers -> - lists:foreach( - fun(T) -> - case T of - {{XMLNS, Host}, _Module, _Function, _Opts} -> - catch mod_disco:register_feature(Host, XMLNS); - {{XMLNS, Host}, _Module, _Function} -> - catch mod_disco:register_feature(Host, XMLNS); - _ -> - ok - end - end, ets:tab2list(?IQTABLE)), - loop(); - _ -> - loop() - end. - - -do_route(From, To, Packet) -> - ?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", - [From, To, Packet, 8]), - if - To#jid.luser /= "" -> - ejabberd_sm:route(From, To, Packet); - To#jid.lresource == "" -> - {xmlelement, Name, Attrs, _Els} = Packet, - case Name of - "iq" -> - process_iq(From, To, Packet); - "message" -> - ok; - "presence" -> - ok; - _ -> - ok - end; - true -> - {xmlelement, Name, Attrs, _Els} = Packet, - case xml:get_attr_s("type", Attrs) of - "error" -> ok; - "result" -> ok; - _ -> - ejabberd_hooks:run(local_send_to_resource_hook, - To#jid.lserver, - [From, To, Packet]) - end - end. + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). process_iq(From, To, Packet) -> IQ = jlib:iq_query_info(Packet), @@ -173,3 +100,146 @@ bounce_resource_packet(From, To, Packet) -> Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND), ejabberd_router:route(To, From, Err), stop. + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([]) -> + lists:foreach( + fun(Host) -> + ejabberd_router:register_route(Host, {apply, ?MODULE, route}), + ejabberd_hooks:add(local_send_to_resource_hook, Host, + ?MODULE, bounce_resource_packet, 100) + end, ?MYHOSTS), + catch ets:new(?IQTABLE, [named_table, public]), + {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({register_iq_handler, Host, XMLNS, Module, Function}, State) -> + ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}), + catch mod_disco:register_feature(Host, XMLNS), + {noreply, State}; +handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) -> + ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}), + catch mod_disco:register_feature(Host, XMLNS), + {noreply, State}; +handle_info({unregister_iq_handler, Host, XMLNS}, State) -> + case ets:lookup(?IQTABLE, {XMLNS, Host}) of + [{_, Module, Function, Opts}] -> + gen_iq_handler:stop_iq_handler(Module, Function, Opts); + _ -> + ok + end, + ets:delete(?IQTABLE, {XMLNS, Host}), + catch mod_disco:unregister_feature(Host, XMLNS), + {noreply, State}; +handle_info(refresh_iq_handlers, State) -> + lists:foreach( + fun(T) -> + case T of + {{XMLNS, Host}, _Module, _Function, _Opts} -> + catch mod_disco:register_feature(Host, XMLNS); + {{XMLNS, Host}, _Module, _Function} -> + catch mod_disco:register_feature(Host, XMLNS); + _ -> + ok + end + end, ets:tab2list(?IQTABLE)), + {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(From, To, Packet) -> + ?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", + [From, To, Packet, 8]), + if + To#jid.luser /= "" -> + ejabberd_sm:route(From, To, Packet); + To#jid.lresource == "" -> + {xmlelement, Name, _Attrs, _Els} = Packet, + case Name of + "iq" -> + process_iq(From, To, Packet); + "message" -> + ok; + "presence" -> + ok; + _ -> + ok + end; + true -> + {xmlelement, _Name, Attrs, _Els} = Packet, + case xml:get_attr_s("type", Attrs) of + "error" -> ok; + "result" -> ok; + _ -> + ejabberd_hooks:run(local_send_to_resource_hook, + To#jid.lserver, + [From, To, Packet]) + end + end. + diff --git a/src/ejabberd_receiver.erl b/src/ejabberd_receiver.erl index 6977c8931..03618b520 100644 --- a/src/ejabberd_receiver.erl +++ b/src/ejabberd_receiver.erl @@ -13,7 +13,8 @@ -behaviour(gen_server). %% API --export([start/3, +-export([start_link/4, + start/3, change_shaper/2, reset_stream/1, starttls/2, @@ -37,13 +38,20 @@ %%==================================================================== %% API %%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- +start_link(Socket, SockMod, Shaper, C2SPid) -> + gen_server:start_link(?MODULE, [Socket, SockMod, Shaper, C2SPid], []). + %%-------------------------------------------------------------------- %% Function: start() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- start(Socket, SockMod, Shaper) -> - {ok, Pid} = gen_server:start( - ?MODULE, [Socket, SockMod, Shaper, self()], []), + {ok, Pid} = supervisor:start_child(ejabberd_receiver_sup, + [Socket, SockMod, Shaper, self()]), Pid. change_shaper(Pid, Shaper) -> 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 diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 7a07861f0..f5aac6b8a 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -10,7 +10,10 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start_link/0, init/0, +-behaviour(gen_server). + +%% API +-export([start_link/0, route/3, have_connection/1, get_key/1, @@ -18,43 +21,25 @@ remove_connection/1, dirty_get_connections/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(s2s, {fromto, pid, key}). +-record(state, {}). - +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- start_link() -> - Pid = proc_lib:spawn_link(ejabberd_s2s, init, []), - register(ejabberd_s2s, Pid), - {ok, Pid}. - -init() -> - update_tables(), - mnesia:create_table(s2s, [{ram_copies, [node()]}, - {attributes, record_info(fields, s2s)}]), - mnesia:add_table_copy(s2s, node(), ram_copies), - mnesia:subscribe(system), - loop(). - -loop() -> - receive - {mnesia_system_event, {mnesia_down, Node}} -> - clean_table_from_bad_node(Node), - loop(); - {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(); - _ -> - loop() - end. - + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). route(From, To, Packet) -> case catch do_route(From, To, Packet) of @@ -65,28 +50,12 @@ route(From, To, Packet) -> ok end. - remove_connection(FromTo) -> F = fun() -> mnesia:delete({s2s, FromTo}) end, mnesia:transaction(F). - - -clean_table_from_bad_node(Node) -> - F = fun() -> - Es = mnesia:select( - s2s, - [{#s2s{pid = '$1', _ = '_'}, - [{'==', {node, '$1'}, Node}], - ['$_']}]), - lists:foreach(fun(E) -> - mnesia:delete_object(E) - end, Es) - end, - mnesia:transaction(F). - have_connection(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of [_] -> @@ -123,9 +92,103 @@ try_register(FromTo) -> false end. +dirty_get_connections() -> + mnesia:dirty_all_keys(s2s). +%%==================================================================== +%% gen_server callbacks +%%==================================================================== -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([]) -> + update_tables(), + mnesia:create_table(s2s, [{ram_copies, [node()]}, + {attributes, record_info(fields, s2s)}]), + mnesia:add_table_copy(s2s, node(), ram_copies), + mnesia:subscribe(system), + {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({mnesia_system_event, {mnesia_down, Node}}, State) -> + clean_table_from_bad_node(Node), + {noreply, State}; +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(_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 +%%-------------------------------------------------------------------- +clean_table_from_bad_node(Node) -> + F = fun() -> + Es = mnesia:select( + s2s, + [{#s2s{pid = '$1', _ = '_'}, + [{'==', {node, '$1'}, Node}], + ['$_']}]), + lists:foreach(fun(E) -> + mnesia:delete_object(E) + end, Es) + end, + mnesia:transaction(F). do_route(From, To, Packet) -> ?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", @@ -174,15 +237,9 @@ find_connection(From, To) -> {atomic, El#s2s.pid} end. - send_element(Pid, El) -> Pid ! {send_element, El}. - -dirty_get_connections() -> - mnesia:dirty_all_keys(s2s). - - update_tables() -> case catch mnesia:table_info(s2s, attributes) of [fromto, node, key] -> diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 34d2fec47..dffb3c0da 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -10,7 +10,10 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start_link/0, init/0, +-behaviour(gen_server). + +%% API +-export([start_link/0, route/3, open_session/4, close_session/1, bounce_offline_message/3, @@ -27,17 +30,124 @@ unregister_iq_handler/2 ]). +%% 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(session, {sid, usr, us, priority}). +-record(state, {}). +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- start_link() -> - Pid = proc_lib:spawn_link(ejabberd_sm, init, []), - register(ejabberd_sm, Pid), - {ok, Pid}. + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -init() -> +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. + +open_session(SID, User, Server, Resource) -> + set_session(SID, User, Server, Resource, undefined). + +close_session(SID) -> + F = fun() -> + mnesia:delete({session, SID}) + end, + mnesia:sync_dirty(F). + +bounce_offline_message(From, To, Packet) -> + Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE), + ejabberd_router:route(To, From, Err), + stop. + +disconnect_removed_user(User, Server) -> + ejabberd_sm:route(jlib:make_jid("", "", ""), + jlib:make_jid(User, Server, ""), + {xmlelement, "broadcast", [], + [{exit, "User removed"}]}). + +get_user_resources(User, Server) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + US = {LUser, LServer}, + case catch mnesia:dirty_index_read(session, US, #session.us) of + {'EXIT', _Reason} -> + []; + Ss -> + [element(3, S#session.usr) || S <- clean_session_list(Ss)] + end. + +set_presence(SID, User, Server, Resource, Priority) -> + set_session(SID, User, Server, Resource, Priority). + +unset_presence(SID, User, Server, Resource, Status) -> + set_session(SID, User, Server, Resource, undefined), + ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), + [User, Server, Resource, Status]). + +close_session_unset_presence(SID, User, Server, Resource, Status) -> + close_session(SID), + ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), + [User, Server, Resource, Status]). + + +dirty_get_sessions_list() -> + mnesia:dirty_select( + session, + [{#session{usr = '$1', _ = '_'}, + [], + ['$1']}]). + +dirty_get_my_sessions_list() -> + mnesia:dirty_select( + session, + [{#session{sid = {'_', '$1'}, _ = '_'}, + [{'==', {node, '$1'}, node()}], + ['$_']}]). + +get_vh_session_list(Server) -> + LServer = jlib:nameprep(Server), + mnesia:dirty_select( + session, + [{#session{usr = '$1', _ = '_'}, + [{'==', {element, 2, '$1'}, LServer}], + ['$1']}]). + +register_iq_handler(Host, XMLNS, Module, Fun) -> + ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}. + +register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> + ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}. + +unregister_iq_handler(Host, XMLNS) -> + ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}. + + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([]) -> update_tables(), mnesia:create_table(session, [{ram_copies, [node()]}, @@ -54,53 +164,86 @@ init() -> ejabberd_hooks:add(remove_user, Host, ejabberd_sm, disconnect_removed_user, 100) end, ?MYHOSTS), - loop(). + {ok, #state{}}. -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_system_event, {mnesia_down, Node}} -> - clean_table_from_bad_node(Node), - loop(); - {register_iq_handler, Host, XMLNS, Module, Function} -> - ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}), - loop(); - {register_iq_handler, Host, XMLNS, Module, Function, Opts} -> - ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}), - loop(); - {unregister_iq_handler, Host, XMLNS} -> - case ets:lookup(sm_iqtable, {XMLNS, Host}) of - [{_, Module, Function, Opts}] -> - gen_iq_handler:stop_iq_handler(Module, Function, Opts); - _ -> - ok - end, - ets:delete(sm_iqtable, {XMLNS, Host}), - loop(); - _ -> - loop() - end. +%%-------------------------------------------------------------------- +%% 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}. -route(From, To, Packet) -> +%%-------------------------------------------------------------------- +%% 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. + end, + {noreply, State}; +handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> + clean_table_from_bad_node(Node), + {noreply, State}; +handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) -> + ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}), + {noreply, State}; +handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) -> + ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}), + {noreply, State}; +handle_info({unregister_iq_handler, Host, XMLNS}, State) -> + case ets:lookup(sm_iqtable, {XMLNS, Host}) of + [{_, Module, Function, Opts}] -> + gen_iq_handler:stop_iq_handler(Module, Function, Opts); + _ -> + ok + end, + ets:delete(sm_iqtable, {XMLNS, Host}), + {noreply, State}; +handle_info(_Info, State) -> + {noreply, State}. -open_session(SID, User, Server, Resource) -> - set_session(SID, User, Server, Resource, undefined). +%%-------------------------------------------------------------------- +%% 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 +%%-------------------------------------------------------------------- set_session(SID, User, Server, Resource, Priority) -> LUser = jlib:nodeprep(User), @@ -131,12 +274,6 @@ set_session(SID, User, Server, Resource, Priority) -> end, SIDs) end. -close_session(SID) -> - F = fun() -> - mnesia:delete({session, SID}) - end, - mnesia:sync_dirty(F). - clean_table_from_bad_node(Node) -> F = fun() -> @@ -314,31 +451,9 @@ route_message(From, To, Packet) -> end end. -bounce_offline_message(From, To, Packet) -> - Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err), - stop. - -disconnect_removed_user(User, Server) -> - ejabberd_sm:route(jlib:make_jid("", "", ""), - jlib:make_jid(User, Server, ""), - {xmlelement, "broadcast", [], - [{exit, "User removed"}]}). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -get_user_resources(User, Server) -> - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_index_read(session, US, #session.us) of - {'EXIT', _Reason} -> - []; - Ss -> - [element(3, S#session.usr) || S <- clean_session_list(Ss)] - end. - clean_session_list(Ss) -> clean_session_list(lists:keysort(#session.usr, Ss), []). @@ -362,19 +477,6 @@ clean_session_list([S1, S2 | Rest], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -set_presence(SID, User, Server, Resource, Priority) -> - set_session(SID, User, Server, Resource, Priority). - -unset_presence(SID, User, Server, Resource, Status) -> - set_session(SID, User, Server, Resource, undefined), - ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), - [User, Server, Resource, Status]). - -close_session_unset_presence(SID, User, Server, Resource, Status) -> - close_session(SID), - ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), - [User, Server, Resource, Status]). - get_user_present_resources(LUser, LServer) -> US = {LUser, LServer}, case catch mnesia:dirty_index_read(session, US, #session.us) of @@ -385,28 +487,6 @@ get_user_present_resources(LUser, LServer) -> S <- clean_session_list(Ss), is_integer(S#session.priority)] end. -dirty_get_sessions_list() -> - mnesia:dirty_select( - session, - [{#session{usr = '$1', _ = '_'}, - [], - ['$1']}]). - -dirty_get_my_sessions_list() -> - mnesia:dirty_select( - session, - [{#session{sid = {'_', '$1'}, _ = '_'}, - [{'==', {node, '$1'}, node()}], - ['$_']}]). - -get_vh_session_list(Server) -> - LServer = jlib:nameprep(Server), - mnesia:dirty_select( - session, - [{#session{usr = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, LServer}], - ['$1']}]). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -441,15 +521,6 @@ process_iq(From, To, Packet) -> ok end. -register_iq_handler(Host, XMLNS, Module, Fun) -> - ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}. - -register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> - ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}. - -unregister_iq_handler(Host, XMLNS) -> - ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}. - update_tables() -> diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index d58ec422f..1e6851dc2 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -68,6 +68,14 @@ init([]) -> infinity, supervisor, [ejabberd_listener]}, + ReceiverSupervisor = + {ejabberd_receiver_sup, + {ejabberd_tmp_sup, start_link, + [ejabberd_receiver_sup, ejabberd_receiver]}, + permanent, + infinity, + supervisor, + [ejabberd_tmp_sup]}, C2SSupervisor = {ejabberd_c2s_sup, {ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]}, @@ -130,6 +138,7 @@ init([]) -> SM, S2S, Local, + ReceiverSupervisor, C2SSupervisor, S2SInSupervisor, S2SOutSupervisor, diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl index 103b20509..a094bd2ce 100644 --- a/src/ejabberd_update.erl +++ b/src/ejabberd_update.erl @@ -95,11 +95,17 @@ update() -> make_script(UpdatedBeams) -> lists:map( fun(Module) -> - {ok, {Module, [{attributes, Attrs}]}} = + {ok, {Module, [{attributes, NewAttrs}]}} = beam_lib:chunks(code:which(Module), [attributes]), - case lists:keysearch(update_info, 1, Attrs) of - {value, {_, [{update, Extra}]}} -> - {update, Module, {advanced, Extra}}; + CurAttrs = Module:module_info(attributes), + case lists:keysearch(update_info, 1, NewAttrs) of + {value, {_, [{update, _}]}} -> + case lists:keysearch(update_info, 1, CurAttrs) of + {value, {_, [{update, Extra}]}} -> + {update, Module, {advanced, Extra}}; + false -> + {update, Module, {advanced, 0}} + end; false -> {load_module, Module} end diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl index caed05000..f0c77f78c 100644 --- a/src/gen_iq_handler.erl +++ b/src/gen_iq_handler.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : gen_iq_handler.erl %%% Author : Alexey Shchepin -%%% Purpose : +%%% Purpose : IQ handler support %%% Created : 22 Jan 2003 by Alexey Shchepin %%% Id : $Id$ %%%---------------------------------------------------------------------- @@ -10,19 +10,35 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start/0, - start_link/3, +-behaviour(gen_server). + +%% API +-export([start_link/3, add_iq_handler/6, remove_iq_handler/3, stop_iq_handler/3, handle/7, - process_iq/6, - queue_init/3]). + process_iq/6]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). -include("ejabberd.hrl"). -start() -> - ok. +-record(state, {host, + module, + function}). + +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- +start_link(Host, Module, Function) -> + gen_server:start_link(?MODULE, [Host, Module, Function], []). add_iq_handler(Component, Host, NS, Module, Function, Type) -> case Type of @@ -40,10 +56,10 @@ add_iq_handler(Component, Host, NS, Module, Function, Type) -> remove_iq_handler(Component, Host, NS) -> Component:unregister_iq_handler(Host, NS). -stop_iq_handler(Module, Function, Opts) -> +stop_iq_handler(_Module, _Function, Opts) -> case Opts of {one_queue, Pid} -> - exit(Pid, kill); + gen_server:call(Pid, stop); _ -> ok end. @@ -75,18 +91,76 @@ process_iq(_Host, Module, Function, From, To, IQ) -> end end. -start_link(Host, Module, Function) -> - {ok, proc_lib:spawn_link(?MODULE, queue_init, [Host, Module, Function])}. +%%==================================================================== +%% gen_server callbacks +%%==================================================================== -queue_init(Host, Module, Function) -> - queue_loop(Host, Module, Function). +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([Host, Module, Function]) -> + {ok, #state{host = Host, + module = Module, + function = Function}}. -% TODO: use gen_event -queue_loop(Host, Module, Function) -> - receive - {process_iq, From, To, IQ} -> - process_iq(Host, Module, Function, From, To, IQ), - queue_loop(Host, Module, Function); - _ -> - queue_loop(Host, Module, Function) - end. +%%-------------------------------------------------------------------- +%% 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(stop, _From, State) -> + Reply = ok, + {stop, normal, 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({process_iq, From, To, IQ}, + #state{host = Host, + module = Module, + function = Function} = State) -> + process_iq(Host, Module, Function, From, To, IQ), + {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 +%%-------------------------------------------------------------------- diff --git a/src/odbc/pg.sql b/src/odbc/pg.sql index e8404b134..0db27d2c5 100644 --- a/src/odbc/pg.sql +++ b/src/odbc/pg.sql @@ -36,12 +36,12 @@ CREATE TABLE rostergroups ( CREATE INDEX pk_rosterg_user_jid ON rostergroups USING btree (username, jid); -;; To update from previous table definition: -; CREATE SEQUENCE spool_seq_seq; -; ALTER TABLE spool ADD COLUMN seq integer; -; ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq'); -; UPDATE spool SET seq = DEFAULT; -; ALTER TABLE spool ALTER COLUMN seq SET NOT NULL; +--- To update from previous table definition: +-- CREATE SEQUENCE spool_seq_seq; +-- ALTER TABLE spool ADD COLUMN seq integer; +-- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq'); +-- UPDATE spool SET seq = DEFAULT; +-- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL; CREATE TABLE spool ( username text NOT NULL,