📄 release_handler.erl
字号:
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%-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, install_release/1, install_release/2, remove_release/1, which_releases/0, 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, do_write_file/2, 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%% remove -%% permanent make other permanent old%% install permanent%% old reboot permanen%% install current%% remove -%%-----------------------------------------------------------------%% libs = [{Lib, Vsn, Dir}]-record(release, {name, vsn, erts_vsn, libs = [], status}).-define(timeout, 10000).%%-----------------------------------------------------------------%% 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) -> gen_server:call(release_handler, {unpack_release, ReleaseName}, infinity). %%-----------------------------------------------------------------%% Purpose: Checks the relup script for the specified version.%% The release must be unpacked.%% Returns: {ok, FromVsn, Descr} | {error, Reason}%% Reason = {already_installed, Vsn} |%% {bad_relup_file, RelFile} |%% {no_such_release, Vsn} |%% {no_such_from_vsn, Vsn} |%% exit_reason()%%-----------------------------------------------------------------check_install_release(Vsn) -> gen_server:call(release_handler, {check_install_release, Vsn}, infinity).%%-----------------------------------------------------------------%% Purpose: Executes the relup script for the specified version.%% The release must be unpacked.%% Returns: {ok, FromVsn, Descr} | {error, Reason}%% Reason = {already_installed, Vsn} |%% {bad_relup_file, RelFile} |%% {no_such_release, Vsn} |%% {no_such_from_vsn, Vsn} |%% {illegal_option, Opt}} |%% exit_reason()%%-----------------------------------------------------------------install_release(Vsn) -> gen_server:call(release_handler, {install_release, Vsn, restart, []}, infinity).install_release(Vsn, Opt) -> case check_install_options(Opt, restart, []) of {ok, ErrorAction, InstallOpt} -> gen_server:call(release_handler, {install_release, Vsn, ErrorAction, InstallOpt}, infinity); 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: 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) -> gen_server:call(release_handler, {make_permanent, Vsn}, infinity).%%-----------------------------------------------------------------%% Purpose: Reboots the system from an old release.%%-----------------------------------------------------------------reboot_old_release(Vsn) -> gen_server:call(release_handler, {reboot_old_release, Vsn}, infinity).%%-----------------------------------------------------------------%% 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) -> gen_server:call(release_handler, {remove_release, Vsn}, infinity).%%-----------------------------------------------------------------%% 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) -> gen_server:call(release_handler, {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) -> gen_server:call(release_handler, {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) -> gen_server:call(release_handler, {install_file, File, Vsn}).%%-----------------------------------------------------------------%% Returns: [{Name, Vsn, [LibName], Status}]%% Status = unpacked | current | permanent | old%%-----------------------------------------------------------------which_releases() -> gen_server:call(release_handler, which_releases).%%-----------------------------------------------------------------%% check_script(Script, LibDirs) -> ok | {error, Reason}%%-----------------------------------------------------------------check_script(Script, LibDirs) -> release_handler_1:check_script(Script, LibDirs).%%-----------------------------------------------------------------%% eval_script(Script, Apps, LibDirs, Opts) -> {ok, UnPurged} |%% restart_new_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.%%-----------------------------------------------------------------eval_script(Script, Apps, LibDirs, Opts) -> catch release_handler_1:eval_script(Script, Apps, LibDirs, 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_new_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_new_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:tokens(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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -