/lib/sasl/src/release_handler.erl
Erlang | 2301 lines | 1600 code | 188 blank | 513 comment | 18 complexity | 0a680c04422a6b5be62be39428a201c6 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- %%
- %% %CopyrightBegin%
- %%
- %% Copyright Ericsson AB 1996-2018. All Rights Reserved.
- %%
- %% Licensed under the Apache License, Version 2.0 (the "License");
- %% you may not use this file except in compliance with the License.
- %% You may obtain a copy of the License at
- %%
- %% http://www.apache.org/licenses/LICENSE-2.0
- %%
- %% Unless required by applicable law or agreed to in writing, software
- %% distributed under the License is distributed on an "AS IS" BASIS,
- %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- %% See the License for the specific language governing permissions and
- %% limitations under the License.
- %%
- %% %CopyrightEnd%
- %%
- -module(release_handler).
- -behaviour(gen_server).
- -include_lib("kernel/include/file.hrl").
- %% External exports
- -export([start_link/0,
- create_RELEASES/1, create_RELEASES/2, create_RELEASES/4,
- unpack_release/1,
- check_install_release/1, check_install_release/2,
- install_release/1, install_release/2, new_emulator_upgrade/2,
- remove_release/1, which_releases/0, which_releases/1,
- make_permanent/1, reboot_old_release/1,
- set_unpacked/2, set_removed/1, install_file/2]).
- -export([upgrade_app/2, downgrade_app/2, downgrade_app/3,
- upgrade_script/2, downgrade_script/3,
- eval_appup_script/4]).
- %% Internal exports
- -export([init/1, handle_call/3, handle_info/2, terminate/2,
- handle_cast/2, code_change/3]).
- %% Internal exports, a client release_handler may call this functions.
- -export([do_write_release/3, do_copy_file/2, do_copy_files/2,
- do_copy_files/1, do_rename_files/1, do_remove_files/1,
- remove_file/1, do_write_file/2, do_write_file/3,
- do_ensure_RELEASES/1]).
- -record(state, {unpurged = [],
- root,
- rel_dir,
- releases,
- timer,
- start_prg,
- masters = false,
- client_dir = false,
- static_emulator = false,
- pre_sync_nodes = []}).
- %%-----------------------------------------------------------------
- %% status action next_status
- %% =============================================
- %% - unpack unpacked
- %% unpacked install current
- %% remove -
- %% current make_permanent permanent
- %% install other old
- %% restart node unpacked
- %% remove -
- %% permanent make other permanent old
- %% install permanent
- %% old reboot_old permanent
- %% install current
- %% remove -
- %%-----------------------------------------------------------------
- %% libs = [{Lib, Vsn, Dir}]
- -record(release, {name, vsn, erts_vsn, libs = [], status}).
- -define(timeout, 10000).
- %%-----------------------------------------------------------------
- %% The version set on the temporary release that will be used when the
- %% emulator is upgraded.
- -define(tmp_vsn(__BaseVsn__), "__new_emulator__"++__BaseVsn__).
- %%-----------------------------------------------------------------
- %% Assumes the following file structure:
- %% root --- lib --- Appl-Vsn1 --- <src>
- %% | | |- ebin
- %% | | |_ priv
- %% | |_ Appl-Vsn2
- %% |
- %% |- bin --- start (default; {sasl, start_prg} overrides
- %% | |- run_erl
- %% | |- start_erl (reads start_erl.data)
- %% | |_ <to_erl>
- %% |
- %% |- erts-EVsn1 --- bin --- <jam44>
- %% | |- <epmd>
- %% | |_ erl
- %% |- erts-EVsn2
- %% |
- %% |- clients --- ClientName1 --- bin -- start
- %% <clients use same lib and erts as master>
- %% | | |_ releases --- start_erl.data
- %% | | |_ Vsn1 -- start.boot
- %% | |_ ClientName2
- %% |
- %% |- clients --- Type1 --- lib
- %% <clients use own lib and erts>
- %% | | |- erts-EVsn
- %% | | |- bin -- start
- %% | | |_ ClientName1 -- releases -- start_erl.data
- %% | | |_ start.boot (static)
- %% | | |_ Vsn1
- %% | |_ Type2
- %% |
- %% |- releases --- RELEASES
- %% | |_ <Vsn1.tar.Z>
- %% | |
- %% | |- start_erl.data (generated by rh)
- %% | |
- %% | |_ Vsn1 --- start.boot
- %% | | |- <sys.config>
- %% | | |_ relup
- %% | |_ Vsn2
- %% |
- %% |- log --- erlang.log.N (1 .. 5)
- %%
- %% where <Name> means 'for example Name', and root is
- %% init:get_argument(root)
- %%
- %% It is configurable where the start file is located, and what it
- %% is called.
- %% The paramater is {sasl, start_prg} = File
- %% It is also configurable where the releases directory is located.
- %% Default is $ROOT/releases. $RELDIR overrids, and
- %% {sasl, releases_dir} overrides both.
- %%-----------------------------------------------------------------
- start_link() ->
- gen_server:start_link({local, release_handler}, ?MODULE, [], []).
- %%-----------------------------------------------------------------
- %% Args: ReleaseName is the name of the package file
- %% (without .tar.Z (.tar on non unix systems))
- %% Purpose: Copies all files in the release package to their
- %% directories. Checks that all required libs and erts
- %% files are present.
- %% Returns: {ok, Vsn} | {error, Reason}
- %% Reason = {existing_release, Vsn} |
- %% {no_such_file, File} |
- %% {bad_rel_file, RelFile} |
- %% {file_missing, FileName} | (in the tar package)
- %% exit_reason()
- %%-----------------------------------------------------------------
- unpack_release(ReleaseName) ->
- call({unpack_release, ReleaseName}).
-
- %%-----------------------------------------------------------------
- %% Purpose: Checks the relup script for the specified version.
- %% The release must be unpacked.
- %% Options = [purge] - all old code that can be soft purged
- %% will be purged if all checks succeeds. This can be usefull
- %% in order to reduce time needed in the following call to
- %% install_release.
- %% Returns: {ok, FromVsn, Descr} | {error, Reason}
- %% Reason = {illegal_option, IllegalOpt} |
- %% {already_installed, Vsn} |
- %% {bad_relup_file, RelFile} |
- %% {no_such_release, Vsn} |
- %% {no_such_from_vsn, Vsn} |
- %% exit_reason()
- %%-----------------------------------------------------------------
- check_install_release(Vsn) ->
- check_install_release(Vsn, []).
- check_install_release(Vsn, Opts) ->
- case check_check_install_options(Opts, false) of
- {ok,Purge} ->
- call({check_install_release, Vsn, Purge});
- Error ->
- Error
- end.
- check_check_install_options([purge|Opts], _) ->
- check_check_install_options(Opts, true);
- check_check_install_options([Illegal|_],_Purge) ->
- {error,{illegal_option,Illegal}};
- check_check_install_options([],Purge) ->
- {ok,Purge}.
- %%-----------------------------------------------------------------
- %% Purpose: Executes the relup script for the specified version.
- %% The release must be unpacked.
- %% Returns: {ok, FromVsn, Descr} |
- %% {continue_after_restart, FromVsn, Descr} |
- %% {error, Reason}
- %% Reason = {already_installed, Vsn} |
- %% {bad_relup_file, RelFile} |
- %% {no_such_release, Vsn} |
- %% {no_such_from_vsn, Vsn} |
- %% {could_not_create_hybrid_boot,Why} |
- %% {missing_base_app,Vsn,App} |
- %% {illegal_option, Opt}} |
- %% exit_reason()
- %%-----------------------------------------------------------------
- install_release(Vsn) ->
- call({install_release, Vsn, restart, []}).
- install_release(Vsn, Opt) ->
- case check_install_options(Opt, restart, []) of
- {ok, ErrorAction, InstallOpt} ->
- call({install_release, Vsn, ErrorAction, InstallOpt});
- Error ->
- Error
- end.
- check_install_options([Opt | Opts], ErrAct, InstOpts) ->
- case install_option(Opt) of
- {error_action, EAct} ->
- check_install_options(Opts, EAct, InstOpts);
- true ->
- check_install_options(Opts, ErrAct, [Opt | InstOpts]);
- false ->
- {error, {illegal_option, Opt}}
- end;
- check_install_options([], ErrAct, InstOpts) ->
- {ok, ErrAct, InstOpts}.
- install_option(Opt = {error_action, reboot}) -> Opt;
- install_option(Opt = {error_action, restart}) -> Opt;
- install_option({code_change_timeout, TimeOut}) ->
- check_timeout(TimeOut);
- install_option({suspend_timeout, TimeOut}) ->
- check_timeout(TimeOut);
- install_option({update_paths, Bool}) when Bool==true; Bool==false ->
- true;
- install_option(_Opt) -> false.
- check_timeout(infinity) -> true;
- check_timeout(Int) when is_integer(Int), Int > 0 -> true;
- check_timeout(_Else) -> false.
- %%-----------------------------------------------------------------
- %% Purpose: Called by boot script after emulator is restarted due to
- %% new erts version.
- %% Returns: Same as install_release/2
- %% If this crashes, the emulator restart will fail
- %% (since the function is called from the boot script)
- %% and there will be a rollback.
- %%-----------------------------------------------------------------
- new_emulator_upgrade(Vsn, Opts) ->
- Result = call({install_release, Vsn, reboot, Opts}),
- error_logger:info_msg(
- "~w:install_release(~p,~p) completed after node restart "
- "with new emulator version~nResult: ~p~n",[?MODULE,Vsn,Opts,Result]),
- Result.
- %%-----------------------------------------------------------------
- %% Purpose: Makes the specified release version be the one that is
- %% used when the system starts (or restarts).
- %% The release must be installed (not unpacked).
- %% Returns: ok | {error, Reason}
- %% Reason = {bad_status, Status} |
- %% {no_such_release, Vsn} |
- %% exit_reason()
- %%-----------------------------------------------------------------
- make_permanent(Vsn) ->
- call({make_permanent, Vsn}).
- %%-----------------------------------------------------------------
- %% Purpose: Reboots the system from an old release.
- %%-----------------------------------------------------------------
- reboot_old_release(Vsn) ->
- call({reboot_old_release, Vsn}).
- %%-----------------------------------------------------------------
- %% Purpose: Deletes all files and directories used by the release
- %% version, that are not used by any other release.
- %% The release must not be permanent.
- %% Returns: ok | {error, Reason}
- %% Reason = {permanent, Vsn} |
- %%-----------------------------------------------------------------
- remove_release(Vsn) ->
- call({remove_release, Vsn}).
- %%-----------------------------------------------------------------
- %% Args: RelFile = string()
- %% Libs = [{Lib, LibVsn, Dir}]
- %% Lib = LibVsn = Dir = string()
- %% Purpose: Tells the release handler that a release has been
- %% unpacked, without using the function unpack_release/1.
- %% RelFile is an absolute file name including the extension
- %% .rel.
- %% The release dir will be created. The necessary files can
- %% be installed by calling install_file/2.
- %% The release_handler remebers where all libs are located.
- %% If remove_release is called later,
- %% those libs are removed as well (if no other releases uses
- %% them).
- %% Returns: ok | {error, Reason}
- %%-----------------------------------------------------------------
- set_unpacked(RelFile, LibDirs) ->
- call({set_unpacked, RelFile, LibDirs}).
- %%-----------------------------------------------------------------
- %% Args: Vsn = string()
- %% Purpose: Makes it possible to handle removal of releases
- %% outside the release_handler.
- %% This function won't delete any files at all.
- %% Returns: ok | {error, Reason}
- %%-----------------------------------------------------------------
- set_removed(Vsn) ->
- call({set_removed, Vsn}).
- %%-----------------------------------------------------------------
- %% Purpose: Makes it possible to install the start.boot,
- %% sys.config and relup files if they are not part of a
- %% standard release package. May be used to
- %% install files that are generated, before install_release
- %% is called.
- %% Returns: ok | {error, {no_such_release, Vsn}}
- %%-----------------------------------------------------------------
- install_file(Vsn, File) when is_list(File) ->
- call({install_file, File, Vsn}).
- %%-----------------------------------------------------------------
- %% Returns: [{Name, Vsn, [LibName], Status}]
- %% Status = unpacked | current | permanent | old
- %%-----------------------------------------------------------------
- which_releases() ->
- call(which_releases).
- %%-----------------------------------------------------------------
- %% Returns: [{Name, Vsn, [LibName], Status}]
- %% Status = unpacked | current | permanent | old
- %%-----------------------------------------------------------------
- which_releases(Status) ->
- Releases = which_releases(),
- get_releases_with_status(Releases, Status, []).
- %%-----------------------------------------------------------------
- %% check_script(Script, LibDirs) -> ok | {error, Reason}
- %%-----------------------------------------------------------------
- check_script(Script, LibDirs) ->
- release_handler_1:check_script(Script, LibDirs).
- %%-----------------------------------------------------------------
- %% eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
- %% {ok, UnPurged} |
- %% restart_emulator |
- %% {error, Error}
- %% {'EXIT', Reason}
- %% If sync_nodes is present, the calling process must have called
- %% net_kernel:monitor_nodes(true) before calling this function.
- %% No! No other process than the release_handler can ever call this
- %% function, if sync_nodes is used.
- %%
- %% LibDirs is a list of all applications, while NewLibs is a list of
- %% applications that have changed version between the current and the
- %% new release.
- %% -----------------------------------------------------------------
- eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
- catch release_handler_1:eval_script(Script, Apps, LibDirs, NewLibs, Opts).
- %%-----------------------------------------------------------------
- %% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason}
- %% Types: Root = RelFile = string()
- %% Purpose: Creates an initial RELEASES file.
- %%-----------------------------------------------------------------
- create_RELEASES([Root, RelFile | LibDirs]) ->
- create_RELEASES(Root, filename:join(Root, "releases"), RelFile, LibDirs).
- create_RELEASES(Root, RelFile) ->
- create_RELEASES(Root, filename:join(Root, "releases"), RelFile, []).
- create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
- case catch check_rel(Root, RelFile, LibDirs, false) of
- {error, Reason } ->
- {error, Reason};
- Rel ->
- Rel2 = Rel#release{status = permanent},
- catch write_releases(RelDir, [Rel2], false)
- end.
- %%-----------------------------------------------------------------
- %% Func: upgrade_app(App, Dir) -> {ok, Unpurged}
- %% | restart_emulator
- %% | {error, Error}
- %% Types:
- %% App = atom()
- %% Dir = string() assumed to be application directory, the code
- %% located under Dir/ebin
- %% Purpose: Upgrade to the version in Dir according to an appup file
- %%-----------------------------------------------------------------
- upgrade_app(App, NewDir) ->
- try upgrade_script(App, NewDir) of
- {ok, NewVsn, Script} ->
- eval_appup_script(App, NewVsn, NewDir, Script)
- catch
- throw:Reason ->
- {error, Reason}
- end.
- %%-----------------------------------------------------------------
- %% Func: downgrade_app(App, Dir)
- %% downgrade_app(App, Vsn, Dir) -> {ok, Unpurged}
- %% | restart_emulator
- %% | {error, Error}
- %% Types:
- %% App = atom()
- %% Vsn = string(), may be omitted if Dir == App-Vsn
- %% Dir = string() assumed to be application directory, the code
- %% located under Dir/ebin
- %% Purpose: Downgrade from the version in Dir according to an appup file
- %% located in the ebin dir of the _current_ version
- %%-----------------------------------------------------------------
- downgrade_app(App, OldDir) ->
- case string:lexemes(filename:basename(OldDir), "-") of
- [_AppS, OldVsn] ->
- downgrade_app(App, OldVsn, OldDir);
- _ ->
- {error, {unknown_version, App}}
- end.
- downgrade_app(App, OldVsn, OldDir) ->
- try downgrade_script(App, OldVsn, OldDir) of
- {ok, Script} ->
- eval_appup_script(App, OldVsn, OldDir, Script)
- catch
- throw:Reason ->
- {error, Reason}
- end.
- upgrade_script(App, NewDir) ->
- OldVsn = ensure_running(App),
- OldDir = code:lib_dir(App),
- {NewVsn, Script} = find_script(App, NewDir, OldVsn, up),
- OldAppl = read_app(App, OldVsn, OldDir),
- NewAppl = read_app(App, NewVsn, NewDir),
- case systools_rc:translate_scripts(up,
- [Script],[NewAppl],[OldAppl]) of
- {ok, LowLevelScript} ->
- {ok, NewVsn, LowLevelScript};
- {error, _SystoolsRC, Reason} ->
- throw(Reason)
- end.
- downgrade_script(App, OldVsn, OldDir) ->
- NewVsn = ensure_running(App),
- NewDir = code:lib_dir(App),
- {NewVsn, Script} = find_script(App, NewDir, OldVsn, down),
- OldAppl = read_app(App, OldVsn, OldDir),
- NewAppl = read_app(App, NewVsn, NewDir),
- case systools_rc:translate_scripts(dn,
- [Script],[OldAppl],[NewAppl]) of
- {ok, LowLevelScript} ->
- {ok, LowLevelScript};
- {error, _SystoolsRC, Reason} ->
- throw(Reason)
- end.
- eval_appup_script(App, ToVsn, ToDir, Script) ->
- EnvBefore = application_controller:prep_config_change(),
- AppSpecL = read_appspec(App, ToDir),
- Res = release_handler_1:eval_script(Script,
- [], % [AppSpec]
- [{App, ToVsn, ToDir}],
- [{App, ToVsn, ToDir}],
- []), % [Opt]
- case Res of
- {ok, _Unpurged} ->
- application_controller:change_application_data(AppSpecL,[]),
- application_controller:config_change(EnvBefore);
- _Res ->
- ignore
- end,
- Res.
- ensure_running(App) ->
- case lists:keysearch(App, 1, application:which_applications()) of
- {value, {_App, _Descr, Vsn}} ->
- Vsn;
- false ->
- throw({app_not_running, App})
- end.
- find_script(App, Dir, OldVsn, UpOrDown) ->
- Appup = filename:join([Dir, "ebin", atom_to_list(App)++".appup"]),
- case file:consult(Appup) of
- {ok, [{NewVsn, UpFromScripts, DownToScripts}]} ->
- Scripts = case UpOrDown of
- up -> UpFromScripts;
- down -> DownToScripts
- end,
- case systools_relup:appup_search_for_version(OldVsn,Scripts) of
- {ok,Script} ->
- {NewVsn,Script};
- error ->
- throw({version_not_in_appup, OldVsn})
- end;
- {error, enoent} ->
- throw(no_appup_found);
- {error, Reason} ->
- throw(Reason)
- end.
- read_app(App, Vsn, Dir) ->
- AppS = atom_to_list(App),
- Path = [filename:join(Dir, "ebin")],
- case systools_make:read_application(AppS, Vsn, Path, []) of
- {ok, Appl} ->
- Appl;
- {error, {not_found, _AppFile}} ->
- throw({no_app_found, Vsn, Dir});
- {error, Reason} ->
- throw(Reason)
- end.
- read_appspec(App, Dir) ->
- AppS = atom_to_list(App),
- Path = [filename:join(Dir, "ebin")],
- case file:path_consult(Path, AppS++".app") of
- {ok, AppSpecL, _File} ->
- AppSpecL;
- {error, Reason} ->
- throw(Reason)
- end.
-
- %%-----------------------------------------------------------------
- %% call(Request) -> Term
- %%-----------------------------------------------------------------
- call(Req) ->
- gen_server:call(release_handler, Req, infinity).
- %%-----------------------------------------------------------------
- %% Call-back functions from gen_server
- %%-----------------------------------------------------------------
- init([]) ->
- {ok, [[Root]]} = init:get_argument(root),
- {CliDir, Masters} = is_client(),
- ReleaseDir =
- case application:get_env(sasl, releases_dir) of
- undefined ->
- case os:getenv("RELDIR") of
- false ->
- if
- CliDir == false ->
- filename:join([Root, "releases"]);
- true ->
- filename:join([CliDir, "releases"])
- end;
- RELDIR ->
- RELDIR
- end;
- {ok, Dir} ->
- Dir
- end,
- Releases =
- case consult(filename:join(ReleaseDir, "RELEASES"), Masters) of
- {ok, [Term]} ->
- transform_release(ReleaseDir, Term, Masters);
- _ ->
- {Name, Vsn} = init:script_id(),
- [#release{name = Name, vsn = Vsn, status = permanent}]
- end,
- StartPrg =
- case application:get_env(start_prg) of
- {ok, Found2} when is_list(Found2) ->
- {do_check, Found2};
- _ ->
- {no_check, filename:join([Root, "bin", "start"])}
- end,
- Static =
- case application:get_env(static_emulator) of
- {ok, SFlag} when is_atom(SFlag) -> SFlag;
- _ -> false
- end,
- {ok, #state{root = Root, rel_dir = ReleaseDir, releases = Releases,
- start_prg = StartPrg, masters = Masters,
- client_dir = CliDir, static_emulator = Static}}.
- handle_call({unpack_release, ReleaseName}, _From, S)
- when S#state.masters == false ->
- case catch do_unpack_release(S#state.root, S#state.rel_dir,
- ReleaseName, S#state.releases) of
- {ok, NewReleases, Vsn} ->
- {reply, {ok, Vsn}, S#state{releases = NewReleases}};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({unpack_release, _ReleaseName}, _From, S) ->
- {reply, {error, client_node}, S};
- handle_call({check_install_release, Vsn, Purge}, _From, S) ->
- case catch do_check_install_release(S#state.rel_dir,
- Vsn,
- S#state.releases,
- S#state.masters,
- Purge) of
- {ok, CurrentVsn, Descr} ->
- {reply, {ok, CurrentVsn, Descr}, S};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
- NS = resend_sync_nodes(S),
- case catch do_install_release(S, Vsn, Opts) of
- {ok, NewReleases, [], CurrentVsn, Descr} ->
- {reply, {ok, CurrentVsn, Descr}, NS#state{releases=NewReleases}};
- {ok, NewReleases, Unpurged, CurrentVsn, Descr} ->
- Timer =
- case S#state.timer of
- undefined ->
- {ok, Ref} = timer:send_interval(?timeout, timeout),
- Ref;
- Ref -> Ref
- end,
- NewS = NS#state{releases = NewReleases, unpurged = Unpurged,
- timer = Timer},
- {reply, {ok, CurrentVsn, Descr}, NewS};
- {error, Reason} ->
- {reply, {error, Reason}, NS};
- {restart_emulator, CurrentVsn, Descr} ->
- gen_server:reply(From, {ok, CurrentVsn, Descr}),
- init:reboot(),
- {noreply, NS};
- {restart_new_emulator, CurrentVsn, Descr} ->
- gen_server:reply(From, {continue_after_restart, CurrentVsn, Descr}),
- init:reboot(),
- {noreply, NS};
- {'EXIT', Reason} ->
- io:format("release_handler:"
- "install_release(Vsn=~tp Opts=~tp) failed, "
- "Reason=~tp~n", [Vsn, Opts, Reason]),
- gen_server:reply(From, {error, Reason}),
- case ErrorAction of
- restart ->
- init:restart();
- reboot ->
- init:reboot()
- end,
- {noreply, NS}
- end;
- handle_call({make_permanent, Vsn}, _From, S) ->
- case catch do_make_permanent(S, Vsn) of
- {ok, Releases, Unpurged} ->
- {reply, ok, S#state{releases = Releases, unpurged = Unpurged}};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({reboot_old_release, Vsn}, From, S) ->
- case catch do_reboot_old_release(S, Vsn) of
- ok ->
- gen_server:reply(From, ok),
- init:reboot(),
- {noreply, S};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({remove_release, Vsn}, _From, S)
- when S#state.masters == false ->
- case catch do_remove_release(S#state.root, S#state.rel_dir,
- Vsn, S#state.releases) of
- {ok, NewReleases} ->
- {reply, ok, S#state{releases = NewReleases}};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({remove_release, _Vsn}, _From, S) ->
- {reply, {error, client_node}, S};
- handle_call({set_unpacked, RelFile, LibDirs}, _From, S) ->
- Root = S#state.root,
- case catch do_set_unpacked(Root, S#state.rel_dir, RelFile,
- LibDirs, S#state.releases,
- S#state.masters) of
- {ok, NewReleases, Vsn} ->
- {reply, {ok, Vsn}, S#state{releases = NewReleases}};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({set_removed, Vsn}, _From, S) ->
- case catch do_set_removed(S#state.rel_dir, Vsn,
- S#state.releases,
- S#state.masters) of
- {ok, NewReleases} ->
- {reply, ok, S#state{releases = NewReleases}};
- {error, Reason} ->
- {reply, {error, Reason}, S};
- {'EXIT', Reason} ->
- {reply, {error, Reason}, S}
- end;
- handle_call({install_file, File, Vsn}, _From, S) ->
- Reply =
- case lists:keysearch(Vsn, #release.vsn, S#state.releases) of
- {value, _} ->
- Dir = filename:join([S#state.rel_dir, Vsn]),
- catch copy_file(File, Dir, S#state.masters);
- _ ->
- {error, {no_such_release, Vsn}}
- end,
- {reply, Reply, S};
- handle_call(which_releases, _From, S) ->
- Reply = lists:map(fun(#release{name = Name, vsn = Vsn, libs = Libs,
- status = Status}) ->
- {Name, Vsn, mk_lib_name(Libs), Status}
- end, S#state.releases),
- {reply, Reply, S}.
- mk_lib_name([{LibName, Vsn, _Dir} | T]) ->
- [lists:concat([LibName, "-", Vsn]) | mk_lib_name(T)];
- mk_lib_name([]) -> [].
- handle_info(timeout, S) ->
- case soft_purge(S#state.unpurged) of
- [] ->
- _ = timer:cancel(S#state.timer),
- {noreply, S#state{unpurged = [], timer = undefined}};
- Unpurged ->
- {noreply, S#state{unpurged = Unpurged}}
- end;
- handle_info({sync_nodes, Id, Node}, S) ->
- PSN = S#state.pre_sync_nodes,
- {noreply, S#state{pre_sync_nodes = [{sync_nodes, Id, Node} | PSN]}};
- handle_info(Msg, State) ->
- error_logger:info_msg("release_handler: got unknown message: ~p~n", [Msg]),
- {noreply, State}.
- terminate(_Reason, _State) ->
- ok.
- handle_cast(_Msg, State) ->
- {noreply, State}.
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
- %%%-----------------------------------------------------------------
- %%% Internal functions
- %%%-----------------------------------------------------------------
- is_client() ->
- case application:get_env(masters) of
- {ok, Masters} ->
- Alive = is_alive(),
- case atom_list(Masters) of
- true when Alive == true ->
- case application:get_env(client_directory) of
- {ok, ClientDir} ->
- case int_list(ClientDir) of
- true ->
- {ClientDir, Masters};
- _ ->
- exit({bad_parameter, client_directory,
- ClientDir})
- end;
- _ ->
- {false, false}
- end;
- _ ->
- exit({bad_parameter, masters, Masters})
- end;
- _ ->
- {false, false}
- end.
- atom_list([A|T]) when is_atom(A) -> atom_list(T);
- atom_list([]) -> true;
- atom_list(_) -> false.
- int_list([I|T]) when is_integer(I) -> int_list(T);
- int_list([]) -> true;
- int_list(_) -> false.
- resend_sync_nodes(S) ->
- lists:foreach(fun(Msg) -> self() ! Msg end, S#state.pre_sync_nodes),
- S#state{pre_sync_nodes = []}.
- soft_purge(Unpurged) ->
- lists:filter(fun({Mod, _PostPurgeMethod}) ->
- case code:soft_purge(Mod) of
- true -> false; % No proc left, don't remember Mod
- false -> true % Still proc left, remember it
- end
- end,
- Unpurged).
- brutal_purge(Unpurged) ->
- lists:filter(fun({Mod, brutal_purge}) -> code:purge(Mod), false;
- (_) -> true
- end,
- Unpurged).
- %%-----------------------------------------------------------------
- %% The release package is a RelName.tar.Z (.tar on non unix) file
- %% with the following contents:
- %% - RelName.rel == {release, {Name, Vsn}, {erts, EVsn}, [lib()]}
- %% - <files> according to [lib()]
- %% - lib() = {LibName, LibVsn}
- %% In the Dir, there exists a file called RELEASES, which contains
- %% a [{Vsn, {erts, EVsn}, {libs, [{LibName, LibVsn, LibDir}]}}].
- %% Note that RelDir is an absolute directory name !
- %% Note that this function is not executed by a client
- %% release_handler.
- %%-----------------------------------------------------------------
- do_unpack_release(Root, RelDir, ReleaseName, Releases) ->
- Tar = filename:join(RelDir, ReleaseName ++ ".tar.gz"),
- do_check_file(Tar, regular),
- Rel = ReleaseName ++ ".rel",
- _ = extract_rel_file(filename:join("releases", Rel), Tar, Root),
- RelFile = filename:join(RelDir, Rel),
- Release = check_rel(Root, RelFile, false),
- #release{vsn = Vsn} = Release,
- case lists:keysearch(Vsn, #release.vsn, Releases) of
- {value, _} -> throw({error, {existing_release, Vsn}});
- _ -> ok
- end,
- extract_tar(Root, Tar),
- NewReleases = [Release#release{status = unpacked} | Releases],
- write_releases(RelDir, NewReleases, false),
- %% Keeping this for backwards compatibility reasons with older
- %% systools:make_tar, where there is no copy of the .rel file in
- %% the releases/<vsn> dir. See OTP-9746.
- Dir = filename:join([RelDir, Vsn]),
- copy_file(RelFile, Dir, false),
- %% Clean release
- _ = file:delete(Tar),
- _ = file:delete(RelFile),
- {ok, NewReleases, Vsn}.
-
- check_rel(Root, RelFile, Masters) ->
- check_rel(Root, RelFile, [], Masters).
- check_rel(Root, RelFile, LibDirs, Masters) ->
- case consult(RelFile, Masters) of
- {ok, [RelData]} ->
- check_rel_data(RelData, Root, LibDirs, Masters);
- {ok, _} ->
- throw({error, {bad_rel_file, RelFile}});
- {error, Reason} when is_tuple(Reason) ->
- throw({error, {bad_rel_file, RelFile}});
- {error, FileError} -> % FileError is posix atom | no_master
- throw({error, {FileError, RelFile}})
- end.
- check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs,
- Masters) ->
- Libs2 =
- lists:map(fun(LibSpec) ->
- Lib = element(1, LibSpec),
- LibVsn = element(2, LibSpec),
- LibName = lists:concat([Lib, "-", LibVsn]),
- LibDir =
- case lists:keysearch(Lib, 1, LibDirs) of
- {value, {_Lib, _Vsn, Dir}} ->
- Path = filename:join(Dir,LibName),
- check_path(Path, Masters),
- Path;
- _ ->
- filename:join([Root, "lib", LibName])
- end,
- {Lib, LibVsn, LibDir}
- end,
- Libs),
- #release{name = Name, vsn = Vsn, erts_vsn = EVsn,
- libs = Libs2, status = unpacking};
- check_rel_data(RelData, _Root, _LibDirs, _Masters) ->
- throw({error, {bad_rel_data, RelData}}).
- check_path(Path) ->
- check_path_response(Path, file:read_file_info(Path)).
- check_path(Path, false) -> check_path(Path);
- check_path(Path, Masters) -> check_path_master(Masters, Path).
- %%-----------------------------------------------------------------
- %% check_path at any master node.
- %% If the path does not exist or is not a directory
- %% at one node it should not exist at any other node either.
- %%-----------------------------------------------------------------
- check_path_master([Master|Ms], Path) ->
- case rpc:call(Master, file, read_file_info, [Path]) of
- {badrpc, _} -> consult_master(Ms, Path);
- Res -> check_path_response(Path, Res)
- end;
- check_path_master([], _Path) ->
- {error, no_master}.
- check_path_response(_Path, {ok, Info}) when Info#file_info.type==directory ->
- ok;
- check_path_response(Path, {ok, _Info}) ->
- throw({error, {not_a_directory, Path}});
- check_path_response(Path, {error, _Reason}) ->
- throw({error, {no_such_directory, Path}}).
- do_check_install_release(RelDir, Vsn, Releases, Masters, Purge) ->
- case lists:keysearch(Vsn, #release.vsn, Releases) of
- {value, #release{status = current}} ->
- {error, {already_installed, Vsn}};
- {value, Release} ->
- LatestRelease = get_latest_release(Releases),
- VsnDir = filename:join([RelDir, Vsn]),
- check_file(filename:join(VsnDir, "start.boot"), regular, Masters),
- IsRelup = check_opt_file(filename:join(VsnDir, "relup"), regular, Masters),
- check_opt_file(filename:join(VsnDir, "sys.config"), regular, Masters),
- %% Check that all required libs are present
- Libs = Release#release.libs,
- lists:foreach(fun({_Lib, _LibVsn, LibDir}) ->
- check_file(LibDir, directory, Masters),
- Ebin = filename:join(LibDir, "ebin"),
- check_file(Ebin, directory, Masters)
- end,
- Libs),
- if
- IsRelup ->
- case get_rh_script(LatestRelease, Release, RelDir, Masters) of
- {ok, {CurrentVsn, Descr, Script}} ->
- case catch check_script(Script, Libs) of
- {ok,SoftPurgeMods} when Purge=:=true ->
- %% Get modules with brutal_purge
- %% instructions, but that can be
- %% soft purged
- {ok,BrutalPurgeMods} =
- release_handler_1:check_old_processes(
- Script,brutal_purge),
- lists:foreach(
- fun(Mod) ->
- catch erlang:purge_module(Mod)
- end,
- SoftPurgeMods ++ BrutalPurgeMods),
- {ok, CurrentVsn, Descr};
- {ok,_} ->
- {ok, CurrentVsn, Descr};
- Else ->
- Else
- end;
- Error ->
- Error
- end;
- true ->
- {ok, Vsn, ""}
- end;
- _ ->
- {error, {no_such_release, Vsn}}
- end.
-
- do_install_release(#state{start_prg = StartPrg,
- root = RootDir,
- rel_dir = RelDir, releases = Releases,
- masters = Masters,
- static_emulator = Static},
- Vsn, Opts) ->
- case lists:keysearch(Vsn, #release.vsn, Releases) of
- {value, #release{status = current}} ->
- {error, {already_installed, Vsn}};
- {value, Release} ->
- LatestRelease = get_latest_release(Releases),
- case get_rh_script(LatestRelease, Release, RelDir, Masters) of
- {ok, {_CurrentVsn, _Descr, [restart_new_emulator|_Script]}}
- when Static == true ->
- throw(static_emulator);
- {ok, {CurrentVsn, Descr, [restart_new_emulator|_Script]}} ->
- %% This will only happen if the upgrade includes
- %% an emulator upgrade (and it is not a downgrade)
- %% - then the new emulator must be started before
- %% new code can be loaded.
- %% Create a temporary release which includes new
- %% emulator, kernel, stdlib and sasl - and old
- %% versions of other applications.
- {TmpVsn,TmpRelease} =
- new_emulator_make_tmp_release(LatestRelease,Release,
- RelDir,Opts,Masters),
- NReleases = [TmpRelease|Releases],
- %% Then uppgrade to the temporary release.
- %% The rest of the upgrade will continue after the restart
- prepare_restart_new_emulator(StartPrg, RootDir,
- RelDir, TmpVsn, TmpRelease,
- NReleases, Masters),
- {restart_new_emulator, CurrentVsn, Descr};
- {ok, {CurrentVsn, Descr, Script}} ->
- %% In case there has been an emulator upgrade,
- %% remove the temporary release
- NReleases =
- new_emulator_rm_tmp_release(
- LatestRelease#release.vsn,
- LatestRelease#release.erts_vsn,
- Vsn,RelDir,Releases,Masters),
- %% Then execute the relup script
- mon_nodes(true),
- EnvBefore = application_controller:prep_config_change(),
- Apps = change_appl_data(RelDir, Release, Masters),
- LibDirs = Release#release.libs,
- NewLibs = get_new_libs(LatestRelease#release.libs,
- Release#release.libs),
- case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of
- {ok, Unpurged} ->
- application_controller:config_change(EnvBefore),
- mon_nodes(false),
- NReleases1 = set_status(Vsn, current, NReleases),
- {ok, NReleases1, Unpurged, CurrentVsn, Descr};
- restart_emulator when Static == true ->
- throw(static_emulator);
- restart_emulator ->
- mon_nodes(false),
- prepare_restart_new_emulator(StartPrg, RootDir,
- RelDir, Vsn, Release,
- NReleases, Masters),
- {restart_emulator, CurrentVsn, Descr};
- Else ->
- application_controller:config_change(EnvBefore),
- mon_nodes(false),
- Else
- end;
- Error ->
- Error
- end;
- _ ->
- {error, {no_such_release, Vsn}}
- end.
- new_emulator_make_tmp_release(CurrentRelease,ToRelease,RelDir,Opts,Masters) ->
- CurrentVsn = CurrentRelease#release.vsn,
- ToVsn = ToRelease#release.vsn,
- TmpVsn = ?tmp_vsn(CurrentVsn),
- case get_base_libs(ToRelease#release.libs) of
- {ok,{Kernel,Stdlib,Sasl},_} ->
- case get_base_libs(CurrentRelease#release.libs) of
- {ok,_,RestLibs} ->
- TmpErtsVsn = ToRelease#release.erts_vsn,
- TmpLibs = [Kernel,Stdlib,Sasl|RestLibs],
- TmpRelease = CurrentRelease#release{vsn=TmpVsn,
- erts_vsn=TmpErtsVsn,
- libs = TmpLibs,
- status = unpacked},
- new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,
- RelDir,Opts,Masters),
- new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,
- RelDir,Masters),
- {TmpVsn,TmpRelease};
- {error,{missing,Missing}} ->
- throw({error,{missing_base_app,CurrentVsn,Missing}})
- end;
- {error,{missing,Missing}} ->
- throw({error,{missing_base_app,ToVsn,Missing}})
- end.
- %% Get kernel, stdlib and sasl libs,
- %% and also return the rest of the libs as a list.
- %% Return error if any of kernel, stdlib or sasl does not exist.
- get_base_libs(Libs) ->
- get_base_libs(Libs,undefined,undefined,undefined,[]).
- get_base_libs([{kernel,_,_}=Kernel|Libs],undefined,Stdlib,Sasl,Rest) ->
- get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
- get_base_libs([{stdlib,_,_}=Stdlib|Libs],Kernel,undefined,Sasl,Rest) ->
- get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
- get_base_libs([{sasl,_,_}=Sasl|Libs],Kernel,Stdlib,undefined,Rest) ->
- get_base_libs(Libs,Kernel,Stdlib,Sasl,Rest);
- get_base_libs([Lib|Libs],Kernel,Stdlib,Sasl,Rest) ->
- get_base_libs(Libs,Kernel,Stdlib,Sasl,[Lib|Rest]);
- get_base_libs([],undefined,_Stdlib,_Sasl,_Rest) ->
- {error,{missing,kernel}};
- get_base_libs([],_Kernel,undefined,_Sasl,_Rest) ->
- {error,{missing,stdlib}};
- get_base_libs([],_Kernel,_Stdlib,undefined,_Rest) ->
- {error,{missing,sasl}};
- get_base_libs([],Kernel,Stdlib,Sasl,Rest) ->
- {ok,{Kernel,Stdlib,Sasl},lists:reverse(Rest)}.
- new_emulator_make_hybrid_boot(CurrentVsn,ToVsn,TmpVsn,RelDir,Opts,Masters) ->
- FromBootFile = filename:join([RelDir,CurrentVsn,"start.boot"]),
- ToBootFile = filename:join([RelDir,ToVsn,"start.boot"]),
- TmpBootFile = filename:join([RelDir,TmpVsn,"start.boot"]),
- ensure_dir(TmpBootFile,Masters),
- Args = [ToVsn,Opts],
- {ok,FromBoot} = read_file(FromBootFile,Masters),
- {ok,ToBoot} = read_file(ToBootFile,Masters),
- case systools_make:make_hybrid_boot(TmpVsn,FromBoot,ToBoot,Args) of
- {ok,TmpBoot} ->
- write_file(TmpBootFile,TmpBoot,Masters);
- {error,Reason} ->
- throw({error,{could_not_create_hybrid_boot,Reason}})
- end.
- new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
- FromFile = filename:join([RelDir,CurrentVsn,"sys.config"]),
- ToFile = filename:join([RelDir,ToVsn,"sys.config"]),
- TmpFile = filename:join([RelDir,TmpVsn,"sys.config"]),
- FromConfig =
- case consult(FromFile,Masters) of
- {ok,[FC]} ->
- FC;
- {error,Error1} ->
- io:format("Warning: ~w can not read ~tp: ~tp~n",
- [?MODULE,FromFile,Error1]),
- []
- end,
- [Kernel,Stdlib,Sasl] =
- case consult(ToFile,Masters) of
- {ok,[ToConfig]} ->
- [lists:keyfind(App,1,ToConfig) || App <- [kernel,stdlib,sasl]];
- {error,Error2} ->
- io:format("Warning: ~w can not read ~tp: ~tp~n",
- [?MODULE,ToFile,Error2]),
- [false,false,false]
- end,
- Config1 = replace_config(kernel,FromConfig,Kernel),
- Config2 = replace_config(stdlib,Config1,Stdlib),
- Config3 = replace_config(sasl,Config2,Sasl),
- ConfigStr = io_lib:format("%% ~s~n~tp.~n",
- [epp:encoding_to_string(utf8),Config3]),
- write_file(TmpFile,unicode:characters_to_binary(ConfigStr),Masters).
- %% Take the configuration for application App from the new config and
- %% insert in the old config.
- %% If no entry exists in the new config, then delete the entry (if it exists)
- %% from the old config.
- %% If entry exists in the new config, but not in the old config, then
- %% add the entry.
- replace_config(App,Config,false) ->
- lists:keydelete(App,1,Config);
- replace_config(App,Config,AppConfig) ->
- lists:keystore(App,1,Config,AppConfig).
- %% Remove all files related to the temporary release
- new_emulator_rm_tmp_release(?tmp_vsn(_)=TmpVsn,EVsn,NewVsn,
- RelDir,Releases,Masters) ->
- case os:type() of
- {win32, nt} ->
- rename_tmp_service(EVsn,TmpVsn,NewVsn);
- _ ->
- ok
- end,
- remove_dir(filename:join(RelDir,TmpVsn),Masters),
- lists:keydelete(TmpVsn,#release.vsn,Releases);
- new_emulator_rm_tmp_release(_,_,_,_,Releases,_) ->
- Releases.
- %% Rename the tempoarary service (for erts ugprade) to the real ToVsn
- rename_tmp_service(EVsn,TmpVsn,NewVsn) ->
- FromName = hd(string:lexemes(atom_to_list(node()),"@")) ++ "_" ++ TmpVsn,
- ToName = hd(string:lexemes(atom_to_list(node()),"@")) ++ "_" ++ NewVsn,
- case erlsrv:get_service(EVsn,ToName) of
- {error, _Error} ->
- ok;
- _Data ->
- {ok,_} = erlsrv:remove_service(ToName),
- ok
- end,
- rename_service(EVsn,FromName,ToName).
- %% Rename a service and check that it succeeded
- rename_service(EVsn,FromName,ToName) ->
- case erlsrv:rename_service(EVsn,FromName,ToName) of
- {ok,_} ->
- case erlsrv:get_service(EVsn,ToName) of
- {error,Error1} ->
- throw({error,Error1});
- _Data2 ->
- ok
- end;
- Error2 ->
- throw({error,{service_rename_failed, Error2}})
- end.
- %%% This code chunk updates the services in one of two ways,
- %%% Either the emulator is restarted, in which case the old service
- %%% is to be removed and the new enabled, or the emulator is NOT restarted
- %%% in which case we try to rename the old service to the new name and try
- %%% to update heart's view of what service we are really running.
- do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) ->
- PermName = hd(string:lexemes(atom_to_list(node()),"@"))
- ++ "_" ++ PermanentVsn,
- Name = hd(string:lexemes(atom_to_list(node()),"@"))
- ++ "_" ++ Vsn,
- case erlsrv:get_service(EVsn,Name) of
- {error, _Error} ->
- %% We probably do not need to replace services, just
- %% rename.
- case os:getenv("ERLSRV_SERVICE_NAME") == PermName of
- true ->
- rename_service(EVsn,PermName,Name),
- %% The interfaces for doing this are
- %% NOT published and may be subject to
- %% change. Do NOT do this anywhere else!
- os:putenv("ERLSRV_SERVICE_NAME", Name),
- %% Restart heart port program, this
- %% function is only to be used here.
- heart:cycle();
- false ->
- throw({error,service_name_missmatch})
- end;
- Data ->
- UpdData = erlsrv:new_service(Name, Data, []),
- case erlsrv:store_service(EVsn,UpdData) of
- ok ->
- {ok,_} = erlsrv:disable_service(PermanentEVsn, PermName),
- {ok,_} = erlsrv:enable_service(EVsn, Name),
- {ok,_} = erlsrv:remove_service(PermName),
- %%% Read comments about these above...
- os:putenv("ERLSRV_SERVICE_NAME", Name),
- ok = heart:cycle();
- Error4 ->
- throw(Error4)
- end
- end.
- do_make_permanent(#state{releases = Releases,
- rel_dir = RelDir, unpurged = Unpurged,
- masters = Masters,
- static_emulator = Static},
- Vsn) ->
- case lists:keysearch(Vsn, #release.vsn, Releases) of
- {value, #release{erts_vsn = EVsn, status = Status}}
- when Status /= unpacked, Status /= old, Status /= permanent ->
- Dir = filename:join([RelDir, Vsn]),
- Sys =
- case catch check_file(filename:join(Dir, "sys.config"),
- regular, Masters) of
- ok -> filename:join(Dir, "sys");
- _ -> false
- end,
- Boot = filename:join(Dir, "start.boot"),
- check_file(Boot, regular, Masters),
- set_permanent_files(RelDir, EVsn, Vsn, Masters, Static),
- NewReleases = set_status(Vsn, permanent, Releases),
- write_releases(RelDir, NewReleases, Masters),
- case os:type() of
- {win32, nt} ->
- {value, PermanentRelease} =
- lists:keysearch(permanent, #release.status,
- Releases),
- PermanentVsn = PermanentRelease#release.vsn,
- PermanentEVsn = PermanentRelease#release.erts_vsn,
- case catch do_make_services_permanent(PermanentVsn,
- Vsn,
- PermanentEVsn,
- EVsn) of
- {error,Reason} ->
- throw({error,{service_update_failed, Reason}});
- _ ->
- ok
- end;
- _ ->
- ok
- end,
- ok = init:make_permanent(filename:join(Dir, "start"), Sys),
- {ok, NewReleases, brutal_purge(Unpurged)};
- {value, #release{status = permanent}} ->
- {ok, Releases, Unpurged};
- {value, #release{status = Status}} ->
- {error, {bad_status, Status}};
- false ->
- {error, {no_such_release, Vsn}}
- end.
- do_back_service(OldVersion, CurrentVersion,OldEVsn,CurrentEVsn) ->
- NN = hd(string:lexemes(atom_to_list(node()),"@")),
- OldName = NN ++ "_" ++ OldVersion,
- CurrentName = NN ++ "_" ++ CurrentVersion,
- UpdData = case erlsrv:get_service(CurrentEVsn,CurrentName) of
- {error, Error} ->
- throw({error,Error});
- Data ->
- erlsrv:new_service(OldName, Data, [])
- end,
- _ = case erlsrv:store_service(OldEVsn,UpdData) of
- ok ->
- {ok,_} = erlsrv:disable_service(CurrentEVsn,CurrentName),
- {ok,_} = erlsrv:enable_service(OldEVsn,OldName);
- Error2 ->
- throw(Error2)
- end,
- OldErlSrv = filename:nativename(erlsrv:erlsrv(OldEVsn)),
- CurrentErlSrv = filename:nativename(erlsrv:erlsrv(CurrentEVsn)),
- case heart:set_cmd(CurrentErlSrv ++ " remove " ++ CurrentName ++
- " & " ++ OldErlSrv ++ " start " ++ OldName) of
- ok ->
- ok;
- Error3 ->
- throw({error, {'heart:set_cmd() error', Error3}})
- end.
- do_reboot_old_release(#state{releases = Releases,
- rel_dir = RelDir, masters = Masters,
- static_emulator = Static},
- Vsn) ->
- case lists:keysearch(Vsn, #release.vsn, Releases) of
- {value, #release{erts_vsn = EVsn, status = old}} ->
- CurrentRunning = case os:type() of
- {win32,nt} ->
- %% Get the current release on NT
- case lists:keysearch(permanent,
- #release.status,
- Releases) of
- false ->
- lists:keysearch(current,
- #release.status,
- Releases);
- {value,CR} ->
- CR
- end;
- _ ->
- false
- end,
- set_permanent_files(RelDir, EVsn, Vsn, Masters, Static),
- NewReleases = set_status(Vsn, permanent, Releases),
- write_releases(RelDir, NewReleases, Masters),
- case os:type() of
- {win32,nt} ->
- %% Edit up the services and set a reasonable heart
- %% command
- do_back_service(Vsn,CurrentRunning#release.vsn,EVsn,
- CurrentRunning#release.erts_vsn);
- _ ->
- ok
- end,
- ok;
- {value, #release{status = Status}} ->
- {error, {bad_status, Status}};
- false ->
- {error, {no_such_release, Vsn}}
- end.
- %%-----------------------------------------------------------------
- %% Depending of if the release_handler is running in normal, client or
- %% client with static emulator the new system version is made permanent
- %% in different ways.
- %%-----------------------------------------------------------------
- set_permanent_files(RelDir, EVsn, Vsn, false, _) ->
- write_start(filename:join([RelDir, "start_erl.data"]),
- EVsn ++ " " ++ Vsn,
- false);
- set_permanent_files(RelDir, EVsn, Vsn, Masters, false) ->
- write_start(filename:join([RelDir, "start_erl.data"]),
- EVsn ++ " " ++ Vsn,
- Masters);
- set_permanent_files(RelDir, _EVsn, Vsn, Masters, _Static) ->
- VsnDir = filename:join([RelDir, Vsn]),
- set_static_files(VsnDir, RelDir, Masters).
- do_remove_service(Vsn) ->
- %% Very unconditionally remove the service.
- %% Note that the service could already have been removed when
- %% making another release permanent.
- ServiceName = hd(string:lexemes(atom_to_list(node()),"@"))
- ++ "_" ++ Vsn,
- case erlsrv:get_service(ServiceName) of
- {error, _Error} ->
- ok;
- _Data ->
- {ok,_} = erlsrv:remove_service(ServiceName),
- ok
- end.
- do_remove_release(Root, RelDir, Vsn, Releases) ->
- % Decide which libs should be removed
- case lists:keysearch(Vsn, #release.vsn, Releases) of
- {value, #release{status = permanent}} ->
- {error, {permanent, Vsn}};
- {value, #release{libs = RemoveLibs, vsn = Vsn, erts_vsn = EVsn}} ->
- case os:type() of
- {win32, nt} ->
- do_remove_service(Vsn);
- _ ->
- ok
- end,
- NewReleases = lists:keydelete(Vsn, #release.vsn, Releases),
- RemoveThese =
- lists:foldl(fun(#release{libs = Libs}, Remove) ->
- diff_dir(Remove, Libs)
- end, RemoveLibs, NewReleases),
- lists:foreach(fun({_Lib, _LVsn, LDir}) ->
- remove_file(LDir)
- end, RemoveThese),
- remove_file(filename:join([RelDir, Vsn])),
- case lists:keysearch(EVsn, #release.erts_vsn, NewReleases) of
- {value, _} -> ok;
- false -> % Remove erts library, no more references to it
- remove_file(filename:join(Root, "erts-" ++ EVsn))
- end,
- write_releases(RelDir, NewReleases, false),
- {ok, NewReleases};
- false ->
- {error, {no_such_release, Vsn}}
- end.
- do_set_unpacked(Root, RelDir, RelFile, LibDirs, Releases, Masters) ->
- Release = check_rel(Root, RelF…
Large files files are truncated, but you can click here to view the full file