From 820c78484aba666685304fb7fb5af8d85ce56cac Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 23 May 2005 00:30:29 +0000 Subject: [PATCH] * src/web/ejabberd_web_admin.erl: Updated CSS, added modules management (thanks to Sergei Golovan) * src/gen_mod.erl: Added function loaded_modules_with_opts/0, new API for module stopping (thanks to Sergei Golovan) * src/mod_muc/mod_muc.erl: Moved a process name to a macros, updated module stopping (thanks to Sergei Golovan) * src/mod_irc/mod_irc.erl: Likewise * src/mod_pubsub/mod_pubsub.erl: Likewise * src/mod_announce.erl: Updated module stopping (thanks to Sergei Golovan) * src/mod_echo.erl: Likewise * src/mod_offline.erl: Likewise SVN Revision: 354 --- ChangeLog | 14 +++++ src/gen_mod.erl | 39 +++++++++++- src/mod_announce.erl | 2 +- src/mod_echo.erl | 9 +-- src/mod_irc/mod_irc.erl | 11 ++-- src/mod_muc/mod_muc.erl | 9 +-- src/mod_offline.erl | 2 +- src/mod_pubsub/mod_pubsub.erl | 7 ++- src/web/ejabberd_web_admin.erl | 112 ++++++++++++++++++++++++++++++--- 9 files changed, 176 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 630ed1540..9633b9f17 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2005-05-23 Alexey Shchepin + * src/web/ejabberd_web_admin.erl: Updated CSS, added modules + management (thanks to Sergei Golovan) + + * src/gen_mod.erl: Added function loaded_modules_with_opts/0, new + API for module stopping (thanks to Sergei Golovan) + * src/mod_muc/mod_muc.erl: Moved a process name to a macros, + updated module stopping (thanks to Sergei Golovan) + * src/mod_irc/mod_irc.erl: Likewise + * src/mod_pubsub/mod_pubsub.erl: Likewise + * src/mod_announce.erl: Updated module stopping (thanks to Sergei + Golovan) + * src/mod_echo.erl: Likewise + * src/mod_offline.erl: Likewise + * src/web/ejabberd_http.erl: "Connection:" header value now matched case-insensitive, and returned to client, replaced duplicate is_space($\r) with is_space($\t) (thanks to Maxim diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 205c843c8..768869d52 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -17,6 +17,7 @@ get_opt/3, get_module_opt/3, loaded_modules/0, + loaded_modules_with_opts/0, get_hosts/2]). -export([behaviour_info/1]). @@ -52,11 +53,39 @@ stop_module(Module) -> case catch Module:stop() of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); + {wait, ProcList} when is_list(ProcList) -> + lists:foreach(fun wait_for_process/1, ProcList), + ets:delete(ejabberd_modules, Module), + ok; + {wait, Process} -> + wait_for_process(Process), + ets:delete(ejabberd_modules, Module), + ok; _ -> ets:delete(ejabberd_modules, Module), ok end. +wait_for_process(Process) -> + MonitorReference = erlang:monitor(process, Process), + wait_for_stop(Process, MonitorReference). + +wait_for_stop(Process, MonitorReference) -> + receive + {'DOWN', MonitorReference, _Type, _Object, _Info} -> + ok + after 5000 -> + catch exit(whereis(Process), kill), + wait_for_stop1(MonitorReference) + end. + +wait_for_stop1(MonitorReference) -> + receive + {'DOWN', MonitorReference, _Type, _Object, _Info} -> + ok + after 5000 -> + ok + end. get_opt(Opt, Opts) -> case lists:keysearch(Opt, 1, Opts) of @@ -86,7 +115,15 @@ get_module_opt(Module, Opt, Default) -> loaded_modules() -> ets:select(ejabberd_modules, - [{#ejabberd_module{_ = '_', module = '$1'}, [],['$1']}]). + [{#ejabberd_module{_ = '_', module = '$1'}, + [], + ['$1']}]). + +loaded_modules_with_opts() -> + ets:select(ejabberd_modules, + [{#ejabberd_module{_ = '_', module = '$1', opts = '$2'}, + [], + [{{'$1', '$2'}}]}]). get_hosts(Opts, Prefix) -> case catch gen_mod:get_opt(hosts, Opts) of diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 495eac260..b8581a376 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -70,7 +70,7 @@ stop() -> ejabberd_hooks:delete(sm_register_connection_hook, ?MODULE, send_motd, 50), exit(whereis(?PROCNAME), stop), - ok. + {wait, ?PROCNAME}. announce(From, To, Packet) -> case To of diff --git a/src/mod_echo.erl b/src/mod_echo.erl index f7a268f6a..b93bba912 100644 --- a/src/mod_echo.erl +++ b/src/mod_echo.erl @@ -17,12 +17,12 @@ -include("ejabberd.hrl"). -include("jlib.hrl"). - +-define(PROCNAME, ejabberd_mod_echo). start(Opts) -> %Host = gen_mod:get_opt(host, Opts), Host = gen_mod:get_opt(host, Opts, "echo." ++ ?MYNAME), - register(ejabberd_mod_echo, spawn(?MODULE, init, [Host])). + register(?PROCNAME, spawn(?MODULE, init, [Host])). init(Host) -> ejabberd_router:register_route(Host), @@ -41,5 +41,6 @@ loop(Host) -> end. stop() -> - ejabberd_mod_echo ! stop, - ok. + ?PROCNAME ! stop, + {wait, ?PROCNAME}. + diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl index 337d9761b..9b05876fd 100644 --- a/src/mod_irc/mod_irc.erl +++ b/src/mod_irc/mod_irc.erl @@ -24,6 +24,8 @@ -record(irc_connection, {jid_server_host, pid}). -record(irc_custom, {us_host, data}). +-define(PROCNAME, ejabberd_mod_irc). + start(Opts) -> iconv:start(), mnesia:create_table(irc_custom, @@ -33,7 +35,7 @@ start(Opts) -> Host = hd(Hosts), update_table(Host), Access = gen_mod:get_opt(access, Opts, all), - register(ejabberd_mod_irc, spawn(?MODULE, init, [Hosts, Access])). + register(?PROCNAME, spawn(?MODULE, init, [Hosts, Access])). init(Hosts, Access) -> catch ets:new(irc_connection, [named_table, @@ -172,11 +174,10 @@ do_route1(Host, From, To, Packet) -> end. - - stop() -> - ejabberd_mod_irc ! stop, - ok. + ?PROCNAME ! stop, + {wait, ?PROCNAME}. + closed_connection(Host, From, Server) -> ets:delete(irc_connection, {From, Server, Host}). diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl index e7ffe42db..f3c61e595 100644 --- a/src/mod_muc/mod_muc.erl +++ b/src/mod_muc/mod_muc.erl @@ -30,6 +30,7 @@ -record(muc_online_room, {name_host, pid}). -record(muc_registered, {us_host, nick}). +-define(PROCNAME, ejabberd_mod_muc). start(Opts) -> mnesia:create_table(muc_room, @@ -45,7 +46,7 @@ start(Opts) -> Access = gen_mod:get_opt(access, Opts, all), AccessCreate = gen_mod:get_opt(access_create, Opts, all), AccessAdmin = gen_mod:get_opt(access_admin, Opts, none), - register(ejabberd_mod_muc, + register(?PROCNAME, spawn(?MODULE, init, [Hosts, {Access, AccessCreate, AccessAdmin}])). @@ -252,12 +253,12 @@ do_route1(Host, Access, From, To, Packet) -> room_destroyed(Host, Room) -> - ejabberd_mod_muc ! {room_destroyed, {Room, Host}}, + ?PROCNAME ! {room_destroyed, {Room, Host}}, ok. stop() -> - ejabberd_mod_muc ! stop, - ok. + ?PROCNAME ! stop, + {wait, ?PROCNAME}. store_room(Host, Name, Opts) -> diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 3d7bd0e06..1c79d6a5d 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -89,7 +89,7 @@ stop() -> ejabberd_hooks:delete(remove_user, ?MODULE, remove_user, 50), exit(whereis(?PROCNAME), stop), - ok. + {wait, ?PROCNAME}. store_packet(From, To, Packet) -> Type = xml:get_tag_attr_s("type", Packet), diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 1823be44d..40cdabc44 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -35,6 +35,7 @@ subscription = none}). -record(item, {id, publisher, payload}). +-define(PROCNAME, ejabberd_mod_pubsub). start(Opts) -> mnesia:create_table(pubsub_node, @@ -45,7 +46,7 @@ start(Opts) -> update_table(Host), mnesia:add_table_index(pubsub_node, host_parent), ServedHosts = gen_mod:get_opt(served_hosts, Opts, []), - register(ejabberd_mod_pubsub, + register(?PROCNAME, proc_lib:spawn_link(?MODULE, init, [Hosts, ServedHosts, self()])). @@ -202,8 +203,8 @@ do_route(Host, From, To, Packet) -> stop() -> - ejabberd_mod_pubsub ! stop, - ok. + ?PROCNAME ! stop, + {wait, ?PROCNAME}. diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl index 754e89c37..dacbf7817 100644 --- a/src/web/ejabberd_web_admin.erl +++ b/src/web/ejabberd_web_admin.erl @@ -185,12 +185,12 @@ html>body #container { #navigation ul { position: absolute; - top: 55px; + top: 54px; left: 0; - padding: 0 1px 1px; + padding: 0 1px 1px 1px; margin: 0; font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 7.5pt; + font-size: 8pt; font-weight: bold; background: #d47911; width: 13em; @@ -206,9 +206,9 @@ html>body #container { #navigation ul li a { margin: 0; display: block; - padding: 0.25em 0.5em 0.25em 0.75em; + padding: 3px 6px 3px 9px; border-left: 1em solid #ffc78c; - border-top: 1px solid gray; + border-top: 1px solid #d47911; background: #ffe3c9; text-decoration: none; } @@ -257,7 +257,7 @@ input { input[type=submit] { font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 7pt; + font-size: 8pt; font-weight: bold; color: #ffffff; background-color: #fe8a00; @@ -1683,6 +1683,7 @@ get_node(Node, [], Query, Lang) -> [?LI([?ACT("db/", "DB Management")]), ?LI([?ACT("backup/", "Backup Management")]), ?LI([?ACT("ports/", "Listened Ports Management")]), + ?LI([?ACT("modules/", "Modules Management")]), ?LI([?ACT("stats/", "Statistics")]) ]), ?XAE("form", [{"method", "post"}], @@ -1825,6 +1826,28 @@ get_node(Node, ["ports"], Query, Lang) -> [node_ports_to_xhtml(NewPorts, Lang)]) ]; +get_node(Node, ["modules"], Query, Lang) -> + Modules = rpc:call(Node, gen_mod, loaded_modules_with_opts, []), + Res = case catch node_modules_parse_query(Node, Modules, Query) of + submitted -> + ok; + {'EXIT', Reason} -> + ?INFO_MSG("~p~n", [Reason]), + error; + _ -> + nothing + end, + NewModules = lists:sort(rpc:call(Node, gen_mod, loaded_modules_with_opts, [])), + [?XC("h1", ?T("Modules at ") ++ atom_to_list(Node))] ++ + case Res of + ok -> [?CT("submitted"), ?P]; + error -> [?CT("bad format"), ?P]; + nothing -> [] + end ++ + [?XAE("form", [{"method", "post"}], + [node_modules_to_xhtml(NewModules, Lang)]) + ]; + get_node(Node, ["stats"], Query, Lang) -> UpTime = rpc:call(Node, erlang, statistics, [wall_clock]), UpTimeS = io_lib:format("~.3f", [element(1, UpTime)/1000]), @@ -2041,13 +2064,13 @@ node_ports_parse_query(Node, Ports, Query) -> Module = list_to_atom(SModule), {ok, Tokens, _} = erl_scan:string(SOpts ++ "."), {ok, Opts} = erl_parse:parse_term(Tokens), - ejabberd_listener:delete_listener(Port), - ejabberd_listener:add_listener(Port, Module, Opts), + rpc:call(Node, ejabberd_listener, delete_listener, [Port]), + rpc:call(Node, ejabberd_listener, add_listener, [Port, Module, Opts]), throw(submitted); _ -> case lists:keysearch("delete" ++ SPort, 1, Query) of {value, _} -> - ejabberd_listener:delete_listener(Port), + rpc:call(Node, ejabberd_listener, delete_listener, [Port]), throw(submitted); _ -> ok @@ -2066,12 +2089,81 @@ node_ports_parse_query(Node, Ports, Query) -> Module = list_to_atom(SModule), {ok, Tokens, _} = erl_scan:string(SOpts ++ "."), {ok, Opts} = erl_parse:parse_term(Tokens), - ejabberd_listener:add_listener(Port, Module, Opts), + rpc:call(Node, ejabberd_listener, add_listener, [Port, Module, Opts]), throw(submitted); _ -> ok end. +node_modules_to_xhtml(Modules, Lang) -> + ?XAE("table", [], + [?XE("thead", + [?XE("tr", + [?XCT("td", "Module"), + ?XCT("td", "Options") + ])]), + ?XE("tbody", + lists:map( + fun({Module, Opts} = E) -> + SModule = atom_to_list(Module), + ID = term_to_id(E), + ?XE("tr", + [?XC("td", SModule), + ?XE("td", [?INPUTS("text", "opts" ++ SModule, + term_to_string(Opts), "40")]), + ?XE("td", [?INPUTT("submit", "restart" ++ SModule, + "Restart")]), + ?XE("td", [?INPUTT("submit", "stop" ++ SModule, + "Stop")]) + ] + ) + end, Modules) ++ + [?XE("tr", + [?XE("td", [?INPUT("text", "modulenew", "")]), + ?XE("td", [?INPUTS("text", "optsnew", "", "40")]), + ?XAE("td", [{"colspan", "2"}], + [?INPUTT("submit", "start", "Start")]) + ] + )] + )]). + +node_modules_parse_query(Node, Modules, Query) -> + lists:foreach( + fun({Module, _Opts1}) -> + SModule = atom_to_list(Module), + case lists:keysearch("restart" ++ SModule, 1, Query) of + {value, _} -> + {value, {_, SOpts}} = + lists:keysearch("opts" ++ SModule, 1, Query), + {ok, Tokens, _} = erl_scan:string(SOpts ++ "."), + {ok, Opts} = erl_parse:parse_term(Tokens), + rpc:call(Node, gen_mod, stop_module, [Module]), + rpc:call(Node, gen_mod, start_module, [Module, Opts]), + throw(submitted); + _ -> + case lists:keysearch("stop" ++ SModule, 1, Query) of + {value, _} -> + rpc:call(Node, gen_mod, stop_module, [Module]), + throw(submitted); + _ -> + ok + end + end + end, Modules), + case lists:keysearch("start", 1, Query) of + {value, _} -> + {{value, {_, SModule}}, + {value, {_, SOpts}}} = + {lists:keysearch("modulenew", 1, Query), + lists:keysearch("optsnew", 1, Query)}, + Module = list_to_atom(SModule), + {ok, Tokens, _} = erl_scan:string(SOpts ++ "."), + {ok, Opts} = erl_parse:parse_term(Tokens), + rpc:call(Node, gen_mod, start_module, [Module, Opts]), + throw(submitted); + _ -> + ok + end. pretty_print(El) ->