From 7a3ce2e81a8feb37115a4c17ef3daaa0a09c61c8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 4 Jan 2010 20:35:47 +0000 Subject: [PATCH] Command to stop ejabberd informing users, with grace period, and kindly (EJAB-1112) SVN Revision: 2860 --- src/ejabberd_admin.erl | 55 +++++++++++++++++++++++++++++++++++++++ src/ejabberd_listener.erl | 9 +++++++ src/mod_announce.erl | 24 +++++++++++++++++ src/mod_muc/mod_muc.erl | 2 +- 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index e2c95da91..db305b8e5 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -30,6 +30,7 @@ -export([start/0, stop/0, %% Server status/0, reopen_log/0, + stop_kindly/2, send_service_message_all_mucs/2, %% Accounts register/3, unregister/2, registered_users/1, @@ -79,6 +80,11 @@ commands() -> desc = "Reopen the log files", module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, + #ejabberd_commands{name = stop_kindly, tags = [server], + desc = "Inform users and rooms, wait, and stop the server", + module = ?MODULE, function = stop_kindly, + args = [{delay, integer}, {announcement, string}], + result = {res, rescode}}, #ejabberd_commands{name = get_loglevel, tags = [logs, server], desc = "Get the current loglevel", module = ejabberd_loglevel, function = get, @@ -210,6 +216,55 @@ get_sasl_error_logger_type () -> _ -> all end. +%%% +%%% Stop Kindly +%%% + +stop_kindly(DelaySeconds, AnnouncementText) -> + Subject = io_lib:format("Server stop in ~p seconds!", [DelaySeconds]), + WaitingDesc = io_lib:format("Waiting ~p seconds", [DelaySeconds]), + Steps = [ + {"Stopping ejabberd port listeners", + ejabberd_listener, stop_listeners, []}, + {"Sending announcement to connected users", + mod_announce, send_announcement_to_all, + [?MYNAME, Subject, AnnouncementText]}, + {"Sending service message to MUC rooms", + ejabberd_admin, send_service_message_all_mucs, + [Subject, AnnouncementText]}, + {WaitingDesc, timer, sleep, [DelaySeconds * 1000]}, + {"Stopping ejabberd", application, stop, [ejabberd]}, + {"Stopping Mnesia", mnesia, stop, []}, + {"Stopping Erlang node", init, stop, []} + ], + NumberLast = length(Steps), + TimestampStart = calendar:datetime_to_gregorian_seconds({date(), time()}), + lists:foldl( + fun({Desc, Mod, Func, Args}, NumberThis) -> + SecondsDiff = + calendar:datetime_to_gregorian_seconds({date(), time()}) + - TimestampStart, + io:format("[~p/~p ~ps] ~s... ", + [NumberThis, NumberLast, SecondsDiff, Desc]), + Result = apply(Mod, Func, Args), + io:format("~p~n", [Result]), + NumberThis+1 + end, + 1, + Steps), + ok. + +send_service_message_all_mucs(Subject, AnnouncementText) -> + Message = io_lib:format("~s~n~s", [Subject, AnnouncementText]), + lists:foreach( + fun(ServerHost) -> + MUCHost = gen_mod:get_module_opt_host( + ServerHost, mod_muc, "conference.@HOST@"), + MUCHostB = list_to_binary(MUCHost), + mod_muc:broadcast_service_message(MUCHostB, Message) + end, + ?MYHOSTS). + %%% %%% Account management %%% diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 5d0a55c98..465b311c1 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -31,6 +31,7 @@ init/3, start_listeners/0, start_listener/3, + stop_listeners/0, stop_listener/2, parse_listener_portip/2, add_listener/3, @@ -304,6 +305,14 @@ start_listener_sup(Port, Module, Opts) -> [?MODULE]}, supervisor:start_child(ejabberd_listeners, ChildSpec). +stop_listeners() -> + Ports = ejabberd_config:get_local_option(listen), + lists:foreach( + fun({PortIpNetp, Module, _Opts}) -> + delete_listener(PortIpNetp, Module) + end, + Ports). + %% @spec (PortIP, Module) -> ok %% where %% PortIP = {Port, IPT | IPS} diff --git a/src/mod_announce.erl b/src/mod_announce.erl index b12000067..6c0873962 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -40,6 +40,7 @@ disco_identity/5, disco_features/5, disco_items/5, + send_announcement_to_all/3, announce_commands/4, announce_items/4]). @@ -857,6 +858,29 @@ get_stored_motd(LServer) -> {"", ""} end. +%% This function is similar to others, but doesn't perform any ACL verification +send_announcement_to_all(Host, SubjectS, BodyS) -> + SubjectEls = if SubjectS /= [] -> + [#xmlel{ns = ?NS_JABBER_CLIENT, name = 'subject', children = + [#xmlcdata{cdata = list_to_binary(SubjectS)}]}]; + true -> + [] + end, + BodyEls = if BodyS /= [] -> + [#xmlel{ns = ?NS_JABBER_CLIENT, name = 'body', children = + [#xmlcdata{cdata = list_to_binary(BodyS)}]}]; + true -> + [] + end, + Packet = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', attrs = [?XMLATTR('type', <<"normal">>)], children = SubjectEls ++ BodyEls}, + Sessions = ejabberd_sm:dirty_get_sessions_list(), + Local = exmpp_jid:make(Host), + lists:foreach( + fun({U, S, R}) -> + Dest = exmpp_jid:make(U, S, R), + ejabberd_router:route(Local, Dest, Packet) + end, Sessions). + %%------------------------------------------------------------------------- update_tables() -> diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl index 7908be557..91bfe6445 100644 --- a/src/mod_muc/mod_muc.erl +++ b/src/mod_muc/mod_muc.erl @@ -40,6 +40,7 @@ forget_room/2, create_room/5, process_iq_disco_items/4, + broadcast_service_message/2, can_use_nick/3]). %% gen_server callbacks @@ -869,7 +870,6 @@ clean_table_from_bad_node(Node, Host) -> end, mnesia:transaction(F). - update_tables(Host) -> update_muc_room_table(Host), update_muc_registered_table(Host).