%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(kernel). -behaviour(supervisor). %% External exports -export([start/2, init/1, stop/1]). -export([config_change/3]). %%%----------------------------------------------------------------- %%% The kernel is the first application started. %%% Callback functions for the kernel application. %%%----------------------------------------------------------------- start(_, []) -> case supervisor:start_link({local, kernel_sup}, kernel, []) of {ok, Pid} -> %% add signal handler case whereis(erl_signal_server) of %% in case of minimal mode undefined -> ok; _ -> ok = gen_event:add_handler(erl_signal_server, erl_signal_handler, []) end, %% add error handler Type = get_error_logger_type(), case error_logger:swap_handler(Type) of ok -> {ok, Pid, []}; Error -> %% Not necessary since the node will crash anyway: exit(Pid, shutdown), Error end; Error -> Error end. stop(_State) -> ok. %%------------------------------------------------------------------- %% Some configuration parameters for kernel are changed %%------------------------------------------------------------------- config_change(Changed, New, Removed) -> do_distribution_change(Changed, New, Removed), do_global_groups_change(Changed, New, Removed), ok. get_error_logger_type() -> case application:get_env(kernel, error_logger) of {ok, tty} -> tty; {ok, {file, File}} when is_list(File) -> {logfile, File}; {ok, false} -> false; {ok, silent} -> silent; undefined -> tty; % default value {ok, Bad} -> exit({bad_config, {kernel, {error_logger, Bad}}}) end. %%%----------------------------------------------------------------- %%% The process structure in kernel is as shown in the figure. %%% %%% --------------- %%% | kernel_sup (A)| %%% --------------- %%% | %%% ------------------------------- %%% | | | %%% <std services> ------------- ------------- %%% (file,code, | erl_dist (A)| | safe_sup (1)| %%% rpc, ...) ------------- ------------- %%% | | %%% (net_kernel, (disk_log, pg2, %%% auth, ...) ...) %%% %%% The rectangular boxes are supervisors. All supervisors except %%% for kernel_safe_sup terminates the enitre erlang node if any of %%% their children dies. Any child that can't be restarted in case %%% of failure must be placed under one of these supervisors. Any %%% other child must be placed under safe_sup. These children may %%% be restarted. Be aware that if a child is restarted the old state %%% and all data will be lost. %%%----------------------------------------------------------------- %%% Callback functions for the kernel_sup supervisor. %%%----------------------------------------------------------------- init([]) -> SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, Config = #{id => kernel_config, start => {kernel_config, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [kernel_config]}, Code = #{id => code_server, start => {code, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [code]}, File = #{id => file_server_2, start => {file_server, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [file, file_server, file_io_server, prim_file]}, StdError = #{id => standard_error, start => {standard_error, start_link, []}, restart => temporary, shutdown => 2000, type => supervisor, modules => [user_sup]}, User = #{id => user, start => {user_sup, start, []}, restart => temporary, shutdown => 2000, type => supervisor, modules => [user_sup]}, SafeSup = #{id => kernel_safe_sup, start =>{supervisor, start_link, [{local, kernel_safe_sup}, ?MODULE, safe]}, restart => permanent, shutdown => infinity, type => supervisor, modules => [?MODULE]}, case init:get_argument(mode) of {ok, [["minimal"]]} -> {ok, {SupFlags, [Code, File, StdError, User, Config, SafeSup]}}; _ -> Rpc = #{id => rex, start => {rpc, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [rpc]}, Global = #{id => global_name_server, start => {global, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [global]}, GlGroup = #{id => global_group, start => {global_group,start_link,[]}, restart => permanent, shutdown => 2000, type => worker, modules => [global_group]}, InetDb = #{id => inet_db, start => {inet_db, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [inet_db]}, NetSup = #{id => net_sup, start => {erl_distribution, start_link, []}, restart => permanent, shutdown => infinity, type => supervisor, modules => [erl_distribution]}, SigSrv = #{id => erl_signal_server, start => {gen_event, start_link, [{local, erl_signal_server}]}, restart => permanent, shutdown => 2000, type => worker, modules => dynamic}, DistAC = start_dist_ac(), Timer = start_timer(), {ok, {SupFlags, [Code, Rpc, Global, InetDb | DistAC] ++ [NetSup, GlGroup, File, SigSrv, StdError, User, Config, SafeSup] ++ Timer}} end; init(safe) -> SupFlags = #{strategy => one_for_one, intensity => 4, period => 3600}, Boot = start_boot_server(), DiskLog = start_disk_log(), Pg2 = start_pg2(), %% Run the on_load handlers for all modules that have been %% loaded so far. Running them at this point means that %% on_load handlers can safely call kernel processes %% (and in particular call code:priv_dir/1 or code:lib_dir/1). init:run_on_load_handlers(), {ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}. start_dist_ac() -> Spec = [#{id => dist_ac, start => {dist_ac,start_link,[]}, restart => permanent, shutdown => 2000, type => worker, modules => [dist_ac]}], case application:get_env(kernel, start_dist_ac) of {ok, true} -> Spec; {ok, false} -> []; undefined -> case application:get_env(kernel, distributed) of {ok, _} -> Spec; _ -> [] end end. start_boot_server() -> case application:get_env(kernel, start_boot_server) of {ok, true} -> Args = get_boot_args(), [#{id => boot_server, start => {erl_boot_server, start_link, [Args]}, restart => permanent, shutdown => 1000, type => worker, modules => [erl_boot_server]}]; _ -> [] end. get_boot_args() -> case application:get_env(kernel, boot_server_slaves) of {ok, Slaves} -> Slaves; _ -> [] end. start_disk_log() -> case application:get_env(kernel, start_disk_log) of {ok, true} -> [#{id => disk_log_server, start => {disk_log_server, start_link, []}, restart => permanent, shutdown => 2000, type => worker, modules => [disk_log_server]}, #{id => disk_log_sup, start => {disk_log_sup, start_link, []}, restart => permanent, shutdown => 1000, type => supervisor, modules => [disk_log_sup]}]; _ -> [] end. start_pg2() -> case application:get_env(kernel, start_pg2) of {ok, true} -> [#{id => pg2, start => {pg2, start_link, []}, restart => permanent, shutdown => 1000, type => worker, modules => [pg2]}]; _ -> [] end. start_timer() -> case application:get_env(kernel, start_timer) of {ok, true} -> [#{id => timer_server, start => {timer, start_link, []}, restart => permanent, shutdown => 1000, type => worker, modules => [timer]}]; _ -> [] end. %%----------------------------------------------------------------- %% The change of the distributed parameter is taken care of here %%----------------------------------------------------------------- do_distribution_change(Changed, New, Removed) -> %% check if the distributed parameter is changed. It is not allowed %% to make a local application to a distributed one, or vice versa. case is_dist_changed(Changed, New, Removed) of %%{changed, new, removed} {false, false, false} -> ok; {C, false, false} -> %% At last, update the parameter. gen_server:call(dist_ac, {distribution_changed, C}, infinity); {false, _, false} -> error_logger:error_report("Distribution not changed: " "Not allowed to add the 'distributed' " "parameter."), {error, {distribution_not_changed, "Not allowed to add the " "'distributed' parameter"}}; {false, false, _} -> error_logger:error_report("Distribution not changed: " "Not allowed to remove the " "distribution parameter."), {error, {distribution_not_changed, "Not allowed to remove the " "'distributed' parameter"}} end. %%----------------------------------------------------------------- %% Check if distribution is changed in someway. %%----------------------------------------------------------------- is_dist_changed(Changed, New, Removed) -> C = case lists:keyfind(distributed, 1, Changed) of false -> false; {distributed, NewDistC} -> NewDistC end, N = case lists:keyfind(distributed, 1, New) of false -> false; {distributed, NewDistN} -> NewDistN end, R = lists:member(distributed, Removed), {C, N, R}. %%----------------------------------------------------------------- %% The change of the global_groups parameter is taken care of here %%----------------------------------------------------------------- do_global_groups_change(Changed, New, Removed) -> %% check if the global_groups parameter is changed. case is_gg_changed(Changed, New, Removed) of %%{changed, new, removed} {false, false, false} -> ok; {C, false, false} -> %% At last, update the parameter. global_group:global_groups_changed(C); {false, N, false} -> global_group:global_groups_added(N); {false, false, R} -> global_group:global_groups_removed(R) end. %%----------------------------------------------------------------- %% Check if global_groups is changed in someway. %%----------------------------------------------------------------- is_gg_changed(Changed, New, Removed) -> C = case lists:keyfind(global_groups, 1, Changed) of false -> false; {global_groups, NewDistC} -> NewDistC end, N = case lists:keyfind(global_groups, 1, New) of false -> false; {global_groups, NewDistN} -> NewDistN end, R = lists:member(global_groups, Removed), {C, N, R}.