1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
21
%% A primary filer, provides two different methods to fetch a file:
22
%% efile and inet. The efile method is simple communication with a
25
%% The distribution loading was removed and replaced with
28
%% The start_it/4 function initializes a record with callback
29
%% functions used to handle the interface functions.
32
-module(erl_prim_loader).
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)
42
-include("inet_boot.hrl").
45
-export([start/3, set_path/1, get_path/0, get_file/1, get_files/2,
46
list_dir/1, read_file_info/1, get_cwd/0, get_cwd/1]).
48
%% Used by erl_boot_server
49
-export([prim_init/0, prim_get_file/2, prim_list_dir/2,
50
prim_read_file_info/2, prim_get_cwd/2]).
52
%% Used by escript and code
53
-export([set_primary_archive/2, release_archives/0]).
55
%% Internal function. Exported to avoid dialyzer warnings
58
-include_lib("kernel/include/file.hrl").
61
{loader :: 'efile' | 'inet',
62
hosts = [], % hosts list (to boot from)
63
id, % not used any more?
65
timeout, % idle timeout
66
n_timeouts, % Number of timeouts before archives are released
67
multi_get = false :: bool(),
68
prim_state}). % state for efile code loader
70
-define(IDLE_TIMEOUT, 60000). %% tear inet connection after 1 minutes
71
-define(N_TIMEOUTS, 6). %% release efile archive after 6 minutes
73
%% Defines for inet as prim_loader
74
-define(INET_FAMILY, inet).
75
-define(INET_ADDRESS, {0,0,0,0}).
78
-define(dbg(Tag, Data), erlang:display({Tag,Data})).
80
-define(dbg(Tag, Data), true).
83
-define(SAFE2(Expr, State),
86
{'EXIT',XXXReason} -> {{error,XXXReason}, State};
91
-record(prim_state, {debug, cache, primary_archive}).
93
debug(#prim_state{debug = Deb}, Term) ->
96
true -> erlang:display(Term)
99
%%% --------------------------------------------------------
100
%%% Interface Functions.
101
%%% --------------------------------------------------------
103
-spec(start/3 :: (_, atom() | string(), atom() | [atom()]) -> {'ok',pid()} | {'error',_}).
105
start(Id, Pgm, Hosts) when is_atom(Hosts) ->
106
start(Id, Pgm, [Hosts]);
107
start(Id, Pgm0, Hosts) ->
115
Pid = spawn_link(fun() -> start_it(Pgm, Id, Self, Hosts) end),
116
register(erl_prim_loader, Pid),
120
{'EXIT',Pid,Reason} ->
124
start_it("ose_inet", Id, Pid, Hosts) ->
125
%% Setup reserved port for ose_inet driver (only OSE)
126
case catch erlang:open_port({spawn,ose_inet},[binary]) of
128
?dbg(ose_inet_port_open_fail, Why),
131
?dbg(ose_inet_port, OseInetPort),
134
start_it("inet", Id, Pid, Hosts);
136
%% Hosts must be a list on form ['1.2.3.4' ...]
137
start_it("inet", Id, Pid, Hosts) ->
138
process_flag(trap_exit, true),
139
?dbg(inet, {Id,Pid,Hosts}),
140
AL = ipv4_list(Hosts),
142
{ok,Tcp} = find_master(AL),
145
State = #state {loader = inet,
149
timeout = ?IDLE_TIMEOUT,
150
n_timeouts = ?N_TIMEOUTS,
152
loop(State, Pid, []);
154
start_it("efile", Id, Pid, _Hosts) ->
155
process_flag(trap_exit, true),
156
{ok, Port} = prim_file:open([binary]),
158
MultiGet = case erlang:system_info(thread_pool_size) of
163
State = #state {loader = efile,
167
multi_get = MultiGet,
169
loop(State, Pid, []).
175
-spec(set_path/1 :: ([string()]) -> 'ok').
177
set_path(Paths) when is_list(Paths) ->
178
request({set_path,Paths}).
181
-spec(get_path/0 :: () -> {'ok',[string()]}).
184
request({get_path,[]}).
186
%% -> {ok,BinFile,File} | error
187
-spec(get_file/1 :: (atom() | string()) -> {'ok',binary(),string()} | 'error').
189
get_file(File) when is_atom(File) ->
190
get_file(atom_to_list(File));
192
check_file_result(get_file, File, request({get_file,File})).
194
%% -> ok | {error,Module}
195
get_files(ModFiles, Fun) ->
196
case request({get_files,{ModFiles,Fun}}) of
200
check_file_result(get_files, M, {error,Reason}),
206
%% -> {ok,List} | error
207
-spec(list_dir/1 :: (string()) -> {'ok',[string()]} | 'error').
210
check_file_result(list_dir, Dir, request({list_dir,Dir})).
212
%% -> {ok,Info} | error
213
-spec(read_file_info/1 :: (string()) -> {'ok',tuple()} | 'error').
215
read_file_info(File) ->
216
check_file_result(read_file_info, File, request({read_file_info,File})).
218
%% -> {ok,Cwd} | error
219
-spec(get_cwd/0 :: () -> {'ok',string()} | 'error').
222
check_file_result(get_cwd, [], request({get_cwd,[]})).
224
%% -> {ok,Cwd} | error
225
-spec(get_cwd/1 :: (string()) -> {'ok',string()} | 'error').
228
check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})).
230
-spec(set_primary_archive/2 :: (File :: string() | 'undefined',
231
ArchiveBin :: binary() | 'undefined')
232
-> {ok, [string()]} | {error,_}).
234
set_primary_archive(undefined, undefined) ->
235
request({set_primary_archive, undefined, undefined});
236
set_primary_archive(File, ArchiveBin)
237
when is_list(File), is_binary(ArchiveBin) ->
238
request({set_primary_archive, File, ArchiveBin}).
240
-spec(release_archives/0 :: () -> ok | {error,_}).
242
release_archives() ->
243
request(release_archives).
246
Loader = whereis(erl_prim_loader),
247
Loader ! {self(),Req},
251
{'EXIT',Loader,_What} ->
255
check_file_result(_, _, {error,enoent}) ->
257
check_file_result(_, _, {error,enotdir}) ->
259
check_file_result(Func, Target, {error,Reason}) ->
260
case (catch atom_to_list(Reason)) of
261
{'EXIT',_} -> % exit trapped
264
Process = case process_info(self(), registered_name) of
265
{registered_name,R} ->
266
"Process: " ++ atom_to_list(R) ++ ".";
271
if is_atom(Target) -> atom_to_list(Target);
272
is_list(Target) -> Target;
278
"File operation error: " ++ Errno ++ ". " ++
279
"Function: " ++ atom_to_list(Func) ++ ". " ++ Process;
281
"File operation error: " ++ Errno ++ ". " ++
282
"Target: " ++ TargetStr ++ ". " ++
283
"Function: " ++ atom_to_list(Func) ++ ". " ++ Process
285
%% this is equal to calling error_logger:error_report/1 which
286
%% we don't want to do from code_server during system boot
287
error_logger ! {notify,{error_report,group_leader(),
288
{self(),std_error,Report}}},
291
check_file_result(_, _, Other) ->
294
%%% --------------------------------------------------------
296
%%% --------------------------------------------------------
298
loop(State, Parent, Paths) ->
300
{Pid,Req} when is_pid(Pid) ->
301
%% erlang:display(Req),
302
{Resp,State2,Paths2} =
304
{set_path,NewPaths} ->
305
{ok,State,to_strs(NewPaths)};
307
{{ok,Paths},State,Paths};
309
{Res,State1} = handle_get_file(State, Paths, File),
311
{get_files,{ModFiles,Fun}} ->
312
{Res,State1} = handle_get_files(State, ModFiles, Paths, Fun),
315
{Res,State1} = handle_list_dir(State, Dir),
317
{read_file_info,File} ->
318
{Res,State1} = handle_read_file_info(State, File),
321
{Res,State1} = handle_get_cwd(State, []),
323
{get_cwd,[_]=Args} ->
324
{Res,State1} = handle_get_cwd(State, Args),
326
{set_primary_archive,File,Bin} ->
327
{Res,State1} = handle_set_primary_archive(State, File, Bin),
330
{Res,State1} = handle_release_archives(State),
335
if Resp =:= ignore -> ok;
336
true -> Pid ! {self(),Resp}
339
is_record(State2, state) ->
340
loop(State2, Parent, Paths2);
342
exit({bad_state, Req, State2})
348
State1 = handle_exit(State, P, W),
349
loop(State1, Parent, Paths);
351
loop(State, Parent, Paths)
352
after State#state.timeout ->
353
State1 = handle_timeout(State, Parent),
354
loop(State1, Parent, Paths)
357
handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) ->
358
?SAFE2(efile_multi_get_file_from_port(State, ModFiles, Paths, Fun), State);
359
handle_get_files(State, _ModFiles, _Paths, _Fun) -> % no multi get
360
{{error,no_multi_get},State}.
362
handle_get_file(State = #state{loader = efile}, Paths, File) ->
363
?SAFE2(efile_get_file_from_port(State, File, Paths), State);
364
handle_get_file(State = #state{loader = inet}, Paths, File) ->
365
?SAFE2(inet_get_file_from_port(State, File, Paths), State).
367
handle_set_primary_archive(State= #state{loader = efile}, File, Bin) ->
368
?SAFE2(efile_set_primary_archive(State, File, Bin), State).
370
handle_release_archives(State= #state{loader = efile}) ->
371
?SAFE2(efile_release_archives(State), State).
373
handle_list_dir(State = #state{loader = efile}, Dir) ->
374
?SAFE2(efile_list_dir(State, Dir), State);
375
handle_list_dir(State = #state{loader = inet}, Dir) ->
376
?SAFE2(inet_list_dir(State, Dir), State).
378
handle_read_file_info(State = #state{loader = efile}, File) ->
379
?SAFE2(efile_read_file_info(State, File), State);
380
handle_read_file_info(State = #state{loader = inet}, File) ->
381
?SAFE2(inet_read_file_info(State, File), State).
383
handle_get_cwd(State = #state{loader = efile}, Drive) ->
384
?SAFE2(efile_get_cwd(State, Drive), State);
385
handle_get_cwd(State = #state{loader = inet}, Drive) ->
386
?SAFE2(inet_get_cwd(State, Drive), State).
388
handle_stop(State = #state{loader = efile}) ->
389
efile_stop_port(State);
390
handle_stop(State = #state{loader = inet}) ->
391
inet_stop_port(State).
393
handle_exit(State = #state{loader = efile}, Who, Reason) ->
394
efile_exit_port(State, Who, Reason);
395
handle_exit(State = #state{loader = inet}, Who, Reason) ->
396
inet_exit_port(State, Who, Reason).
398
handle_timeout(State = #state{loader = efile}, Parent) ->
399
efile_timeout_handler(State, Parent);
400
handle_timeout(State = #state{loader = inet}, Parent) ->
401
inet_timeout_handler(State, Parent).
403
%%% --------------------------------------------------------
404
%%% Functions which handles efile as prim_loader (default).
405
%%% --------------------------------------------------------
407
%%% Reading many files in parallel is an optimization.
408
%%% See also comment in init.erl.
410
%% -> {ok,State} | {{error,Module},State} | {{error,Reason,Module},State}
411
efile_multi_get_file_from_port(State, ModFiles, Paths, Fun) ->
413
%% More than 200 processes is no gain.
414
Max = min(200, erlang:system_info(thread_pool_size)),
415
efile_multi_get_file_from_port2(ModFiles, 0, Max, State, Paths, Fun, Ref, ok).
417
efile_multi_get_file_from_port2([MF | MFs], Out, Max, State, Paths, Fun, Ref, Ret) when Out < Max ->
419
_Pid = spawn(fun() -> efile_par_get_file(Ref, State, MF, Paths, Self, Fun) end),
420
efile_multi_get_file_from_port2(MFs, Out+1, Max, State, Paths, Fun, Ref, Ret);
421
efile_multi_get_file_from_port2(MFs, Out, Max, _State, Paths, Fun, Ref, Ret) when Out > 0 ->
424
efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Ret);
425
{Ref, {error,_Mod} = Error, State1} ->
426
efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Error);
427
{Ref, MF, {error,emfile,State1}} ->
428
%% Max can take negative values. Out cannot.
429
efile_multi_get_file_from_port2([MF | MFs], Out-1, Max-1, State1, Paths, Fun, Ref, Ret);
430
{Ref, {M,_F}, {error,Error,State1}} ->
431
efile_multi_get_file_from_port2(MFs, Out-1, 0, State1, Paths, Fun, Ref, {error,Error,M})
433
efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) ->
436
efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) ->
437
%% One port for each file read in "parallel":
438
case prim_file:open([binary]) of
440
Port0 = State#state.data,
441
State1 = State#state{data = Port},
442
R = case efile_get_file_from_port(State1, File, Paths) of
443
{{error,Reason},State2} ->
444
{Ref,MF,{error,Reason,State2}};
445
{{ok,BinFile,Full},State2} ->
446
%% Fun(...) -> ok | {error,Mod}
447
{Ref,Fun(Mod, BinFile, Full),State2#state{data=Port0}}
449
prim_file:close(Port),
452
Pid ! {Ref,MF,{error,Error,State}}
455
%% -> {{ok,BinFile,File},State} | {{error,Reason},State}
456
efile_get_file_from_port(State, File, Paths) ->
457
case is_basename(File) of
458
false -> % get absolute file name.
459
efile_get_file_from_port2(State, File);
460
true when Paths =:= [] -> % get plain file name.
461
efile_get_file_from_port2(State, File);
463
efile_get_file_from_port3(State, File, Paths)
466
efile_get_file_from_port2(#state{prim_state = PS} = State, File) ->
467
{Res, PS2} = prim_get_file(PS, File),
470
exit('prim_load port died');
472
{{error,Reason},State#state{prim_state = PS2}};
474
{{ok,BinFile,File},State#state{prim_state = PS2}}
477
efile_get_file_from_port3(State, File, [P | Paths]) ->
478
case efile_get_file_from_port2(State, concat([P,"/",File])) of
479
{{error,Reason},State1} when Reason =/= emfile ->
481
[] -> % return last error
482
{{error,Reason},State1};
483
_ -> % try more paths
484
efile_get_file_from_port3(State1, File, Paths)
489
efile_get_file_from_port3(State, _File, []) ->
490
{{error,enoent},State}.
492
efile_set_primary_archive(#state{prim_state = PS} = State, File, Bin) ->
493
{Res, PS2} = prim_set_primary_archive(PS, File, Bin),
494
{Res,State#state{prim_state = PS2}}.
496
efile_release_archives(#state{prim_state = PS} = State) ->
497
{Res, PS2} = prim_release_archives(PS),
498
{Res,State#state{prim_state = PS2}}.
500
efile_list_dir(#state{prim_state = PS} = State, Dir) ->
501
{Res, PS2} = prim_list_dir(PS, Dir),
502
{Res, State#state{prim_state = PS2}}.
504
efile_read_file_info(#state{prim_state = PS} = State, File) ->
505
{Res, PS2} = prim_read_file_info(PS, File),
506
{Res, State#state{prim_state = PS2}}.
508
efile_get_cwd(#state{prim_state = PS} = State, Drive) ->
509
{Res, PS2} = prim_get_cwd(PS, Drive),
510
{Res, State#state{prim_state = PS2}}.
512
efile_stop_port(#state{data=Port}=State) ->
513
prim_file:close(Port),
514
State#state{data=noport}.
516
efile_exit_port(State, Port, Reason) when State#state.data =:= Port ->
517
exit({port_died,Reason});
518
efile_exit_port(State, _Port, _Reason) ->
521
efile_timeout_handler(#state{n_timeouts = N} = State, _Parent) ->
524
{_Res, State2} = efile_release_archives(State),
525
State2#state{n_timeouts = ?N_TIMEOUTS};
527
State#state{n_timeouts = N - 1}
530
%%% --------------------------------------------------------
531
%%% Functions which handles inet prim_loader
532
%%% --------------------------------------------------------
535
%% Connect to a boot master
536
%% return {ok, Socket} TCP
537
%% AL is a list of boot servers (including broadcast addresses)
540
find_master(AL, ?EBOOT_RETRY, ?EBOOT_REQUEST_DELAY, ?EBOOT_SHORT_RETRY_SLEEP,
541
?EBOOT_UNSUCCESSFUL_TRIES, ?EBOOT_LONG_RETRY_SLEEP).
543
find_master(AL, Retry, ReqDelay, SReSleep, Tries, LReSleep) ->
544
{ok,U} = ll_udp_open(0),
545
find_master(U, Retry, AL, ReqDelay, SReSleep, [], Tries, LReSleep).
548
%% Master connect loop
550
find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
551
case find_loop(U, Retry, AddrL, ReqDelay, SReSleep, Ignore,
554
find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore,
557
?dbg(servers, Servers),
558
case connect_master(Servers) of
563
find_master(U, Retry, AddrL, ReqDelay, SReSleep,
564
Servers ++ Ignore, Tries, LReSleep)
568
connect_master([{_Prio,IP,Port} | Servers]) ->
569
case ll_tcp_connect(0, IP, Port) of
571
_Error -> connect_master(Servers)
573
connect_master([]) ->
577
%% Always return a list of boot servers or hang.
579
find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
580
case find_loop(U, Retry, AL, ReqDelay, []) of
581
[] -> % no response from any server
582
erlang:display({erl_prim_loader,'no server found'}), % lifesign
583
Tries1 = if Tries > 0 ->
590
find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries1, LReSleep);
592
keysort(1, Servers -- Ignore)
596
find_loop(_U, 0, _AL, _Delay, Acc) ->
598
find_loop(U, Retry, AL, Delay, Acc) ->
599
send_all(U, AL, [?EBOOT_REQUEST, erlang:system_info(version)]),
600
find_collect(U, Retry-1, AL, Delay, Acc).
602
find_collect(U,Retry,AL,Delay,Acc) ->
604
{udp, U, IP, _Port, [$E,$B,$O,$O,$T,$R,Priority,T1,T0 | _Version]} ->
605
Elem = {Priority,IP,T1*256+T0},
607
case member(Elem, Acc) of
608
false -> find_collect(U, Retry, AL, Delay, [Elem | Acc]);
609
true -> find_collect(U, Retry, AL, Delay, Acc)
612
?dbg(collect_garbage, _Garbage),
613
find_collect(U, Retry, AL, Delay, Acc)
616
?dbg(collected, Acc),
617
case keymember(0, 1, Acc) of %% got high priority server?
619
false -> find_loop(U, Retry, AL, Delay, Acc)
625
receive after Time -> ok end.
627
inet_exit_port(State, Port, _Reason) when State#state.data =:= Port ->
628
State#state { data = noport, timeout = infinity };
629
inet_exit_port(State, _, _) ->
633
inet_timeout_handler(State, _Parent) ->
634
Tcp = State#state.data,
635
if is_port(Tcp) -> ll_close(Tcp);
638
State#state { timeout = infinity, data = noport }.
640
%% -> {{ok,BinFile,Tag},State} | {{error,Reason},State}
641
inet_get_file_from_port(State, File, Paths) ->
642
case is_basename(File) of
643
false -> % get absolute file name.
644
inet_send_and_rcv({get,File}, File, State);
645
true when Paths =:= [] -> % get plain file name.
646
inet_send_and_rcv({get,File}, File, State);
648
inet_get_file_from_port1(File, Paths, State)
651
inet_get_file_from_port1(File, [P | Paths], State) ->
652
File1 = concat([P,"/",File]),
653
case inet_send_and_rcv({get,File1}, File1, State) of
654
{{error,Reason},State1} ->
656
[] -> % return last error
657
{{error,Reason},State1};
658
_ -> % try more paths
659
inet_get_file_from_port1(File, Paths, State1)
663
inet_get_file_from_port1(_File, [], State) ->
664
{{error,file_not_found},State}.
666
inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport ->
667
{ok,Tcp} = find_master(State#state.hosts), %% reconnect
668
inet_send_and_rcv(Msg, Tag, State#state { data = Tcp,
669
timeout = ?IDLE_TIMEOUT });
670
inet_send_and_rcv(Msg, Tag, #state{data=Tcp,timeout=Timeout}=State) ->
671
prim_inet:send(Tcp, term_to_binary(Msg)),
674
case catch binary_to_term(BinMsg) of
675
{get,{ok,BinFile}} ->
676
{{ok,BinFile,Tag},State};
679
{_Cmd,{error,Error}} ->
680
{{error,Error},State};
682
{{error,Error},State};
684
{{error,Error},State}
687
%% Ok we must reconnect
688
inet_send_and_rcv(Msg, Tag, State#state { data = noport });
689
{tcp_error,Tcp,_Reason} ->
690
%% Ok we must reconnect
691
inet_send_and_rcv(Msg, Tag, inet_stop_port(State));
693
%% Ok we must reconnect
694
inet_send_and_rcv(Msg, Tag, State#state { data = noport })
696
%% Ok we must reconnect
697
inet_send_and_rcv(Msg, Tag, inet_stop_port(State))
700
%% -> {{ok,List},State} | {{error,Reason},State}
701
inet_list_dir(State, Dir) ->
702
inet_send_and_rcv({list_dir,Dir}, list_dir, State).
704
%% -> {{ok,Info},State} | {{error,Reason},State}
705
inet_read_file_info(State, File) ->
706
inet_send_and_rcv({read_file_info,File}, read_file_info, State).
708
%% -> {{ok,Cwd},State} | {{error,Reason},State}
709
inet_get_cwd(State, []) ->
710
inet_send_and_rcv(get_cwd, get_cwd, State);
711
inet_get_cwd(State, [Drive]) ->
712
inet_send_and_rcv({get_cwd,Drive}, get_cwd, State).
714
inet_stop_port(#state{data=Tcp}=State) ->
715
prim_inet:close(Tcp),
716
State#state{data=noport}.
718
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
720
%% Direct inet_drv access
722
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725
[{mode,binary}, {packet,4}, {active, true}, {deliver,term}].
730
%% options for udp [list, {broadcast, true}, {active,true}]
732
[{mode,list}, {active, true}, {deliver,term}, {broadcast,true}].
734
%% INET version IPv4 addresses
736
ll_tcp_connect(LocalPort, IP, RemotePort) ->
737
case ll_open_set_bind(tcp, ?INET_FAMILY, tcp_options(),
738
?INET_ADDRESS, LocalPort) of
740
case prim_inet:connect(S, IP, RemotePort, tcp_timeout()) of
742
Error -> port_error(S, Error)
748
%% Open and initialize an udp port for broadcast
751
ll_open_set_bind(udp, ?INET_FAMILY, udp_options(), ?INET_ADDRESS, P).
754
ll_open_set_bind(Protocol, Family, SOpts, IP, Port) ->
755
case prim_inet:open(Protocol, Family) of
757
case prim_inet:setopts(S, SOpts) of
759
case prim_inet:bind(S, IP, Port) of
762
Error -> port_error(S, Error)
764
Error -> port_error(S, Error)
774
port_error(S, Error) ->
779
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783
case init:get_argument(loader_debug) of
787
cache_new(#prim_state{debug = Deb}).
789
prim_release_archives(PS) ->
790
debug(PS, release_archives),
791
{Res, PS2}= prim_do_release_archives(PS, get(), []),
792
debug(PS2, {return, Res}),
795
prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) ->
798
{primary, _PrimZip} ->
799
ok; % Keep primary archive
801
debug(PS, {release, cache, ArchiveFile}),
803
clear_cache(ArchiveFile, Cache)
807
prim_do_release_archives(PS, KeyVals, Acc);
809
prim_do_release_archives(PS, KeyVals, [{ArchiveFile, Reason} | Acc])
811
prim_do_release_archives(PS, [], []) ->
812
{ok, PS#prim_state{primary_archive = undefined}};
813
prim_do_release_archives(PS, [], Errors) ->
814
{{error, Errors}, PS#prim_state{primary_archive = undefined}}.
816
prim_set_primary_archive(PS, undefined, undefined) ->
817
debug(PS, {set_primary_archive, clean}),
818
case PS#prim_state.primary_archive of
820
Res = {error, enoent},
821
debug(PS, {return, Res}),
824
{primary, PrimZip} = erase(ArchiveFile),
825
ok = prim_zip:close(PrimZip),
826
PS2 = PS#prim_state{primary_archive = undefined},
828
debug(PS2, {return, Res}),
831
prim_set_primary_archive(PS, ArchiveFile, ArchiveBin)
832
when is_list(ArchiveFile), is_binary(ArchiveBin) ->
833
%% Try the archive file
834
debug(PS, {set_primary_archive, ArchiveFile, byte_size(ArchiveBin)}),
836
case PS#prim_state.primary_archive of
839
fun({Funny, _GI, _GB}, A) ->
841
["", "nibe", RevApp] -> % Reverse ebin
842
%% Collect ebin directories in archive
843
Ebin = reverse(RevApp) ++ "/ebin",
849
Ebins0 = [ArchiveFile],
850
case open_archive({ArchiveFile, ArchiveBin}, Ebins0, Fun) of
851
{ok, PrimZip, RevEbins} ->
852
Ebins = reverse(RevEbins),
853
debug(PS, {set_primary_archive, Ebins}),
854
put(ArchiveFile, {primary, PrimZip}),
855
{{ok, Ebins}, PS#prim_state{primary_archive = ArchiveFile}};
857
debug(PS, {set_primary_archive, Error}),
861
debug(PS, {set_primary_archive, clean}),
862
PrimZip = erase(OldArchiveFile),
863
ok = prim_zip:close(PrimZip),
864
PS2 = PS#prim_state{primary_archive = undefined},
865
prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin)
867
debug(PS3, {return, Res3}),
870
prim_get_file(PS, File) ->
871
debug(PS, {get_file, File}),
873
case name_split(PS#prim_state.primary_archive, File) of
875
Res = prim_file:read_file(PrimFile),
877
{archive, ArchiveFile, FileInArchive} ->
878
debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
879
FunnyFile = funny_split(FileInArchive, $/),
881
fun({Funny, _GetInfo, GetBin}, Acc) ->
883
Funny =:= FunnyFile ->
884
{false, {ok, GetBin()}};
889
apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
891
debug(PS, {return, Res2}),
894
%% -> {{ok,List},State} | {{error,Reason},State}
895
prim_list_dir(PS, Dir) ->
896
debug(PS, {list_dir, Dir}),
898
case name_split(PS#prim_state.primary_archive, Dir) of
900
Res = prim_file:list_dir(PrimDir),
902
{archive, ArchiveFile, FileInArchive} ->
903
debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
904
FunnyDir = funny_split(FileInArchive, $/),
906
fun({Funny, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
908
[RevName | FD] when FD =:= FunnyDir ->
911
%% The listed directory
915
Name = reverse(RevName),
916
{true, {Status, [Name | Names]}}
918
["", RevName | FD] when FD =:= FunnyDir ->
920
Name = reverse(RevName),
921
{true, {Status, [Name | Names]}};
922
[RevName] when FunnyDir =:= [""] ->
924
Name = reverse(RevName),
925
{true, {ok, [Name | Names]}};
926
["", RevName] when FunnyDir =:= [""] ->
928
Name = reverse(RevName),
929
{true, {ok, [Name | Names]}};
935
{{Status, Names}, PS2} =
936
apply_archive(PS, Fun, {error, []}, ArchiveFile),
938
ok -> {{ok, Names}, PS2};
939
error -> {{error, enotdir}, PS2}
942
debug(PS, {return, Res2}),
945
%% -> {{ok,Info},State} | {{error,Reason},State}
946
prim_read_file_info(PS, File) ->
947
debug(PS, {read_file_info, File}),
949
case name_split(PS#prim_state.primary_archive, File) of
951
Res = prim_file:read_file_info(PrimFile),
953
{archive, ArchiveFile, []} ->
954
%% Fake top directory
955
debug(PS, {archive_read_file_info, ArchiveFile}),
956
case prim_file:read_file_info(ArchiveFile) of
958
{{ok, FI#file_info{type = directory}}, PS};
962
{archive, ArchiveFile, FileInArchive} ->
963
debug(PS, {archive_read_file_info, File}),
964
FunnyFile = funny_split(FileInArchive, $/),
966
fun({Funny, GetInfo, _GetBin}, Acc) ->
969
tl(Funny) =:= FunnyFile ->
971
{false, {ok, GetInfo()}};
972
Funny =:= FunnyFile ->
974
{false, {ok, GetInfo()}};
980
apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
982
debug(PS2, {return, Res2}),
985
prim_get_cwd(PS, []) ->
986
debug(PS, {get_cwd, []}),
987
Res = prim_file:get_cwd(),
988
debug(PS, {return, Res}),
990
prim_get_cwd(PS, [Drive]) ->
991
debug(PS, {get_cwd, Drive}),
992
Res = prim_file:get_cwd(Drive),
993
debug(PS, {return, Res}),
996
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998
apply_archive(PS, Fun, Acc, Archive) ->
1001
case prim_file:read_file_info(Archive) of
1002
{ok, #file_info{mtime = Mtime}} ->
1003
case open_archive(Archive, Acc, Fun) of
1004
{ok, PrimZip, Acc2} ->
1005
debug(PS, {cache, ok}),
1006
put(Archive, {Mtime, {ok, PrimZip}}),
1009
debug(PS, {cache, Error}),
1010
put(Archive, {Mtime, Error}),
1014
debug(PS, {cache, Error}),
1017
{primary, PrimZip} ->
1018
case foldl_archive(PrimZip, Acc, Fun) of
1019
{ok, _PrimZip2, Acc2} ->
1022
debug(PS, {primary, Error}),
1026
case prim_file:read_file_info(Archive) of
1027
{ok, #file_info{mtime = Mtime2}} when Mtime2 =:= Mtime ->
1030
case foldl_archive(PrimZip, Acc, Fun) of
1031
{ok, _PrimZip2, Acc2} ->
1034
debug(PS, {cache, {clear, Error}}),
1035
clear_cache(Archive, Cache),
1036
debug(PS, {cache, Error}),
1037
put(Archive, {Mtime, Error}),
1041
debug(PS, {cache, Error}),
1045
debug(PS, {cache, {clear, Error}}),
1046
clear_cache(Archive, Cache),
1047
apply_archive(PS, Fun, Acc, Archive)
1051
open_archive(Archive, Acc, Fun) ->
1053
fun({N, GI, GB}, A) ->
1054
%% Ensure full iteration at open
1055
Funny = funny_split(N, $/),
1056
{_Continue, A2} = Fun({Funny, GI, GB}, A),
1057
{true, {true, Funny}, A2}
1059
prim_zip:open(Wrapper, Acc, Archive).
1061
foldl_archive(PrimZip, Acc, Fun) ->
1063
fun({N, GI, GB}, A) ->
1064
%% Allow partial iteration at foldl
1065
{Continue, A2} = Fun({N, GI, GB}, A),
1066
{Continue, true, A2}
1068
prim_zip:foldl(Wrapper, Acc, PrimZip).
1073
clear_cache(Archive, Cache) ->
1077
prim_zip:close(PrimZip);
1082
%%% --------------------------------------------------------
1083
%%% Misc. functions.
1084
%%% --------------------------------------------------------
1086
%%% Look for directory separators
1087
is_basename(File) ->
1088
case member($/, File) of
1092
case erlang:system_info(os_type) of
1096
_ -> not member($\\, File)
1103
send_all(U, [IP | AL], Cmd) ->
1104
?dbg(sendto, {U, IP, ?EBOOT_PORT, Cmd}),
1105
prim_inet:sendto(U, IP, ?EBOOT_PORT, Cmd),
1106
send_all(U, AL, Cmd);
1107
send_all(_U, [], _) -> ok.
1109
concat([A|T]) when is_atom(A) -> %Atom
1110
atom_to_list(A) ++ concat(T);
1111
concat([C|T]) when C >= 0, C =< 255 ->
1113
concat([S|T]) -> %String
1118
member(X, [X|_]) -> true;
1119
member(X, [_|Y]) -> member(X, Y);
1120
member(_X, []) -> false.
1122
keymember(X, I, [Y | _]) when element(I,Y) =:= X -> true;
1123
keymember(X, I, [_ | T]) -> keymember(X, I, T);
1124
keymember(_X, _I, []) -> false.
1126
keysort(I, L) -> keysort(I, L, []).
1128
keysort(I, [X | L], Ls) ->
1129
keysort(I, L, keyins(X, I, Ls));
1130
keysort(_I, [], Ls) -> Ls.
1132
keyins(X, I, [Y | T]) when X < element(I,Y) -> [X,Y|T];
1133
keyins(X, I, [Y | T]) -> [Y | keyins(X, I, T)];
1134
keyins(X, _I, []) -> [X].
1136
min(X, Y) when X < Y -> X;
1139
to_strs([P|Paths]) when is_atom(P) ->
1140
[atom_to_list(P)|to_strs(Paths)];
1141
to_strs([P|Paths]) when is_list(P) ->
1143
to_strs([_|Paths]) ->
1154
reverse([A, B | L]) ->
1155
lists:reverse(L, [B, A]). % BIF
1157
%% Returns all lists in reverse order
1158
funny_split(List, Sep) ->
1159
funny_split(List, Sep, [], []).
1161
funny_split([Sep | Tail], Sep, Path, Paths) ->
1162
funny_split(Tail, Sep, [], [Path | Paths]);
1163
funny_split([Head | Tail], Sep, Path, Paths) ->
1164
funny_split(Tail, Sep, [Head | Path], Paths);
1165
funny_split([], _Sep, Path, Paths) ->
1168
name_split(ArchiveFile, File0) ->
1169
File = absname(File0),
1170
do_name_split(ArchiveFile, File).
1172
do_name_split(undefined, File) ->
1173
%% Ignore primary archive
1174
case string_split(File, init:archive_extension(), []) of
1178
{split, _RevArchiveBase, RevArchiveFile, []} ->
1179
%% Top dir in archive
1180
ArchiveFile = reverse(RevArchiveFile),
1181
{archive, ArchiveFile, []};
1182
{split, _RevArchiveBase, RevArchiveFile, [$/ | FileInArchive]} ->
1184
ArchiveFile = reverse(RevArchiveFile),
1185
{archive, ArchiveFile, FileInArchive};
1186
{split, _RevArchiveBase, _RevArchiveFile, _FileInArchive} ->
1187
%% False match. Assume plain file
1190
do_name_split(ArchiveFile0, File) ->
1191
%% Look first in primary archive
1192
ArchiveFile = absname(ArchiveFile0),
1193
case string_match(File, ArchiveFile, []) of
1195
%% Archive or plain file
1196
do_name_split(undefined, File);
1197
{match, _RevPrimArchiveFile, FileInArchive} ->
1199
case FileInArchive of
1200
[$/ | FileInArchive2] ->
1201
{archive, ArchiveFile, FileInArchive2};
1203
{archive, ArchiveFile, FileInArchive}
1207
string_match([Char | File], [Char | Archive], RevTop) ->
1208
string_match(File, Archive, [Char | RevTop]);
1209
string_match(File, [], RevTop) ->
1210
{match, RevTop, File};
1211
string_match(_File, _Archive, _RevTop) ->
1214
string_split([Char | File], [Char | Ext] = FullExt, RevTop) ->
1215
RevTop2 = [Char | RevTop],
1216
string_split2(File, Ext, RevTop, RevTop2, File, FullExt, RevTop2);
1217
string_split([Char | File], Ext, RevTop) ->
1218
string_split(File, Ext, [Char | RevTop]);
1219
string_split([], _Ext, _RevTop) ->
1222
string_split2([Char | File], [Char | Ext], RevBase, RevTop, SaveFile, SaveExt, SaveTop) ->
1223
string_split2(File, Ext, RevBase, [Char | RevTop], SaveFile, SaveExt, SaveTop);
1224
string_split2(File, [], RevBase, RevTop, _SaveFile, _SaveExt, _SaveTop) ->
1225
{split, RevBase, RevTop, File};
1226
string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) ->
1227
string_split(SaveFile, SaveExt, SaveTop).
1229
%% Parse list of ipv4 addresses
1230
ipv4_list([H | T]) ->
1231
IPV = if is_atom(H) -> ipv4_address(atom_to_list(H));
1232
is_list(H) -> ipv4_address(H);
1233
true -> {error,einal}
1236
{ok,IP} -> [IP | ipv4_list(T)];
1239
ipv4_list([]) -> [].
1242
%% Parse Ipv4 address: d1.d2.d3.d4 (from inet_parse)
1244
%% Return {ok, IP} | {error, einval}
1247
case catch ipv4_addr(Cs, []) of
1248
{'EXIT',_} -> {error,einval};
1252
ipv4_addr([C | Cs], IP) when C >= $0, C =< $9 -> ipv4_addr(Cs, C-$0, IP).
1254
ipv4_addr([$.|Cs], N, IP) when N < 256 -> ipv4_addr(Cs, [N|IP]);
1255
ipv4_addr([C|Cs], N, IP) when C >= $0, C =< $9 ->
1256
ipv4_addr(Cs, N*10 + (C-$0), IP);
1257
ipv4_addr([], D, [C,B,A]) when D < 256 -> {A,B,C,D}.
1259
%% A simplified version of filename:absname/1
1261
case pathtype(Name) of
1263
normalize(Name, []);
1265
case prim_file:get_cwd() of
1267
normalize(Cwd ++ "/" ++ Name, []);
1272
case prim_file:get_cwd() of
1274
absname_vr(Name, Cwd);
1280
absname_vr([$/ | NameRest], [Drive, $\: | _]) ->
1281
%% Absolute path on current drive.
1282
normalize([Drive, $\: | NameRest], []);
1283
absname_vr([Drive, $\: | NameRest], [Drive, $\: | _] = Cwd) ->
1284
%% Relative to current directory on current drive.
1285
normalize(Cwd ++ "/" ++ NameRest, []);
1286
absname_vr([Drive, $: | NameRest], _) ->
1287
%% Relative to current directory on another drive.
1288
case prim_file:get_cwd([Drive, $:]) of
1289
{ok, DriveCwd} -> normalize(DriveCwd ++ "/" ++ NameRest, []);
1290
{error, _} -> normalize([Drive, $\:, $/] ++ NameRest, [])
1294
case erlang:system_info(os_type) of
1297
[$/, $/|_] -> absolute;
1298
[$\\, $/|_] -> absolute;
1299
[$/, $\\|_] -> absolute;
1300
[$\\, $\\|_] -> absolute;
1301
[$/|_] -> volumerelative;
1302
[$\\|_] -> volumerelative;
1303
[_Drive, $\:, $/ | _] -> absolute;
1304
[_Drive, $\:, $\\ | _] -> absolute;
1305
[_Drive, $\: | _] -> volumerelative;
1311
[$\\|_] -> absolute;
1316
normalize([$\\ | Chars], Acc) ->
1317
normalize(Chars, [$/ | Acc]);
1318
normalize([Char | Chars], Acc) ->
1319
normalize(Chars, [Char | Acc]);
1320
normalize([], Acc) ->