1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2020. 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(application_controller).
21
22%% External exports
23-export([start/1,
24	 load_application/1, unload_application/1,
25	 start_application/2, start_boot_application/2, stop_application/1,
26	 control_application/1,
27	 change_application_data/2, prep_config_change/0, config_change/1,
28	 which_applications/0, which_applications/1,
29	 loaded_applications/0, info/0, set_env/2,
30	 get_pid_env/2, get_env/2, get_pid_all_env/1, get_all_env/1,
31	 get_pid_key/2, get_key/2, get_pid_all_key/1, get_all_key/1,
32	 get_master/1, get_application/1, get_application_module/1,
33	 start_type/1, permit_application/2, do_config_diff/2,
34	 set_env/3, set_env/4, unset_env/2, unset_env/3]).
35
36%% Internal exports
37-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2,
38	 code_change/3, init_starter/4, get_loaded/1]).
39
40%% Test exports, only to be used from the test suites
41-export([test_change_apps/2]).
42
43-import(lists, [zf/2, map/2, foreach/2, foldl/3,
44		keyfind/3, keydelete/3, keyreplace/4]).
45
46-include("application_master.hrl").
47-include("logger.hrl").
48
49-define(AC, ?MODULE). % Name of process
50
51%%%-----------------------------------------------------------------
52%%% The application_controller controls local applications only.  A
53%%% local application can be loaded/started/stopped/unloaded and
54%%% changed.  The control of distributed applications is taken care of
55%%% by another process (default is dist_ac).
56%%%
57%%% When an application has been started (by a call to application:start)
58%%% it can be running or not running (on this node).  For example,
59%%% a distributed application must be started on all nodes, but
60%%% may be running on one node at the time.
61%%%
62%%% The external API to this module is in the module 'application'.
63%%%
64%%% The process that controls distributed applications (called dist
65%%% ac).  calls application_controller:control_application(Name) to
66%%% take responsibility for an application.  The interface between AC
67%%% and the dist_ac process is message-based:
68%%%
69%%% AC                                        DIST AC
70%%% ==                                        =======
71%%%     --> {ac_load_application_req, Name}
72%%%     <-- {ac_load_application_reply, Name, LoadReply}
73%%%     --> {ac_start_application_req, Name}       (*)
74%%%     <-- {ac_start_application_reply, Name, StartReply}
75%%%     --> {ac_application_run, Name, Res}
76%%%     --> {ac_application_not_run, Name, Res}
77%%%     --> {ac_application_stopped, Name}
78%%%     --> {ac_application_unloaded, Name}
79%%%     <-- {ac_change_application_req, Name, Req} (**)
80%%%
81%%% Where LoadReply =
82%%%   ok              - App is loaded
83%%%   {error, R}      - An error occurred
84%%% And StartReply =
85%%%   start_it        - DIST AC decided that AC should start the app
86%%%   {started, Node} - The app is started distributed at Node
87%%%   not_started     - The app should not be running at this time
88%%%   {takeover, Node}- The app should takeover from Node
89%%%   {error, R}      - an error occurred
90%%% And Req =
91%%%   start_it        - DIST AC wants AC to start the app locally
92%%%   stop_it         - AC should stop the app.
93%%%   {takeover, Node, RestartType}
94%%%                   - AC should start the app as a takeover
95%%%   {failover, Node, RestartType}
96%%%                   - AC should start the app as a failover
97%%%   {started, Node} - The app is started at Node
98%%%                     NOTE: The app must have been started at this node
99%%%                     before this request is sent!
100%%% And Res =
101%%%   ok              - Application is started locally
102%%%   {error, R}      - Start of application failed
103%%%
104%%% (*)
105%%%  The call to application:start() doesn't return until the
106%%%  ac_start_application_reply has been received by AC.  AC
107%%%  itself is not blocked however.
108%%% (**)
109%%%  DIST AC gets ACK to its ac_change_application_req, but not as a
110%%%  separate messgage.  Instead the normal messages are used as:
111%%%   start_it   generates an ac_application_run
112%%%   stop_it    generates an ac_application_not_run
113%%%   takeover   generates an ac_application_run
114%%%   started    doesn't generate anything
115%%%
116%%% There is a distinction between application:stop and stop_it
117%%% from a dist ac process.  The first one stops the application,
118%%% and resets the internal structures as they were before start was
119%%% called.  stop_it stops the application, but just marks it as
120%%% not being running.
121%%%
122%%% When a dist ac process has taken control of an application, no
123%%% other process can take the control.
124%%%-----------------------------------------------------------------
125
126%%-----------------------------------------------------------------
127%% Naming conventions:
128%%   App = appl_descr()
129%%   Appl = #appl
130%%   AppName = atom()
131%%   Application = App | AppName
132%%-----------------------------------------------------------------
133
134-type appname() :: atom().
135
136-record(state, {loading = [], starting = [], start_p_false = [], running = [],
137		control = [], started = [], start_req = [], conf_data}).
138-type state() :: #state{}.
139
140%%-----------------------------------------------------------------
141%% loading     = [{AppName, From}] - Load not yet finished
142%% starting    = [{AppName, RestartType, Type, From}] - Start not
143%%                 yet finished
144%% start_p_false = [{AppName, RestartType, Type, From}] - Start not
145%%                 executed because permit == false
146%% running     = [{AppName, Pid}] - running locally (Pid == application_master)
147%%               [{AppName, {distributed, Node}}] - running on Node
148%% control     = [{AppName, Controller}]
149%% started     = [{AppName, RestartType}] - Names of all apps that
150%%                 have been started (but may not run because
151%%                 permission = false)
152%% conf_data   = [{AppName, Env}]
153%% start_req   = [{AppName, From}] - list of all start requests
154%% Id          = AMPid | undefined | {distributed, Node}
155%% Env         = [{Key, Value}]
156%%-----------------------------------------------------------------
157
158-record(appl, {name, appl_data, descr, id, vsn, restart_type, inc_apps, apps}).
159
160%%-----------------------------------------------------------------
161%% Func: start/1
162%% Args: KernelApp = appl_descr()
163%%       appl_descr() = [{application, Name, [appl_opt()]}]
164%%       appl_opt() = {description, string()}           |
165%%                    {vsn, string()}                   |
166%%                    {id, string()},                   |
167%%                    {modules, [Module]}  |
168%%                    {registered, [atom()]}            |
169%%                    {applications, [atom()]}          |
170%%                    {included_applications, [atom()]} |
171%%                    {env, [{atom(), term()}]}         |
172%%                    {start_phases, [{atom(), term()}]}|
173%%                    {maxT, integer()|infinity}        |
174%%                    {maxP, integer()|infinity}        |
175%%                    {mod, {Module, term()}}
176%%         Module = atom()
177%% Purpose: Starts the application_controller.  This process starts all
178%%          application masters for the applications.
179%%          The kernel application is the only application that is
180%%          treated specially.  The reason for this is that the kernel
181%%          starts user.  This process is special because it should
182%%          be group_leader for this process.
183%% Pre: All modules are loaded, or will be loaded on demand.
184%% Returns: {ok, Pid} | ReasonStr
185%%-----------------------------------------------------------------
186start(KernelApp) ->
187    %% OTP-5811 Don't start as a gen_server to prevent crash report
188    %% when (if) the process terminates
189    Init = self(),
190    AC = spawn_link(fun() -> init(Init, KernelApp) end),
191    receive
192	{ack, AC, ok} ->
193	    {ok, AC};
194	{ack, AC, {error, Reason}} ->
195	    to_string(Reason); % init doesn't want error tuple, only a reason
196	{'EXIT', _Pid, Reason} ->
197	    to_string(Reason)
198    end.
199
200%%-----------------------------------------------------------------
201%% Func: load_application/1
202%% Args: Application = appl_descr() | atom()
203%% Purpose: Loads an application.  Currently just inserts the
204%%          application's env.
205%% Returns: ok | {error, Reason}
206%%-----------------------------------------------------------------
207load_application(Application) ->
208    gen_server:call(?AC, {load_application, Application}, infinity).
209
210unload_application(AppName) ->
211    gen_server:call(?AC, {unload_application, AppName}, infinity).
212
213%%-----------------------------------------------------------------
214%% Func: start_application/2
215%% Args: Application = atom()
216%%       RestartType = permanent | transient | temporary
217%% Purpose: Starts a new application.
218%%          The RestartType specifies what should happen if the
219%%          application dies:
220%%          If it is permanent, all other applications are terminated,
221%%            and the application_controller dies.
222%%          If it is transient, and the application dies normally,
223%%            this is reported and no other applications are terminated.
224%%            If the application dies abnormally, all other applications
225%%            are terminated, and the application_controller dies.
226%%          If it is temporary and the application dies this is reported
227%%            and no other applications are terminated.  In this way,
228%%            an application can run in test mode, without disturbing
229%%            the other applications.
230%%          The caller of this function is suspended until the application
231%%          is started, either locally or distributed.
232%% Returns: ok | {error, Reason}
233%%-----------------------------------------------------------------
234start_application(AppName, RestartType) ->
235    gen_server:call(?AC, {start_application, AppName, RestartType}, infinity).
236
237%%-----------------------------------------------------------------
238%% Func: start_boot_application/2
239%% The same as start_application/2 expect that this function is
240%% called from the boot script file. It mustnot be used by the operator.
241%% This function will cause a node crash if a permanent application
242%% fails to boot start
243%%-----------------------------------------------------------------
244start_boot_application(Application, RestartType) ->
245    case {application:load(Application), RestartType} of
246	{ok, _} ->
247	    AppName = get_appl_name(Application),
248	    gen_server:call(?AC, {start_application, AppName, RestartType}, infinity);
249	{{error, {already_loaded, AppName}}, _} ->
250	    gen_server:call(?AC, {start_application, AppName, RestartType}, infinity);
251	{{error,{bad_environment_value,Env}}, permanent} ->
252	    Txt = io_lib:format("Bad environment variable: ~tp  Application: ~p",
253				[Env, Application]),
254	    exit({error, list_to_atom(lists:flatten(Txt))});
255	{Error, _} ->
256	    Error
257    end.
258
259stop_application(AppName) ->
260    gen_server:call(?AC, {stop_application, AppName}, infinity).
261
262%%-----------------------------------------------------------------
263%% Returns: [{Name, Descr, Vsn}]
264%%-----------------------------------------------------------------
265which_applications() ->
266    gen_server:call(?AC, which_applications).
267which_applications(Timeout) ->
268    gen_server:call(?AC, which_applications, Timeout).
269
270loaded_applications() ->
271    ets:select(ac_tab,
272               [{
273                  {{loaded, '$1'}, #appl{descr = '$2', vsn = '$3', _ = '_'}},
274                  [],
275                  [{{'$1', '$2', '$3'}}]
276                }]).
277
278%% Returns some debug info
279info() ->
280    gen_server:call(?AC, info).
281
282control_application(AppName) ->
283    gen_server:call(?AC, {control_application, AppName}, infinity).
284
285%%-----------------------------------------------------------------
286%% Func: change_application_data/2
287%% Args: Applications = [appl_descr()]
288%%       Config = [{AppName, [{Par,Val}]}]
289%% Purpose: Change all applications and their parameters on this node.
290%%          This function should be used from a release handler, at
291%%          the same time as the .app or start.boot file is
292%%          introduced.  Note that during some time the ACs may have
293%%          different view of e.g. the distributed applications.
294%%          This is solved by syncing the release installation.
295%%          However, strange things may happen if a node crashes
296%%          and two other nodes have different opinons about who's
297%%          gonna start the applications.  The release handler must
298%%          shutdown each involved node in this case.
299%%          Note that this function is used to change existing apps,
300%%          adding new/deleting old isn't handled by this function.
301%%          Changes an application's vsn, descr and env.
302%% Returns: ok | {error, Reason}
303%%          If an error occurred, the situation may be inconsistent,
304%%          so the release handler must restart the node.  E.g. if
305%%          some applicatation may have got new config data.
306%%-----------------------------------------------------------------
307change_application_data(Applications, Config) ->
308    gen_server:call(?AC,
309		    {change_application_data, Applications, Config},
310		    infinity).
311
312prep_config_change() ->
313    gen_server:call(?AC,
314		    prep_config_change,
315		    infinity).
316
317
318config_change(EnvPrev) ->
319    gen_server:call(?AC,
320		    {config_change, EnvPrev},
321		    infinity).
322
323
324
325get_pid_env(Master, Key) ->
326    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
327	[[AppName]] -> get_env(AppName, Key);
328	_ -> undefined
329    end.
330
331get_env(AppName, Key) ->
332    case ets:lookup(ac_tab, {env, AppName, Key}) of
333	[{_, Val}] -> {ok, Val};
334	_ -> undefined
335    end.
336
337get_pid_all_env(Master) ->
338    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
339	[[AppName]] -> get_all_env(AppName);
340	_ -> []
341    end.
342
343get_all_env(AppName) ->
344    map(fun([Key, Val]) -> {Key, Val} end,
345	ets:match(ac_tab, {{env, AppName, '$1'}, '$2'})).
346
347get_pid_key(Master, Key) ->
348    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
349	[[AppName]] -> get_key(AppName, Key);
350	_ -> undefined
351    end.
352
353get_key(AppName, Key) ->
354    case ets:lookup(ac_tab, {loaded, AppName}) of
355	[{_, Appl}] ->
356	    case Key of
357		description ->
358		    {ok, Appl#appl.descr};
359		id ->
360		    {ok, Appl#appl.id};
361		vsn ->
362		    {ok, Appl#appl.vsn};
363		modules ->
364		    {ok, (Appl#appl.appl_data)#appl_data.mods};
365		maxP ->
366		    {ok, (Appl#appl.appl_data)#appl_data.maxP};
367		maxT ->
368		    {ok, (Appl#appl.appl_data)#appl_data.maxT};
369		registered ->
370		    {ok, (Appl#appl.appl_data)#appl_data.regs};
371		included_applications ->
372		    {ok, Appl#appl.inc_apps};
373		applications ->
374		    {ok, Appl#appl.apps};
375		env ->
376		    {ok, get_all_env(AppName)};
377		mod ->
378		    {ok, (Appl#appl.appl_data)#appl_data.mod};
379		start_phases ->
380		    {ok, (Appl#appl.appl_data)#appl_data.phases};
381		_ -> undefined
382	    end;
383	_ ->
384	    undefined
385    end.
386
387get_pid_all_key(Master) ->
388    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
389	[[AppName]] -> get_all_key(AppName);
390	_ -> []
391    end.
392
393get_all_key(AppName) ->
394    case ets:lookup(ac_tab, {loaded, AppName}) of
395	[{_, Appl}] ->
396	    {ok, [{description, Appl#appl.descr},
397		  {id, Appl#appl.id},
398		  {vsn, Appl#appl.vsn},
399		  {modules, (Appl#appl.appl_data)#appl_data.mods},
400		  {maxP, (Appl#appl.appl_data)#appl_data.maxP},
401		  {maxT, (Appl#appl.appl_data)#appl_data.maxT},
402		  {registered, (Appl#appl.appl_data)#appl_data.regs},
403		  {included_applications, Appl#appl.inc_apps},
404		  {applications, Appl#appl.apps},
405		  {env, get_all_env(AppName)},
406		  {mod, (Appl#appl.appl_data)#appl_data.mod},
407		  {start_phases, (Appl#appl.appl_data)#appl_data.phases}
408		 ]};
409	_ ->
410	    undefined
411    end.
412
413
414start_type(Master) ->
415    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
416	[[AppName]] ->
417	    gen_server:call(?AC, {start_type, AppName}, infinity);
418	_X ->
419	    undefined
420    end.
421
422
423
424
425
426
427get_master(AppName) ->
428    case ets:lookup(ac_tab, {application_master, AppName}) of
429	[{_, Pid}] -> Pid;
430	_ -> undefined
431    end.
432
433get_application(Master) ->
434    case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
435	[[AppName]] -> {ok, AppName};
436	_ -> undefined
437    end.
438
439get_application_module(Module) ->
440    ApplDataPattern = #appl_data{mods='$2', _='_'},
441    ApplPattern = #appl{appl_data=ApplDataPattern, _='_'},
442    AppModules = ets:match(ac_tab, {{loaded, '$1'}, ApplPattern}),
443    get_application_module(Module, AppModules).
444
445get_application_module(Module, [[AppName, Modules]|AppModules]) ->
446    case lists:member(Module, Modules) of
447	true ->
448	    {ok, AppName};
449	false ->
450	    get_application_module(Module, AppModules)
451    end;
452get_application_module(_Module, []) ->
453    undefined.
454
455permit_application(ApplName, Flag) ->
456    gen_server:call(?AC,
457		    {permit_application, ApplName, Flag},
458		    infinity).
459
460set_env(Config, Opts) ->
461    case check_conf_data(Config) of
462	ok ->
463	    Timeout = proplists:get_value(timeout, Opts, 5000),
464	    gen_server:call(?AC, {set_env, Config, Opts}, Timeout);
465
466	{error, _} = Error ->
467	    Error
468    end.
469
470set_env(AppName, Key, Val) ->
471    gen_server:call(?AC, {set_env, AppName, Key, Val, []}).
472set_env(AppName, Key, Val, Opts) ->
473    Timeout = proplists:get_value(timeout, Opts, 5000),
474    gen_server:call(?AC, {set_env, AppName, Key, Val, Opts}, Timeout).
475
476unset_env(AppName, Key) ->
477    gen_server:call(?AC, {unset_env, AppName, Key, []}).
478unset_env(AppName, Key, Opts) ->
479    Timeout = proplists:get_value(timeout, Opts, 5000),
480    gen_server:call(?AC, {unset_env, AppName, Key, Opts}, Timeout).
481
482%%%-----------------------------------------------------------------
483%%% call-back functions from gen_server
484%%%-----------------------------------------------------------------
485init(Init, Kernel) ->
486    register(?AC, self()),
487    process_flag(trap_exit, true),
488    put('$ancestors', [Init]), % OTP-5811, for gen_server compatibility
489    put('$initial_call', {application_controller, start, 1}),
490
491    case catch check_conf() of
492	{ok, ConfData} ->
493	    %% Actually, we don't need this info in an ets table anymore.
494	    %% This table was introduced because starting applications
495	    %% should be able to get som info from AC (e.g. loaded_apps).
496	    %% The new implementation makes sure the AC process can be
497	    %% called during start-up of any app.
498	    case check_conf_data(ConfData) of
499		ok ->
500		    _ = ets:new(ac_tab, [set, public, named_table,
501                                         {read_concurrency,true}]),
502		    S = #state{conf_data = ConfData},
503		    {ok, KAppl} = make_appl(Kernel),
504		    case catch load(S, KAppl) of
505			{'EXIT', LoadError} ->
506			    Reason = {'load error', LoadError},
507			    Init ! {ack, self(), {error, to_string(Reason)}};
508                        {error, Error} ->
509                            Init ! {ack, self(), {error, to_string(Error)}};
510			{ok, NewS} ->
511			    Init ! {ack, self(), ok},
512			    gen_server:enter_loop(?MODULE, [], NewS,
513						  {local, ?AC})
514		    end;
515		{error, ErrorStr} ->
516		    Str = lists:flatten(io_lib:format("invalid config data: ~ts", [ErrorStr])),
517		    Init ! {ack, self(), {error, to_string(Str)}}
518	    end;
519	{error, {File, Line, Str}} ->
520	    ReasonStr =
521		lists:flatten(io_lib:format("error in config file "
522					    "~tp (~w): ~ts",
523					    [File, Line, Str])),
524	    Init ! {ack, self(), {error, to_string(ReasonStr)}}
525    end.
526
527
528%% Check the syntax of the .config file
529%%  [{ApplicationName, [{Parameter, Value}]}].
530
531check_conf_data([]) ->
532    ok;
533check_conf_data(ConfData) when is_list(ConfData) ->
534    [Application | ConfDataRem] = ConfData,
535    case Application of
536	{AppName, List} when is_atom(AppName), is_list(List) ->
537	    case lists:keymember(AppName, 1, ConfDataRem) of
538		true ->
539		    {error, "duplicate application config: " ++ atom_to_list(AppName)};
540		false ->
541		    case check_para(List, AppName) of
542			ok -> check_conf_data(ConfDataRem);
543			Error -> Error
544		    end
545	    end;
546	{AppName, List} when is_list(List)  ->
547	    ErrMsg = "application: "
548		++ lists:flatten(io_lib:format("~tp",[AppName]))
549		++ "; application name must be an atom",
550	    {error, ErrMsg};
551	{AppName, _List} ->
552	    ErrMsg = "application: "
553		++ lists:flatten(io_lib:format("~tp",[AppName]))
554		++ "; parameters must be a list",
555	    {error, ErrMsg};
556	Else ->
557	    ErrMsg = "invalid application config: "
558		++ lists:flatten(io_lib:format("~tp",[Else])),
559	    {error, ErrMsg}
560    end;
561check_conf_data(_ConfData) ->
562    {error, "configuration must be a list ended by <dot><whitespace>"}.
563
564
565check_para([], _AppName) ->
566    ok;
567check_para([{Para, Val} | ParaList], AppName) when is_atom(Para) ->
568    case lists:keymember(Para, 1, ParaList) of
569	true ->
570	    ErrMsg =  "application: " ++ atom_to_list(AppName)
571		++ "; duplicate parameter: " ++ atom_to_list(Para),
572	    {error, ErrMsg};
573	false ->
574	    case check_para_value(Para, Val, AppName) of
575		ok -> check_para(ParaList, AppName);
576		{error, _} = Error -> Error
577	    end
578    end;
579check_para([{Para, _Val} | _ParaList], AppName) ->
580    {error, "application: " ++ atom_to_list(AppName) ++ "; invalid parameter name: " ++
581     lists:flatten(io_lib:format("~tp",[Para]))};
582check_para([Else | _ParaList], AppName) ->
583    {error, "application: " ++ atom_to_list(AppName) ++ "; invalid parameter: " ++
584     lists:flatten(io_lib:format("~tp",[Else]))}.
585
586check_para_value(distributed, Apps, kernel) -> check_distributed(Apps);
587check_para_value(_Para, _Val, _AppName) -> ok.
588
589%% Special check of distributed parameter for kernel
590check_distributed([]) ->
591    ok;
592check_distributed([{App, List} | Apps]) when is_atom(App), is_list(List) ->
593    check_distributed(Apps);
594check_distributed([{App, infinity, List} | Apps]) when is_atom(App), is_list(List) ->
595    check_distributed(Apps);
596check_distributed([{App, Time, List} | Apps]) when is_atom(App), is_integer(Time), is_list(List) ->
597    check_distributed(Apps);
598check_distributed(_Else) ->
599    {error, "application: kernel; erroneous parameter: distributed"}.
600
601
602-type calls() :: 'info' | 'prep_config_change' | 'which_applications'
603               | {'config_change' | 'control_application' |
604		  'load_application' | 'start_type' | 'stop_application' |
605		  'unload_application', term()}
606               | {'change_application_data', _, _}
607               | {'permit_application', atom() | {'application',atom(),_},_}
608               | {'start_application', _, _}
609               | {'unset_env', _, _, _}
610               | {'set_env', _, _, _, _}.
611
612-spec handle_call(calls(), {pid(), term()}, state()) ->
613        {'noreply', state()} | {'reply', term(), state()}.
614
615handle_call({load_application, Application}, From, S) ->
616    case catch do_load_application(Application, S) of
617	{ok, NewS} ->
618	    AppName = get_appl_name(Application),
619	    case cntrl(AppName, S, {ac_load_application_req, AppName}) of
620		true ->
621		    {noreply, S#state{loading = [{AppName, From} |
622						 S#state.loading]}};
623		false ->
624		    {reply, ok, NewS}
625	    end;
626	{error, _} = Error ->
627	    {reply, Error, S};
628	{'EXIT', R} ->
629	    {reply, {error, R}, S}
630    end;
631
632handle_call({unload_application, AppName}, _From, S) ->
633    case lists:keymember(AppName, 1, S#state.running) of
634	true -> {reply, {error, {running, AppName}}, S};
635	false ->
636	    case get_loaded(AppName) of
637		{true, _} ->
638		    NewS = unload(AppName, S),
639		    cntrl(AppName, S, {ac_application_unloaded, AppName}),
640		    {reply, ok, NewS};
641		false ->
642		    {reply, {error, {not_loaded, AppName}}, S}
643	    end
644    end;
645
646handle_call({start_application, AppName, RestartType}, From, S) ->
647    #state{running = Running, starting = Starting, start_p_false = SPF,
648	   started = Started, start_req = Start_req} = S,
649    %% Check if the commandline environment variables are OK.
650    %% Incase of erroneous variables do not start the application,
651    %% if the application is permanent crash the node.
652    %% Check if the application is already starting.
653    case lists:keyfind(AppName, 1, Start_req) of
654	false ->
655	    case catch check_start_cond(AppName, RestartType, Started, Running) of
656		{ok, Appl} ->
657		    Cntrl = cntrl(AppName, S, {ac_start_application_req, AppName}),
658		    Perm = application:get_env(kernel, permissions),
659		    case {Cntrl, Perm} of
660			{true, _} ->
661			    {noreply, S#state{starting = [{AppName, RestartType, normal, From} |
662							  Starting],
663					      start_req = [{AppName, From} | Start_req]}};
664			{false, undefined} ->
665			    spawn_starter(From, Appl, S, normal),
666			    {noreply, S#state{starting = [{AppName, RestartType, normal, From} |
667							  Starting],
668					      start_req = [{AppName, From} | Start_req]}};
669			{false, {ok, Perms}} ->
670			    case lists:member({AppName, false}, Perms) of
671				false ->
672				    spawn_starter(From, Appl, S, normal),
673				    {noreply, S#state{starting = [{AppName, RestartType, normal, From} |
674								  Starting],
675						      start_req = [{AppName, From} | Start_req]}};
676				true ->
677				    SS = S#state{start_p_false = [{AppName, RestartType, normal, From} |
678								  SPF]},
679				    {reply, ok, SS}
680			    end
681		    end;
682		{error, _R} = Error ->
683		    {reply, Error, S}
684	    end;
685	{AppName, _FromX} ->
686	    SS = S#state{start_req = [{AppName, From} | Start_req]},
687	    {noreply, SS}
688    end;
689
690handle_call({permit_application, AppName, Bool}, From, S) ->
691    Control = S#state.control,
692    Starting = S#state.starting,
693    SPF = S#state.start_p_false,
694    Started = S#state.started,
695    Running = S#state.running,
696    Start_req = S#state.start_req,
697    IsLoaded = get_loaded(AppName),
698    IsStarting = lists:keysearch(AppName, 1, Starting),
699    IsSPF = lists:keysearch(AppName, 1, SPF),
700    IsStarted = lists:keysearch(AppName, 1, Started),
701    IsRunning = lists:keysearch(AppName, 1, Running),
702
703    case lists:keymember(AppName, 1, Control) of
704	%%========================
705	%% distributed application
706	%%========================
707	true ->
708	    case {IsLoaded, IsStarting, IsStarted} of
709		%% not loaded
710		{false, _, _} ->
711		    {reply, {error, {not_loaded, AppName}}, S};
712		%% only loaded
713		{{true, _Appl}, false, false} ->
714		    update_permissions(AppName, Bool),
715		    {reply, {distributed_application, only_loaded}, S};
716		_ ->
717		    update_permissions(AppName, Bool),
718		    {reply, distributed_application, S}
719	    end;
720	%%========================
721	%% local application
722	%%========================
723	false ->
724	    case {Bool, IsLoaded, IsStarting, IsSPF, IsStarted, IsRunning} of
725		%%------------------------
726		%% permit the applicaition
727		%%------------------------
728		%% already running
729		{true, _, _, _, _, {value, _Tuple}} ->
730		    {reply, ok, S};
731		%% not loaded
732		{true, false, _, _, _, _} ->
733		    {reply, {error, {not_loaded, AppName}}, S};
734		%% only loaded
735		{true, {true, _Appl}, false, false, false, false} ->
736		    update_permissions(AppName, Bool),
737                    {reply, ok, S};
738		%% starting
739		{true, {true, _Appl}, {value, _Tuple}, false, false, false} ->
740		    update_permissions(AppName, Bool),
741                    {reply, ok, S}; %% check the permission after then app is started
742		%% start requested but not started because permit was false
743		{true, {true, Appl}, false, {value, Tuple}, false, false} ->
744		    update_permissions(AppName, Bool),
745		    {_AppName2, RestartType, normal, _From} = Tuple,
746		    spawn_starter(From, Appl, S, normal),
747		    SS = S#state{starting = [{AppName, RestartType, normal, From} | Starting],
748				 start_p_false = keydelete(AppName, 1, SPF),
749				 start_req = [{AppName, From} | Start_req]},
750		    {noreply, SS};
751		%% started but not running
752		{true, {true, Appl}, _, _, {value, {AppName, RestartType}}, false} ->
753		    update_permissions(AppName, Bool),
754		    spawn_starter(From, Appl, S, normal),
755		    SS = S#state{starting = [{AppName, RestartType, normal, From} | Starting],
756				 started = keydelete(AppName, 1, Started),
757				 start_req = [{AppName, From} | Start_req]},
758		    {noreply, SS};
759
760		%%==========================
761		%% unpermit the application
762		%%==========================
763		%% running
764		{false, _, _, _,  _, {value, {_AppName, Id}}} ->
765		    {_AppName2, Type} = lists:keyfind(AppName, 1, Started),
766		    stop_appl(AppName, Id, Type),
767		    NRunning = keydelete(AppName, 1, Running),
768		    {reply, ok, S#state{running = NRunning}};
769		%% not loaded
770		{false, false, _, _, _,  _} ->
771		    {reply, {error, {not_loaded, AppName}}, S};
772		%% only loaded
773		{false, {true, _Appl}, false, false, false, false} ->
774		    update_permissions(AppName, Bool),
775                    {reply, ok, S};
776		%% starting
777		{false, {true, _Appl}, {value, _Tuple}, false, false, false} ->
778		    update_permissions(AppName, Bool),
779		    {reply, ok, S};
780		%% start requested but not started because permit was false
781		{false, {true, _Appl}, false, {value, _Tuple}, false, false} ->
782		    update_permissions(AppName, Bool),
783		    SS = S#state{start_p_false = keydelete(AppName, 1, SPF)},
784		    {reply, ok, SS};
785		%% started but not running
786		{false, {true, _Appl}, _,  _, {value, _Tuple}, false} ->
787		    update_permissions(AppName, Bool),
788		    {reply, ok, S}
789
790	    end
791    end;
792
793handle_call({stop_application, AppName}, _From, S) ->
794    #state{running = Running, started = Started} = S,
795    case lists:keyfind(AppName, 1, Running) of
796	{_AppName, Id} ->
797	    {_AppName2, Type} = lists:keyfind(AppName, 1, Started),
798	    stop_appl(AppName, Id, Type),
799	    NRunning = keydelete(AppName, 1, Running),
800	    NStarted = keydelete(AppName, 1, Started),
801	    cntrl(AppName, S, {ac_application_stopped, AppName}),
802	    {reply, ok, S#state{running = NRunning, started = NStarted}};
803	false ->
804	    case lists:keymember(AppName, 1, Started) of
805		true ->
806		    NStarted = keydelete(AppName, 1, Started),
807		    cntrl(AppName, S, {ac_application_stopped, AppName}),
808		    {reply, ok, S#state{started = NStarted}};
809		false ->
810		    {reply, {error, {not_started, AppName}}, S}
811	    end
812    end;
813
814handle_call({change_application_data, Applications, Config}, _From, S) ->
815    OldAppls = ets:filter(ac_tab,
816			  fun([{{loaded, _AppName}, Appl}]) ->
817				  {true, Appl};
818			     (_) ->
819				  false
820			  end,
821			  []),
822    case catch do_change_apps(Applications, Config, OldAppls) of
823	{error, _} = Error ->
824	    {reply, Error, S};
825	{'EXIT', R} ->
826	    {reply, {error, R}, S};
827	{NewAppls, NewConfig} ->
828	    lists:foreach(fun(Appl) ->
829				  ets:insert(ac_tab, {{loaded, Appl#appl.name},
830						      Appl})
831			  end, NewAppls),
832	    {reply, ok, S#state{conf_data = NewConfig}}
833    end;
834
835handle_call(prep_config_change, _From, S) ->
836    RunningApps = S#state.running,
837    EnvBefore = lists:reverse(do_prep_config_change(RunningApps)),
838    {reply, EnvBefore, S};
839
840handle_call({config_change, EnvBefore}, _From, S) ->
841    RunningApps = S#state.running,
842    R = do_config_change(RunningApps, EnvBefore),
843    {reply, R, S};
844
845handle_call(which_applications, _From, S) ->
846    Reply = zf(fun({Name, Id}) ->
847		       case Id of
848			   {distributed, _Node} ->
849			       false;
850			   _ ->
851			       {true, #appl{descr = Descr, vsn = Vsn}} =
852				   get_loaded(Name),
853			       {true, {Name, Descr, Vsn}}
854		       end
855	       end, S#state.running),
856    {reply, Reply, S};
857
858handle_call({set_env, Config, Opts}, _From, S) ->
859    _ = [add_env(AppName, Env) || {AppName, Env} <- Config],
860
861    case proplists:get_value(persistent, Opts, false) of
862	true ->
863	    {reply, ok, S#state{conf_data = merge_env(S#state.conf_data, Config)}};
864	false ->
865	    {reply, ok, S}
866    end;
867
868handle_call({set_env, AppName, Key, Val, Opts}, _From, S) ->
869    ets:insert(ac_tab, {{env, AppName, Key}, Val}),
870    case proplists:get_value(persistent, Opts, false) of
871	true ->
872	    Fun = fun(Env) -> lists:keystore(Key, 1, Env, {Key, Val}) end,
873	    {reply, ok, S#state{conf_data = change_app_env(S#state.conf_data, AppName, Fun)}};
874	false ->
875	    {reply, ok, S}
876    end;
877
878handle_call({unset_env, AppName, Key, Opts}, _From, S) ->
879    ets:delete(ac_tab, {env, AppName, Key}),
880    case proplists:get_value(persistent, Opts, false) of
881	true ->
882	    Fun = fun(Env) -> lists:keydelete(Key, 1, Env) end,
883	    {reply, ok, S#state{conf_data = change_app_env(S#state.conf_data, AppName, Fun)}};
884	false ->
885	    {reply, ok, S}
886    end;
887
888handle_call({control_application, AppName}, {Pid, _Tag}, S) ->
889    Control = S#state.control,
890    case lists:keymember(AppName, 1, Control) of
891	false ->
892	    link(Pid),
893	    {reply, true, S#state{control = [{AppName, Pid} | Control]}};
894	true ->
895	    {reply, false, S}
896    end;
897
898handle_call({start_type, AppName}, _From, S) ->
899    Starting = S#state.starting,
900    StartType = case lists:keyfind(AppName, 1, Starting) of
901		    false ->
902			local;
903		    {_AppName, _RestartType, Type, _F} ->
904			Type
905		end,
906    {reply, StartType, S};
907
908handle_call(info, _From, S) ->
909    Reply = [{loaded, loaded_applications()},
910	     {loading, S#state.loading},
911	     {started, S#state.started},
912	     {start_p_false, S#state.start_p_false},
913	     {running, S#state.running},
914	     {starting, S#state.starting}],
915    {reply, Reply, S}.
916
917-spec handle_cast({'application_started', appname(), _}, state()) ->
918        {'noreply', state()} | {'stop', string(), state()}.
919
920handle_cast({application_started, AppName, Res}, S) ->
921    handle_application_started(AppName, Res, S).
922
923handle_application_started(AppName, Res, S) ->
924    #state{starting = Starting, running = Running, started = Started,
925	   start_req = Start_req} = S,
926    Start_reqN = reply_to_requester(AppName, Start_req, Res),
927    {_AppName, RestartType, _Type, _From} = lists:keyfind(AppName, 1, Starting),
928    case Res of
929	{ok, Id} ->
930	    case AppName of
931		kernel -> check_user();
932		_ -> ok
933	    end,
934	    info_started(AppName, nd(Id)),
935	    notify_cntrl_started(AppName, Id, S, ok),
936	    NRunning = keyreplaceadd(AppName, 1, Running,{AppName,Id}),
937	    NStarted = keyreplaceadd(AppName, 1, Started,{AppName,RestartType}),
938	    NewS =  S#state{starting = keydelete(AppName, 1, Starting),
939			    running = NRunning,
940			    started = NStarted,
941			    start_req = Start_reqN},
942	    %% The permission may have been changed during start
943	    Perm = application:get_env(kernel, permissions),
944	    case {Perm, Id} of
945		{undefined, _} ->
946		    {noreply, NewS};
947		%% Check only if the application is started on the own node
948		{{ok, Perms}, {distributed, StartNode}} when StartNode =:= node() ->
949		    case lists:member({AppName, false}, Perms) of
950			true ->
951			    #state{running = StopRunning, started = StopStarted} = NewS,
952			    case lists:keyfind(AppName, 1, StopRunning) of
953				{_AppName, Id} ->
954				    {_AppName2, Type} =
955					lists:keyfind(AppName, 1, StopStarted),
956				    stop_appl(AppName, Id, Type),
957				    NStopRunning = keydelete(AppName, 1, StopRunning),
958				    cntrl(AppName, NewS, {ac_application_stopped, AppName}),
959				    {noreply, NewS#state{running = NStopRunning,
960							started = StopStarted}};
961				false ->
962				    {noreply, NewS}
963			    end;
964			false ->
965			    {noreply, NewS}
966		    end;
967		_ ->
968		    {noreply, NewS}
969	    end;
970	{error, R} = Error when RestartType =:= temporary ->
971	    notify_cntrl_started(AppName, undefined, S, Error),
972	    info_exited(AppName, R, RestartType),
973	    {noreply, S#state{starting = keydelete(AppName, 1, Starting),
974			      start_req = Start_reqN}};
975	{info, R} when RestartType =:= temporary ->
976	    notify_cntrl_started(AppName, undefined, S, {error, R}),
977	    {noreply, S#state{starting = keydelete(AppName, 1, Starting),
978			      start_req = Start_reqN}};
979	{ErrInf, R} when RestartType =:= transient, ErrInf =:= error;
980			 RestartType =:= transient, ErrInf =:= info ->
981	    notify_cntrl_started(AppName, undefined, S, {error, R}),
982	    case ErrInf of
983		error ->
984		    info_exited(AppName, R, RestartType);
985		info ->
986		    ok
987	    end,
988	    case R of
989		{{'EXIT',normal},_Call} ->
990		    {noreply, S#state{starting = keydelete(AppName, 1, Starting),
991				      start_req = Start_reqN}};
992		_ ->
993		    Reason = {application_start_failure, AppName, R},
994		    {stop, to_string(Reason), S}
995	    end;
996	{error, R} = Error -> %% permanent
997	    notify_cntrl_started(AppName, undefined, S, Error),
998	    info_exited(AppName, R, RestartType),
999	    Reason = {application_start_failure, AppName, R},
1000	    {stop, to_string(Reason), S};
1001	{info, R} -> %% permanent
1002	    notify_cntrl_started(AppName, undefined, S, {error, R}),
1003	    Reason = {application_start_failure, AppName, R},
1004	    {stop, to_string(Reason), S}
1005    end.
1006
1007-spec handle_info(term(), state()) ->
1008        {'noreply', state()} | {'stop', string(), state()}.
1009
1010handle_info({ac_load_application_reply, AppName, Res}, S) ->
1011    case keysearchdelete(AppName, 1, S#state.loading) of
1012	{value, {_AppName, From}, Loading} ->
1013	    gen_server:reply(From, Res),
1014	    case Res of
1015		ok ->
1016		    {noreply, S#state{loading = Loading}};
1017		{error, _R} ->
1018		    NewS = unload(AppName, S),
1019		    {noreply, NewS#state{loading = Loading}}
1020	    end;
1021	false ->
1022	    {noreply, S}
1023    end;
1024
1025handle_info({ac_start_application_reply, AppName, Res}, S) ->
1026    Start_req = S#state.start_req,
1027    case lists:keyfind(AppName, 1, Starting = S#state.starting) of
1028	{_AppName, RestartType, Type, From} ->
1029	    case Res of
1030		start_it ->
1031		    {true, Appl} = get_loaded(AppName),
1032		    spawn_starter(From, Appl, S, Type),
1033		    {noreply, S};
1034		{started, Node} ->
1035		    handle_application_started(AppName,
1036					       {ok, {distributed, Node}},
1037					       S);
1038		not_started ->
1039		    Started = S#state.started,
1040		    Start_reqN =
1041			reply_to_requester(AppName, Start_req, ok),
1042		    {noreply,
1043		     S#state{starting = keydelete(AppName, 1, Starting),
1044			     started = [{AppName, RestartType} | Started],
1045			     start_req = Start_reqN}};
1046		{takeover, _Node} = Takeover ->
1047		    {true, Appl} = get_loaded(AppName),
1048		    spawn_starter(From, Appl, S, Takeover),
1049		    NewStarting1 = keydelete(AppName, 1, Starting),
1050		    NewStarting = [{AppName, RestartType, Takeover, From} | NewStarting1],
1051		    {noreply, S#state{starting = NewStarting}};
1052		{error, Reason} = Error when RestartType =:= permanent ->
1053		    Start_reqN = reply_to_requester(AppName, Start_req, Error),
1054		    {stop, to_string(Reason), S#state{start_req = Start_reqN}};
1055		{error, _Reason} = Error ->
1056		    Start_reqN = reply_to_requester(AppName, Start_req, Error),
1057		    {noreply, S#state{starting =
1058					  keydelete(AppName, 1, Starting),
1059				      start_req = Start_reqN}}
1060	    end;
1061	false ->
1062	    {noreply, S} % someone called stop before control got that
1063    end;
1064
1065handle_info({ac_change_application_req, AppName, Msg}, S) ->
1066    Running = S#state.running,
1067    Started = S#state.started,
1068    Starting = S#state.starting,
1069    case {keyfind(AppName, 1, Running), keyfind(AppName, 1, Started)} of
1070	{{AppName, Id}, {_AppName2, Type}} ->
1071	    case Msg of
1072		{started, Node} ->
1073		    stop_appl(AppName, Id, Type),
1074		    NRunning = [{AppName, {distributed, Node}} |
1075				keydelete(AppName, 1, Running)],
1076		    {noreply, S#state{running = NRunning}};
1077		{takeover, _Node, _RT} when is_pid(Id) -> % it is running already
1078		    notify_cntrl_started(AppName, Id, S, ok),
1079		    {noreply, S};
1080		{takeover, Node, RT} ->
1081		    NewS = do_start(AppName, RT, {takeover, Node}, undefined, S),
1082		    {noreply, NewS};
1083		{failover, _Node, _RT} when is_pid(Id) -> % it is running already
1084		    notify_cntrl_started(AppName, Id, S, ok),
1085		    {noreply, S};
1086		{failover, Node, RT} ->
1087		    case application:get_key(AppName, start_phases) of
1088			{ok, undefined} ->
1089			    %% to be backwards compatible the application
1090			    %% is not started as failover if start_phases
1091			    %% is not defined in the .app file
1092			    NewS = do_start(AppName, RT, normal, undefined, S),
1093			    {noreply, NewS};
1094			{ok, _StartPhases} ->
1095			    NewS = do_start(AppName, RT, {failover, Node}, undefined, S),
1096			    {noreply, NewS}
1097		    end;
1098		stop_it ->
1099		    stop_appl(AppName, Id, Type),
1100		    cntrl(AppName, S, {ac_application_not_run, AppName}),
1101		    NRunning = keyreplace(AppName, 1, Running,
1102					 {AppName, {distributed, []}}),
1103		    {noreply, S#state{running = NRunning}};
1104		%% We should not try to start a running application!
1105		start_it when is_pid(Id) ->
1106		    notify_cntrl_started(AppName, Id, S, ok),
1107		    {noreply, S};
1108		start_it ->
1109		    NewS = do_start(AppName, undefined, normal, undefined, S),
1110		    {noreply, NewS};
1111		not_running ->
1112		    NRunning = keydelete(AppName, 1, Running),
1113		    {noreply, S#state{running = NRunning}};
1114		_ ->
1115		    {noreply, S}
1116	    end;
1117	_ ->
1118	    IsLoaded = get_loaded(AppName),
1119	    IsStarting = lists:keysearch(AppName, 1, Starting),
1120	    IsStarted = lists:keysearch(AppName, 1, Started),
1121	    IsRunning = lists:keysearch(AppName, 1, Running),
1122
1123	    case Msg of
1124		start_it ->
1125		    case {IsLoaded, IsStarting, IsStarted, IsRunning} of
1126			%% already running
1127			{_, _, _, {value, _Tuple}} ->
1128			    {noreply, S};
1129			%% not loaded
1130			{false, _, _, _} ->
1131			    {noreply, S};
1132			%% only loaded
1133			{{true, _Appl}, false, false, false} ->
1134			    {noreply, S};
1135			%% starting
1136			{{true, _Appl}, {value, Tuple}, false, false} ->
1137			    {_AppName, _RStype, _Type, From} = Tuple,
1138			    NewS = do_start(AppName, undefined, normal, From, S),
1139			    {noreply, NewS};
1140			%% started but not running
1141			{{true, _Appl}, _, {value, {AppName, _RestartType}}, false} ->
1142			    NewS = do_start(AppName, undefined, normal, undefined, S),
1143			    SS = NewS#state{started = keydelete(AppName, 1, Started)},
1144			    {noreply, SS}
1145		    end;
1146		{started, Node} ->
1147		    NRunning = [{AppName, {distributed, Node}} |
1148				keydelete(AppName, 1, Running)],
1149		    {noreply, S#state{running = NRunning}};
1150		_ ->
1151		    {noreply, S} % someone called stop before control got that
1152	    end
1153    end;
1154
1155%%-----------------------------------------------------------------
1156%% An application died.  Check its restart_type.  Maybe terminate
1157%% all other applications.
1158%%-----------------------------------------------------------------
1159handle_info({'EXIT', Pid, Reason}, S) ->
1160    ets:match_delete(ac_tab, {{application_master, '_'}, Pid}),
1161    NRunning = keydelete(Pid, 2, S#state.running),
1162    NewS = S#state{running = NRunning},
1163    case lists:keyfind(Pid, 2, S#state.running) of
1164	{AppName, _AmPid} ->
1165	    cntrl(AppName, S, {ac_application_stopped, AppName}),
1166	    case lists:keyfind(AppName, 1, S#state.started) of
1167		{_AppName, temporary} ->
1168		    info_exited(AppName, Reason, temporary),
1169		    {noreply, NewS};
1170		{_AppName, transient} when Reason =:= normal ->
1171		    info_exited(AppName, Reason, transient),
1172		    {noreply, NewS};
1173		{_AppName, Type} ->
1174		    info_exited(AppName, Reason, Type),
1175		    {stop, to_string({application_terminated, AppName, Reason}), NewS}
1176	    end;
1177	false ->
1178	    {noreply, S#state{control = del_cntrl(S#state.control, Pid)}}
1179    end;
1180
1181handle_info(_, S) ->
1182    {noreply, S}.
1183
1184-spec terminate(term(), state()) -> 'ok'.
1185
1186terminate(Reason, S) ->
1187    case application:get_env(kernel, shutdown_func) of
1188	{ok, {M, F}} ->
1189	    catch M:F(Reason);
1190	_ ->
1191	    ok
1192    end,
1193    ShutdownTimeout =
1194	case application:get_env(kernel, shutdown_timeout) of
1195	    undefined -> infinity;
1196	    {ok,T} -> T
1197	end,
1198    foreach(fun({_AppName, Id}) when is_pid(Id) ->
1199		    Ref = erlang:monitor(process, Id),
1200		    unlink(Id),
1201		    exit(Id, shutdown),
1202		    receive
1203			%% Proc died before link
1204			{'EXIT', Id, _} -> ok
1205		    after 0 ->
1206			    receive
1207				{'DOWN', Ref, process, Id, _} -> ok
1208			    after ShutdownTimeout ->
1209				    exit(Id, kill),
1210				    receive
1211					{'DOWN', Ref, process, Id, _} -> ok
1212				    end
1213			    end
1214		    end;
1215	       (_) -> ok
1216	    end,
1217	    S#state.running),
1218    true = ets:delete(ac_tab),
1219    ok.
1220
1221-spec code_change(term(), state(), term()) -> {'ok', state()}.
1222
1223code_change(_OldVsn, State, _Extra) ->
1224    {ok, State}.
1225
1226
1227%%%-----------------------------------------------------------------
1228%%% Internal functions
1229%%%-----------------------------------------------------------------
1230cntrl(AppName, #state{control = Control}, Msg) ->
1231    case lists:keyfind(AppName, 1, Control) of
1232	{_AppName, Pid} ->
1233	    Pid ! Msg,
1234	    true;
1235	false ->
1236	    false
1237    end.
1238
1239notify_cntrl_started(_AppName, {distributed, _Node}, _S, _Res) ->
1240    ok;
1241notify_cntrl_started(AppName, _Id, S, Res) ->
1242    cntrl(AppName, S, {ac_application_run, AppName, Res}).
1243
1244del_cntrl([{_, Pid}|T], Pid) ->
1245    del_cntrl(T, Pid);
1246del_cntrl([H|T], Pid) ->
1247    [H|del_cntrl(T, Pid)];
1248del_cntrl([], _Pid) ->
1249    [].
1250
1251get_loaded(App) ->
1252    AppName = get_appl_name(App),
1253    case ets:lookup(ac_tab, {loaded, AppName}) of
1254	[{_Key, Appl}] -> {true, Appl};
1255	_  -> false
1256    end.
1257
1258do_load_application(Application, S) ->
1259    case get_loaded(Application) of
1260	{true, _} ->
1261	    throw({error, {already_loaded, Application}});
1262	false ->
1263	    case make_appl(Application) of
1264		{ok, Appl} -> load(S, Appl);
1265		Error -> Error
1266	    end
1267    end.
1268
1269%% Recursively load the application and its included apps.
1270%load(S, {ApplData, ApplEnv, IncApps, Descr, Vsn, Apps}) ->
1271load(S, {ApplData, ApplEnv, IncApps, Descr, Id, Vsn, Apps}) ->
1272    Name = ApplData#appl_data.name,
1273    ConfEnv = get_env_i(Name, S),
1274    NewEnv = merge_app_env(ApplEnv, ConfEnv),
1275    CmdLineEnv = get_cmd_env(Name),
1276    NewEnv2 = merge_app_env(NewEnv, CmdLineEnv),
1277    add_env(Name, NewEnv2),
1278    Appl = #appl{name = Name, descr = Descr, id = Id, vsn = Vsn,
1279		 appl_data = ApplData, inc_apps = IncApps, apps = Apps},
1280    ets:insert(ac_tab, {{loaded, Name}, Appl}),
1281    NewS =
1282	foldl(fun(App, S1) ->
1283		      case get_loaded(App) of
1284			  {true, _} -> S1;
1285			  false ->
1286			      case do_load_application(App, S1) of
1287				  {ok, S2} -> S2;
1288				  Error -> throw(Error)
1289			      end
1290		      end
1291	      end, S, IncApps),
1292    {ok, NewS}.
1293
1294unload(AppName, S) ->
1295    {ok, IncApps} = get_key(AppName, included_applications),
1296    del_env(AppName),
1297    ets:delete(ac_tab, {loaded, AppName}),
1298    foldl(fun(App, S1) ->
1299		  case get_loaded(App) of
1300		      false -> S1;
1301		      {true, _} -> unload(App, S1)
1302		  end
1303	  end, S, IncApps).
1304
1305check_start_cond(AppName, RestartType, Started, Running) ->
1306    validRestartType(RestartType),
1307    case get_loaded(AppName) of
1308	{true, Appl} ->
1309	    %% Check Running; not Started.  An exited app is not running,
1310	    %% but started.  It must be possible to start an exited app!
1311	    case lists:keymember(AppName, 1, Running) of
1312		true ->
1313		    {error, {already_started, AppName}};
1314		false ->
1315		    foreach(
1316		      fun(AppName2) ->
1317			      case lists:keymember(AppName2, 1, Started) of
1318				  true -> ok;
1319				  false ->
1320				      throw({error, {not_started, AppName2}})
1321			      end
1322		      end, Appl#appl.apps),
1323		    {ok, Appl}
1324	    end;
1325	false ->
1326	    {error, {not_loaded, AppName}}
1327    end.
1328
1329do_start(AppName, RT, Type, From, S) ->
1330    RestartType = case lists:keyfind(AppName, 1, S#state.started) of
1331		      {_AppName2, OldRT} ->
1332			  get_restart_type(RT, OldRT);
1333		      false ->
1334			  RT
1335		  end,
1336    %% UW 990913: We check start_req instead of starting, because starting
1337    %% has already been checked.
1338    case lists:keymember(AppName, 1, S#state.start_req) of
1339	false ->
1340	    {true, Appl} = get_loaded(AppName),
1341	    Start_req = S#state.start_req,
1342	    spawn_starter(undefined, Appl, S, Type),
1343	    Starting = case lists:keymember(AppName, 1, S#state.starting) of
1344			   false ->
1345			       %% UW: don't know if this is necessary
1346			       [{AppName, RestartType, Type, From} |
1347				S#state.starting];
1348			   true ->
1349			       S#state.starting
1350		       end,
1351	    S#state{starting = Starting,
1352		    start_req = [{AppName, From} | Start_req]};
1353	true -> % otherwise we're already starting the app...
1354	    S
1355    end.
1356
1357spawn_starter(From, Appl, S, Type) ->
1358    spawn_link(?MODULE, init_starter, [From, Appl, S, Type]).
1359
1360init_starter(_From, Appl, S, Type) ->
1361    process_flag(trap_exit, true),
1362    AppName = Appl#appl.name,
1363    gen_server:cast(?AC, {application_started, AppName,
1364			  catch start_appl(Appl, S, Type)}).
1365
1366reply(undefined, _Reply) ->
1367    ok;
1368reply(From, Reply) -> gen_server:reply(From, Reply).
1369
1370start_appl(Appl, S, Type) ->
1371    ApplData = Appl#appl.appl_data,
1372    case ApplData#appl_data.mod of
1373	[] ->
1374	    {ok, undefined};
1375	_ ->
1376	    %% Name = ApplData#appl_data.name,
1377	    Running = S#state.running,
1378	    foreach(
1379	      fun(AppName) ->
1380		      case lists:keymember(AppName, 1, Running) of
1381			  true ->
1382			      ok;
1383			  false ->
1384			      throw({info, {not_running, AppName}})
1385		      end
1386	      end, Appl#appl.apps),
1387	    case application_master:start_link(ApplData, Type) of
1388		{ok, _Pid} = Ok ->
1389		    Ok;
1390		{error, _Reason} = Error ->
1391		    throw(Error)
1392	    end
1393    end.
1394
1395
1396%%-----------------------------------------------------------------
1397%% Stop application locally.
1398%%-----------------------------------------------------------------
1399stop_appl(AppName, Id, Type) when is_pid(Id) ->
1400    unlink(Id),
1401    application_master:stop(Id),
1402    info_exited(AppName, stopped, Type),
1403    ets:delete(ac_tab, {application_master, AppName});
1404stop_appl(AppName, undefined, Type) ->
1405    %% Code-only application stopped
1406    info_exited(AppName, stopped, Type);
1407stop_appl(_AppName, _Id, _Type) ->
1408    %% Distributed application stopped
1409    ok.
1410
1411keysearchdelete(Key, Pos, List) ->
1412    ksd(Key, Pos, List, []).
1413
1414ksd(Key, Pos, [H | T], Rest) when element(Pos, H) =:= Key ->
1415    {value, H, Rest ++ T};
1416ksd(Key, Pos, [H | T], Rest) ->
1417    ksd(Key, Pos, T, [H | Rest]);
1418ksd(_Key, _Pos, [], _Rest) ->
1419    false.
1420
1421keyreplaceadd(Key, Pos, List, New) ->
1422    %% Maintains the order!
1423    case lists:keymember(Key, Pos, List) of
1424	true -> keyreplace(Key, Pos, List, New);
1425	false -> [New | List]
1426    end.
1427
1428validRestartType(permanent)   -> true;
1429validRestartType(temporary)   -> true;
1430validRestartType(transient)   -> true;
1431validRestartType(RestartType) ->
1432    throw({error, {invalid_restart_type, RestartType}}).
1433
1434nd({distributed, Node}) -> Node;
1435nd(_) -> node().
1436
1437get_restart_type(undefined, OldRT) ->
1438    OldRT;
1439get_restart_type(RT, _OldRT) ->
1440    RT.
1441
1442get_appl_name(Name) when is_atom(Name) -> Name;
1443get_appl_name({application, Name, _}) when is_atom(Name) -> Name;
1444get_appl_name(Appl) -> throw({error, {bad_application, Appl}}).
1445
1446make_appl(Name) when is_atom(Name) ->
1447    FName = atom_to_list(Name) ++ ".app",
1448    case code:where_is_file(FName) of
1449	non_existing ->
1450	    {error, {file:format_error(enoent), FName}};
1451	FullName ->
1452	    case prim_consult(FullName) of
1453		{ok, [Application]} ->
1454		    {ok, make_appl_i(Application)};
1455		{error, Reason} ->
1456		    {error, {file:format_error(Reason), FName}};
1457                error ->
1458                    {error, "bad encoding"}
1459	    end
1460    end;
1461make_appl(Application) ->
1462    {ok, make_appl_i(Application)}.
1463
1464prim_consult(FullName) ->
1465    case erl_prim_loader:get_file(FullName) of
1466	{ok, Bin, _} ->
1467            case file_binary_to_list(Bin) of
1468                {ok, String} ->
1469                    case erl_scan:string(String) of
1470                        {ok, Tokens, _EndLine} ->
1471                            prim_parse(Tokens, []);
1472                        {error, Reason, _EndLine} ->
1473                            {error, Reason}
1474                    end;
1475                error ->
1476                    error
1477            end;
1478	error ->
1479	    {error, enoent}
1480    end.
1481
1482prim_parse(Tokens, Acc) ->
1483    case lists:splitwith(fun(T) -> element(1,T) =/= dot end, Tokens) of
1484	{[], []} ->
1485	    {ok, lists:reverse(Acc)};
1486	{Tokens2, [{dot,_} = Dot | Rest]} ->
1487	    case erl_parse:parse_term(Tokens2 ++ [Dot]) of
1488		{ok, Term} ->
1489		    prim_parse(Rest, [Term | Acc]);
1490		{error, _R} = Error ->
1491		    Error
1492	    end;
1493	{Tokens2, []} ->
1494	    case erl_parse:parse_term(Tokens2) of
1495		{ok, Term} ->
1496		    {ok, lists:reverse([Term | Acc])};
1497		{error, _R} = Error ->
1498		    Error
1499	    end
1500    end.
1501
1502make_appl_i({application, Name, Opts}) when is_atom(Name), is_list(Opts) ->
1503    Descr = get_opt(description, Opts, ""),
1504    Id = get_opt(id, Opts, ""),
1505    Vsn = get_opt(vsn, Opts, ""),
1506    Mods = get_opt(modules, Opts, []),
1507    Regs = get_opt(registered, Opts, []),
1508    Apps = get_opt(applications, Opts, []),
1509    Mod =
1510	case get_opt(mod, Opts, []) of
1511	    {M,_A}=MA when is_atom(M) -> MA;
1512	    [] -> [];
1513	    Other -> throw({error, {badstartspec, Other}})
1514	end,
1515    Phases = get_opt(start_phases, Opts, undefined),
1516    Env = get_opt(env, Opts, []),
1517    MaxP = get_opt(maxP, Opts, infinity),
1518    MaxT = get_opt(maxT, Opts, infinity),
1519    IncApps = get_opt(included_applications, Opts, []),
1520    {#appl_data{name = Name, regs = Regs, mod = Mod, phases = Phases,
1521		mods = Mods, inc_apps = IncApps, maxP = MaxP, maxT = MaxT},
1522     Env, IncApps, Descr, Id, Vsn, Apps};
1523make_appl_i({application, Name, Opts}) when is_list(Opts) ->
1524    throw({error,{invalid_name,Name}});
1525make_appl_i({application, _Name, Opts}) ->
1526    throw({error,{invalid_options, Opts}});
1527make_appl_i(Appl) -> throw({error, {bad_application, Appl}}).
1528
1529
1530%%-----------------------------------------------------------------
1531%% Merge current applications with changes.
1532%%-----------------------------------------------------------------
1533
1534%% do_change_apps(Applications, Config, OldAppls) -> NewAppls
1535%%   Applications = [{application, AppName, [{Key,Value}]}]
1536%%   Config = [{AppName,[{Par,Value}]} | File]
1537%%   OldAppls = NewAppls = [#appl{}]
1538do_change_apps(Applications, Config, OldAppls) ->
1539
1540    %% OTP-4867
1541    %% Config = contents of sys.config file
1542    %% May now contain names of other .config files as well as
1543    %% configuration parameters.
1544    %% Therefore read and merge contents.
1545    {ok, SysConfig, Errors} = check_conf_sys(Config),
1546
1547    %% Report errors, but do not terminate
1548    %% (backwards compatible behaviour)
1549    lists:foreach(fun({error, {SysFName, Line, Str}}) ->
1550			  ?LOG_ERROR("~tp: ~w: ~ts~n",[SysFName, Line, Str],
1551                                     #{error_logger=>#{tag=>error}})
1552		  end,
1553		  Errors),
1554
1555    {map(fun(Appl) ->
1556		 AppName = Appl#appl.name,
1557		 case is_loaded_app(AppName, Applications) of
1558		     {true, Application} ->
1559			 do_change_appl(make_appl(Application),
1560					Appl, SysConfig);
1561
1562		     %% ignored removed apps - handled elsewhere
1563		     false ->
1564			 Appl
1565		 end
1566	 end, OldAppls),
1567     SysConfig}.
1568
1569is_loaded_app(AppName, [{application, AppName, App} | _]) ->
1570    {true, {application, AppName, App}};
1571is_loaded_app(AppName, [_ | T]) -> is_loaded_app(AppName, T);
1572is_loaded_app(_AppName, []) -> false.
1573
1574do_change_appl({ok, {ApplData, Env, IncApps, Descr, Id, Vsn, Apps}},
1575	       OldAppl, Config) ->
1576    AppName = OldAppl#appl.name,
1577
1578    %% Merge application env with env from sys.config, if any
1579    ConfEnv = get_opt(AppName, Config, []),
1580    NewEnv1 = merge_app_env(Env, ConfEnv),
1581
1582    %% Merge application env with command line arguments, if any
1583    CmdLineEnv = get_cmd_env(AppName),
1584    NewEnv2 = merge_app_env(NewEnv1, CmdLineEnv),
1585
1586    %% Update ets table with new application env
1587    del_env(AppName),
1588    add_env(AppName, NewEnv2),
1589
1590    OldAppl#appl{appl_data=ApplData,
1591		 descr=Descr,
1592		 id=Id,
1593		 vsn=Vsn,
1594		 inc_apps=IncApps,
1595		 apps=Apps};
1596do_change_appl({error, _R} = Error, _Appl, _ConfData) ->
1597    throw(Error).
1598
1599get_opt(Key, List, Default) ->
1600    case lists:keyfind(Key, 1, List) of
1601	{_Key, Val} -> Val;
1602	_ -> Default
1603    end.
1604
1605get_cmd_env(Name) ->
1606    case init:get_argument(Name) of
1607	{ok, Args} ->
1608	   foldl(fun(List, Res) -> conv(List) ++ Res end, [], Args);
1609	_ -> []
1610    end.
1611
1612conv([Key, Val | T]) ->
1613    [{make_term(Key), make_term(Val)} | conv(T)];
1614conv(_) -> [].
1615
1616make_term(Str) ->
1617    case erl_scan:string(Str) of
1618	{ok, Tokens, _} ->
1619	    case erl_parse:parse_term(Tokens ++ [{dot, erl_anno:new(1)}]) of
1620		{ok, Term} ->
1621		    Term;
1622		{error, {_,M,Reason}} ->
1623                    handle_make_term_error(M, Reason, Str)
1624	    end;
1625	{error, {_,M,Reason}, _} ->
1626            handle_make_term_error(M, Reason, Str)
1627    end.
1628
1629handle_make_term_error(Mod, Reason, Str) ->
1630    ?LOG_ERROR("application_controller: ~ts: ~ts~n",
1631               [Mod:format_error(Reason), Str],
1632               #{error_logger=>#{tag=>error}}),
1633    throw({error, {bad_environment_value, Str}}).
1634
1635get_env_i(Name, #state{conf_data = ConfData}) when is_list(ConfData) ->
1636    case lists:keyfind(Name, 1, ConfData) of
1637	{_Name, Env} -> Env;
1638	_ -> []
1639    end;
1640get_env_i(_Name, _) -> [].
1641
1642%% Merges envs for all apps.  Env2 overrides Env1
1643merge_env(Env1, Env2) ->
1644    merge_env(Env1, Env2, []).
1645
1646merge_env([{App, AppEnv1} | T], Env2, Res) ->
1647    case get_env_key(App, Env2) of
1648	{value, AppEnv2, RestEnv2} ->
1649	    NewAppEnv = merge_app_env(AppEnv1, AppEnv2),
1650	    merge_env(T, RestEnv2, [{App, NewAppEnv} | Res]);
1651	_ ->
1652	    merge_env(T, Env2, [{App, AppEnv1} | Res])
1653    end;
1654merge_env([], Env2, Res) ->
1655    Env2 ++ Res.
1656
1657%% Changes the environment for the given application
1658%% If there is no application, an empty one is created
1659change_app_env(Env, App, Fun) ->
1660    case get_env_key(App, Env) of
1661	{value, AppEnv, RestEnv} ->
1662	    [{App, Fun(AppEnv)} | RestEnv];
1663	_ ->
1664	    [{App, Fun([])} | Env]
1665    end.
1666
1667%% Merges envs for an application.  Env2 overrides Env1
1668merge_app_env(Env1, Env2) ->
1669    merge_app_env(Env1, Env2, []).
1670
1671merge_app_env([{Key, Val} | T], Env2, Res) ->
1672    case get_env_key(Key, Env2) of
1673	{value, NewVal, RestEnv} ->
1674	    merge_app_env(T, RestEnv, [{Key, NewVal}|Res]);
1675	_ ->
1676	    merge_app_env(T, Env2, [{Key, Val} | Res])
1677    end;
1678merge_app_env([], Env2, Res) ->
1679    Env2 ++ Res.
1680
1681get_env_key(Key, Env) -> get_env_key(Env, Key, []).
1682get_env_key([{Key, Val} | T], Key, Res) ->
1683    {value, Val, T ++ Res};
1684get_env_key([H | T], Key, Res) ->
1685    get_env_key(T, Key, [H | Res]);
1686get_env_key([], _Key, Res) -> Res.
1687
1688add_env(Name, Env) ->
1689    foreach(fun({Key, Value}) ->
1690			  ets:insert(ac_tab, {{env, Name, Key}, Value})
1691		  end,
1692		  Env).
1693
1694del_env(Name) ->
1695    ets:match_delete(ac_tab, {{env, Name, '_'}, '_'}).
1696
1697check_user() ->
1698    case whereis(user) of
1699	User when is_pid(User) -> group_leader(User, self());
1700	_ -> ok
1701    end.
1702
1703
1704%%-----------------------------------------------------------------
1705%% Prepare for a release upgrade by reading all the evironment variables.
1706%%-----------------------------------------------------------------
1707do_prep_config_change(Apps) ->
1708    do_prep_config_change(Apps, []).
1709
1710do_prep_config_change([], EnvBefore) ->
1711    EnvBefore;
1712do_prep_config_change([{App, _Id} | Apps], EnvBefore) ->
1713    Env = application:get_all_env(App),
1714    do_prep_config_change(Apps, [{App, Env} | EnvBefore]).
1715
1716
1717
1718%%-----------------------------------------------------------------
1719%% Inform all running applications about the changed configuration.
1720%%-----------------------------------------------------------------
1721do_config_change(Apps, EnvBefore) ->
1722    do_config_change(Apps, EnvBefore, []).
1723
1724do_config_change([], _EnvBefore, []) ->
1725    ok;
1726do_config_change([], _EnvBefore, Errors) ->
1727    {error, Errors};
1728do_config_change([{App, _Id} | Apps], EnvBefore, Errors) ->
1729    AppEnvNow = lists:sort(application:get_all_env(App)),
1730    AppEnvBefore = case lists:keyfind(App, 1, EnvBefore) of
1731		       false ->
1732			   [];
1733		       {App, AppEnvBeforeT} ->
1734			   lists:sort(AppEnvBeforeT)
1735		   end,
1736    Res =
1737	case AppEnvNow of
1738	    AppEnvBefore ->
1739		ok;
1740	    _ ->
1741		case do_config_diff(AppEnvNow, AppEnvBefore) of
1742		    {[], [], []} ->
1743			ok;
1744		    {Changed, New, Removed} ->
1745			case application:get_key(App, mod) of
1746			    {ok, {Mod, _Para}} ->
1747				case catch Mod:config_change(Changed, New,
1748							     Removed) of
1749				    ok ->
1750					ok;
1751				    %% It is not considered as an error
1752				    %% if the cb-function is not defined
1753				    {'EXIT', {undef, _}} ->
1754					ok;
1755				    {error, _} = Error ->
1756					Error;
1757				    Else ->
1758					{error, Else}
1759				end;
1760			    {ok, []} ->
1761				{error, {module_not_defined, App}};
1762			    undefined ->
1763				{error, {application_not_found, App}}
1764			end
1765		end
1766	end,
1767
1768    case Res of
1769	ok ->
1770	    do_config_change(Apps, EnvBefore, Errors);
1771	{error, NewError} ->
1772	    do_config_change(Apps, EnvBefore,[NewError | Errors])
1773    end.
1774
1775
1776%%-----------------------------------------------------------------
1777%% Check if the configuration is changed in anyway.
1778%%-----------------------------------------------------------------
1779do_config_diff(AppEnvNow, AppEnvBefore) ->
1780    do_config_diff(AppEnvNow, AppEnvBefore, {[], []}).
1781
1782do_config_diff([], AppEnvBefore, {Changed, New}) ->
1783    Removed = lists:foldl(fun({Env, _Value}, Acc) -> [Env | Acc] end, [], AppEnvBefore),
1784    {Changed, New, Removed};
1785do_config_diff(AppEnvNow, [], {Changed, New}) ->
1786    {Changed, AppEnvNow++New, []};
1787do_config_diff([{Env, Value} | AppEnvNow], AppEnvBefore, {Changed, New}) ->
1788    case lists:keyfind(Env, 1, AppEnvBefore) of
1789	{Env, Value} ->
1790	    do_config_diff(AppEnvNow, lists:keydelete(Env,1,AppEnvBefore), {Changed, New});
1791	{Env, _OtherValue} ->
1792	    do_config_diff(AppEnvNow, lists:keydelete(Env,1,AppEnvBefore),
1793			   {[{Env, Value} | Changed], New});
1794	false ->
1795	    do_config_diff(AppEnvNow, AppEnvBefore, {Changed, [{Env, Value}|New]})
1796    end.
1797
1798
1799%%-----------------------------------------------------------------
1800%% Read the .config files.
1801%%-----------------------------------------------------------------
1802check_conf() ->
1803    case init:get_argument(config) of
1804	{ok, Files} ->
1805	    {ok, lists:foldl(
1806		   fun([File], Env) ->
1807			   BFName = filename:basename(File,".config"),
1808			   FName = filename:join(filename:dirname(File),
1809						 BFName ++ ".config"),
1810			   case load_file(FName) of
1811			       {ok, NewEnv} ->
1812				   %% OTP-4867
1813				   %% sys.config may now contain names of
1814				   %% other .config files as well as
1815				   %% configuration parameters.
1816				   %% Therefore read and merge contents.
1817				   if
1818				       BFName =:= "sys" ->
1819					   DName = filename:dirname(FName),
1820					   {ok, SysEnv, Errors} =
1821					       check_conf_sys(NewEnv, [], [], DName),
1822
1823					   %% Report first error, if any, and
1824					   %% terminate
1825					   %% (backwards compatible behaviour)
1826					   case Errors of
1827					       [] ->
1828						   merge_env(Env, SysEnv);
1829					       [{error, {SysFName, Line, Str}}|_] ->
1830						   throw({error, {SysFName, Line, Str}})
1831					   end;
1832				       true ->
1833					   merge_env(Env, NewEnv)
1834				   end;
1835			       {error, {Line, _Mod, Str}} ->
1836				   throw({error, {FName, Line, Str}})
1837			   end
1838		   end, [], Files)};
1839	_ -> {ok, []}
1840    end.
1841
1842check_conf_sys(Env) ->
1843    check_conf_sys(Env, [], [], []).
1844
1845check_conf_sys([File|T], SysEnv, Errors, DName) when is_list(File),is_list(DName) ->
1846    BFName = filename:basename(File, ".config"),
1847    FName = filename:join(filename:dirname(File), BFName ++ ".config"),
1848    LName = case filename:pathtype(FName) of
1849               relative when (DName =/= []) ->
1850                  % Check if relative to sys.config dir otherwise use legacy mode,
1851                  % i.e relative to cwd.
1852                  RName = filename:join(DName, FName),
1853                  case erl_prim_loader:read_file_info(RName) of
1854                     {ok, _} -> RName ;
1855                     error   -> FName
1856                  end;
1857		_          -> FName
1858	    end,
1859    case load_file(LName) of
1860	{ok, NewEnv} ->
1861	    check_conf_sys(T, merge_env(SysEnv, NewEnv), Errors, DName);
1862	{error, {Line, _Mod, Str}} ->
1863	    check_conf_sys(T, SysEnv, [{error, {LName, Line, Str}}|Errors], DName)
1864    end;
1865check_conf_sys([Tuple|T], SysEnv, Errors, DName) ->
1866    check_conf_sys(T, merge_env(SysEnv, [Tuple]), Errors, DName);
1867check_conf_sys([], SysEnv, Errors, _) ->
1868    {ok, SysEnv, lists:reverse(Errors)}.
1869
1870load_file(File) ->
1871    %% We can't use file:consult/1 here. Too bad.
1872    case erl_prim_loader:get_file(File) of
1873	{ok, Bin, _FileName} ->
1874	    %% Make sure that there is some whitespace at the end of the string
1875	    %% (so that reading a file with no NL following the "." will work).
1876            case file_binary_to_list(Bin) of
1877                {ok, String} ->
1878                    scan_file(String ++ " ");
1879                error ->
1880                    {error, {none, scan_file, "bad encoding"}}
1881            end;
1882	error ->
1883	    {error, {none, open_file, "configuration file not found"}}
1884    end.
1885
1886scan_file(Str) ->
1887    case erl_scan:tokens([], Str, 1) of
1888	{done, {ok, Tokens, _}, Left} ->
1889	    case erl_parse:parse_term(Tokens) of
1890		{ok,L}=Res when is_list(L) ->
1891		    case only_ws(Left) of
1892			true ->
1893			    Res;
1894			false ->
1895			    %% There was trailing garbage found after the list.
1896			    config_error()
1897		    end;
1898		{ok,_} ->
1899		    %% Parsing succeeded but the result is not a list.
1900		    config_error();
1901		Error ->
1902		    Error
1903	    end;
1904	{done, Result, _} ->
1905	    {error, {none, parse_file, tuple_to_list(Result)}};
1906	{more, _} ->
1907	    {error, {none, load_file, "no ending <dot> found"}}
1908    end.
1909
1910only_ws([C|Cs]) when C =< $\s -> only_ws(Cs);
1911only_ws([$%|Cs]) -> only_ws(strip_comment(Cs));   % handle comment
1912only_ws([_|_]) -> false;
1913only_ws([]) -> true.
1914
1915strip_comment([$\n|Cs]) -> Cs;
1916strip_comment([_|Cs]) -> strip_comment(Cs);
1917strip_comment([]) -> [].
1918
1919config_error() ->
1920    {error,
1921     {none, load_file,
1922      "configuration file must contain ONE list ended by <dot>"}}.
1923
1924%%-----------------------------------------------------------------
1925%% Info messages sent to logger
1926%%-----------------------------------------------------------------
1927info_started(Name, Node) ->
1928    ?LOG_INFO(#{label=>{application_controller,progress},
1929                report=>[{application, Name},
1930                         {started_at, Node}]},
1931              #{domain=>[otp,sasl],
1932                report_cb=>fun logger:format_otp_report/1,
1933                logger_formatter=>#{title=>"PROGRESS REPORT"},
1934                error_logger=>#{tag=>info_report,type=>progress}}).
1935
1936info_exited(Name, Reason, Type) ->
1937    ?LOG_NOTICE(#{label=>{application_controller,exit},
1938                  report=>[{application, Name},
1939                           {exited, Reason},
1940                           {type, Type}]},
1941                #{domain=>[otp],
1942                  report_cb=>fun logger:format_otp_report/1,
1943                  error_logger=>#{tag=>info_report,type=>std_info}}).
1944
1945%%-----------------------------------------------------------------
1946%% Reply to all processes waiting this application to be started.
1947%%-----------------------------------------------------------------
1948reply_to_requester(AppName, Start_req, Res) ->
1949    R = case Res of
1950	    {ok, _Id} ->
1951		ok;
1952	    {info, Reason} ->
1953		{error, Reason};
1954	    Error ->
1955		Error
1956	end,
1957
1958    lists:foldl(fun(Sp, AccIn) ->
1959			case Sp of
1960			    {AppName, From} ->
1961				reply(From, R),
1962				AccIn;
1963			    _ ->
1964				[Sp | AccIn]
1965			end
1966		end,
1967		[],
1968		Start_req).
1969
1970
1971%%-----------------------------------------------------------------
1972%% Update the environment variable permission for an application.
1973%%-----------------------------------------------------------------
1974update_permissions(AppName, Bool) ->
1975    T = {env, kernel, permissions},
1976    case ets:lookup(ac_tab, T) of
1977	[] ->
1978	    ets:insert(ac_tab, {T, [{AppName, Bool}]});
1979	[{_, Perm}] ->
1980	    Perm2 = lists:keydelete(AppName, 1, Perm),
1981	    ets:insert(ac_tab, {T, [{AppName, Bool}|Perm2]})
1982    end.
1983
1984%%-----------------------------------------------------------------
1985%% These functions are only to be used from testsuites.
1986%%-----------------------------------------------------------------
1987test_change_apps(Apps, Conf) ->
1988    Res = test_make_apps(Apps, []),
1989    test_do_change_appl(Apps, Conf, Res).
1990
1991test_do_change_appl([], _, _) ->
1992    ok;
1993test_do_change_appl([A|Apps], [], [R|Res]) ->
1994    _ = do_change_appl(R, #appl{name = A}, []),
1995    test_do_change_appl(Apps, [], Res);
1996test_do_change_appl([A|Apps], [C|Conf], [R|Res]) ->
1997    _ = do_change_appl(R, #appl{name = A}, C),
1998    test_do_change_appl(Apps, Conf, Res).
1999
2000test_make_apps([], Res) ->
2001    lists:reverse(Res);
2002test_make_apps([A|Apps], Res) ->
2003    test_make_apps(Apps, [make_appl(A) | Res]).
2004
2005file_binary_to_list(Bin) ->
2006    Enc = case epp:read_encoding_from_binary(Bin) of
2007              none -> epp:default_encoding();
2008              Encoding -> Encoding
2009          end,
2010    case catch unicode:characters_to_list(Bin, Enc) of
2011        String when is_list(String) ->
2012            {ok, String};
2013        _ ->
2014            error
2015    end.
2016
2017%%-----------------------------------------------------------------
2018%% String conversion
2019%% Exit reason needs to be a printable string
2020%% (and of length <200, but init now does the chopping).
2021%%-----------------------------------------------------------------
2022
2023-spec to_string(term()) -> string().
2024
2025to_string(Term) ->
2026    case io_lib:printable_list(Term) of
2027	true ->
2028	    Term;
2029	false ->
2030	    lists:flatten(io_lib:format("~0p", [Term]))
2031    end.
2032