diff options
Diffstat (limited to 'src/pubsub_debug.erl')
-rw-r--r-- | src/pubsub_debug.erl | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/pubsub_debug.erl b/src/pubsub_debug.erl new file mode 100644 index 000000000..5734a0932 --- /dev/null +++ b/src/pubsub_debug.erl @@ -0,0 +1,283 @@ +-module(pubsub_debug). + +-author('christophe.romain@process-one.net'). + +-include("pubsub.hrl"). + +-compile(export_all). + +-spec(nodeid/2 :: +( + Host :: mod_pubsub:host(), + Node :: mod_pubsub:nodeId()) + -> mod_pubsub:nodeIdx() | 0 +). +nodeid(Host, Node) -> + case mnesia:dirty_read({pubsub_node, {Host, Node}}) of + [N] -> nodeid(N); + _ -> 0 + end. + +nodeid(N) -> N#pubsub_node.id. + +-spec(nodeids/0 :: () -> [0 | mod_pubsub:nodeIdx()]). + +nodeids() -> + [nodeid(Host, Node) + || {Host, Node} <- mnesia:dirty_all_keys(pubsub_node)]. + +-spec(nodeids_by_type/1 :: +( + Type :: binary()) + -> [0 | mod_pubsub:nodeIdx()] +). +nodeids_by_type(Type) -> + [nodeid(N) + || N + <- mnesia:dirty_match_object(#pubsub_node{type = Type, + _ = '_'})]. + +nodeids_by_option(Key, Value) -> + [nodeid(N) + || N + <- mnesia:dirty_match_object(#pubsub_node{_ = '_'}), + lists:member({Key, Value}, N#pubsub_node.options)]. + +nodeids_by_owner(JID) -> + [nodeid(N) + || N + <- mnesia:dirty_match_object(#pubsub_node{_ = '_'}), + lists:member(JID, N#pubsub_node.owners)]. + +nodes_by_id(I) -> + mnesia:dirty_match_object(#pubsub_node{id = I, + _ = '_'}). + +nodes() -> + [element(2, element(2, N)) + || N + <- mnesia:dirty_match_object(#pubsub_node{_ = '_'})]. + +state(JID, NodeId) -> + case mnesia:dirty_read({pubsub_state, {JID, NodeId}}) of + [S] -> S; + _ -> undefined + end. + +states(NodeId) -> + mnesia:dirty_index_read(pubsub_state, NodeId, + #pubsub_state.nodeidx). + +stateid(S) -> element(1, S#pubsub_state.stateid). + +stateids(NodeId) -> [stateid(S) || S <- states(NodeId)]. + +states_by_jid(JID) -> + mnesia:dirty_match_object(#pubsub_state{stateid = + {JID, '_'}, + _ = '_'}). + +item(ItemId, NodeId) -> + case mnesia:dirty_read({pubsub_item, {ItemId, NodeId}}) + of + [I] -> I; + _ -> undefined + end. + +items(NodeId) -> + mnesia:dirty_index_read(pubsub_item, NodeId, + #pubsub_item.nodeidx). + +itemid(I) -> element(1, I#pubsub_item.itemid). + +itemids(NodeId) -> [itemid(I) || I <- items(NodeId)]. + +items_by_id(ItemId) -> + mnesia:dirty_match_object(#pubsub_item{itemid = + {ItemId, '_'}, + _ = '_'}). + +affiliated(NodeId) -> + [stateid(S) + || S <- states(NodeId), + S#pubsub_state.affiliation =/= none]. + +subscribed(NodeId) -> + [stateid(S) + || S <- states(NodeId), + S#pubsub_state.subscriptions =/= []]. + +offline_subscribers(NodeId) -> + lists:filter(fun ({U, S, <<"">>}) -> + ejabberd_sm:get_user_resources(U, S) == []; + ({U, S, R}) -> + not + lists:member(R, ejabberd_sm:get_user_resources(U, S)) + end, + subscribed(NodeId)). + + +owners(NodeId) -> + [stateid(S) + || S <- states(NodeId), + S#pubsub_state.affiliation == owner]. + +orphan_items(NodeId) -> + itemids(NodeId) -- + lists:foldl(fun (S, A) -> A ++ S#pubsub_state.items end, + [], states(NodeId)). + +newer_items(NodeId, Seconds) -> + Now = calendar:universal_time(), + Oldest = calendar:seconds_to_daystime(Seconds), + [itemid(I) + || I <- items(NodeId), + calendar:time_difference(calendar:now_to_universal_time(element(1, + I#pubsub_item.modification)), + Now) + < Oldest]. + +older_items(NodeId, Seconds) -> + Now = calendar:universal_time(), + Oldest = calendar:seconds_to_daystime(Seconds), + [itemid(I) + || I <- items(NodeId), + calendar:time_difference(calendar:now_to_universal_time(element(1, + I#pubsub_item.modification)), + Now) + > Oldest]. + +orphan_nodes() -> + [I || I <- nodeids(), owners(I) == []]. + +duplicated_nodes() -> + L = nodeids(), + lists:usort(L -- lists:seq(1, lists:max(L))). + +node_options(NodeId) -> + [N] = mnesia:dirty_match_object(#pubsub_node{id = + NodeId, + _ = '_'}), + N#pubsub_node.options. + +update_node_options(Key, Value, NodeId) -> + [N] = mnesia:dirty_match_object(#pubsub_node{id = + NodeId, + _ = '_'}), + NewOptions = lists:keyreplace(Key, 1, + N#pubsub_node.options, {Key, Value}), + mnesia:dirty_write(N#pubsub_node{options = NewOptions}). + +check() -> + mnesia:transaction(fun () -> + case mnesia:read({pubsub_index, node}) of + [Idx] -> + Free = Idx#pubsub_index.free, + Last = Idx#pubsub_index.last, + Allocated = lists:seq(1, Last) -- Free, + NodeIds = mnesia:foldl(fun (N, A) -> + [nodeid(N) + | A] + end, + [], pubsub_node), + StateIds = lists:usort(mnesia:foldl(fun (S, + A) -> + [element(2, + S#pubsub_state.stateid) + | A] + end, + [], + pubsub_state)), + ItemIds = lists:usort(mnesia:foldl(fun (I, + A) -> + [element(2, + I#pubsub_item.itemid) + | A] + end, + [], + pubsub_item)), + BadNodeIds = NodeIds -- Allocated, + BadStateIds = StateIds -- NodeIds, + BadItemIds = ItemIds -- NodeIds, + Lost = Allocated -- NodeIds, + [{bad_nodes, + [N#pubsub_node.nodeid + || N + <- lists:flatten([mnesia:match_object(#pubsub_node{id + = + I, + _ + = + '_'}) + || I + <- BadNodeIds])]}, + {bad_states, + lists:foldl(fun (N, A) -> + A ++ + [{I, N} + || I + <- stateids(N)] + end, + [], BadStateIds)}, + {bad_items, + lists:foldl(fun (N, A) -> + A ++ + [{I, N} + || I + <- itemids(N)] + end, + [], BadItemIds)}, + {lost_idx, Lost}, + {orphaned, + [I || I <- NodeIds, owners(I) == []]}, + {duplicated, + lists:usort(NodeIds -- + lists:seq(1, + lists:max(NodeIds)))}]; + _ -> no_index + end + end). + +rebuild_index() -> + mnesia:transaction(fun () -> + NodeIds = mnesia:foldl(fun (N, A) -> + [nodeid(N) | A] + end, + [], pubsub_node), + Last = lists:max(NodeIds), + Free = lists:seq(1, Last) -- NodeIds, + mnesia:write(#pubsub_index{index = node, + last = Last, + free = Free}) + end). + +pep_subscriptions(LUser, LServer, LResource) -> + case ejabberd_sm:get_session_pid(LUser, LServer, LResource) of + C2SPid when is_pid(C2SPid) -> + case catch ejabberd_c2s:get_subscribed(C2SPid) of + Contacts when is_list(Contacts) -> + lists:map(fun ({U, S, _}) -> + io_lib:format("~s@~s", [U, S]) + end, + Contacts); + _ -> [] + end; + _ -> [] + end. + +purge_offline_subscriptions() -> + lists:foreach(fun (K) -> + [N] = mnesia:dirty_read({pubsub_node, K}), + I = element(3, N), + lists:foreach(fun (JID) -> + case mnesia:dirty_read({pubsub_state, {JID, I}}) of + [{pubsub_state, K, _, _, _, [{subscribed, S}]}] -> + mnesia:dirty_delete({pubsub_subscription, S}); + _ -> + ok + end, + mnesia:dirty_delete({pubsub_state, {JID, I}}) + end, + offline_subscribers(I)) + end, + mnesia:dirty_all_keys(pubsub_node)). |