PageRenderTime 70ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/erts/preloaded/src/erl_prim_loader.erl

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