PageRenderTime 73ms CodeModel.GetById 2ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/reltool_util/src/reltool_util.erl

http://github.com/okeuday/CloudI
Erlang | 1287 lines | 910 code | 116 blank | 261 comment | 1 complexity | f0bbaf331b717d9056d4f279f81acdc4 MD5 | raw file
   1%-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-
   2% ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et:
   3%%%
   4%%%------------------------------------------------------------------------
   5%%% @doc
   6%%% ==reltool Utility Functions==
   7%%% All the functions here are probably considered unorthodox, but
   8%%% are useful for runtime usage of applications and releases.
   9%%% @end
  10%%%
  11%%% BSD LICENSE
  12%%% 
  13%%% Copyright (c) 2013, Michael Truog <mjtruog at gmail dot com>
  14%%% All rights reserved.
  15%%% 
  16%%% Redistribution and use in source and binary forms, with or without
  17%%% modification, are permitted provided that the following conditions are met:
  18%%% 
  19%%%     * Redistributions of source code must retain the above copyright
  20%%%       notice, this list of conditions and the following disclaimer.
  21%%%     * Redistributions in binary form must reproduce the above copyright
  22%%%       notice, this list of conditions and the following disclaimer in
  23%%%       the documentation and/or other materials provided with the
  24%%%       distribution.
  25%%%     * All advertising materials mentioning features or use of this
  26%%%       software must display the following acknowledgment:
  27%%%         This product includes software developed by Michael Truog
  28%%%     * The name of the author may not be used to endorse or promote
  29%%%       products derived from this software without specific prior
  30%%%       written permission
  31%%% 
  32%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  33%%% CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  34%%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35%%% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  36%%% DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  37%%% CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38%%% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  39%%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  40%%% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  41%%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  42%%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  43%%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  44%%% OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  45%%% DAMAGE.
  46%%%
  47%%% @author Michael Truog <mjtruog [at] gmail (dot) com>
  48%%% @copyright 2013 Michael Truog
  49%%% @version 0.8.0 {@date} {@time}
  50%%%------------------------------------------------------------------------
  51
  52-module(reltool_util).
  53-author('mjtruog [at] gmail (dot) com').
  54
  55-export([application_start/1,
  56         application_start/2,
  57         application_start/3,
  58         applications_start/1,
  59         applications_start/2,
  60         application_stop/1,
  61         application_remove/1,
  62         application_remove/2,
  63         application_purged/1,
  64         application_purged/2,
  65         application_running/1,
  66         application_running/2,
  67         application_loaded/1,
  68         application_modules/1,
  69         application_modules/2,
  70         ensure_application_loaded/1,
  71         ensure_application_started/1,
  72         ensure_application_stopped/1,
  73         module_loaded/1,
  74         is_module_loaded/1,
  75         is_module_loaded/2,
  76         module_purged/1,
  77         module_purged/2,
  78         module_exports/1,
  79         script_start/1,
  80         script_remove/1,
  81         script_remove/2]).
  82
  83-define(IS_MODULE_LOADED_DELTA, 100).
  84-define(MODULES_PURGED_DELTA, 100).
  85
  86-compile({no_auto_import, [{module_loaded, 1}]}).
  87
  88%%%------------------------------------------------------------------------
  89%%% External interface functions
  90%%%------------------------------------------------------------------------
  91
  92%%-------------------------------------------------------------------------
  93%% @doc
  94%% ===Start all the dependent applications manually.===
  95%% @end
  96%%-------------------------------------------------------------------------
  97
  98-spec application_start(Application :: atom()) ->
  99    ok |
 100    {error, any()}.
 101
 102application_start(Application)
 103    when is_atom(Application) ->
 104    application_start(Application, [], 5000).
 105
 106%%-------------------------------------------------------------------------
 107%% @doc
 108%% ===Start all the dependent applications manually with a specific configuration.===
 109%% @end
 110%%-------------------------------------------------------------------------
 111
 112-spec application_start(Application :: atom(),
 113                        Env :: list({atom(), any()})) ->
 114    ok |
 115    {error, any()}.
 116
 117application_start(Application, Env)
 118    when is_atom(Application), is_list(Env) ->
 119    application_start(Application, Env, 5000).
 120
 121%%-------------------------------------------------------------------------
 122%% @doc
 123%% ===Start all the dependent applications manually with a specific configuration and timeout.===
 124%% @end
 125%%-------------------------------------------------------------------------
 126
 127-spec application_start(Application :: atom(),
 128                        Env :: list({atom(), any()}),
 129                        Timeout :: pos_integer() | infinity) ->
 130    ok |
 131    {error, any()}.
 132
 133application_start(Application, Env, Timeout)
 134    when is_atom(Application), is_list(Env) ->
 135    case ensure_application_loaded(Application) of
 136        ok ->
 137            case application_start_set_env(Env, Application, Timeout) of
 138                ok ->
 139                    case applications_dependencies(Application) of
 140                        {ok, As} ->
 141                            case application_start_dependencies(As) of
 142                                ok ->
 143                                    ensure_application_started(Application);
 144                                {error, _} = Error ->
 145                                    Error
 146                            end;
 147                        {error, _} = Error ->
 148                            Error
 149                    end;
 150                {error, _} = Error ->
 151                    Error
 152            end;
 153        {error, _} = Error ->
 154            Error
 155    end.
 156
 157%%-------------------------------------------------------------------------
 158%% @doc
 159%% ===Start all the dependent applications manually.===
 160%% @end
 161%%-------------------------------------------------------------------------
 162
 163-spec applications_start(Applications :: list(atom() | {atom(), list()})) ->
 164    ok |
 165    {error, any()}.
 166
 167applications_start([_ | _] = Applications) ->
 168    applications_start(Applications, 5000).
 169
 170%%-------------------------------------------------------------------------
 171%% @doc
 172%% ===Start all the dependent applications manually.===
 173%% @end
 174%%-------------------------------------------------------------------------
 175
 176-spec applications_start(Applications :: list(atom() | {atom(), list()}),
 177                         Timeout :: pos_integer() | infinity) ->
 178    ok |
 179    {error, any()}.
 180
 181applications_start([_ | _] = Applications, Timeout) ->
 182    applications_start_element(Applications, Timeout).
 183
 184%%-------------------------------------------------------------------------
 185%% @doc
 186%% ===Stop an application and its dependencies.===
 187%% Only stop dependencies that are not required for other applications.
 188%% @end
 189%%-------------------------------------------------------------------------
 190
 191-spec application_stop(Application :: atom()) ->
 192    ok |
 193    {error, any()}.
 194
 195application_stop(Application) ->
 196    case application_stop_dependencies(Application) of
 197        {ok, _} ->
 198            ok;
 199        {error, _} = Error ->
 200            Error
 201    end.
 202
 203%%-------------------------------------------------------------------------
 204%% @doc
 205%% ===Stop and purge the modules of an application and all of its dependencies.===
 206%% Only application dependencies that are not required for other
 207%% applications are removed.
 208%% @end
 209%%-------------------------------------------------------------------------
 210
 211-spec application_remove(Application :: atom()) ->
 212    ok |
 213    {error, any()}.
 214
 215application_remove(Application) ->
 216    application_remove(Application, 5000).
 217
 218%%-------------------------------------------------------------------------
 219%% @doc
 220%% ===Stop and purge the modules of an application and all of its dependencies with a timeout.===
 221%% Only application dependencies that are not required for other
 222%% applications are removed.
 223%% @end
 224%%-------------------------------------------------------------------------
 225
 226-spec application_remove(Application :: atom(),
 227                         Timeout :: pos_integer() | infinity) ->
 228    ok |
 229    {error, any()}.
 230
 231application_remove(Application, infinity)
 232    when is_atom(Application) ->
 233    case application_stop_dependencies(Application) of
 234        {ok, Applications} ->
 235            applications_purged(Applications, infinity);
 236        {error, _} = Error ->
 237            Error
 238    end;
 239application_remove(Application, Timeout)
 240    when is_atom(Application), is_integer(Timeout), Timeout > 0 ->
 241    case application_stop_dependencies(Application) of
 242        {ok, Applications} ->
 243            TimeoutSlice = erlang:round(
 244                0.5 + Timeout / erlang:length(Applications)),
 245            applications_purged(Applications, TimeoutSlice);
 246        {error, _} = Error ->
 247            Error
 248    end.
 249
 250%%-------------------------------------------------------------------------
 251%% @doc
 252%% ===Purge a loaded application's modules and unload the application.===
 253%% The application is stopped if it is running, but its dependencies are
 254%% ignored.
 255%% @end
 256%%-------------------------------------------------------------------------
 257
 258-spec application_purged(Application :: atom()) ->
 259    ok |
 260    {error, any()}.
 261
 262application_purged(Application) ->
 263    application_purged(Application, 5000).
 264
 265%%-------------------------------------------------------------------------
 266%% @doc
 267%% ===Purge a loaded application's modules and unload the application with a specific timeout.===
 268%% The application is stopped if it is running, but its dependencies are
 269%% ignored.
 270%% @end
 271%%-------------------------------------------------------------------------
 272
 273-spec application_purged(Application :: atom(),
 274                         Timeout :: pos_integer() | infinity) ->
 275    ok |
 276    {error, any()}.
 277
 278application_purged(Application, Timeout)
 279    when is_atom(Application) ->
 280    case application_loaded(Application) of
 281        {ok, _} ->
 282            case ensure_application_stopped(Application) of
 283                ok ->
 284                    case application_modules(Application) of
 285                        {ok, Modules} ->
 286                            case modules_purged(Modules, Timeout) of
 287                                ok ->
 288                                    application:unload(Application);
 289                                {error, _} = Error ->
 290                                    Error
 291                            end;
 292                        {error, _} = Error ->
 293                            Error
 294                    end;
 295                {error, _} = Error ->
 296                    Error
 297            end;
 298        {error, _} = Error ->
 299            Error
 300    end.
 301
 302%%-------------------------------------------------------------------------
 303%% @doc
 304%% ===Check if an application is currently running.===
 305%% @end
 306%%-------------------------------------------------------------------------
 307
 308-spec application_running(Application :: atom()) ->
 309    {ok, {atom(), string()}} |
 310    {error, any()}.
 311
 312application_running(Application)
 313    when is_atom(Application) ->
 314    application_running(Application, 5000).
 315
 316%%-------------------------------------------------------------------------
 317%% @doc
 318%% ===Check if an application is currently running with a timeout.===
 319%% @end
 320%%-------------------------------------------------------------------------
 321
 322-spec application_running(Application :: atom(),
 323                          Timeout :: pos_integer() | infinity) ->
 324    {ok, {atom(), string()}} |
 325    {error, any()}.
 326
 327application_running(Application, Timeout)
 328    when is_atom(Application) ->
 329    try application:which_applications(Timeout) of
 330        Apps ->
 331            case lists:keyfind(Application, 1, Apps) of
 332                {Application, _, VSN} ->
 333                    {ok, {Application, VSN}};
 334                false ->
 335                    {error, {not_found, Application}}
 336            end
 337    catch exit:{timeout, _} ->
 338        {error, application_controller_timeout}
 339    end.
 340
 341%%-------------------------------------------------------------------------
 342%% @doc
 343%% ===Check if an application is currently loaded.===
 344%% @end
 345%%-------------------------------------------------------------------------
 346
 347-spec application_loaded(Application :: atom()) ->
 348    {ok, {atom(), string()}} |
 349    {error, any()}.
 350
 351application_loaded(Application)
 352    when is_atom(Application) ->
 353    Apps = application:loaded_applications(),
 354    case lists:keyfind(Application, 1, Apps) of
 355        {Application, _, VSN} ->
 356            {ok, {Application, VSN}};
 357        false ->
 358            {error, {not_found, Application}}
 359    end.
 360
 361%%-------------------------------------------------------------------------
 362%% @doc
 363%% ===Retrieve a list of application modules.===
 364%% @end
 365%%-------------------------------------------------------------------------
 366
 367-spec application_modules(Application :: atom()) ->
 368    {ok, list(atom())} |
 369    {error, any()}.
 370
 371application_modules(Application) ->
 372    application_modules(Application, []).
 373
 374%%-------------------------------------------------------------------------
 375%% @doc
 376%% ===Retrieve a list of application modules with filter options.===
 377%% Options can contain {behavior, ModuleName} to list all the modules
 378%% that use a specific behaviour (the information will not be present if
 379%% the beam file was stripped).
 380%% @end
 381%%-------------------------------------------------------------------------
 382
 383-spec application_modules(Application :: atom(),
 384                          Options :: list({atom(), any()})) ->
 385    {ok, list(atom())} |
 386    {error, any()}.
 387
 388application_modules(Application, Options)
 389    when is_atom(Application), is_list(Options) ->
 390    case application:get_key(Application, modules) of
 391        {ok, Modules} ->
 392            {ok, modules_filter(Options, Modules, false)};
 393        undefined ->
 394            {error, {modules_missing, Application}}
 395    end.
 396
 397%%-------------------------------------------------------------------------
 398%% @doc
 399%% ===Make sure an application is loaded.===
 400%% @end
 401%%-------------------------------------------------------------------------
 402
 403-spec ensure_application_loaded(Application :: atom()) ->
 404    ok |
 405    {error, any()}.
 406
 407ensure_application_loaded(kernel) ->
 408    ok;
 409ensure_application_loaded(stdlib) ->
 410    ok;
 411ensure_application_loaded(Application) ->
 412    Loaded = case application:load(Application) of
 413        ok ->
 414            ok;
 415        {error, {already_loaded, Application}} ->
 416            ok;
 417        {error, _} = Error ->
 418            Error
 419     end,
 420     if
 421        Loaded =:= ok ->
 422            case application:get_key(Application, modules) of
 423                {ok, Modules} ->
 424                    % make sure all modules are loaded, even if the
 425                    % application information is already loaded, since
 426                    % loading the application data does not automatically
 427                    % load its modules
 428                    lists:foreach(fun(M) ->
 429                        % valid results, others cause a crash
 430                        case module_loaded(M) of
 431                            ok ->
 432                                ok;
 433                            {error, nofile} ->
 434                                error_logger:warning_msg("broken "
 435                                                         "application ~p "
 436                                                         "missing ~p file~n",
 437                                                         [Application, M]);
 438                            {error, Reason} ->
 439                                error_logger:error_msg("application ~p load "
 440                                                       "error ~p on ~p file~n",
 441                                                       [Application,
 442                                                        Reason, M])
 443                        end
 444                    end, Modules);
 445                undefined ->
 446                    ok
 447            end;
 448        true ->
 449            Loaded
 450    end.
 451
 452%%-------------------------------------------------------------------------
 453%% @doc
 454%% ===Make sure an application is started.===
 455%% @end
 456%%-------------------------------------------------------------------------
 457
 458-spec ensure_application_started(Application :: atom()) ->
 459    ok |
 460    {error, any()}.
 461
 462ensure_application_started(Application) ->
 463    case application:start(Application, temporary) of
 464        ok ->
 465            ok;
 466        {error, {already_started, Application}} ->
 467            ok;
 468        {error, _} = Error ->
 469            Error
 470    end.
 471
 472%%-------------------------------------------------------------------------
 473%% @doc
 474%% ===Make sure an application is stopped.===
 475%% @end
 476%%-------------------------------------------------------------------------
 477
 478-spec ensure_application_stopped(Application :: atom()) ->
 479    ok |
 480    {error, any()}.
 481
 482ensure_application_stopped(Application) ->
 483    case application:stop(Application) of
 484        ok ->
 485            ok;
 486        {error, {not_started, Application}} ->
 487            ok;
 488        {error, _} = Error ->
 489            Error
 490    end.
 491
 492%%-------------------------------------------------------------------------
 493%% @doc
 494%% ===Make sure a module is loaded.===
 495%% If the module is not loaded, attempt to load it.
 496%% @end
 497%%-------------------------------------------------------------------------
 498
 499-spec module_loaded(Module :: atom()) ->
 500    ok |
 501    {error, any()}.
 502
 503module_loaded(Module)
 504    when is_atom(Module) ->
 505    case is_module_loaded_check(Module) of
 506        false ->
 507            case code:load_file(Module) of
 508                {module, Module} ->
 509                    ok;
 510                {error, _} = Error ->
 511                    Error
 512            end;
 513        true ->
 514            ok
 515    end.
 516
 517%%-------------------------------------------------------------------------
 518%% @doc
 519%% ===Wait to check if a module is loaded.===
 520%% @end
 521%%-------------------------------------------------------------------------
 522
 523-spec is_module_loaded(Module :: atom()) ->
 524    ok |
 525    {error, any()}.
 526
 527is_module_loaded(Module)
 528    when is_atom(Module) ->
 529    case is_module_loaded(Module, 5000) of
 530        {ok, _} ->
 531            ok;
 532        {error, _} = Error ->
 533            Error
 534    end.
 535
 536%%-------------------------------------------------------------------------
 537%% @doc
 538%% ===Wait to check if a module is loaded.===
 539%% Return a new timeout value with the elapsed time subtracted.
 540%% @end
 541%%-------------------------------------------------------------------------
 542
 543-spec is_module_loaded(Module :: atom(),
 544                       Timeout :: non_neg_integer()) ->
 545    {ok, non_neg_integer()} |
 546    {error, any()}.
 547
 548is_module_loaded(Module, Timeout)
 549    when is_atom(Module), is_integer(Timeout), Timeout >= 0 ->
 550    case is_module_loaded_check(Module) of
 551        true ->
 552            {ok, Timeout};
 553        false ->
 554            case erlang:max(Timeout - ?IS_MODULE_LOADED_DELTA, 0) of
 555                0 ->
 556                    {error, timeout};
 557                NextTimeout ->
 558                    receive after ?IS_MODULE_LOADED_DELTA -> ok end,
 559                    is_module_loaded(Module, NextTimeout)
 560            end
 561    end.
 562
 563%%-------------------------------------------------------------------------
 564%% @doc
 565%% ===Make sure a module is purged.===
 566%% If the module is not loaded, ignore it.
 567%% @end
 568%%-------------------------------------------------------------------------
 569
 570-spec module_purged(Module :: atom()) ->
 571    ok |
 572    {error, any()}.
 573
 574module_purged(Module) ->
 575    module_purged(Module, 5000).
 576
 577%%-------------------------------------------------------------------------
 578%% @doc
 579%% ===Make sure a module is purged with a timeout.===
 580%% If the module is not loaded, ignore it.
 581%% @end
 582%%-------------------------------------------------------------------------
 583
 584-spec module_purged(Module :: atom(),
 585                    Timeout :: non_neg_integer() | infinity) ->
 586    ok |
 587    {error, any()}.
 588
 589module_purged(Module, Timeout)
 590    when is_atom(Module) ->
 591    modules_purged([Module], Timeout).
 592
 593%%-------------------------------------------------------------------------
 594%% @doc
 595%% ===List the exported functions of a module.===
 596%% @end
 597%%-------------------------------------------------------------------------
 598
 599-spec module_exports(Module :: atom()) ->
 600    list({atom(), pos_integer()}).
 601
 602module_exports(Module)
 603    when is_atom(Module) ->
 604    {exports, L0} = lists:keyfind(exports, 1, Module:module_info()),
 605    {value, _, L1} = lists:keytake(module_info, 1, L0),
 606    {value, _, L2} = lists:keytake(module_info, 1, L1),
 607    L2.
 608
 609%%-------------------------------------------------------------------------
 610%% @doc
 611%% ===Start everything specified within a script file.===
 612%% A script file is the input used when creating a boot file, which is the
 613%% file used when first starting the Erlang VM.  This function checks
 614%% all applications to determine if they are already running with the
 615%% expected versions.  All modules are checked to make sure they have
 616%% been loaded, if they are expected to have been loaded. Normally,
 617%% the script is only used in the binary boot file format and only a single
 618%% boot file is used during the lifetime of the Erlang VM
 619%% (so it is unclear if using this function is bad or just unorthodox).
 620%% The script file is expected to be within a release directory created
 621%% by reltool.
 622%% @end
 623%%-------------------------------------------------------------------------
 624
 625-spec script_start(FilePath :: string()) ->
 626    {ok, list(atom())} |
 627    {error, any()}.
 628
 629script_start(FilePath)
 630    when is_list(FilePath) ->
 631    true = lists:suffix(".script", FilePath),
 632    % system name and version are ignored
 633    {ok, [{script, {_Name, _Vsn}, Instructions}]} = file:consult(FilePath),
 634    Dir = filename:dirname(FilePath),
 635    % expects the typical directory structure produced by reltool
 636    DirNames = filename:split(Dir),
 637    case erlang:length(DirNames) of
 638        DirNamesLength when DirNamesLength > 2 ->
 639            Root = lists:sublist(DirNames, DirNamesLength - 2),
 640            case filelib:is_dir(filename:join(Root ++ ["lib"])) of
 641                true ->
 642                    % on success, return the last application to be started
 643                    % (should be the main application since all the application
 644                    %  dependencies are started first)
 645                    script_start_instructions(Instructions, Root);
 646                false ->
 647                    {error, invalid_release_structure}
 648            end;
 649        _ ->
 650            {error, invalid_release_directory}
 651    end.
 652
 653%%-------------------------------------------------------------------------
 654%% @doc
 655%% ===Stop everything specified within a script file.===
 656%% A script file is the input used when creating a boot file, which is the
 657%% file used when first starting the Erlang VM.  This function checks
 658%% all applications to determine applications which can be safely removed
 659%% (assuming the application dependencies are correct).  The applications
 660%% will then be stopped and their modules will be purged.  Normally,
 661%% the script is only used in the binary boot file format and only a single
 662%% boot file is used during the lifetime of the Erlang VM
 663%% (so it is unclear if using this function is bad or just unorthodox).
 664%% The script file is expected to be within a release directory created
 665%% by reltool.
 666%% @end
 667%%-------------------------------------------------------------------------
 668
 669-spec script_remove(FilePath :: string()) ->
 670    ok |
 671    {error, any()}.
 672
 673script_remove(FilePath) ->
 674    script_remove(FilePath, 5000).
 675
 676%%-------------------------------------------------------------------------
 677%% @doc
 678%% ===Stop everything specified within a script file with a timeout.===
 679%% A script file is the input used when creating a boot file, which is the
 680%% file used when first starting the Erlang VM.  This function checks
 681%% all applications to determine applications which can be safely removed
 682%% (assuming the application dependencies are correct).  The applications
 683%% will then be stopped and their modules will be purged.  Normally,
 684%% the script is only used in the binary boot file format and only a single
 685%% boot file is used during the lifetime of the Erlang VM
 686%% (so it is unclear if using this function is bad or just unorthodox).
 687%% The script file is expected to be within a release directory created
 688%% by reltool.
 689%% @end
 690%%-------------------------------------------------------------------------
 691
 692-spec script_remove(FilePath :: string(),
 693                    Timeout :: pos_integer() | infinity) ->
 694    ok |
 695    {error, any()}.
 696
 697script_remove(FilePath, Timeout)
 698    when is_list(FilePath),
 699         ((is_integer(Timeout) andalso (Timeout > 0)) orelse
 700          (Timeout =:= infinity))->
 701    true = lists:suffix(".script", FilePath),
 702    % system name and version are ignored
 703    {ok, [{script, {_Name, _Vsn}, Instructions}]} = file:consult(FilePath),
 704    Dir = filename:dirname(FilePath),
 705    % expects the typical directory structure produced by reltool
 706    DirNames = filename:split(Dir),
 707    case erlang:length(DirNames) of
 708        DirNamesLength when DirNamesLength > 2 ->
 709            Root = lists:sublist(DirNames, DirNamesLength - 2),
 710            case filelib:is_dir(filename:join(Root ++ ["lib"])) of
 711                true ->
 712                    case script_remove_instructions(Instructions) of
 713                        {ok, Applications} ->
 714                            NewTimeout = if
 715                                Timeout =:= infinity ->
 716                                    Timeout;
 717                                is_integer(Timeout) ->
 718                                    erlang:round(0.5 + Timeout /
 719                                        erlang:length(Applications))
 720                            end,
 721                            applications_remove(Applications, NewTimeout);
 722                        {error, _} = Error ->
 723                            Error
 724                    end;
 725                false ->
 726                    {error, invalid_release_structure}
 727            end;
 728        _ ->
 729            {error, invalid_release_directory}
 730    end.
 731
 732%%%------------------------------------------------------------------------
 733%%% Private functions
 734%%%------------------------------------------------------------------------
 735
 736application_start_set_env([], _, _) ->
 737    ok;
 738application_start_set_env([{K, V} | L], Application, Timeout) ->
 739    try application:set_env(Application, K, V, Timeout) of
 740        ok ->
 741            application_start_set_env(L, Application, Timeout)
 742    catch
 743        exit:{timeout, _} ->
 744            {error, application_controller_timeout}
 745    end;
 746application_start_set_env(_, _, _) ->
 747    {error, invalid_application_env}.
 748
 749application_start_dependencies([]) ->
 750    ok;
 751application_start_dependencies([A | As]) ->
 752    case ensure_application_started(A) of
 753        ok ->
 754            application_start_dependencies(As);
 755        {error, _} = Error ->
 756            Error
 757    end.
 758
 759applications_start_element([], _) ->
 760    ok;
 761applications_start_element([Application | Applications], Timeout)
 762    when is_atom(Application) ->
 763    case application_start(Application, [], Timeout) of
 764        ok ->
 765            applications_start_element(Applications, Timeout);
 766        {error, _} = Error ->
 767            Error
 768    end;
 769applications_start_element([{Application, Env} | Applications], Timeout)
 770    when is_atom(Application), is_list(Env) ->
 771    case application_start(Application, Env, Timeout) of
 772        ok ->
 773            applications_start_element(Applications, Timeout);
 774        {error, _} = Error ->
 775            Error
 776    end.
 777
 778application_stop_external([], Apps) ->
 779    {ok, Apps};
 780application_stop_external([A | As], Apps) ->
 781    case lists:keytake(A, 1, Apps) of
 782        false ->
 783            {error, {not_started, A}};
 784        {value, _, NextApps} ->
 785            application_stop_external(As, NextApps)
 786    end.
 787
 788application_stop_ignore(L) ->
 789    application_stop_ignore(sets:new(), L).
 790
 791application_stop_ignore(Required, []) ->
 792    sets:to_list(Required);
 793application_stop_ignore(Required, [{A, _, _} | L]) ->
 794    case applications_dependencies(A) of
 795        {ok, As} ->
 796            application_stop_ignore(
 797                sets:union(Required, sets:from_list(As)), L);
 798        {error, _} = Error ->
 799            Error
 800    end.
 801
 802application_stop_all([]) ->
 803    ok;
 804application_stop_all([A | As]) ->
 805    case ensure_application_stopped(A) of
 806        ok ->
 807            application_stop_all(As);
 808        {error, _} = Error ->
 809            Error
 810    end.
 811
 812application_stop_dependencies(Application)
 813    when is_atom(Application) ->
 814    case applications_dependencies(Application) of
 815        {ok, StopAs0} ->
 816            case ensure_application_stopped(Application) of
 817                ok ->
 818                    StopAs1 = delete_all(kernel, StopAs0),
 819                    StopAs2 = delete_all(stdlib, StopAs1),
 820                    Apps = application:loaded_applications(),
 821                    {value, _, OtherApps0} = lists:keytake(Application,
 822                                                           1, Apps),
 823                    % determine applications which are not dependencies
 824                    case application_stop_external(StopAs2, OtherApps0) of
 825                        {ok, OtherAppsN} ->
 826                            % check to see the required applications
 827                            % separate from the application dependencies
 828                            RequiredAs = application_stop_ignore(OtherAppsN),
 829                            % ignore all applications that are requirements
 830                            % of other applications
 831                            StopAsN = lists:reverse(lists:foldl(fun(A, As) ->
 832                                delete_all(A, As)
 833                            end, StopAs2, RequiredAs)),
 834                            % stop all the application dependencies
 835                            % that are no longer required
 836                            case application_stop_all(StopAsN) of
 837                                ok ->
 838                                    {ok, [Application | StopAsN]};
 839                                {error, _} = Error ->
 840                                    Error
 841                            end;
 842                        {error, _} = Error ->
 843                            Error
 844                    end;
 845                {error, _} = Error ->
 846                    Error
 847            end;
 848        {error, _} = Error ->
 849            Error
 850    end.
 851
 852applications_remove([], _) ->
 853    ok;
 854applications_remove([Application | Applications], Timeout) ->
 855    case application_remove(Application, Timeout) of
 856        ok ->
 857            applications_remove(Applications, Timeout);
 858        {error, _} = Error ->
 859            Error
 860    end.
 861
 862applications_purged([], _) ->
 863    ok;
 864applications_purged([Application | Applications], Timeout) ->
 865    case application_purged(Application, Timeout) of
 866        ok ->
 867            applications_purged(Applications, Timeout);
 868        {error, _} = Error ->
 869            Error
 870    end.
 871
 872applications_dependencies(A) ->
 873    Included = case application:get_key(A, included_applications) of
 874        undefined ->
 875            ok;
 876        {ok, LoadAs} ->
 877            applications_dependencies_load(LoadAs)
 878    end,
 879    if
 880        Included =:= ok ->
 881            case application:get_key(A, applications) of
 882                undefined ->
 883                    {error, {undefined_dependencies, A}};
 884                {ok, As} ->
 885                    applications_dependencies(As, As)
 886            end;
 887        true ->
 888            Included
 889    end.
 890
 891applications_dependencies_load([]) ->
 892    ok;
 893applications_dependencies_load([A | Rest]) ->
 894    case ensure_application_loaded(A) of
 895        ok ->
 896            As1 = case application:get_key(A, included_applications) of
 897                undefined ->
 898                    [];
 899                {ok, As0} ->
 900                    As0
 901            end,
 902            LoadAs = case application:get_key(A, applications) of
 903                undefined ->
 904                    As1;
 905                {ok, As2} ->
 906                    As1 ++ As2
 907            end,
 908            case applications_dependencies_load(LoadAs) of
 909                ok ->
 910                    applications_dependencies_load(Rest);
 911                {error, _} = Error ->
 912                    Error
 913            end;
 914        {error, _} = Error ->
 915            Error
 916    end.
 917
 918applications_dependencies([], As) ->
 919    {ok, As};
 920applications_dependencies([A | Rest], As) ->
 921    case ensure_application_loaded(A) of
 922        ok ->
 923            Included = case application:get_key(A, included_applications) of
 924                undefined ->
 925                    ok;
 926                {ok, LoadAs} ->
 927                    applications_dependencies_load(LoadAs)
 928            end,
 929            if
 930                Included =:= ok ->
 931                    case application:get_key(A, applications) of
 932                        undefined ->
 933                            {error, {undefined_dependencies, A}};
 934                        {ok, []} ->
 935                            applications_dependencies(Rest, As);
 936                        {ok, NextAs} ->
 937                            OtherAs = lists:foldl(fun(NextA, L) ->
 938                                lists:delete(NextA, L)
 939                            end, As, NextAs),
 940                            case applications_dependencies(NextAs,
 941                                                           NextAs ++ OtherAs) of
 942                                {ok, NewAs} ->
 943                                    applications_dependencies(Rest, NewAs);
 944                                {error, _} = Error ->
 945                                    Error
 946                            end
 947                    end;
 948                true ->
 949                    Included
 950            end;
 951        {error, _} = Error ->
 952            Error
 953    end.
 954
 955applications_top_level(Applications) ->
 956    true = erlang:length(lists:usort(Applications)) ==
 957           erlang:length(Applications), % no duplicates
 958    case applications_top_level(Applications, sets:new()) of
 959        {ok, Dependencies} ->
 960            TopLevelApplications = lists:foldl(fun(A, L) ->
 961                lists:delete(A, L)
 962            end, Applications, sets:to_list(Dependencies)),
 963            {ok, TopLevelApplications};
 964        {error, _} = Error ->
 965            Error
 966    end.
 967
 968applications_top_level([], Dependencies) ->
 969    {ok, Dependencies};
 970applications_top_level([Application | Applications], Dependencies) ->
 971    case applications_dependencies(Application) of
 972        {ok, As} ->
 973            applications_top_level(Applications,
 974                                   sets:union(sets:from_list(As),
 975                                              Dependencies));
 976        {error, _} = Error ->
 977            Error
 978    end.
 979
 980script_start_instructions(L, Root) ->
 981    Apps = application:loaded_applications(),
 982    script_start_instructions(L, preload, [], Root, Apps).
 983
 984script_start_instructions([], started, Applications, _, _) ->
 985    applications_top_level(Applications);
 986script_start_instructions([{progress, Progress} | L],
 987                          _, Applications, Root, Apps) ->
 988    script_start_instructions(L, Progress, Applications, Root, Apps);
 989script_start_instructions([{preLoaded, _} | L],
 990                          preload, Applications, Root, Apps) ->
 991    script_start_instructions(L, preload, Applications, Root, Apps);
 992script_start_instructions([{kernel_load_completed} | L],
 993                          preloaded, Applications, Root, Apps) ->
 994    script_start_instructions(L, kernel_load_completed,
 995                              Applications, Root, Apps);
 996script_start_instructions([{path, Paths} | L],
 997                          preloaded, Applications, Root, Apps) ->
 998    case ensure_code_paths(Paths, Apps) of
 999        ok ->
1000            script_start_instructions(L, preloaded, Applications, Root, Apps);
1001        {error, _} = Error ->
1002            Error
1003    end;
1004script_start_instructions([{primLoad, Modules} | L],
1005                          preloaded, Applications, Root, Apps) ->
1006    Loaded = lists:all(fun(M) ->
1007        is_module_loaded_check(M) =:= true
1008    end, Modules),
1009    if
1010        Loaded ->
1011            script_start_instructions(L, preloaded, Applications, Root, Apps);
1012        true ->
1013            {error, modules_not_preloaded}
1014    end;
1015script_start_instructions([{kernel_load_completed} | L],
1016                          kernel_load_completed, Applications, Root, Apps) ->
1017    script_start_instructions(L, kernel_load_completed,
1018                              Applications, Root, Apps);
1019script_start_instructions([{primLoad, Modules} | L],
1020                          kernel_load_completed, Applications, Root, Apps) ->
1021    case ensure_all_loaded(Modules) of
1022        ok ->
1023            script_start_instructions(L, kernel_load_completed,
1024                                      Applications, Root, Apps);
1025        {error, _} = Error ->
1026            Error
1027    end;
1028script_start_instructions([{path, Paths} | L],
1029                          kernel_load_completed, Applications, Root, Apps) ->
1030    case load_all_paths(Paths, Root) of
1031        ok ->
1032            script_start_instructions(L, kernel_load_completed,
1033                                      Applications, Root, Apps);
1034        {error, _} = Error ->
1035            Error
1036    end;
1037script_start_instructions([{path, _} | L],
1038                          modules_loaded, Applications, Root, Apps) ->
1039    script_start_instructions(L, modules_loaded, Applications, Root, Apps);
1040script_start_instructions([{kernelProcess, _, _} | L],
1041                          modules_loaded, Applications, Root, Apps) ->
1042    script_start_instructions(L, modules_loaded, Applications, Root, Apps);
1043script_start_instructions([{apply, {application, load, [AppDescr]}} | L],
1044                          init_kernel_started, Applications, Root, Apps) ->
1045    {application, A, [_ | _] = AppSpecKeys} = AppDescr,
1046    case lists:keyfind(A, 1, Apps) of
1047        {A, _, VSN} ->
1048            {vsn, RequestedVSN} = lists:keyfind(vsn, 1, AppSpecKeys),
1049            if
1050                VSN == RequestedVSN ->
1051                    script_start_instructions(L, init_kernel_started,
1052                                              [A | Applications], Root, Apps);
1053                true ->
1054                    {error, {version_mismatch, A, RequestedVSN, VSN}}
1055            end;
1056        false ->
1057            case application:load(AppDescr) of
1058                ok ->
1059                    script_start_instructions(L, init_kernel_started,
1060                                              [A | Applications], Root, Apps);
1061                {error, _} = Error ->
1062                    Error
1063            end
1064    end;
1065script_start_instructions([{apply,
1066                            {application, start_boot, [A | _]}} | L],
1067                          applications_loaded, Applications, Root, Apps)
1068    when A =:= kernel; A =:= stdlib ->
1069    % if this code is being used, kernel and stdlib should have already
1070    % been started with the boot file that was used to start the Erlang VM
1071    script_start_instructions(L, applications_loaded, Applications, Root, Apps);
1072script_start_instructions([{apply,
1073                            {application, start_boot, [A | _]}} | L],
1074                          applications_loaded, Applications, Root, Apps) ->
1075    case ensure_application_started(A) of
1076        ok ->
1077            script_start_instructions(L, applications_loaded,
1078                                      Applications, Root, Apps);
1079        {error, _} = Error ->
1080            Error
1081    end;
1082script_start_instructions([{apply, {c, erlangrc, _}} | L],
1083                          applications_loaded, Applications, Root, Apps) ->
1084    script_start_instructions(L, applications_loaded,
1085                              Applications, Root, Apps).
1086
1087script_remove_instructions(L) ->
1088    script_remove_instructions(L, preload, []).
1089
1090script_remove_instructions([], started, Applications) ->
1091    applications_top_level(Applications);
1092script_remove_instructions([{progress, Progress} | L], _, Applications) ->
1093    script_remove_instructions(L, Progress, Applications);
1094script_remove_instructions([{preLoaded, _} | L], preload, Applications) ->
1095    script_remove_instructions(L, preload, Applications);
1096script_remove_instructions([{kernel_load_completed} | L],
1097                           preloaded, Applications) ->
1098    script_remove_instructions(L, kernel_load_completed, Applications);
1099script_remove_instructions([{path, _} | L],
1100                           preloaded, Applications) ->
1101    script_remove_instructions(L, preloaded, Applications);
1102script_remove_instructions([{primLoad, _} | L],
1103                           preloaded, Applications) ->
1104    script_remove_instructions(L, preloaded, Applications);
1105script_remove_instructions([{kernel_load_completed} | L],
1106                           kernel_load_completed, Applications) ->
1107    script_remove_instructions(L, kernel_load_completed, Applications);
1108script_remove_instructions([{primLoad, _} | L],
1109                           kernel_load_completed, Applications) ->
1110    script_remove_instructions(L, kernel_load_completed, Applications);
1111script_remove_instructions([{path, _} | L],
1112                           kernel_load_completed, Applications) ->
1113    script_remove_instructions(L, kernel_load_completed, Applications);
1114script_remove_instructions([{path, _} | L],
1115                           modules_loaded, Applications) ->
1116    script_remove_instructions(L, modules_loaded, Applications);
1117script_remove_instructions([{kernelProcess, _, _} | L],
1118                           modules_loaded, Applications) ->
1119    script_remove_instructions(L, modules_loaded, Applications);
1120script_remove_instructions([{apply, {application, load, [AppDescr]}} | L],
1121                           init_kernel_started, Applications) ->
1122    {application, A, [_ | _]} = AppDescr,
1123    script_remove_instructions(L, init_kernel_started, [A | Applications]);
1124script_remove_instructions([{apply, {application, start_boot, _}} | L],
1125                           applications_loaded, Applications) ->
1126    script_remove_instructions(L, applications_loaded, Applications);
1127script_remove_instructions([{apply, {c, erlangrc, _}} | L],
1128                           applications_loaded, Applications) ->
1129    script_remove_instructions(L, applications_loaded, Applications).
1130
1131ensure_all_loaded([]) ->
1132    ok;
1133ensure_all_loaded([Module | Modules]) ->
1134    case is_module_loaded_check(Module) of
1135        true ->
1136            Loaded = lists:all(fun(M) ->
1137                is_module_loaded_check(M) =:= true
1138            end, Modules),
1139            if
1140                Loaded ->
1141                    ok;
1142                true ->
1143                    {error, modules_partially_loaded}
1144            end;
1145        false ->
1146            NotLoaded = lists:all(fun(M) ->
1147                is_module_loaded_check(M) =:= false
1148            end, Modules),
1149            if
1150                NotLoaded ->
1151                    load_all_modules([Module | Modules]);
1152                true ->
1153                    {error, modules_partially_loaded}
1154            end
1155    end.
1156
1157ensure_code_paths([], _) ->
1158    ok;
1159ensure_code_paths([P | Paths], Apps) ->
1160    ["$ROOT", "lib", NameVSN, "ebin"] = filename:split(P),
1161    {Name, VSN} = split_name_vsn(NameVSN),
1162    Application = erlang:list_to_existing_atom(Name),
1163    case lists:keyfind(Application, 1, Apps) of
1164        {Application, _, VSN} ->
1165            ensure_code_paths(Paths, Apps);
1166        {Application, _, InvalidVSN} ->
1167            {error, {version_mismatch, Application, VSN, InvalidVSN}};
1168        false ->
1169            {error, {not_loaded, Application, VSN}}
1170    end.
1171
1172is_module_loaded_check(Module) when is_atom(Module) ->
1173    case code:is_loaded(Module) of
1174        {file, _} ->
1175            true;
1176        false ->
1177            false
1178    end.
1179
1180modules_purged(Modules, infinity) ->
1181    modules_purged(Modules, [], 5000);
1182modules_purged(Modules, Timeout)
1183    when is_list(Modules), is_integer(Timeout), Timeout >= 0 ->
1184    modules_purged(Modules, [], Timeout).
1185
1186modules_purged([], [], _) ->
1187    ok;
1188modules_purged([], BusyModules, Timeout) ->
1189    case erlang:max(Timeout - ?MODULES_PURGED_DELTA, 0) of
1190        0 ->
1191            % attempt to force the purge, killing any processes that remain
1192            % executing the code
1193            case lists:dropwhile(fun code:purge/1, BusyModules) of
1194                [] ->
1195                    ok;
1196                _ ->
1197                    {error, timeout}
1198            end;
1199        NextTimeout ->
1200            receive after ?MODULES_PURGED_DELTA -> ok end,
1201            modules_purged(lists:reverse(BusyModules), [], NextTimeout)
1202    end;
1203modules_purged([Module | Modules], BusyModules, Timeout) ->
1204    % purge the Module if no processes remain executing the code
1205    case code:soft_purge(Module) of
1206        true ->
1207            modules_purged(Modules, BusyModules, Timeout);
1208        false ->
1209            modules_purged(Modules, [Module | BusyModules], Timeout)
1210    end.
1211
1212modules_filter([], Modules, _) ->
1213    Modules;
1214modules_filter([{Behaviour, Name} | Options], Modules, Loaded)
1215    when Behaviour =:= behaviour; Behaviour =:= behavior ->
1216    NewModules = lists:filter(fun(Module) ->
1217        Attributes = if
1218            Loaded =:= true ->
1219                lists:keyfind(attributes, 1, Module:module_info());
1220            Loaded =:= false ->
1221                case code:is_loaded(Module) of
1222                    {file, _} ->
1223                        lists:keyfind(attributes, 1, Module:module_info());
1224                    false ->
1225                        false
1226                end
1227        end,
1228        case Attributes of
1229            false ->
1230                false;
1231            {attributes, []} ->
1232                false;
1233            {attributes, AttributesL} ->
1234                case lists:keyfind(behaviour, 1, AttributesL) of
1235                    false ->
1236                        false;
1237                    {behaviour, []} ->
1238                        false;
1239                    {behaviour, Names} ->
1240                        lists:member(Name, Names)
1241                end
1242        end
1243    end, Modules),
1244    modules_filter(Options, NewModules, true);
1245modules_filter([_ | _], _, _) ->
1246    erlang:exit(badarg).
1247
1248load_all_modules([]) ->
1249    ok;
1250load_all_modules([Module | Modules]) ->
1251    case code:load_file(Module) of
1252        {module, Module} ->
1253            load_all_modules(Modules);
1254        {error, Reason} ->
1255            {error, {Reason, Module}}
1256    end.
1257
1258load_all_paths([], _) ->
1259    ok;
1260load_all_paths([P | Paths], Root) ->
1261    ["$ROOT", "lib", NameVSN, "ebin"] = filename:split(P),
1262    CodePath = filename:join(Root ++ ["lib", NameVSN, "ebin"]),
1263    case code:add_pathz(CodePath) of
1264        true ->
1265            load_all_paths(Paths, Root);
1266        {error, Reason} ->
1267            {error, {Reason, CodePath}}
1268    end.
1269
1270split_name_vsn(NameVSN) ->
1271    split_name_vsn([], [], NameVSN).
1272split_name_vsn([_ | _] = Name, VSN, []) ->
1273    [$- | FinalVSN] = lists:reverse(VSN),
1274    {Name, FinalVSN};
1275split_name_vsn(Name, NameSegment, [$- | L]) ->
1276    split_name_vsn(lists:reverse(NameSegment) ++ Name, [$-], L);
1277split_name_vsn(Name, VSN, [C | L]) ->
1278    split_name_vsn(Name, [C | VSN], L).
1279
1280delete_all(Elem, List) when is_list(List) ->
1281    delete_all(Elem, [], List).
1282delete_all(Elem, L, [Elem | T]) ->
1283    delete_all(Elem, L, T);
1284delete_all(Elem, L, [H | T]) ->
1285    delete_all(Elem, [H | L], T);
1286delete_all(_, L, []) ->
1287    lists:reverse(L).