aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl/src/release_handler.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sasl/src/release_handler.erl')
-rw-r--r--lib/sasl/src/release_handler.erl456
1 files changed, 371 insertions, 85 deletions
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index bc08f94dff..522c7b496b 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -26,8 +26,9 @@
create_RELEASES/1, create_RELEASES/2, create_RELEASES/4,
unpack_release/1,
check_install_release/1, check_install_release/2,
- install_release/1, install_release/2, remove_release/1,
- which_releases/0, make_permanent/1, reboot_old_release/1,
+ install_release/1, install_release/2, new_emulator_upgrade/2,
+ remove_release/1, which_releases/0, which_releases/1,
+ make_permanent/1, reboot_old_release/1,
set_unpacked/2, set_removed/1, install_file/2]).
-export([upgrade_app/2, downgrade_app/2, downgrade_app/3,
upgrade_script/2, downgrade_script/3,
@@ -40,7 +41,7 @@
%% Internal exports, a client release_handler may call this functions.
-export([do_write_release/3, do_copy_file/2, do_copy_files/2,
do_copy_files/1, do_rename_files/1, do_remove_files/1,
- do_write_file/2, do_ensure_RELEASES/1]).
+ remove_file/1, do_write_file/2, do_ensure_RELEASES/1]).
-record(state, {unpurged = [],
root,
@@ -61,10 +62,11 @@
%% remove -
%% current make_permanent permanent
%% install other old
+%% restart node unpacked
%% remove -
%% permanent make other permanent old
%% install permanent
-%% old reboot permanen
+%% old reboot_old permanent
%% install current
%% remove -
%%-----------------------------------------------------------------
@@ -74,6 +76,14 @@
-define(timeout, 10000).
%%-----------------------------------------------------------------
+%% The version set on the temporary release that will be used when the
+%% emulator is upgraded.
+-define(tmp_vsn(__BaseVsn__), "__new_emulator__"++__BaseVsn__).
+
+
+
+
+%%-----------------------------------------------------------------
%% Assumes the following file structure:
%% root --- lib --- Appl-Vsn1 --- <src>
%% | | |- ebin
@@ -183,11 +193,15 @@ check_check_install_options([],Purge) ->
%%-----------------------------------------------------------------
%% Purpose: Executes the relup script for the specified version.
%% The release must be unpacked.
-%% Returns: {ok, FromVsn, Descr} | {error, Reason}
+%% Returns: {ok, FromVsn, Descr} |
+%% {continue_after_restart, FromVsn, Descr} |
+%% {error, Reason}
%% Reason = {already_installed, Vsn} |
%% {bad_relup_file, RelFile} |
%% {no_such_release, Vsn} |
%% {no_such_from_vsn, Vsn} |
+%% {could_not_create_hybrid_boot,Why} |
+%% {missing_base_app,Vsn,App} |
%% {illegal_option, Opt}} |
%% exit_reason()
%%-----------------------------------------------------------------
@@ -230,6 +244,21 @@ check_timeout(Int) when is_integer(Int), Int > 0 -> true;
check_timeout(_Else) -> false.
%%-----------------------------------------------------------------
+%% Purpose: Called by boot script after emulator is restarted due to
+%% new erts version.
+%% Returns: Same as install_release/2
+%% If this crashes, the emulator restart will fail
+%% (since the function is called from the boot script)
+%% and there will be a rollback.
+%%-----------------------------------------------------------------
+new_emulator_upgrade(Vsn, Opts) ->
+ Result = call({install_release, Vsn, reboot, Opts}),
+ error_logger:info_msg(
+ "~p:install_release(~p,~p) completed after node restart "
+ "with new emulator version~nResult: ~p~n",[?MODULE,Vsn,Opts,Result]),
+ Result.
+
+%%-----------------------------------------------------------------
%% Purpose: Makes the specified release version be the one that is
%% used when the system starts (or restarts).
%% The release must be installed (not unpacked).
@@ -305,6 +334,14 @@ which_releases() ->
call(which_releases).
%%-----------------------------------------------------------------
+%% Returns: [{Name, Vsn, [LibName], Status}]
+%% Status = unpacked | current | permanent | old
+%%-----------------------------------------------------------------
+which_releases(Status) ->
+ Releases = which_releases(),
+ get_releases_with_status(Releases, Status, []).
+
+%%-----------------------------------------------------------------
%% check_script(Script, LibDirs) -> ok | {error, Reason}
%%-----------------------------------------------------------------
check_script(Script, LibDirs) ->
@@ -313,7 +350,7 @@ check_script(Script, LibDirs) ->
%%-----------------------------------------------------------------
%% eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
%% {ok, UnPurged} |
-%% restart_new_emulator |
+%% restart_emulator |
%% {error, Error}
%% {'EXIT', Reason}
%% If sync_nodes is present, the calling process must have called
@@ -350,7 +387,7 @@ create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
%%-----------------------------------------------------------------
%% Func: upgrade_app(App, Dir) -> {ok, Unpurged}
-%% | restart_new_emulator
+%% | restart_emulator
%% | {error, Error}
%% Types:
%% App = atom()
@@ -370,7 +407,7 @@ upgrade_app(App, NewDir) ->
%%-----------------------------------------------------------------
%% Func: downgrade_app(App, Dir)
%% downgrade_app(App, Vsn, Dir) -> {ok, Unpurged}
-%% | restart_new_emulator
+%% | restart_emulator
%% | {error, Error}
%% Types:
%% App = atom()
@@ -578,7 +615,7 @@ handle_call({check_install_release, Vsn, Purge}, _From, S) ->
handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
NS = resend_sync_nodes(S),
case catch do_install_release(S, Vsn, Opts) of
- {ok, NewReleases, CurrentVsn, Descr} ->
+ {ok, NewReleases, [], CurrentVsn, Descr} ->
{reply, {ok, CurrentVsn, Descr}, NS#state{releases=NewReleases}};
{ok, NewReleases, Unpurged, CurrentVsn, Descr} ->
Timer =
@@ -593,10 +630,14 @@ handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
{reply, {ok, CurrentVsn, Descr}, NewS};
{error, Reason} ->
{reply, {error, Reason}, NS};
- {restart_new_emulator, CurrentVsn, Descr} ->
+ {restart_emulator, CurrentVsn, Descr} ->
gen_server:reply(From, {ok, CurrentVsn, Descr}),
init:reboot(),
{noreply, NS};
+ {restart_new_emulator, CurrentVsn, Descr} ->
+ gen_server:reply(From, {continue_after_restart, CurrentVsn, Descr}),
+ init:reboot(),
+ {noreply, NS};
{'EXIT', Reason} ->
io:format("release_handler:"
"install_release(Vsn=~p Opts=~p) failed, "
@@ -801,8 +842,13 @@ do_unpack_release(Root, RelDir, ReleaseName, Releases) ->
extract_tar(Root, Tar),
NewReleases = [Release#release{status = unpacked} | Releases],
write_releases(RelDir, NewReleases, false),
+
+ %% Keeping this for backwards compatibility reasons with older
+ %% systools:make_tar, where there is no copy of the .rel file in
+ %% the releases/<vsn> dir. See OTP-9746.
Dir = filename:join([RelDir, Vsn]),
copy_file(RelFile, Dir, false),
+
{ok, NewReleases, Vsn}.
%% Note that this function is not executed by a client
@@ -941,7 +987,38 @@ do_install_release(#state{start_prg = StartPrg,
{value, Release} ->
LatestRelease = get_latest_release(Releases),
case get_rh_script(LatestRelease, Release, RelDir, Masters) of
+ {ok, {_CurrentVsn, _Descr, [restart_new_emulator|_Script]}}
+ when Static == true ->
+ throw(static_emulator);
+ {ok, {CurrentVsn, Descr, [restart_new_emulator|_Script]}} ->
+ %% This will only happen if the upgrade includes
+ %% an emulator upgrade (and it is not a downgrade)
+ %% - then the new emulator must be started before
+ %% new code can be loaded.
+ %% Create a temporary release which includes new
+ %% emulator, kernel, stdlib and sasl - and old
+ %% versions of other applications.
+ {TmpVsn,TmpRelease} =
+ new_emulator_make_tmp_release(LatestRelease,Release,
+ RelDir,Opts,Masters),
+ NReleases = [TmpRelease|Releases],
+
+ %% Then uppgrade to the temporary release.
+ %% The rest of the upgrade will continue after the restart
+ prepare_restart_new_emulator(StartPrg, RootDir,
+ RelDir, TmpVsn, TmpRelease,
+ NReleases, Masters),
+ {restart_new_emulator, CurrentVsn, Descr};
{ok, {CurrentVsn, Descr, Script}} ->
+ %% In case there has been an emulator upgrade,
+ %% remove the temporary release
+ NReleases =
+ new_emulator_rm_tmp_release(
+ LatestRelease#release.vsn,
+ LatestRelease#release.erts_vsn,
+ Vsn,RelDir,Releases,Masters),
+
+ %% Then execute the relup script
mon_nodes(true),
EnvBefore = application_controller:prep_config_change(),
Apps = change_appl_data(RelDir, Release, Masters),
@@ -949,31 +1026,19 @@ do_install_release(#state{start_prg = StartPrg,
NewLibs = get_new_libs(LatestRelease#release.libs,
Release#release.libs),
case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of
- {ok, []} ->
- application_controller:config_change(EnvBefore),
- mon_nodes(false),
- NewReleases = set_status(Vsn, current, Releases),
- {ok, NewReleases, CurrentVsn, Descr};
{ok, Unpurged} ->
application_controller:config_change(EnvBefore),
mon_nodes(false),
- NewReleases = set_status(Vsn, current, Releases),
- {ok, NewReleases, Unpurged, CurrentVsn, Descr};
- restart_new_emulator when Static == true ->
+ NReleases1 = set_status(Vsn, current, NReleases),
+ {ok, NReleases1, Unpurged, CurrentVsn, Descr};
+ restart_emulator when Static == true ->
throw(static_emulator);
- restart_new_emulator ->
+ restart_emulator ->
mon_nodes(false),
- {value, PermanentRelease} =
- lists:keysearch(permanent, #release.status,
- Releases),
- NReleases = set_status(Vsn, current, Releases),
- NReleases2 = set_status(Vsn,tmp_current,NReleases),
- write_releases(RelDir, NReleases2, Masters),
prepare_restart_new_emulator(StartPrg, RootDir,
- RelDir, Release,
- PermanentRelease,
- Masters),
- {restart_new_emulator, CurrentVsn, Descr};
+ RelDir, Vsn, Release,
+ NReleases, Masters),
+ {restart_emulator, CurrentVsn, Descr};
Else ->
application_controller:config_change(EnvBefore),
mon_nodes(false),
@@ -986,6 +1051,158 @@ do_install_release(#state{start_prg = StartPrg,
{error, {no_such_release, Vsn}}
end.
+new_emulator_make_tmp_release(CurrentRelease,ToRelease,RelDir,Opts,Masters) ->
+ CurrentVsn = CurrentRelease#release.vsn,
+ ToVsn = ToRelease#release.vsn,
+ TmpVsn = ?tmp_vsn(CurrentVsn),
+ case get_base_libs(ToRelease#release.libs) of
+ {ok,{Kernel,Stdlib,Sasl}=BaseLibs,_} ->
+ case get_base_libs(ToRelease#release.libs) of
+ {ok,_,RestLibs} ->
+ TmpErtsVsn = ToRelease#release.erts_vsn,
+ TmpLibs = [Kernel,Stdlib,Sasl|RestLibs],
+ TmpRelease = CurrentRelease#release{vsn=TmpVsn,
+ erts_vsn=TmpErtsVsn,
+ libs = TmpLibs,
+ status = unpacked},
+ new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,
+ BaseLibs,RelDir,Opts,Masters),
+ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,
+ RelDir,Masters),
+ {TmpVsn,TmpRelease};
+ {error,{missing,Missing}} ->
+ throw({error,{missing_base_app,CurrentVsn,Missing}})
+ end;
+ {error,{missing,Missing}} ->
+ throw({error,{missing_base_app,ToVsn,Missing}})
+ end.
+
+%% Get kernel, stdlib and sasl libs,
+%% and also return the rest of the libs as a list.
+%% Return error if any of kernel, stdlib or sasl does not exist.
+get_base_libs(Libs) ->
+ get_base_libs(Libs,undefined,undefined,undefined,[]).
+get_base_libs([{kernel,_,_}=Kernel|Libs],undefined,Stdlib,Sasl,Rest) ->
+ get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
+get_base_libs([{stdlib,_,_}=Stdlib|Libs],Kernel,undefined,Sasl,Rest) ->
+ get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
+get_base_libs([{sasl,_,_}=Sasl|Libs],Kernel,Stdlib,undefined,Rest) ->
+ get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
+get_base_libs([Lib|Libs],Kernel,Stdlib,Sasl,Rest) ->
+ get_base_libs(Libs,Kernel,Stdlib,Sasl,[Lib|Rest]);
+get_base_libs([],undefined,_Stdlib,_Sasl,_Rest) ->
+ {error,{missing,kernel}};
+get_base_libs([],_Kernel,undefined,_Sasl,_Rest) ->
+ {error,{missing,stdlib}};
+get_base_libs([],_Kernel,_Stdlib,undefined,_Rest) ->
+ {error,{missing,sasl}};
+get_base_libs([],Kernel,Stdlib,Sasl,Rest) ->
+ {ok,{Kernel,Stdlib,Sasl},lists:reverse(Rest)}.
+
+new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,BaseLibs,RelDir,Opts,Masters) ->
+ FromBootFile = filename:join([RelDir,CurrentVsn,"start.boot"]),
+ ToBootFile = filename:join([RelDir,ToVsn,"start.boot"]),
+ TmpBootFile = filename:join([RelDir,TmpVsn,"start.boot"]),
+ ensure_dir(TmpBootFile,Masters),
+ Args = [ToVsn,Opts],
+ {ok,FromBoot} = read_file(FromBootFile,Masters),
+ {ok,ToBoot} = read_file(ToBootFile,Masters),
+ {{_,_,KernelPath},{_,_,SaslPath},{_,_,StdlibPath}} = BaseLibs,
+ Paths = {filename:join(KernelPath,"ebin"),
+ filename:join(StdlibPath,"ebin"),
+ filename:join(SaslPath,"ebin")},
+ case systools_make:make_hybrid_boot(TmpVsn,FromBoot,ToBoot,Paths,Args) of
+ {ok,TmpBoot} ->
+ write_file(TmpBootFile,TmpBoot,Masters);
+ {error,Reason} ->
+ throw({error,{could_not_create_hybrid_boot,Reason}})
+ end.
+
+new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
+ FromFile = filename:join([RelDir,CurrentVsn,"sys.config"]),
+ ToFile = filename:join([RelDir,ToVsn,"sys.config"]),
+ TmpFile = filename:join([RelDir,TmpVsn,"sys.config"]),
+
+ FromConfig =
+ case consult(FromFile,Masters) of
+ {ok,[FC]} ->
+ FC;
+ {error,Error1} ->
+ io:format("Warning: ~p can not read ~p: ~p~n",
+ [?MODULE,FromFile,Error1]),
+ []
+ end,
+
+ [Kernel,Stdlib,Sasl] =
+ case consult(ToFile,Masters) of
+ {ok,[ToConfig]} ->
+ [lists:keyfind(App,1,ToConfig) || App <- [kernel,stdlib,sasl]];
+ {error,Error2} ->
+ io:format("Warning: ~p can not read ~p: ~p~n",
+ [?MODULE,ToFile,Error2]),
+ [false,false,false]
+ end,
+
+ Config1 = replace_config(kernel,FromConfig,Kernel),
+ Config2 = replace_config(stdlib,Config1,Stdlib),
+ Config3 = replace_config(sasl,Config2,Sasl),
+
+ ConfigStr = io_lib:format("~p.~n",[Config3]),
+ write_file(TmpFile,ConfigStr,Masters).
+
+%% Take the configuration for application App from the new config and
+%% insert in the old config.
+%% If no entry exists in the new config, then delete the entry (if it exists)
+%% from the old config.
+%% If entry exists in the new config, but not in the old config, then
+%% add the entry.
+replace_config(App,Config,false) ->
+ lists:keydelete(App,1,Config);
+replace_config(App,Config,AppConfig) ->
+ lists:keystore(App,1,Config,AppConfig).
+
+%% Remove all files related to the temporary release
+new_emulator_rm_tmp_release(?tmp_vsn(_)=TmpVsn,EVsn,NewVsn,
+ RelDir,Releases,Masters) ->
+ case os:type() of
+ {win32, nt} ->
+ rename_tmp_service(EVsn,TmpVsn,NewVsn);
+ _ ->
+ ok
+ end,
+ remove_dir(filename:join(RelDir,TmpVsn),Masters),
+ lists:keydelete(TmpVsn,#release.vsn,Releases);
+new_emulator_rm_tmp_release(_,_,_,_,Releases,_) ->
+ Releases.
+
+%% Rename the tempoarary service (for erts ugprade) to the real ToVsn
+rename_tmp_service(EVsn,TmpVsn,NewVsn) ->
+ FromName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ TmpVsn,
+ ToName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ NewVsn,
+ case erlsrv:get_service(EVsn,ToName) of
+ {error, _Error} ->
+ ok;
+ _Data ->
+ erlsrv:remove_service(ToName)
+ end,
+ rename_service(EVsn,FromName,ToName).
+
+
+%% Rename a service and check that it succeeded
+rename_service(EVsn,FromName,ToName) ->
+ case erlsrv:rename_service(EVsn,FromName,ToName) of
+ {ok,_} ->
+ case erlsrv:get_service(EVsn,ToName) of
+ {error,Error1} ->
+ throw({error,Error1});
+ _Data2 ->
+ ok
+ end;
+ Error2 ->
+ throw({error,{service_rename_failed, Error2}})
+ end.
+
+
%%% This code chunk updates the services in one of two ways,
%%% Either the emulator is restarted, in which case the old service
%%% is to be removed and the new enabled, or the emulator is NOT restarted
@@ -1002,26 +1219,16 @@ do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) ->
%% rename.
case os:getenv("ERLSRV_SERVICE_NAME") == PermName of
true ->
- case erlsrv:rename_service(EVsn,PermName,Name) of
- {ok,_} ->
- case erlsrv:get_service(EVsn,Name) of
- {error,Error2} ->
- throw({error,Error2});
- _Data2 ->
- %% The interfaces for doing this are
- %% NOT published and may be subject to
- %% change. Do NOT do this anywhere else!
-
- os:putenv("ERLSRV_SERVICE_NAME", Name),
-
- %% Restart heart port program, this
- %% function is only to be used here.
- heart:cycle(),
- ok
- end;
- Error3 ->
- throw({error,{service_rename_failed, Error3}})
- end;
+ rename_service(EVsn,PermName,Name),
+ %% The interfaces for doing this are
+ %% NOT published and may be subject to
+ %% change. Do NOT do this anywhere else!
+
+ os:putenv("ERLSRV_SERVICE_NAME", Name),
+
+ %% Restart heart port program, this
+ %% function is only to be used here.
+ heart:cycle();
false ->
throw({error,service_name_missmatch})
end;
@@ -1260,21 +1467,31 @@ do_set_removed(RelDir, Vsn, Releases, Masters) ->
%% corresponding relup instructions, we check if it's possible to
%% downgrade from CurrentVsn to ToVsn.
%%-----------------------------------------------------------------
+get_rh_script(#release{vsn = ?tmp_vsn(CurrentVsn)},
+ #release{vsn = ToVsn},
+ RelDir,
+ Masters) ->
+ {ok,{Vsn,Descr,[restart_new_emulator|Script]}} =
+ do_get_rh_script(CurrentVsn,ToVsn,RelDir,Masters),
+ {ok,{Vsn,Descr,Script}};
get_rh_script(#release{vsn = CurrentVsn},
- #release{vsn = Vsn},
+ #release{vsn = ToVsn},
RelDir,
Masters) ->
- Relup = filename:join([RelDir, Vsn, "relup"]),
- case try_upgrade(Vsn, CurrentVsn, Relup, Masters) of
+ do_get_rh_script(CurrentVsn,ToVsn,RelDir,Masters).
+
+do_get_rh_script(CurrentVsn, ToVsn, RelDir, Masters) ->
+ Relup = filename:join([RelDir, ToVsn, "relup"]),
+ case try_upgrade(ToVsn, CurrentVsn, Relup, Masters) of
{ok, RhScript} ->
{ok, RhScript};
_ ->
Relup2 = filename:join([RelDir, CurrentVsn,"relup"]),
- case try_downgrade(Vsn, CurrentVsn, Relup2, Masters) of
+ case try_downgrade(ToVsn, CurrentVsn, Relup2, Masters) of
{ok, RhScript} ->
{ok, RhScript};
_ ->
- throw({error, {no_matching_relup, Vsn, CurrentVsn}})
+ throw({error, {no_matching_relup, ToVsn, CurrentVsn}})
end
end.
@@ -1487,6 +1704,15 @@ prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn},
%% restart is performed by calling init:reboot() higher up.
%%-----------------------------------------------------------------
prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
+ Vsn, Release, Releases, Masters) ->
+ {value, PRelease} = lists:keysearch(permanent, #release.status,Releases),
+ NReleases1 = set_status(Vsn, current, Releases),
+ NReleases2 = set_status(Vsn,tmp_current,NReleases1),
+ write_releases(RelDir, NReleases2, Masters),
+ prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
+ Release, PRelease, Masters).
+
+prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
Release, PRelease, Masters) ->
#release{erts_vsn = EVsn, vsn = Vsn} = Release,
Data = EVsn ++ " " ++ Vsn,
@@ -1512,19 +1738,10 @@ check_start_prg({do_check, StartPrg}, Masters) ->
check_start_prg({_, StartPrg}, _) ->
StartPrg.
-write_new_start_erl(Data, RelDir, false) ->
- DataFile = filename:join([RelDir, "new_start_erl.data"]),
- case do_write_file(DataFile, Data) of
- ok -> DataFile;
- Error -> throw(Error)
- end;
write_new_start_erl(Data, RelDir, Masters) ->
DataFile = filename:join([RelDir, "new_start_erl.data"]),
- case at_all_masters(Masters, ?MODULE, do_write_file,
- [DataFile, Data]) of
- ok -> DataFile;
- Error -> throw(Error)
- end.
+ write_file(DataFile, Data, Masters),
+ DataFile.
%%-----------------------------------------------------------------
%% When a new emulator shall be restarted, the current release
@@ -1538,27 +1755,41 @@ write_new_start_erl(Data, RelDir, Masters) ->
%% If the release is made permanent, this is written to disk.
%%-----------------------------------------------------------------
transform_release(ReleaseDir, Releases, Masters) ->
- F = fun(Release) when Release#release.status == tmp_current ->
- Release#release{status = unpacked};
- (Release) -> Release
- end,
- case lists:map(F, Releases) of
- Releases ->
- Releases;
- DReleases ->
+ case init:script_id() of
+ {Name, ?tmp_vsn(_)=TmpVsn} ->
+ %% This is was a reboot due to a new emulator version. The
+ %% current release is a temporary internal release, which
+ %% must be removed. It is the "real new release" that is
+ %% set to unpacked on disk and current in memory.
+ DReleases = lists:keydelete(TmpVsn,#release.vsn,Releases),
write_releases(ReleaseDir, DReleases, Masters),
- F1 = fun(Release) when Release#release.status == tmp_current ->
- case init:script_id() of
- {_Name, Vsn} when Release#release.vsn == Vsn ->
- Release#release{status = current};
- _ ->
- Release#release{status = unpacked}
- end;
- (Release) -> Release
- end,
- lists:map(F1, Releases)
+ set_current({Name,TmpVsn},Releases);
+ ScriptId ->
+ F = fun(Release) when Release#release.status == tmp_current ->
+ Release#release{status = unpacked};
+ (Release) -> Release
+ end,
+ case lists:map(F, Releases) of
+ Releases ->
+ Releases;
+ DReleases ->
+ write_releases(ReleaseDir, DReleases, Masters),
+ set_current(ScriptId, Releases)
+ end
end.
+set_current(ScriptId, Releases) ->
+ F1 = fun(Release) when Release#release.status == tmp_current ->
+ case ScriptId of
+ {_Name,Vsn} when Release#release.vsn == Vsn ->
+ Release#release{status = current};
+ _ ->
+ Release#release{status = unpacked}
+ end;
+ (Release) -> Release
+ end,
+ lists:map(F1, Releases).
+
%%-----------------------------------------------------------------
%% Functions handling files, RELEASES, start_erl.data etc.
%% This functions consider if the release_handler is a client and
@@ -1617,12 +1848,25 @@ extract_tar(Root, Tar) ->
throw({error, {cannot_extract_file, Name, Reason}})
end.
-write_releases(Dir, NewReleases, false) ->
+write_releases(Dir, Releases, Masters) ->
+ %% We must never write 'current' to disk, since this will confuse
+ %% us after a node restart - since we would then have a permanent
+ %% release running, but state set to current for a non-running
+ %% release.
+ NewReleases = lists:zf(fun(Release) when Release#release.status == current ->
+ {true, Release#release{status = unpacked}};
+ (_) ->
+ true
+ end, Releases),
+ write_releases_1(Dir, NewReleases, Masters).
+
+
+write_releases_1(Dir, NewReleases, false) ->
case do_write_release(Dir, "RELEASES", NewReleases) of
ok -> ok;
Error -> throw(Error)
end;
-write_releases(Dir, NewReleases, Masters) ->
+write_releases_1(Dir, NewReleases, Masters) ->
all_masters(Masters),
write_releases_m(Dir, NewReleases, Masters).
@@ -1844,6 +2088,37 @@ read_file(File, false) ->
read_file(File, Masters) ->
read_master(Masters, File).
+write_file(File, Data, false) ->
+ case file:write_file(File, Data) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+write_file(File, Data, Masters) ->
+ case at_all_masters(Masters, file, write_file, [File, Data]) of
+ ok -> ok;
+ Error -> throw(Error)
+ end.
+
+ensure_dir(File, false) ->
+ case filelib:ensure_dir(File) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+ensure_dir(File, Masters) ->
+ case at_all_masters(Masters,filelib,ensure_dir,[File]) of
+ ok -> ok;
+ Error -> throw(Error)
+ end.
+
+remove_dir(Dir, false) ->
+ remove_file(Dir);
+remove_dir(Dir, Masters) ->
+ case at_all_masters(Masters,?MODULE,remove_file,[Dir]) of
+ ok -> ok;
+ Error -> throw(Error)
+ end.
+
+
%% Ignore status of each delete !
remove_files(Master, Files, Masters) ->
takewhile(Master, Masters, ?MODULE, do_remove_files, [Files]).
@@ -2010,3 +2285,14 @@ get_new_libs([{App,Vsn,_LibDir}|CurrentLibs], NewLibs) ->
end;
get_new_libs([],_) ->
[].
+
+%%-----------------------------------------------------------------
+%% Return a list of releases witch a specific status
+%%-----------------------------------------------------------------
+get_releases_with_status([], _, Acc) ->
+ Acc;
+get_releases_with_status([ {_, _, _, ReleaseStatus } = Head | Tail],
+ Status, Acc) when ReleaseStatus == Status ->
+ get_releases_with_status(Tail, Status, [Head | Acc]);
+get_releases_with_status([_ | Tail], Status, Acc) ->
+ get_releases_with_status(Tail, Status, Acc).