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).
21
22-export([ensure_all_started/1, ensure_all_started/2, start/1, start/2,
23	 start_boot/1, start_boot/2, stop/1,
24	 load/1, load/2, unload/1, takeover/2,
25	 which_applications/0, which_applications/1,
26	 loaded_applications/0, permit/2]).
27-export([ensure_started/1, ensure_started/2]).
28-export([set_env/1, set_env/2, set_env/3, set_env/4, unset_env/2, unset_env/3]).
29-export([get_env/1, get_env/2, get_env/3, get_all_env/0, get_all_env/1]).
30-export([get_key/1, get_key/2, get_all_key/0, get_all_key/1]).
31-export([get_application/0, get_application/1, info/0]).
32-export([start_type/0]).
33
34-export_type([start_type/0]).
35
36%%%-----------------------------------------------------------------
37
38-type start_type() :: 'normal'
39                    | {'takeover', Node :: node()}
40                    | {'failover', Node :: node()}.
41-type restart_type() :: 'permanent' | 'transient' | 'temporary'.
42-type application_opt() :: {'description', Description :: string()}
43                         | {'vsn', Vsn :: string()}
44                         | {'id', Id :: string()}
45                         | {'modules', [Module :: module()]}
46                         | {'registered', Names :: [Name :: atom()]}
47                         | {'applications', [Application :: atom()]}
48                         | {'included_applications', [Application :: atom()]}
49                         | {'env', [{Par :: atom(), Val :: term()}]}
50                         | {'start_phases',
51                            [{Phase :: atom(), PhaseArgs :: term()}] | 'undefined'}
52                         | {'maxT', MaxT :: timeout()}          % max timeout
53                         | {'maxP',
54                            MaxP :: pos_integer() | 'infinity'} % max processes
55                         | {'mod', Start :: {Module :: module(), StartArgs :: term()}}.
56-type application_spec() :: {'application',
57                             Application :: atom(),
58                             AppSpecKeys :: [application_opt()]}.
59
60-type(tuple_of(_T) :: tuple()).
61
62%%------------------------------------------------------------------
63
64-callback start(StartType :: start_type(), StartArgs :: term()) ->
65    {'ok', pid()} | {'ok', pid(), State :: term()} | {'error', Reason :: term()}.
66
67-callback stop(State :: term()) ->
68    term().
69
70%%%-----------------------------------------------------------------
71%%% This module is API towards application_controller and
72%%% application_master.
73%%%-----------------------------------------------------------------
74
75-spec load(AppDescr) -> 'ok' | {'error', Reason} when
76      AppDescr :: Application | (AppSpec :: application_spec()),
77      Application :: atom(),
78      Reason :: term().
79
80load(Application) ->
81    load1(Application, []).
82
83-spec load(AppDescr, Distributed) -> 'ok' | {'error', Reason} when
84      AppDescr :: Application | (AppSpec :: application_spec()),
85      Application :: atom(),
86      Distributed :: {Application,Nodes}
87                   | {Application,Time,Nodes}
88                   | 'default',
89      Nodes :: [node() | tuple_of(node())],
90      Time :: pos_integer(),
91      Reason :: term().
92
93load(Application, DistNodes) ->
94    load1(Application, DistNodes).
95
96%% Workaround due to specs.
97load1(Application, DistNodes) ->
98    case application_controller:load_application(Application) of
99	ok when DistNodes =/= [] ->
100	    AppName = get_appl_name(Application),
101	    case dist_ac:load_application(AppName, DistNodes) of
102		ok ->
103		    ok;
104		{error, R} ->
105		    application_controller:unload_application(AppName),
106		    {error, R}
107	    end;
108	Else ->
109	    Else
110    end.
111
112-spec unload(Application) -> 'ok' | {'error', Reason} when
113      Application :: atom(),
114      Reason :: term().
115
116unload(Application) ->
117    application_controller:unload_application(Application).
118
119
120-spec ensure_all_started(Application) -> {'ok', Started} | {'error', Reason} when
121      Application :: atom(),
122      Started :: [atom()],
123      Reason :: term().
124ensure_all_started(Application) ->
125    ensure_all_started(Application, temporary).
126
127-spec ensure_all_started(Application, Type) -> {'ok', Started} | {'error', Reason} when
128      Application :: atom(),
129      Type :: restart_type(),
130      Started :: [atom()],
131      Reason :: term().
132ensure_all_started(Application, Type) ->
133    case ensure_all_started([Application], [], Type, []) of
134	{ok, Started} ->
135	    {ok, lists:reverse(Started)};
136	{error, Reason, Started} ->
137	    _ = [stop(App) || App <- Started],
138	    {error, Reason}
139    end.
140
141ensure_all_started([App | Apps], OptionalApps, Type, Started) ->
142    %% In case the app is already running, we just skip it instead
143    %% of attempting to start all of its children - which would
144    %% have already been loaded and started anyway.
145    case application_controller:is_running(App) of
146        false ->
147            case ensure_loaded(App) of
148                {ok, Name} ->
149                    case ensure_started(Name, App, Type, Started) of
150                        {ok, NewStarted} ->
151                            ensure_all_started(Apps, OptionalApps, Type, NewStarted);
152                        Error ->
153                            Error
154                    end;
155                {error, {"no such file or directory", _} = Reason} ->
156                    case lists:member(App, OptionalApps) of
157                        true ->
158                            ensure_all_started(Apps, OptionalApps, Type, Started);
159                        false ->
160                            {error, {App, Reason}, Started}
161                    end;
162                {error, Reason} ->
163                    {error, {App, Reason}, Started}
164            end;
165        true ->
166            ensure_all_started(Apps, OptionalApps, Type, Started)
167    end;
168ensure_all_started([], _OptionalApps, _Type, Started) ->
169    {ok, Started}.
170
171ensure_started(Name, App, Type, Started) ->
172    {ok, ChildApps} = get_key(Name, applications),
173    {ok, OptionalApps} = get_key(Name, optional_applications),
174
175    case ensure_all_started(ChildApps, OptionalApps, Type, Started) of
176	{ok, NewStarted} ->
177	    case application_controller:start_application(Name, Type) of
178		ok ->
179		    {ok, [App | NewStarted]};
180		{error, {already_started, App}} ->
181		    {ok, NewStarted};
182		{error, Reason} ->
183		    {error, {App, Reason}, NewStarted}
184	    end;
185	Error ->
186	    Error
187    end.
188
189-spec start(Application) -> 'ok' | {'error', Reason} when
190      Application :: atom(),
191      Reason :: term().
192
193start(Application) ->
194    start(Application, temporary).
195
196-spec start(Application, Type) -> 'ok' | {'error', Reason} when
197      Application :: atom(),
198      Type :: restart_type(),
199      Reason :: term().
200
201start(Application, RestartType) ->
202    case ensure_loaded(Application) of
203	{ok, Name} ->
204	    application_controller:start_application(Name, RestartType);
205	Error ->
206	    Error
207    end.
208
209ensure_loaded(Application) ->
210    case load(Application) of
211	ok ->
212	    {ok, get_appl_name(Application)};
213	{error, {already_loaded, Name}} ->
214	    {ok, Name};
215	Error ->
216	    Error
217    end.
218
219-spec ensure_started(Application) -> 'ok' | {'error', Reason} when
220      Application :: atom(),
221      Reason :: term().
222
223ensure_started(Application) ->
224    ensure_started(Application, temporary).
225
226-spec ensure_started(Application, Type) -> 'ok' | {'error', Reason} when
227      Application :: atom(),
228      Type :: restart_type(),
229      Reason :: term().
230
231ensure_started(Application, RestartType) ->
232    case start(Application, RestartType) of
233	ok ->
234	    ok;
235	{error, {already_started, Application}} ->
236	    ok;
237	Error ->
238	    Error
239    end.
240
241-spec start_boot(Application :: atom()) -> 'ok' | {'error', term()}.
242
243start_boot(Application) ->
244    start_boot(Application, temporary).
245
246-spec start_boot(Application :: atom(), RestartType :: restart_type()) ->
247	     'ok' | {'error', term()}.
248
249start_boot(Application, RestartType) ->
250    application_controller:start_boot_application(Application, RestartType).
251
252-spec takeover(Application, Type) -> 'ok' | {'error', Reason} when
253      Application :: atom(),
254      Type :: restart_type(),
255      Reason :: term().
256
257takeover(Application, RestartType) ->
258    dist_ac:takeover_application(Application, RestartType).
259
260-spec permit(Application, Permission) -> 'ok' | {'error', Reason} when
261      Application :: atom(),
262      Permission :: boolean(),
263      Reason :: term().
264
265permit(Application, Bool) ->
266    case Bool of
267	true -> ok;
268	false -> ok;
269	Bad -> exit({badarg, {?MODULE, permit, [Application, Bad]}})
270    end,
271    case application_controller:permit_application(Application, Bool) of
272	distributed_application ->
273	    dist_ac:permit_application(Application, Bool);
274	{distributed_application, only_loaded} ->
275	    dist_ac:permit_only_loaded_application(Application, Bool);
276	LocalResult ->
277	    LocalResult
278    end.
279
280-spec stop(Application) -> 'ok' | {'error', Reason} when
281      Application :: atom(),
282      Reason :: term().
283
284stop(Application) ->
285    application_controller:stop_application(Application).
286
287-spec which_applications() -> [{Application, Description, Vsn}] when
288      Application :: atom(),
289      Description :: string(),
290      Vsn :: string().
291
292which_applications() ->
293    application_controller:which_applications().
294
295-spec which_applications(Timeout) -> [{Application, Description, Vsn}] when
296      Timeout :: timeout(),
297      Application :: atom(),
298      Description :: string(),
299      Vsn :: string().
300
301which_applications(infinity) ->
302    application_controller:which_applications(infinity);
303which_applications(Timeout) when is_integer(Timeout), Timeout>=0 ->
304    application_controller:which_applications(Timeout).
305
306-spec loaded_applications() -> [{Application, Description, Vsn}] when
307      Application :: atom(),
308      Description :: string(),
309      Vsn :: string().
310
311loaded_applications() ->
312    application_controller:loaded_applications().
313
314-spec info() -> term().
315
316info() ->
317    application_controller:info().
318
319-spec set_env(Config) -> 'ok' when
320      Config :: [{Application, Env}],
321      Application :: atom(),
322      Env :: [{Par :: atom(), Val :: term()}].
323
324set_env(Config) when is_list(Config) ->
325    set_env(Config, []).
326
327-spec set_env(Config, Opts) -> 'ok' when
328      Config :: [{Application, Env}],
329      Application :: atom(),
330      Env :: [{Par :: atom(), Val :: term()}],
331      Opts :: [{timeout, timeout()} | {persistent, boolean()}].
332
333set_env(Config, Opts) when is_list(Config), is_list(Opts) ->
334    case application_controller:set_env(Config, Opts) of
335	ok -> ok;
336	{error, Msg} -> erlang:error({badarg, Msg}, [Config, Opts])
337    end.
338
339-spec set_env(Application, Par, Val) -> 'ok' when
340      Application :: atom(),
341      Par :: atom(),
342      Val :: term().
343
344set_env(Application, Key, Val) ->
345    application_controller:set_env(Application, Key, Val).
346
347-spec set_env(Application, Par, Val, Opts) -> 'ok' when
348      Application :: atom(),
349      Par :: atom(),
350      Val :: term(),
351      Opts :: [{timeout, timeout()} | {persistent, boolean()}].
352
353set_env(Application, Key, Val, infinity) ->
354    set_env(Application, Key, Val, [{timeout, infinity}]);
355set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 ->
356    set_env(Application, Key, Val, [{timeout, Timeout}]);
357set_env(Application, Key, Val, Opts) when is_list(Opts) ->
358    application_controller:set_env(Application, Key, Val, Opts).
359
360-spec unset_env(Application, Par) -> 'ok' when
361      Application :: atom(),
362      Par :: atom().
363
364unset_env(Application, Key) ->
365    application_controller:unset_env(Application, Key).
366
367-spec unset_env(Application, Par, Opts) -> 'ok' when
368      Application :: atom(),
369      Par :: atom(),
370      Opts :: [{timeout, timeout()} | {persistent, boolean()}].
371
372unset_env(Application, Key, infinity) ->
373    unset_env(Application, Key, [{timeout, infinity}]);
374unset_env(Application, Key, Timeout) when is_integer(Timeout), Timeout>=0 ->
375    unset_env(Application, Key, [{timeout, Timeout}]);
376unset_env(Application, Key, Opts) when is_list(Opts) ->
377    application_controller:unset_env(Application, Key, Opts).
378
379-spec get_env(Par) -> 'undefined' | {'ok', Val} when
380      Par :: atom(),
381      Val :: term().
382
383get_env(Key) ->
384    application_controller:get_pid_env(group_leader(), Key).
385
386-spec get_env(Application, Par) -> 'undefined' | {'ok', Val} when
387      Application :: atom(),
388      Par :: atom(),
389      Val :: term().
390
391get_env(Application, Key) ->
392    application_controller:get_env(Application, Key).
393
394-spec get_env(Application, Par, Def) -> Val when
395      Application :: atom(),
396      Par :: atom(),
397      Def :: term(),
398      Val :: term().
399
400get_env(Application, Key, Def) ->
401    case get_env(Application, Key) of
402    {ok, Val} ->
403        Val;
404    undefined ->
405        Def
406    end.
407
408-spec get_all_env() -> Env when
409      Env :: [{Par :: atom(), Val :: term()}].
410
411get_all_env() ->
412    application_controller:get_pid_all_env(group_leader()).
413
414-spec get_all_env(Application) -> Env when
415      Application :: atom(),
416      Env :: [{Par :: atom(), Val :: term()}].
417
418get_all_env(Application) ->
419    application_controller:get_all_env(Application).
420
421-spec get_key(Key) -> 'undefined' | {'ok', Val} when
422      Key :: atom(),
423      Val :: term().
424
425get_key(Key) ->
426    application_controller:get_pid_key(group_leader(), Key).
427
428-spec get_key(Application, Key) -> 'undefined' | {'ok', Val} when
429      Application :: atom(),
430      Key :: atom(),
431      Val :: term().
432
433get_key(Application, Key) ->
434    application_controller:get_key(Application, Key).
435
436-spec get_all_key() -> [] | {'ok', Keys} when
437      Keys :: [{Key :: atom(),Val :: term()},...].
438
439get_all_key() ->
440    application_controller:get_pid_all_key(group_leader()).
441
442-spec get_all_key(Application) -> 'undefined' | Keys when
443      Application :: atom(),
444      Keys :: {'ok', [{Key :: atom(),Val :: term()},...]}.
445
446get_all_key(Application) ->
447    application_controller:get_all_key(Application).
448
449-spec get_application() -> 'undefined' | {'ok', Application} when
450      Application :: atom().
451
452get_application() ->
453    application_controller:get_application(group_leader()).
454
455-spec get_application(PidOrModule) -> 'undefined' | {'ok', Application} when
456      PidOrModule :: (Pid :: pid()) | (Module :: module()),
457      Application :: atom().
458
459get_application(Pid) when is_pid(Pid) ->
460    case process_info(Pid, group_leader) of
461	{group_leader, Gl} ->
462	    application_controller:get_application(Gl);
463	undefined ->
464	    undefined
465    end;
466get_application(Module) when is_atom(Module) ->
467    application_controller:get_application_module(Module).
468
469-spec start_type() -> StartType | 'undefined' | 'local' when
470      StartType :: start_type().
471
472start_type() ->
473    application_controller:start_type(group_leader()).
474
475%% Internal
476get_appl_name(Name) when is_atom(Name) -> Name;
477get_appl_name({application, Name, _}) when is_atom(Name) -> Name.
478