%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.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.erlang.org/EPL1_0.txt
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Original Code is Erlang-4.7.3, December, 1998.
%% 
%% The Initial Developer of the Original Code is Ericsson Telecom
%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
%% Telecom AB. All Rights Reserved.
%% 
%% Contributor(s): ______________________________________.''
%%
-module(kernel).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/free/1').
-author('mbj@erlang.ericsson.se').

-behaviour(application).
-behaviour(supervisor).

%% External exports
-export([start/2, init/1, stop/1]).

%%%-----------------------------------------------------------------
%%% The kernel is the first application started.
%%% Callback functions for the kernel application.
%%%-----------------------------------------------------------------
-record(state, {error_logger_type}).

start(_, []) ->
    Type = get_error_logger_type(),
    case supervisor:start_link({local, kernel_sup}, kernel, []) of
	{ok, Pid} ->
	    swap_error_logger(Type),
	    {ok, Pid, #state{error_logger_type = Type}};
	Error -> Error
    end.

stop(_State) ->
    ok.

get_error_logger_type() ->
    case application:get_env(kernel, error_logger) of
	{ok, false} -> undefined;
	{ok, {file, File}} when list(File) -> {file, File};
	{ok, tty} -> tty;
	{ok, Bad} -> exit({bad_config, {kernel, {error_logger, Bad}}});
	_ -> undefined
    end.

swap_error_logger(undefined) -> ok;
swap_error_logger(Type) ->
    error_logger:swap_handler(args(Type)).

args({file, File}) -> {logfile, File};
args(X) -> X.

%%%-----------------------------------------------------------------
%%% 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, ...)    os_server, ...)
%%%
%%% 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 = {one_for_all, 0, 1},
    Config = {kernel_config,
	      {kernel_config, start_link, []},
	      permanent, 2000, worker, [kernel_config]},
    Code = {code_server,
	    {code, start_link, get_code_args()},
	    permanent, 2000, worker, [code]},
    File = {file_server,
	    {file, start_link, []},
	    permanent, 2000, worker, [file]},
    User = {user,
	    {user_sup, start, []},
	    temporary, 2000, supervisor, [user_sup]},
    Rpc = {rex, {rpc, start_link, []}, permanent, 2000, worker, [rpc]},
    Global = {global_name_server, {global, start_link, []}, permanent, 2000,
	      worker, [global]},
    Glo_grp = {global_group,{global_group,start_link,[]},permanent,2000,
	       worker,[global_group]},
    InetDb = {inet_db, {inet_db, start_link, []},
	      permanent, 2000, worker, [inet_db]},
    NetSup = {net_sup, {erl_distribution, start_link, []}, permanent,
	      infinity, supervisor,[erl_distribution]},
    DistAC = start_dist_ac(),
    Ddll = start_ddll(),
    Timer = start_timer(),
    SafeSupervisor = {kernel_safe_sup,
		      {supervisor, start_link,
		       [{local, kernel_safe_sup}, ?MODULE, safe]},
		      permanent, infinity, supervisor, [?MODULE]},
    {ok, {SupFlags,
	  [Rpc, Global, InetDb | DistAC] ++ [NetSup, Glo_grp, File, Code,
	   User, Config, SafeSupervisor] ++
	  Ddll ++ Timer}};

init(safe) ->
    SupFlags = {one_for_one, 4, 3600},
    Boot = start_boot_server(),
    DiskLog = start_disk_log(),
    Pg2 = start_pg2(),
    Os = start_os(),
    {ok, {SupFlags, Boot ++ DiskLog ++ Pg2 ++ Os}}.

get_code_args() ->
    case init:get_argument(nostick) of
	{ok, [[]]} -> [[nostick]];
	_ -> []
    end.

start_dist_ac() ->
    Spec = [{dist_ac,{dist_ac,start_link,[]},permanent,2000,worker,[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_ddll() ->
    case application:get_env(kernel, start_ddll) of
	{ok, true} ->
	    [{ddll_server, {erl_ddll, start_link, []}, permanent, 1000,
	      worker, [erl_ddll]}];
	_ ->
	    []
    end.

start_boot_server() ->
    case application:get_env(kernel, start_boot_server) of
	{ok, true} ->
	    Args = get_boot_args(),
	    [{boot_server, {erl_boot_server, start_link, [Args]}, permanent,
	      1000, worker, [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} ->
	    [{disk_log_server,
	      {disk_log_server, start_link, []},
	      permanent, 2000, worker, [disk_log_server]},
	     {disk_log_sup, {disk_log_sup, start_link, []}, permanent,
	      1000, supervisor, [disk_log_sup]}];
	_ ->
	    []
    end.

start_pg2() ->
    case application:get_env(kernel, start_pg2) of
	{ok, true} ->
	    [{pg2, {pg2, start_link, []}, permanent, 1000, worker, [pg2]}];
	_ ->
	    []
    end.

start_os() ->
    case application:get_env(kernel, start_os) of
	{ok, true} ->
	    [{os_server, {os, start_link, []}, permanent, 1000, worker, [os]}];
	_ ->
	    []
    end.

start_timer() ->
    case application:get_env(kernel, start_timer) of
	{ok, true} -> 
	    [{timer_server, {timer, start_link, []}, permanent, 1000, worker, 
	      [timer]}];
	_ ->
	    []
    end.
