Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update on_load handling during boot #6817

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file modified bootstrap/lib/compiler/ebin/v3_core.beam
Binary file not shown.
Binary file modified bootstrap/lib/kernel/ebin/inet.beam
Binary file not shown.
Binary file modified bootstrap/lib/kernel/ebin/kernel.beam
Binary file not shown.
Binary file modified bootstrap/lib/kernel/ebin/user_drv.beam
Binary file not shown.
Binary file modified erts/preloaded/ebin/init.beam
Binary file not shown.
27 changes: 9 additions & 18 deletions erts/preloaded/src/init.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2022. All Rights Reserved.
%% Copyright Ericsson AB 1996-2023. 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.
Expand Down Expand Up @@ -53,7 +53,7 @@
get_argument/1,script_id/0,script_name/0]).

%% for the on_load functionality; not for general use
-export([run_on_load_handlers/0, run_on_load_handlers/1]).
-export([run_on_load_handlers/0]).

%% internal exports
-export([fetch_loaded/0,ensure_loaded/1,make_permanent/2,
Expand Down Expand Up @@ -1502,26 +1502,22 @@ archive_extension() ->
%%%

run_on_load_handlers() ->
run_on_load_handlers(all).

run_on_load_handlers(Mods) when is_list(Mods); Mods =:= all ->
Ref = monitor(process, ?ON_LOAD_HANDLER),
catch ?ON_LOAD_HANDLER ! {run_on_load, self(), Ref, Mods},
_ = catch ?ON_LOAD_HANDLER ! {run_on_load, Ref},
receive
{'DOWN',Ref,process,_,noproc} ->
%% There is no on_load handler process,
%% probably because init:restart/0 has been
%% called and it is not the first time we
%% pass through here.
ok;
{'DOWN',Ref,process,_,on_load_done} ->
{'DOWN',Ref,process,_,Ref} ->
%% All on_load handlers have run succesfully
ok;
{'DOWN',Ref,process,_,Res} ->
{'DOWN',Ref,process,_,Reason} ->
%% Failure to run an on_load handler.
%% This is fatal during start-up.
exit(Res);
{reply, Ref, on_load_done} ->
ok
exit(Reason)
end.

start_on_load_handler_process() ->
Expand All @@ -1537,14 +1533,9 @@ on_load_loop(Mods, Debug0) ->
on_load_loop(Mods, Debug);
{loaded,Mod} ->
on_load_loop([Mod|Mods], Debug0);
{run_on_load, _, _, all} ->
{run_on_load, Ref} ->
run_on_load_handlers(Mods, Debug0),
exit(on_load_done);
{run_on_load, From, Ref, ModsToRun} ->
[run_on_load_handlers([Mod], Debug0)
|| Mod <- ModsToRun, lists:member(Mod, Mods)],
From ! {reply, Ref, on_load_done},
on_load_loop(Mods -- ModsToRun, Debug0)
exit(Ref)
end.

run_on_load_handlers([M|Ms], Debug) ->
Expand Down
54 changes: 35 additions & 19 deletions lib/kernel/src/kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2022. All Rights Reserved.
%% Copyright Ericsson AB 1996-2023. 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.
Expand Down Expand Up @@ -58,14 +58,19 @@ config_change(Changed, New, Removed) ->
%%% | kernel_sup (A)|
%%% ---------------
%%% |
%%% -------------------------------
%%% | | |
%%% <std services> ------------- -------------
%%% (file,code, | erl_dist (A)| | safe_sup (1)|
%%% rpc, ...) ------------- -------------
%%% | |
%%% (net_kernel, (disk_log, pg,
%%% auth, ...) ...)
%%% ------------------------------------------
%%% | | | | |
%%% <std services> | ------------- | -------------
%%% (code, [stderr], | | erl_dist (A)| | | safe_sup (1)|
%%% ...) | ------------- | -------------
%%% | | | |
%%% <on_load> ([net_sup], | (disk_log, pg,
%%% (transient) rpc, global, | ...)
%%% ...) |
%%% |
%%% <more services>
%%% (file, peer, [user],
%%% [logger], ...)
%%%
%%% The rectangular boxes are supervisors. All supervisors except
%%% for kernel_safe_sup terminates the entire erlang node if any of
Expand Down Expand Up @@ -118,6 +123,15 @@ init([]) ->
type => supervisor,
modules => [standard_error]},

OnLoad = #{id => on_load,
start =>
{proc_lib, start_link,
[?MODULE, ?FUNCTION_NAME, [on_load]]},
restart => transient,
shutdown => 2000,
type => worker,
modules => [?MODULE]},

User = #{id => user,
start => {user_sup, start, []},
restart => temporary,
Expand Down Expand Up @@ -148,7 +162,8 @@ init([]) ->
case init:get_argument(mode) of
{ok, [["minimal"]|_]} ->
{ok, {SupFlags,
[Code, File, StdError] ++ Peer ++
[Code, StdError, OnLoad] ++
[File | Peer] ++
[User, LoggerSup, Config, RefC, SafeSup]}};
_ ->
DistChildren =
Expand All @@ -175,11 +190,18 @@ init([]) ->
CompileServer = start_compile_server(),

{ok, {SupFlags,
[Code, InetDb | DistChildren] ++
[File, SigSrv, StdError] ++ Peer ++
[User, Config, RefC, SafeSup, LoggerSup] ++
[Code, StdError, OnLoad, InetDb | DistChildren] ++
[File, SigSrv | Peer] ++
[User, LoggerSup, Config, RefC, SafeSup] ++
Timer ++ CompileServer}}
end;
init(on_load) ->
%% 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 some essential kernel processes,
%% in particular call code:priv_dir/1 or code:lib_dir/1.
init:run_on_load_handlers(),
proc_lib:init_ack({ok, self()});
init(safe) ->
SupFlags = #{strategy => one_for_one,
intensity => 4,
Expand All @@ -189,12 +211,6 @@ init(safe) ->
DiskLog = start_disk_log(),
Pg = start_pg(),

%% 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 ++ Pg}}.

start_distribution() ->
Expand Down
5 changes: 1 addition & 4 deletions lib/kernel/src/user_drv.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2021. All Rights Reserved.
%% Copyright Ericsson AB 1996-2023. 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.
Expand Down Expand Up @@ -139,9 +139,6 @@ callback_mode() -> state_functions.
-spec init(arguments()) -> gen_statem:init_result(init).
init(Args) ->
process_flag(trap_exit, true),
%% When running in embedded mode we need to call prim_tty:on_load manually here
%% as the automatic call happens after user is started.
ok = init:run_on_load_handlers([prim_tty]),

IsTTY = prim_tty:isatty(stdin) =:= true andalso prim_tty:isatty(stdout) =:= true,
StartShell = maps:get(initial_shell, Args, undefined) =/= noshell,
Expand Down
35 changes: 1 addition & 34 deletions lib/ssl/src/inet_tls_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2011-2022. All Rights Reserved.
%% Copyright Ericsson AB 2011-2023. 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.
Expand Down Expand Up @@ -291,7 +291,6 @@ spawn_accept({Driver, Listen, Kernel}) ->
accept_one(Driver, Kernel, Socket) ->
Opts = setup_verify_client(Socket, get_ssl_options(server)),
KTLS = proplists:get_value(ktls, Opts, false),
wait_for_code_server(),
case
ssl:handshake(
Socket,
Expand Down Expand Up @@ -435,36 +434,6 @@ verify_client(PeerCert, valid_peer, {AllowedHosts,PeerIP} = S) ->
end.


wait_for_code_server() ->
%% This is an ugly hack. Upgrading a socket to TLS requires the
%% crypto module to be loaded. Loading the crypto module triggers
%% its on_load function, which calls code:priv_dir/1 to find the
%% directory where its NIF library is. However, distribution is
%% started earlier than the code server, so the code server is not
%% necessarily started yet, and code:priv_dir/1 might fail because
%% of that, if we receive an incoming connection on the
%% distribution port early enough.
%%
%% If the on_load function of a module fails, the module is
%% unloaded, and the function call that triggered loading it fails
%% with 'undef', which is rather confusing.
%%
%% Thus, the accept process will terminate, and be
%% restarted by ssl_dist_sup. However, it won't have any memory
%% of being asked by net_kernel to listen for incoming
%% connections. Hence, the node will believe that it's open for
%% distribution, but it actually isn't.
%%
%% So let's avoid that by waiting for the code server to start.
case whereis(code_server) of
undefined ->
timer:sleep(10),
wait_for_code_server();
Pid when is_pid(Pid) ->
init:run_on_load_handlers([crypto,asn1rt_nif]),
ok
end.

%% -------------------------------------------------------------------------

accept_connection(AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) ->
Expand Down Expand Up @@ -599,8 +568,6 @@ setup_fun(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->

-spec do_setup(_,_,_,_,_,_,_) -> no_return().
do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
wait_for_code_server(),

{Name, Address} = split_node(Driver, Node, LongOrShortNames),
ErlEpmd = net_kernel:epmd_module(),
{ARMod, ARFun} = get_address_resolver(ErlEpmd, Driver),
Expand Down