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(kernel).
21
22-behaviour(supervisor).
23
24%% External exports
25-export([start/2, init/1, stop/1]).
26-export([config_change/3]).
27
28%%%-----------------------------------------------------------------
29%%% The kernel is the first application started.
30%%% Callback functions for the kernel application.
31%%%-----------------------------------------------------------------
32start(_, []) ->
33    %% Setup the logger and configure the kernel logger environment
34    ok = logger:internal_init_logger(),
35    case supervisor:start_link({local, kernel_sup}, kernel, []) of
36	{ok, Pid} ->
37            ok = erl_signal_handler:start(),
38            ok = logger:add_handlers(kernel),
39            {ok, Pid, []};
40	Error -> Error
41    end.
42
43stop(_State) ->
44    ok.
45
46%%-------------------------------------------------------------------
47%% Some configuration parameters for kernel are changed
48%%-------------------------------------------------------------------
49config_change(Changed, New, Removed) ->
50    do_distribution_change(Changed, New, Removed),
51    do_global_groups_change(Changed, New, Removed),
52    ok.
53
54%%%-----------------------------------------------------------------
55%%% The process structure in kernel is as shown in the figure.
56%%%
57%%%               ---------------
58%%%              | kernel_sup (A)|
59%%%	          ---------------
60%%%                      |
61%%%        -------------------------------
62%%%       |              |                |
63%%%  <std services> -------------   -------------
64%%%   (file,code,  | erl_dist (A)| | safe_sup (1)|
65%%%    rpc, ...)    -------------   -------------
66%%%		          |               |
67%%%                  (net_kernel,  (disk_log, pg,
68%%%          	      auth, ...)     ...)
69%%%
70%%% The rectangular boxes are supervisors.  All supervisors except
71%%% for kernel_safe_sup terminates the entire erlang node if any of
72%%% their children dies.  Any child that can't be restarted in case
73%%% of failure must be placed under one of these supervisors.  Any
74%%% other child must be placed under safe_sup.  These children may
75%%% be restarted. Be aware that if a child is restarted the old state
76%%% and all data will be lost.
77%%%-----------------------------------------------------------------
78%%% Callback functions for the kernel_sup supervisor.
79%%%-----------------------------------------------------------------
80
81init([]) ->
82    SupFlags = #{strategy => one_for_all,
83                 intensity => 0,
84                 period => 1},
85
86    Config = #{id => kernel_config,
87               start => {kernel_config, start_link, []},
88               restart => permanent,
89               shutdown => 2000,
90               type => worker,
91               modules => [kernel_config]},
92
93    RefC = #{id => kernel_refc,
94             start => {kernel_refc, start_link, []},
95             restart => permanent,
96             shutdown => 2000,
97             type => worker,
98             modules => [kernel_refc]},
99
100    Code = #{id => code_server,
101             start => {code, start_link, []},
102             restart => permanent,
103             shutdown => 2000,
104             type => worker,
105             modules => [code]},
106
107    File = #{id => file_server_2,
108             start => {file_server, start_link, []},
109             restart => permanent,
110             shutdown => 2000,
111             type => worker,
112             modules => [file, file_server, file_io_server, prim_file]},
113
114    StdError = #{id => standard_error,
115                 start => {standard_error, start_link, []},
116                 restart => temporary,
117                 shutdown => 2000,
118                 type => supervisor,
119                 modules => [standard_error]},
120
121    User = #{id => user,
122             start => {user_sup, start, []},
123             restart => temporary,
124             shutdown => 2000,
125             type => supervisor,
126             modules => [user_sup]},
127
128    SafeSup = #{id => kernel_safe_sup,
129                start =>{supervisor, start_link, [{local, kernel_safe_sup}, ?MODULE, safe]},
130                restart => permanent,
131                shutdown => infinity,
132                type => supervisor,
133                modules => [?MODULE]},
134
135
136    LoggerSup = #{id => logger_sup,
137                  start => {logger_sup, start_link, []},
138                  restart => permanent,
139                  shutdown => infinity,
140                  type => supervisor,
141                  modules => [logger_sup]},
142
143    case init:get_argument(mode) of
144        {ok, [["minimal"]|_]} ->
145            {ok, {SupFlags,
146                  [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}};
147        _ ->
148            DistChildren =
149		case application:get_env(kernel, start_distribution) of
150		    {ok, false} -> [];
151		    _ -> start_distribution()
152		end,
153
154            InetDb = #{id => inet_db,
155                       start => {inet_db, start_link, []},
156                       restart => permanent,
157                       shutdown => 2000,
158                       type => worker,
159                       modules => [inet_db]},
160
161            SigSrv = #{id => erl_signal_server,
162                       start => {gen_event, start_link, [{local, erl_signal_server}]},
163                       restart => permanent,
164                       shutdown => 2000,
165                       type => worker,
166                       modules => dynamic},
167
168            Timer = start_timer(),
169            CompileServer = start_compile_server(),
170
171            {ok, {SupFlags,
172                  [Code, InetDb | DistChildren] ++
173                      [File, SigSrv, StdError, User, Config, RefC, SafeSup, LoggerSup] ++
174                      Timer ++ CompileServer}}
175    end;
176init(safe) ->
177    SupFlags = #{strategy => one_for_one,
178                 intensity => 4,
179                 period => 3600},
180
181    Boot = start_boot_server(),
182    DiskLog = start_disk_log(),
183    Pg = start_pg2() ++ start_pg(),
184
185    %% Run the on_load handlers for all modules that have been
186    %% loaded so far. Running them at this point means that
187    %% on_load handlers can safely call kernel processes
188    %% (and in particular call code:priv_dir/1 or code:lib_dir/1).
189    init:run_on_load_handlers(),
190
191    {ok, {SupFlags, Boot ++ DiskLog ++ Pg}}.
192
193start_distribution() ->
194    Rpc = #{id => rex,
195            start => {rpc, start_link, []},
196            restart => permanent,
197            shutdown => 2000,
198            type => worker,
199            modules => [rpc]},
200
201    Global = #{id => global_name_server,
202               start => {global, start_link, []},
203               restart => permanent,
204               shutdown => 2000,
205               type => worker,
206               modules => [global]},
207
208    DistAC = start_dist_ac(),
209
210    NetSup = #{id => net_sup,
211               start => {erl_distribution, start_link, []},
212               restart => permanent,
213               shutdown => infinity,
214               type => supervisor,
215               modules => [erl_distribution]},
216
217    GlGroup = #{id => global_group,
218                start => {global_group,start_link,[]},
219                restart => permanent,
220                shutdown => 2000,
221                type => worker,
222                modules => [global_group]},
223
224    [Rpc, Global | DistAC] ++ [NetSup, GlGroup].
225
226start_dist_ac() ->
227    Spec = [#{id => dist_ac,
228              start => {dist_ac,start_link,[]},
229              restart => permanent,
230              shutdown => 2000,
231              type => worker,
232              modules => [dist_ac]}],
233    case application:get_env(kernel, start_dist_ac) of
234        {ok, true} -> Spec;
235        {ok, false} -> [];
236        undefined ->
237            case application:get_env(kernel, distributed) of
238                {ok, _} -> Spec;
239                _ -> []
240            end
241    end.
242
243start_boot_server() ->
244    case application:get_env(kernel, start_boot_server) of
245        {ok, true} ->
246            Args = get_boot_args(),
247            [#{id => boot_server,
248               start => {erl_boot_server, start_link, [Args]},
249               restart => permanent,
250               shutdown => 1000,
251               type => worker,
252               modules => [erl_boot_server]}];
253        _ ->
254            []
255    end.
256
257get_boot_args() ->
258    case application:get_env(kernel, boot_server_slaves) of
259        {ok, Slaves} -> Slaves;
260        _            -> []
261    end.
262
263start_disk_log() ->
264    case application:get_env(kernel, start_disk_log) of
265        {ok, true} ->
266            [#{id => disk_log_server,
267               start => {disk_log_server, start_link, []},
268               restart => permanent,
269               shutdown => 2000,
270               type => worker,
271               modules => [disk_log_server]},
272             #{id => disk_log_sup,
273               start => {disk_log_sup, start_link, []},
274               restart => permanent,
275               shutdown => 1000,
276               type => supervisor,
277               modules => [disk_log_sup]}];
278        _ ->
279            []
280    end.
281
282start_pg() ->
283    case application:get_env(kernel, start_pg) of
284        {ok, true} ->
285            [#{id => pg,
286                start => {pg, start_link, []},
287                restart => permanent,
288                shutdown => 1000,
289                type => worker,
290                modules => [pg]}];
291        _ ->
292            []
293    end.
294
295start_pg2() ->
296    case application:get_env(kernel, start_pg2) of
297        {ok, true} ->
298            [#{id => pg2,
299               start => {pg2, start_link, []},
300               restart => permanent,
301               shutdown => 1000,
302               type => worker,
303               modules => [pg2]}];
304        _ ->
305            []
306    end.
307
308start_timer() ->
309    case application:get_env(kernel, start_timer) of
310        {ok, true} ->
311            [#{id => timer_server,
312               start => {timer, start_link, []},
313               restart => permanent,
314               shutdown => 1000,
315               type => worker,
316               modules => [timer]}];
317        _ ->
318            []
319    end.
320
321start_compile_server() ->
322    case application:get_env(kernel, start_compile_server) of
323        {ok, true} ->
324            [#{id => erl_compile_server,
325               start => {erl_compile_server, start_link, []},
326               restart => permanent,
327               shutdown => 2000,
328               type => worker,
329               modules => [erl_compile_server]}];
330        _ ->
331            []
332    end.
333
334%%-----------------------------------------------------------------
335%% The change of the distributed parameter is taken care of here
336%%-----------------------------------------------------------------
337do_distribution_change(Changed, New, Removed) ->
338    %% check if the distributed parameter is changed. It is not allowed
339    %% to make a local application to a distributed one, or vice versa.
340    case is_dist_changed(Changed, New, Removed) of
341	%%{changed, new, removed}
342	{false, false, false} ->
343	    ok;
344	{C, false, false} ->
345	    %% At last, update the parameter.
346	    gen_server:call(dist_ac, {distribution_changed, C}, infinity);
347	{false, _, false} ->
348	    error_logger:error_report("Distribution not changed: "
349				      "Not allowed to add the 'distributed' "
350				      "parameter."),
351	    {error, {distribution_not_changed, "Not allowed to add the "
352		     "'distributed' parameter"}};
353	{false, false, _} ->
354	    error_logger:error_report("Distribution not changed: "
355				      "Not allowed to remove the "
356				      "distribution parameter."),
357	    {error, {distribution_not_changed, "Not allowed to remove the "
358		     "'distributed' parameter"}}
359    end.
360
361%%-----------------------------------------------------------------
362%% Check if distribution is changed in someway.
363%%-----------------------------------------------------------------
364is_dist_changed(Changed, New, Removed) ->
365    C = case lists:keyfind(distributed, 1, Changed) of
366	    false ->
367		false;
368	    {distributed, NewDistC} ->
369		NewDistC
370	end,
371    N = case lists:keyfind(distributed, 1, New) of
372	    false ->
373		false;
374	    {distributed, NewDistN} ->
375		NewDistN
376	end,
377    R = lists:member(distributed, Removed),
378    {C, N, R}.
379
380%%-----------------------------------------------------------------
381%% The change of the global_groups parameter is taken care of here
382%%-----------------------------------------------------------------
383do_global_groups_change(Changed, New, Removed) ->
384    %% check if the global_groups parameter is changed.
385    case is_gg_changed(Changed, New, Removed) of
386	%%{changed, new, removed}
387	{false, false, false} ->
388	    ok;
389	{C, false, false} ->
390	    %% At last, update the parameter.
391	    global_group:global_groups_changed(C);
392	{false, N, false} ->
393	    global_group:global_groups_added(N);
394	{false, false, R} ->
395	    global_group:global_groups_removed(R)
396    end.
397
398%%-----------------------------------------------------------------
399%% Check if global_groups is changed in someway.
400%%-----------------------------------------------------------------
401is_gg_changed(Changed, New, Removed) ->
402    C = case lists:keyfind(global_groups, 1, Changed) of
403	    false ->
404		false;
405	    {global_groups, NewDistC} ->
406		NewDistC
407	end,
408    N = case lists:keyfind(global_groups, 1, New) of
409	    false ->
410		false;
411	    {global_groups, NewDistN} ->
412		NewDistN
413	end,
414    R = lists:member(global_groups, Removed),
415    {C, N, R}.
416