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