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
21%% A primary loader, provides two different methods to fetch a file:
22%% efile and inet. The efile method is simple communication with a
23%% port program.
24%%
25%% The distribution loading was removed and replaced with
26%% inet loading
27%%
28%% The start_it/4 function initializes a record with callback
29%% functions used to handle the interface functions.
30%%
31
32-module(erl_prim_loader).
33
34%% If the macro DEBUG is defined during compilation,
35%% debug printouts are done through erlang:display/1.
36%% Activate this feature by starting the compiler
37%% with> erlc -DDEBUG ...
38%% or by> setenv ERL_COMPILER_FLAGS DEBUG
39%% before running make (in the OTP make system)
40%% (the example is for tcsh)
41
42-include("inet_boot.hrl").
43
44%% Public
45-export([start/0, set_path/1, get_path/0, get_file/1,
46         list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]).
47
48%% Used by erl_boot_server
49-export([prim_init/0, prim_get_file/2, prim_list_dir/2,
50         prim_read_file_info/3, prim_get_cwd/2]).
51
52%% Used by escript and code
53-export([set_primary_archive/4]).
54
55%% Used by test suites
56-export([purge_archive_cache/0]).
57
58%% Used by init and the code server.
59-export([get_modules/2,get_modules/3, is_basename/1]).
60
61-include_lib("kernel/include/file.hrl").
62
63-type host() :: atom().
64
65-record(prim_state, {debug :: boolean(),
66		     primary_archive}).
67-type prim_state() :: #prim_state{}.
68
69-record(state,
70        {loader            :: 'efile' | 'inet',
71         hosts = []        :: [host()], % hosts list (to boot from)
72         data              :: 'noport' | port(), % data port etc
73         timeout           :: timeout(),	 % idle timeout
74         prim_state        :: prim_state()}).    % state for efile code loader
75
76-define(EFILE_IDLE_TIMEOUT, (6*60*1000)).	%purge archives
77-define(INET_IDLE_TIMEOUT, (60*1000)). 		%tear down connection timeout
78
79%% Defines for inet as prim_loader
80-define(INET_FAMILY, inet).
81-define(INET_ADDRESS, {0,0,0,0}).
82
83-ifdef(DEBUG).
84-define(dbg(Tag, Data), erlang:display({Tag,Data})).
85-else.
86-define(dbg(Tag, Data), true).
87-endif.
88
89-define(SAFE2(Expr, State),
90        fun() ->
91                case catch Expr of
92                    {'EXIT',XXXReason} -> {{error,XXXReason}, State};
93                    XXXRes -> XXXRes
94                end
95        end()).
96
97debug(#prim_state{debug = Deb}, Term) ->
98    case Deb of
99        false -> ok;
100        true  -> erlang:display(Term)
101    end.
102
103%%% --------------------------------------------------------
104%%% Interface Functions.
105%%% --------------------------------------------------------
106
107-spec start() ->
108	    {'ok', Pid} | {'error', What} when
109      Pid :: pid(),
110      What :: term().
111start() ->
112    Self = self(),
113    Pid = spawn_link(fun() -> start_it(Self) end),
114    receive
115        {Pid,ok} ->
116            {ok,Pid};
117        {'EXIT',Pid,Reason} ->
118            {error,Reason}
119    end.
120
121start_it(Parent) ->
122    process_flag(trap_exit, true),
123    register(erl_prim_loader, self()),
124    Loader = case init:get_argument(loader) of
125		 {ok,[[Loader0]]} ->
126		     Loader0;
127		 error ->
128		     "efile"
129	     end,
130    case Loader of
131	"efile" -> start_efile(Parent);
132	"inet" -> start_inet(Parent)
133    end.
134
135%% Hosts must be a list of form ['1.2.3.4' ...]
136start_inet(Parent) ->
137    Hosts = case init:get_argument(hosts) of
138		{ok,[Hosts0]} -> Hosts0;
139		_ -> []
140	    end,
141    AL = ipv4_list(Hosts),
142    ?dbg(addresses, AL),
143    {ok,Tcp} = find_master(AL),
144    init_ack(Parent),
145    PS = prim_init(),
146    State = #state {loader = inet,
147                    hosts = AL,
148                    data = Tcp,
149                    timeout = ?INET_IDLE_TIMEOUT,
150                    prim_state = PS},
151    loop(State, Parent, []).
152
153start_efile(Parent) ->
154    %% Check that we started in a valid directory.
155    case prim_file:get_cwd() of
156	{error, _} ->
157	    %% At this point in the startup, we have no error_logger at all.
158	    Report = "Invalid current directory or invalid filename "
159		"mode: loader cannot read current directory\n",
160	    erlang:display(Report),
161	    exit({error, invalid_current_directory});
162	_ ->
163	    init_ack(Parent)
164    end,
165    PS = prim_init(),
166    State = #state {loader = efile,
167                    data = noport,
168                    timeout = ?EFILE_IDLE_TIMEOUT,
169                    prim_state = PS},
170    loop(State, Parent, []).
171
172init_ack(Pid) ->
173    Pid ! {self(),ok},
174    ok.
175
176-spec set_path(Path) -> 'ok' when
177      Path :: [Dir :: string()].
178set_path(Paths) when is_list(Paths) ->
179    request({set_path,Paths}).
180
181-spec get_path() -> {'ok', Path} when
182      Path :: [Dir :: string()].
183get_path() ->
184    request({get_path,[]}).
185
186-spec get_file(Filename) -> {'ok', Bin, FullName} | 'error' when
187      Filename :: atom() | string(),
188      Bin :: binary(),
189      FullName :: string().
190get_file(File) when is_atom(File) ->
191    get_file(atom_to_list(File));
192get_file(File) ->
193    check_file_result(get_file, File, request({get_file,File})).
194
195-spec list_dir(Dir) -> {'ok', Filenames} | 'error' when
196      Dir :: string(),
197      Filenames :: [Filename :: string()].
198list_dir(Dir) ->
199    check_file_result(list_dir, Dir, request({list_dir,Dir})).
200
201-spec read_file_info(Filename) -> {'ok', FileInfo} | 'error' when
202      Filename :: string(),
203      FileInfo :: file:file_info().
204read_file_info(File) ->
205    check_file_result(read_file_info, File, request({read_file_info,File})).
206
207-spec read_link_info(Filename) -> {'ok', FileInfo} | 'error' when
208      Filename :: string(),
209      FileInfo :: file:file_info().
210read_link_info(File) ->
211    check_file_result(read_link_info, File, request({read_link_info,File})).
212
213-spec get_cwd() -> {'ok', string()} | 'error'.
214get_cwd() ->
215    check_file_result(get_cwd, [], request({get_cwd,[]})).
216
217-spec get_cwd(string()) -> {'ok', string()} | 'error'.
218get_cwd(Drive) ->
219    check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})).
220
221-spec set_primary_archive(File :: string() | 'undefined',
222			  ArchiveBin :: binary() | 'undefined',
223			  FileInfo :: #file_info{} | 'undefined',
224			  ParserFun :: fun())
225			 -> {ok, [string()]} | {error,_}.
226
227set_primary_archive(undefined, undefined, undefined, ParserFun) ->
228    request({set_primary_archive, undefined, undefined, undefined, ParserFun});
229set_primary_archive(File, ArchiveBin, FileInfo, ParserFun)
230  when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) ->
231    request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}).
232
233%% NOTE: Does not close the primary archive. Only closes all
234%% open zip files kept in the cache. Should be called before an archive
235%% file is to be removed (for example in the test suites).
236
237-spec purge_archive_cache() -> 'ok' | {'error', _}.
238purge_archive_cache() ->
239    request(purge_archive_cache).
240
241-spec get_modules([module()],
242		  fun((atom(), string(), binary()) ->
243			     {'ok',any()} | {'error',any()})) ->
244			 {'ok',{[any()],[any()]}}.
245
246get_modules(Modules, Fun) ->
247    request({get_modules,{Modules,Fun}}).
248
249-spec get_modules([module()],
250		  fun((atom(), string(), binary()) ->
251			     {'ok',any()} | {'error',any()}),
252		  [string()]) ->
253			 {'ok',{[any()],[any()]}}.
254
255get_modules(Modules, Fun, Path) ->
256    request({get_modules,{Modules,Fun,Path}}).
257
258request(Req) ->
259    Loader = whereis(erl_prim_loader),
260    Loader ! {self(),Req},
261    receive
262        {Loader,Res} ->
263            Res;
264        {'EXIT',Loader,_What} ->
265            error
266    end.
267
268check_file_result(_, _, {error,enoent}) ->
269    error;
270check_file_result(_, _, {error,enotdir}) ->
271    error;
272check_file_result(_, _, {error,einval}) ->
273    error;
274check_file_result(Func, Target, {error,Reason}) ->
275    case (catch atom_to_list(Reason)) of
276        {'EXIT',_} ->                           % exit trapped
277            error;
278        Errno ->                                % errno
279            Process = case process_info(self(), registered_name) of
280                          {registered_name,R} ->
281                              "Process: " ++ atom_to_list(R) ++ ".";
282                          _ ->
283                              ""
284                      end,
285            TargetStr =
286                if is_atom(Target) -> atom_to_list(Target);
287                   is_list(Target) -> Target;
288                   true -> []
289                end,
290            Report =
291                case TargetStr of
292                    [] ->
293                        "File operation error: " ++ Errno ++ ". " ++
294                        "Function: " ++ atom_to_list(Func) ++ ". " ++ Process;
295                    _ ->
296                        "File operation error: " ++ Errno ++ ". " ++
297                        "Target: " ++ TargetStr ++ ". " ++
298                        "Function: " ++ atom_to_list(Func) ++ ". " ++ Process
299                end,
300            %% This is equal to calling logger:error/2 which
301            %% we don't want to do from code_server during system boot.
302            %% We don't want to call logger:timestamp() either.
303            _ = try
304                    logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
305                              #{pid=>self(),
306                                gl=>group_leader(),
307                                time=>os:system_time(microsecond),
308                                error_logger=>#{tag=>error_report,
309                                                type=>std_error}}}
310                catch _:_ ->
311                        %% If logger has not been started yet we just display it
312                        erlang:display({?MODULE,file_error}),
313                        erlang:display(Report)
314                end,
315            error
316    end;
317check_file_result(_, _, Other) ->
318    Other.
319
320%%% --------------------------------------------------------
321%%% The main loop.
322%%% --------------------------------------------------------
323
324loop(St0, Parent, Paths) ->
325    receive
326	{Pid,{set_path,NewPaths}} when is_pid(Pid) ->
327	    Pid ! {self(),ok},
328	    loop(St0, Parent, to_strs(NewPaths));
329        {Pid,Req} when is_pid(Pid) ->
330	    case handle_request(Req, Paths, St0) of
331		ignore ->
332		    ok;
333		{Resp,#state{}=St1} ->
334		    Pid ! {self(),Resp},
335                    loop(St1, Parent, Paths);
336		{_,State2,_} ->
337                    exit({bad_state,Req,State2})
338            end;
339        {'EXIT',Parent,W} ->
340            _ = handle_stop(St0),
341            exit(W);
342        {'EXIT',P,W} ->
343            St1 = handle_exit(St0, P, W),
344            loop(St1, Parent, Paths);
345        _Message ->
346            loop(St0, Parent, Paths)
347    after St0#state.timeout ->
348            St1 = handle_timeout(St0, Parent),
349            loop(St1, Parent, Paths)
350    end.
351
352handle_request(Req, Paths, St0) ->
353    case Req of
354	{get_path,_} ->
355	    {{ok,Paths},St0};
356	{get_file,File} ->
357	    handle_get_file(St0, Paths, File);
358	{get_modules,{Modules,Fun}} ->
359	    handle_get_modules(St0, Modules, Fun, Paths);
360	{get_modules,{Modules,Fun,ModPaths}} ->
361	    handle_get_modules(St0, Modules, Fun, ModPaths);
362	{list_dir,Dir} ->
363	    handle_list_dir(St0, Dir);
364	{read_file_info,File} ->
365	    handle_read_file_info(St0, File);
366	{read_link_info,File} ->
367	    handle_read_link_info(St0, File);
368	{get_cwd,[]} ->
369	    handle_get_cwd(St0, []);
370	{get_cwd,[_]=Args} ->
371	    handle_get_cwd(St0, Args);
372	{set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
373	    handle_set_primary_archive(St0, File, ArchiveBin,
374				       FileInfo, ParserFun);
375	purge_archive_cache ->
376	    handle_purge_archive_cache(St0);
377	_ ->
378	    ignore
379    end.
380
381handle_get_file(State = #state{loader = efile}, Paths, File) ->
382    ?SAFE2(efile_get_file_from_port(State, File, Paths), State);
383handle_get_file(State = #state{loader = inet}, Paths, File) ->
384    ?SAFE2(inet_get_file_from_port(State, File, Paths), State).
385
386handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) ->
387    ?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State).
388
389handle_purge_archive_cache(#state{loader = efile}=State) ->
390    prim_purge_cache(),
391    {ok,State}.
392
393handle_list_dir(State = #state{loader = efile}, Dir) ->
394    ?SAFE2(efile_list_dir(State, Dir), State);
395handle_list_dir(State = #state{loader = inet}, Dir) ->
396    ?SAFE2(inet_list_dir(State, Dir), State).
397
398handle_read_file_info(State = #state{loader = efile}, File) ->
399    ?SAFE2(efile_read_file_info(State, File, true), State);
400handle_read_file_info(State = #state{loader = inet}, File) ->
401    ?SAFE2(inet_read_file_info(State, File), State).
402
403handle_read_link_info(State = #state{loader = efile}, File) ->
404    ?SAFE2(efile_read_file_info(State, File, false), State);
405handle_read_link_info(State = #state{loader = inet}, File) ->
406    ?SAFE2(inet_read_link_info(State, File), State).
407
408handle_get_cwd(State = #state{loader = efile}, Drive) ->
409    ?SAFE2(efile_get_cwd(State, Drive), State);
410handle_get_cwd(State = #state{loader = inet}, Drive) ->
411    ?SAFE2(inet_get_cwd(State, Drive), State).
412
413handle_stop(State = #state{loader = efile}) ->
414    State;
415handle_stop(State = #state{loader = inet}) ->
416    inet_stop_port(State).
417
418handle_exit(State = #state{loader = efile}, _Who, _Reason) ->
419    State;
420handle_exit(State = #state{loader = inet}, Who, Reason) ->
421    inet_exit_port(State, Who, Reason).
422
423handle_timeout(State = #state{loader = efile}, Parent) ->
424    efile_timeout_handler(State, Parent);
425handle_timeout(State = #state{loader = inet}, Parent) ->
426    inet_timeout_handler(State, Parent).
427
428%%% --------------------------------------------------------
429%%% Functions which handle efile as prim_loader (default).
430%%% --------------------------------------------------------
431
432
433%% -> {{ok,BinFile,File},State} | {{error,Reason},State}
434efile_get_file_from_port(State, File, Paths) ->
435    case is_basename(File) of
436        false ->                        % get absolute file name.
437            efile_get_file_from_port2(State, File);
438        true when Paths =:= [] ->       % get plain file name.
439            efile_get_file_from_port2(State, File);
440        true ->                         % use paths.
441            efile_get_file_from_port3(State, File, Paths)
442    end.
443
444efile_get_file_from_port2(#state{prim_state = PS} = State, File) ->
445    {Res, PS2} = prim_get_file(PS, File),
446    case Res of
447        {error,port_died} ->
448            exit('prim_load port died');
449        {error,Reason} ->
450            {{error,Reason},State#state{prim_state = PS2}};
451        {ok,BinFile} ->
452            {{ok,BinFile,File},State#state{prim_state = PS2}}
453    end.
454
455efile_get_file_from_port3(State, File, [P | Paths]) ->
456    case efile_get_file_from_port2(State, join(P, File)) of
457        {{error,Reason},State1} when Reason =/= emfile ->
458            case Paths of
459                [] ->                           % return last error
460                    {{error,Reason},State1};
461                _ ->                            % try more paths
462                    efile_get_file_from_port3(State1, File, Paths)
463            end;
464        Result ->
465            Result
466    end;
467efile_get_file_from_port3(State, _File, []) ->
468    {{error,enoent},State}.
469
470efile_set_primary_archive(#state{prim_state = PS} = State, File,
471			  ArchiveBin, FileInfo, ParserFun) ->
472    {Res, PS2} = prim_set_primary_archive(PS, File, ArchiveBin,
473					  FileInfo, ParserFun),
474    {Res,State#state{prim_state = PS2}}.
475
476efile_list_dir(#state{prim_state = PS} = State, Dir) ->
477    {Res, PS2} = prim_list_dir(PS, Dir),
478    {Res, State#state{prim_state = PS2}}.
479
480efile_read_file_info(#state{prim_state = PS} = State, File, FollowLinks) ->
481    {Res, PS2} = prim_read_file_info(PS, File, FollowLinks),
482    {Res, State#state{prim_state = PS2}}.
483
484efile_get_cwd(#state{prim_state = PS} = State, Drive) ->
485    {Res, PS2} = prim_get_cwd(PS, Drive),
486    {Res, State#state{prim_state = PS2}}.
487
488efile_timeout_handler(State, _Parent) ->
489    prim_purge_cache(),
490    State.
491
492%%% --------------------------------------------------------
493%%% Read and process severals modules in parallel.
494%%% --------------------------------------------------------
495
496handle_get_modules(#state{loader=efile}=St, Ms, Process, Paths) ->
497    Primary = (St#state.prim_state)#prim_state.primary_archive,
498    Res = case efile_any_archives(Paths, Primary) of
499	      false ->
500		  efile_get_mods_par(Ms, Process, Paths);
501	      true ->
502		  Get = fun efile_get_file_from_port/3,
503		  gm_get_mods(St, Get, Ms, Process, Paths)
504	  end,
505    {Res,St};
506handle_get_modules(#state{loader=inet}=St, Ms, Process, Paths) ->
507    Get = fun inet_get_file_from_port/3,
508    {gm_get_mods(St, Get, Ms, Process, Paths),St}.
509
510efile_get_mods_par(Ms, Process, Paths) ->
511    Self = self(),
512    Ref = make_ref(),
513    GmSpawn = fun() ->
514		      efile_gm_spawn({Self,Ref}, Ms, Process, Paths)
515	      end,
516    _ = spawn_link(GmSpawn),
517    N = length(Ms),
518    efile_gm_recv(N, Ref, [], []).
519
520efile_any_archives([H|T], Primary) ->
521    case name_split(Primary, H) of
522	{file,_} -> efile_any_archives(T, Primary);
523	{archive,_,_} -> true
524    end;
525efile_any_archives([], _) ->
526    false.
527
528efile_gm_recv(0, _Ref, Succ, Fail) ->
529    {ok,{Succ,Fail}};
530efile_gm_recv(N, Ref, Succ, Fail) ->
531    receive
532	{Ref,Mod,{ok,Res}} ->
533	    efile_gm_recv(N-1, Ref, [{Mod,Res}|Succ], Fail);
534	{Ref,Mod,{error,Res}} ->
535	    efile_gm_recv(N-1, Ref, Succ, [{Mod,Res}|Fail])
536    end.
537
538efile_gm_spawn(ParentRef, Ms, Process, Paths) ->
539    efile_gm_spawn_1(0, Ms, ParentRef, Process, Paths).
540
541efile_gm_spawn_1(N, Ms, ParentRef, Process, Paths) when N >= 32 ->
542    receive
543	{'DOWN',_,process,_,_} ->
544	    efile_gm_spawn_1(N-1, Ms, ParentRef, Process, Paths)
545    end;
546efile_gm_spawn_1(N, [M|Ms], ParentRef, Process, Paths) ->
547    Get = fun() -> efile_gm_get(Paths, M, ParentRef, Process) end,
548    _ = spawn_monitor(Get),
549    efile_gm_spawn_1(N+1, Ms, ParentRef, Process, Paths);
550efile_gm_spawn_1(_, [], _, _, _) ->
551    ok.
552
553efile_gm_get(Paths, Mod, ParentRef, Process) ->
554    File = atom_to_list(Mod) ++ init:objfile_extension(),
555    efile_gm_get_1(Paths, File, Mod, ParentRef, Process).
556
557efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) ->
558    File = join(P, File0),
559    try prim_file:read_file(File) of
560	{ok,Bin} ->
561	    Res = gm_process(Mod, File, Bin, Process),
562	    Parent ! {Ref,Mod,Res};
563	Error ->
564	    _ = check_file_result(get_modules, File, Error),
565	    efile_gm_get_1(Ps, File0, Mod, PR, Process)
566    catch
567	_:Reason ->
568	    Res = {error,{crash,Reason}},
569	    Parent ! {Ref,Mod,Res}
570    end;
571efile_gm_get_1([], _, Mod, {Parent,Ref}, _Process) ->
572    Parent ! {Ref,Mod,{error,enoent}}.
573
574gm_get_mods(St, Get, Ms, Process, Paths) ->
575    gm_get_mods(St, Get, Ms, Process, Paths, [], []).
576
577gm_get_mods(St, Get, [M|Ms], Process, Paths, Succ, Fail) ->
578    File = atom_to_list(M) ++ init:objfile_extension(),
579    case gm_arch_get(St, Get, M, File, Paths, Process) of
580	{ok,Res} ->
581	    gm_get_mods(St, Get, Ms, Process, Paths,
582			[{M,Res}|Succ], Fail);
583	{error,Res} ->
584	    gm_get_mods(St, Get, Ms, Process, Paths,
585			Succ, [{M,Res}|Fail])
586    end;
587gm_get_mods(_St, _Get, [], _, _, Succ, Fail) ->
588    {ok,{Succ,Fail}}.
589
590gm_arch_get(St, Get, Mod, File, Paths, Process) ->
591    case Get(St, File, Paths) of
592	{{error,_}=E,_} ->
593	    E;
594	{{ok,Bin,Full},_} ->
595	    gm_process(Mod, Full, Bin, Process)
596    end.
597
598gm_process(Mod, File, Bin, Process) ->
599    try Process(Mod, File, Bin) of
600	{ok,_}=Res -> Res;
601	{error,_}=Res -> Res;
602	Other -> {error,{bad_return,Other}}
603    catch
604	_:Error ->
605	    {error,{crash,Error}}
606    end.
607
608
609%%% --------------------------------------------------------
610%%% Functions which handle inet prim_loader
611%%% --------------------------------------------------------
612
613%%
614%% Connect to a boot master
615%% return {ok, Socket}  TCP
616%% AL is a list of boot servers (including broadcast addresses)
617%%
618find_master(AL) ->
619    find_master(AL, ?EBOOT_RETRY, ?EBOOT_REQUEST_DELAY, ?EBOOT_SHORT_RETRY_SLEEP,
620               ?EBOOT_UNSUCCESSFUL_TRIES, ?EBOOT_LONG_RETRY_SLEEP).
621
622find_master(AL, Retry, ReqDelay, SReSleep, Tries, LReSleep) ->
623    {ok,U} = ll_udp_open(0),
624    find_master(U, Retry, AL, ReqDelay, SReSleep, [], Tries, LReSleep).
625
626%%
627%% Master connect loop
628%%
629find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
630    case find_loop(U, Retry, AddrL, ReqDelay, SReSleep, Ignore,
631                   Tries, LReSleep) of
632        [] ->
633            find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore,
634                        Tries, LReSleep);
635        Servers ->
636            ?dbg(servers, Servers),
637            case connect_master(Servers) of
638                {ok, Socket} ->
639                    ll_close(U),
640                    {ok, Socket};
641                _Error ->
642                    find_master(U, Retry, AddrL, ReqDelay, SReSleep,
643                                Servers ++ Ignore, Tries, LReSleep)
644            end
645    end.
646
647connect_master([{_Prio,IP,Port} | Servers]) ->
648    case ll_tcp_connect(0, IP, Port) of
649        {ok, S} -> {ok, S};
650        _Error -> connect_master(Servers)
651    end;
652connect_master([]) ->
653    {error, ebusy}.
654
655%%
656%% Always return a list of boot servers or hang.
657%%
658find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
659    case find_loop(U, Retry, AL, ReqDelay, []) of
660        [] ->                                   % no response from any server
661            erlang:display({erl_prim_loader,'no server found'}), % lifesign
662            Tries1 =
663		if Tries > 0 ->
664			sleep(SReSleep),
665			Tries - 1;
666		   true ->
667			sleep(LReSleep),
668			0
669		end,
670            find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries1, LReSleep);
671        Servers ->
672            keysort(1, Servers -- Ignore)
673    end.
674
675%% broadcast or send
676find_loop(_U, 0, _AL, _Delay, Acc) ->
677    Acc;
678find_loop(U, Retry, AL, Delay, Acc) ->
679    send_all(U, AL, [?EBOOT_REQUEST, erlang:system_info(version)]),
680    find_collect(U, Retry-1, AL, Delay, Acc).
681
682find_collect(U,Retry,AL,Delay,Acc) ->
683    receive
684        {udp, U, IP, _Port, [$E,$B,$O,$O,$T,$R,Priority,T1,T0 | _Version]} ->
685            Elem = {Priority,IP,T1*256+T0},
686            ?dbg(got, Elem),
687            case member(Elem, Acc) of
688                false  -> find_collect(U, Retry, AL, Delay, [Elem | Acc]);
689                true -> find_collect(U, Retry, AL, Delay, Acc)
690            end;
691        _Garbage ->
692            ?dbg(collect_garbage, _Garbage),
693            find_collect(U, Retry, AL, Delay, Acc)
694    after Delay ->
695            ?dbg(collected, Acc),
696            case keymember(0, 1, Acc) of  %% got high priority server?
697                true -> Acc;
698                false -> find_loop(U, Retry, AL, Delay, Acc)
699            end
700    end.
701
702
703sleep(Time) ->
704    receive after Time -> ok end.
705
706inet_exit_port(State, Port, _Reason) when State#state.data =:= Port ->
707    State#state{data = noport, timeout = infinity};
708inet_exit_port(State, _, _) ->
709    State.
710
711
712inet_timeout_handler(State, _Parent) ->
713    Tcp = State#state.data,
714    if is_port(Tcp) -> ll_close(Tcp);
715       true -> ok
716    end,
717    State#state{timeout = infinity, data = noport}.
718
719%% -> {{ok,BinFile,Tag},State} | {{error,Reason},State}
720inet_get_file_from_port(State, File, Paths) ->
721    case is_basename(File) of
722        false ->                        % get absolute file name.
723            inet_send_and_rcv({get,File}, File, State);
724        true when Paths =:= [] ->       % get plain file name.
725            inet_send_and_rcv({get,File}, File, State);
726        true ->                         % use paths.
727            inet_get_file_from_port1(File, Paths, State)
728    end.
729
730inet_get_file_from_port1(File, [P | Paths], State) ->
731    File1 = join(P, File),
732    case inet_send_and_rcv({get,File1}, File1, State) of
733        {{error,Reason},State1} ->
734            case Paths of
735                [] ->                           % return last error
736                    {{error,Reason},State1};
737                _ ->                            % try more paths
738                    inet_get_file_from_port1(File, Paths, State1)
739            end;
740        Result -> Result
741    end;
742inet_get_file_from_port1(_File, [], State) ->
743    {{error,file_not_found},State}.
744
745inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport ->
746    {ok,Tcp} = find_master(State#state.hosts),     %% reconnect
747    inet_send_and_rcv(Msg, Tag, State#state{data = Tcp,
748					    timeout = ?INET_IDLE_TIMEOUT});
749inet_send_and_rcv(Msg, Tag, #state{data = Tcp, timeout = Timeout} = State) ->
750    prim_inet:send(Tcp, term_to_binary(Msg)),
751    receive
752        {tcp,Tcp,BinMsg} ->
753            case catch binary_to_term(BinMsg) of
754                {get,{ok,BinFile}} ->
755                    {{ok,BinFile,Tag},State};
756                {_Cmd,Res={ok,_}} ->
757                    {Res,State};
758                {_Cmd,{error,Error}} ->
759                    {{error,Error},State};
760                {error,Error} ->
761                    {{error,Error},State};
762                {'EXIT',Error} ->
763                    {{error,Error},State}
764            end;
765        {tcp_closed,Tcp} ->
766            %% Ok we must reconnect
767            inet_send_and_rcv(Msg, Tag, State#state{data = noport});
768        {tcp_error,Tcp,_Reason} ->
769            %% Ok we must reconnect
770            inet_send_and_rcv(Msg, Tag, inet_stop_port(State));
771        {'EXIT', Tcp, _} ->
772            %% Ok we must reconnect
773            inet_send_and_rcv(Msg, Tag, State#state{data = noport})
774    after Timeout ->
775            %% Ok we must reconnect
776            inet_send_and_rcv(Msg, Tag, inet_stop_port(State))
777    end.
778
779%% -> {{ok,List},State} | {{error,Reason},State}
780inet_list_dir(State, Dir) ->
781    inet_send_and_rcv({list_dir,Dir}, list_dir, State).
782
783%% -> {{ok,Info},State} | {{error,Reason},State}
784inet_read_file_info(State, File) ->
785    inet_send_and_rcv({read_file_info,File}, read_file_info, State).
786
787%% -> {{ok,Info},State} | {{error,Reason},State}
788inet_read_link_info(State, File) ->
789    inet_send_and_rcv({read_link_info,File}, read_link_info, State).
790
791%% -> {{ok,Cwd},State} | {{error,Reason},State}
792inet_get_cwd(State, []) ->
793    inet_send_and_rcv(get_cwd, get_cwd, State);
794inet_get_cwd(State, [Drive]) ->
795    inet_send_and_rcv({get_cwd,Drive}, get_cwd, State).
796
797inet_stop_port(#state{data=Tcp}=State) ->
798    prim_inet:close(Tcp),
799    State#state{data=noport}.
800
801%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802%%
803%% Direct inet_drv access
804%%
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806
807tcp_options() ->
808    [{mode,binary}, {packet,4}, {active, true}, {deliver,term}].
809
810tcp_timeout() ->
811    15000.
812
813%% options for udp  [list, {broadcast, true}, {active,true}]
814udp_options() ->
815    [{mode,list}, {active, true}, {deliver,term}, {broadcast,true}].
816%%
817%% INET version IPv4 addresses
818%%
819ll_tcp_connect(LocalPort, IP, RemotePort) ->
820    case ll_open_set_bind(tcp, ?INET_FAMILY, stream, tcp_options(),
821                          ?INET_ADDRESS, LocalPort) of
822        {ok,S} ->
823            case prim_inet:connect(S, IP, RemotePort, tcp_timeout()) of
824                ok -> {ok, S};
825                Error -> port_error(S, Error)
826            end;
827        Error -> Error
828    end.
829
830%%
831%% Open and initialize an udp port for broadcast
832%%
833ll_udp_open(P) ->
834    ll_open_set_bind(udp, ?INET_FAMILY, dgram, udp_options(), ?INET_ADDRESS, P).
835
836
837ll_open_set_bind(Protocol, Family, Type, SOpts, IP, Port) ->
838    case prim_inet:open(Protocol, Family, Type) of
839        {ok, S} ->
840            case prim_inet:setopts(S, SOpts) of
841                ok ->
842                    case prim_inet:bind(S, IP, Port) of
843                        {ok,_} ->
844                            {ok, S};
845                        Error -> port_error(S, Error)
846                    end;
847                Error -> port_error(S, Error)
848            end;
849        Error -> Error
850    end.
851
852
853ll_close(S) ->
854    unlink(S),
855    exit(S, kill).
856
857port_error(S, Error) ->
858    unlink(S),
859    prim_inet:close(S),
860    Error.
861
862%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
863
864-spec prim_init() -> prim_state().
865prim_init() ->
866    Deb =
867        case init:get_argument(loader_debug) of
868            {ok, _} -> true;
869            error -> false
870        end,
871    cache_new(#prim_state{debug = Deb}).
872
873prim_purge_cache() ->
874    do_prim_purge_cache(get()).
875
876do_prim_purge_cache([{Key,Val}|T]) ->
877    case Val of
878	{Cache,_FI} ->
879	    catch clear_cache(Key, Cache);
880	_ ->
881	    ok
882    end,
883    do_prim_purge_cache(T);
884do_prim_purge_cache([]) ->
885    ok.
886
887prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) ->
888    debug(PS, {set_primary_archive, clean}),
889    case PS#prim_state.primary_archive of
890        undefined ->
891            Res = {error, enoent},
892            debug(PS, {return, Res}),
893            {Res, PS};
894        ArchiveFile ->
895            {primary, PrimZip, _FI, _ParserFun2} = erase(ArchiveFile),
896            ok = prim_zip:close(PrimZip),
897            PS2 = PS#prim_state{primary_archive = undefined},
898            Res = {ok, []},
899            debug(PS2, {return, Res}),
900            {Res, PS2}
901    end;
902
903prim_set_primary_archive(PS, ArchiveFile0, ArchiveBin,
904			 #file_info{} = FileInfo, ParserFun)
905  when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
906    %% Try the archive file
907    debug(PS, {set_primary_archive, ArchiveFile0, byte_size(ArchiveBin)}),
908    ArchiveFile = real_path(absname(ArchiveFile0)),
909    {Res3, PS3} =
910        case PS#prim_state.primary_archive of
911            undefined ->
912                case load_prim_archive(ArchiveFile, ArchiveBin, FileInfo) of
913                    {ok, PrimZip, FI, Ebins} ->
914                        debug(PS, {set_primary_archive, Ebins}),
915                        put(ArchiveFile, {primary, PrimZip, FI, ParserFun}),
916                        {{ok, Ebins},
917                         PS#prim_state{primary_archive = ArchiveFile}};
918                    Error ->
919                        debug(PS, {set_primary_archive, Error}),
920                        {Error, PS}
921                end;
922            OldArchiveFile ->
923                debug(PS, {set_primary_archive, clean}),
924                {primary, PrimZip, _FI, _ParserFun} = erase(OldArchiveFile),
925                ok = prim_zip:close(PrimZip),
926                PS2 = PS#prim_state{primary_archive = undefined},
927                prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin,
928					 FileInfo, ParserFun)
929        end,
930    debug(PS3, {return, Res3}),
931    {Res3, PS3}.
932
933-spec prim_get_file(prim_state(), file:filename()) -> {_, prim_state()}.
934prim_get_file(PS, File) ->
935    debug(PS, {get_file, File}),
936    {Res2, PS2} =
937        case name_split(PS#prim_state.primary_archive, File) of
938            {file, PrimFile} ->
939                Res = prim_file:read_file(PrimFile),
940                {Res, PS};
941            {archive, ArchiveFile, FileInArchive} ->
942                debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
943                FileComponents = path_split(FileInArchive),
944                Fun =
945                    fun({Components, _GetInfo, GetBin}, Acc) ->
946                            if
947                                Components =:= FileComponents ->
948                                    {false, {ok, GetBin()}};
949                                true ->
950                                    {true, Acc}
951                            end
952                    end,
953                apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
954        end,
955    debug(PS, {return, Res2}),
956    {Res2, PS2}.
957
958-spec prim_list_dir(prim_state(), file:filename()) ->
959	 {{'ok', [file:filename()]}, prim_state()}
960       | {{'error', term()}, prim_state()}.
961prim_list_dir(PS, Dir) ->
962    debug(PS, {list_dir, Dir}),
963    {Res2, PS3} =
964        case name_split(PS#prim_state.primary_archive, Dir) of
965            {file, PrimDir} ->
966                Res = prim_file:list_dir(PrimDir),
967                {Res, PS};
968            {archive, ArchiveFile, FileInArchive} ->
969                debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
970                DirComponents = path_split(FileInArchive),
971                Fun =
972                    fun({Components, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
973                            case Components of
974                                [RevName | DC] when DC =:= DirComponents ->
975                                    case RevName of
976                                        "" ->
977                                            %% The listed directory
978                                            {true, {ok, Names}};
979                                        _ ->
980                                            %% Plain file
981                                            Name = reverse(RevName),
982                                            {true, {Status, [Name | Names]}}
983                                    end;
984                                ["", RevName | DC] when DC =:= DirComponents ->
985                                    %% Directory
986                                    Name = reverse(RevName),
987                                    {true, {Status, [Name | Names]}};
988                                [RevName] when DirComponents =:= [""] ->
989                                    %% File in top directory
990                                    Name = reverse(RevName),
991                                    {true, {ok, [Name | Names]}};
992                                ["", RevName] when DirComponents =:= [""] ->
993                                    %% Directory in top directory
994                                    Name = reverse(RevName),
995                                    {true, {ok, [Name | Names]}};
996                                _ ->
997                                    %% No match
998                                    {true, Acc}
999                            end
1000                    end,
1001                {{Status, Names}, PS2} =
1002                    apply_archive(PS, Fun, {error, []}, ArchiveFile),
1003                case Status of
1004                    ok    -> {{ok, Names}, PS2};
1005                    error -> {{error, enotdir}, PS2}
1006                end
1007        end,
1008    debug(PS, {return, Res2}),
1009    {Res2, PS3}.
1010
1011-spec prim_read_file_info(prim_state(), file:filename(), boolean()) ->
1012	{{'ok', #file_info{}}, prim_state()}
1013      | {{'error', term()}, prim_state()}.
1014prim_read_file_info(PS, File, FollowLinks) ->
1015    debug(PS, {read_file_info, File}),
1016    {Res2, PS2} =
1017        case name_split(PS#prim_state.primary_archive, File) of
1018            {file, PrimFile} ->
1019                case FollowLinks of
1020                    true -> {prim_file:read_file_info(PrimFile), PS};
1021                    false -> {prim_file:read_link_info(PrimFile), PS}
1022                end;
1023            {archive, ArchiveFile, []} ->
1024                %% Fake top directory
1025                debug(PS, {archive_read_file_info, ArchiveFile}),
1026                case prim_file:read_file_info(ArchiveFile) of
1027                    {ok, FI} ->
1028                        {{ok, FI#file_info{type = directory}}, PS};
1029                    Other ->
1030                        {Other, PS}
1031                end;
1032            {archive, ArchiveFile, FileInArchive} ->
1033                debug(PS, {archive_read_file_info, File}),
1034                FileComponents = path_split(FileInArchive),
1035                Fun =
1036                    fun({Components, GetInfo, _GetBin}, Acc)  ->
1037			    case Components of
1038				["" | F] when F =:= FileComponents ->
1039                                    %% Directory
1040                                    {false, {ok, GetInfo()}};
1041                                F when F =:= FileComponents ->
1042                                    %% Plain file
1043                                    {false, {ok, GetInfo()}};
1044                                _ ->
1045                                    %% No match
1046                                    {true, Acc}
1047                            end
1048                    end,
1049                apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
1050        end,
1051    debug(PS2, {return, Res2}),
1052    {Res2, PS2}.
1053
1054-spec prim_get_cwd(prim_state(), [file:filename()]) ->
1055        {{'error', term()} | {'ok', _}, prim_state()}.
1056prim_get_cwd(PS, []) ->
1057    debug(PS, {get_cwd, []}),
1058    Res = prim_file:get_cwd(),
1059    debug(PS, {return, Res}),
1060    {Res, PS};
1061prim_get_cwd(PS, [Drive]) ->
1062    debug(PS, {get_cwd, Drive}),
1063    Res = prim_file:get_cwd(Drive),
1064    debug(PS, {return, Res}),
1065    {Res, PS}.
1066
1067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1068
1069apply_archive(PS, Fun, Acc, Archive) ->
1070    case get(Archive) of
1071        undefined ->
1072	    case open_archive(Archive, Acc, Fun) of
1073		{ok, PrimZip, {Acc2, FI, _}} ->
1074		    debug(PS, {cache, ok}),
1075		    put(Archive, {{ok, PrimZip}, FI}),
1076		    {Acc2, PS};
1077		Error ->
1078		    debug(PS, {cache, Error}),
1079		    %% put(Archive, {Error, FI}),
1080		    {Error, PS}
1081	    end;
1082        {primary, PrimZip, FI, ParserFun} ->
1083	    case prim_file:read_file_info(Archive) of
1084                {ok, FI2}
1085		  when FI#file_info.mtime =:= FI2#file_info.mtime ->
1086		    case foldl_archive(PrimZip, Acc, Fun) of
1087			{ok, _PrimZip2, Acc2} ->
1088			    {Acc2, PS};
1089			Error ->
1090			    debug(PS, {primary, Error}),
1091			    {Error, PS}
1092		    end;
1093		{ok, FI2} ->
1094		    ok = clear_cache(Archive, {ok, PrimZip}),
1095		    case load_prim_archive(Archive, FI2, ParserFun) of
1096			{ok, PrimZip2, FI3, _Ebins} ->
1097			    debug(PS, {cache, {update, Archive}}),
1098			    put(Archive, {primary, PrimZip2, FI3, ParserFun});
1099			Error2 ->
1100			    debug(PS, {cache, {clear, Error2}})
1101		    end,
1102		    apply_archive(PS, Fun, Acc, Archive);
1103		Error ->
1104		    debug(PS, {cache, {clear, Error}}),
1105		    ok = clear_cache(Archive, {ok, PrimZip}),
1106		    apply_archive(PS, Fun, Acc, Archive)
1107	    end;
1108        {Cache, FI} ->
1109            case prim_file:read_file_info(Archive) of
1110                {ok, FI2}
1111		  when FI#file_info.mtime =:= FI2#file_info.mtime ->
1112                    case Cache of
1113                        {ok, PrimZip} ->
1114                            case foldl_archive(PrimZip, Acc, Fun) of
1115                                {ok, _PrimZip2, Acc2} ->
1116                                    {Acc2, PS};
1117                                Error ->
1118                                    debug(PS, {cache, {clear, Error}}),
1119                                    ok = clear_cache(Archive, Cache),
1120                                    debug(PS, {cache, Error}),
1121				    erase(Archive),
1122                                    %% put(Archive, {Error, FI}),
1123                                    {Error, PS}
1124                            end;
1125                        Error ->
1126                            debug(PS, {cache, Error}),
1127                            {Error, PS}
1128                    end;
1129                Error ->
1130                    debug(PS, {cache, {clear, Error}}),
1131                    ok = clear_cache(Archive, Cache),
1132                    apply_archive(PS, Fun, Acc, Archive)
1133            end
1134    end.
1135
1136open_archive(Archive, Acc, Fun) ->
1137    case prim_file:read_file_info(Archive) of
1138	{ok, FileInfo} ->
1139	    open_archive(Archive, FileInfo, Acc, Fun);
1140	{error, Reason} ->
1141	    {error, Reason}
1142    end.
1143
1144%% Open the given archive and iterate through all files with an own
1145%% wrapper fun in order to identify each file as a component list as
1146%% returned from path_split/1.
1147%%
1148%% In the archive (zip) file, directory elements might or might not be
1149%% present. To ensure consistency, a directory element is added if it
1150%% does not already exist (ensure_virtual_dirs/6). NOTE that there will
1151%% be no such directory element for the top directory of the archive.
1152open_archive(Archive, FileInfo, Acc, Fun) ->
1153    FakeFI = FileInfo#file_info{type = directory},
1154    Wrapper =
1155	fun({N, GI, GB}, {A, I, Dirs}) ->
1156		Components = path_split(N),
1157		Dirs2 =
1158		    case Components of
1159			["" | Dir] ->
1160			    %% This is a directory
1161			    [Dir | Dirs];
1162			_ ->
1163			    %% This is a regular file
1164			    Dirs
1165		    end,
1166		{Includes, Dirs3, A2} =
1167		    ensure_virtual_dirs(Components, Fun, FakeFI,
1168					[{true, Components}], Dirs2, A),
1169		{_Continue, A3} = Fun({Components, GI, GB}, A2),
1170		{true, Includes, {A3, I, Dirs3}}
1171	end,
1172    prim_zip:open(Wrapper, {Acc, FakeFI, []}, Archive).
1173
1174ensure_virtual_dirs(Components, Fun, FakeFI, Includes, Dirs, Acc) ->
1175    case Components of
1176	[_] ->
1177	    %% Don't add virtual dir for top directory
1178	    {Includes, Dirs, Acc};
1179	[_ | Dir] ->
1180	    case lists:member(Dir, Dirs) of % BIF
1181		false ->
1182		    %% The directory does not yet exist - add it
1183		    GetInfo = fun() -> FakeFI end,
1184		    GetBin = fun() -> <<>> end,
1185		    VirtualDir = ["" | Dir],
1186		    Includes2 = [{true, VirtualDir, GetInfo, GetBin} | Includes],
1187		    Dirs2 = [Dir | Dirs],
1188
1189		    %% Recursively ensure dir elements on all levels
1190		    {I, F, Acc2} = ensure_virtual_dirs(Dir, Fun, FakeFI,
1191						       Includes2, Dirs2, Acc),
1192
1193		    {_Continue, Acc3} = Fun({VirtualDir, GetInfo, GetBin}, Acc2),
1194		    {I, F, Acc3};
1195		true ->
1196		    %% The directory element does already exist
1197		    %% Recursively ensure dir elements on all levels
1198		    ensure_virtual_dirs(Dir,Fun,FakeFI,Includes,Dirs,Acc)
1199	    end
1200    end.
1201
1202foldl_archive(PrimZip, Acc, Fun) ->
1203    Wrapper =
1204        fun({Components, GI, GB}, A) ->
1205                %% Allow partial iteration at foldl
1206                {Continue, A2} = Fun({Components, GI, GB}, A),
1207                {Continue, true, A2}
1208        end,
1209    prim_zip:foldl(Wrapper, Acc, PrimZip).
1210
1211cache_new(PS) ->
1212    PS.
1213
1214clear_cache(Archive, Cache) ->
1215    erase(Archive),
1216    case Cache of
1217        {ok, PrimZip} ->
1218            prim_zip:close(PrimZip);
1219        {error, _} ->
1220            ok
1221    end.
1222
1223%%% --------------------------------------------------------
1224%%% Misc. functions.
1225%%% --------------------------------------------------------
1226
1227%%% Look for directory separators
1228is_basename(File) ->
1229    case deep_member($/, File) of
1230        true ->
1231            false;
1232        false ->
1233            case erlang:system_info(os_type) of
1234                {win32, _} ->
1235                    case File of
1236                        [_,$: | _] ->
1237			    false;
1238                        _ ->
1239			    not deep_member($\\, File)
1240                    end;
1241                _ ->
1242                    true
1243            end
1244    end.
1245
1246send_all(U, [IP | AL], Cmd) ->
1247    ?dbg(sendto, {U, IP, ?EBOOT_PORT, Cmd}),
1248    prim_inet:sendto(U, IP, ?EBOOT_PORT, Cmd),
1249    send_all(U, AL, Cmd);
1250send_all(_U, [], _) -> ok.
1251
1252join(P, F) ->
1253    P ++ "/" ++ F.
1254
1255member(X, [X|_]) -> true;
1256member(X, [_|Y]) -> member(X, Y);
1257member(_X, [])   -> false.
1258
1259deep_member(X, [X|_]) ->
1260    true;
1261deep_member(X, [List | Y]) when is_list(List) ->
1262    deep_member(X, List) orelse deep_member(X, Y);
1263deep_member(X, [Atom | Y]) when is_atom(Atom) ->
1264    deep_member(X, atom_to_list(Atom)) orelse deep_member(X, Y);
1265deep_member(X, [_ | Y]) ->
1266    deep_member(X, Y);
1267deep_member(_X, [])   ->
1268    false.
1269
1270keymember(X, I, [Y | _]) when element(I,Y) =:= X -> true;
1271keymember(X, I, [_ | T]) -> keymember(X, I, T);
1272keymember(_X, _I, []) -> false.
1273
1274keysort(I, L) -> keysort(I, L, []).
1275
1276keysort(I, [X | L], Ls) ->
1277    keysort(I, L, keyins(X, I, Ls));
1278keysort(_I, [], Ls) -> Ls.
1279
1280keyins(X, I, [Y | T]) when X < element(I,Y) -> [X,Y|T];
1281keyins(X, I, [Y | T]) -> [Y | keyins(X, I, T)];
1282keyins(X, _I, []) -> [X].
1283
1284to_strs([P|Paths]) when is_atom(P) ->
1285    [atom_to_list(P)|to_strs(Paths)];
1286to_strs([P|Paths]) when is_list(P) ->
1287    [P|to_strs(Paths)];
1288to_strs([_|Paths]) ->
1289    to_strs(Paths);
1290to_strs([]) ->
1291    [].
1292
1293reverse([] = L) ->
1294    L;
1295reverse([_] = L) ->
1296    L;
1297reverse([A, B]) ->
1298    [B, A];
1299reverse([A, B | L]) ->
1300    lists:reverse(L, [B, A]). % BIF
1301
1302%% Returns a reversed list of path components, each component itself a
1303%% reversed list (string), e.g.
1304%% /path/to/file -> ["elif","ot","htap",""]
1305%% /path/to/dir/ -> ["","rid","ot","htap",""]
1306%% Note the "" marking leading and trailing / (slash).
1307path_split(List) ->
1308   path_split(List, [], []).
1309
1310path_split([$/ | Tail], Path, Paths) ->
1311    path_split(Tail, [], [Path | Paths]);
1312path_split([Head | Tail], Path, Paths) ->
1313    path_split(Tail, [Head | Path], Paths);
1314path_split([], Path, Paths) ->
1315    [Path | Paths].
1316
1317%% The opposite of path_split/1
1318path_join(Paths) ->
1319    path_join(Paths,[]).
1320
1321path_join([""],Acc) ->
1322    Acc;
1323path_join([Path],Acc) ->
1324    reverse(Path) ++ Acc;
1325path_join([Path|Paths],Acc) ->
1326    path_join(Paths,"/" ++ reverse(Path) ++ Acc).
1327
1328name_split(undefined, File) ->
1329    %% Ignore primary archive
1330    RevExt = reverse(init:archive_extension()),
1331    case archive_split(File, RevExt, []) of
1332        no_split ->
1333            {file, File};
1334	Archive ->
1335	    Archive
1336    end;
1337name_split(ArchiveFile, File0) ->
1338    %% Look first in primary archive
1339    File = absname(File0),
1340    case string_match(real_path(File), ArchiveFile) of
1341        no_match ->
1342            %% Archive or plain file
1343            name_split(undefined, File);
1344        {match, FileInArchive} ->
1345            %% Primary archive
1346	    {archive, ArchiveFile, FileInArchive}
1347    end.
1348
1349string_match([Char | File], [Char | Archive]) ->
1350    string_match(File, Archive);
1351string_match([] = File, []) ->
1352    {match, File};
1353string_match([$/ | File], []) ->
1354    {match, File};
1355string_match(_File, _Archive) ->
1356    no_match.
1357
1358archive_split("/"++File, RevExt, Acc) ->
1359    case is_prefix(RevExt, Acc) of
1360	false ->
1361	    archive_split(File, RevExt, [$/|Acc]);
1362	true ->
1363	    ArchiveFile = absname(reverse(Acc)),
1364	    {archive, ArchiveFile, File}
1365    end;
1366archive_split([H|T], RevExt, Acc) ->
1367    archive_split(T, RevExt, [H|Acc]);
1368archive_split([], RevExt, Acc) ->
1369    case is_prefix(RevExt, Acc) of
1370	false ->
1371	    no_split;
1372	true ->
1373	    ArchiveFile = absname(reverse(Acc)),
1374	    {archive, ArchiveFile, []}
1375    end.
1376
1377is_prefix([H|T1], [H|T2]) -> is_prefix(T1, T2);
1378is_prefix([_|_], _) -> false;
1379is_prefix([], _ ) -> true.
1380
1381%% Parse list of ipv4 addresses
1382ipv4_list([H | T]) ->
1383    case ipv4_address(H) of
1384        {ok,IP} -> [IP | ipv4_list(T)];
1385        _ -> ipv4_list(T)
1386    end;
1387ipv4_list([]) -> [].
1388
1389%%
1390%% Parse Ipv4 address: d1.d2.d3.d4 (from inet_parse)
1391%%
1392%% Return {ok, IP} | {error, einval}
1393%%
1394ipv4_address(Cs) ->
1395    case catch ipv4_addr(Cs, []) of
1396        {'EXIT',_} -> {error,einval};
1397        Addr -> {ok,Addr}
1398    end.
1399
1400ipv4_addr([C | Cs], IP) when C >= $0, C =< $9 -> ipv4_addr(Cs, C-$0, IP).
1401
1402ipv4_addr([$.|Cs], N, IP) when N < 256 -> ipv4_addr(Cs, [N|IP]);
1403ipv4_addr([C|Cs], N, IP) when C >= $0, C =< $9 ->
1404    ipv4_addr(Cs, N*10 + (C-$0), IP);
1405ipv4_addr([], D, [C,B,A]) when D < 256 -> {A,B,C,D}.
1406
1407%% A simplified version of filename:absname/1
1408absname(Name) ->
1409    Name2 = normalize(Name, []),
1410    case pathtype(Name2) of
1411	absolute ->
1412	    Name2;
1413	relative ->
1414	    case prim_file:get_cwd() of
1415		{ok, Cwd} ->
1416		    Cwd ++ "/" ++ Name2;
1417		{error, _} ->
1418		    Name2
1419	    end;
1420	volumerelative ->
1421	    case prim_file:get_cwd() of
1422		{ok, Cwd} ->
1423		    absname_vr(Name2, Cwd);
1424		{error, _} ->
1425		    Name2
1426	    end
1427    end.
1428
1429%% Assumes normalized name
1430absname_vr([$/ | NameRest], [Drive, $\: | _]) ->
1431    %% Absolute path on current drive.
1432    [Drive, $\: | NameRest];
1433absname_vr([Drive, $\: | NameRest], [Drive, $\: | _] = Cwd) ->
1434    %% Relative to current directory on current drive.
1435    Cwd ++ "/" ++ NameRest;
1436absname_vr([Drive, $\: | NameRest], _) ->
1437    %% Relative to current directory on another drive.
1438    case prim_file:get_cwd([Drive, $\:]) of
1439	{ok, DriveCwd}  ->
1440	    DriveCwd ++ "/" ++ NameRest;
1441	{error, _} ->
1442	    [Drive, $\:, $/] ++ NameRest
1443    end.
1444
1445%% Assumes normalized name
1446pathtype(Name) when is_list(Name) ->
1447    case erlang:system_info(os_type) of
1448	{unix, _}  ->
1449	    unix_pathtype(Name);
1450	{win32, _} ->
1451	    win32_pathtype(Name)
1452    end.
1453
1454unix_pathtype(Name) ->
1455    case Name of
1456	[$/|_] ->
1457	    absolute;
1458	[List|Rest] when is_list(List) ->
1459	    unix_pathtype(List++Rest);
1460	[Atom|Rest] when is_atom(Atom) ->
1461	    atom_to_list(Atom)++Rest;
1462	_ ->
1463	    relative
1464    end.
1465
1466win32_pathtype(Name) ->
1467    case Name of
1468	[List|Rest] when is_list(List) ->
1469	    win32_pathtype(List++Rest);
1470	[Atom|Rest] when is_atom(Atom) ->
1471	    win32_pathtype(atom_to_list(Atom)++Rest);
1472	[Char, List | Rest] when is_list(List) ->
1473	    win32_pathtype([Char | List++Rest]);
1474	[$/, $/|_] ->
1475	    absolute;
1476	[$/|_] ->
1477	    volumerelative;
1478	[C1, C2, List | Rest] when is_list(List) ->
1479	    win32_pathtype([C1, C2|List ++ Rest]);
1480	[_Letter, $:, $/|_] ->
1481	    absolute;
1482	[_Letter, $:|_] ->
1483	    volumerelative;
1484	_ ->
1485	    relative
1486    end.
1487
1488normalize(Name, Acc) ->
1489    case Name of
1490	[List | Rest] when is_list(List) ->
1491	    normalize(List ++ Rest, Acc);
1492	[Atom | Rest] when is_atom(Atom) ->
1493	    normalize(atom_to_list(Atom) ++ Rest, Acc);
1494	[$\\ | Chars] ->
1495	    case erlang:system_info(os_type) of
1496                {win32, _} ->
1497		    normalize(Chars, [$/ | Acc]);
1498		_ ->
1499		    normalize(Chars, [$\\ | Acc])
1500	    end;
1501	[Char | Chars] ->
1502	    normalize(Chars, [Char | Acc]);
1503	[] ->
1504	    reverse(Acc)
1505    end.
1506
1507%% Remove .. and . from the path, e.g.
1508%% /path/./to/this/../file -> /path/to/file
1509%% This includes resolving symlinks.
1510%%
1511%% This is done to ensure that paths are totally normalized before
1512%% comparing to find out if a file is inside the primary archive or
1513%% not.
1514real_path(Name) ->
1515    real_path(Name,reverse(path_split(Name)),[],[]).
1516
1517real_path(_Name,[],Acc,_Links) ->
1518    path_join(Acc);
1519real_path(Name,["."|Paths],Acc,Links) ->
1520    real_path(Name,Paths,Acc,Links);
1521real_path(Name,[".."|Paths],[""]=Acc,Links) ->
1522    %% /.. -> / (can't get higher than root)
1523    real_path(Name,Paths,Acc,Links);
1524real_path(Name,[".."|Paths],[Prev|Acc],Links) when Prev=/=".." ->
1525    real_path(Name,Paths,Acc,Links);
1526real_path(Name,[Path|Paths],Acc,Links) ->
1527    This = [Path|Acc],
1528    ThisFile = path_join(This),
1529    case lists:member(ThisFile,Links) of
1530	true -> % circular!!
1531	    Name;
1532	false ->
1533	    case prim_file:read_link(ThisFile) of
1534		{ok,Link} ->
1535		    case reverse(path_split(Link)) of
1536			[""|_] = LinkPaths ->
1537			    real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]);
1538			LinkPaths ->
1539                % windows currently does not allow creation of relative symlinks
1540                % across different drives
1541				case erlang:system_info(os_type) of
1542                 {win32, _} ->
1543                     real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]);
1544                 _ ->
1545                     real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links])
1546                end
1547		    end;
1548		_ ->
1549		    real_path(Name,Paths,This,Links)
1550	    end
1551    end.
1552
1553load_prim_archive(ArchiveFile, ArchiveBin, #file_info{}=FileInfo) ->
1554    Fun = fun({Components, _GI, _GB}, A) ->
1555		  case Components of
1556		      ["", "nibe", RevApp] -> % Reverse ebin
1557			  %% Collect ebin directories in archive
1558			  Ebin = lists:reverse(RevApp, "/ebin"),
1559			  {true, [Ebin | A]};
1560		      _ ->
1561			  {true, A}
1562		  end
1563	  end,
1564    Ebins0 = [ArchiveFile],
1565    case open_archive({ArchiveFile, ArchiveBin}, FileInfo,
1566		      Ebins0, Fun) of
1567	{ok, PrimZip, {RevEbins, FI, _}} ->
1568	    Ebins = reverse(RevEbins),
1569	    {ok, PrimZip, FI, Ebins};
1570	Error ->
1571	    Error
1572    end;
1573load_prim_archive(ArchiveFile, FileInfo, ParserFun) ->
1574    case ParserFun(ArchiveFile) of
1575	{ok, ArchiveBin} ->
1576	    load_prim_archive(ArchiveFile, ArchiveBin, FileInfo);
1577	Error ->
1578	    Error
1579    end.
1580