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