diff options
-rw-r--r-- | erts/doc/src/erl_prim_loader.xml | 42 | ||||
-rw-r--r-- | erts/preloaded/ebin/erl_prim_loader.beam | bin | 56052 -> 51424 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/erlang.beam | bin | 102012 -> 102012 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/erts_internal.beam | bin | 6260 -> 6260 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/init.beam | bin | 48592 -> 44700 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/otp_ring0.beam | bin | 1452 -> 1452 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_eval.beam | bin | 1324 -> 1324 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_file.beam | bin | 44780 -> 44892 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_inet.beam | bin | 72612 -> 72612 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_zip.beam | bin | 23284 -> 23284 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/zlib.beam | bin | 14160 -> 14160 bytes | |||
-rw-r--r-- | erts/preloaded/src/erl_prim_loader.erl | 321 | ||||
-rw-r--r-- | erts/preloaded/src/init.erl | 397 | ||||
-rw-r--r-- | erts/preloaded/src/prim_file.erl | 1 | ||||
-rw-r--r-- | lib/kernel/test/init_SUITE.erl | 4 |
15 files changed, 276 insertions, 489 deletions
diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index db4f132609..8f66e07ae1 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -50,36 +50,9 @@ <c>-loader_debug</c> are also experimental</p></warning> </description> - <datatypes> - <datatype> - <name name="host"/> - </datatype> - </datatypes> <funcs> <func> - <name name="start" arity="3"/> - <fsummary>Start the Erlang low level loader</fsummary> - <desc> - <p>Starts the Erlang low level loader. This function is called - by the <c>init</c> process (and module). The <c>init</c> - process reads the command line flags <c>-id <anno>Id</anno></c>, - <c>-loader <anno>Loader</anno></c>, and <c>-hosts <anno>Hosts</anno></c>. These are - the arguments supplied to the <c>start/3</c> function.</p> - <p>If <c>-loader</c> is not given, the default loader is - <c>efile</c> which tells the system to read from the file - system.</p> - <p>If <c>-loader</c> is <c>inet</c>, the <c>-id <anno>Id</anno></c>, - <c>-hosts <anno>Hosts</anno></c>, and <c>-setcookie Cookie</c> flags must - also be supplied. <c><anno>Hosts</anno></c> identifies hosts which this - node can contact in order to load modules. One Erlang - runtime system with a <c>erl_boot_server</c> process must be - started on each of hosts given in <c><anno>Hosts</anno></c> in order to - answer the requests. See <seealso - marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>.</p> - </desc> - </func> - <func> <name name="get_file" arity="1"/> <fsummary>Get a file</fsummary> <desc> @@ -87,8 +60,6 @@ <c><anno>Filename</anno></c> is either an absolute file name or just the name of the file, for example <c>"lists.beam"</c>. If an internal path is set to the loader, this path is used to find the file. - If a user supplied loader is used, the path can be stripped - off if it is obsolete, and the loader does not use a path. <c><anno>FullName</anno></c> is the complete name of the fetched file. <c><anno>Bin</anno></c> is the contents of the file as a binary.</p> @@ -189,17 +160,12 @@ <p>Specifies which other Erlang nodes the <c>inet</c> loader can use. This flag is mandatory if the <c>-loader inet</c> flag is present. On each host, there must be on Erlang node - with the <c>erl_boot_server</c> which handles the load - requests. <c>Hosts</c> is a list of IP addresses (hostnames + with the <seealso + marker="kernel:erl_boot_server">erl_boot_server(3)</seealso> + which handles the load requests. + <c>Hosts</c> is a list of IP addresses (hostnames are not acceptable).</p> </item> - <tag><c>-id Id</c></tag> - <item> - <p>Specifies the identity of the Erlang runtime system. If - the system runs as a distributed node, <c>Id</c> must be - identical to the name supplied with the <c>-sname</c> or - <c>-name</c> distribution flags.</p> - </item> <tag><c>-setcookie Cookie</c></tag> <item> <p>Specifies the cookie of the Erlang runtime system. This flag diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 8ccbd1e36b..6ee26b7575 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 87a48525bb..77f25653a3 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex 191dd332e2..4e1cb7f8a0 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex a78d9abc20..a44b022931 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam Binary files differindex 9cc91be343..328520844d 100644 --- a/erts/preloaded/ebin/otp_ring0.beam +++ b/erts/preloaded/ebin/otp_ring0.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex e84c8ffd2d..8d6c1927fd 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex e1b2a1c8eb..1221c513db 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 8853ae8bda..fa617f1f51 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex 563e604a1d..83e1e49974 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 304783ae37..8f654e3abf 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 9f6cba33bd..5f88029585 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -42,7 +42,7 @@ -include("inet_boot.hrl"). %% Public --export([start/3, set_path/1, get_path/0, get_file/1, get_files/2, +-export([start/0, set_path/1, get_path/0, get_file/1, list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]). %% Used by erl_boot_server @@ -64,12 +64,10 @@ -record(state, {loader :: 'efile' | 'inet', hosts = [] :: [host()], % hosts list (to boot from) - id, % not used any more? data :: 'noport' | port(), % data port etc - timeout :: timeout(), % idle timeout + timeout :: timeout(), % idle timeout %% Number of timeouts before archives are released n_timeouts :: non_neg_integer(), - multi_get = false :: boolean(), prim_state :: prim_state()}). % state for efile code loader -define(IDLE_TIMEOUT, 60000). %% tear inet connection after 1 minutes @@ -103,26 +101,13 @@ debug(#prim_state{debug = Deb}, Term) -> %%% Interface Functions. %%% -------------------------------------------------------- --spec start(Id, Loader, Hosts) -> +-spec start() -> {'ok', Pid} | {'error', What} when - Id :: term(), - Loader :: atom() | string(), - Hosts :: Host | [Host], - Host :: host(), Pid :: pid(), What :: term(). -start(Id, Pgm, Hosts) when is_atom(Hosts) -> - start(Id, Pgm, [Hosts]); -start(Id, Pgm0, Hosts) -> - Pgm = if - is_atom(Pgm0) -> - atom_to_list(Pgm0); - true -> - Pgm0 - end, +start() -> Self = self(), - Pid = spawn_link(fun() -> start_it(Pgm, Id, Self, Hosts) end), - register(erl_prim_loader, Pid), + Pid = spawn_link(fun() -> start_it(Self) end), receive {Pid,ok} -> {ok,Pid}; @@ -130,26 +115,40 @@ start(Id, Pgm0, Hosts) -> {error,Reason} end. -%% Hosts must be a list of form ['1.2.3.4' ...] -start_it("inet", Id, Pid, Hosts) -> +start_it(Parent) -> process_flag(trap_exit, true), - ?dbg(inet, {Id,Pid,Hosts}), + register(erl_prim_loader, self()), + Loader = case init:get_argument(loader) of + {ok,[[Loader0]]} -> + Loader0; + error -> + "efile" + end, + case Loader of + "efile" -> start_efile(Parent); + "inet" -> start_inet(Parent) + end. + +%% Hosts must be a list of form ['1.2.3.4' ...] +start_inet(Parent) -> + Hosts = case init:get_argument(hosts) of + {ok,[Hosts0]} -> Hosts0; + _ -> [] + end, AL = ipv4_list(Hosts), ?dbg(addresses, AL), {ok,Tcp} = find_master(AL), - init_ack(Pid), + init_ack(Parent), PS = prim_init(), State = #state {loader = inet, hosts = AL, - id = Id, data = Tcp, timeout = ?IDLE_TIMEOUT, n_timeouts = ?N_TIMEOUTS, prim_state = PS}, - loop(State, Pid, []); + loop(State, Parent, []). -start_it("efile", Id, Pid, _Hosts) -> - process_flag(trap_exit, true), +start_efile(Parent) -> {ok, Port} = prim_file:start(), %% Check that we started in a valid directory. case prim_file:get_cwd(Port) of @@ -160,20 +159,14 @@ start_it("efile", Id, Pid, _Hosts) -> erlang:display(Report), exit({error, invalid_current_directory}); _ -> - init_ack(Pid) + init_ack(Parent) end, - MultiGet = case erlang:system_info(thread_pool_size) of - 0 -> false; - _ -> true - end, PS = prim_init(), State = #state {loader = efile, - id = Id, data = Port, timeout = infinity, - multi_get = MultiGet, prim_state = PS}, - loop(State, Pid, []). + loop(State, Parent, []). init_ack(Pid) -> Pid ! {self(),ok}, @@ -198,20 +191,6 @@ get_file(File) when is_atom(File) -> get_file(File) -> check_file_result(get_file, File, request({get_file,File})). --spec get_files([{atom(), string()}], - fun((atom(),binary(),string()) -> 'ok' | {'error', atom()})) -> - 'ok' | {'error', atom()}. -get_files(ModFiles, Fun) -> - case request({get_files,{ModFiles,Fun}}) of - E = {error,_M} -> - E; - {error,Reason,M} -> - check_file_result(get_files, M, {error,Reason}), - {error,M}; - ok -> - ok - end. - -spec list_dir(Dir) -> {'ok', Filenames} | 'error' when Dir :: string(), Filenames :: [Filename :: string()]. @@ -310,76 +289,59 @@ check_file_result(_, _, Other) -> %%% The main loop. %%% -------------------------------------------------------- -loop(State, Parent, Paths) -> +loop(St0, Parent, Paths) -> receive + {Pid,{set_path,NewPaths}} when is_pid(Pid) -> + Pid ! {self(),ok}, + loop(St0, Parent, to_strs(NewPaths)); {Pid,Req} when is_pid(Pid) -> - %% erlang:display(Req), - {Resp,State2,Paths2} = - case Req of - {set_path,NewPaths} -> - {ok,State,to_strs(NewPaths)}; - {get_path,_} -> - {{ok,Paths},State,Paths}; - {get_file,File} -> - {Res,State1} = handle_get_file(State, Paths, File), - {Res,State1,Paths}; - {get_files,{ModFiles,Fun}} -> - {Res,State1} = handle_get_files(State, ModFiles, Paths, Fun), - {Res,State1,Paths}; - {list_dir,Dir} -> - {Res,State1} = handle_list_dir(State, Dir), - {Res,State1,Paths}; - {read_file_info,File} -> - {Res,State1} = handle_read_file_info(State, File), - {Res,State1,Paths}; - {read_link_info,File} -> - {Res,State1} = handle_read_link_info(State, File), - {Res,State1,Paths}; - {get_cwd,[]} -> - {Res,State1} = handle_get_cwd(State, []), - {Res,State1,Paths}; - {get_cwd,[_]=Args} -> - {Res,State1} = handle_get_cwd(State, Args), - {Res,State1,Paths}; - {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} -> - {Res,State1} = - handle_set_primary_archive(State, File, - ArchiveBin, FileInfo, - ParserFun), - {Res,State1,Paths}; - release_archives -> - {Res,State1} = handle_release_archives(State), - {Res,State1,Paths}; - _Other -> - {ignore,State,Paths} - end, - if Resp =:= ignore -> ok; - true -> Pid ! {self(),Resp}, ok - end, - if - is_record(State2, state) -> - loop(State2, Parent, Paths2); - true -> - exit({bad_state, Req, State2}) + case handle_request(Req, Paths, St0) of + ignore -> + ok; + {Resp,#state{}=St1} -> + Pid ! {self(),Resp}, + loop(St1, Parent, Paths); + {_,State2,_} -> + exit({bad_state,Req,State2}) end; {'EXIT',Parent,W} -> - _State1 = handle_stop(State), + _ = handle_stop(St0), exit(W); {'EXIT',P,W} -> - State1 = handle_exit(State, P, W), - loop(State1, Parent, Paths); + St1 = handle_exit(St0, P, W), + loop(St1, Parent, Paths); _Message -> - loop(State, Parent, Paths) - after State#state.timeout -> - State1 = handle_timeout(State, Parent), - loop(State1, Parent, Paths) + loop(St0, Parent, Paths) + after St0#state.timeout -> + St1 = handle_timeout(St0, Parent), + loop(St1, Parent, Paths) + end. + +handle_request(Req, Paths, St0) -> + case Req of + {get_path,_} -> + {{ok,Paths},St0}; + {get_file,File} -> + handle_get_file(St0, Paths, File); + {list_dir,Dir} -> + handle_list_dir(St0, Dir); + {read_file_info,File} -> + handle_read_file_info(St0, File); + {read_link_info,File} -> + handle_read_link_info(St0, File); + {get_cwd,[]} -> + handle_get_cwd(St0, []); + {get_cwd,[_]=Args} -> + handle_get_cwd(St0, Args); + {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} -> + handle_set_primary_archive(St0, File, ArchiveBin, + FileInfo, ParserFun); + release_archives -> + handle_release_archives(St0); + _ -> + ignore end. -handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) -> - ?SAFE2(efile_multi_get_file_from_port(State, ModFiles, Paths, Fun), State); -handle_get_files(State, _ModFiles, _Paths, _Fun) -> % no multi get - {{error,no_multi_get},State}. - handle_get_file(State = #state{loader = efile}, Paths, File) -> ?SAFE2(efile_get_file_from_port(State, File, Paths), State); handle_get_file(State = #state{loader = inet}, Paths, File) -> @@ -430,53 +392,6 @@ handle_timeout(State = #state{loader = inet}, Parent) -> %%% Functions which handle efile as prim_loader (default). %%% -------------------------------------------------------- -%%% Reading many files in parallel is an optimization. -%%% See also comment in init.erl. - -%% -> {ok,State} | {{error,Module},State} | {{error,Reason,Module},State} -efile_multi_get_file_from_port(State, ModFiles, Paths, Fun) -> - Ref = make_ref(), - %% More than 200 processes is no gain. - Max = erlang:min(200, erlang:system_info(thread_pool_size)), - efile_multi_get_file_from_port2(ModFiles, 0, Max, State, Paths, Fun, Ref, ok). - -efile_multi_get_file_from_port2([MF | MFs], Out, Max, State, Paths, Fun, Ref, Ret) when Out < Max -> - Self = self(), - _Pid = spawn(fun() -> efile_par_get_file(Ref, State, MF, Paths, Self, Fun) end), - efile_multi_get_file_from_port2(MFs, Out+1, Max, State, Paths, Fun, Ref, Ret); -efile_multi_get_file_from_port2(MFs, Out, Max, _State, Paths, Fun, Ref, Ret) when Out > 0 -> - receive - {Ref, ok, State1} -> - efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Ret); - {Ref, {error,_Mod} = Error, State1} -> - efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Error); - {Ref, MF, {error,emfile,State1}} -> - %% Max can take negative values. Out cannot. - efile_multi_get_file_from_port2([MF | MFs], Out-1, Max-1, State1, Paths, Fun, Ref, Ret); - {Ref, {M,_F}, {error,Error,State1}} -> - efile_multi_get_file_from_port2(MFs, Out-1, 0, State1, Paths, Fun, Ref, {error,Error,M}) - end; -efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) -> - {Ret,State}. - -efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) -> - %% One port for each file read in "parallel": - case prim_file:start() of - {ok, Port} -> - Port0 = State#state.data, - State1 = State#state{data = Port}, - R = case efile_get_file_from_port(State1, File, Paths) of - {{error,Reason},State2} -> - {Ref,MF,{error,Reason,State2}}; - {{ok,BinFile,Full},State2} -> - %% Fun(...) -> ok | {error,Mod} - {Ref,Fun(Mod, BinFile, Full),State2#state{data=Port0}} - end, - prim_file:close(Port), - Pid ! R; - {error, Error} -> - Pid ! {Ref,MF,{error,Error,State}} - end. %% -> {{ok,BinFile,File},State} | {{error,Reason},State} efile_get_file_from_port(State, File, Paths) -> @@ -1287,70 +1202,62 @@ path_join([Path],Acc) -> path_join([Path|Paths],Acc) -> path_join(Paths,"/" ++ reverse(Path) ++ Acc). -name_split(ArchiveFile, File0) -> - File = absname(File0), - do_name_split(ArchiveFile, File). - -do_name_split(undefined, File) -> +name_split(undefined, File) -> %% Ignore primary archive - case string_split(File, init:archive_extension(), []) of + RevExt = reverse(init:archive_extension()), + case archive_split(File, RevExt, []) of no_split -> - %% Plain file {file, File}; - {split, _RevArchiveBase, RevArchiveFile, []} -> - %% Top dir in archive - ArchiveFile = reverse(RevArchiveFile), - {archive, ArchiveFile, []}; - {split, _RevArchiveBase, RevArchiveFile, [$/ | FileInArchive]} -> - %% File in archive - ArchiveFile = reverse(RevArchiveFile), - {archive, ArchiveFile, FileInArchive}; - {split, _RevArchiveBase, _RevArchiveFile, _FileInArchive} -> - %% False match. Assume plain file - {file, File} + Archive -> + Archive end; -do_name_split(ArchiveFile, File) -> +name_split(ArchiveFile, File0) -> %% Look first in primary archive - case string_match(real_path(File), ArchiveFile, []) of + File = absname(File0), + case string_match(real_path(File), ArchiveFile) of no_match -> %% Archive or plain file - do_name_split(undefined, File); - {match, _RevPrimArchiveFile, FileInArchive} -> + name_split(undefined, File); + {match, FileInArchive} -> %% Primary archive {archive, ArchiveFile, FileInArchive} end. -string_match([Char | File], [Char | Archive], RevTop) -> - string_match(File, Archive, [Char | RevTop]); -string_match([] = File, [], RevTop) -> - {match, RevTop, File}; -string_match([$/ | File], [], RevTop) -> - {match, RevTop, File}; -string_match(_File, _Archive, _RevTop) -> +string_match([Char | File], [Char | Archive]) -> + string_match(File, Archive); +string_match([] = File, []) -> + {match, File}; +string_match([$/ | File], []) -> + {match, File}; +string_match(_File, _Archive) -> no_match. -string_split([Char | File], [Char | Ext] = FullExt, RevTop) -> - RevTop2 = [Char | RevTop], - string_split2(File, Ext, RevTop, RevTop2, File, FullExt, RevTop2); -string_split([Char | File], Ext, RevTop) -> - string_split(File, Ext, [Char | RevTop]); -string_split([], _Ext, _RevTop) -> - no_split. - -string_split2([Char | File], [Char | Ext], RevBase, RevTop, SaveFile, SaveExt, SaveTop) -> - string_split2(File, Ext, RevBase, [Char | RevTop], SaveFile, SaveExt, SaveTop); -string_split2(File, [], RevBase, RevTop, _SaveFile, _SaveExt, _SaveTop) -> - {split, RevBase, RevTop, File}; -string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) -> - string_split(SaveFile, SaveExt, SaveTop). +archive_split("/"++File, RevExt, Acc) -> + case is_prefix(RevExt, Acc) of + false -> + archive_split(File, RevExt, [$/|Acc]); + true -> + ArchiveFile = absname(reverse(Acc)), + {archive, ArchiveFile, File} + end; +archive_split([H|T], RevExt, Acc) -> + archive_split(T, RevExt, [H|Acc]); +archive_split([], RevExt, Acc) -> + case is_prefix(RevExt, Acc) of + false -> + no_split; + true -> + ArchiveFile = absname(reverse(Acc)), + {archive, ArchiveFile, []} + end. + +is_prefix([H|T1], [H|T2]) -> is_prefix(T1, T2); +is_prefix([_|_], _) -> false; +is_prefix([], _ ) -> true. %% Parse list of ipv4 addresses ipv4_list([H | T]) -> - IPV = if is_atom(H) -> ipv4_address(atom_to_list(H)); - is_list(H) -> ipv4_address(H); - true -> {error,einal} - end, - case IPV of + case ipv4_address(H) of {ok,IP} -> [IP | ipv4_list(T)]; _ -> ipv4_list(T) end; @@ -1415,8 +1322,6 @@ absname_vr([Drive, $\: | NameRest], _) -> %% Assumes normalized name pathtype(Name) when is_list(Name) -> case erlang:system_info(os_type) of - {ose, _} -> - unix_pathtype(Name); {unix, _} -> unix_pathtype(Name); {win32, _} -> diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 0ad5824ad1..383c4a1ec6 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -23,7 +23,6 @@ %% a local file or distributed from another erlang node. %% %% Flags: -%% -id Identity : identity of the system. %% -boot File : Absolute file name of the boot script. %% -boot_var Var Value %% : $Var in the boot script is expanded to @@ -75,6 +74,19 @@ subscribed = []}). -type state() :: #state{}. +%% Data for eval_script/2. +-record(es, + {init, + debug, + path, + pa, + pz, + path_choice, + prim_load, + load_mode, + vars + }). + -define(ON_LOAD_HANDLER, init__boot__on_load_handler). debug(false, _) -> ok; @@ -169,8 +181,7 @@ boot(BootArgs) -> process_flag(trap_exit, true), {Start0,Flags,Args} = parse_boot_args(BootArgs), Start = map(fun prepare_run_args/1, Start0), - Flags0 = flags_to_atoms_again(Flags), - boot(Start,Flags0,Args). + boot(Start, Flags, Args). prepare_run_args({eval, [Expr]}) -> {eval,Expr}; @@ -202,16 +213,6 @@ map(_F, []) -> map(F, [X|Rest]) -> [F(X) | map(F, Rest)]. -flags_to_atoms_again([]) -> - []; -flags_to_atoms_again([{F0,L0}|Rest]) -> - L = L0, - F = b2a(F0), - [{F,L}|flags_to_atoms_again(Rest)]; -flags_to_atoms_again([{F0}|Rest]) -> - F = b2a(F0), - [{F}|flags_to_atoms_again(Rest)]. - -spec code_path_choice() -> 'relaxed' | 'strict'. code_path_choice() -> case get_argument(code_path_choice) of @@ -337,7 +338,7 @@ boot_loop(BootPid, State) -> end. ensure_loaded(Module, Loaded) -> - File = concat([Module,objfile_extension()]), + File = atom_to_list(Module) ++ objfile_extension(), case catch load_mod(Module,File) of {ok, FullName} -> {{module, Module}, [{Module, FullName}|Loaded]}; @@ -451,9 +452,9 @@ do_handle_msg(Msg,State) -> %%% ------------------------------------------------- make_permanent(Boot,Config,Flags0,State) -> - case set_flag('-boot',Boot,Flags0) of + case set_flag(boot, Boot, Flags0) of {ok,Flags1} -> - case set_flag('-config',Config,Flags1) of + case set_flag(config, Config, Flags1) of {ok,Flags} -> {ok,State#state{flags = Flags}}; Error -> @@ -691,17 +692,15 @@ sleep(T) -> receive after T -> ok end. %%% The loader shall run for ever! %%% ------------------------------------------------- -start_prim_loader(Init,Id,Pgm,Nodes,Path,{Pa,Pz}) -> - case erl_prim_loader:start(Id,Pgm,Nodes) of - {ok,Pid} when Path =:= false -> - InitPath = append(Pa,["."|Pz]), - erl_prim_loader:set_path(InitPath), - add_to_kernel(Init,Pid), - Pid; +start_prim_loader(Init, Path0, {Pa,Pz}) -> + Path = case Path0 of + false -> Pa ++ ["."|Pz]; + _ -> Path0 + end, + case erl_prim_loader:start() of {ok,Pid} -> erl_prim_loader:set_path(Path), - add_to_kernel(Init,Pid), - Pid; + add_to_kernel(Init, Pid); {error,Reason} -> erlang:display({"cannot start loader",Reason}), exit(Reason) @@ -715,13 +714,6 @@ add_to_kernel(Init,Pid) -> ok end. -prim_load_flags(Flags) -> - PortPgm = get_flag('-loader',Flags,<<"efile">>), - Hosts = get_flag_list('-hosts', Flags, []), - Id = get_flag('-id',Flags,none), - Path = get_flag_list('-path',Flags,false), - {PortPgm, Hosts, Id, Path}. - %%% ------------------------------------------------- %%% The boot process fetches a boot script and loads %%% all modules specified and starts spec. processes. @@ -734,24 +726,23 @@ do_boot(Flags,Start) -> do_boot(Init,Flags,Start) -> process_flag(trap_exit,true), - {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags), - Root = b2s(get_flag('-root',Flags)), - PathFls = path_flags(Flags), - Pgm = b2s(Pgm0), - _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes), - bs2ss(Path),PathFls), + Root = get_root(Flags), + Path = get_flag_list(path, Flags, false), + {Pa,Pz} = PathFls = path_flags(Flags), + start_prim_loader(Init, bs2ss(Path), PathFls), BootFile = bootfile(Flags,Root), BootList = get_boot(BootFile,Root), - LoadMode = b2a(get_flag('-mode',Flags,false)), - Deb = b2a(get_flag('-init_debug',Flags,false)), + LoadMode = b2a(get_flag(mode, Flags, false)), + Deb = b2a(get_flag(init_debug, Flags, false)), catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, - BootVars = get_flag_args('-boot_var',Flags), - ParallelLoad = - (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0), + BootVars = get_boot_vars(Root, Flags), PathChoice = code_path_choice(), - eval_script(BootList,Init,PathFls,{Root,BootVars},Path, - {true,LoadMode,ParallelLoad},Deb,PathChoice), + Es = #es{init=Init,debug=Deb,path=Path,pa=Pa,pz=Pz, + path_choice=PathChoice, + prim_load=true,load_mode=LoadMode, + vars=BootVars}, + eval_script(BootList, Es), %% To help identifying Purify windows that pop up, %% print the node name into the Purify log. @@ -759,21 +750,43 @@ do_boot(Init,Flags,Start) -> start_em(Start). +get_root(Flags) -> + case get_argument(root, Flags) of + {ok,[[Root]]} -> + Root; + _ -> + exit(no_or_multiple_root_variables) + end. + +get_boot_vars(Root, Flags) -> + BootVars = get_boot_vars_1(#{}, Flags), + RootKey = <<"ROOT">>, + BootVars#{RootKey=>Root}. + +get_boot_vars_1(Vars, [{boot_var,[Key,Value]}|T]) -> + get_boot_vars_1(Vars#{Key=>Value}, T); +get_boot_vars_1(_, [{boot_var,_}|_]) -> + exit(invalid_boot_var_argument); +get_boot_vars_1(Vars, [_|T]) -> + get_boot_vars_1(Vars, T); +get_boot_vars_1(Vars, []) -> + Vars. + bootfile(Flags,Root) -> - b2s(get_flag('-boot',Flags,concat([Root,"/bin/start"]))). + b2s(get_flag(boot, Flags, Root++"/bin/start")). path_flags(Flags) -> - Pa = append(reverse(get_flag_args('-pa',Flags))), - Pz = append(get_flag_args('-pz',Flags)), + Pa = append(reverse(get_flag_args(pa, Flags))), + Pz = append(get_flag_args(pz, Flags)), {bs2ss(Pa),bs2ss(Pz)}. get_boot(BootFile0,Root) -> - BootFile = concat([BootFile0,".boot"]), + BootFile = BootFile0 ++ ".boot", case get_boot(BootFile) of {ok, CmdList} -> CmdList; not_found -> %% Check for default. - BootF = concat([Root,"/bin/",BootFile]), + BootF = Root ++ "/bin/" ++ BootFile, case get_boot(BootF) of {ok, CmdList} -> CmdList; @@ -807,93 +820,67 @@ get_boot(BootFile) -> %% boot process hangs (we want to ensure syncronicity). %% -eval_script([{progress,Info}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> - debug(Deb,{progress,Info}), +eval_script([{progress,Info}=Progress|T], #es{debug=Deb}=Es) -> + debug(Deb, Progress), init ! {self(),progress,Info}, - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{preLoaded,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{path,Path}|CfgL],Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice) -> + eval_script(T, Es); +eval_script([{preLoaded,_}|T], #es{}=Es) -> + eval_script(T, Es); +eval_script([{path,Path}|T], #es{path=false,pa=Pa,pz=Pz, + path_choice=PathChoice, + vars=Vars}=Es) -> RealPath0 = make_path(Pa, Pz, Path, Vars), RealPath = patch_path(RealPath0, PathChoice), erl_prim_loader:set_path(RealPath), - eval_script(CfgL,Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice); -eval_script([{path,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> + eval_script(T, Es); +eval_script([{path,_}|T], #es{}=Es) -> %% Ignore, use the command line -path flag. - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,embedded,Par},Deb,PathChoice) -> - eval_script(CfgL,Init,PathFs,Vars,P,{true,embedded,Par},Deb,PathChoice); -eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,E,Par},Deb,PathChoice) -> - eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice); -eval_script([{primLoad,Mods}|CfgL],Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice) + eval_script(T, Es); +eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) -> + Es = case Mode of + embedded -> Es0; + _ -> Es0#es{prim_load=false} + end, + eval_script(T, Es); +eval_script([{primLoad,[Mod]}|T], #es{prim_load=true}=Es) -> + %% Common special case (loading of error_handler). Nothing + %% to gain by parallel loading. + File = atom_to_list(Mod) ++ objfile_extension(), + {ok,Full} = load_mod(Mod, File), + init ! {self(),loaded,{Mod,Full}}, % Tell init about loaded module + eval_script(T, Es); +eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad}=Es) when is_list(Mods) -> - if - Par =:= true -> - par_load_modules(Mods,Init); + case PrimLoad of true -> - load_modules(Mods) + load_modules(Mods, Init); + false -> + %% Do not load now, code_server does that dynamically! + ok end, - eval_script(CfgL,Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice); -eval_script([{primLoad,_Mods}|CfgL],Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice) -> - %% Do not load now, code_server does that dynamically! - eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice); -eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|CfgL],Init, - PathFs,Vars,P,Ph,Deb,PathChoice) -> - debug(Deb,{start,Server}), - start_in_kernel(Server,Mod,Fun,Args,Init), - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([{apply,{Mod,Fun,Args}}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) -> - debug(Deb,{apply,{Mod,Fun,Args}}), - apply(Mod,Fun,Args), - eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice); -eval_script([],_,_,_,_,_,_,_) -> + eval_script(T, Es); +eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|T], + #es{init=Init,debug=Deb}=Es) -> + debug(Deb, {start,Server}), + start_in_kernel(Server, Mod, Fun, Args, Init), + eval_script(T, Es); +eval_script([{apply,{Mod,Fun,Args}}=Apply|T], #es{debug=Deb}=Es) -> + debug(Deb, Apply), + apply(Mod, Fun, Args), + eval_script(T, Es); +eval_script([], #es{}) -> ok; -eval_script(What,_,_,_,_,_,_,_) -> +eval_script(What, #es{}) -> exit({'unexpected command in bootfile',What}). -load_modules([Mod|Mods]) -> - File = concat([Mod,objfile_extension()]), +load_modules([Mod|Mods], Init) -> + File = atom_to_list(Mod) ++ objfile_extension(), {ok,Full} = load_mod(Mod,File), - init ! {self(),loaded,{Mod,Full}}, %% Tell init about loaded module - load_modules(Mods); -load_modules([]) -> + Init ! {self(),loaded,{Mod,Full}}, %Tell init about loaded module + load_modules(Mods, Init); +load_modules([], _) -> ok. -%%% An optimization: erl_prim_loader gets the chance of loading many -%%% files in parallel, using threads. This will reduce the seek times, -%%% and loaded code can be processed while other threads are waiting -%%% for the disk. The optimization is not tried unless the loader is -%%% "efile" and there is a non-empty pool of threads. -%%% -%%% Many threads are needed to get a good result, so it would be -%%% beneficial to load several applications in parallel. However, -%%% measurements show that the file system handles one directory at a -%%% time, regardless if parallel threads are created for files on -%%% several directories (a guess: writing the meta information when -%%% the file was last read ('mtime'), forces the file system to sync -%%% between directories). - -par_load_modules(Mods,Init) -> - Ext = objfile_extension(), - ModFiles = [{Mod,concat([Mod,Ext])} || Mod <- Mods, - not erlang:module_loaded(Mod)], - Self = self(), - Fun = fun(Mod, BinCode, FullName) -> - case catch load_mod_code(Mod, BinCode, FullName) of - {ok, _} -> - Init ! {Self,loaded,{Mod,FullName}}, - ok; - _EXIT -> - {error, Mod} - end - end, - case erl_prim_loader:get_files(ModFiles, Fun) of - ok -> - ok; - {error,Mod} -> - exit({'cannot load',Mod,get_files}) - end. - make_path(Pa, Pz, Path, Vars) -> append([Pa,append([fix_path(Path,Vars),Pz])]). @@ -908,34 +895,25 @@ fix_path([Path|Ps], Vars) -> fix_path(_, _) -> []. -add_var("$ROOT/" ++ Path, {Root,_}) -> - concat([Root, "/", Path]); -add_var([$$|Path0], {_,VarList}) -> - {Var,Path} = extract_var(Path0,[]), - Value = b2s(get_var_value(list_to_binary(Var),VarList)), - concat([Value, "/", Path]); -add_var(Path, _) -> +add_var("$"++Path0, Vars) -> + {Var,Path} = extract_var(Path0, []), + Key = list_to_binary(Var), + case Vars of + #{Key:=Value0} -> + Value = b2s(Value0), + Value ++ "/" ++ Path; + _ -> + Error0 = "cannot expand $" ++ Var ++ " in bootfile", + Error = list_to_atom(Error0), + exit(Error) + end; +add_var(Path, _) -> Path. extract_var([$/|Path],Var) -> {reverse(Var),Path}; extract_var([H|T],Var) -> extract_var(T,[H|Var]); extract_var([],Var) -> {reverse(Var),[]}. -%% get_var_value(Var, [Vars]) where Vars == [atom()] -get_var_value(Var,[Vars|VarList]) -> - case get_var_val(Var,Vars) of - {ok, Value} -> - Value; - _ -> - get_var_value(Var,VarList) - end; -get_var_value(Var,[]) -> - exit(list_to_atom(concat(["cannot expand \$", Var, " in bootfile"]))). - -get_var_val(Var,[Var,Value|_]) -> {ok, Value}; -get_var_val(Var,[_,_|Vars]) -> get_var_val(Var,Vars); -get_var_val(_,_) -> false. - patch_path(Dirs, strict) -> Dirs; patch_path(Dirs, relaxed) -> @@ -1049,18 +1027,10 @@ start_it({eval,Bin}) -> {value, _Value, _Bs} = erl_eval:exprs(Expr, erl_eval:new_bindings()), ok; start_it([_|_]=MFA) -> - Ref = make_ref(), - case catch {Ref,case MFA of - [M] -> M:start(); - [M,F] -> M:F(); - [M,F|Args] -> M:F(Args) % Args is a list - end} of - {Ref,R} -> - R; - {'EXIT',Reason} -> - exit(Reason); - Other -> - throw(Other) + case MFA of + [M] -> M:start(); + [M,F] -> M:F(); + [M,F|Args] -> M:F(Args) % Args is a list end. %% @@ -1102,7 +1072,7 @@ load_mod_code(Mod, BinCode, FullName) -> %% -------------------------------------------------------- shutdown_timer(Flags) -> - case get_flag('-shutdown_time',Flags,infinity) of + case get_flag(shutdown_time, Flags, infinity) of infinity -> self(); Time -> @@ -1152,14 +1122,10 @@ parse_boot_args([B|Bs], Ss, Fs, As) -> eval_arg -> {Expr,Rest} = get_args(Bs, []), parse_boot_args(Rest, [{eval, Expr}|Ss], Fs, As); - flag -> + {flag,A} -> {F,Rest} = get_args(Bs, []), - Fl = case F of - [] -> [B]; - FF -> [B,FF] - end, - parse_boot_args(Rest, Ss, - [list_to_tuple(Fl)|Fs], As); + Fl = {A,F}, + parse_boot_args(Rest, Ss, [Fl|Fs], As); arg -> parse_boot_args(Bs, Ss, Fs, [B|As]); end_args -> @@ -1173,12 +1139,8 @@ check(<<"-s">>) -> start_arg; check(<<"-run">>) -> start_arg2; check(<<"-eval">>) -> eval_arg; check(<<"--">>) -> end_args; -check(X) when is_binary(X) -> - case binary_to_list(X) of - [$-|_Rest] -> flag; - _Chars -> arg %Even empty atoms - end; -check(_X) -> arg. %This should never occur +check(<<"-",Flag/binary>>) -> {flag,b2a(Flag)}; +check(_) -> arg. get_args([B|Bs], As) -> case check(B) of @@ -1187,7 +1149,7 @@ get_args([B|Bs], As) -> start_arg2 -> {reverse(As), [B|Bs]}; eval_arg -> {reverse(As), [B|Bs]}; end_args -> {reverse(As), Bs}; - flag -> {reverse(As), [B|Bs]}; + {flag,_} -> {reverse(As), [B|Bs]}; arg -> get_args(Bs, [B|As]) end; @@ -1199,44 +1161,28 @@ get_args([], As) -> {reverse(As),[]}. %% atom() if a single arg was given. %% list(atom()) if several args were given. %% -get_flag(F,Flags,Default) -> - case catch get_flag(F,Flags) of - {'EXIT',_} -> - Default; - Value -> - Value - end. - -get_flag(F,Flags) -> - case search(F,Flags) of - {value,{F,[V]}} -> +get_flag(F, Flags, Default) -> + case lists:keyfind(F, 1, Flags) of + {F,[]} -> + true; + {F,[V]} -> V; - {value,{F,V}} -> + {F,V} -> V; - {value,{F}} -> % Flag given! - true; _ -> - exit(list_to_atom(concat(["no ",F," flag"]))) + Default end. %% %% Internal get_flag function, with default value. %% Return: list(atom()) %% -get_flag_list(F,Flags,Default) -> - case catch get_flag_list(F,Flags) of - {'EXIT',_} -> - Default; - Value -> - Value - end. - -get_flag_list(F,Flags) -> - case search(F,Flags) of - {value,{F,V}} -> +get_flag_list(F, Flags, Default) -> + case lists:keyfind(F, 1, Flags) of + {F,[_|_]=V} -> V; _ -> - exit(list_to_atom(concat(["no ",F," flag"]))) + Default end. %% @@ -1246,21 +1192,15 @@ get_flag_list(F,Flags) -> %% get_flag_args(F,Flags) -> get_flag_args(F,Flags,[]). -get_flag_args(F,[{F,V}|Flags],Acc) when is_list(V) -> - get_flag_args(F,Flags,[V|Acc]); get_flag_args(F,[{F,V}|Flags],Acc) -> - get_flag_args(F,Flags,[[V]|Acc]); + get_flag_args(F,Flags,[V|Acc]); get_flag_args(F,[_|Flags],Acc) -> get_flag_args(F,Flags,Acc); get_flag_args(_,[],Acc) -> reverse(Acc). get_arguments([{F,V}|Flags]) -> - [$-|Fl] = atom_to_list(F), - [{list_to_atom(Fl),to_strings(V)}|get_arguments(Flags)]; -get_arguments([{F}|Flags]) -> - [$-|Fl] = atom_to_list(F), - [{list_to_atom(Fl),[]}|get_arguments(Flags)]; + [{F,to_strings(V)}|get_arguments(Flags)]; get_arguments([]) -> []. @@ -1268,44 +1208,26 @@ to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)]; to_strings([H|T]) when is_binary(H) -> [b2s(H)|to_strings(T)]; to_strings([]) -> []. -get_argument(Arg,Flags) -> - Args = get_arguments(Flags), - case get_argument1(Arg,Args) of - [] -> - error; - Value -> - {ok,Value} +get_argument(Arg, Flags) -> + case get_argument1(Arg, Flags) of + [] -> error; + Value -> {ok,Value} end. -get_argument1(Arg,[{Arg,V}|Args]) -> - [V|get_argument1(Arg,Args)]; -get_argument1(Arg,[_|Args]) -> - get_argument1(Arg,Args); -get_argument1(_,[]) -> +get_argument1(Arg, [{Arg,V}|Args]) -> + [to_strings(V)|get_argument1(Arg, Args)]; +get_argument1(Arg, [_|Args]) -> + get_argument1(Arg, Args); +get_argument1(_, []) -> []. set_argument([{Flag,_}|Flags],Flag,Value) -> [{Flag,[Value]}|Flags]; -set_argument([{Flag}|Flags],Flag,Value) -> - [{Flag,[Value]}|Flags]; set_argument([Item|Flags],Flag,Value) -> [Item|set_argument(Flags,Flag,Value)]; set_argument([],Flag,Value) -> [{Flag,[Value]}]. -concat([A|T]) when is_atom(A) -> - atom_to_list(A) ++ concat(T); -concat([C|T]) when is_integer(C), 0 =< C, C =< 255 -> - [C|concat(T)]; -concat([Bin|T]) when is_binary(Bin) -> - binary_to_list(Bin) ++ concat(T); -concat([S|T]) -> - S ++ concat(T); -concat([]) -> - []. - -append(L, Z) -> L ++ Z. - append([E]) -> E; append([H|T]) -> H ++ append(T); @@ -1320,13 +1242,6 @@ reverse([A, B]) -> reverse([A, B | L]) -> lists:reverse(L, [B, A]). % BIF -search(Key, [H|_T]) when is_tuple(H), element(1, H) =:= Key -> - {value, H}; -search(Key, [_|T]) -> - search(Key, T); -search(_Key, []) -> - false. - -spec objfile_extension() -> nonempty_string(). objfile_extension() -> ".beam". diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index c87b2645ec..2eb1b1d408 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -1276,6 +1276,7 @@ lseek_position(_) -> %% Translates the response from the driver into %% {ok, Result} or {error, Reason}. +-dialyzer({no_improper_lists, translate_response/2}). translate_response(?FILE_RESP_OK, []) -> ok; translate_response(?FILE_RESP_ERROR, List) when is_list(List) -> diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 54ab5aa566..3fe618ea4c 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -307,8 +307,8 @@ create_boot(Config) -> is_real_system(KernelVsn, StdlibVsn) -> LibDir = code:lib_dir(), - filelib:is_dir(filename:join(LibDir, "kernel"++KernelVsn)) andalso - filelib:is_dir(filename:join(LibDir, "stdlib"++StdlibVsn)). + filelib:is_dir(filename:join(LibDir, "kernel-"++KernelVsn)) andalso + filelib:is_dir(filename:join(LibDir, "stdlib-"++StdlibVsn)). %% ------------------------------------------------ %% Slave executes erlang:halt() on master nodedown. |