From 814b80c644fa425990dc63875e1da841d698b0fa Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 23 Jul 2016 01:08:05 +0200 Subject: [PATCH] Preserve PID for offline sessions Don't set the PID to 'undefined' when a session goes offline, as this looses the information which node created the session table entry. Fixes #1196. --- include/ejabberd_sm.hrl | 4 ++-- src/ejabberd_sm.erl | 42 +++++++++++++++++++++++------------------ src/mod_admin_extra.erl | 19 +++++++++++-------- src/mod_configure.erl | 36 +++++++++++++++++++++-------------- 4 files changed, 59 insertions(+), 42 deletions(-) diff --git a/include/ejabberd_sm.hrl b/include/ejabberd_sm.hrl index 38298d66a..f86ab1c15 100644 --- a/include/ejabberd_sm.hrl +++ b/include/ejabberd_sm.hrl @@ -1,9 +1,9 @@ -ifndef(EJABBERD_SM_HRL). -define(EJABBERD_SM_HRL, true). --record(session, {sid, usr, us, priority, info}). +-record(session, {sid, usr, us, priority, info = []}). -record(session_counter, {vhost, count}). --type sid() :: {erlang:timestamp(), pid()} | {erlang:timestamp(), undefined}. +-type sid() :: {erlang:timestamp(), pid()}. -type ip() :: {inet:ip_address(), inet:port_number()} | undefined. -type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()} | {oor, boolean()} | {auth_module, atom()} diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 8d94bc6aa..16e0f9114 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -270,25 +270,28 @@ get_session_pid(User, Server, Resource) -> -spec set_offline_info(sid(), binary(), binary(), binary(), info()) -> ok. -set_offline_info({Time, _Pid}, User, Server, Resource, Info) -> - SID = {Time, undefined}, +set_offline_info(SID, User, Server, Resource, Info) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), - set_session(SID, LUser, LServer, LResource, undefined, Info). + set_session(SID, LUser, LServer, LResource, undefined, [offline | Info]). -spec get_offline_info(erlang:timestamp(), binary(), binary(), binary()) -> none | info(). get_offline_info(Time, User, Server, Resource) -> - SID = {Time, undefined}, LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), case Mod:get_sessions(LUser, LServer, LResource) of - [#session{sid = SID, info = Info}] -> - Info; + [#session{sid = {Time, _}, info = Info}] -> + case proplists:get_bool(offline, Info) of + true -> + Info; + false -> + none + end; _ -> none end. @@ -425,11 +428,12 @@ set_session(SID, User, Server, Resource, Priority, Info) -> -spec online([#session{}]) -> [#session{}]. online(Sessions) -> - lists:filter(fun(#session{sid = {_, undefined}}) -> - false; - (_) -> - true - end, Sessions). + lists:filter(fun is_online/1, Sessions). + +-spec is_online(#session{}) -> boolean(). + +is_online(#session{info = Info}) -> + not proplists:get_bool(offline, Info). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -678,15 +682,17 @@ check_for_sessions_to_replace(User, Server, Resource) -> check_max_sessions(LUser, LServer). check_existing_resources(LUser, LServer, LResource) -> - SIDs = get_resource_sessions(LUser, LServer, LResource), - if SIDs == [] -> ok; + Mod = get_sm_backend(LServer), + Ss = Mod:get_sessions(LUser, LServer, LResource), + {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss), + lists:foreach(fun(#session{sid = S}) -> + Mod:delete_session(LUser, LServer, LResource, S) + end, OfflineSs), + if OnlineSs == [] -> ok; true -> + SIDs = [SID || #session{sid = SID} <- OnlineSs], MaxSID = lists:max(SIDs), - lists:foreach(fun ({_, undefined} = S) -> - Mod = get_sm_backend(LServer), - Mod:delete_session(LUser, LServer, LResource, - S); - ({_, Pid} = S) when S /= MaxSID -> + lists:foreach(fun ({_, Pid} = S) when S /= MaxSID -> Pid ! replaced; (_) -> ok end, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 8f6724281..2ad1cc28e 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -863,12 +863,15 @@ connected_users_vhost(Host) -> %% Code copied from ejabberd_sm.erl and customized dirty_get_sessions_list2() -> - mnesia:dirty_select( - session, - [{#session{usr = '$1', sid = {'$2', '$3'}, priority = '$4', info = '$5', - _ = '_'}, - [{is_pid, '$3'}], - [['$1', {{'$2', '$3'}}, '$4', '$5']]}]). + Ss = mnesia:dirty_select( + session, + [{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', + _ = '_'}, + [], + [['$1', '$2', '$3', '$4']]}]), + lists:filter(fun([_USR, _SID, _Priority, Info]) -> + not proplists:get_bool(offline, Info) + end, Ss). %% Make string more print-friendly stringize(String) -> @@ -903,8 +906,8 @@ user_sessions_info(User, Host) -> {'EXIT', _Reason} -> []; Ss -> - lists:filter(fun(#session{sid = {_, Pid}}) -> - is_pid(Pid) + lists:filter(fun(#session{info = Info}) -> + not proplists:get_bool(offline, Info) end, Ss) end, lists:map( diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 97c944842..d0e0166a4 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -1917,21 +1917,29 @@ set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>), Xmlelement = ?SERRT_POLICY_VIOLATION(Lang, <<"has been kicked">>), case JID#jid.lresource of <<>> -> - SIDs = mnesia:dirty_select(session, - [{#session{sid = {'$1', '$2'}, - usr = {LUser, LServer, '_'}, - _ = '_'}, - [{is_pid, '$2'}], - [{{'$1', '$2'}}]}]), - [Pid ! {kick, kicked_by_admin, Xmlelement} || {_, Pid} <- SIDs]; + SIs = mnesia:dirty_select(session, + [{#session{usr = {LUser, LServer, '_'}, + sid = '$1', + info = '$2', + _ = '_'}, + [], [{{'$1', '$2'}}]}]), + Pids = [P || {{_, P}, Info} <- SIs, + not proplists:get_bool(offline, Info)], + lists:foreach(fun(Pid) -> + Pid ! {kick, kicked_by_admin, Xmlelement} + end, Pids); R -> - [{_, Pid}] = mnesia:dirty_select(session, - [{#session{sid = {'$1', '$2'}, - usr = {LUser, LServer, R}, - _ = '_'}, - [{is_pid, '$2'}], - [{{'$1', '$2'}}]}]), - Pid ! {kick, kicked_by_admin, Xmlelement} + [{{_, Pid}, Info}] = mnesia:dirty_select( + session, + [{#session{usr = {LUser, LServer, R}, + sid = '$1', + info = '$2', + _ = '_'}, + [], [{{'$1', '$2'}}]}]), + case proplists:get_bool(offline, Info) of + true -> ok; + false -> Pid ! {kick, kicked_by_admin, Xmlelement} + end end, {result, []}; set_form(From, Host, -- 2.40.0