application_controller.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,925 行 · 第 1/5 页

ERL
1,925
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(application_controller).%% External exports-export([start/1, 	 load_application/1, unload_application/1, 	 start_application/2, start_boot_application/2, stop_application/1,	 control_application/1,	 change_application_data/2, prep_config_change/0, config_change/1,	 which_applications/0, which_applications/1,	 loaded_applications/0, info/0,	 get_pid_env/2, get_env/2, get_pid_all_env/1, get_all_env/1,	 get_pid_key/2, get_key/2, get_pid_all_key/1, get_all_key/1,	 get_master/1, get_application/1, get_application_module/1,	 start_type/1, permit_application/2, do_config_diff/2,	 set_env/3, set_env/4, unset_env/2, unset_env/3]).%% Internal exports-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2, 	 code_change/3, init_starter/4, get_loaded/1]).%% Test exports, only to be used from the test suites-export([test_change_apps/2]).-import(lists, [zf/2, map/2, foreach/2, foldl/3,		keysearch/3, keydelete/3, keyreplace/4]).-include("application_master.hrl").-define(AC, ?MODULE). % Name of process%%%-----------------------------------------------------------------%%% The application_controller controls local applications only.  A%%% local application can be loaded/started/stopped/unloaded and%%% changed.  The control of distributed applications is taken care of%%% by another process (default is dist_ac).%%%%%% When an application has been started (by a call to application:start)%%% it can be running or not running (on this node).  For example,%%% a distributed application must be started on all nodes, but%%% may be running on one node at the time.%%%%%% The external API to this module is in the module 'application'.%%% %%% The process that controls distributed applications (called dist%%% ac).  calls application_controller:control_application(Name) to%%% take responsibility for an application.  The interface between AC%%% and the dist_ac process is message-based:%%%%%% AC                                        DIST AC%%% ==                                        =======%%%     --> {ac_load_application_req, Name}%%%     <-- {ac_load_application_reply, Name, LoadReply}%%%     --> {ac_start_application_req, Name}       (*)%%%     <-- {ac_start_application_reply, Name, StartReply}%%%     --> {ac_application_run, Name, Res}%%%     --> {ac_application_not_run, Name, Res}%%%     --> {ac_application_stopped, Name}%%%     --> {ac_application_unloaded, Name}%%%     <-- {ac_change_application_req, Name, Req} (**)%%%%%% Where LoadReply =%%%   ok              - App is loaded%%%   {error, R}      - An error occurred%%% And StartReply =%%%   start_it        - DIST AC decided that AC should start the app%%%   {started, Node} - The app is started distributed at Node%%%   not_started     - The app should not be running at this time%%%   {takeover, Node}- The app should takeover from Node%%%   {error, R}      - an error occurred%%% And Req =%%%   start_it        - DIST AC wants AC to start the app locally%%%   stop_it         - AC should stop the app.%%%   {takeover, Node, RestartType}%%%                   - AC should start the app as a takeover%%%   {failover, Node, RestartType}%%%                   - AC should start the app as a failover%%%   {started, Node} - The app is started at Node%%%                     NOTE: The app must have been started at this node%%%                     before this request is sent!%%% And Res =%%%   ok              - Application is started locally%%%   {error, R}      - Start of application failed%%%%%% (*)%%%  The call to application:start() doesn't return until the%%%  ac_start_application_reply has been received by AC.  AC%%%  itself is not blocked however.%%% (**)%%%  DIST AC gets ACK to its ac_change_application_req, but not as a%%%  separate messgage.  Instead the normal messages are used as:%%%   start_it   generates an ac_application_run%%%   stop_it    generates an ac_application_not_run%%%   takeover   generates an ac_application_run%%%   started    doesn't generate anything%%%%%% There is a distinction between application:stop and stop_it%%% from a dist ac process.  The first one stops the application,%%% and resets the internal structures as they were before start was%%% called.  stop_it stops the application, but just marks it as%%% not being running.%%%%%% When a dist ac process has taken control of an application, no%%% other process can take the control.%%%-----------------------------------------------------------------%%-----------------------------------------------------------------%% Naming conventions:%%   App = appl_descr()%%   Appl = #appl%%   AppName = atom()%%   Application = App | AppName%%------------------------------------------------------------------record(state, {loading = [], starting = [], start_p_false = [], running = [],		control = [], started = [], start_req = [], conf_data}).%%-----------------------------------------------------------------%% loading     = [{AppName, From}] - Load not yet finished%% starting    = [{AppName, RestartType, Type, From}] - Start not%%                 yet finished%% start_p_false = [{AppName, RestartType, Type, From}] - Start not%%                 executed because permit == false%% running     = [{AppName, Pid}] - running locally (Pid == application_master)%%               [{AppName, {distributed, Node}}] - running on Node%% control     = [{AppName, Controller}]%% started     = [{AppName, RestartType}] - Names of all apps that%%                 have been started (but may not run because%%                 permission = false)%% conf_data   = [{AppName, Env}]%% start_req   = [{AppName, From}] - list of all start requests%% Id          = AMPid | undefined | {distributed, Node}%% Env         = [{Key, Value}]%%------------------------------------------------------------------record(appl, {name, appl_data, descr, id, vsn, restart_type, inc_apps, apps}).%%-----------------------------------------------------------------%% Func: start/1%% Args: KernelApp = appl_descr()%%       appl_descr() = [{application, Name, [appl_opt()]}]%%       appl_opt() = {description, string()}           |%%                    {vsn, string()}                   |%%                    {id, string()},                   |%%                    {modules, [Module|{Module,Vsn}]}  |%%                    {registered, [atom()]}            |%%                    {applications, [atom()]}          |%%                    {included_applications, [atom()]} |%%                    {env, [{atom(), term()}]}         |%%                    {start_phases, [{atom(), term()}]}|%%                    {maxT, integer()|infinity}        |%%                    {maxP, integer()|infinity}        |%%                    {mod, {Module, term()}}%%         Module = atom()%%         Vsn = term()%% Purpose: Starts the application_controller.  This process starts all%%          application masters for the applications.%%          The kernel application is the only application that is%%          treated specially.  The reason for this is that the kernel%%          starts user.  This process is special because it should%%          be group_leader for this process.%% Pre: All modules are loaded, or will be loaded on demand.%% Returns: {ok, Pid} | ReasonStr%%-----------------------------------------------------------------start(KernelApp) ->    %% OTP-5811 Don't start as a gen_server to prevent crash report    %% when (if) the process terminates    Init = self(),    AC = spawn_link(fun() -> init(Init, KernelApp) end),    receive	{ack, AC, ok} ->	    {ok, AC};	{ack, AC, {error, Reason}} ->	    to_string(Reason); % init doesn't want error tuple, only a reason	{'EXIT', _Pid, Reason} ->	    to_string(Reason)    end.	%%-----------------------------------------------------------------%% Func: load_application/1%% Args: Application = appl_descr() | atom()%% Purpose: Loads an application.  Currently just inserts the%%          application's env.%% Returns: ok | {error, Reason}%%-----------------------------------------------------------------load_application(Application) ->    gen_server:call(?AC, {load_application, Application}, infinity).unload_application(AppName) ->    gen_server:call(?AC, {unload_application, AppName}, infinity).%%-----------------------------------------------------------------%% Func: start_application/2%% Args: Application = atom()%%       RestartType = permanent | transient | temporary%% Purpose: Starts a new application.%%          The RestartType specifies what should happen if the%%          application dies:%%          If it is permanent, all other applications are terminated,%%            and the application_controller dies.%%          If it is transient, and the application dies normally,%%            this is reported and no other applications are terminated.%%            If the application dies abnormally, all other applications%%            are terminated, and the application_controller dies.%%          If it is temporary and the application dies this is reported%%            and no other applications are terminated.  In this way,%%            an application can run in test mode, without disturbing%%            the other applications.%%          The caller of this function is suspended until the application%%          is started, either locally or distributed.%% Returns: ok | {error, Reason}%%-----------------------------------------------------------------start_application(AppName, RestartType) ->    gen_server:call(?AC, {start_application, AppName, RestartType}, infinity).%%-----------------------------------------------------------------%% Func: start_boot_application/2%% The same as start_application/2 expect that this function is%% called from the boot script file. It mustnot be used by the operator.%% This function will cause a node crash if a permanent application %% fails to boot start%%-----------------------------------------------------------------start_boot_application(Application, RestartType) ->    case {application:load(Application), RestartType} of	{ok, _} ->	    AppName = get_appl_name(Application),	    gen_server:call(?AC, {start_application, AppName, RestartType}, infinity);	{{error, {already_loaded, AppName}}, _} ->	    gen_server:call(?AC, {start_application, AppName, RestartType}, infinity);	{{error,{bad_environment_value,Env}}, permanent} ->	    Txt = io_lib:format("Bad environment variable: ~p  Application: ~p",				[Env, Application]),	    exit({error, list_to_atom(lists:flatten(Txt))});	{Error, _} ->	    Error    end.stop_application(AppName) ->    gen_server:call(?AC, {stop_application, AppName}, infinity).%%-----------------------------------------------------------------%% Returns: [{Name, Descr, Vsn}]%%-----------------------------------------------------------------which_applications() ->    gen_server:call(?AC, which_applications).    which_applications(Timeout) ->    gen_server:call(?AC, which_applications, Timeout).loaded_applications() ->    ets:filter(ac_tab,	       fun([{{loaded, AppName}, #appl{descr = Descr, vsn = Vsn}}]) ->		       {true, {AppName, Descr, Vsn}};		  (_) ->		       false	       end,	       []).%% Returns some debug infoinfo() ->    gen_server:call(?AC, info).    control_application(AppName) ->    gen_server:call(?AC, {control_application, AppName}, infinity).%%-----------------------------------------------------------------%% Func: change_application_data/2%% Args: Applications = [appl_descr()]%%       Config = [{AppName, [{Par,Val}]}]%% Purpose: Change all applications and their parameters on this node.%%          This function should be used from a release handler, at%%          the same time as the .app or start.boot file is%%          introduced.  Note that during some time the ACs may have%%          different view of e.g. the distributed applications.%%          This is solved by syncing the release installation.%%          However, strange things may happen if a node crashes%%          and two other nodes have different opinons about who's%%          gonna start the applications.  The release handler must%%          shutdown each involved node in this case.%%          Note that this function is used to change existing apps,%%          adding new/deleting old isn't handled by this function.%%          Changes an application's vsn, descr and env.%% Returns: ok | {error, Reason}%%          If an error occurred, the situation may be inconsistent,%%          so the release handler must restart the node.  E.g. if%%          some applicatation may have got new config data.%%-----------------------------------------------------------------change_application_data(Applications, Config) ->    gen_server:call(?AC, 		    {change_application_data, Applications, Config},		    infinity).prep_config_change() ->    gen_server:call(?AC, 		    prep_config_change,		    infinity).config_change(EnvPrev) ->    gen_server:call(?AC, 		    {config_change, EnvPrev},		    infinity).get_pid_env(Master, Key) ->    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of	[[AppName]] -> get_env(AppName, Key);	_ -> undefined    end.get_env(AppName, Key) ->    case ets:lookup(ac_tab, {env, AppName, Key}) of	[{_, Val}] -> {ok, Val};	_ -> undefined    end.get_pid_all_env(Master) ->    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of	[[AppName]] -> get_all_env(AppName);	_ -> []    end.get_all_env(AppName) ->    map(fun([Key, Val]) -> {Key, Val} end,	ets:match(ac_tab, {{env, AppName, '$1'}, '$2'})).get_pid_key(Master, Key) ->    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of	[[AppName]] -> get_key(AppName, Key);	_ -> undefined    end.get_key(AppName, Key) ->    case ets:lookup(ac_tab, {loaded, AppName}) of	[{_, Appl}] ->	    case Key of 		description ->		    {ok, Appl#appl.descr};		id ->		    {ok, Appl#appl.id};		vsn ->		    {ok, Appl#appl.vsn};		modules ->		    {ok, (Appl#appl.appl_data)#appl_data.mods};		maxP ->		    {ok, (Appl#appl.appl_data)#appl_data.maxP};		maxT ->		    {ok, (Appl#appl.appl_data)#appl_data.maxT};		registered ->		    {ok, (Appl#appl.appl_data)#appl_data.regs};		included_applications ->		    {ok, Appl#appl.inc_apps};		applications ->		    {ok, Appl#appl.apps};		env ->		    {ok, get_all_env(AppName)};		mod ->		    {ok, (Appl#appl.appl_data)#appl_data.mod};		start_phases ->		    {ok, (Appl#appl.appl_data)#appl_data.phases};		_ -> undefined	    end;	_ ->	    undefined    end.	    get_pid_all_key(Master) ->    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?