25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-18 17:24:31 +01:00
xmpp.chapril.org-ejabberd/src/ejabberd_update.erl
Holger Weiss 3d9a5a1635 Fix 'update' command output
So far, ejabberd_update:update/0 returned the return value of
release_handler_1:eval_script/1.  That function returns the list of
updated but unpurged modules, i.e., modules where one or more processes
are still running an old version of the code.  Since commit
5a34020d23, the ejabberd 'update' command
assumes that value to be the list of updated modules instead.  As
that seems more useful, modify ejabberd_update:update/0 accordingly.
This fixes the 'update' command output.
2024-09-14 21:54:01 +02:00

251 lines
8.5 KiB
Erlang

%%%-------------------------------------------------------------------
%%% File : ejabberd_update.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : ejabberd code updater
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2024 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%-------------------------------------------------------------------
-module(ejabberd_update).
-author('alexey@process-one.net').
%% API
-export([update/0, update/1, update_info/0]).
-include("logger.hrl").
%%====================================================================
%% API
%%====================================================================
%% Update all the modified modules
update() ->
case update_info() of
{ok, Dir, UpdatedBeams, _Script, LowLevelScript, _Check} ->
case eval_script(
LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]) of
{ok, _} ->
?DEBUG("Updated: ~p~n", [UpdatedBeams]),
{ok, UpdatedBeams};
Eval ->
?DEBUG("Eval: ~p~n", [Eval]),
Eval
end;
{error, Reason} ->
{error, Reason}
end.
%% 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),
case eval_script(
LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]) of
{ok, _} ->
?DEBUG("Updated: ~p~n", [UpdatedBeamsNow]),
{ok, UpdatedBeamsNow};
Eval ->
?DEBUG("Eval: ~p~n", [Eval]),
Eval
end;
{error, Reason} ->
{error, Reason}
end.
eval_script(Script, Apps, LibDirs) ->
release_handler_1:eval_script(Script, Apps, LibDirs, [], []).
%% 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
%%--------------------------------------------------------------------
update_info(Dir, Files) ->
Beams = lists:sort(get_beams(Files)),
UpdatedBeams = get_updated_beams(Beams),
?DEBUG("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),
case lists:keysearch(vsn, 1, Attrs) of
{value, {vsn, CurVsn}} -> CurVsn;
_ -> unknown_version
end.
%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check}
build_script(Dir, UpdatedBeams) ->
Script = make_script(UpdatedBeams),
LowLevelScript = make_low_level_script(UpdatedBeams, Script),
Check =
release_handler_1:check_script(
LowLevelScript,
[{ejabberd, "", filename:join(Dir, "..")}]),
Check1 = case Check of
{ok, []} ->
?DEBUG("Script: ~p~n", [Script]),
?DEBUG("Low level script: ~p~n", [LowLevelScript]),
?DEBUG("Check: ~p~n", [Check]),
ok;
_ ->
?ERROR_MSG("Script: ~p~n", [Script]),
?ERROR_MSG("Low level script: ~p~n", [LowLevelScript]),
?ERROR_MSG("Check: ~p~n", [Check]),
error
end,
{Script, LowLevelScript, Check1}.
%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl
-ifdef(SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL).
-record(application,
{name, %% Name of the application, atom().
type = permanent, %% Application start type, atom().
vsn = "", %% Version of the application, string().
id = "", %% Id of the application, string().
description = "", %% Description of application, string().
modules = [], %% [Module | {Module,Vsn}] of modules
%% incorporated in the application,
%% Module = atom(), Vsn = string().
uses = [], %% [Application] list of applications required
%% by the application, Application = atom().
includes = [], %% [Application] list of applications included
%% by the application, Application = atom().
regs = [], %% [RegNames] a list of registered process
%% names used by the application, RegNames =
%% atom().
env = [], %% [{Key,Value}] environment variable of
%% application, Key = Value = term().
maxT = infinity, %% Max time an application may exist,
%% integer() | infinity.
maxP = infinity, %% Max number of processes in an application,
%% integer() | infinity.
mod = [], %% [] | {Mod, StartArgs}, Mod= atom(),
%% StartArgs = list().
start_phases, %% [{Phase, PhaseArgs}] | undefined,
%% Phase = atom(),
%% PhaseArgs = list().
dir = "" %% The directory where the .app file was
%% found (internal use).
}).
-else.
-record(application,
{name, %% Name of the application, atom().
type = permanent, %% Application start type, atom().
vsn = "", %% Version of the application, string().
id = "", %% Id of the application, string().
description = "", %% Description of application, string().
modules = [], %% [Module | {Module,Vsn}] of modules
%% incorporated in the application,
%% Module = atom(), Vsn = string().
uses = [], %% [Application] list of applications required
%% by the application, Application = atom().
optional = [], %% [Application] list of applications in uses
%% that are optional, Application = atom().
includes = [], %% [Application] list of applications included
%% by the application, Application = atom().
regs = [], %% [RegNames] a list of registered process
%% names used by the application, RegNames =
%% atom().
env = [], %% [{Key,Value}] environment variable of
%% application, Key = Value = term().
maxT = infinity, %% Max time an application may exist,
%% integer() | infinity.
maxP = infinity, %% Max number of processes in an application,
%% integer() | infinity.
mod = [], %% [] | {Mod, StartArgs}, Mod= atom(),
%% StartArgs = list().
start_phases, %% [{Phase, PhaseArgs}] | undefined,
%% Phase = atom(),
%% PhaseArgs = list().
dir = "" %% The directory where the .app file was
%% found (internal use).
}).
-endif.
make_script(UpdatedBeams) ->
lists:map(
fun(Module) ->
{ok, {Module, [{attributes, NewAttrs}]}} =
beam_lib:chunks(code:which(Module), [attributes]),
CurAttrs = Module:module_info(attributes),
case lists:keysearch(update_info, 1, NewAttrs) of
{value, {_, [{update, _}]}} ->
case lists:keysearch(update_info, 1, CurAttrs) of
{value, {_, [{update, Extra}]}} ->
{update, Module, {advanced, Extra}};
false ->
{update, Module, {advanced, 0}}
end;
false ->
{load_module, Module}
end
end, UpdatedBeams).
make_low_level_script(UpdatedBeams, Script) ->
EJDApp = #application{name = ejabberd,
modules = UpdatedBeams},
{ok, LowLevelScript} =
systools_rc:translate_scripts([Script], [EJDApp], [EJDApp]),
LowLevelScript.