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 cannot 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 cannot 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, RelFile, LibDirs, Masters),
1429    #release{vsn = Vsn} = Release,
1430    case lists:keysearch(Vsn, #release.vsn, Releases) of
1431	{value, _} -> throw({error, {existing_release, Vsn}});
1432	false -> ok
1433    end,
1434    NewReleases = [Release#release{status = unpacked} | Releases],
1435    VsnDir = filename:join([RelDir, Vsn]),
1436    make_dir(VsnDir, Masters),
1437    write_releases(RelDir, NewReleases, Masters),
1438    {ok, NewReleases, Vsn}.
1439
1440do_set_removed(RelDir, Vsn, Releases, Masters) ->
1441    case lists:keysearch(Vsn, #release.vsn, Releases) of
1442	{value, #release{status = permanent}} ->
1443	    {error, {permanent, Vsn}};
1444	{value, _} ->
1445	    NewReleases = lists:keydelete(Vsn, #release.vsn, Releases),
1446	    write_releases(RelDir, NewReleases, Masters),
1447	    {ok, NewReleases};
1448	false ->
1449	    {error, {no_such_release, Vsn}}
1450    end.
1451
1452
1453%%-----------------------------------------------------------------
1454%% A relup file consists of:
1455%%   {Vsn, [{FromVsn, Descr, RhScript}], [{ToVsn, Descr, RhScript}]}.
1456%% It describes how to get to this release from previous releases,
1457%% and how to get from this release to previous releases.
1458%% We can get from a FromVsn that's a substring of CurrentVsn (e.g.
1459%% 1.1 is a substring of 1.1.1, but not 1.2), but when we get to
1460%% ToVsn, we must have an exact match.
1461%%
1462%% We do not put any semantics into the version strings, i.e. we
1463%% don't know if going from Vsn1 to Vsn2 represents a upgrade or
1464%% a downgrade.  For both upgrades and downgrades, the relup file
1465%% is located in the directory of the latest version.  Since we
1466%% do not which version is latest, we first suppose that ToVsn >
1467%% CurrentVsn, i.e. we perform an upgrade.  If we don't find the
1468%% corresponding relup instructions, we check if it's possible to
1469%% downgrade from CurrentVsn to ToVsn.
1470%%-----------------------------------------------------------------
1471get_rh_script(#release{vsn = ?tmp_vsn(CurrentVsn)},
1472	      #release{vsn = ToVsn},
1473	      RelDir,
1474	      Masters) ->
1475    {ok,{Vsn,Descr,[restart_new_emulator|Script]}} =
1476	do_get_rh_script(CurrentVsn,ToVsn,RelDir,Masters),
1477    {ok,{Vsn,Descr,Script}};
1478get_rh_script(#release{vsn = CurrentVsn},
1479	      #release{vsn = ToVsn},
1480	      RelDir,
1481	      Masters) ->
1482    do_get_rh_script(CurrentVsn,ToVsn,RelDir,Masters).
1483
1484do_get_rh_script(CurrentVsn, ToVsn, RelDir, Masters) ->
1485    Relup = filename:join([RelDir, ToVsn, "relup"]),
1486    case try_upgrade(ToVsn, CurrentVsn, Relup, Masters) of
1487	{ok, RhScript} ->
1488	    {ok, RhScript};
1489	_ ->
1490	    Relup2 = filename:join([RelDir, CurrentVsn,"relup"]),
1491	    case try_downgrade(ToVsn, CurrentVsn, Relup2, Masters) of
1492		{ok, RhScript} ->
1493		    {ok, RhScript};
1494		_ ->
1495		    throw({error, {no_matching_relup, ToVsn, CurrentVsn}})
1496	    end
1497    end.
1498
1499try_upgrade(ToVsn, CurrentVsn, Relup, Masters) ->
1500    case consult(Relup, Masters) of
1501	{ok, [{ToVsn, ListOfRhScripts, _}]} ->
1502	    case lists:keysearch(CurrentVsn, 1, ListOfRhScripts) of
1503		{value, RhScript} ->
1504		    {ok, RhScript};
1505		_ ->
1506		    error
1507	    end;
1508	{ok, _} ->
1509	    throw({error, {bad_relup_file, Relup}});
1510	{error, Reason} when is_tuple(Reason) ->
1511	    throw({error, {bad_relup_file, Relup}});
1512	{error, enoent} ->
1513	    error;
1514	{error, FileError} -> % FileError is posix atom | no_master
1515	    throw({error, {FileError, Relup}})
1516    end.
1517
1518try_downgrade(ToVsn, CurrentVsn, Relup, Masters) ->
1519    case consult(Relup, Masters) of
1520	{ok, [{CurrentVsn, _, ListOfRhScripts}]} ->
1521	    case lists:keysearch(ToVsn, 1, ListOfRhScripts) of
1522		{value, RhScript} ->
1523		    {ok, RhScript};
1524		_ ->
1525		    error
1526	    end;
1527	{ok, _} ->
1528	    throw({error, {bad_relup_file, Relup}});
1529	{error, Reason} when is_tuple(Reason) ->
1530	    throw({error, {bad_relup_file, Relup}});
1531	{error, FileError} -> % FileError is posix atom | no_master
1532	    throw({error, {FileError, Relup}})
1533    end.
1534
1535
1536%% Status = current | tmp_current | permanent
1537set_status(Vsn, Status, Releases) ->
1538    lists:zf(fun(Release) when Release#release.vsn == Vsn,
1539		               Release#release.status == permanent ->
1540		     %% If a permanent rel is installed, it keeps its
1541		     %% permanent status (not changed to current).
1542		     %% The current becomes old though.
1543		     true;
1544		(Release) when Release#release.vsn == Vsn ->
1545		     {true, Release#release{status = Status}};
1546		(Release) when Release#release.status == Status ->
1547		     {true, Release#release{status = old}};
1548		(_) ->
1549		     true
1550	     end, Releases).
1551
1552get_latest_release(Releases) ->
1553    case lists:keysearch(current, #release.status, Releases) of
1554	{value, Release} ->
1555	    Release;
1556	false ->
1557	    {value, Release} =
1558		lists:keysearch(permanent, #release.status, Releases),
1559	    Release
1560    end.
1561
1562%% Returns: [{Lib, Vsn, Dir}] to be removed
1563diff_dir([H | T], L) ->
1564    case memlib(H, L) of
1565	true -> diff_dir(T, L);
1566	false -> [H | diff_dir(T, L)]
1567    end;
1568diff_dir([], _) -> [].
1569
1570memlib({Lib, Vsn, _Dir}, [{Lib, Vsn, _Dir2} | _T]) -> true;
1571memlib(Lib, [_H | T]) -> memlib(Lib, T);
1572memlib(_Lib, []) -> false.
1573
1574%% recursively remove file or directory
1575remove_file(File) ->
1576    case file:read_link_info(File) of
1577	{ok, Info} when Info#file_info.type==directory ->
1578	    case file:list_dir(File) of
1579		{ok, Files} ->
1580		    lists:foreach(fun(File2) ->
1581					 remove_file(filename:join(File,File2))
1582				  end, Files),
1583		    case file:del_dir(File) of
1584			ok -> ok;
1585			{error, Reason} -> throw({error, Reason})
1586		    end;
1587		{error, Reason} ->
1588		    throw({error, Reason})
1589	    end;
1590	{ok, _Info} ->
1591	    case file:delete(File) of
1592		ok -> ok;
1593		{error, Reason} -> throw({error, Reason})
1594	    end;
1595	{error, _Reason} ->
1596	    throw({error, {no_such_file, File}})
1597
1598    end.
1599
1600do_write_file(File, Str) ->
1601    do_write_file(File, Str, []).
1602do_write_file(File, Str, FileOpts) ->
1603    case file:open(File, [write | FileOpts]) of
1604	{ok, Fd} ->
1605	    io:put_chars(Fd, Str),
1606	    ok = file:close(Fd);
1607	{error, Reason} ->
1608	    {error, {Reason, File}}
1609    end.
1610
1611%%-----------------------------------------------------------------
1612%% Change current applications (specifically, update their version,
1613%% description and env.)
1614%%-----------------------------------------------------------------
1615change_appl_data(RelDir, #release{vsn = Vsn}, Masters) ->
1616    Dir = filename:join([RelDir, Vsn]),
1617    BootFile = filename:join(Dir, "start.boot"),
1618    case read_file(BootFile, Masters) of
1619	{ok, Bin} ->
1620	    Config = case consult(filename:join(Dir, "sys.config"), Masters) of
1621			 {ok, [Conf]} -> Conf;
1622			 _ -> []
1623		     end,
1624	    Appls = get_appls(binary_to_term(Bin)),
1625	    case application_controller:change_application_data(Appls,Config) of
1626		ok -> Appls;
1627		{error, Reason} -> exit({change_appl_data, Reason})
1628	    end;
1629	{error, _Reason} ->
1630	    throw({error, {no_such_file, BootFile}})
1631    end.
1632
1633%%-----------------------------------------------------------------
1634%% This function is dependent on the application functions and
1635%% the start script syntax.
1636%%-----------------------------------------------------------------
1637get_appls({script, _, Script}) -> get_appls(Script, []).
1638
1639%% kernel is taken care of separately
1640get_appls([{kernelProcess, application_controller,
1641	    {application_controller, start, [App]}} |T], Res) ->
1642    get_appls(T, [App | Res]);
1643%% other applications but kernel
1644get_appls([{apply, {application, load, [App]}} |T], Res) ->
1645    get_appls(T, [App | Res]);
1646get_appls([_ | T], Res) ->
1647    get_appls(T, Res);
1648get_appls([], Res) ->
1649    Res.
1650
1651
1652mon_nodes(true) ->
1653    ok = net_kernel:monitor_nodes(true);
1654mon_nodes(false) ->
1655    ok = net_kernel:monitor_nodes(false),
1656    flush().
1657
1658flush() ->
1659    receive
1660	{nodedown, _} -> flush();
1661	{nodeup, _} -> flush()
1662    after
1663	0 -> ok
1664    end.
1665
1666prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn},
1667		   #release{erts_vsn = PermEVsn, vsn = PermVsn},
1668		   DataFileName) ->
1669    CurrentServiceName = hd(string:lexemes(atom_to_list(node()),"@"))
1670	++ "_" ++ PermVsn,
1671    FutureServiceName = hd(string:lexemes(atom_to_list(node()),"@"))
1672	++ "_" ++ Vsn,
1673    CurrentService = case erlsrv:get_service(PermEVsn,CurrentServiceName) of
1674			 {error, _} = Error1 ->
1675			     throw(Error1);
1676			 CS ->
1677			     CS
1678		     end,
1679    FutureService =  erlsrv:new_service(FutureServiceName,
1680					CurrentService,
1681					filename:nativename(DataFileName),
1682					%% This is rather icky... On a
1683					%% non permanent service, the
1684					%% ERLSRV_SERVICE_NAME is
1685					%% actually that of an old service,
1686					%% to make heart commands work...
1687					CurrentServiceName),
1688
1689    case erlsrv:store_service(EVsn, FutureService) of
1690	{error, _} = Error2 ->
1691	    throw(Error2);
1692	_X ->
1693	    {ok,_} = erlsrv:disable_service(EVsn, FutureServiceName),
1694	    ErlSrv = filename:nativename(erlsrv:erlsrv(EVsn)),
1695	    StartDisabled = ErlSrv ++ " start_disabled " ++ FutureServiceName,
1696	    case heart:set_cmd(StartDisabled) of
1697		ok ->
1698		    ok;
1699		Error3 ->
1700		    throw({error, {'heart:set_cmd() error', Error3}})
1701	    end
1702    end.
1703
1704%%-----------------------------------------------------------------
1705%% Set things up for restarting the new emulator.  The actual
1706%% restart is performed by calling init:reboot() higher up.
1707%%-----------------------------------------------------------------
1708prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
1709			     Vsn, Release, Releases, Masters) ->
1710    {value, PRelease} = lists:keysearch(permanent, #release.status,Releases),
1711    NReleases1 = set_status(Vsn, current, Releases),
1712    NReleases2 = set_status(Vsn,tmp_current,NReleases1),
1713    write_releases(RelDir, NReleases2, Masters),
1714    prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
1715				 Release, PRelease, Masters).
1716
1717prepare_restart_new_emulator(StartPrg, RootDir, RelDir,
1718			     Release, PRelease, Masters) ->
1719    #release{erts_vsn = EVsn, vsn = Vsn} = Release,
1720    Data = EVsn ++ " " ++ Vsn,
1721    DataFile = write_new_start_erl(Data, RelDir, Masters),
1722    %% Tell heart to use DataFile instead of start_erl.data
1723    case os:type() of
1724	{win32,nt} ->
1725	    write_ini_file(RootDir,EVsn,Masters),
1726	    prepare_restart_nt(Release,PRelease,DataFile);
1727	{unix,_} ->
1728	    StartP = check_start_prg(StartPrg, Masters),
1729	    case heart:set_cmd(StartP ++ " " ++ DataFile) of
1730		ok ->
1731		    ok;
1732		Error ->
1733		    throw({error, {'heart:set_cmd() error', Error}})
1734	    end
1735    end.
1736
1737check_start_prg({do_check, StartPrg}, Masters) ->
1738    check_file(StartPrg, regular, Masters),
1739    StartPrg;
1740check_start_prg({_, StartPrg}, _) ->
1741    StartPrg.
1742
1743write_new_start_erl(Data, RelDir, Masters) ->
1744    DataFile = filename:join([RelDir, "new_start_erl.data"]),
1745    write_file(DataFile, Data, Masters),
1746    DataFile.
1747
1748%%-----------------------------------------------------------------
1749%% When a new emulator shall be restarted, the current release
1750%% is written with status tmp_current.  When the new emulator
1751%% is started, this function is called.  The tmp_current release
1752%% gets status unpacked on disk, and current in memory.  If a reboot
1753%% is made (due to a crash), the release is just unpacked.  If a crash
1754%% occurs before a call to transform_release is made, the old emulator
1755%% is started, and transform_release is called for it.  The tmp_current
1756%% release is changed to unpacked.
1757%% If the release is made permanent, this is written to disk.
1758%%-----------------------------------------------------------------
1759transform_release(ReleaseDir, Releases, Masters) ->
1760    case init:script_id() of
1761	{Name, ?tmp_vsn(_)=TmpVsn} ->
1762	    %% This is was a reboot due to a new emulator version. The
1763	    %% current release is a temporary internal release, which
1764	    %% must be removed. It is the "real new release" that is
1765	    %% set to unpacked on disk and current in memory.
1766	    DReleases = lists:keydelete(TmpVsn,#release.vsn,Releases),
1767	    write_releases(ReleaseDir, DReleases, Masters),
1768	    set_current({Name,TmpVsn},Releases);
1769	ScriptId ->
1770	    F = fun(Release) when Release#release.status == tmp_current ->
1771			Release#release{status = unpacked};
1772		   (Release) -> Release
1773		end,
1774	    case lists:map(F, Releases) of
1775		Releases ->
1776		    Releases;
1777		DReleases ->
1778		    write_releases(ReleaseDir, DReleases, Masters),
1779		    set_current(ScriptId, Releases)
1780	    end
1781    end.
1782
1783set_current(ScriptId, Releases) ->
1784    F1 = fun(Release) when Release#release.status == tmp_current ->
1785		 case ScriptId of
1786		     {_Name,Vsn} when Release#release.vsn == Vsn ->
1787			 Release#release{status = current};
1788		     _ ->
1789			 Release#release{status = unpacked}
1790		 end;
1791	    (Release) -> Release
1792	 end,
1793    lists:map(F1, Releases).
1794
1795%%-----------------------------------------------------------------
1796%% Functions handling files, RELEASES, start_erl.data etc.
1797%% This functions consider if the release_handler is a client and
1798%% in that case performs the operations at all master nodes or at
1799%% none (in case of failure).
1800%%-----------------------------------------------------------------
1801
1802check_opt_file(FileName, Type, Masters) ->
1803    case catch check_file(FileName, Type, Masters) of
1804	ok ->
1805	    true;
1806	_Error ->
1807	    io:format("Warning: ~tp missing (optional)~n", [FileName]),
1808	    false
1809    end.
1810
1811check_file(FileName, Type, false) ->
1812    do_check_file(FileName, Type);
1813check_file(FileName, Type, Masters) ->
1814    check_file_masters(FileName, Type, Masters).
1815
1816%% Check that file exists at all masters.
1817check_file_masters(FileName, Type, [Master|Masters]) ->
1818    do_check_file(Master, FileName, Type),
1819    check_file_masters(FileName, Type, Masters);
1820check_file_masters(_FileName, _Type, []) ->
1821    ok.
1822
1823%% Type == regular | directory
1824do_check_file(FileName, Type) ->
1825    case file:read_file_info(FileName) of
1826	{ok, Info} when Info#file_info.type==Type -> ok;
1827	{error, _Reason} -> throw({error, {no_such_file, FileName}})
1828    end.
1829
1830do_check_file(Master, FileName, Type) ->
1831    case rpc:call(Master, file, read_file_info, [FileName]) of
1832	{ok, Info} when Info#file_info.type==Type -> ok;
1833	_ -> throw({error, {no_such_file, {Master, FileName}}})
1834    end.
1835
1836%%-----------------------------------------------------------------
1837%% If Rel doesn't exists in tar it could have been created
1838%% by the user in another way, i.e. ignore this here.
1839%%-----------------------------------------------------------------
1840extract_rel_file(Rel, Tar, Root) ->
1841    _ = erl_tar:extract(Tar, [{files, [Rel]}, {cwd, Root}, compressed]).
1842
1843extract_tar(Root, Tar) ->
1844    case erl_tar:extract(Tar, [keep_old_files, {cwd, Root}, compressed]) of
1845	ok ->
1846	    ok;
1847	{error, {Name, Reason}} ->		% New erl_tar (R3A).
1848	    throw({error, {cannot_extract_file, Name, Reason}})
1849    end.
1850
1851write_releases(Dir, Releases, Masters) ->
1852    %% We must never write 'current' to disk, since this will confuse
1853    %% us after a node restart - since we would then have a permanent
1854    %% release running, but state set to current for a non-running
1855    %% release.
1856    NewReleases = lists:zf(fun(Release) when Release#release.status == current ->
1857				   {true, Release#release{status = unpacked}};
1858			      (_) ->
1859				   true
1860			   end, Releases),
1861    write_releases_1(Dir, NewReleases, Masters).
1862
1863
1864write_releases_1(Dir, NewReleases, false) ->
1865    case do_write_release(Dir, "RELEASES", NewReleases) of
1866	ok    -> ok;
1867	Error -> throw(Error)
1868    end;
1869write_releases_1(Dir, NewReleases, Masters) ->
1870    all_masters(Masters),
1871    write_releases_m(Dir, NewReleases, Masters).
1872
1873do_write_release(Dir, RELEASES, NewReleases) ->
1874    case file:open(filename:join(Dir, RELEASES), [write,{encoding,utf8}]) of
1875	{ok, Fd} ->
1876	    ok = io:format(Fd, "%% ~s~n~tp.~n",
1877                           [epp:encoding_to_string(utf8),NewReleases]),
1878	    ok = file:close(Fd);
1879	{error, Reason} ->
1880	    {error, Reason}
1881    end.
1882
1883%%-----------------------------------------------------------------
1884%% Write the "RELEASES" file at all master nodes.
1885%%   1. Save "RELEASES.backup" at all nodes.
1886%%   2. Save "RELEASES.change" at all nodes.
1887%%   3. Update the "RELEASES.change" file at all nodes.
1888%%   4. Move "RELEASES.change" to "RELEASES".
1889%%   5. Remove "RELEASES.backup" at all nodes.
1890%%
1891%% If one of the steps above fails, all steps is recovered from
1892%% (as long as possible), except for 5 which is allowed to fail.
1893%%-----------------------------------------------------------------
1894write_releases_m(Dir, NewReleases, Masters) ->
1895    RelFile = filename:join(Dir, "RELEASES"),
1896    Backup = filename:join(Dir, "RELEASES.backup"),
1897    Change = filename:join(Dir, "RELEASES.change"),
1898    ensure_RELEASES_exists(Masters, RelFile),
1899    case at_all_masters(Masters, ?MODULE, do_copy_files,
1900			[RelFile, [Backup, Change]]) of
1901	ok ->
1902	    case at_all_masters(Masters, ?MODULE, do_write_release,
1903				[Dir, "RELEASES.change", NewReleases]) of
1904		ok ->
1905		    case at_all_masters(Masters, file, rename,
1906					[Change, RelFile]) of
1907			ok ->
1908			    remove_files(all, [Backup, Change], Masters),
1909			    ok;
1910			{error, {Master, R}} ->
1911			    takewhile(Master, Masters, file, rename,
1912				      [Backup, RelFile]),
1913			    remove_files(all, [Backup, Change], Masters),
1914			    throw({error, {Master, R, move_releases}})
1915		    end;
1916		{error, {Master, R}} ->
1917		    remove_files(all, [Backup, Change], Masters),
1918		    throw({error, {Master, R, update_releases}})
1919	    end;
1920	{error, {Master, R}} ->
1921	    remove_files(Master, [Backup, Change], Masters),
1922	    throw({error, {Master, R, backup_releases}})
1923    end.
1924
1925ensure_RELEASES_exists(Masters, RelFile) ->
1926    case at_all_masters(Masters, ?MODULE, do_ensure_RELEASES, [RelFile]) of
1927	ok ->
1928	    ok;
1929	{error, {Master, R}} ->
1930	    throw({error, {Master, R, ensure_RELEASES_exists}})
1931    end.
1932
1933copy_file(File, Dir, false) ->
1934    case do_copy_file(File, Dir) of
1935	ok    -> ok;
1936	Error -> throw(Error)
1937    end;
1938copy_file(File, Dir, Masters) ->
1939    all_masters(Masters),
1940    copy_file_m(File, Dir, Masters).
1941
1942%%-----------------------------------------------------------------
1943%% copy File to Dir at every master node.
1944%% If an error occurs at a node, the total copy failed.
1945%% We do not have to cleanup in case of failure as this
1946%% copy_file is harmless.
1947%%-----------------------------------------------------------------
1948copy_file_m(File, Dir, [Master|Masters]) ->
1949    case rpc:call(Master, ?MODULE, do_copy_file, [File, Dir]) of
1950	ok                   -> copy_file_m(File, Dir, Masters);
1951	{error, {Reason, F}} -> throw({error, {Master, Reason, F}});
1952	Other                -> throw({error, {Master, Other, File}})
1953    end;
1954copy_file_m(_File, _Dir, []) ->
1955    ok.
1956
1957do_copy_file(File, Dir) ->
1958    File2 = filename:join(Dir, filename:basename(File)),
1959    do_copy_file1(File, File2).
1960
1961do_copy_file1(File, File2) ->
1962    case file:read_file(File) of
1963	{ok, Bin} ->
1964	    case file:write_file(File2, Bin) of
1965		ok -> ok;
1966		{error, Reason} ->
1967		    {error, {Reason, File2}}
1968	    end;
1969	{error, Reason} ->
1970	    {error, {Reason, File}}
1971    end.
1972
1973%%-----------------------------------------------------------------
1974%% Copy File to a list of files.
1975%%-----------------------------------------------------------------
1976do_copy_files(File, [ToFile|ToFiles]) ->
1977    case do_copy_file1(File, ToFile) of
1978	ok    -> do_copy_files(File, ToFiles);
1979	Error -> Error
1980    end;
1981do_copy_files(_, []) ->
1982    ok.
1983
1984%%-----------------------------------------------------------------
1985%% Copy each Src file to Dest file in the list of files.
1986%%-----------------------------------------------------------------
1987do_copy_files([{Src, Dest}|Files]) ->
1988    case do_copy_file1(Src, Dest) of
1989	ok    -> do_copy_files(Files);
1990	Error -> Error
1991    end;
1992do_copy_files([]) ->
1993    ok.
1994
1995%%-----------------------------------------------------------------
1996%% Rename each Src file to Dest file in the list of files.
1997%%-----------------------------------------------------------------
1998do_rename_files([{Src, Dest}|Files]) ->
1999    case file:rename(Src, Dest) of
2000	ok    -> do_rename_files(Files);
2001	Error -> Error
2002    end;
2003do_rename_files([]) ->
2004    ok.
2005
2006%%-----------------------------------------------------------------
2007%% Remove a list of files. Ignore failure.
2008%%-----------------------------------------------------------------
2009do_remove_files([File|Files]) ->
2010    _ = file:delete(File),
2011    do_remove_files(Files);
2012do_remove_files([]) ->
2013    ok.
2014
2015
2016%%-----------------------------------------------------------------
2017%% Ensure that the RELEASES file exists.
2018%% If not create an empty RELEASES file.
2019%%-----------------------------------------------------------------
2020do_ensure_RELEASES(RelFile) ->
2021    case file:read_file_info(RelFile) of
2022	{ok, _} -> ok;
2023	_       -> do_write_file(RelFile, "[]. ")
2024    end.
2025
2026%%-----------------------------------------------------------------
2027%% Make a directory, ignore failures (captured later).
2028%%-----------------------------------------------------------------
2029make_dir(Dir, false) ->
2030    _ = file:make_dir(Dir),
2031    ok;
2032make_dir(Dir, Masters) ->
2033    lists:foreach(fun(Master) -> rpc:call(Master, file, make_dir, [Dir]) end,
2034		  Masters).
2035
2036%%-----------------------------------------------------------------
2037%% Check that all masters are alive.
2038%%-----------------------------------------------------------------
2039all_masters(Masters) ->
2040    case rpc:multicall(Masters, erlang, info, [version]) of
2041	{_, []}       -> ok;
2042	{_, BadNodes} -> throw({error, {bad_masters, BadNodes}})
2043    end.
2044
2045%%-----------------------------------------------------------------
2046%% Evaluate {M,F,A} at all masters.
2047%% {M,F,A} is supposed to return ok. Otherwise at_all_masters
2048%% returns {error, {Master, Other}}.
2049%%-----------------------------------------------------------------
2050at_all_masters([Master|Masters], M, F, A) ->
2051    case rpc:call(Master, M, F, A) of
2052	ok    -> at_all_masters(Masters, M, F, A);
2053	Error -> {error, {Master, Error}}
2054    end;
2055at_all_masters([], _, _, _) ->
2056    ok.
2057
2058%%-----------------------------------------------------------------
2059%% Evaluate {M,F,A} at all masters until Master is found.
2060%% Ignore {M,F,A} return value.
2061%%-----------------------------------------------------------------
2062takewhile(Master, Masters, M, F, A) ->
2063    _ = lists:takewhile(fun(Ma) when Ma == Master ->
2064				false;
2065			   (Ma) ->
2066				rpc:call(Ma, M, F, A),
2067				true
2068			end, Masters),
2069    ok.
2070
2071consult(File, false)   -> file:consult(File);
2072consult(File, Masters) -> consult_master(Masters, File).
2073
2074%%-----------------------------------------------------------------
2075%% consult the File at any master node.
2076%% If the file does not exist at one node it should
2077%% not exist at any other node either.
2078%%-----------------------------------------------------------------
2079consult_master([Master|Ms], File) ->
2080    case rpc:call(Master, file, consult, [File]) of
2081	{badrpc, _} -> consult_master(Ms, File);
2082	Res         -> Res
2083    end;
2084consult_master([], _File) ->
2085    {error, no_master}.
2086
2087read_file(File, false) ->
2088    file:read_file(File);
2089read_file(File, Masters) ->
2090    read_master(Masters, File).
2091
2092write_file(File, Data, false) ->
2093    case file:write_file(File, Data) of
2094	ok    -> ok;
2095	Error -> throw(Error)
2096    end;
2097write_file(File, Data, Masters) ->
2098    case at_all_masters(Masters, file, write_file, [File, Data]) of
2099	ok    -> ok;
2100	Error -> throw(Error)
2101    end.
2102
2103ensure_dir(File, false) ->
2104    case filelib:ensure_dir(File) of
2105	ok -> ok;
2106	Error -> throw(Error)
2107    end;
2108ensure_dir(File, Masters) ->
2109    case at_all_masters(Masters,filelib,ensure_dir,[File]) of
2110	ok -> ok;
2111	Error -> throw(Error)
2112    end.
2113
2114remove_dir(Dir, false) ->
2115    remove_file(Dir);
2116remove_dir(Dir, Masters) ->
2117    case at_all_masters(Masters,?MODULE,remove_file,[Dir]) of
2118	ok -> ok;
2119	Error -> throw(Error)
2120    end.
2121
2122
2123%% Ignore status of each delete !
2124remove_files(Master, Files, Masters) ->
2125    takewhile(Master, Masters, ?MODULE, do_remove_files, [Files]).
2126
2127%%-----------------------------------------------------------------
2128%% read the File at any master node.
2129%% If the file does not exist at one node it should
2130%% not exist at any other node either.
2131%%-----------------------------------------------------------------
2132read_master([Master|Ms], File) ->
2133    case rpc:call(Master, file, read_file, [File]) of
2134	{badrpc, _} -> read_master(Ms, File);
2135	Res         -> Res
2136    end;
2137read_master([], _File) ->
2138    {error, no_master}.
2139
2140%%-----------------------------------------------------------------
2141%% Write start_erl.data.
2142%%-----------------------------------------------------------------
2143write_start(File, Data, false) ->
2144    case do_write_file(File, Data) of
2145	ok    -> ok;
2146	Error -> throw(Error)
2147    end;
2148write_start(File, Data, Masters) ->
2149    all_masters(Masters),
2150    safe_write_file_m(File, Data, Masters).
2151
2152
2153%%-----------------------------------------------------------------
2154%% Copy the "start.boot" and "sys.config" from SrcDir to DestDir at all
2155%% master nodes.
2156%%   1. Save DestDir/"start.backup" and DestDir/"sys.backup" at all nodes.
2157%%   2. Copy files at all nodes.
2158%%   3. Remove backup files at all nodes.
2159%%
2160%% If one of the steps above fails, all steps is recovered from
2161%% (as long as possible), except for 3 which is allowed to fail.
2162%%-----------------------------------------------------------------
2163set_static_files(SrcDir, DestDir, Masters) ->
2164    all_masters(Masters),
2165    Boot = "start.boot",
2166    Config = "sys.config",
2167    SrcBoot = filename:join(SrcDir, Boot),
2168    DestBoot = filename:join(DestDir, Boot),
2169    BackupBoot = filename:join(DestDir, "start.backup"),
2170    SrcConf = filename:join(SrcDir, Config),
2171    DestConf = filename:join(DestDir, Config),
2172    BackupConf = filename:join(DestDir, "sys.backup"),
2173
2174    case at_all_masters(Masters, ?MODULE, do_copy_files,
2175			[[{DestBoot, BackupBoot},
2176			  {DestConf, BackupConf}]]) of
2177	ok ->
2178	    case at_all_masters(Masters, ?MODULE, do_copy_files,
2179				[[{SrcBoot, DestBoot},
2180				  {SrcConf, DestConf}]]) of
2181		ok ->
2182		    remove_files(all, [BackupBoot, BackupConf], Masters),
2183		    ok;
2184		{error, {Master, R}} ->
2185		    takewhile(Master, Masters, ?MODULE, do_rename_files,
2186			      [{BackupBoot, DestBoot},
2187			       {BackupConf, DestConf}]),
2188		    remove_files(all, [BackupBoot, BackupConf], Masters),
2189		    throw({error, {Master, R, copy_start_config}})
2190	    end;
2191	{error, {Master, R}} ->
2192	    remove_files(Master, [BackupBoot, BackupConf], Masters),
2193	    throw({error, {Master, R, backup_start_config}})
2194    end.
2195
2196%%-----------------------------------------------------------------
2197%% Write erl.ini
2198%% Writes the erl.ini file used by erl.exe when (re)starting the erlang node.
2199%% At first installation, this is done by Install.exe, which means that if
2200%% the format of this file for some reason is changed, then Install.c must
2201%% also be updated (and probably some other c-files which read erl.ini)
2202%%-----------------------------------------------------------------
2203write_ini_file(RootDir,EVsn,Masters) ->
2204   BinDir = filename:join([RootDir,"erts-"++EVsn,"bin"]),
2205   Str0 = io_lib:format("[erlang]~n"
2206                        "Bindir=~ts~n"
2207                        "Progname=erl~n"
2208                        "Rootdir=~ts~n",
2209		        [filename:nativename(BinDir),
2210		         filename:nativename(RootDir)]),
2211   Str = re:replace(Str0,"\\\\","\\\\\\\\",[{return,list},global,unicode]),
2212   IniFile = filename:join(BinDir,"erl.ini"),
2213   do_write_ini_file(IniFile,Str,Masters).
2214
2215do_write_ini_file(File,Data,false) ->
2216    case do_write_file(File, Data, [{encoding,utf8}]) of
2217	ok    -> ok;
2218	Error -> throw(Error)
2219    end;
2220do_write_ini_file(File,Data,Masters) ->
2221    all_masters(Masters),
2222    safe_write_file_m(File, Data, [{encoding,utf8}], Masters).
2223
2224
2225%%-----------------------------------------------------------------
2226%% Write the given file at all master nodes.
2227%%   1. Save <File>.backup at all nodes.
2228%%   2. Write <File>.change at all nodes.
2229%%   3. Move <File>.change to <File>
2230%%   4. Remove <File>.backup at all nodes.
2231%%
2232%% If one of the steps above fails, all steps are recovered from
2233%% (as long as possible), except for 4 which is allowed to fail.
2234%%-----------------------------------------------------------------
2235safe_write_file_m(File, Data, Masters) ->
2236    safe_write_file_m(File, Data, [], Masters).
2237safe_write_file_m(File, Data, FileOpts, Masters) ->
2238    Backup = File ++ ".backup",
2239    Change = File ++ ".change",
2240    case at_all_masters(Masters, ?MODULE, do_copy_files,
2241			[File, [Backup]]) of
2242	ok ->
2243	    case at_all_masters(Masters, ?MODULE, do_write_file,
2244				[Change, Data, FileOpts]) of
2245		ok ->
2246		    case at_all_masters(Masters, file, rename,
2247					[Change, File]) of
2248			ok ->
2249			    remove_files(all, [Backup, Change], Masters),
2250			    ok;
2251			{error, {Master, R}} ->
2252			    takewhile(Master, Masters, file, rename,
2253				      [Backup, File]),
2254			    remove_files(all, [Backup, Change], Masters),
2255			    throw({error, {Master, R, rename,
2256					   filename:basename(Change),
2257					   filename:basename(File)}})
2258		    end;
2259		{error, {Master, R}} ->
2260		    remove_files(all, [Backup, Change], Masters),
2261		    throw({error, {Master, R, write, filename:basename(Change)}})
2262	    end;
2263	{error, {Master, R}} ->
2264	    remove_files(Master, [Backup], Masters),
2265	    throw({error, {Master, R, backup,
2266			   filename:basename(File),
2267			   filename:basename(Backup)}})
2268    end.
2269
2270%%-----------------------------------------------------------------
2271%% Figure out which applications that have changed version between the
2272%% two releases. The paths for these applications must always be
2273%% updated, even if the relup script does not load any modules. See
2274%% OTP-9402.
2275%%
2276%% A different situation is when the same application version is used
2277%% in old and new release, but the path has changed. This is not
2278%% handled here - instead it must be explicitely indicated by the
2279%% 'update_paths' option to release_handler:install_release/2 if the
2280%% code path shall be updated then.
2281%% -----------------------------------------------------------------
2282get_new_libs([{App,Vsn,_LibDir}|CurrentLibs], NewLibs) ->
2283    case lists:keyfind(App,1,NewLibs) of
2284	{App,NewVsn,_} = LibInfo when NewVsn =/= Vsn ->
2285	    [LibInfo | get_new_libs(CurrentLibs,NewLibs)];
2286	_ ->
2287	    get_new_libs(CurrentLibs,NewLibs)
2288    end;
2289get_new_libs([],_) ->
2290    [].
2291
2292%%-----------------------------------------------------------------
2293%% Return a list of releases witch a specific status
2294%%-----------------------------------------------------------------
2295get_releases_with_status([], _, Acc) ->
2296    Acc;
2297get_releases_with_status([ {_, _, _, ReleaseStatus } = Head | Tail],
2298                         Status, Acc) when ReleaseStatus == Status ->
2299    get_releases_with_status(Tail, Status, [Head | Acc]);
2300get_releases_with_status([_ | Tail], Status, Acc) ->
2301    get_releases_with_status(Tail, Status, Acc).
2302