~statik/ubuntu/maverick/erlang/erlang-merge-testing

« back to all changes in this revision

Viewing changes to lib/kernel/src/erl_prim_loader.erl

  • Committer: Bazaar Package Importer
  • Author(s): Sergei Golovan
  • Date: 2009-05-01 10:14:38 UTC
  • mfrom: (3.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20090501101438-6qlr6rsdxgyzrg2z
Tags: 1:13.b-dfsg-2
* Cleaned up patches: removed unneeded patch which helped to support
  different SCTP library versions, made sure that changes for m68k
  architecture applied only when building on this architecture.
* Removed duplicated information from binary packages descriptions.
* Don't require libsctp-dev build-dependency on solaris-i386 architecture
  which allows to build Erlang on Nexenta (thanks to Tim Spriggs for
  the suggestion).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
%% ``The contents of this file are subject to the Erlang Public License,
2
 
%% Version 1.1, (the "License"); you may not use this file except in
3
 
%% compliance with the License. You should have received a copy of the
4
 
%% Erlang Public License along with this software. If not, it can be
5
 
%% retrieved via the world wide web at http://www.erlang.org/.
6
 
%% 
7
 
%% Software distributed under the License is distributed on an "AS IS"
8
 
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
 
%% the License for the specific language governing rights and limitations
10
 
%% under the License.
11
 
%% 
12
 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
 
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
 
%% AB. All Rights Reserved.''
15
 
%% 
16
 
%%     $Id $
17
 
%%
18
 
%%
19
 
 
20
 
 
21
 
%% A primary filer, 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/3, set_path/1, get_path/0, get_file/1, get_files/2,
46
 
         list_dir/1, read_file_info/1, get_cwd/0, get_cwd/1]).
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/2, prim_get_cwd/2]).
51
 
 
52
 
%% Used by escript and code
53
 
-export([set_primary_archive/2, release_archives/0]).
54
 
 
55
 
%% Internal function. Exported to avoid dialyzer warnings
56
 
-export([concat/1]).
57
 
 
58
 
-include_lib("kernel/include/file.hrl").
59
 
 
60
 
-record(state, 
61
 
        {loader              :: 'efile' | 'inet',
62
 
         hosts = [],              % hosts list (to boot from)
63
 
         id,                      % not used any more?
64
 
         data,                    % data port etc
65
 
         timeout,                 % idle timeout
66
 
         n_timeouts,              % Number of timeouts before archives are released
67
 
         multi_get = false   :: bool(),
68
 
         prim_state}).            % state for efile code loader
69
 
 
70
 
-define(IDLE_TIMEOUT, 60000).  %% tear inet connection after 1 minutes
71
 
-define(N_TIMEOUTS, 6).        %% release efile archive after 6 minutes
72
 
 
73
 
%% Defines for inet as prim_loader
74
 
-define(INET_FAMILY, inet).
75
 
-define(INET_ADDRESS, {0,0,0,0}).
76
 
 
77
 
-ifdef(DEBUG).
78
 
-define(dbg(Tag, Data), erlang:display({Tag,Data})).
79
 
-else.
80
 
-define(dbg(Tag, Data), true).
81
 
-endif.
82
 
 
83
 
-define(SAFE2(Expr, State), 
84
 
        fun() ->
85
 
                case catch Expr of
86
 
                    {'EXIT',XXXReason} -> {{error,XXXReason}, State};
87
 
                    XXXRes -> XXXRes
88
 
                end
89
 
        end()).
90
 
 
91
 
-record(prim_state, {debug, cache, primary_archive}).
92
 
 
93
 
debug(#prim_state{debug = Deb}, Term) ->
94
 
    case Deb of
95
 
        false -> ok;
96
 
        true  -> erlang:display(Term)
97
 
    end.
98
 
 
99
 
%%% --------------------------------------------------------
100
 
%%% Interface Functions. 
101
 
%%% --------------------------------------------------------
102
 
 
103
 
-spec(start/3 :: (_, atom() | string(), atom() | [atom()]) -> {'ok',pid()} | {'error',_}).
104
 
 
105
 
start(Id, Pgm, Hosts) when is_atom(Hosts) ->
106
 
    start(Id, Pgm, [Hosts]);
107
 
start(Id, Pgm0, Hosts) ->
108
 
    Pgm = if
109
 
              is_atom(Pgm0) ->
110
 
                  atom_to_list(Pgm0);
111
 
              true ->
112
 
                  Pgm0
113
 
          end,
114
 
    Self = self(),
115
 
    Pid = spawn_link(fun() -> start_it(Pgm, Id, Self, Hosts) end),
116
 
    register(erl_prim_loader, Pid),
117
 
    receive
118
 
        {Pid,ok} ->
119
 
            {ok,Pid};
120
 
        {'EXIT',Pid,Reason} ->
121
 
            {error,Reason}
122
 
    end.
123
 
 
124
 
start_it("ose_inet", Id, Pid, Hosts) ->
125
 
    %% Setup reserved port for ose_inet driver (only OSE)
126
 
    case catch erlang:open_port({spawn,ose_inet},[binary]) of
127
 
        {'EXIT',Why} ->
128
 
            ?dbg(ose_inet_port_open_fail, Why),
129
 
            Why;
130
 
        OseInetPort ->
131
 
            ?dbg(ose_inet_port, OseInetPort),
132
 
            OseInetPort
133
 
    end,
134
 
    start_it("inet", Id, Pid, Hosts);
135
 
 
136
 
%% Hosts must be a list on form ['1.2.3.4' ...]
137
 
start_it("inet", Id, Pid, Hosts) ->
138
 
    process_flag(trap_exit, true),
139
 
    ?dbg(inet, {Id,Pid,Hosts}),
140
 
    AL = ipv4_list(Hosts),
141
 
    ?dbg(addresses, AL),
142
 
    {ok,Tcp} = find_master(AL),
143
 
    init_ack(Pid),
144
 
    PS = prim_init(),
145
 
    State = #state {loader = inet,
146
 
                    hosts = AL,
147
 
                    id = Id,
148
 
                    data = Tcp,
149
 
                    timeout = ?IDLE_TIMEOUT,
150
 
                    n_timeouts = ?N_TIMEOUTS,
151
 
                    prim_state = PS},
152
 
    loop(State, Pid, []);
153
 
 
154
 
start_it("efile", Id, Pid, _Hosts) ->
155
 
    process_flag(trap_exit, true),
156
 
    {ok, Port} = prim_file:open([binary]),
157
 
    init_ack(Pid),
158
 
    MultiGet = case erlang:system_info(thread_pool_size) of
159
 
                   0 -> false;
160
 
                   _ -> true
161
 
               end,
162
 
    PS = prim_init(),
163
 
    State = #state {loader = efile,
164
 
                    id = Id,
165
 
                    data = Port,
166
 
                    timeout = infinity,
167
 
                    multi_get = MultiGet,
168
 
                    prim_state = PS},
169
 
    loop(State, Pid, []).
170
 
 
171
 
init_ack(Pid) ->
172
 
    Pid ! {self(),ok}.
173
 
 
174
 
%% -> ok
175
 
-spec(set_path/1 :: ([string()]) -> 'ok').
176
 
 
177
 
set_path(Paths) when is_list(Paths) ->
178
 
    request({set_path,Paths}).
179
 
 
180
 
%% -> {ok,Paths}
181
 
-spec(get_path/0 :: () -> {'ok',[string()]}).
182
 
 
183
 
get_path() ->
184
 
    request({get_path,[]}).
185
 
 
186
 
%% -> {ok,BinFile,File} | error
187
 
-spec(get_file/1 :: (atom() | string()) -> {'ok',binary(),string()} | 'error').
188
 
 
189
 
get_file(File) when is_atom(File) ->
190
 
    get_file(atom_to_list(File));
191
 
get_file(File) ->
192
 
    check_file_result(get_file, File, request({get_file,File})).
193
 
 
194
 
%% -> ok | {error,Module}
195
 
get_files(ModFiles, Fun) ->
196
 
    case request({get_files,{ModFiles,Fun}}) of
197
 
        E = {error,_M} ->
198
 
            E;
199
 
        {error,Reason,M} ->
200
 
            check_file_result(get_files, M, {error,Reason}),
201
 
            {error,M};
202
 
        ok ->
203
 
            ok
204
 
    end.
205
 
 
206
 
%% -> {ok,List} | error
207
 
-spec(list_dir/1 :: (string()) -> {'ok',[string()]} | 'error').
208
 
 
209
 
list_dir(Dir) ->
210
 
    check_file_result(list_dir, Dir, request({list_dir,Dir})).
211
 
 
212
 
%% -> {ok,Info} | error
213
 
-spec(read_file_info/1 :: (string()) -> {'ok',tuple()} | 'error').
214
 
 
215
 
read_file_info(File) ->
216
 
    check_file_result(read_file_info, File, request({read_file_info,File})).
217
 
 
218
 
%% -> {ok,Cwd} | error
219
 
-spec(get_cwd/0 :: () -> {'ok',string()} | 'error').
220
 
 
221
 
get_cwd() ->
222
 
    check_file_result(get_cwd, [], request({get_cwd,[]})).
223
 
 
224
 
%% -> {ok,Cwd} | error
225
 
-spec(get_cwd/1 :: (string()) -> {'ok',string()} | 'error').
226
 
 
227
 
get_cwd(Drive) ->
228
 
    check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})).
229
 
 
230
 
-spec(set_primary_archive/2 :: (File :: string() | 'undefined', 
231
 
                                ArchiveBin :: binary() | 'undefined')
232
 
      -> {ok, [string()]} | {error,_}).
233
 
 
234
 
set_primary_archive(undefined, undefined) ->
235
 
    request({set_primary_archive, undefined, undefined});
236
 
set_primary_archive(File, ArchiveBin)
237
 
  when is_list(File), is_binary(ArchiveBin) ->
238
 
    request({set_primary_archive, File, ArchiveBin}).
239
 
 
240
 
-spec(release_archives/0 :: () -> ok | {error,_}).
241
 
 
242
 
release_archives() ->
243
 
    request(release_archives).
244
 
 
245
 
request(Req) ->
246
 
    Loader = whereis(erl_prim_loader),
247
 
    Loader ! {self(),Req},
248
 
    receive
249
 
        {Loader,Res} ->
250
 
            Res;
251
 
        {'EXIT',Loader,_What} ->
252
 
            error
253
 
    end.
254
 
 
255
 
check_file_result(_, _, {error,enoent}) ->
256
 
    error;
257
 
check_file_result(_, _, {error,enotdir}) ->
258
 
    error;
259
 
check_file_result(Func, Target, {error,Reason}) ->   
260
 
    case (catch atom_to_list(Reason)) of
261
 
        {'EXIT',_} ->                           % exit trapped
262
 
            error;
263
 
        Errno ->                                % errno
264
 
            Process = case process_info(self(), registered_name) of
265
 
                          {registered_name,R} -> 
266
 
                              "Process: " ++ atom_to_list(R) ++ ".";
267
 
                          _ -> 
268
 
                              ""
269
 
                      end,
270
 
            TargetStr =
271
 
                if is_atom(Target) -> atom_to_list(Target);
272
 
                   is_list(Target) -> Target;
273
 
                   true -> []
274
 
                end,
275
 
            Report = 
276
 
                case TargetStr of
277
 
                    [] ->
278
 
                        "File operation error: " ++ Errno ++ ". " ++
279
 
                        "Function: " ++ atom_to_list(Func) ++ ". " ++ Process;
280
 
                    _ ->
281
 
                        "File operation error: " ++ Errno ++ ". " ++
282
 
                        "Target: " ++ TargetStr ++ ". " ++
283
 
                        "Function: " ++ atom_to_list(Func) ++ ". " ++ Process
284
 
                end,
285
 
            %% this is equal to calling error_logger:error_report/1 which
286
 
            %% we don't want to do from code_server during system boot
287
 
            error_logger ! {notify,{error_report,group_leader(),
288
 
                                    {self(),std_error,Report}}},
289
 
            error
290
 
    end;
291
 
check_file_result(_, _, Other) ->
292
 
    Other.
293
 
 
294
 
%%% --------------------------------------------------------
295
 
%%% The main loop.
296
 
%%% --------------------------------------------------------
297
 
 
298
 
loop(State, Parent, Paths) ->
299
 
    receive
300
 
        {Pid,Req} when is_pid(Pid) ->
301
 
            %% erlang:display(Req),
302
 
            {Resp,State2,Paths2} =
303
 
                case Req of
304
 
                    {set_path,NewPaths} ->
305
 
                        {ok,State,to_strs(NewPaths)};
306
 
                    {get_path,_} ->
307
 
                        {{ok,Paths},State,Paths};
308
 
                    {get_file,File} ->
309
 
                        {Res,State1} = handle_get_file(State, Paths, File),
310
 
                        {Res,State1,Paths};
311
 
                    {get_files,{ModFiles,Fun}} ->
312
 
                        {Res,State1} = handle_get_files(State, ModFiles, Paths, Fun),
313
 
                        {Res,State1,Paths};
314
 
                    {list_dir,Dir} ->
315
 
                        {Res,State1} = handle_list_dir(State, Dir),
316
 
                        {Res,State1,Paths};
317
 
                    {read_file_info,File} ->
318
 
                        {Res,State1} = handle_read_file_info(State, File),
319
 
                        {Res,State1,Paths};
320
 
                    {get_cwd,[]} ->
321
 
                        {Res,State1} = handle_get_cwd(State, []),
322
 
                        {Res,State1,Paths};
323
 
                    {get_cwd,[_]=Args} ->
324
 
                        {Res,State1} = handle_get_cwd(State, Args),
325
 
                        {Res,State1,Paths};
326
 
                    {set_primary_archive,File,Bin} ->
327
 
                        {Res,State1} = handle_set_primary_archive(State, File, Bin),
328
 
                        {Res,State1,Paths};
329
 
                    release_archives ->
330
 
                        {Res,State1} = handle_release_archives(State),
331
 
                        {Res,State1,Paths};
332
 
                    _Other ->
333
 
                        {ignore,State,Paths}
334
 
                end,
335
 
            if Resp =:= ignore -> ok;
336
 
               true -> Pid ! {self(),Resp}
337
 
            end,
338
 
            if 
339
 
                is_record(State2, state) ->
340
 
                    loop(State2, Parent, Paths2);
341
 
                true ->
342
 
                    exit({bad_state, Req, State2})          
343
 
            end;
344
 
        {'EXIT',Parent,W} ->
345
 
            handle_stop(State),
346
 
            exit(W);
347
 
        {'EXIT',P,W} ->
348
 
            State1 = handle_exit(State, P, W),
349
 
            loop(State1, Parent, Paths);
350
 
        _Message ->
351
 
            loop(State, Parent, Paths)
352
 
    after State#state.timeout ->
353
 
            State1 = handle_timeout(State, Parent),
354
 
            loop(State1, Parent, Paths)
355
 
    end.
356
 
 
357
 
handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) ->
358
 
    ?SAFE2(efile_multi_get_file_from_port(State, ModFiles, Paths, Fun), State);
359
 
handle_get_files(State, _ModFiles, _Paths, _Fun) ->     % no multi get
360
 
    {{error,no_multi_get},State}.
361
 
    
362
 
handle_get_file(State = #state{loader = efile}, Paths, File) ->
363
 
    ?SAFE2(efile_get_file_from_port(State, File, Paths), State);
364
 
handle_get_file(State = #state{loader = inet}, Paths, File) ->
365
 
    ?SAFE2(inet_get_file_from_port(State, File, Paths), State).
366
 
 
367
 
handle_set_primary_archive(State= #state{loader = efile}, File, Bin) ->
368
 
    ?SAFE2(efile_set_primary_archive(State, File, Bin), State).
369
 
 
370
 
handle_release_archives(State= #state{loader = efile}) ->
371
 
    ?SAFE2(efile_release_archives(State), State).
372
 
 
373
 
handle_list_dir(State = #state{loader = efile}, Dir) ->
374
 
    ?SAFE2(efile_list_dir(State, Dir), State);
375
 
handle_list_dir(State = #state{loader = inet}, Dir) ->
376
 
    ?SAFE2(inet_list_dir(State, Dir), State).
377
 
 
378
 
handle_read_file_info(State = #state{loader = efile}, File) ->
379
 
    ?SAFE2(efile_read_file_info(State, File), State);
380
 
handle_read_file_info(State = #state{loader = inet}, File) ->
381
 
    ?SAFE2(inet_read_file_info(State, File), State).
382
 
 
383
 
handle_get_cwd(State = #state{loader = efile}, Drive) ->
384
 
    ?SAFE2(efile_get_cwd(State, Drive), State);
385
 
handle_get_cwd(State = #state{loader = inet}, Drive) ->
386
 
    ?SAFE2(inet_get_cwd(State, Drive), State).
387
 
    
388
 
handle_stop(State = #state{loader = efile}) ->
389
 
    efile_stop_port(State);
390
 
handle_stop(State = #state{loader = inet}) ->
391
 
    inet_stop_port(State).
392
 
 
393
 
handle_exit(State = #state{loader = efile}, Who, Reason) ->
394
 
    efile_exit_port(State, Who, Reason);
395
 
handle_exit(State = #state{loader = inet}, Who, Reason) ->
396
 
    inet_exit_port(State, Who, Reason).
397
 
 
398
 
handle_timeout(State = #state{loader = efile}, Parent) ->
399
 
    efile_timeout_handler(State, Parent);
400
 
handle_timeout(State = #state{loader = inet}, Parent) ->
401
 
    inet_timeout_handler(State, Parent).
402
 
 
403
 
%%% --------------------------------------------------------
404
 
%%% Functions which handles efile as prim_loader (default).
405
 
%%% --------------------------------------------------------
406
 
 
407
 
%%% Reading many files in parallel is an optimization. 
408
 
%%% See also comment in init.erl.
409
 
 
410
 
%% -> {ok,State} | {{error,Module},State} | {{error,Reason,Module},State}
411
 
efile_multi_get_file_from_port(State, ModFiles, Paths, Fun) ->
412
 
    Ref = make_ref(),
413
 
    %% More than 200 processes is no gain.
414
 
    Max = min(200, erlang:system_info(thread_pool_size)),
415
 
    efile_multi_get_file_from_port2(ModFiles, 0, Max, State, Paths, Fun, Ref, ok).
416
 
 
417
 
efile_multi_get_file_from_port2([MF | MFs], Out, Max, State, Paths, Fun, Ref, Ret) when Out < Max ->
418
 
    Self = self(),
419
 
    _Pid = spawn(fun() -> efile_par_get_file(Ref, State, MF, Paths, Self, Fun) end),
420
 
    efile_multi_get_file_from_port2(MFs, Out+1, Max, State, Paths, Fun, Ref, Ret);
421
 
efile_multi_get_file_from_port2(MFs, Out, Max, _State, Paths, Fun, Ref, Ret) when Out > 0 ->
422
 
    receive 
423
 
        {Ref, ok, State1} ->
424
 
            efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Ret);
425
 
        {Ref, {error,_Mod} = Error, State1} ->
426
 
            efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Error);
427
 
        {Ref, MF, {error,emfile,State1}} ->
428
 
            %% Max can take negative values. Out cannot.
429
 
            efile_multi_get_file_from_port2([MF | MFs], Out-1, Max-1, State1, Paths, Fun, Ref, Ret);
430
 
        {Ref, {M,_F}, {error,Error,State1}} -> 
431
 
            efile_multi_get_file_from_port2(MFs, Out-1, 0, State1, Paths, Fun, Ref, {error,Error,M})
432
 
    end;
433
 
efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) ->
434
 
    {Ret,State}.
435
 
 
436
 
efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) ->
437
 
    %% One port for each file read in "parallel":
438
 
    case prim_file:open([binary]) of
439
 
        {ok, Port} ->
440
 
            Port0 = State#state.data,
441
 
            State1 = State#state{data = Port},
442
 
            R = case efile_get_file_from_port(State1, File, Paths) of
443
 
                    {{error,Reason},State2} -> 
444
 
                        {Ref,MF,{error,Reason,State2}};
445
 
                    {{ok,BinFile,Full},State2} -> 
446
 
                        %% Fun(...) -> ok | {error,Mod}
447
 
                        {Ref,Fun(Mod, BinFile, Full),State2#state{data=Port0}}
448
 
                end,
449
 
            prim_file:close(Port),
450
 
            Pid ! R;
451
 
        {error, Error} ->
452
 
            Pid ! {Ref,MF,{error,Error,State}}
453
 
    end.
454
 
 
455
 
%% -> {{ok,BinFile,File},State} | {{error,Reason},State}
456
 
efile_get_file_from_port(State, File, Paths) ->
457
 
    case is_basename(File) of
458
 
        false ->                        % get absolute file name.
459
 
            efile_get_file_from_port2(State, File);
460
 
        true when Paths =:= [] ->       % get plain file name.
461
 
            efile_get_file_from_port2(State, File);
462
 
        true ->                         % use paths.
463
 
            efile_get_file_from_port3(State, File, Paths)
464
 
    end.
465
 
 
466
 
efile_get_file_from_port2(#state{prim_state = PS} = State, File) ->
467
 
    {Res, PS2} = prim_get_file(PS, File),
468
 
    case Res of
469
 
        {error,port_died} ->
470
 
            exit('prim_load port died');
471
 
        {error,Reason} ->
472
 
            {{error,Reason},State#state{prim_state = PS2}};
473
 
        {ok,BinFile} ->
474
 
            {{ok,BinFile,File},State#state{prim_state = PS2}}
475
 
    end.
476
 
 
477
 
efile_get_file_from_port3(State, File, [P | Paths]) ->
478
 
    case efile_get_file_from_port2(State, concat([P,"/",File])) of
479
 
        {{error,Reason},State1} when Reason =/= emfile ->
480
 
            case Paths of
481
 
                [] ->                           % return last error
482
 
                    {{error,Reason},State1};
483
 
                _ ->                            % try more paths
484
 
                    efile_get_file_from_port3(State1, File, Paths)
485
 
            end;
486
 
        Result ->
487
 
            Result
488
 
    end;
489
 
efile_get_file_from_port3(State, _File, []) ->
490
 
    {{error,enoent},State}.
491
 
 
492
 
efile_set_primary_archive(#state{prim_state = PS} = State, File, Bin) ->
493
 
    {Res, PS2} = prim_set_primary_archive(PS, File, Bin),
494
 
    {Res,State#state{prim_state = PS2}}.
495
 
 
496
 
efile_release_archives(#state{prim_state = PS} = State) ->
497
 
    {Res, PS2} = prim_release_archives(PS),
498
 
    {Res,State#state{prim_state = PS2}}.
499
 
 
500
 
efile_list_dir(#state{prim_state = PS} = State, Dir) ->
501
 
    {Res, PS2} = prim_list_dir(PS, Dir),
502
 
    {Res, State#state{prim_state = PS2}}.
503
 
 
504
 
efile_read_file_info(#state{prim_state = PS} = State, File) ->
505
 
    {Res, PS2} = prim_read_file_info(PS, File),
506
 
    {Res, State#state{prim_state = PS2}}.
507
 
 
508
 
efile_get_cwd(#state{prim_state = PS} = State, Drive) ->
509
 
    {Res, PS2} = prim_get_cwd(PS, Drive),
510
 
    {Res, State#state{prim_state = PS2}}.
511
 
 
512
 
efile_stop_port(#state{data=Port}=State) ->
513
 
    prim_file:close(Port),
514
 
    State#state{data=noport}.
515
 
 
516
 
efile_exit_port(State, Port, Reason) when State#state.data =:= Port ->
517
 
    exit({port_died,Reason});
518
 
efile_exit_port(State, _Port, _Reason) ->
519
 
    State.
520
 
 
521
 
efile_timeout_handler(#state{n_timeouts = N} = State, _Parent) ->
522
 
    if
523
 
        N =< 0 ->
524
 
            {_Res, State2} = efile_release_archives(State),
525
 
            State2#state{n_timeouts = ?N_TIMEOUTS};
526
 
        true ->
527
 
            State#state{n_timeouts = N - 1}
528
 
    end.
529
 
 
530
 
%%% --------------------------------------------------------
531
 
%%% Functions which handles inet prim_loader
532
 
%%% --------------------------------------------------------
533
 
 
534
 
%%
535
 
%% Connect to a boot master
536
 
%% return {ok, Socket}  TCP
537
 
%% AL is a list of boot servers (including broadcast addresses)
538
 
%%
539
 
find_master(AL) ->
540
 
    find_master(AL, ?EBOOT_RETRY, ?EBOOT_REQUEST_DELAY, ?EBOOT_SHORT_RETRY_SLEEP, 
541
 
               ?EBOOT_UNSUCCESSFUL_TRIES, ?EBOOT_LONG_RETRY_SLEEP).
542
 
 
543
 
find_master(AL, Retry, ReqDelay, SReSleep, Tries, LReSleep) ->
544
 
    {ok,U} = ll_udp_open(0),
545
 
    find_master(U, Retry, AL, ReqDelay, SReSleep, [], Tries, LReSleep).
546
 
 
547
 
%%
548
 
%% Master connect loop
549
 
%%
550
 
find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
551
 
    case find_loop(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, 
552
 
                   Tries, LReSleep) of
553
 
        [] ->   
554
 
            find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, 
555
 
                        Tries, LReSleep);
556
 
        Servers ->
557
 
            ?dbg(servers, Servers),
558
 
            case connect_master(Servers) of
559
 
                {ok, Socket} -> 
560
 
                    ll_close(U),
561
 
                    {ok, Socket};
562
 
                _Error ->
563
 
                    find_master(U, Retry, AddrL, ReqDelay, SReSleep, 
564
 
                                Servers ++ Ignore, Tries, LReSleep)
565
 
            end
566
 
    end.
567
 
 
568
 
connect_master([{_Prio,IP,Port} | Servers]) ->
569
 
    case ll_tcp_connect(0, IP, Port) of
570
 
        {ok, S} -> {ok, S};
571
 
        _Error -> connect_master(Servers)
572
 
    end;
573
 
connect_master([]) ->
574
 
    {error, ebusy}.
575
 
 
576
 
%%
577
 
%% Always return a list of boot servers or hang.
578
 
%%
579
 
find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
580
 
    case find_loop(U, Retry, AL, ReqDelay, []) of
581
 
        [] ->                                   % no response from any server
582
 
            erlang:display({erl_prim_loader,'no server found'}), % lifesign
583
 
            Tries1 = if Tries > 0 ->
584
 
                             sleep(SReSleep),
585
 
                             Tries - 1;
586
 
                        true ->
587
 
                             sleep(LReSleep),
588
 
                             0
589
 
                     end,
590
 
            find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries1, LReSleep);
591
 
        Servers ->
592
 
            keysort(1, Servers -- Ignore)
593
 
    end.
594
 
 
595
 
%% broadcast or send
596
 
find_loop(_U, 0, _AL, _Delay, Acc) ->
597
 
    Acc;
598
 
find_loop(U, Retry, AL, Delay, Acc) ->
599
 
    send_all(U, AL, [?EBOOT_REQUEST, erlang:system_info(version)]),
600
 
    find_collect(U, Retry-1, AL, Delay, Acc).
601
 
 
602
 
find_collect(U,Retry,AL,Delay,Acc) ->
603
 
    receive
604
 
        {udp, U, IP, _Port, [$E,$B,$O,$O,$T,$R,Priority,T1,T0 | _Version]} ->
605
 
            Elem = {Priority,IP,T1*256+T0},
606
 
            ?dbg(got, Elem),
607
 
            case member(Elem, Acc) of
608
 
                false  -> find_collect(U, Retry, AL, Delay, [Elem | Acc]);
609
 
                true -> find_collect(U, Retry, AL, Delay, Acc)
610
 
            end;
611
 
        _Garbage ->
612
 
            ?dbg(collect_garbage, _Garbage),
613
 
            find_collect(U, Retry, AL, Delay, Acc)
614
 
            
615
 
    after Delay ->
616
 
            ?dbg(collected, Acc),
617
 
            case keymember(0, 1, Acc) of  %% got high priority server?
618
 
                true -> Acc;
619
 
                false -> find_loop(U, Retry, AL, Delay, Acc)
620
 
            end
621
 
    end.
622
 
 
623
 
    
624
 
sleep(Time) ->
625
 
    receive after Time -> ok end.
626
 
 
627
 
inet_exit_port(State, Port, _Reason) when State#state.data =:= Port ->
628
 
    State#state { data = noport, timeout = infinity };
629
 
inet_exit_port(State, _, _) ->
630
 
    State.
631
 
 
632
 
 
633
 
inet_timeout_handler(State, _Parent) ->
634
 
    Tcp = State#state.data,
635
 
    if is_port(Tcp) -> ll_close(Tcp);
636
 
       true -> ok
637
 
    end,
638
 
    State#state { timeout = infinity, data = noport }.
639
 
 
640
 
%% -> {{ok,BinFile,Tag},State} | {{error,Reason},State}
641
 
inet_get_file_from_port(State, File, Paths) ->
642
 
    case is_basename(File) of
643
 
        false ->                        % get absolute file name.
644
 
            inet_send_and_rcv({get,File}, File, State);
645
 
        true when Paths =:= [] ->       % get plain file name.
646
 
            inet_send_and_rcv({get,File}, File, State);
647
 
        true ->                         % use paths.
648
 
            inet_get_file_from_port1(File, Paths, State)
649
 
    end.
650
 
 
651
 
inet_get_file_from_port1(File, [P | Paths], State) ->
652
 
    File1 = concat([P,"/",File]),
653
 
    case inet_send_and_rcv({get,File1}, File1, State) of
654
 
        {{error,Reason},State1} ->
655
 
            case Paths of
656
 
                [] ->                           % return last error
657
 
                    {{error,Reason},State1};
658
 
                _ ->                            % try more paths            
659
 
                    inet_get_file_from_port1(File, Paths, State1)
660
 
            end;
661
 
        Result -> Result
662
 
    end;
663
 
inet_get_file_from_port1(_File, [], State) ->
664
 
    {{error,file_not_found},State}.
665
 
 
666
 
inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport ->
667
 
    {ok,Tcp} = find_master(State#state.hosts),     %% reconnect
668
 
    inet_send_and_rcv(Msg, Tag, State#state { data = Tcp,
669
 
                                              timeout = ?IDLE_TIMEOUT });
670
 
inet_send_and_rcv(Msg, Tag, #state{data=Tcp,timeout=Timeout}=State) ->
671
 
    prim_inet:send(Tcp, term_to_binary(Msg)),
672
 
    receive
673
 
        {tcp,Tcp,BinMsg} ->
674
 
            case catch binary_to_term(BinMsg) of
675
 
                {get,{ok,BinFile}} ->
676
 
                    {{ok,BinFile,Tag},State};
677
 
                {_Cmd,Res={ok,_}} ->
678
 
                    {Res,State};
679
 
                {_Cmd,{error,Error}} ->
680
 
                    {{error,Error},State};
681
 
                {error,Error} ->
682
 
                    {{error,Error},State};
683
 
                {'EXIT',Error} ->
684
 
                    {{error,Error},State}
685
 
            end;
686
 
        {tcp_closed,Tcp} ->
687
 
            %% Ok we must reconnect
688
 
            inet_send_and_rcv(Msg, Tag, State#state { data = noport });
689
 
        {tcp_error,Tcp,_Reason} ->
690
 
            %% Ok we must reconnect
691
 
            inet_send_and_rcv(Msg, Tag, inet_stop_port(State));
692
 
        {'EXIT', Tcp, _} -> 
693
 
            %% Ok we must reconnect
694
 
            inet_send_and_rcv(Msg, Tag, State#state { data = noport })
695
 
    after Timeout ->
696
 
            %% Ok we must reconnect
697
 
            inet_send_and_rcv(Msg, Tag, inet_stop_port(State))
698
 
    end.
699
 
 
700
 
%% -> {{ok,List},State} | {{error,Reason},State}
701
 
inet_list_dir(State, Dir) ->
702
 
    inet_send_and_rcv({list_dir,Dir}, list_dir, State).
703
 
 
704
 
%% -> {{ok,Info},State} | {{error,Reason},State}
705
 
inet_read_file_info(State, File) ->
706
 
    inet_send_and_rcv({read_file_info,File}, read_file_info, State).
707
 
 
708
 
%% -> {{ok,Cwd},State} | {{error,Reason},State}
709
 
inet_get_cwd(State, []) ->
710
 
    inet_send_and_rcv(get_cwd, get_cwd, State);
711
 
inet_get_cwd(State, [Drive]) ->
712
 
    inet_send_and_rcv({get_cwd,Drive}, get_cwd, State).
713
 
 
714
 
inet_stop_port(#state{data=Tcp}=State) ->
715
 
    prim_inet:close(Tcp),
716
 
    State#state{data=noport}.
717
 
 
718
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719
 
%%
720
 
%% Direct inet_drv access
721
 
%%
722
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723
 
 
724
 
tcp_options() ->
725
 
    [{mode,binary}, {packet,4}, {active, true}, {deliver,term}].
726
 
 
727
 
tcp_timeout() -> 
728
 
    15000.
729
 
 
730
 
%% options for udp  [list, {broadcast, true}, {active,true}]
731
 
udp_options() ->
732
 
    [{mode,list}, {active, true}, {deliver,term}, {broadcast,true}].
733
 
%%
734
 
%% INET version IPv4 addresses
735
 
%%
736
 
ll_tcp_connect(LocalPort, IP, RemotePort) ->
737
 
    case ll_open_set_bind(tcp, ?INET_FAMILY, tcp_options(),
738
 
                          ?INET_ADDRESS, LocalPort) of
739
 
        {ok,S} ->
740
 
            case prim_inet:connect(S, IP, RemotePort, tcp_timeout()) of
741
 
                ok -> {ok, S};
742
 
                Error -> port_error(S, Error)
743
 
            end;
744
 
        Error -> Error
745
 
    end.
746
 
 
747
 
%%
748
 
%% Open and initialize an udp port for broadcast
749
 
%%
750
 
ll_udp_open(P) ->
751
 
    ll_open_set_bind(udp, ?INET_FAMILY, udp_options(), ?INET_ADDRESS, P).
752
 
 
753
 
 
754
 
ll_open_set_bind(Protocol, Family, SOpts, IP, Port) ->
755
 
    case prim_inet:open(Protocol, Family) of
756
 
        {ok, S} ->
757
 
            case prim_inet:setopts(S, SOpts) of
758
 
                ok ->
759
 
                    case prim_inet:bind(S, IP, Port) of
760
 
                        {ok,_} ->
761
 
                            {ok, S};
762
 
                        Error -> port_error(S, Error)
763
 
                    end;
764
 
                Error -> port_error(S, Error)
765
 
            end;
766
 
        Error -> Error
767
 
    end.
768
 
                    
769
 
 
770
 
ll_close(S) ->
771
 
    unlink(S),
772
 
    exit(S, kill).
773
 
 
774
 
port_error(S, Error) ->
775
 
    unlink(S),
776
 
    prim_inet:close(S),
777
 
    Error.
778
 
    
779
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780
 
 
781
 
prim_init() ->
782
 
    Deb =
783
 
        case init:get_argument(loader_debug) of
784
 
            {ok, _} -> true;
785
 
            error -> false
786
 
        end,
787
 
    cache_new(#prim_state{debug = Deb}).
788
 
    
789
 
prim_release_archives(PS) ->
790
 
    debug(PS, release_archives),
791
 
    {Res, PS2}= prim_do_release_archives(PS, get(), []),
792
 
    debug(PS2, {return, Res}),
793
 
    {Res, PS2}.
794
 
 
795
 
prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) ->
796
 
    Res = 
797
 
        case DictVal of
798
 
            {primary, _PrimZip} ->
799
 
                ok; % Keep primary archive
800
 
            {_Mtime, Cache} ->
801
 
                debug(PS, {release, cache, ArchiveFile}),
802
 
                erase(ArchiveFile),
803
 
                clear_cache(ArchiveFile, Cache)
804
 
        end,
805
 
    case Res of
806
 
        ok ->
807
 
            prim_do_release_archives(PS, KeyVals, Acc);
808
 
        {error, Reason} ->
809
 
            prim_do_release_archives(PS, KeyVals, [{ArchiveFile, Reason} | Acc])
810
 
    end;
811
 
prim_do_release_archives(PS, [], []) ->
812
 
    {ok, PS#prim_state{primary_archive = undefined}};
813
 
prim_do_release_archives(PS, [], Errors) ->
814
 
    {{error, Errors}, PS#prim_state{primary_archive = undefined}}.
815
 
 
816
 
prim_set_primary_archive(PS, undefined, undefined) ->
817
 
    debug(PS, {set_primary_archive, clean}),
818
 
    case PS#prim_state.primary_archive of
819
 
        undefined ->
820
 
            Res = {error, enoent},
821
 
            debug(PS, {return, Res}),
822
 
            {Res, PS};
823
 
        ArchiveFile ->
824
 
            {primary, PrimZip} = erase(ArchiveFile),
825
 
            ok = prim_zip:close(PrimZip),
826
 
            PS2 = PS#prim_state{primary_archive = undefined},
827
 
            Res = {ok, []},
828
 
            debug(PS2, {return, Res}),
829
 
            {Res, PS2}
830
 
    end;
831
 
prim_set_primary_archive(PS, ArchiveFile, ArchiveBin)
832
 
  when is_list(ArchiveFile), is_binary(ArchiveBin) ->
833
 
    %% Try the archive file
834
 
    debug(PS, {set_primary_archive, ArchiveFile, byte_size(ArchiveBin)}),
835
 
    {Res3, PS3} =
836
 
        case PS#prim_state.primary_archive of
837
 
            undefined ->
838
 
                Fun =
839
 
                    fun({Funny, _GI, _GB}, A) ->
840
 
                            case Funny of
841
 
                                ["", "nibe", RevApp] -> % Reverse ebin
842
 
                                    %% Collect ebin directories in archive
843
 
                                    Ebin = reverse(RevApp) ++ "/ebin",
844
 
                                    {true, [Ebin | A]};
845
 
                                _ ->
846
 
                                    {true, A}
847
 
                            end
848
 
                    end,
849
 
                Ebins0 = [ArchiveFile],
850
 
                case open_archive({ArchiveFile, ArchiveBin}, Ebins0, Fun) of
851
 
                    {ok, PrimZip, RevEbins} ->
852
 
                        Ebins = reverse(RevEbins),
853
 
                        debug(PS, {set_primary_archive, Ebins}),
854
 
                        put(ArchiveFile, {primary, PrimZip}),
855
 
                        {{ok, Ebins}, PS#prim_state{primary_archive = ArchiveFile}};
856
 
                    Error ->
857
 
                        debug(PS, {set_primary_archive, Error}),
858
 
                        {Error, PS}
859
 
                end;
860
 
            OldArchiveFile ->
861
 
                debug(PS, {set_primary_archive, clean}),
862
 
                PrimZip = erase(OldArchiveFile),
863
 
                ok = prim_zip:close(PrimZip),
864
 
                PS2 = PS#prim_state{primary_archive = undefined},
865
 
                prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin)
866
 
        end,
867
 
    debug(PS3, {return, Res3}),
868
 
    {Res3, PS3}.
869
 
 
870
 
prim_get_file(PS, File) ->
871
 
    debug(PS, {get_file, File}),
872
 
    {Res2, PS2} =
873
 
        case name_split(PS#prim_state.primary_archive, File) of
874
 
            {file, PrimFile} ->
875
 
                Res = prim_file:read_file(PrimFile),
876
 
                {Res, PS};
877
 
            {archive, ArchiveFile, FileInArchive} ->
878
 
                debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
879
 
                FunnyFile = funny_split(FileInArchive, $/),
880
 
                Fun =
881
 
                    fun({Funny, _GetInfo, GetBin}, Acc) ->
882
 
                            if
883
 
                                Funny =:= FunnyFile ->
884
 
                                    {false, {ok, GetBin()}};
885
 
                                true ->
886
 
                                    {true, Acc}
887
 
                            end
888
 
                    end,
889
 
                apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
890
 
        end,
891
 
    debug(PS, {return, Res2}),
892
 
    {Res2, PS2}.    
893
 
 
894
 
%% -> {{ok,List},State} | {{error,Reason},State}
895
 
prim_list_dir(PS, Dir) ->
896
 
    debug(PS, {list_dir, Dir}),
897
 
    {Res2, PS3} =
898
 
        case name_split(PS#prim_state.primary_archive, Dir) of
899
 
            {file, PrimDir} ->
900
 
                Res = prim_file:list_dir(PrimDir),
901
 
                {Res, PS};
902
 
            {archive, ArchiveFile, FileInArchive} ->
903
 
                debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
904
 
                FunnyDir = funny_split(FileInArchive, $/),
905
 
                Fun =
906
 
                    fun({Funny, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
907
 
                            case Funny of
908
 
                                [RevName | FD] when FD =:= FunnyDir ->
909
 
                                    case RevName of
910
 
                                        "" ->
911
 
                                            %% The listed directory
912
 
                                            {true, {ok, Names}};
913
 
                                        _ ->
914
 
                                            %% Plain file
915
 
                                            Name = reverse(RevName),
916
 
                                            {true, {Status, [Name | Names]}}
917
 
                                    end;
918
 
                                ["", RevName | FD] when FD =:= FunnyDir ->
919
 
                                    %% Directory
920
 
                                    Name = reverse(RevName),
921
 
                                    {true, {Status, [Name | Names]}};
922
 
                                [RevName] when FunnyDir =:= [""] ->
923
 
                                    %% Top file
924
 
                                    Name = reverse(RevName),
925
 
                                    {true, {ok, [Name | Names]}};
926
 
                                ["", RevName] when FunnyDir =:= [""] ->
927
 
                                    %% Top file
928
 
                                    Name = reverse(RevName),
929
 
                                    {true, {ok, [Name | Names]}};
930
 
                                _ ->
931
 
                                    %% No match
932
 
                                    {true, Acc}
933
 
                            end
934
 
                    end,
935
 
                {{Status, Names}, PS2} =
936
 
                    apply_archive(PS, Fun, {error, []}, ArchiveFile),
937
 
                case Status of
938
 
                    ok    -> {{ok, Names}, PS2};
939
 
                    error -> {{error, enotdir}, PS2}
940
 
                end
941
 
        end,
942
 
    debug(PS, {return, Res2}),
943
 
    {Res2, PS3}.
944
 
 
945
 
%% -> {{ok,Info},State} | {{error,Reason},State}
946
 
prim_read_file_info(PS, File) ->
947
 
    debug(PS, {read_file_info, File}),
948
 
    {Res2, PS2} =
949
 
        case name_split(PS#prim_state.primary_archive, File) of
950
 
            {file, PrimFile} ->
951
 
                Res = prim_file:read_file_info(PrimFile),
952
 
                {Res, PS};
953
 
            {archive, ArchiveFile, []} ->
954
 
                %% Fake top directory
955
 
                debug(PS, {archive_read_file_info, ArchiveFile}),
956
 
                case prim_file:read_file_info(ArchiveFile) of
957
 
                    {ok, FI} ->
958
 
                        {{ok, FI#file_info{type = directory}}, PS};
959
 
                    Other ->
960
 
                        {Other, PS}
961
 
                end;
962
 
            {archive, ArchiveFile, FileInArchive} ->
963
 
                debug(PS, {archive_read_file_info, File}),
964
 
                FunnyFile = funny_split(FileInArchive, $/),
965
 
                Fun =
966
 
                    fun({Funny, GetInfo, _GetBin}, Acc)  ->
967
 
                            if
968
 
                                hd(Funny) =:= "",
969
 
                                tl(Funny) =:= FunnyFile ->
970
 
                                    %% Directory
971
 
                                    {false, {ok, GetInfo()}};
972
 
                                Funny =:= FunnyFile ->
973
 
                                    %% Plain file
974
 
                                    {false, {ok, GetInfo()}};
975
 
                                true ->
976
 
                                    %% No match
977
 
                                    {true, Acc}
978
 
                            end
979
 
                    end,
980
 
                apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
981
 
        end,
982
 
    debug(PS2, {return, Res2}),
983
 
    {Res2, PS2}.
984
 
 
985
 
prim_get_cwd(PS, []) ->
986
 
    debug(PS, {get_cwd, []}),
987
 
    Res = prim_file:get_cwd(),
988
 
    debug(PS, {return, Res}),
989
 
    {Res, PS};
990
 
prim_get_cwd(PS, [Drive]) ->
991
 
    debug(PS, {get_cwd, Drive}),
992
 
    Res = prim_file:get_cwd(Drive),
993
 
    debug(PS, {return, Res}),
994
 
    {Res, PS}.
995
 
 
996
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997
 
 
998
 
apply_archive(PS, Fun, Acc, Archive) ->
999
 
    case get(Archive) of
1000
 
        undefined ->
1001
 
            case prim_file:read_file_info(Archive) of
1002
 
                {ok, #file_info{mtime = Mtime}} ->
1003
 
                    case open_archive(Archive, Acc, Fun) of
1004
 
                        {ok, PrimZip, Acc2} ->
1005
 
                            debug(PS, {cache, ok}),
1006
 
                            put(Archive, {Mtime, {ok, PrimZip}}),
1007
 
                            {Acc2, PS};
1008
 
                        Error ->
1009
 
                            debug(PS, {cache, Error}),
1010
 
                            put(Archive, {Mtime, Error}),
1011
 
                            {Error, PS}
1012
 
                    end;
1013
 
                Error ->
1014
 
                    debug(PS, {cache, Error}),
1015
 
                    {Error, PS}
1016
 
            end;
1017
 
        {primary, PrimZip} ->
1018
 
            case foldl_archive(PrimZip, Acc, Fun) of
1019
 
                {ok, _PrimZip2, Acc2} ->
1020
 
                    {Acc2, PS};
1021
 
                Error ->
1022
 
                    debug(PS, {primary, Error}),
1023
 
                    {Error, PS}
1024
 
            end;
1025
 
        {Mtime, Cache} ->
1026
 
            case prim_file:read_file_info(Archive) of
1027
 
                {ok, #file_info{mtime = Mtime2}} when Mtime2 =:= Mtime ->
1028
 
                    case Cache of
1029
 
                        {ok, PrimZip} ->
1030
 
                            case foldl_archive(PrimZip, Acc, Fun) of
1031
 
                                {ok, _PrimZip2, Acc2} ->
1032
 
                                    {Acc2, PS};
1033
 
                                Error ->
1034
 
                                    debug(PS, {cache, {clear, Error}}),
1035
 
                                    clear_cache(Archive, Cache),
1036
 
                                    debug(PS, {cache, Error}),
1037
 
                                    put(Archive, {Mtime, Error}),
1038
 
                                    {Error, PS}
1039
 
                            end;
1040
 
                        Error ->
1041
 
                            debug(PS, {cache, Error}),
1042
 
                            {Error, PS}
1043
 
                    end;
1044
 
                Error ->
1045
 
                    debug(PS, {cache, {clear, Error}}),
1046
 
                    clear_cache(Archive, Cache),
1047
 
                    apply_archive(PS, Fun, Acc, Archive)
1048
 
            end
1049
 
    end.
1050
 
 
1051
 
open_archive(Archive, Acc, Fun) ->
1052
 
    Wrapper =
1053
 
        fun({N, GI, GB}, A) ->
1054
 
                %% Ensure full iteration at open
1055
 
                Funny = funny_split(N, $/),
1056
 
                {_Continue, A2} = Fun({Funny, GI, GB}, A),
1057
 
                {true, {true, Funny}, A2}
1058
 
        end,
1059
 
    prim_zip:open(Wrapper, Acc, Archive).
1060
 
 
1061
 
foldl_archive(PrimZip, Acc, Fun) ->
1062
 
    Wrapper =
1063
 
        fun({N, GI, GB}, A) ->
1064
 
                %% Allow partial iteration at foldl
1065
 
                {Continue, A2} = Fun({N, GI, GB}, A),
1066
 
                {Continue, true, A2}
1067
 
        end,                        
1068
 
    prim_zip:foldl(Wrapper, Acc, PrimZip).
1069
 
 
1070
 
cache_new(PS) ->
1071
 
    PS.
1072
 
 
1073
 
clear_cache(Archive, Cache) ->
1074
 
    erase(Archive),
1075
 
    case Cache of
1076
 
        {ok, PrimZip} ->
1077
 
            prim_zip:close(PrimZip);
1078
 
        {error, _} ->
1079
 
            ok
1080
 
    end.
1081
 
 
1082
 
%%% --------------------------------------------------------
1083
 
%%% Misc. functions.
1084
 
%%% --------------------------------------------------------
1085
 
 
1086
 
%%% Look for directory separators
1087
 
is_basename(File) ->
1088
 
    case member($/, File) of
1089
 
        true -> 
1090
 
            false;
1091
 
        false ->
1092
 
            case erlang:system_info(os_type) of
1093
 
                {win32, _} ->
1094
 
                    case File of
1095
 
                        [_,$:|_] -> false;
1096
 
                        _        -> not member($\\, File)
1097
 
                    end;
1098
 
                _ ->
1099
 
                    true
1100
 
            end
1101
 
    end.
1102
 
 
1103
 
send_all(U, [IP | AL], Cmd) ->
1104
 
    ?dbg(sendto, {U, IP, ?EBOOT_PORT, Cmd}),
1105
 
    prim_inet:sendto(U, IP, ?EBOOT_PORT, Cmd),
1106
 
    send_all(U, AL, Cmd);
1107
 
send_all(_U, [], _) -> ok.
1108
 
 
1109
 
concat([A|T]) when is_atom(A) ->                        %Atom
1110
 
    atom_to_list(A) ++ concat(T);
1111
 
concat([C|T]) when C >= 0, C =< 255 ->
1112
 
    [C|concat(T)];
1113
 
concat([S|T]) ->                                %String
1114
 
    S ++ concat(T);
1115
 
concat([]) ->
1116
 
    [].
1117
 
 
1118
 
member(X, [X|_]) -> true;
1119
 
member(X, [_|Y]) -> member(X, Y);
1120
 
member(_X, [])    -> false.
1121
 
 
1122
 
keymember(X, I, [Y | _]) when element(I,Y) =:= X -> true;
1123
 
keymember(X, I, [_ | T]) -> keymember(X, I, T);
1124
 
keymember(_X, _I, []) -> false.
1125
 
 
1126
 
keysort(I, L) -> keysort(I, L, []).
1127
 
 
1128
 
keysort(I, [X | L], Ls) ->
1129
 
    keysort(I, L, keyins(X, I, Ls));
1130
 
keysort(_I, [], Ls) -> Ls.
1131
 
 
1132
 
keyins(X, I, [Y | T]) when X < element(I,Y) -> [X,Y|T];
1133
 
keyins(X, I, [Y | T]) -> [Y | keyins(X, I, T)];
1134
 
keyins(X, _I, []) -> [X].
1135
 
 
1136
 
min(X, Y) when X < Y -> X;
1137
 
min(_X, Y) -> Y.
1138
 
 
1139
 
to_strs([P|Paths]) when is_atom(P) ->
1140
 
    [atom_to_list(P)|to_strs(Paths)];
1141
 
to_strs([P|Paths]) when is_list(P) ->
1142
 
    [P|to_strs(Paths)];
1143
 
to_strs([_|Paths]) ->
1144
 
    to_strs(Paths);
1145
 
to_strs([]) ->
1146
 
    [].
1147
 
 
1148
 
reverse([] = L) ->
1149
 
    L;
1150
 
reverse([_] = L) ->
1151
 
    L;
1152
 
reverse([A, B]) ->
1153
 
    [B, A];
1154
 
reverse([A, B | L]) ->
1155
 
    lists:reverse(L, [B, A]). % BIF
1156
 
                        
1157
 
%% Returns all lists in reverse order
1158
 
funny_split(List, Sep) ->
1159
 
   funny_split(List, Sep, [], []).
1160
 
 
1161
 
funny_split([Sep | Tail], Sep, Path, Paths) ->
1162
 
    funny_split(Tail, Sep, [], [Path | Paths]);
1163
 
funny_split([Head | Tail], Sep, Path, Paths) ->
1164
 
    funny_split(Tail, Sep, [Head | Path], Paths);
1165
 
funny_split([], _Sep, Path, Paths) ->
1166
 
    [Path | Paths].
1167
 
 
1168
 
name_split(ArchiveFile, File0) ->
1169
 
    File = absname(File0),
1170
 
    do_name_split(ArchiveFile, File).
1171
 
    
1172
 
do_name_split(undefined, File) ->
1173
 
    %% Ignore primary archive
1174
 
    case string_split(File, init:archive_extension(), []) of
1175
 
        no_split ->
1176
 
            %% Plain file
1177
 
            {file, File};
1178
 
        {split, _RevArchiveBase, RevArchiveFile, []} ->
1179
 
            %% Top dir in archive
1180
 
            ArchiveFile = reverse(RevArchiveFile),
1181
 
            {archive, ArchiveFile, []};
1182
 
        {split, _RevArchiveBase, RevArchiveFile, [$/ | FileInArchive]} ->
1183
 
            %% File in archive
1184
 
            ArchiveFile = reverse(RevArchiveFile),
1185
 
            {archive, ArchiveFile, FileInArchive};
1186
 
        {split, _RevArchiveBase, _RevArchiveFile, _FileInArchive} ->
1187
 
            %% False match. Assume plain file
1188
 
            {file, File}
1189
 
    end;
1190
 
do_name_split(ArchiveFile0, File) ->
1191
 
    %% Look first in primary archive
1192
 
    ArchiveFile = absname(ArchiveFile0),
1193
 
    case string_match(File, ArchiveFile, []) of
1194
 
        no_match ->
1195
 
            %% Archive or plain file
1196
 
            do_name_split(undefined, File);
1197
 
        {match, _RevPrimArchiveFile, FileInArchive} ->
1198
 
            %% Primary archive
1199
 
            case FileInArchive of
1200
 
                [$/ | FileInArchive2] ->
1201
 
                    {archive, ArchiveFile, FileInArchive2};
1202
 
                _ ->
1203
 
                    {archive, ArchiveFile, FileInArchive}
1204
 
            end
1205
 
    end.
1206
 
 
1207
 
string_match([Char | File], [Char | Archive], RevTop) ->
1208
 
    string_match(File, Archive, [Char | RevTop]);
1209
 
string_match(File, [], RevTop) ->
1210
 
    {match, RevTop, File};
1211
 
string_match(_File, _Archive, _RevTop) ->
1212
 
    no_match.
1213
 
 
1214
 
string_split([Char | File], [Char | Ext] = FullExt, RevTop) ->
1215
 
    RevTop2 = [Char | RevTop],
1216
 
    string_split2(File, Ext, RevTop, RevTop2, File, FullExt, RevTop2);
1217
 
string_split([Char | File], Ext, RevTop) ->
1218
 
    string_split(File, Ext, [Char | RevTop]);
1219
 
string_split([], _Ext, _RevTop) ->
1220
 
    no_split.
1221
 
 
1222
 
string_split2([Char | File], [Char | Ext], RevBase, RevTop, SaveFile, SaveExt, SaveTop) ->
1223
 
    string_split2(File, Ext, RevBase, [Char | RevTop], SaveFile, SaveExt, SaveTop);
1224
 
string_split2(File, [], RevBase, RevTop, _SaveFile, _SaveExt, _SaveTop) ->
1225
 
    {split, RevBase, RevTop, File};
1226
 
string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) ->
1227
 
    string_split(SaveFile, SaveExt, SaveTop).
1228
 
 
1229
 
%% Parse list of ipv4 addresses 
1230
 
ipv4_list([H | T]) ->
1231
 
    IPV = if is_atom(H) -> ipv4_address(atom_to_list(H));
1232
 
             is_list(H) -> ipv4_address(H);
1233
 
             true -> {error,einal}
1234
 
          end,
1235
 
    case IPV of
1236
 
        {ok,IP} -> [IP | ipv4_list(T)];
1237
 
        _ -> ipv4_list(T)
1238
 
    end;
1239
 
ipv4_list([]) -> [].
1240
 
    
1241
 
%%
1242
 
%% Parse Ipv4 address: d1.d2.d3.d4 (from inet_parse)
1243
 
%%
1244
 
%% Return {ok, IP} | {error, einval}
1245
 
%%
1246
 
ipv4_address(Cs) ->
1247
 
    case catch ipv4_addr(Cs, []) of
1248
 
        {'EXIT',_} -> {error,einval};
1249
 
        Addr -> {ok,Addr}
1250
 
    end.
1251
 
 
1252
 
ipv4_addr([C | Cs], IP) when C >= $0, C =< $9 -> ipv4_addr(Cs, C-$0, IP).
1253
 
 
1254
 
ipv4_addr([$.|Cs], N, IP) when N < 256 -> ipv4_addr(Cs, [N|IP]);
1255
 
ipv4_addr([C|Cs], N, IP) when C >= $0, C =< $9 ->
1256
 
    ipv4_addr(Cs, N*10 + (C-$0), IP);
1257
 
ipv4_addr([], D, [C,B,A]) when D < 256 -> {A,B,C,D}.
1258
 
 
1259
 
%% A simplified version of filename:absname/1
1260
 
absname(Name) ->
1261
 
    case pathtype(Name) of
1262
 
        absolute ->
1263
 
            normalize(Name, []);
1264
 
        relative ->
1265
 
            case prim_file:get_cwd() of
1266
 
                {ok, Cwd} ->
1267
 
                    normalize(Cwd ++ "/" ++ Name, []);
1268
 
                {error, _} -> 
1269
 
                    normalize(Name, [])
1270
 
            end;
1271
 
        volumerelative ->
1272
 
            case prim_file:get_cwd() of
1273
 
                {ok, Cwd} ->
1274
 
                    absname_vr(Name, Cwd);
1275
 
                {error, _} -> 
1276
 
                    normalize(Name, [])
1277
 
            end
1278
 
    end.
1279
 
 
1280
 
absname_vr([$/ | NameRest], [Drive, $\: | _]) ->
1281
 
    %% Absolute path on current drive.
1282
 
    normalize([Drive, $\: | NameRest], []);
1283
 
absname_vr([Drive, $\: | NameRest], [Drive, $\: | _] = Cwd) ->
1284
 
    %% Relative to current directory on current drive.
1285
 
    normalize(Cwd ++ "/" ++ NameRest, []);
1286
 
absname_vr([Drive, $: | NameRest], _) ->
1287
 
    %% Relative to current directory on another drive.
1288
 
    case prim_file:get_cwd([Drive, $:]) of
1289
 
        {ok, DriveCwd}  -> normalize(DriveCwd ++ "/" ++ NameRest, []);
1290
 
        {error, _}      -> normalize([Drive, $\:, $/] ++ NameRest, [])
1291
 
    end.
1292
 
 
1293
 
pathtype(Name) ->
1294
 
    case erlang:system_info(os_type) of
1295
 
        {win32, _} ->
1296
 
            case Name of
1297
 
                [$/, $/|_]             -> absolute;
1298
 
                [$\\, $/|_]            -> absolute;
1299
 
                [$/, $\\|_]            -> absolute;
1300
 
                [$\\, $\\|_]           -> absolute;
1301
 
                [$/|_]                 -> volumerelative;
1302
 
                [$\\|_]                -> volumerelative;
1303
 
                [_Drive, $\:, $/ | _]  -> absolute;
1304
 
                [_Drive, $\:, $\\ | _] -> absolute;
1305
 
                [_Drive, $\: | _]      -> volumerelative;
1306
 
                _                      -> relative
1307
 
            end;
1308
 
        _ ->
1309
 
            case Name of
1310
 
                [$/|_]  -> absolute;
1311
 
                [$\\|_] -> absolute;
1312
 
                _       -> relative
1313
 
            end
1314
 
    end.
1315
 
 
1316
 
normalize([$\\ | Chars], Acc) ->
1317
 
    normalize(Chars, [$/ | Acc]);
1318
 
normalize([Char | Chars], Acc) ->
1319
 
    normalize(Chars, [Char | Acc]);
1320
 
normalize([], Acc) ->
1321
 
    reverse(Acc).