diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl index 7d5ae36c9..de2333558 100644 --- a/src/ejabberd_update.erl +++ b/src/ejabberd_update.erl @@ -28,13 +28,15 @@ -author('alexey@process-one.net'). %% API --export([update/0, update_info/0]). +-export([update/0, update/1, update_info/0]). -include("ejabberd.hrl"). %%==================================================================== %% API %%==================================================================== + +%% Update all the modified modules update() -> case update_info() of {ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} -> @@ -48,48 +50,91 @@ update() -> {error, Reason} end. -update_info() -> - Dir = filename:dirname(code:which(ejabberd)), - case file:list_dir(Dir) of - {ok, Files} -> - Beams = [list_to_atom(filename:rootname(FN)) || - FN <- Files, lists:suffix(".beam", FN)], - UpdatedBeams = - lists:filter( - fun(Module) -> - {ok, {Module, NewVsn}} = - beam_lib:version(code:which(Module)), - case code:is_loaded(Module) of - {file, _} -> - Attrs = Module:module_info(attributes), - {value, {vsn, CurVsn}} = - lists:keysearch(vsn, 1, Attrs), - NewVsn /= CurVsn; - false -> - false - end - end, Beams), - ?INFO_MSG("beam files: ~p~n", [UpdatedBeams]), - Script = make_script(UpdatedBeams), - ?INFO_MSG("script: ~p~n", [Script]), - LowLevelScript = make_low_level_script(UpdatedBeams, Script), - ?INFO_MSG("low level script: ~p~n", [LowLevelScript]), - Check = - release_handler_1:check_script( - LowLevelScript, +%% Update only the specified modules +update(ModulesToUpdate) -> + case update_info() of + {ok, Dir, UpdatedBeamsAll, _Script, _LowLevelScript, _Check} -> + UpdatedBeamsNow = + [A || A <- UpdatedBeamsAll, B <- ModulesToUpdate, A == B], + {_, LowLevelScript, _} = build_script(Dir, UpdatedBeamsNow), + Eval = + release_handler_1:eval_script( + LowLevelScript, [], [{ejabberd, "", filename:join(Dir, "..")}]), - ?INFO_MSG("check: ~p~n", [Check]), - {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}; + ?INFO_MSG("eval: ~p~n", [Eval]), + Eval; {error, Reason} -> {error, Reason} end. +%% Get information about the modified modules +update_info() -> + Dir = filename:dirname(code:which(ejabberd)), + case file:list_dir(Dir) of + {ok, Files} -> + update_info(Dir, Files); + {error, Reason} -> + {error, Reason} + end. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -%% From systools.hrl +update_info(Dir, Files) -> + Beams = lists:sort(get_beams(Files)), + UpdatedBeams = get_updated_beams(Beams), + ?INFO_MSG("beam files: ~p~n", [UpdatedBeams]), + {Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams), + {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}. + +get_beams(Files) -> + [list_to_atom(filename:rootname(FN)) + || FN <- Files, lists:suffix(".beam", FN)]. + +%% Return only the beams that have different version +get_updated_beams(Beams) -> + lists:filter( + fun(Module) -> + NewVsn = get_new_version(Module), + case code:is_loaded(Module) of + {file, _} -> + CurVsn = get_current_version(Module), + (NewVsn /= CurVsn + andalso NewVsn /= unknown_version); + false -> + false + end + end, Beams). + +get_new_version(Module) -> + Path = code:which(Module), + VersionRes = beam_lib:version(Path), + case VersionRes of + {ok, {Module, NewVsn}} -> NewVsn; + %% If a m1.erl has -module("m2"): + _ -> unknown_version + end. + +get_current_version(Module) -> + Attrs = Module:module_info(attributes), + {value, {vsn, CurVsn}} = lists:keysearch(vsn, 1, Attrs), + CurVsn. + +%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check} +build_script(Dir, UpdatedBeams) -> + Script = make_script(UpdatedBeams), + ?INFO_MSG("script: ~p~n", [Script]), + LowLevelScript = make_low_level_script(UpdatedBeams, Script), + ?INFO_MSG("low level script: ~p~n", [LowLevelScript]), + Check = + release_handler_1:check_script( + LowLevelScript, + [{ejabberd, "", filename:join(Dir, "..")}]), + ?INFO_MSG("check: ~p~n", [Check]), + {Script, LowLevelScript, Check}. + +%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl -record(application, {name, %% Name of the application, atom(). type = permanent, %% Application start type, atom(). diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl index 0b74a7ee0..50f9499e9 100644 --- a/src/web/ejabberd_web_admin.erl +++ b/src/web/ejabberd_web_admin.erl @@ -39,6 +39,12 @@ -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). +-define(INPUTATTRS(Type, Name, Value, Attrs), + ?XA("input", Attrs ++ + [{"type", Type}, + {"name", Name}, + {"value", Value}])). + process(["doc", LocalFile], _Request) -> DocPath = case os:getenv("EJABBERD_DOC_PATH") of @@ -145,6 +151,8 @@ make_xhtml(Els, Host, Node, Lang) -> [?XCT("title", "ejabberd Web Admin"), {xmlelement, "meta", [{"http-equiv", "Content-Type"}, {"content", "text/html; charset=utf-8"}], []}, + {xmlelement, "script", [{"src", Base ++ "/additions.js"}, + {"type", "text/javascript"}], [?C(" ")]}, {xmlelement, "link", [{"href", Base ++ "favicon.ico"}, {"type", "image/x-icon"}, {"rel", "shortcut icon"}], []}, @@ -184,6 +192,24 @@ get_base_path(Host, cluster) -> "/admin/server/" ++ Host ++ "/"; get_base_path(global, Node) -> "/admin/node/" ++ atom_to_list(Node) ++ "/"; get_base_path(Host, Node) -> "/admin/server/" ++ Host ++ "/node/" ++ atom_to_list(Node) ++ "/". +additions_js() -> +" +function selectAll() { + for(i=0;i Base = get_base_path(Host, cluster), " @@ -519,6 +545,10 @@ h3 { padding-left: 10px; } +#content ul.nolistyle>li { + list-style-type: none; +} + #content li.big { font-size: 10pt; } @@ -640,6 +670,9 @@ process_admin(_Host, #request{path = ["logo.png"]}) -> process_admin(_Host, #request{path = ["logo-fill.png"]}) -> {200, [{"Content-Type", "image/png"}, last_modified(), cache_control_public()], logo_fill()}; +process_admin(_Host, #request{path = ["additions.js"]}) -> + {200, [{"Content-Type", "text/javascript"}, last_modified(), cache_control_public()], additions_js()}; + process_admin(Host, #request{path = ["acls-raw"], q = Query, @@ -1927,9 +1960,28 @@ get_node(global, Node, ["update"], Query, Lang) -> [] -> ?CT("None"); _ -> - ?XE("ul", - [?LI([?C(atom_to_list(Beam))]) || - Beam <- UpdatedBeams]) + BeamsLis = + lists:map( + fun(Beam) -> + BeamString = atom_to_list(Beam), + ?LI([ + ?INPUT("checkbox", "selected", BeamString), + %%?XA("input", [{"checked", ""}, %% Selected by default + %% {"type", "checkbox"}, + %% {"name", "selected"}, + %% {"value", BeamString}]), + ?C(BeamString)]) + end, + UpdatedBeams), + SelectButtons = + [?BR, + ?INPUTATTRS("button", "selectall", "Select All", + [{"onClick", "selectAll()"}]), + ?C(" "), + ?INPUTATTRS("button", "unselectall", "Unselect All", + [{"onClick", "unSelectAll()"}])], + %%?XE("ul", BeamsLis) + ?XAE("ul", [{"class", "nolistyle"}], BeamsLis ++ SelectButtons) end, FmtScript = ?XC("pre", io_lib:format("~p", [Script])), FmtLowLevelScript = ?XC("pre", io_lib:format("~p", [LowLevelScript])), @@ -1946,6 +1998,7 @@ get_node(global, Node, ["update"], Query, Lang) -> ?XCT("h3", "Update script"), FmtScript, ?XCT("h3", "Low level update script"), FmtLowLevelScript, ?XCT("h3", "Script check"), ?XC("pre", atom_to_list(Check)), + ?BR, ?INPUTT("submit", "update", "Update") ]) ]; @@ -2268,7 +2321,9 @@ node_modules_parse_query(Host, Node, Modules, Query) -> node_update_parse_query(Node, Query) -> case lists:keysearch("update", 1, Query) of {value, _} -> - case rpc:call(Node, ejabberd_update, update, []) of + ModulesToUpdateStrings = proplists:get_all_values("selected",Query), + ModulesToUpdate = [list_to_atom(M) || M <- ModulesToUpdateStrings], + case rpc:call(Node, ejabberd_update, update, [ModulesToUpdate]) of {ok, _} -> ok; {error, Error} ->