aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sasl/src')
-rw-r--r--lib/sasl/src/release_handler.erl438
-rw-r--r--lib/sasl/src/release_handler_1.erl120
-rw-r--r--lib/sasl/src/sasl.appup.src26
-rw-r--r--lib/sasl/src/systools_make.erl144
-rw-r--r--lib/sasl/src/systools_rc.erl40
-rw-r--r--lib/sasl/src/systools_relup.erl153
6 files changed, 738 insertions, 183 deletions
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index bc08f94dff..4e8cb4628c 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, "
@@ -941,7 +982,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 +1021,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 +1046,145 @@ 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),
+ BaseApps = [kernel,stdlib,sasl],
+ BaseLibs = [{App,Vsn,Lib} || {App,Vsn,Lib} <- ToRelease#release.libs,
+ lists:member(App,BaseApps)],
+ check_base_libs(BaseLibs,ToVsn),
+ OldBaseLibs = [{App,Vsn,Lib} || {App,Vsn,Lib} <- CurrentRelease#release.libs,
+ lists:member(App,BaseApps)],
+ check_base_libs(OldBaseLibs,CurrentVsn),
+ RestLibs = [{App,Vsn,Lib} || {App,Vsn,Lib} <- CurrentRelease#release.libs,
+ not lists:member(App,BaseApps)],
+ TmpRelease = CurrentRelease#release{vsn=TmpVsn,
+ erts_vsn=ToRelease#release.erts_vsn,
+ libs = BaseLibs ++ RestLibs,
+ 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}.
+
+check_base_libs([_,_,_]=BaseLibs,_Vsn) ->
+ [Kernel,Sasl,Stdlib] = lists:keysort(1,BaseLibs),
+ [Kernel,Stdlib,Sasl];
+check_base_libs(SomeMissing,Vsn) ->
+ find_missing(SomeMissing,[kernel,stdlib,sasl],Vsn).
+
+find_missing(SomeMissing,[H|T],Vsn) ->
+ case lists:keymember(H,1,SomeMissing) of
+ true ->
+ find_missing(SomeMissing,T,Vsn);
+ false ->
+ throw({error,{missing_base_app,Vsn,H}})
+ end.
+
+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] =
+ [filename:join(Path,ebin) || {_,_,Path} <- lists:keysort(1,BaseLibs)],
+ Paths = {KernelPath,StdlibPath,SaslPath},
+ 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 +1201,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 +1449,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 +1686,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 +1720,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 +1737,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 +1830,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 +2070,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 +2267,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).
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index 8d0baf3ab1..b4b288646f 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -47,26 +47,38 @@
%%%-----------------------------------------------------------------
%%% This is a low-level release handler.
%%%-----------------------------------------------------------------
+check_script([restart_new_emulator|Script], LibDirs) ->
+ %% There is no need to check for old processes, since the node
+ %% will be restarted before anything else happens.
+ do_check_script(Script, LibDirs, []);
check_script(Script, LibDirs) ->
case catch check_old_processes(Script,soft_purge) of
{ok, PurgeMods} ->
- {Before, _After} = split_instructions(Script),
- case catch lists:foldl(fun(Instruction, EvalState1) ->
- eval(Instruction, EvalState1)
- end,
- #eval_state{libdirs = LibDirs},
- Before) of
- EvalState2 when is_record(EvalState2, eval_state) ->
- {ok,PurgeMods};
- {error, Error} ->
- {error, Error};
- Other ->
- {error, Other}
- end;
+ do_check_script(Script, LibDirs, PurgeMods);
{error, Mod} ->
{error, {old_processes, Mod}}
end.
+do_check_script(Script, LibDirs, PurgeMods) ->
+ {Before, After} = split_instructions(Script),
+ case catch lists:foldl(fun(Instruction, EvalState1) ->
+ eval(Instruction, EvalState1)
+ end,
+ #eval_state{libdirs = LibDirs},
+ Before) of
+ EvalState2 when is_record(EvalState2, eval_state) ->
+ case catch syntax_check_script(After) of
+ ok ->
+ {ok,PurgeMods};
+ Other ->
+ {error,Other}
+ end;
+ {error, Error} ->
+ {error, Error};
+ Other ->
+ {error, Other}
+ end.
+
%% eval_script/1 - For testing only - no apps added, just testing instructions
eval_script(Script) ->
eval_script(Script, [], [], [], []).
@@ -83,22 +95,30 @@ eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
newlibs = NewLibs,
opts = Opts},
Before) of
- EvalState2 when is_record(EvalState2, eval_state) ->
- case catch lists:foldl(fun(Instruction, EvalState3) ->
- eval(Instruction, EvalState3)
- end,
- EvalState2,
- After) of
- EvalState4 when is_record(EvalState4, eval_state) ->
- {ok, EvalState4#eval_state.unpurged};
- restart_new_emulator ->
- restart_new_emulator;
- Error ->
- {'EXIT', Error}
- end;
- {error, Error} -> {error, Error};
- Other -> {error, Other}
- end;
+ EvalState2 when is_record(EvalState2, eval_state) ->
+ case catch syntax_check_script(After) of
+ ok ->
+ case catch lists:foldl(
+ fun(Instruction, EvalState3) ->
+ eval(Instruction,
+ EvalState3)
+ end,
+ EvalState2,
+ After) of
+ EvalState4
+ when is_record(EvalState4, eval_state) ->
+ {ok, EvalState4#eval_state.unpurged};
+ restart_emulator ->
+ restart_emulator;
+ Error ->
+ {'EXIT', Error}
+ end;
+ Other ->
+ {error,Other}
+ end;
+ {error, Error} -> {error, Error};
+ Other -> {error, Other}
+ end;
{error, Mod} ->
{error, {old_processes, Mod}}
end.
@@ -174,6 +194,42 @@ do_check_old_code(Mod,Procs) ->
[Mod].
+%% Check the last part of the script, i.e. the part after point_of_no_return.
+%% More or less a syntax check in case the script was handwritten.
+syntax_check_script([point_of_no_return | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{load, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{remove, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{purge, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{suspend, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{resume, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{code_change, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{code_change, _, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{stop, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{start, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{sync_nodes, _, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{sync_nodes, _, _} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([{apply, {_,_,_}} | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([restart_emulator | Script]) ->
+ syntax_check_script(Script);
+syntax_check_script([Illegal | _Script]) ->
+ throw({illegal_instruction_after_point_of_no_return,Illegal});
+syntax_check_script([]) ->
+ ok.
+
+
%%-----------------------------------------------------------------
%% An unpurged module is a module for which there exist an old
%% version of the code. This should only be the case if there are
@@ -243,7 +299,7 @@ do_check_old_code(Mod,Procs) ->
%% must also exectue the same line. Waits for all these nodes to get
%% to this line.
%% point_of_no_return
-%% restart_new_emulator
+%% restart_emulator
%% {stop_application, Appl} - Impl with apply
%% {unload_application, Appl} - Impl with {remove..}
%% {load_application, Appl} - Impl with {load..}
@@ -402,8 +458,8 @@ eval({sync_nodes, Id, Nodes}, EvalState) ->
eval({apply, {M, F, A}}, EvalState) ->
apply(M, F, A),
EvalState;
-eval(restart_new_emulator, _EvalState) ->
- throw(restart_new_emulator).
+eval(restart_emulator, _EvalState) ->
+ throw(restart_emulator).
get_opt(Tag, EvalState, Default) ->
case lists:keysearch(Tag, 1, EvalState#eval_state.opts) of
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index 64c653a4e5..ce4aa1f8f8 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -1,25 +1,27 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
-%% %CopyrightEnd%
%%
-
+%% %CopyrightEnd%
{"%VSN%",
- [{"2.1.4", [{load_module, release_handler},
- {load_module, systools_relup}]}],
- [{"2.1.4", [{load_module, release_handler},
- {load_module, systools_relup}]}]
+ %% Up from - max two major revisions back
+ [{<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04 (and later?)
+ {<<"2\\.1\\.[6-9](\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13B-R14B03
+ %% Down to - max two major revisions back
+ [{<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04 (and later?)
+ {<<"2\\.1\\.[6-9](\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13B-R14B03
}.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 7f400f5cce..ce37f3c2ce 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -31,6 +31,8 @@
-export([read_application/4]).
+-export([make_hybrid_boot/5]).
+
-import(lists, [filter/2, keysort/2, keysearch/3, map/2, reverse/1,
append/1, foldl/3, member/2, foreach/2]).
@@ -162,6 +164,118 @@ return({error,Mod,Error},_,Flags) ->
error
end.
+
+%%-----------------------------------------------------------------
+%% Make hybrid boot file for upgrading emulator. The resulting boot
+%% file is a combination of the two input files, where kernel, stdlib
+%% and sasl versions are taken from the second file (the boot file of
+%% the new release), and all other application versions from the first
+%% file (the boot file of the old release).
+%%
+%% The most important thing that can fail here is that the input boot
+%% files do not contain all three base applications - kernel, stdlib
+%% and sasl.
+%%
+%% TmpVsn = string(),
+%% Paths = {KernelPath,StdlibPath,SaslPath}
+%% Returns {ok,Boot} | {error,Reason}
+%% Boot1 = Boot2 = Boot = binary()
+%% Reason = {app_not_found,App} | {app_not_replaced,App}
+%% App = kernel | stdlib | sasl
+make_hybrid_boot(TmpVsn, Boot1, Boot2, Paths, Args) ->
+ catch do_make_hybrid_boot(TmpVsn, Boot1, Boot2, Paths, Args).
+do_make_hybrid_boot(TmpVsn, Boot1, Boot2, Paths, Args) ->
+ {script,{_RelName1,_RelVsn1},Script1} = binary_to_term(Boot1),
+ {script,{RelName2,_RelVsn2},Script2} = binary_to_term(Boot2),
+ MatchPaths = get_regexp_path(Paths),
+ NewScript1 = replace_paths(Script1,MatchPaths),
+ {Kernel,Stdlib,Sasl} = get_apps(Script2,undefined,undefined,undefined),
+ NewScript2 = replace_apps(NewScript1,Kernel,Stdlib,Sasl),
+ NewScript3 = add_apply_upgrade(NewScript2,Args),
+ Boot = term_to_binary({script,{RelName2,TmpVsn},NewScript3}),
+ {ok,Boot}.
+
+%% For each app, compile a regexp that can be used for finding its path
+get_regexp_path({KernelPath,StdlibPath,SaslPath}) ->
+ {ok,KernelMP} = re:compile("kernel-[0-9\.]+",[unicode]),
+ {ok,StdlibMP} = re:compile("stdlib-[0-9\.]+",[unicode]),
+ {ok,SaslMP} = re:compile("sasl-[0-9\.]+",[unicode]),
+ [{KernelMP,KernelPath},{StdlibMP,StdlibPath},{SaslMP,SaslPath}].
+
+%% For each path in the script, check if it matches any of the MPs
+%% found above, and if so replace it with the correct new path.
+replace_paths([{path,Path}|Script],MatchPaths) ->
+ [{path,replace_path(Path,MatchPaths)}|replace_paths(Script,MatchPaths)];
+replace_paths([Stuff|Script],MatchPaths) ->
+ [Stuff|replace_paths(Script,MatchPaths)];
+replace_paths([],_) ->
+ [].
+
+replace_path([Path|Paths],MatchPaths) ->
+ [do_replace_path(Path,MatchPaths)|replace_path(Paths,MatchPaths)];
+replace_path([],_) ->
+ [].
+
+do_replace_path(Path,[{MP,ReplacePath}|MatchPaths]) ->
+ case re:run(Path,MP,[{capture,none}]) of
+ nomatch -> do_replace_path(Path,MatchPaths);
+ match -> ReplacePath
+ end;
+do_replace_path(Path,[]) ->
+ Path.
+
+%% Return the entries for loading the three base applications
+get_apps([{kernelProcess,application_controller,
+ {application_controller,start,[{application,kernel,_}]}}=Kernel|
+ Script],_,Stdlib,Sasl) ->
+ get_apps(Script,Kernel,Stdlib,Sasl);
+get_apps([{apply,{application,load,[{application,stdlib,_}]}}=Stdlib|Script],
+ Kernel,_,Sasl) ->
+ get_apps(Script,Kernel,Stdlib,Sasl);
+get_apps([{apply,{application,load,[{application,sasl,_}]}}=Sasl|_Script],
+ Kernel,Stdlib,_) ->
+ {Kernel,Stdlib,Sasl};
+get_apps([_|Script],Kernel,Stdlib,Sasl) ->
+ get_apps(Script,Kernel,Stdlib,Sasl);
+get_apps([],undefined,_,_) ->
+ throw({error,{app_not_found,kernel}});
+get_apps([],_,undefined,_) ->
+ throw({error,{app_not_found,stdlib}});
+get_apps([],_,_,undefined) ->
+ throw({error,{app_not_found,sasl}}).
+
+
+%% Replace the entries for loading the base applications
+replace_apps([{kernelProcess,application_controller,
+ {application_controller,start,[{application,kernel,_}]}}|
+ Script],Kernel,Stdlib,Sasl) ->
+ [Kernel|replace_apps(Script,undefined,Stdlib,Sasl)];
+replace_apps([{apply,{application,load,[{application,stdlib,_}]}}|Script],
+ Kernel,Stdlib,Sasl) ->
+ [Stdlib|replace_apps(Script,Kernel,undefined,Sasl)];
+replace_apps([{apply,{application,load,[{application,sasl,_}]}}|Script],
+ _Kernel,_Stdlib,Sasl) ->
+ [Sasl|Script];
+replace_apps([Stuff|Script],Kernel,Stdlib,Sasl) ->
+ [Stuff|replace_apps(Script,Kernel,Stdlib,Sasl)];
+replace_apps([],undefined,undefined,_) ->
+ throw({error,{app_not_replaced,sasl}});
+replace_apps([],undefined,_,_) ->
+ throw({error,{app_not_replaced,stdlib}});
+replace_apps([],_,_,_) ->
+ throw({error,{app_not_replaced,kernel}}).
+
+
+%% Finally add an apply of release_handler:new_emulator_upgrade - which will
+%% complete the execution of the upgrade script (relup).
+add_apply_upgrade(Script,Args) ->
+ [{progress, started} | RevScript] = lists:reverse(Script),
+ lists:reverse([{progress,started},
+ {apply,{release_handler,new_emulator_upgrade,Args}} |
+ RevScript]).
+
+
+
%%-----------------------------------------------------------------
%% Create a release package from a release file.
%% Options is a list of {path, Path} | silent |
@@ -250,13 +364,13 @@ get_release(File, Path, ModTestP, Machine) ->
end.
get_release1(File, Path, ModTestP, Machine) ->
- {ok, Release} = read_release(File, Path),
+ {ok, Release, Warnings1} = read_release(File, Path),
{ok, Appls0} = collect_applications(Release, Path),
{ok, Appls1} = check_applications(Appls0),
{ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121
- {ok, Warnings} = check_modules(Appls2, Path, ModTestP, Machine),
+ {ok, Warnings2} = check_modules(Appls2, Path, ModTestP, Machine),
{ok, Appls} = sort_appls(Appls2),
- {ok, Release, Appls, Warnings}.
+ {ok, Release, Appls, Warnings1 ++ Warnings2}.
%%______________________________________________________________________
%% read_release(File, Path) -> {ok, #release} | throw({error, What})
@@ -271,11 +385,12 @@ read_release(File, Path) ->
check_rel(Release) ->
case catch check_rel1(Release) of
- {ok, {Name,Vsn,Evsn,Appl,Incl}} ->
+ {ok, {Name,Vsn,Evsn,Appl,Incl}, Ws} ->
{ok, #release{name=Name, vsn=Vsn,
erts_vsn=Evsn,
applications=Appl,
- incl_apps=Incl}};
+ incl_apps=Incl},
+ Ws};
{error, Error} ->
throw({error,?MODULE,Error});
Error ->
@@ -286,8 +401,8 @@ check_rel1({release,{Name,Vsn},{erts,EVsn},Appl}) when is_list(Appl) ->
check_name(Name),
check_vsn(Vsn),
check_evsn(EVsn),
- {Appls,Incls} = check_appl(Appl),
- {ok, {Name,Vsn,EVsn,Appls,Incls}};
+ {{Appls,Incls},Ws} = check_appl(Appl),
+ {ok, {Name,Vsn,EVsn,Appls,Incls},Ws};
check_rel1(_) ->
{error, badly_formatted_release}.
@@ -340,8 +455,8 @@ check_appl(Appl) ->
end,
Appl) of
[] ->
- mandatory_applications(Appl),
- split_app_incl(Appl);
+ {ok,Ws} = mandatory_applications(Appl),
+ {split_app_incl(Appl),Ws};
Illegal ->
throw({error, {illegal_applications,Illegal}})
end.
@@ -352,7 +467,12 @@ mandatory_applications(Appl) ->
Mand = mandatory_applications(),
case filter(fun(X) -> member(X, AppNames) end, Mand) of
Mand ->
- ok;
+ case member(sasl,AppNames) of
+ true ->
+ {ok,[]};
+ _ ->
+ {ok, [{warning,missing_sasl}]}
+ end;
_ ->
throw({error, {missing_mandatory_app, Mand}})
end.
@@ -2198,5 +2318,9 @@ form_warn(Prefix, {exref_undef, Undef}) ->
[Prefix,M,F,A])
end,
map(F, Undef);
+form_warn(Prefix, missing_sasl) ->
+ io_lib:format("~s: Missing application sasl. "
+ "Can not upgrade with this release~n",
+ [Prefix]);
form_warn(Prefix, What) ->
io_lib:format("~s ~p~n", [Prefix,What]).
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index daadb79967..c16f6aa845 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.erl
@@ -54,6 +54,7 @@
%% {sync_nodes, Id, Nodes}
%% {apply, {M, F, A}}
%% restart_new_emulator
+%% restart_emulator
%%-----------------------------------------------------------------
%% High-level instructions that contain dependencies
@@ -144,7 +145,10 @@ translate_merged_script(Mode, Script, Appls, PreAppls) ->
{Before2, After2} = translate_dependent_instrs(Mode, Before1, After1,
Appls),
Before3 = merge_load_object_code(Before2),
- NewScript = Before3 ++ [point_of_no_return | After2],
+
+ {Before4,After4} = sort_emulator_restart(Mode,Before3,After2),
+ NewScript = Before4 ++ [point_of_no_return | After4],
+
check_syntax(NewScript),
{ok, NewScript}.
@@ -699,6 +703,39 @@ mlo([{load_object_code, {Lib, LibVsn, Mods}} | T]) ->
mlo([]) -> [].
%%-----------------------------------------------------------------
+%% RESTART EMULATOR
+%% -----------------------------------------------------------------
+%% -----------------------------------------------------------------
+%% Check if there are any 'restart_new_emulator' instructions (i.e. if
+%% the emulator or core application version is changed). If so, this
+%% must be done first for upgrade and last for downgrade.
+%% Check if there are any 'restart_emulator' instructions, if so
+%% remove all and place one the end.
+%% -----------------------------------------------------------------
+sort_emulator_restart(Mode,Before,After) ->
+ {Before1,After1} =
+ case filter_out(restart_new_emulator, After) of
+ After ->
+ {Before,After};
+ A1 when Mode==up ->
+ {[restart_new_emulator|Before],A1};
+ A1 when Mode==dn ->
+ {Before,A1++[restart_emulator]}
+ end,
+ After2 =
+ case filter_out(restart_emulator, After1) of
+ After1 ->
+ After1;
+ A2 ->
+ A2++[restart_emulator]
+ end,
+ {Before1,After2}.
+
+
+filter_out(What,List) ->
+ lists:filter(fun(X) when X=:=What -> false; (_) -> true end, List).
+
+%%-----------------------------------------------------------------
%% SYNTAX CHECK
%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
@@ -817,6 +854,7 @@ check_op({apply, {M, F, A}}) ->
check_func(F),
check_args(A);
check_op(restart_new_emulator) -> ok;
+check_op(restart_emulator) -> ok;
check_op(X) -> throw({error, {bad_instruction, X}}).
check_mod(Mod) when is_atom(Mod) -> ok;
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 6d9e922900..7fb623bb85 100644
--- a/lib/sasl/src/systools_relup.erl
+++ b/lib/sasl/src/systools_relup.erl
@@ -112,6 +112,11 @@
-export([mk_relup/3, mk_relup/4, format_error/1, format_warning/1]).
-include("systools.hrl").
+-define(R15_SASL_VSN,"2.2").
+
+%% For test purposes only - used by kernel, stdlib and sasl tests
+-export([appup_search_for_version/2]).
+
%%-----------------------------------------------------------------
%% mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs)
%% mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Opts) -> Ret
@@ -200,7 +205,13 @@ do_mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Path, Opts) ->
%% TopRel = #release
%% NameVsnApps = [{{Name, Vsn}, #application}]
{ok, TopRel, NameVsnApps, Ws0} ->
- %%
+ case lists:member({warning,missing_sasl},Ws0) of
+ true ->
+ throw({error,?MODULE,{missing_sasl,TopRel}});
+ false ->
+ ok
+ end,
+
%% TopApps = [#application]
TopApps = lists:map(fun({_, App}) -> App end, NameVsnApps),
@@ -247,7 +258,21 @@ foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
Ws, Acc) ->
BaseRelFile = extract_filename(BaseRelDc),
- {ok, BaseRel} = systools_make:read_release(BaseRelFile, Path),
+ {BaseRel, {BaseNameVsns, BaseApps}, Ws0} =
+ case systools_make:get_release(BaseRelFile, Path) of
+ {ok, BR, NameVsnApps, Warns} ->
+ case lists:member({warning,missing_sasl},Warns) of
+ true ->
+ throw({error,?MODULE,{missing_sasl,BR}});
+ false ->
+ %% NameVsnApps = [{{Name,Vsn},#application}]
+ %% Gives two lists - [{Name,Vsn}] and [#application]
+ {BR,lists:unzip(NameVsnApps),Warns}
+ end;
+ Other1 ->
+ throw(Other1)
+ end,
+
%%
%% BaseRel = #release
@@ -257,29 +282,23 @@ foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
%% application, one for each added applications, and one for
%% each removed applications.
%%
- {RUs1, Ws1} = collect_appup_scripts(up, TopApps, BaseRel, Ws, []),
+ {RUs1, Ws1} = collect_appup_scripts(up, TopApps, BaseRel, Ws0++Ws, []),
{RUs2, Ws2} = create_add_app_scripts(BaseRel, TopRel, RUs1, Ws1),
{RUs3, Ws3} = create_remove_app_scripts(BaseRel, TopRel, RUs2, Ws2),
- {RUs4, Ws4} =
- check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
-
- BaseApps =
- case systools_make:get_release(BaseRelFile, Path) of
- {ok, _, NameVsnApps, _Warns} ->
- lists:map(fun({_,App}) -> App end, NameVsnApps);
- Other1 ->
- throw(Other1)
- end,
+ {RUs4, Ws4} = check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
case systools_rc:translate_scripts(up, RUs4, TopApps, BaseApps) of
- {ok, RUs} ->
+ {ok, RUs5} ->
+
+ {RUs, Ws5} = fix_r15_sasl_upgrade(RUs5,Ws4,BaseNameVsns),
+
VDR = {BaseRel#release.vsn,
extract_description(BaseRelDc), RUs},
foreach_baserel_up(TopRel, TopApps, BaseRelDcs, Path,
- Opts, Ws4, [VDR| Acc]);
+ Opts, Ws5, [VDR| Acc]);
XXX ->
throw(XXX)
end;
@@ -294,42 +313,41 @@ foreach_baserel_dn(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
Ws, Acc) ->
BaseRelFile = extract_filename(BaseRelDc),
- {ok, BaseRel} = systools_make:read_release(BaseRelFile, Path),
-
- %% BaseRel = #release
-
- %% RUs = (release upgrade scripts)
- %%
- {RUs1, Ws1} = collect_appup_scripts(dn, TopApps, BaseRel, Ws, []),
-
- {BaseApps, Ws2} =
+ {BaseRel, BaseApps, Ws0} =
case systools_make:get_release(BaseRelFile, Path) of
%%
%% NameVsnApps = [{{Name, Vsn}, #application}]
- {ok, _, NameVsnApps, Warns} ->
- %%
- %% NApps = [#application]
- NApps = lists:map(fun({_,App}) -> App end, NameVsnApps),
- {NApps, Warns ++ Ws1};
+ {ok, BR, NameVsnApps, Warns} ->
+ case lists:member({warning,missing_sasl},Warns) of
+ true ->
+ throw({error,?MODULE,{missing_sasl,BR}});
+ false ->
+ %% NApps = [#application]
+ NApps = lists:map(fun({_,App}) -> App end, NameVsnApps),
+ {BR, NApps, Warns}
+ end;
Other ->
throw(Other)
end,
- RUs2 = RUs1,
+ %% BaseRel = #release
+
+ %% RUs = (release upgrade scripts)
+ %%
+ {RUs1, Ws1} = collect_appup_scripts(dn, TopApps, BaseRel, Ws0++Ws, []),
- {RUs3, Ws3} = create_add_app_scripts(TopRel, BaseRel, RUs2, Ws2),
+ {RUs2, Ws2} = create_add_app_scripts(TopRel, BaseRel, RUs1, Ws1),
- {RUs4, Ws4} = create_remove_app_scripts(TopRel, BaseRel, RUs3, Ws3),
+ {RUs3, Ws3} = create_remove_app_scripts(TopRel, BaseRel, RUs2, Ws2),
- {RUs5, Ws5} = check_for_emulator_restart(TopRel, BaseRel,
- RUs4, Ws4, Opts),
+ {RUs4, Ws4} = check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
- case systools_rc:translate_scripts(dn, RUs5, BaseApps, TopApps) of
+ case systools_rc:translate_scripts(dn, RUs4, BaseApps, TopApps) of
{ok, RUs} ->
VDR = {BaseRel#release.vsn,
extract_description(BaseRelDc), RUs},
foreach_baserel_dn(TopRel, TopApps, BaseRelDcs, Path,
- Opts, Ws5, [VDR| Acc]);
+ Opts, Ws4, [VDR| Acc]);
XXX ->
throw(XXX)
end;
@@ -343,14 +361,42 @@ foreach_baserel_dn( _, _, [], _, _, Ws, Acc) ->
%%
check_for_emulator_restart(#release{erts_vsn = Vsn1, name = N1},
#release{erts_vsn = Vsn2, name = N2}, RUs, Ws,
- _Opts) when Vsn1 /= Vsn2 ->
- {RUs++[[restart_new_emulator]], [{erts_vsn_changed, {N1, N2}} | Ws]};
+ Opts) when Vsn1 /= Vsn2 ->
+ %% Automatically insert a restart_new_emulator instruction when
+ %% erts version is changed. Also allow extra restart at the end of
+ %% the upgrade if restart_emulator option is given.
+ NewRUs = [[restart_new_emulator]|RUs],
+ NewWs = [{erts_vsn_changed, {{N1,Vsn1}, {N2,Vsn2}}} | Ws],
+ check_for_restart_emulator_opt(NewRUs, NewWs, Opts);
check_for_emulator_restart(_, _, RUs, Ws, Opts) ->
+ check_for_restart_emulator_opt(RUs, Ws, Opts).
+
+check_for_restart_emulator_opt(RUs, Ws, Opts) ->
case get_opt(restart_emulator, Opts) of
- true -> {RUs++[[restart_new_emulator]], Ws};
+ true -> {RUs++[[restart_emulator]], Ws};
_ -> {RUs, Ws}
end.
+
+%% Special handling of the upgrade from pre R15 to post R15. In R15,
+%% upgrade of the emulator was improved by moving the restart of the
+%% emulator before the rest of the upgrade instructions. However, it
+%% can only work if the release_handler is already upgraded to a post
+%% R15 version. If not, the upgrade instructions must be backwards
+%% compatible - i.e. restart_new_emulator will be the last
+%% instruction, executed after all code loading, code_change etc.
+fix_r15_sasl_upgrade([restart_new_emulator | RestRUs]=RUs, Ws, BaseApps) ->
+ case lists:keyfind(sasl,1,BaseApps) of
+ {sasl,Vsn} when Vsn < ?R15_SASL_VSN ->
+ {lists:delete(restart_emulator,RestRUs) ++ [restart_new_emulator],
+ [pre_R15_emulator_upgrade|Ws]};
+ _ ->
+ {RUs,Ws}
+ end;
+fix_r15_sasl_upgrade(RUs, Ws, _BaseApps) ->
+ {RUs,Ws}.
+
+
%% collect_appup_scripts(Mode, TopApps, BaseRel, Ws, RUs) -> {NRUs, NWs}
%% Mode = up | dn
%% TopApps = [#application]
@@ -440,13 +486,32 @@ get_script_from_appup(Mode, TopApp, BaseVsn, Ws, RUs) ->
%% XXX Why is this a warning only?
[{bad_vsn, {TopVsn, TopApp#application.vsn}}| Ws]
end,
- case lists:keysearch(BaseVsn, 1, VsnRUs) of
- {value, {_, RU}} ->
+ case appup_search_for_version(BaseVsn, VsnRUs) of
+ {ok, RU} ->
{RUs ++ [RU], Ws1};
- _ ->
+ error ->
throw({error, ?MODULE, {no_relup, FName, TopApp, BaseVsn}})
end.
+appup_search_for_version(BaseVsn, VsnRUs) ->
+ appup_search_for_version(BaseVsn, length(BaseVsn), VsnRUs).
+
+appup_search_for_version(BaseVsn,_,[{BaseVsn,RU}|_]) ->
+ {ok,RU};
+appup_search_for_version(BaseVsn,Size,[{Vsn,RU}|VsnRUs]) when is_binary(Vsn) ->
+ case re:run(BaseVsn,Vsn,[unicode,{capture,first,index}]) of
+ {match,[{0,Size}]} ->
+ {ok, RU};
+ _ ->
+ appup_search_for_version(BaseVsn,Size,VsnRUs)
+ end;
+appup_search_for_version(BaseVsn,Size,[_|VsnRUs]) ->
+ appup_search_for_version(BaseVsn,Size,VsnRUs);
+appup_search_for_version(_,_,[]) ->
+ error.
+
+
+
%% Primitives for the "lists of release names" that we upgrade from
%% and to.
@@ -543,7 +608,9 @@ format_error({no_relup, File, App, Vsn}) ->
"in file ~p~n",
[App#application.name, App#application.vsn,
App#application.name, Vsn, File]);
-
+format_error({missing_sasl,Release}) ->
+ io_lib:format("No sasl application in release ~p, ~p. Can not be upgraded.",
+ [Release#release.name, Release#release.vsn]);
format_error(Error) ->
io:format("~p~n", [Error]).