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