application_controller.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,925 行 · 第 1/5 页
ERL
1,925 行
[[AppName]] -> get_all_key(AppName); _ -> [] end.get_all_key(AppName) -> case ets:lookup(ac_tab, {loaded, AppName}) of [{_, Appl}] -> {ok, [{description, Appl#appl.descr}, {id, Appl#appl.id}, {vsn, Appl#appl.vsn}, {modules, (Appl#appl.appl_data)#appl_data.mods}, {maxP, (Appl#appl.appl_data)#appl_data.maxP}, {maxT, (Appl#appl.appl_data)#appl_data.maxT}, {registered, (Appl#appl.appl_data)#appl_data.regs}, {included_applications, Appl#appl.inc_apps}, {applications, Appl#appl.apps}, {env, get_all_env(AppName)}, {mod, (Appl#appl.appl_data)#appl_data.mod}, {start_phases, (Appl#appl.appl_data)#appl_data.phases} ]}; _ -> undefined end.start_type(Master) -> case ets:match(ac_tab, {{application_master, '$1'}, Master}) of [[AppName]] -> gen_server:call(?AC, {start_type, AppName}, infinity); _X -> undefined end.get_master(AppName) -> case ets:lookup(ac_tab, {application_master, AppName}) of [{_, Pid}] -> Pid; _ -> undefined end.get_application(Master) -> case ets:match(ac_tab, {{application_master, '$1'}, Master}) of [[AppName]] -> {ok, AppName}; _ -> undefined end.get_application_module(Module) -> ApplDataPattern = #appl_data{mods='$2', _='_'}, ApplPattern = #appl{appl_data=ApplDataPattern, _='_'}, AppModules = ets:match(ac_tab, {{loaded, '$1'}, ApplPattern}), get_application_module(Module, AppModules).get_application_module(Module, [[AppName, Modules]|AppModules]) -> case in_modules(Module, Modules) of true -> {ok, AppName}; false -> get_application_module(Module, AppModules) end;get_application_module(_Module, []) -> undefined.%% 'modules' key in .app is a list of Module or {Module,Vsn}in_modules(Module, [Module|_Modules]) -> true;in_modules(Module, [{Module, _Vsn}|_Modules]) -> true;in_modules(Module, [_Module|Modules]) -> in_modules(Module, Modules);in_modules(_Module, []) -> false.permit_application(ApplName, Flag) -> gen_server:call(?AC, {permit_application, ApplName, Flag}, infinity).set_env(AppName, Key, Val) -> gen_server:call(?AC, {set_env, AppName, Key, Val}).set_env(AppName, Key, Val, Timeout) -> gen_server:call(?AC, {set_env, AppName, Key, Val}, Timeout).unset_env(AppName, Key) -> gen_server:call(?AC, {unset_env, AppName, Key}).unset_env(AppName, Key, Timeout) -> gen_server:call(?AC, {unset_env, AppName, Key}, Timeout).%%%-----------------------------------------------------------------%%% call-back functions from gen_server%%%-----------------------------------------------------------------init(Init, Kernel) -> register(?AC, self()), process_flag(trap_exit, true), put('$ancestors', [Init]), % OTP-5811, for gen_server compatibility put('$initial_call', {application_controller, start, [Kernel]}), case catch check_conf() of {ok, ConfData} -> %% Actually, we don't need this info in an ets table anymore. %% This table was introduced because starting applications %% should be able to get som info from AC (e.g. loaded_apps). %% The new implementation makes sure the AC process can be %% called during start-up of any app. case check_conf_data(ConfData) of ok -> ets:new(ac_tab, [set, public, named_table]), S = #state{conf_data = ConfData}, {ok, KAppl} = make_appl(Kernel), case catch load(S, KAppl) of {'EXIT', LoadError} -> Reason = {'load error', LoadError}, Init ! {ack, self(), {error, to_string(Reason)}}; {ok, NewS} -> Init ! {ack, self(), ok}, gen_server:enter_loop(?MODULE, [], NewS, {local, ?AC}) end; {error, ErrorStr} -> Str = lists:flatten(io_lib:format("invalid config data: ~s", [ErrorStr])), Init ! {ack, self(), {error, to_string(Str)}} end; {error, {File, Line, Str}} -> ReasonStr = lists:flatten(io_lib:format("error in config file " "~p (~w): ~s", [File, Line, Str])), Init ! {ack, self(), {error, to_string(ReasonStr)}} end.%% Check the syntax of the .config file [{ApplicationName, [{Parameter, Value}]}].check_conf_data([]) -> ok;check_conf_data(ConfData) when is_list(ConfData) -> [Application | ConfDataRem] = ConfData, case Application of {kernel, List} when is_list(List) -> case check_para_kernel(List) of ok -> check_conf_data(ConfDataRem); Error1 -> Error1 end; {AppName, List} when is_atom(AppName), is_list(List) -> case check_para(List, atom_to_list(AppName)) of ok -> check_conf_data(ConfDataRem); Error2 -> Error2 end; {AppName, List} when is_list(List) -> ErrMsg = "application: " ++ lists:flatten(io_lib:format("~p",[AppName])) ++ "; application name must be an atom", {error, ErrMsg}; {AppName, _List} -> ErrMsg = "application: " ++ lists:flatten(io_lib:format("~p",[AppName])) ++ "; parameters must be a list", {error, ErrMsg}; Else -> ErrMsg = "invalid application name: " ++ lists:flatten(io_lib:format(" ~p",[Else])), {error, ErrMsg} end;check_conf_data(_ConfData) -> {error, 'configuration must be a list ended by <dot><whitespace>'}. %% Special check of distributed parameter for kernelcheck_para_kernel([]) -> ok;check_para_kernel([{distributed, Apps} | ParaList]) when is_list(Apps) -> case check_distributed(Apps) of {error, ErrorMsg} -> {error, ErrorMsg}; _ -> check_para_kernel(ParaList) end;check_para_kernel([{distributed, _Apps} | _ParaList]) -> {error, "application: kernel; erroneous parameter: distributed"};check_para_kernel([{Para, _Val} | ParaList]) when is_atom(Para) -> check_para_kernel(ParaList);check_para_kernel([{Para, _Val} | _ParaList]) -> {error, "application: kernel; invalid parameter: " ++ lists:flatten(io_lib:format("~p",[Para]))};check_para_kernel(Else) -> {error, "application: kernel; invalid parameter list: " ++ lists:flatten(io_lib:format("~p",[Else]))}. check_distributed([]) -> ok;check_distributed([{App, List} | Apps]) when is_atom(App), is_list(List) -> check_distributed(Apps);check_distributed([{App, infinity, List} | Apps]) when is_atom(App), is_list(List) -> check_distributed(Apps);check_distributed([{App, Time, List} | Apps]) when is_atom(App), is_integer(Time), is_list(List) -> check_distributed(Apps);check_distributed(_Else) -> {error, "application: kernel; erroneous parameter: distributed"}.check_para([], _AppName) -> ok;check_para([{Para, _Val} | ParaList], AppName) when is_atom(Para) -> check_para(ParaList, AppName);check_para([{Para, _Val} | _ParaList], AppName) -> {error, "application: " ++ AppName ++ "; invalid parameter: " ++ lists:flatten(io_lib:format("~p",[Para]))};check_para([Else | _ParaList], AppName) -> {error, "application: " ++ AppName ++ "; invalid parameter: " ++ lists:flatten(io_lib:format("~p",[Else]))}.handle_call({load_application, Application}, From, S) -> case catch do_load_application(Application, S) of {ok, NewS} -> AppName = get_appl_name(Application), case cntrl(AppName, S, {ac_load_application_req, AppName}) of true -> {noreply, S#state{loading = [{AppName, From} | S#state.loading]}}; false -> {reply, ok, NewS} end; {error, Error} -> {reply, {error, Error}, S}; {'EXIT',R} -> {reply, {error, R}, S} end;handle_call({unload_application, AppName}, _From, S) -> case keysearch(AppName, 1, S#state.running) of {value, _} -> {reply, {error, {running, AppName}}, S}; false -> case get_loaded(AppName) of {true, _} -> NewS = unload(AppName, S), cntrl(AppName, S, {ac_application_unloaded, AppName}), {reply, ok, NewS}; false -> {reply, {error, {not_loaded, AppName}}, S} end end;handle_call({start_application, AppName, RestartType}, From, S) -> #state{running = Running, starting = Starting, start_p_false = SPF, started = Started, start_req = Start_req} = S, %% Check if the commandline environment variables are OK. %% Incase of erroneous variables do not start the application, %% if the application is permanent crash the node. %% Check if the application is already starting. case lists:keysearch(AppName, 1, Start_req) of false -> case catch check_start_cond(AppName, RestartType, Started, Running) of {ok, Appl} -> Cntrl = cntrl(AppName, S, {ac_start_application_req, AppName}), Perm = application:get_env(kernel, permissions), case {Cntrl, Perm} of {true, _} -> {noreply, S#state{starting = [{AppName, RestartType, normal, From} | Starting], start_req = [{AppName, From} | Start_req]}}; {false, undefined} -> spawn_starter(From, Appl, S, normal), {noreply, S#state{starting = [{AppName, RestartType, normal, From} | Starting], start_req = [{AppName, From} | Start_req]}}; {false, {ok, Perms}} -> case lists:member({AppName, false}, Perms) of false -> spawn_starter(From, Appl, S, normal), {noreply, S#state{starting = [{AppName, RestartType, normal, From} | Starting], start_req = [{AppName, From} | Start_req]}}; true -> SS = S#state{start_p_false = [{AppName, RestartType, normal, From} | SPF]}, {reply, ok, SS} end end; {error, R} -> {reply, {error, R}, S} end; {value, {AppName, _FromX}} -> SS = S#state{start_req = [{AppName, From} | Start_req]}, {noreply, SS} end;handle_call({permit_application, AppName, Bool}, From, S) -> Control = S#state.control, Starting = S#state.starting, SPF = S#state.start_p_false, Started = S#state.started, Running = S#state.running, Start_req = S#state.start_req, IsLoaded = get_loaded(AppName), IsStarting = lists:keysearch(AppName, 1, Starting), IsSPF = lists:keysearch(AppName, 1, SPF), IsStarted = lists:keysearch(AppName, 1, Started), IsRunning = lists:keysearch(AppName, 1, Running), case keysearch(AppName, 1, Control) of %%======================== %% distributed application %%======================== {value, _} -> case {IsLoaded, IsStarting, IsStarted} of %% not loaded {false, _, _} -> {reply, {error, {not_loaded, AppName}}, S}; %% only loaded {{true, _Appl}, false, false} -> update_permissions(AppName, Bool), {reply, {distributed_application, only_loaded}, S}; _ -> update_permissions(AppName, Bool), {reply, distributed_application, S} end; %%======================== %% local application %%======================== false -> case {Bool, IsLoaded, IsStarting, IsSPF, IsStarted, IsRunning} of %%------------------------ %% permit the applicaition %%------------------------ %% already running {true, _, _, _, _, {value, _Tuple}} -> {reply, ok, S}; %% not loaded {true, false, _, _, _, _} -> {reply, {error, {not_loaded, AppName}}, S}; %% only loaded {true, {true, _Appl}, false, false, false, false} -> update_permissions(AppName, Bool), {reply, ok, S}; %% starting {true, {true, _Appl}, {value, _Tuple}, false, false, false} -> update_permissions(AppName, Bool), {reply, ok, S}; %% check the permission after then app is started %% start requested but not started because permit was false {true, {true, Appl}, false, {value, Tuple}, false, false} -> update_permissions(AppName, Bool), {_AppName2, RestartType, normal, _From} = Tuple, spawn_starter(From, Appl, S, normal), SS = S#state{starting = [{AppName, RestartType, normal, From} | Starting], start_p_false = keydelete(AppName, 1, SPF), start_req = [{AppName, From} | Start_req]}, {noreply, SS}; %% started but not running {true, {true, Appl}, _, _, {value, {AppName, RestartType}}, false} -> update_permissions(AppName, Bool), spawn_starter(From, Appl, S, normal), SS = S#state{starting = [{AppName, RestartType, normal, From} | Starting], started = keydelete(AppName, 1, Started), start_req = [{AppName, From} | Start_req]}, {noreply, SS}; %%========================== %% unpermit the applicaition %%========================== %% running {false, _, _, _, _, {value, {_AppName, Id}}} -> {value, {_AppName2, Type}} = keysearch(AppName, 1, Started), stop_appl(AppName, Id, Type), NRunning = keydelete(AppName, 1, Running), {reply, ok, S#state{running = NRunning}}; %% not loaded {false, false, _, _, _, _} -> {reply, {error, {not_loaded, AppName}}, S}; %% only loaded {false, {true, _Appl}, false, false, false, false} -> update_permissions(AppName, Bool), {reply, ok, S};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?