PageRenderTime 12ms CodeModel.GetById 49ms app.highlight 137ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/sasl/src/release_handler.erl

https://github.com/cobusc/otp
Erlang | 2301 lines | 1600 code | 188 blank | 513 comment | 18 complexity | 0a680c04422a6b5be62be39428a201c6 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1%%
   2%% %CopyrightBegin%
   3%%
   4%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
   5%%
   6%% Licensed under the Apache License, Version 2.0 (the "License");
   7%% you may not use this file except in compliance with the License.
   8%% You may obtain a copy of the License at
   9%%
  10%%     http://www.apache.org/licenses/LICENSE-2.0
  11%%
  12%% Unless required by applicable law or agreed to in writing, software
  13%% distributed under the License is distributed on an "AS IS" BASIS,
  14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15%% See the License for the specific language governing permissions and
  16%% limitations under the License.
  17%%
  18%% %CopyrightEnd%
  19%%
  20-module(release_handler).
  21-behaviour(gen_server).
  22
  23-include_lib("kernel/include/file.hrl").
  24
  25%% External exports
  26-export([start_link/0,
  27	 create_RELEASES/1, create_RELEASES/2, create_RELEASES/4,
  28	 unpack_release/1,
  29	 check_install_release/1, check_install_release/2,
  30	 install_release/1, install_release/2, new_emulator_upgrade/2,
  31	 remove_release/1, which_releases/0, which_releases/1,
  32	 make_permanent/1, reboot_old_release/1,
  33	 set_unpacked/2, set_removed/1, install_file/2]).
  34-export([upgrade_app/2, downgrade_app/2, downgrade_app/3,
  35	 upgrade_script/2, downgrade_script/3,
  36	 eval_appup_script/4]).
  37
  38%% Internal exports
  39-export([init/1, handle_call/3, handle_info/2, terminate/2,
  40	 handle_cast/2, code_change/3]).
  41
  42%% Internal exports, a client release_handler may call this functions.
  43-export([do_write_release/3, do_copy_file/2, do_copy_files/2,
  44	 do_copy_files/1, do_rename_files/1, do_remove_files/1,
  45	 remove_file/1, do_write_file/2, do_write_file/3,
  46	 do_ensure_RELEASES/1]).
  47
  48-record(state, {unpurged = [],
  49		root,
  50		rel_dir,
  51		releases,
  52		timer,
  53		start_prg,
  54		masters = false,
  55		client_dir = false,
  56	        static_emulator = false,
  57		pre_sync_nodes = []}).
  58
  59%%-----------------------------------------------------------------
  60%% status      action                next_status
  61%% =============================================
  62%%   -         unpack                unpacked
  63%% unpacked    install               current
  64%%             remove                -
  65%% current     make_permanent        permanent
  66%%             install other         old
  67%%             restart node          unpacked
  68%%             remove                -
  69%% permanent   make other permanent  old
  70%%             install               permanent
  71%% old         reboot_old            permanent
  72%%             install               current
  73%%             remove                -
  74%%-----------------------------------------------------------------
  75%% libs = [{Lib, Vsn, Dir}]
  76-record(release, {name, vsn, erts_vsn, libs = [], status}).
  77
  78-define(timeout, 10000).
  79
  80%%-----------------------------------------------------------------
  81%% The version set on the temporary release that will be used when the
  82%% emulator is upgraded.
  83-define(tmp_vsn(__BaseVsn__), "__new_emulator__"++__BaseVsn__).
  84
  85
  86
  87
  88%%-----------------------------------------------------------------
  89%% Assumes the following file structure:
  90%% root --- lib --- Appl-Vsn1 --- <src>
  91%%       |       |             |- ebin
  92%%       |       |             |_ priv
  93%%       |       |_ Appl-Vsn2
  94%%       |
  95%%       |- bin --- start (default; {sasl, start_prg} overrides
  96%%       |       |- run_erl
  97%%       |       |- start_erl (reads start_erl.data)
  98%%       |       |_ <to_erl>
  99%%       |
 100%%       |- erts-EVsn1 --- bin --- <jam44>
 101%%       |                      |- <epmd>
 102%%       |                      |_ erl
 103%%       |- erts-EVsn2
 104%%       |
 105%%       |- clients --- ClientName1 --- bin -- start
 106%%         <clients use same lib and erts as master>
 107%%       |           |               |_ releases --- start_erl.data
 108%%       |           |                           |_ Vsn1 -- start.boot
 109%%       |           |_ ClientName2
 110%%       |
 111%%       |- clients --- Type1 --- lib
 112%%         <clients use own lib and erts>
 113%%       |           |         |- erts-EVsn
 114%%       |           |         |- bin -- start
 115%%       |           |         |_ ClientName1 -- releases -- start_erl.data
 116%%       |           |                                    |_ start.boot (static)
 117%%       |           |                                    |_ Vsn1
 118%%       |           |_ Type2 
 119%%       |
 120%%       |- releases --- RELEASES
 121%%       |            |_ <Vsn1.tar.Z>
 122%%       |            |
 123%%       |            |- start_erl.data (generated by rh)
 124%%       |            |
 125%%       |            |_ Vsn1 --- start.boot
 126%%       |            |        |- <sys.config>
 127%%       |            |        |_ relup
 128%%       |            |_ Vsn2        
 129%%       |
 130%%       |- log --- erlang.log.N (1 .. 5)
 131%%
 132%% where <Name> means 'for example Name', and root is
 133%% init:get_argument(root)
 134%%
 135%% It is configurable where the start file is located, and what it
 136%% is called.
 137%%   The paramater is {sasl, start_prg} = File
 138%% It is also configurable where the releases directory is located.
 139%% Default is $ROOT/releases.  $RELDIR overrids, and
 140%% {sasl, releases_dir} overrides both.
 141%%-----------------------------------------------------------------
 142start_link() ->
 143    gen_server:start_link({local, release_handler}, ?MODULE, [], []).
 144
 145%%-----------------------------------------------------------------
 146%% Args: ReleaseName is the name of the package file
 147%%       (without .tar.Z (.tar on non unix systems))
 148%% Purpose: Copies all files in the release package to their
 149%%          directories.  Checks that all required libs and erts
 150%%          files are present.
 151%% Returns: {ok, Vsn} | {error, Reason}
 152%%          Reason = {existing_release, Vsn} |
 153%%                   {no_such_file, File} |
 154%%                   {bad_rel_file, RelFile} |
 155%%                   {file_missing, FileName} |  (in the tar package)
 156%%                   exit_reason()
 157%%-----------------------------------------------------------------
 158unpack_release(ReleaseName) ->
 159    call({unpack_release, ReleaseName}).
 160    
 161%%-----------------------------------------------------------------
 162%% Purpose: Checks the relup script for the specified version.
 163%%          The release must be unpacked.
 164%%          Options = [purge] - all old code that can be soft purged
 165%%          will be purged if all checks succeeds. This can be usefull
 166%%          in order to reduce time needed in the following call to
 167%%          install_release.
 168%% Returns: {ok, FromVsn, Descr} | {error, Reason}
 169%%          Reason = {illegal_option, IllegalOpt} |
 170%%                   {already_installed, Vsn} |
 171%%                   {bad_relup_file, RelFile} |
 172%%                   {no_such_release, Vsn} |
 173%%                   {no_such_from_vsn, Vsn} |
 174%%                   exit_reason()
 175%%-----------------------------------------------------------------
 176check_install_release(Vsn) ->
 177    check_install_release(Vsn, []).
 178
 179check_install_release(Vsn, Opts) ->
 180    case check_check_install_options(Opts, false) of
 181	{ok,Purge} ->
 182	    call({check_install_release, Vsn, Purge});
 183	Error ->
 184	    Error
 185    end.
 186
 187check_check_install_options([purge|Opts], _) ->
 188    check_check_install_options(Opts, true);
 189check_check_install_options([Illegal|_],_Purge) ->
 190    {error,{illegal_option,Illegal}};
 191check_check_install_options([],Purge) ->
 192    {ok,Purge}.
 193
 194
 195%%-----------------------------------------------------------------
 196%% Purpose: Executes the relup script for the specified version.
 197%%          The release must be unpacked.
 198%% Returns: {ok, FromVsn, Descr} |
 199%%          {continue_after_restart, FromVsn, Descr} |
 200%%          {error, Reason}
 201%%          Reason = {already_installed, Vsn} |
 202%%                   {bad_relup_file, RelFile} |
 203%%                   {no_such_release, Vsn} |
 204%%                   {no_such_from_vsn, Vsn} |
 205%%                   {could_not_create_hybrid_boot,Why} |
 206%%                   {missing_base_app,Vsn,App} |
 207%%                   {illegal_option, Opt}} |
 208%%                   exit_reason()
 209%%-----------------------------------------------------------------
 210install_release(Vsn) ->
 211    call({install_release, Vsn, restart, []}).
 212
 213
 214install_release(Vsn, Opt) ->
 215    case check_install_options(Opt, restart, []) of
 216	{ok, ErrorAction, InstallOpt} ->
 217	    call({install_release, Vsn, ErrorAction, InstallOpt});
 218	Error ->
 219	    Error
 220    end.
 221
 222check_install_options([Opt | Opts], ErrAct, InstOpts) ->
 223    case install_option(Opt) of
 224	{error_action, EAct} ->
 225	    check_install_options(Opts, EAct, InstOpts);
 226	true ->
 227	    check_install_options(Opts, ErrAct, [Opt | InstOpts]);
 228	false ->
 229	    {error, {illegal_option, Opt}}
 230    end;
 231check_install_options([], ErrAct, InstOpts) ->
 232    {ok, ErrAct, InstOpts}.
 233
 234install_option(Opt = {error_action, reboot}) -> Opt;
 235install_option(Opt = {error_action, restart}) -> Opt;
 236install_option({code_change_timeout, TimeOut}) ->
 237    check_timeout(TimeOut);
 238install_option({suspend_timeout, TimeOut}) ->
 239    check_timeout(TimeOut);
 240install_option({update_paths, Bool}) when Bool==true; Bool==false ->
 241    true;
 242install_option(_Opt) -> false.
 243
 244check_timeout(infinity) -> true;
 245check_timeout(Int) when is_integer(Int), Int > 0 -> true;
 246check_timeout(_Else) -> false.
 247
 248%%-----------------------------------------------------------------
 249%% Purpose: Called by boot script after emulator is restarted due to
 250%%          new erts version.
 251%% Returns: Same as install_release/2
 252%%          If this crashes, the emulator restart will fail
 253%%          (since the function is called from the boot script)
 254%%          and there will be a rollback.
 255%%-----------------------------------------------------------------
 256new_emulator_upgrade(Vsn, Opts) ->
 257    Result = call({install_release, Vsn, reboot, Opts}),
 258    error_logger:info_msg(
 259      "~w:install_release(~p,~p) completed after node restart "
 260      "with new emulator version~nResult: ~p~n",[?MODULE,Vsn,Opts,Result]),
 261    Result.
 262
 263%%-----------------------------------------------------------------
 264%% Purpose: Makes the specified release version be the one that is
 265%%          used when the system starts (or restarts).
 266%%          The release must be installed (not unpacked).
 267%% Returns: ok | {error, Reason}
 268%%          Reason = {bad_status, Status} |
 269%%                   {no_such_release, Vsn} |
 270%%                   exit_reason()
 271%%-----------------------------------------------------------------
 272make_permanent(Vsn) ->
 273    call({make_permanent, Vsn}).
 274
 275%%-----------------------------------------------------------------
 276%% Purpose: Reboots the system from an old release.
 277%%-----------------------------------------------------------------
 278reboot_old_release(Vsn) ->
 279    call({reboot_old_release, Vsn}).
 280
 281%%-----------------------------------------------------------------
 282%% Purpose: Deletes all files and directories used by the release
 283%%          version, that are not used by any other release.
 284%%          The release must not be permanent.
 285%% Returns: ok | {error, Reason}
 286%%          Reason = {permanent, Vsn} |
 287%%-----------------------------------------------------------------
 288remove_release(Vsn) ->
 289    call({remove_release, Vsn}).
 290
 291%%-----------------------------------------------------------------
 292%% Args: RelFile = string()
 293%%       Libs = [{Lib, LibVsn, Dir}]
 294%%       Lib = LibVsn = Dir = string()
 295%% Purpose: Tells the release handler that a release has been
 296%%          unpacked, without using the function unpack_release/1.
 297%%          RelFile is an absolute file name including the extension
 298%%          .rel.
 299%%          The release dir will be created.  The necessary files can
 300%%          be installed by calling install_file/2.
 301%%          The release_handler remebers where all libs are located.
 302%%          If remove_release is called later,
 303%%          those libs are removed as well (if no other releases uses
 304%%          them).
 305%% Returns: ok | {error, Reason}
 306%%-----------------------------------------------------------------
 307set_unpacked(RelFile, LibDirs) ->
 308    call({set_unpacked, RelFile, LibDirs}).
 309
 310%%-----------------------------------------------------------------
 311%% Args: Vsn = string()
 312%% Purpose: Makes it possible to handle removal of releases
 313%%          outside the release_handler.
 314%%          This function won't delete any files at all.
 315%% Returns: ok | {error, Reason}
 316%%-----------------------------------------------------------------
 317set_removed(Vsn) ->
 318    call({set_removed, Vsn}).
 319
 320%%-----------------------------------------------------------------
 321%% Purpose: Makes it possible to install the start.boot,
 322%%          sys.config and relup files if they are not part of a 
 323%%          standard release package.  May be used to
 324%%          install files that are generated, before install_release
 325%%          is called.
 326%% Returns: ok | {error, {no_such_release, Vsn}}
 327%%-----------------------------------------------------------------
 328install_file(Vsn, File) when is_list(File) ->
 329    call({install_file, File, Vsn}).
 330
 331%%-----------------------------------------------------------------
 332%% Returns: [{Name, Vsn, [LibName], Status}]
 333%%          Status = unpacked | current | permanent | old
 334%%-----------------------------------------------------------------
 335which_releases() ->
 336    call(which_releases).
 337
 338%%-----------------------------------------------------------------
 339%% Returns: [{Name, Vsn, [LibName], Status}]
 340%%          Status = unpacked | current | permanent | old
 341%%-----------------------------------------------------------------
 342which_releases(Status) ->
 343    Releases = which_releases(),
 344    get_releases_with_status(Releases, Status, []).
 345
 346%%-----------------------------------------------------------------
 347%% check_script(Script, LibDirs) -> ok | {error, Reason}
 348%%-----------------------------------------------------------------
 349check_script(Script, LibDirs) ->
 350    release_handler_1:check_script(Script, LibDirs).
 351
 352%%-----------------------------------------------------------------
 353%% eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
 354%%                                             {ok, UnPurged} |
 355%%                                             restart_emulator |
 356%%                                             {error, Error}
 357%%                                             {'EXIT', Reason}
 358%% If sync_nodes is present, the calling process must have called
 359%% net_kernel:monitor_nodes(true) before calling this function.
 360%% No!  No other process than the release_handler can ever call this
 361%% function, if sync_nodes is used.
 362%%
 363%% LibDirs is a list of all applications, while NewLibs is a list of
 364%% applications that have changed version between the current and the
 365%% new release.
 366%% -----------------------------------------------------------------
 367eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
 368    catch release_handler_1:eval_script(Script, Apps, LibDirs, NewLibs, Opts).
 369
 370%%-----------------------------------------------------------------
 371%% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason}
 372%% Types: Root = RelFile = string()
 373%% Purpose: Creates an initial RELEASES file.
 374%%-----------------------------------------------------------------
 375create_RELEASES([Root, RelFile | LibDirs]) ->
 376    create_RELEASES(Root, filename:join(Root, "releases"), RelFile, LibDirs).
 377
 378create_RELEASES(Root, RelFile) ->
 379    create_RELEASES(Root, filename:join(Root, "releases"), RelFile, []).
 380
 381create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
 382    case catch check_rel(Root, RelFile, LibDirs, false) of
 383	{error, Reason } ->
 384	    {error, Reason};
 385	Rel ->
 386	    Rel2 = Rel#release{status = permanent},
 387	    catch write_releases(RelDir, [Rel2], false)
 388    end.
 389
 390%%-----------------------------------------------------------------
 391%% Func: upgrade_app(App, Dir) -> {ok, Unpurged}
 392%%                              | restart_emulator
 393%%                              | {error, Error}
 394%% Types:
 395%%   App = atom()
 396%%   Dir = string() assumed to be application directory, the code
 397%%         located under Dir/ebin
 398%% Purpose: Upgrade to the version in Dir according to an appup file
 399%%-----------------------------------------------------------------
 400upgrade_app(App, NewDir) ->
 401    try upgrade_script(App, NewDir) of
 402	{ok, NewVsn, Script} ->
 403	    eval_appup_script(App, NewVsn, NewDir, Script)
 404    catch
 405	throw:Reason ->
 406	    {error, Reason}
 407    end.
 408
 409%%-----------------------------------------------------------------
 410%% Func: downgrade_app(App, Dir)
 411%%       downgrade_app(App, Vsn, Dir) -> {ok, Unpurged}
 412%%                                     | restart_emulator
 413%%                                     | {error, Error}
 414%% Types:
 415%%   App = atom()
 416%%   Vsn = string(), may be omitted if Dir == App-Vsn
 417%%   Dir = string() assumed to be application directory, the code
 418%%         located under Dir/ebin
 419%% Purpose: Downgrade from the version in Dir according to an appup file
 420%%          located in the ebin dir of the _current_ version
 421%%-----------------------------------------------------------------
 422downgrade_app(App, OldDir) ->
 423    case string:lexemes(filename:basename(OldDir), "-") of
 424	[_AppS, OldVsn] ->
 425	    downgrade_app(App, OldVsn, OldDir);
 426	_ ->
 427	    {error, {unknown_version, App}}
 428    end.
 429downgrade_app(App, OldVsn, OldDir) ->
 430    try downgrade_script(App, OldVsn, OldDir) of
 431	{ok, Script} ->
 432	    eval_appup_script(App, OldVsn, OldDir, Script)
 433    catch
 434	throw:Reason ->
 435	    {error, Reason}
 436    end.
 437
 438upgrade_script(App, NewDir) ->
 439    OldVsn = ensure_running(App),
 440    OldDir = code:lib_dir(App),
 441    {NewVsn, Script} = find_script(App, NewDir, OldVsn, up),
 442    OldAppl = read_app(App, OldVsn, OldDir),
 443    NewAppl = read_app(App, NewVsn, NewDir),
 444    case systools_rc:translate_scripts(up,
 445				       [Script],[NewAppl],[OldAppl]) of
 446	{ok, LowLevelScript} ->
 447	    {ok, NewVsn, LowLevelScript};
 448	{error, _SystoolsRC, Reason} ->
 449	    throw(Reason)
 450    end.
 451
 452downgrade_script(App, OldVsn, OldDir) ->
 453    NewVsn = ensure_running(App),
 454    NewDir = code:lib_dir(App),
 455    {NewVsn, Script} = find_script(App, NewDir, OldVsn, down),
 456    OldAppl = read_app(App, OldVsn, OldDir),
 457    NewAppl = read_app(App, NewVsn, NewDir),
 458    case systools_rc:translate_scripts(dn,
 459				       [Script],[OldAppl],[NewAppl]) of
 460	{ok, LowLevelScript} ->
 461	    {ok, LowLevelScript};
 462	{error, _SystoolsRC, Reason} ->
 463	    throw(Reason)
 464    end.
 465
 466eval_appup_script(App, ToVsn, ToDir, Script) ->
 467    EnvBefore = application_controller:prep_config_change(),
 468    AppSpecL = read_appspec(App, ToDir),
 469    Res = release_handler_1:eval_script(Script,
 470					[], % [AppSpec]
 471					[{App, ToVsn, ToDir}],
 472					[{App, ToVsn, ToDir}],
 473					[]), % [Opt]
 474    case Res of
 475	{ok, _Unpurged} ->
 476	    application_controller:change_application_data(AppSpecL,[]),
 477	    application_controller:config_change(EnvBefore);
 478	_Res ->
 479	    ignore
 480    end,
 481    Res.
 482
 483ensure_running(App) ->
 484    case lists:keysearch(App, 1, application:which_applications()) of
 485	{value, {_App, _Descr, Vsn}} ->
 486	    Vsn;
 487	false ->
 488	    throw({app_not_running, App})
 489    end.
 490
 491find_script(App, Dir, OldVsn, UpOrDown) ->
 492    Appup = filename:join([Dir, "ebin", atom_to_list(App)++".appup"]),
 493    case file:consult(Appup) of
 494	{ok, [{NewVsn, UpFromScripts, DownToScripts}]} ->
 495	    Scripts = case UpOrDown of
 496			  up -> UpFromScripts;
 497			  down -> DownToScripts
 498		      end,
 499	    case systools_relup:appup_search_for_version(OldVsn,Scripts) of
 500		{ok,Script} ->
 501		    {NewVsn,Script};
 502		error ->
 503		    throw({version_not_in_appup, OldVsn})
 504	    end;
 505	{error, enoent} ->
 506	    throw(no_appup_found);
 507	{error, Reason} ->
 508	    throw(Reason)
 509    end.
 510
 511read_app(App, Vsn, Dir) ->
 512    AppS = atom_to_list(App),
 513    Path = [filename:join(Dir, "ebin")],
 514    case systools_make:read_application(AppS, Vsn, Path, []) of
 515	{ok, Appl} ->
 516	    Appl;
 517	{error, {not_found, _AppFile}} ->
 518	    throw({no_app_found, Vsn, Dir});
 519	{error, Reason} ->
 520	    throw(Reason)
 521    end.
 522
 523read_appspec(App, Dir) ->
 524    AppS = atom_to_list(App),
 525    Path = [filename:join(Dir, "ebin")],
 526    case file:path_consult(Path, AppS++".app") of
 527	{ok, AppSpecL, _File} ->
 528	    AppSpecL;
 529	{error, Reason} ->
 530	    throw(Reason)
 531    end.
 532				      
 533%%-----------------------------------------------------------------
 534%% call(Request) -> Term
 535%%-----------------------------------------------------------------
 536call(Req) ->
 537    gen_server:call(release_handler, Req, infinity).
 538
 539
 540%%-----------------------------------------------------------------
 541%% Call-back functions from gen_server
 542%%-----------------------------------------------------------------
 543init([]) ->
 544    {ok, [[Root]]} = init:get_argument(root),
 545    {CliDir, Masters} = is_client(),
 546    ReleaseDir =
 547	case application:get_env(sasl, releases_dir) of
 548	    undefined ->
 549		case os:getenv("RELDIR") of
 550		    false ->
 551			if
 552			    CliDir == false ->
 553				filename:join([Root, "releases"]);
 554			    true ->
 555				filename:join([CliDir, "releases"])
 556			end;
 557		    RELDIR ->
 558			RELDIR
 559		end;
 560	    {ok, Dir} ->
 561		Dir
 562	end,
 563    Releases =
 564	case consult(filename:join(ReleaseDir, "RELEASES"), Masters) of
 565	    {ok, [Term]} ->
 566		transform_release(ReleaseDir, Term, Masters);
 567	    _ ->
 568		{Name, Vsn} = init:script_id(),
 569		[#release{name = Name, vsn = Vsn, status = permanent}]
 570	end,
 571    StartPrg =
 572	case application:get_env(start_prg) of
 573	    {ok, Found2} when is_list(Found2) ->
 574		{do_check, Found2};
 575	    _ ->
 576		{no_check, filename:join([Root, "bin", "start"])}
 577	end,
 578    Static =
 579	case application:get_env(static_emulator) of
 580	    {ok, SFlag} when is_atom(SFlag) -> SFlag;
 581	    _                            -> false
 582	end,
 583    {ok, #state{root = Root, rel_dir = ReleaseDir, releases = Releases,
 584		start_prg = StartPrg, masters = Masters,
 585	        client_dir = CliDir, static_emulator = Static}}.
 586
 587handle_call({unpack_release, ReleaseName}, _From, S)
 588  when S#state.masters == false ->
 589    case catch do_unpack_release(S#state.root, S#state.rel_dir,
 590				 ReleaseName, S#state.releases) of
 591	{ok, NewReleases, Vsn} -> 
 592	    {reply, {ok, Vsn}, S#state{releases = NewReleases}};
 593	{error, Reason}   ->
 594	    {reply, {error, Reason}, S}; 
 595	{'EXIT', Reason} ->
 596	    {reply, {error, Reason}, S}
 597    end;
 598handle_call({unpack_release, _ReleaseName}, _From, S) ->
 599    {reply, {error, client_node}, S};
 600
 601handle_call({check_install_release, Vsn, Purge}, _From, S) ->
 602    case catch do_check_install_release(S#state.rel_dir,
 603					Vsn,
 604					S#state.releases,
 605					S#state.masters,
 606					Purge) of
 607	{ok, CurrentVsn, Descr} -> 
 608	    {reply, {ok, CurrentVsn, Descr}, S};
 609	{error, Reason}   ->
 610	    {reply, {error, Reason}, S}; 
 611	{'EXIT', Reason} ->
 612	    {reply, {error, Reason}, S}
 613    end;
 614
 615handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
 616    NS = resend_sync_nodes(S),
 617    case catch do_install_release(S, Vsn, Opts) of
 618	{ok, NewReleases, [], CurrentVsn, Descr} ->
 619	    {reply, {ok, CurrentVsn, Descr}, NS#state{releases=NewReleases}};
 620	{ok, NewReleases, Unpurged, CurrentVsn, Descr} ->
 621	    Timer =
 622		case S#state.timer of
 623		    undefined ->
 624			{ok, Ref} = timer:send_interval(?timeout, timeout),
 625			Ref;
 626		    Ref -> Ref
 627		end,
 628	    NewS = NS#state{releases = NewReleases, unpurged = Unpurged,
 629			    timer = Timer},
 630	    {reply, {ok, CurrentVsn, Descr}, NewS};
 631	{error, Reason}   ->
 632	    {reply, {error, Reason}, NS}; 
 633	{restart_emulator, CurrentVsn, Descr} ->
 634	    gen_server:reply(From, {ok, CurrentVsn, Descr}),
 635	    init:reboot(),
 636	    {noreply, NS};
 637	{restart_new_emulator, CurrentVsn, Descr} ->
 638	    gen_server:reply(From, {continue_after_restart, CurrentVsn, Descr}),
 639	    init:reboot(),
 640	    {noreply, NS};
 641	{'EXIT', Reason} ->
 642	    io:format("release_handler:"
 643		      "install_release(Vsn=~tp Opts=~tp) failed, "
 644		      "Reason=~tp~n", [Vsn, Opts, Reason]),
 645	    gen_server:reply(From, {error, Reason}),
 646	    case ErrorAction of
 647		restart ->
 648		    init:restart();
 649		reboot ->
 650		    init:reboot()
 651	    end,
 652	    {noreply, NS}
 653    end;
 654
 655handle_call({make_permanent, Vsn}, _From, S) ->
 656    case catch do_make_permanent(S, Vsn) of
 657	{ok, Releases, Unpurged} ->
 658	    {reply, ok, S#state{releases = Releases, unpurged = Unpurged}};
 659	{error, Reason}   ->
 660	    {reply, {error, Reason}, S}; 
 661	{'EXIT', Reason} ->
 662	    {reply, {error, Reason}, S}
 663    end;
 664
 665handle_call({reboot_old_release, Vsn}, From, S) ->
 666    case catch do_reboot_old_release(S, Vsn) of
 667	ok ->
 668	    gen_server:reply(From, ok),
 669	    init:reboot(),
 670	    {noreply, S};
 671	{error, Reason}   ->
 672	    {reply, {error, Reason}, S}; 
 673	{'EXIT', Reason} ->
 674	    {reply, {error, Reason}, S}
 675    end;
 676
 677handle_call({remove_release, Vsn}, _From, S)
 678  when S#state.masters == false ->
 679    case catch do_remove_release(S#state.root, S#state.rel_dir,
 680				 Vsn, S#state.releases) of
 681	{ok, NewReleases} -> 
 682	    {reply, ok, S#state{releases = NewReleases}};
 683	{error, Reason}   ->
 684	    {reply, {error, Reason}, S}; 
 685	{'EXIT', Reason} ->
 686	    {reply, {error, Reason}, S}
 687    end;
 688handle_call({remove_release, _Vsn}, _From, S) ->
 689    {reply, {error, client_node}, S};
 690
 691handle_call({set_unpacked, RelFile, LibDirs}, _From, S) ->
 692    Root = S#state.root,
 693    case catch do_set_unpacked(Root, S#state.rel_dir, RelFile,
 694			       LibDirs, S#state.releases,
 695			       S#state.masters) of
 696	{ok, NewReleases, Vsn} -> 
 697	    {reply, {ok, Vsn}, S#state{releases = NewReleases}};
 698	{error, Reason}   ->
 699	    {reply, {error, Reason}, S}; 
 700	{'EXIT', Reason} ->
 701	    {reply, {error, Reason}, S}
 702    end;
 703
 704handle_call({set_removed, Vsn}, _From, S) ->
 705    case catch do_set_removed(S#state.rel_dir, Vsn,
 706			      S#state.releases,
 707			      S#state.masters) of
 708	{ok, NewReleases} ->
 709	    {reply, ok, S#state{releases = NewReleases}};
 710	{error, Reason}   ->
 711	    {reply, {error, Reason}, S}; 
 712	{'EXIT', Reason} ->
 713	    {reply, {error, Reason}, S}
 714    end;
 715
 716handle_call({install_file, File, Vsn}, _From, S) ->
 717    Reply = 
 718	case lists:keysearch(Vsn, #release.vsn, S#state.releases) of
 719	    {value, _} ->
 720		Dir = filename:join([S#state.rel_dir, Vsn]),
 721		catch copy_file(File, Dir, S#state.masters);
 722	    _ ->
 723		{error, {no_such_release, Vsn}}
 724	end,
 725    {reply, Reply, S};
 726
 727handle_call(which_releases, _From, S) ->
 728    Reply = lists:map(fun(#release{name = Name, vsn = Vsn, libs = Libs,
 729				   status = Status}) ->
 730			      {Name, Vsn, mk_lib_name(Libs), Status}
 731		      end, S#state.releases),
 732    {reply, Reply, S}.
 733
 734mk_lib_name([{LibName, Vsn, _Dir} | T]) ->
 735    [lists:concat([LibName, "-", Vsn]) | mk_lib_name(T)];
 736mk_lib_name([]) -> [].
 737
 738handle_info(timeout, S) ->
 739    case soft_purge(S#state.unpurged) of
 740	[] ->
 741	    _ = timer:cancel(S#state.timer),
 742	    {noreply, S#state{unpurged = [], timer = undefined}};
 743	Unpurged ->
 744	    {noreply, S#state{unpurged = Unpurged}}
 745    end;
 746
 747handle_info({sync_nodes, Id, Node}, S) ->
 748    PSN = S#state.pre_sync_nodes,
 749    {noreply, S#state{pre_sync_nodes = [{sync_nodes, Id, Node} | PSN]}};
 750
 751handle_info(Msg, State) ->
 752    error_logger:info_msg("release_handler: got unknown message: ~p~n", [Msg]),
 753    {noreply, State}.
 754
 755terminate(_Reason, _State) ->
 756    ok.
 757
 758handle_cast(_Msg, State) ->
 759    {noreply, State}.
 760code_change(_OldVsn, State, _Extra) ->
 761    {ok, State}.
 762
 763%%%-----------------------------------------------------------------
 764%%% Internal functions
 765%%%-----------------------------------------------------------------
 766is_client() ->
 767    case application:get_env(masters) of
 768	{ok, Masters} ->
 769	    Alive = is_alive(),
 770	    case atom_list(Masters) of
 771		true when Alive == true ->
 772		    case application:get_env(client_directory) of
 773			{ok, ClientDir} ->
 774			    case int_list(ClientDir) of
 775				true ->
 776				    {ClientDir, Masters};
 777				_ ->
 778				    exit({bad_parameter, client_directory,
 779					  ClientDir})
 780			    end;
 781			_ ->
 782			    {false, false}
 783		    end;
 784		_ ->
 785		    exit({bad_parameter, masters, Masters})
 786	    end;
 787	_ ->
 788	    {false, false}
 789    end.
 790
 791atom_list([A|T]) when is_atom(A) -> atom_list(T);
 792atom_list([])                    -> true;
 793atom_list(_)                     -> false.
 794
 795int_list([I|T]) when is_integer(I) -> int_list(T);
 796int_list([])                       -> true;
 797int_list(_)                        -> false.
 798
 799resend_sync_nodes(S) ->
 800    lists:foreach(fun(Msg) -> self() ! Msg end, S#state.pre_sync_nodes),
 801    S#state{pre_sync_nodes = []}.
 802
 803soft_purge(Unpurged) ->
 804    lists:filter(fun({Mod, _PostPurgeMethod}) ->
 805			 case code:soft_purge(Mod) of
 806			     true -> false; % No proc left, don't remember Mod
 807			     false -> true  % Still proc left, remember it
 808			 end
 809		 end,
 810		 Unpurged).
 811
 812brutal_purge(Unpurged) ->
 813    lists:filter(fun({Mod, brutal_purge}) -> code:purge(Mod), false;
 814		    (_) -> true
 815		 end,
 816		 Unpurged).
 817
 818%%-----------------------------------------------------------------
 819%% The release package is a RelName.tar.Z (.tar on non unix) file
 820%% with the following contents:
 821%%   - RelName.rel   == {release, {Name, Vsn}, {erts, EVsn}, [lib()]}
 822%%   - <files> according to [lib()]
 823%%   - lib() = {LibName, LibVsn}
 824%% In the Dir, there exists a file called RELEASES, which contains
 825%% a [{Vsn, {erts, EVsn}, {libs, [{LibName, LibVsn, LibDir}]}}].
 826%% Note that RelDir is an absolute directory name !
 827%% Note that this function is not executed by a client
 828%% release_handler.
 829%%-----------------------------------------------------------------
 830do_unpack_release(Root, RelDir, ReleaseName, Releases) ->
 831    Tar = filename:join(RelDir, ReleaseName ++ ".tar.gz"),
 832    do_check_file(Tar, regular),
 833    Rel = ReleaseName ++ ".rel",
 834    _ = extract_rel_file(filename:join("releases", Rel), Tar, Root),
 835    RelFile = filename:join(RelDir, Rel),
 836    Release = check_rel(Root, RelFile, false),
 837    #release{vsn = Vsn} = Release,
 838    case lists:keysearch(Vsn, #release.vsn, Releases) of
 839	{value, _} -> throw({error, {existing_release, Vsn}});
 840	_          -> ok
 841    end,
 842    extract_tar(Root, Tar),
 843    NewReleases = [Release#release{status = unpacked} | Releases],
 844    write_releases(RelDir, NewReleases, false),
 845
 846    %% Keeping this for backwards compatibility reasons with older
 847    %% systools:make_tar, where there is no copy of the .rel file in
 848    %% the releases/<vsn> dir. See OTP-9746.
 849    Dir = filename:join([RelDir, Vsn]),
 850    copy_file(RelFile, Dir, false),
 851
 852    %% Clean release
 853    _ = file:delete(Tar),
 854    _ = file:delete(RelFile),
 855
 856    {ok, NewReleases, Vsn}.
 857   
 858check_rel(Root, RelFile, Masters) ->
 859    check_rel(Root, RelFile, [], Masters).
 860check_rel(Root, RelFile, LibDirs, Masters) ->
 861    case consult(RelFile, Masters) of
 862	{ok, [RelData]} ->
 863	    check_rel_data(RelData, Root, LibDirs, Masters);
 864	{ok, _} ->
 865	    throw({error, {bad_rel_file, RelFile}});
 866	{error, Reason} when is_tuple(Reason) ->
 867	    throw({error, {bad_rel_file, RelFile}});
 868	{error, FileError} -> % FileError is posix atom | no_master
 869	    throw({error, {FileError, RelFile}})
 870    end.
 871
 872check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs,
 873		Masters) ->
 874    Libs2 =
 875	lists:map(fun(LibSpec) ->
 876			  Lib = element(1, LibSpec),
 877			  LibVsn = element(2, LibSpec),
 878			  LibName = lists:concat([Lib, "-", LibVsn]),
 879			  LibDir = 
 880			      case lists:keysearch(Lib, 1, LibDirs) of
 881				  {value, {_Lib, _Vsn, Dir}} ->
 882				      Path = filename:join(Dir,LibName),
 883				      check_path(Path, Masters),
 884				      Path;
 885				  _ ->
 886				      filename:join([Root, "lib", LibName])
 887			      end,
 888			  {Lib, LibVsn, LibDir}
 889		  end,
 890		  Libs),
 891    #release{name = Name, vsn = Vsn, erts_vsn = EVsn,
 892	     libs = Libs2, status = unpacking};
 893check_rel_data(RelData, _Root, _LibDirs, _Masters) ->
 894    throw({error, {bad_rel_data, RelData}}).
 895
 896check_path(Path) ->
 897	check_path_response(Path, file:read_file_info(Path)).
 898check_path(Path, false)   -> check_path(Path);
 899check_path(Path, Masters) -> check_path_master(Masters, Path).
 900
 901%%-----------------------------------------------------------------
 902%% check_path at any master node.
 903%% If the path does not exist or is not a directory
 904%% at one node it should not exist at any other node either.
 905%%-----------------------------------------------------------------
 906check_path_master([Master|Ms], Path) ->
 907	case rpc:call(Master, file, read_file_info, [Path]) of
 908	{badrpc, _} -> consult_master(Ms, Path);
 909	Res         -> check_path_response(Path, Res)
 910	end;
 911check_path_master([], _Path) ->
 912	{error, no_master}.
 913
 914check_path_response(_Path, {ok, Info}) when Info#file_info.type==directory ->
 915	ok;
 916check_path_response(Path, {ok, _Info}) ->
 917	throw({error, {not_a_directory, Path}});
 918check_path_response(Path, {error, _Reason}) ->
 919	throw({error, {no_such_directory, Path}}).
 920
 921do_check_install_release(RelDir, Vsn, Releases, Masters, Purge) ->
 922    case lists:keysearch(Vsn, #release.vsn, Releases) of
 923	{value, #release{status = current}} ->
 924	    {error, {already_installed, Vsn}};
 925	{value, Release} ->
 926	    LatestRelease = get_latest_release(Releases),
 927	    VsnDir = filename:join([RelDir, Vsn]),
 928	    check_file(filename:join(VsnDir, "start.boot"), regular, Masters),
 929	    IsRelup = check_opt_file(filename:join(VsnDir, "relup"), regular, Masters),
 930	    check_opt_file(filename:join(VsnDir, "sys.config"), regular, Masters),
 931
 932	    %% Check that all required libs are present
 933	    Libs = Release#release.libs,
 934	    lists:foreach(fun({_Lib, _LibVsn, LibDir}) ->
 935				  check_file(LibDir, directory, Masters),
 936				  Ebin = filename:join(LibDir, "ebin"),
 937				  check_file(Ebin, directory, Masters)
 938			  end,
 939			  Libs),
 940
 941	    if
 942		IsRelup ->
 943		    case get_rh_script(LatestRelease, Release, RelDir, Masters) of
 944			{ok, {CurrentVsn, Descr, Script}} ->
 945			    case catch check_script(Script, Libs) of
 946				{ok,SoftPurgeMods} when Purge=:=true ->
 947				    %% Get modules with brutal_purge
 948				    %% instructions, but that can be
 949				    %% soft purged
 950				    {ok,BrutalPurgeMods} =
 951					release_handler_1:check_old_processes(
 952					  Script,brutal_purge),
 953				    lists:foreach(
 954				      fun(Mod) ->
 955					      catch erlang:purge_module(Mod)
 956				      end,
 957				      SoftPurgeMods ++ BrutalPurgeMods),
 958				    {ok, CurrentVsn, Descr};
 959				{ok,_} ->
 960				    {ok, CurrentVsn, Descr};
 961				Else ->
 962				    Else
 963			    end;
 964			Error ->
 965			    Error
 966		    end;
 967		true ->
 968		    {ok, Vsn, ""}
 969	    end;
 970	_ ->
 971	    {error, {no_such_release, Vsn}}
 972    end.
 973	    
 974do_install_release(#state{start_prg = StartPrg,
 975			  root = RootDir,
 976			  rel_dir = RelDir, releases = Releases,
 977			  masters = Masters,
 978			  static_emulator = Static},
 979		   Vsn, Opts) ->
 980    case lists:keysearch(Vsn, #release.vsn, Releases) of
 981	{value, #release{status = current}} ->
 982	    {error, {already_installed, Vsn}};
 983	{value, Release} ->
 984	    LatestRelease = get_latest_release(Releases),
 985	    case get_rh_script(LatestRelease, Release, RelDir, Masters) of
 986		{ok, {_CurrentVsn, _Descr, [restart_new_emulator|_Script]}}
 987		  when Static == true ->
 988		    throw(static_emulator);
 989		{ok, {CurrentVsn, Descr, [restart_new_emulator|_Script]}} ->
 990		    %% This will only happen if the upgrade includes
 991		    %% an emulator upgrade (and it is not a downgrade)
 992		    %% - then the new emulator must be started before
 993		    %% new code can be loaded.
 994		    %% Create a temporary release which includes new
 995		    %% emulator, kernel, stdlib and sasl - and old
 996		    %% versions of other applications.
 997		    {TmpVsn,TmpRelease} =
 998			new_emulator_make_tmp_release(LatestRelease,Release,
 999						      RelDir,Opts,Masters),
1000		    NReleases = [TmpRelease|Releases],
1001
1002		    %% Then uppgrade to the temporary release.
1003		    %% The rest of the upgrade will continue after the restart
1004		    prepare_restart_new_emulator(StartPrg, RootDir,
1005						 RelDir, TmpVsn, TmpRelease,
1006						 NReleases, Masters),
1007		    {restart_new_emulator, CurrentVsn, Descr};
1008		{ok, {CurrentVsn, Descr, Script}} ->
1009		    %% In case there has been an emulator upgrade,
1010		    %% remove the temporary release
1011		    NReleases =
1012			new_emulator_rm_tmp_release(
1013			  LatestRelease#release.vsn,
1014			  LatestRelease#release.erts_vsn,
1015			  Vsn,RelDir,Releases,Masters),
1016
1017		    %% Then execute the relup script
1018		    mon_nodes(true),
1019		    EnvBefore = application_controller:prep_config_change(),
1020		    Apps = change_appl_data(RelDir, Release, Masters),
1021		    LibDirs = Release#release.libs,
1022		    NewLibs = get_new_libs(LatestRelease#release.libs,
1023					   Release#release.libs),
1024		    case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of
1025			{ok, Unpurged} ->
1026			    application_controller:config_change(EnvBefore),
1027			    mon_nodes(false),
1028			    NReleases1 = set_status(Vsn, current, NReleases),
1029			    {ok, NReleases1, Unpurged, CurrentVsn, Descr};
1030			restart_emulator when Static == true ->
1031			    throw(static_emulator);
1032			restart_emulator ->
1033			    mon_nodes(false),
1034			    prepare_restart_new_emulator(StartPrg, RootDir,
1035							 RelDir, Vsn, Release,
1036							 NReleases, Masters),
1037			    {restart_emulator, CurrentVsn, Descr};
1038			Else ->
1039			    application_controller:config_change(EnvBefore),
1040			    mon_nodes(false),
1041			    Else
1042		    end;
1043		Error ->
1044		    Error
1045	    end;
1046	_ ->
1047	    {error, {no_such_release, Vsn}}
1048    end.
1049
1050new_emulator_make_tmp_release(CurrentRelease,ToRelease,RelDir,Opts,Masters) ->
1051    CurrentVsn = CurrentRelease#release.vsn,
1052    ToVsn = ToRelease#release.vsn,
1053    TmpVsn = ?tmp_vsn(CurrentVsn),
1054    case get_base_libs(ToRelease#release.libs) of
1055	{ok,{Kernel,Stdlib,Sasl},_} ->
1056	    case get_base_libs(CurrentRelease#release.libs) of
1057		{ok,_,RestLibs} ->
1058		    TmpErtsVsn = ToRelease#release.erts_vsn,
1059		    TmpLibs = [Kernel,Stdlib,Sasl|RestLibs],
1060		    TmpRelease = CurrentRelease#release{vsn=TmpVsn,
1061							erts_vsn=TmpErtsVsn,
1062							libs = TmpLibs,
1063							status = unpacked},
1064		    new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,
1065						  RelDir,Opts,Masters),
1066		    new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,
1067						    RelDir,Masters),
1068		    {TmpVsn,TmpRelease};
1069		{error,{missing,Missing}} ->
1070		    throw({error,{missing_base_app,CurrentVsn,Missing}})
1071	    end;
1072	{error,{missing,Missing}} ->
1073	    throw({error,{missing_base_app,ToVsn,Missing}})
1074    end.
1075
1076%% Get kernel, stdlib and sasl libs,
1077%% and also return the rest of the libs as a list.
1078%% Return error if any of kernel, stdlib or sasl does not exist.
1079get_base_libs(Libs) ->
1080    get_base_libs(Libs,undefined,undefined,undefined,[]).
1081get_base_libs([{kernel,_,_}=Kernel|Libs],undefined,Stdlib,Sasl,Rest) ->
1082    get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
1083get_base_libs([{stdlib,_,_}=Stdlib|Libs],Kernel,undefined,Sasl,Rest) ->
1084    get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
1085get_base_libs([{sasl,_,_}=Sasl|Libs],Kernel,Stdlib,undefined,Rest) ->
1086    get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
1087get_base_libs([Lib|Libs],Kernel,Stdlib,Sasl,Rest) ->
1088    get_base_libs(Libs,Kernel,Stdlib,Sasl,[Lib|Rest]);
1089get_base_libs([],undefined,_Stdlib,_Sasl,_Rest) ->
1090    {error,{missing,kernel}};
1091get_base_libs([],_Kernel,undefined,_Sasl,_Rest) ->
1092    {error,{missing,stdlib}};
1093get_base_libs([],_Kernel,_Stdlib,undefined,_Rest) ->
1094    {error,{missing,sasl}};
1095get_base_libs([],Kernel,Stdlib,Sasl,Rest) ->
1096    {ok,{Kernel,Stdlib,Sasl},lists:reverse(Rest)}.
1097
1098new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,RelDir,Opts,Masters) ->
1099    FromBootFile = filename:join([RelDir,CurrentVsn,"start.boot"]),
1100    ToBootFile = filename:join([RelDir,ToVsn,"start.boot"]),
1101    TmpBootFile = filename:join([RelDir,TmpVsn,"start.boot"]),
1102    ensure_dir(TmpBootFile,Masters),
1103    Args = [ToVsn,Opts],
1104    {ok,FromBoot} = read_file(FromBootFile,Masters),
1105    {ok,ToBoot} = read_file(ToBootFile,Masters),
1106    case systools_make:make_hybrid_boot(TmpVsn,FromBoot,ToBoot,Args) of
1107	{ok,TmpBoot} ->
1108	    write_file(TmpBootFile,TmpBoot,Masters);
1109	{error,Reason} ->
1110	    throw({error,{could_not_create_hybrid_boot,Reason}})
1111    end.
1112
1113new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
1114    FromFile = filename:join([RelDir,CurrentVsn,"sys.config"]),
1115    ToFile = filename:join([RelDir,ToVsn,"sys.config"]),
1116    TmpFile = filename:join([RelDir,TmpVsn,"sys.config"]),
1117
1118    FromConfig =
1119	case consult(FromFile,Masters) of
1120	    {ok,[FC]} ->
1121		FC;
1122	    {error,Error1} ->
1123		io:format("Warning: ~w can not read ~tp: ~tp~n",
1124			  [?MODULE,FromFile,Error1]),
1125		[]
1126	end,
1127
1128    [Kernel,Stdlib,Sasl] =
1129	case consult(ToFile,Masters) of
1130	    {ok,[ToConfig]} ->
1131		[lists:keyfind(App,1,ToConfig) || App <- [kernel,stdlib,sasl]];
1132	    {error,Error2} ->
1133		io:format("Warning: ~w can not read ~tp: ~tp~n",
1134			  [?MODULE,ToFile,Error2]),
1135		[false,false,false]
1136	end,
1137
1138    Config1 = replace_config(kernel,FromConfig,Kernel),
1139    Config2 = replace_config(stdlib,Config1,Stdlib),
1140    Config3 = replace_config(sasl,Config2,Sasl),
1141
1142    ConfigStr = io_lib:format("%% ~s~n~tp.~n",
1143                              [epp:encoding_to_string(utf8),Config3]),
1144    write_file(TmpFile,unicode:characters_to_binary(ConfigStr),Masters).
1145
1146%% Take the configuration for application App from the new config and
1147%% insert in the old config.
1148%% If no entry exists in the new config, then delete the entry (if it exists)
1149%% from the old config.
1150%% If entry exists in the new config, but not in the old config, then
1151%% add the entry.
1152replace_config(App,Config,false) ->
1153    lists:keydelete(App,1,Config);
1154replace_config(App,Config,AppConfig) ->
1155    lists:keystore(App,1,Config,AppConfig).
1156
1157%% Remove all files related to the temporary release
1158new_emulator_rm_tmp_release(?tmp_vsn(_)=TmpVsn,EVsn,NewVsn,
1159			    RelDir,Releases,Masters) ->
1160    case os:type() of
1161	{win32, nt} ->
1162	    rename_tmp_service(EVsn,TmpVsn,NewVsn);
1163	_ ->
1164	    ok
1165    end,
1166    remove_dir(filename:join(RelDir,TmpVsn),Masters),
1167    lists:keydelete(TmpVsn,#release.vsn,Releases);
1168new_emulator_rm_tmp_release(_,_,_,_,Releases,_) ->
1169    Releases.
1170
1171%% Rename the tempoarary service (for erts ugprade) to the real ToVsn
1172rename_tmp_service(EVsn,TmpVsn,NewVsn) ->
1173    FromName = hd(string:lexemes(atom_to_list(node()),"@")) ++ "_" ++ TmpVsn,
1174    ToName = hd(string:lexemes(atom_to_list(node()),"@")) ++ "_" ++ NewVsn,
1175    case erlsrv:get_service(EVsn,ToName) of
1176	{error, _Error} ->
1177	    ok;
1178	_Data ->
1179	    {ok,_} = erlsrv:remove_service(ToName),
1180	    ok
1181    end,
1182    rename_service(EVsn,FromName,ToName).
1183
1184
1185%% Rename a service and check that it succeeded
1186rename_service(EVsn,FromName,ToName) ->
1187    case erlsrv:rename_service(EVsn,FromName,ToName) of
1188	{ok,_} ->
1189	    case erlsrv:get_service(EVsn,ToName) of
1190		{error,Error1} ->
1191		    throw({error,Error1});
1192		_Data2 ->
1193		    ok
1194	    end;
1195	Error2 ->
1196	    throw({error,{service_rename_failed, Error2}})
1197    end.
1198
1199
1200%%% This code chunk updates the services in one of two ways,
1201%%% Either the emulator is restarted, in which case the old service
1202%%% is to be removed and the new enabled, or the emulator is NOT restarted
1203%%% in which case we try to rename the old service to the new name and try
1204%%% to update heart's view of what service we are really running.
1205do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) ->
1206    PermName = hd(string:lexemes(atom_to_list(node()),"@"))
1207	++ "_" ++ PermanentVsn,
1208    Name = hd(string:lexemes(atom_to_list(node()),"@"))
1209	++ "_" ++ Vsn,
1210    case erlsrv:get_service(EVsn,Name) of
1211	{error, _Error} ->
1212	    %% We probably do not need to replace services, just 
1213	    %% rename.
1214	    case os:getenv("ERLSRV_SERVICE_NAME") == PermName of
1215		true ->
1216		    rename_service(EVsn,PermName,Name),
1217		    %% The interfaces for doing this are
1218		    %% NOT published and may be subject to
1219		    %% change. Do NOT do this anywhere else!
1220
1221		    os:putenv("ERLSRV_SERVICE_NAME", Name),
1222
1223		    %% Restart heart port program, this
1224		    %% function is only to be used here.
1225		    heart:cycle();
1226		false ->
1227		    throw({error,service_name_missmatch})
1228	    end;
1229	Data ->
1230	    UpdData = erlsrv:new_service(Name, Data, []),
1231	    case erlsrv:store_service(EVsn,UpdData) of
1232		ok ->
1233		    {ok,_} = erlsrv:disable_service(PermanentEVsn, PermName),
1234		    {ok,_} = erlsrv:enable_service(EVsn, Name),
1235		    {ok,_} = erlsrv:remove_service(PermName),
1236		    %%% Read comments about these above...
1237		    os:putenv("ERLSRV_SERVICE_NAME", Name),
1238		    ok = heart:cycle();
1239		Error4 ->
1240		    throw(Error4)
1241	    end
1242    end.
1243
1244do_make_permanent(#state{releases = Releases,
1245			 rel_dir = RelDir, unpurged = Unpurged,
1246			 masters = Masters,
1247			 static_emulator = Static},
1248		  Vsn) ->
1249    case lists:keysearch(Vsn, #release.vsn, Releases) of
1250	{value, #release{erts_vsn = EVsn, status = Status}}
1251	  when Status /= unpacked, Status /= old, Status /= permanent ->
1252	    Dir = filename:join([RelDir, Vsn]),
1253	    Sys =
1254		case catch check_file(filename:join(Dir, "sys.config"),
1255				      regular, Masters) of
1256		    ok ->     filename:join(Dir, "sys");
1257		    _ -> false
1258		end,
1259	    Boot = filename:join(Dir, "start.boot"),
1260	    check_file(Boot, regular, Masters),
1261	    set_permanent_files(RelDir, EVsn, Vsn, Masters, Static),
1262	    NewReleases = set_status(Vsn, permanent, Releases),
1263	    write_releases(RelDir, NewReleases, Masters),
1264	    case os:type() of
1265		{win32, nt} ->
1266		    {value, PermanentRelease} = 
1267				lists:keysearch(permanent, #release.status,
1268						Releases),
1269		    PermanentVsn = PermanentRelease#release.vsn,
1270		    PermanentEVsn = PermanentRelease#release.erts_vsn,
1271		    case catch do_make_services_permanent(PermanentVsn, 
1272							  Vsn, 
1273							  PermanentEVsn,
1274							  EVsn)  of
1275			{error,Reason} ->
1276			    throw({error,{service_update_failed, Reason}});
1277			_ ->
1278			    ok
1279		    end;
1280		_ ->
1281		    ok
1282	    end,
1283	    ok = init:make_permanent(filename:join(Dir, "start"), Sys),
1284	    {ok, NewReleases, brutal_purge(Unpurged)};
1285	{value, #release{status = permanent}} ->
1286	    {ok, Releases, Unpurged};
1287	{value, #release{status = Status}} ->
1288	    {error, {bad_status, Status}};
1289	false ->
1290	    {error, {no_such_release, Vsn}}
1291    end.
1292
1293
1294do_back_service(OldVersion, CurrentVersion,OldEVsn,CurrentEVsn) ->
1295    NN = hd(string:lexemes(atom_to_list(node()),"@")),
1296    OldName = NN ++ "_" ++ OldVersion,
1297    CurrentName = NN ++ "_" ++ CurrentVersion,
1298    UpdData = case erlsrv:get_service(CurrentEVsn,CurrentName) of
1299		  {error, Error} ->
1300		      throw({error,Error});
1301		  Data ->
1302		      erlsrv:new_service(OldName, Data, [])
1303	      end,
1304    _ = case erlsrv:store_service(OldEVsn,UpdData) of
1305	    ok ->
1306		{ok,_} = erlsrv:disable_service(CurrentEVsn,CurrentName),
1307		{ok,_} = erlsrv:enable_service(OldEVsn,OldName);
1308	    Error2 ->
1309		throw(Error2)
1310	end,
1311    OldErlSrv = filename:nativename(erlsrv:erlsrv(OldEVsn)),
1312    CurrentErlSrv = filename:nativename(erlsrv:erlsrv(CurrentEVsn)),
1313    case heart:set_cmd(CurrentErlSrv ++ " remove " ++ CurrentName ++ 
1314		       " & " ++ OldErlSrv ++ " start " ++ OldName) of
1315	ok ->
1316	    ok;
1317	Error3 ->
1318	    throw({error, {'heart:set_cmd() error', Error3}})
1319    end.
1320
1321do_reboot_old_release(#state{releases = Releases,
1322			     rel_dir = RelDir, masters = Masters,
1323			     static_emulator = Static},
1324		      Vsn) ->
1325    case lists:keysearch(Vsn, #release.vsn, Releases) of
1326	{value, #release{erts_vsn = EVsn, status = old}} ->
1327	    CurrentRunning = case os:type() of
1328				 {win32,nt} ->
1329				     %% Get the current release on NT
1330				     case lists:keysearch(permanent, 
1331							  #release.status,
1332							  Releases) of
1333					 false ->
1334					     lists:keysearch(current,
1335							     #release.status,
1336							     Releases);
1337					 {value,CR} ->
1338					     CR
1339				     end;
1340				 _ ->
1341				     false
1342			     end,
1343	    set_permanent_files(RelDir, EVsn, Vsn, Masters, Static),
1344	    NewReleases = set_status(Vsn, permanent, Releases),
1345	    write_releases(RelDir, NewReleases, Masters),
1346	    case os:type() of
1347		{win32,nt} ->
1348		    %% Edit up the services and set a reasonable heart 
1349		    %% command
1350		    do_back_service(Vsn,CurrentRunning#release.vsn,EVsn,
1351				   CurrentRunning#release.erts_vsn);
1352		_ ->
1353		    ok
1354	    end,
1355	    ok;
1356	{value, #release{status = Status}} ->
1357	    {error, {bad_status, Status}};
1358	false ->
1359	    {error, {no_such_release, Vsn}}
1360    end.
1361
1362%%-----------------------------------------------------------------
1363%% Depending of if the release_handler is running in normal, client or
1364%% client with static emulator the new system version is made permanent
1365%% in different ways.
1366%%-----------------------------------------------------------------
1367set_permanent_files(RelDir, EVsn, Vsn, false, _) ->
1368    write_start(filename:join([RelDir, "start_erl.data"]),
1369		EVsn ++ " " ++ Vsn,
1370		false);
1371set_permanent_files(RelDir, EVsn, Vsn, Masters, false) ->
1372    write_start(filename:join([RelDir, "start_erl.data"]),
1373		EVsn ++ " " ++ Vsn,
1374		Masters);
1375set_permanent_files(RelDir, _EVsn, Vsn, Masters, _Static) ->
1376    VsnDir = filename:join([RelDir, Vsn]),
1377    set_static_files(VsnDir, RelDir, Masters).
1378
1379
1380do_remove_service(Vsn) ->
1381    %% Very unconditionally remove the service.
1382    %% Note that the service could already have been removed when
1383    %% making another release permanent.
1384    ServiceName = hd(string:lexemes(atom_to_list(node()),"@"))
1385	++ "_" ++ Vsn,
1386    case erlsrv:get_service(ServiceName) of
1387	{error, _Error} ->
1388	    ok;
1389	_Data ->
1390	    {ok,_} = erlsrv:remove_service(ServiceName),
1391	    ok
1392    end.
1393
1394do_remove_release(Root, RelDir, Vsn, Releases) ->
1395    % Decide which libs should be removed
1396    case lists:keysearch(Vsn, #release.vsn, Releases) of
1397	{value, #release{status = permanent}} ->
1398	    {error, {permanent, Vsn}};
1399	{value, #release{libs = RemoveLibs, vsn = Vsn, erts_vsn = EVsn}} ->
1400	    case os:type() of
1401		{win32, nt} ->
1402		    do_remove_service(Vsn);
1403		_ ->
1404		    ok
1405	    end,
1406
1407	    NewReleases = lists:keydelete(Vsn, #release.vsn, Releases),
1408	    RemoveThese =
1409		lists:foldl(fun(#release{libs = Libs}, Remove) ->
1410				    diff_dir(Remove, Libs)
1411			    end, RemoveLibs, NewReleases),
1412	    lists:foreach(fun({_Lib, _LVsn, LDir}) ->
1413				  remove_file(LDir)
1414			  end, RemoveThese),
1415	    remove_file(filename:join([RelDir, Vsn])),
1416	    case lists:keysearch(EVsn, #release.erts_vsn, NewReleases) of
1417		{value, _} -> ok;
1418		false -> % Remove erts library, no more references to it
1419		    remove_file(filename:join(Root, "erts-" ++ EVsn))
1420	    end,
1421	    write_releases(RelDir, NewReleases, false),
1422	    {ok, NewReleases};
1423	false ->
1424	    {error, {no_such_release, Vsn}}
1425    end.
1426
1427do_set_unpacked(Root, RelDir, RelFile, LibDirs, Releases, Masters) ->
1428    Release = check_rel(Root, RelF

Large files files are truncated, but you can click here to view the full file