~clint-fewbar/ubuntu/precise/erlang/merge-15b

« back to all changes in this revision

Viewing changes to lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_bup.erl

  • Committer: Package Import Robot
  • Author(s): Sergei Golovan
  • Date: 2011-12-15 19:20:10 UTC
  • mfrom: (1.1.18) (3.5.15 sid)
  • mto: (3.5.16 sid)
  • mto: This revision was merged to the branch mainline in revision 33.
  • Revision ID: package-import@ubuntu.com-20111215192010-jnxcfe3tbrpp0big
Tags: 1:15.b-dfsg-1
* New upstream release.
* Upload to experimental because this release breaks external drivers
  API along with ABI, so several applications are to be fixed.
* Removed SSL patch because the old SSL implementation is removed from
  the upstream distribution.
* Removed never used patch which added native code to erlang beam files.
* Removed the erlang-docbuilder binary package because the docbuilder
  application was dropped by upstream.
* Documented dropping ${erlang-docbuilder:Depends} substvar in
  erlang-depends(1) manpage.
* Made erlang-base and erlang-base-hipe provide virtual package
  erlang-abi-15.b (the number means the first erlang version, which
  provides current ABI).

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: mnesia_bup.erl,v 1.1 2008/12/17 09:53:37 mikpe Exp $
 
17
%%
 
18
-module(mnesia_bup).
 
19
-export([
 
20
         %% Public interface
 
21
         iterate/4,
 
22
         read_schema/2,
 
23
         fallback_bup/0,
 
24
         fallback_exists/0,
 
25
         tm_fallback_start/1,
 
26
         create_schema/1,
 
27
         install_fallback/1,
 
28
         install_fallback/2,
 
29
         uninstall_fallback/0,
 
30
         uninstall_fallback/1,
 
31
         traverse_backup/4,
 
32
         traverse_backup/6,
 
33
         make_initial_backup/3,
 
34
         fallback_to_schema/0,
 
35
         lookup_schema/2,
 
36
         schema2bup/1,
 
37
         refresh_cookie/2,
 
38
 
 
39
         %% Internal
 
40
         fallback_receiver/2,
 
41
         install_fallback_master/2,
 
42
         uninstall_fallback_master/2,
 
43
         local_uninstall_fallback/2,
 
44
         do_traverse_backup/7,
 
45
         trav_apply/4
 
46
        ]).
 
47
 
 
48
-include("mnesia.hrl").
 
49
-import(mnesia_lib, [verbose/2, dbg_out/2]).
 
50
 
 
51
-record(restore, {mode, bup_module, bup_data}).
 
52
 
 
53
-record(fallback_args, {opaque,
 
54
                        scope = global,
 
55
                        module = mnesia_monitor:get_env(backup_module),
 
56
                        use_default_dir = true,
 
57
                        mnesia_dir,
 
58
                        fallback_bup,
 
59
                        fallback_tmp,
 
60
                        skip_tables = [],
 
61
                        keep_tables = [],
 
62
                        default_op = keep_tables
 
63
                       }).
 
64
 
 
65
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
66
%% Backup iterator
 
67
 
 
68
%% Reads schema section and iterates over all records in a backup.
 
69
%%
 
70
%% Fun(BunchOfRecords, Header, Schema, Acc) is applied when a suitable amount
 
71
%% of records has been collected.
 
72
%%
 
73
%% BunchOfRecords will be [] when the iteration is done.
 
74
iterate(Mod, Fun, Opaque, Acc) ->
 
75
    R = #restore{bup_module = Mod, bup_data = Opaque},
 
76
    case catch read_schema_section(R) of
 
77
        {error, Reason} ->
 
78
            {error, Reason};
 
79
        {R2, {Header, Schema, Rest}} ->
 
80
            case catch iter(R2, Header, Schema, Fun, Acc, Rest) of
 
81
                {ok, R3, Res} ->
 
82
                    catch safe_apply(R3, close_read, [R3#restore.bup_data]),
 
83
                    {ok, Res};
 
84
                {error, Reason} ->
 
85
                    catch safe_apply(R2, close_read, [R2#restore.bup_data]),
 
86
                    {error, Reason};
 
87
                {'EXIT', Pid, Reason} ->
 
88
                    catch safe_apply(R2, close_read, [R2#restore.bup_data]),
 
89
                    {error, {'EXIT', Pid, Reason}};
 
90
                {'EXIT', Reason} ->
 
91
                    catch safe_apply(R2, close_read, [R2#restore.bup_data]),
 
92
                    {error, {'EXIT', Reason}}
 
93
            end
 
94
    end.
 
95
 
 
96
iter(R, Header, Schema, Fun, Acc, []) ->
 
97
    case safe_apply(R, read, [R#restore.bup_data]) of
 
98
        {R2, []} ->
 
99
            Res = Fun([], Header, Schema, Acc),
 
100
            {ok, R2, Res};
 
101
        {R2, BupItems} ->
 
102
            iter(R2, Header, Schema, Fun, Acc, BupItems)
 
103
    end;
 
104
iter(R, Header, Schema, Fun, Acc, BupItems) ->
 
105
    Acc2 = Fun(BupItems, Header, Schema, Acc),
 
106
    iter(R, Header, Schema, Fun, Acc2, []).
 
107
 
 
108
safe_apply(R, write, [_, Items]) when Items == [] ->
 
109
    R;
 
110
safe_apply(R, What, Args) ->
 
111
    Abort = fun(Re) -> abort_restore(R, What, Args, Re) end,
 
112
    receive
 
113
        {'EXIT', Pid, Re} -> Abort({'EXIT', Pid, Re})
 
114
    after 0 ->
 
115
            Mod = R#restore.bup_module,
 
116
            case catch apply(Mod, What, Args) of
 
117
                {ok, Opaque, Items} when What == read ->
 
118
                    {R#restore{bup_data = Opaque}, Items};
 
119
                {ok, Opaque}  when What /= read->
 
120
                    R#restore{bup_data = Opaque};
 
121
                {error, Re} ->
 
122
                    Abort(Re);
 
123
                Re ->
 
124
                    Abort(Re)
 
125
            end
 
126
    end.
 
127
 
 
128
abort_restore(R, What, Args, Reason) ->
 
129
    Mod = R#restore.bup_module,
 
130
    Opaque = R#restore.bup_data,
 
131
    dbg_out("Restore aborted. ~p:~p~p -> ~p~n",
 
132
            [Mod, What, Args, Reason]),
 
133
    catch apply(Mod, close_read, [Opaque]),
 
134
    throw({error, Reason}).
 
135
 
 
136
fallback_to_schema() ->
 
137
    Fname = fallback_bup(),
 
138
    fallback_to_schema(Fname).
 
139
 
 
140
fallback_to_schema(Fname) ->
 
141
    Mod = mnesia_backup,
 
142
    case read_schema(Mod, Fname) of
 
143
        {error, Reason} ->
 
144
            {error, Reason};
 
145
        Schema ->
 
146
            case catch lookup_schema(schema, Schema) of
 
147
                {error, _} ->
 
148
                    {error, "No schema in fallback"};
 
149
                List ->
 
150
                    {ok, fallback, List}
 
151
            end
 
152
    end.
 
153
 
 
154
%% Opens Opaque reads schema and then close
 
155
read_schema(Mod, Opaque) ->
 
156
    R = #restore{bup_module = Mod, bup_data = Opaque},
 
157
    case catch read_schema_section(R) of
 
158
        {error, Reason} ->
 
159
            {error, Reason};
 
160
        {R2, {_Header, Schema, _}} ->
 
161
            catch safe_apply(R2, close_read, [R2#restore.bup_data]),
 
162
            Schema
 
163
    end.
 
164
 
 
165
%% Open backup media and extract schema
 
166
%% rewind backup media and leave it open
 
167
%% Returns {R, {Header, Schema}}
 
168
read_schema_section(R) ->
 
169
    case catch do_read_schema_section(R) of
 
170
        {'EXIT', Reason} ->
 
171
            catch safe_apply(R, close_read, [R#restore.bup_data]),
 
172
            {error, {'EXIT', Reason}};
 
173
        {error, Reason} ->
 
174
            catch safe_apply(R, close_read, [R#restore.bup_data]),
 
175
            {error, Reason};
 
176
        {R2, {H, Schema, Rest}} ->
 
177
            Schema2 = convert_schema(H#log_header.log_version, Schema),
 
178
            {R2, {H, Schema2, Rest}}
 
179
    end.
 
180
 
 
181
do_read_schema_section(R) ->
 
182
    R2 = safe_apply(R, open_read, [R#restore.bup_data]),
 
183
    {R3, RawSchema} = safe_apply(R2, read, [R2#restore.bup_data]),
 
184
    do_read_schema_section(R3, verify_header(RawSchema), []).
 
185
 
 
186
do_read_schema_section(R, {ok, B, C, []}, Acc) ->
 
187
    case safe_apply(R, read, [R#restore.bup_data]) of
 
188
        {R2, []} ->
 
189
            {R2, {B, Acc, []}};
 
190
        {R2, RawSchema} ->
 
191
            do_read_schema_section(R2, {ok, B, C, RawSchema}, Acc)
 
192
    end;
 
193
 
 
194
do_read_schema_section(R, {ok, B, C, [Head | Tail]}, Acc)
 
195
        when element(1, Head) == schema ->
 
196
    do_read_schema_section(R, {ok, B, C, Tail}, Acc ++ [Head]);
 
197
 
 
198
do_read_schema_section(R, {ok, B, _C, Rest}, Acc) ->
 
199
    {R, {B, Acc, Rest}};
 
200
 
 
201
do_read_schema_section(_R, {error, Reason}, _Acc) ->
 
202
    {error, Reason}.
 
203
 
 
204
verify_header([H | RawSchema])  when record(H, log_header) ->
 
205
    Current = mnesia_log:backup_log_header(),
 
206
    if
 
207
        H#log_header.log_kind == Current#log_header.log_kind ->
 
208
            Versions = ["0.1", "1.1", Current#log_header.log_version],
 
209
            case lists:member(H#log_header.log_version, Versions) of
 
210
                true ->
 
211
                    {ok, H, Current, RawSchema};
 
212
                false ->
 
213
                    {error, {"Bad header version. Cannot be used as backup.", H}}
 
214
            end;
 
215
        true ->
 
216
            {error, {"Bad kind of header. Cannot be used as backup.", H}}
 
217
    end;
 
218
verify_header(RawSchema) ->
 
219
    {error, {"Missing header. Cannot be used as backup.", catch hd(RawSchema)}}.
 
220
 
 
221
refresh_cookie(Schema, NewCookie) ->
 
222
    case lists:keysearch(schema, 2, Schema) of
 
223
        {value, {schema, schema, List}} ->
 
224
            Cs = mnesia_schema:list2cs(List),
 
225
            Cs2 = Cs#cstruct{cookie = NewCookie},
 
226
            Item = {schema, schema, mnesia_schema:cs2list(Cs2)},
 
227
            lists:keyreplace(schema, 2, Schema, Item);
 
228
 
 
229
        false ->
 
230
            Reason = "No schema found. Cannot be used as backup.",
 
231
            throw({error, {Reason, Schema}})
 
232
    end.
 
233
 
 
234
%% Convert schema items from an external backup
 
235
%% If backup format is the latest, no conversion is needed
 
236
%% All supported backup formats should have their converters
 
237
%% here as separate function clauses.
 
238
convert_schema("0.1", Schema) ->
 
239
    convert_0_1(Schema);
 
240
convert_schema("1.1", Schema) ->
 
241
    %% The new backup format is a pure extension of the old one
 
242
    Current = mnesia_log:backup_log_header(),
 
243
    convert_schema(Current#log_header.log_version, Schema);
 
244
convert_schema(Latest, Schema) ->
 
245
    H = mnesia_log:backup_log_header(),
 
246
    if
 
247
        H#log_header.log_version == Latest ->
 
248
            Schema;
 
249
        true ->
 
250
            Reason = "Bad backup header version. Cannot convert schema.",
 
251
            throw({error, {Reason, H}})
 
252
    end.
 
253
 
 
254
%% Backward compatibility for 0.1
 
255
convert_0_1(Schema) ->
 
256
    case lists:keysearch(schema, 2, Schema) of
 
257
        {value, {schema, schema, List}} ->
 
258
            Schema2 = lists:keydelete(schema, 2, Schema),
 
259
            Cs = mnesia_schema:list2cs(List),
 
260
            convert_0_1(Schema2, [], Cs);
 
261
        false ->
 
262
            List = mnesia_schema:get_initial_schema(disc_copies, [node()]),
 
263
            Cs = mnesia_schema:list2cs(List),
 
264
            convert_0_1(Schema, [], Cs)
 
265
    end.
 
266
 
 
267
convert_0_1([{schema, cookie, Cookie} | Schema], Acc, Cs) ->
 
268
    convert_0_1(Schema, Acc, Cs#cstruct{cookie = Cookie});
 
269
convert_0_1([{schema, db_nodes, DbNodes} | Schema], Acc, Cs) ->
 
270
    convert_0_1(Schema, Acc, Cs#cstruct{disc_copies = DbNodes});
 
271
convert_0_1([{schema, version, Version} | Schema], Acc, Cs) ->
 
272
    convert_0_1(Schema, Acc, Cs#cstruct{version = Version});
 
273
convert_0_1([{schema, Tab, Def} | Schema], Acc, Cs) ->
 
274
    Head =
 
275
        case lists:keysearch(index, 1, Def) of
 
276
            {value, {index, PosList}} ->
 
277
                %% Remove the snmp "index"
 
278
                P = PosList -- [snmp],
 
279
                Def2 = lists:keyreplace(index, 1, Def, {index, P}),
 
280
                {schema, Tab, Def2};
 
281
            false ->
 
282
                {schema, Tab, Def}
 
283
        end,
 
284
    convert_0_1(Schema, [Head | Acc], Cs);
 
285
convert_0_1([Head | Schema], Acc, Cs) ->
 
286
    convert_0_1(Schema, [Head | Acc], Cs);
 
287
convert_0_1([], Acc, Cs) ->
 
288
    [schema2bup({schema, schema, Cs}) | Acc].
 
289
 
 
290
%% Returns Val or throw error
 
291
lookup_schema(Key, Schema) ->
 
292
    case lists:keysearch(Key, 2, Schema) of
 
293
        {value, {schema, Key, Val}} -> Val;
 
294
        false -> throw({error, {"Cannot lookup", Key}})
 
295
    end.
 
296
 
 
297
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
298
%% Backup compatibility
 
299
 
 
300
%% Convert internal schema items to backup dito
 
301
schema2bup({schema, Tab}) ->
 
302
    {schema, Tab};
 
303
schema2bup({schema, Tab, TableDef}) ->
 
304
    {schema, Tab, mnesia_schema:cs2list(TableDef)}.
 
305
 
 
306
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
307
%% Create schema on the given nodes
 
308
%% Requires that old schemas has been deleted
 
309
%% Returns ok | {error, Reason}
 
310
create_schema([]) ->
 
311
    create_schema([node()]);
 
312
create_schema(Ns) when list(Ns) ->
 
313
    case is_set(Ns) of
 
314
        true ->
 
315
            create_schema(Ns, mnesia_schema:ensure_no_schema(Ns));
 
316
        false ->
 
317
            {error, {combine_error, Ns}}
 
318
    end;
 
319
create_schema(Ns) ->
 
320
    {error, {badarg, Ns}}.
 
321
 
 
322
is_set(List) when list(List) ->
 
323
    ordsets:is_set(lists:sort(List));
 
324
is_set(_) ->
 
325
    false.
 
326
 
 
327
create_schema(Ns, ok) ->
 
328
    %% Ensure that we access the intended Mnesia
 
329
    %% directory. This function may not be called
 
330
    %% during startup since it will cause the
 
331
    %% application_controller to get into deadlock
 
332
    case mnesia_lib:ensure_loaded(?APPLICATION) of
 
333
        ok ->
 
334
            case mnesia_monitor:get_env(schema_location) of
 
335
                ram ->
 
336
                    {error, {has_no_disc, node()}};
 
337
                _ ->
 
338
                    case mnesia_schema:opt_create_dir(true, mnesia_lib:dir()) of
 
339
                        {error, What} ->
 
340
                            {error, What};
 
341
                        ok ->
 
342
                            Mod = mnesia_backup,
 
343
                            Str = mk_str(),
 
344
                            File = mnesia_lib:dir(Str),
 
345
                            file:delete(File),
 
346
                            case catch make_initial_backup(Ns, File, Mod) of
 
347
                                {ok, _Res} ->
 
348
                                    case do_install_fallback(File, Mod) of
 
349
                                        ok ->
 
350
                                            file:delete(File),
 
351
                                            ok;
 
352
                                        {error, Reason} ->
 
353
                                            {error, Reason}
 
354
                                    end;
 
355
                                {error, Reason} ->
 
356
                                    {error, Reason}
 
357
                            end
 
358
                    end
 
359
            end;
 
360
        {error, Reason} ->
 
361
            {error, Reason}
 
362
    end;
 
363
create_schema(_Ns, {error, Reason}) ->
 
364
    {error, Reason};
 
365
create_schema(_Ns, Reason) ->
 
366
    {error, Reason}.
 
367
 
 
368
mk_str() ->
 
369
    Now = [integer_to_list(I) || I <- tuple_to_list(now())],
 
370
    lists:concat([node()] ++ Now ++ ".TMP").
 
371
 
 
372
make_initial_backup(Ns, Opaque, Mod) ->
 
373
    Schema = [{schema, schema, mnesia_schema:get_initial_schema(disc_copies, Ns)}],
 
374
    O2 = do_apply(Mod, open_write, [Opaque], Opaque),
 
375
    O3 = do_apply(Mod, write, [O2, [mnesia_log:backup_log_header()]], O2),
 
376
    O4 = do_apply(Mod, write, [O3, Schema], O3),
 
377
    O5 = do_apply(Mod, commit_write, [O4], O4),
 
378
    {ok, O5}.
 
379
 
 
380
do_apply(_, write, [_, Items], Opaque) when Items == [] ->
 
381
    Opaque;
 
382
do_apply(Mod, What, Args, _Opaque) ->
 
383
    case catch apply(Mod, What, Args) of
 
384
        {ok, Opaque2} ->  Opaque2;
 
385
        {error, Reason} -> throw({error, Reason});
 
386
        {'EXIT', Reason} -> throw({error, {'EXIT', Reason}})
 
387
    end.
 
388
 
 
389
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
390
%% Restore
 
391
 
 
392
%% Restore schema and possibly other tables from a backup
 
393
%% and replicate them to the necessary nodes
 
394
%% Requires that old schemas has been deleted
 
395
%% Returns ok | {error, Reason}
 
396
install_fallback(Opaque) ->
 
397
    install_fallback(Opaque, []).
 
398
 
 
399
install_fallback(Opaque, Args) ->
 
400
    %% Ensure that we access the intended Mnesia
 
401
    %% directory. This function may not be called
 
402
    %% during startup since it will cause the
 
403
    %% application_controller to get into deadlock
 
404
    case mnesia_lib:ensure_loaded(?APPLICATION) of
 
405
        ok ->
 
406
            do_install_fallback(Opaque, Args);
 
407
        {error, Reason} ->
 
408
            {error, Reason}
 
409
    end.
 
410
 
 
411
do_install_fallback(Opaque,  Mod) when atom(Mod) ->
 
412
    do_install_fallback(Opaque, [{module, Mod}]);
 
413
do_install_fallback(Opaque, Args) when list(Args) ->
 
414
    case check_fallback_args(Args, #fallback_args{opaque = Opaque}) of
 
415
        {ok, FA} ->
 
416
            do_install_fallback(FA);
 
417
        {error, Reason} ->
 
418
            {error, Reason}
 
419
    end;
 
420
do_install_fallback(_Opaque, Args) ->
 
421
    {error, {badarg, Args}}.
 
422
 
 
423
check_fallback_args([Arg | Tail], FA) ->
 
424
    case catch check_fallback_arg_type(Arg, FA) of
 
425
        {'EXIT', _Reason} ->
 
426
            {error, {badarg, Arg}};
 
427
        FA2 ->
 
428
            check_fallback_args(Tail, FA2)
 
429
    end;
 
430
check_fallback_args([], FA) ->
 
431
    {ok, FA}.
 
432
 
 
433
check_fallback_arg_type(Arg, FA) ->
 
434
    case Arg of
 
435
        {scope, global} ->
 
436
            FA#fallback_args{scope = global};
 
437
        {scope, local} ->
 
438
            FA#fallback_args{scope = local};
 
439
        {module, Mod} ->
 
440
            Mod2 = mnesia_monitor:do_check_type(backup_module, Mod),
 
441
            FA#fallback_args{module = Mod2};
 
442
        {mnesia_dir, Dir} ->
 
443
            FA#fallback_args{mnesia_dir = Dir,
 
444
                             use_default_dir = false};
 
445
        {keep_tables, Tabs} ->
 
446
            atom_list(Tabs),
 
447
            FA#fallback_args{keep_tables = Tabs};
 
448
        {skip_tables, Tabs} ->
 
449
            atom_list(Tabs),
 
450
            FA#fallback_args{skip_tables = Tabs};
 
451
        {default_op, keep_tables} ->
 
452
            FA#fallback_args{default_op = keep_tables};
 
453
        {default_op, skip_tables} ->
 
454
            FA#fallback_args{default_op = skip_tables}
 
455
    end.
 
456
 
 
457
atom_list([H | T]) when atom(H) ->
 
458
    atom_list(T);
 
459
atom_list([]) ->
 
460
    ok.
 
461
 
 
462
do_install_fallback(FA) ->
 
463
    Pid = spawn_link(?MODULE, install_fallback_master, [self(), FA]),
 
464
    Res =
 
465
        receive
 
466
            {'EXIT', Pid, Reason} -> % if appl has trapped exit
 
467
                {error, {'EXIT', Reason}};
 
468
            {Pid, Res2} ->
 
469
                case Res2 of
 
470
                    {ok, _} ->
 
471
                        ok;
 
472
                    {error, Reason} ->
 
473
                        {error, {"Cannot install fallback", Reason}}
 
474
                end
 
475
        end,
 
476
    Res.
 
477
 
 
478
install_fallback_master(ClientPid, FA) ->
 
479
    process_flag(trap_exit, true),
 
480
    State = {start, FA},
 
481
    Opaque = FA#fallback_args.opaque,
 
482
    Mod = FA#fallback_args.module,
 
483
    Res = (catch iterate(Mod, fun restore_recs/4, Opaque, State)),
 
484
    unlink(ClientPid),
 
485
    ClientPid ! {self(), Res},
 
486
    exit(shutdown).
 
487
 
 
488
restore_recs(_, _, _, stop) ->
 
489
    throw({error, "restore_recs already stopped"});
 
490
 
 
491
restore_recs(Recs, Header, Schema, {start, FA}) ->
 
492
    %% No records in backup
 
493
    Schema2 = convert_schema(Header#log_header.log_version, Schema),
 
494
    CreateList = lookup_schema(schema, Schema2),
 
495
    case catch mnesia_schema:list2cs(CreateList) of
 
496
        {'EXIT', Reason} ->
 
497
            throw({error, {"Bad schema in restore_recs", Reason}});
 
498
        Cs ->
 
499
            Ns = get_fallback_nodes(FA, Cs#cstruct.disc_copies),
 
500
            global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity),
 
501
            Args = [self(), FA],
 
502
            Pids = [spawn_link(N, ?MODULE, fallback_receiver, Args) || N <- Ns],
 
503
            send_fallback(Pids, {start, Header, Schema2}),
 
504
            Res = restore_recs(Recs, Header, Schema2, Pids),
 
505
            global:del_lock({{mnesia_table_lock, schema}, self()}, Ns),
 
506
            Res
 
507
    end;
 
508
 
 
509
restore_recs([], _Header, _Schema, Pids) ->
 
510
    send_fallback(Pids, swap),
 
511
    send_fallback(Pids, stop),
 
512
    stop;
 
513
 
 
514
restore_recs(Recs, _, _, Pids) ->
 
515
    send_fallback(Pids, {records, Recs}),
 
516
    Pids.
 
517
 
 
518
get_fallback_nodes(FA, Ns) ->
 
519
    This = node(),
 
520
    case lists:member(This, Ns) of
 
521
        true ->
 
522
            case FA#fallback_args.scope of
 
523
                global ->  Ns;
 
524
                local -> [This]
 
525
            end;
 
526
        false ->
 
527
            throw({error, {"No disc resident schema on local node", Ns}})
 
528
    end.
 
529
 
 
530
send_fallback(Pids, Msg) when list(Pids), Pids /= [] ->
 
531
    lists:foreach(fun(Pid) -> Pid ! {self(), Msg} end, Pids),
 
532
    rec_answers(Pids, []).
 
533
 
 
534
rec_answers([], Acc) ->
 
535
    case {lists:keysearch(error, 1, Acc), mnesia_lib:uniq(Acc)} of
 
536
        {{value, {error, Val}}, _} -> throw({error, Val});
 
537
        {_, [SameAnswer]} -> SameAnswer;
 
538
        {_, Other} -> throw({error, {"Different answers", Other}})
 
539
    end;
 
540
rec_answers(Pids, Acc) ->
 
541
    receive
 
542
        {'EXIT', Pid, stopped} ->
 
543
            Pids2 = lists:delete(Pid, Pids),
 
544
            rec_answers(Pids2, [stopped|Acc]);
 
545
        {'EXIT', Pid, Reason} ->
 
546
            Pids2 = lists:delete(Pid, Pids),
 
547
            rec_answers(Pids2, [{error, {'EXIT', Pid, Reason}}|Acc]);
 
548
        {Pid, Reply} ->
 
549
            Pids2 = lists:delete(Pid, Pids),
 
550
            rec_answers(Pids2, [Reply|Acc])
 
551
    end.
 
552
 
 
553
fallback_exists() ->
 
554
    Fname = fallback_bup(),
 
555
    fallback_exists(Fname).
 
556
 
 
557
fallback_exists(Fname) ->
 
558
    case mnesia_monitor:use_dir() of
 
559
        true ->
 
560
            mnesia_lib:exists(Fname);
 
561
        false ->
 
562
            case ?catch_val(active_fallback) of
 
563
                {'EXIT', _} -> false;
 
564
                Bool -> Bool
 
565
            end
 
566
    end.
 
567
 
 
568
fallback_name() -> "FALLBACK.BUP".
 
569
fallback_bup() -> mnesia_lib:dir(fallback_name()).
 
570
 
 
571
fallback_tmp_name() -> "FALLBACK.TMP".
 
572
%% fallback_full_tmp_name() -> mnesia_lib:dir(fallback_tmp_name()).
 
573
 
 
574
fallback_receiver(Master, FA) ->
 
575
    process_flag(trap_exit, true),
 
576
 
 
577
    case catch register(mnesia_fallback, self()) of
 
578
        {'EXIT', _} ->
 
579
            Reason = {already_exists, node()},
 
580
            local_fallback_error(Master, Reason);
 
581
        true ->
 
582
            FA2 = check_fallback_dir(Master, FA),
 
583
            Bup = FA2#fallback_args.fallback_bup,
 
584
            case mnesia_lib:exists(Bup) of
 
585
                true ->
 
586
                    Reason2 = {already_exists, node()},
 
587
                    local_fallback_error(Master, Reason2);
 
588
                false ->
 
589
                    Mod = mnesia_backup,
 
590
                    Tmp = FA2#fallback_args.fallback_tmp,
 
591
                    R = #restore{mode = replace,
 
592
                                 bup_module = Mod,
 
593
                                 bup_data = Tmp},
 
594
                    file:delete(Tmp),
 
595
                    case catch fallback_receiver_loop(Master, R, FA2, schema) of
 
596
                        {error, Reason} ->
 
597
                            local_fallback_error(Master, Reason);
 
598
                        Other ->
 
599
                            exit(Other)
 
600
                    end
 
601
            end
 
602
    end.
 
603
 
 
604
local_fallback_error(Master, Reason) ->
 
605
    Master ! {self(), {error, Reason}},
 
606
    unlink(Master),
 
607
    exit(Reason).
 
608
 
 
609
check_fallback_dir(Master, FA) ->
 
610
    case mnesia:system_info(schema_location) of
 
611
        ram ->
 
612
            Reason = {has_no_disc, node()},
 
613
            local_fallback_error(Master, Reason);
 
614
        _ ->
 
615
            Dir = check_fallback_dir_arg(Master, FA),
 
616
            Bup = filename:join([Dir, fallback_name()]),
 
617
            Tmp = filename:join([Dir, fallback_tmp_name()]),
 
618
            FA#fallback_args{fallback_bup = Bup,
 
619
                             fallback_tmp = Tmp,
 
620
                             mnesia_dir = Dir}
 
621
    end.
 
622
 
 
623
check_fallback_dir_arg(Master, FA) ->
 
624
    case FA#fallback_args.use_default_dir of
 
625
        true ->
 
626
            mnesia_lib:dir();
 
627
        false when FA#fallback_args.scope == local ->
 
628
            Dir = FA#fallback_args.mnesia_dir,
 
629
            case catch mnesia_monitor:do_check_type(dir, Dir) of
 
630
                {'EXIT', _R} ->
 
631
                    Reason = {badarg, {dir, Dir}, node()},
 
632
                    local_fallback_error(Master, Reason);
 
633
                AbsDir->
 
634
                    AbsDir
 
635
            end;
 
636
        false when FA#fallback_args.scope == global ->
 
637
            Reason = {combine_error, global, dir, node()},
 
638
            local_fallback_error(Master, Reason)
 
639
    end.
 
640
 
 
641
fallback_receiver_loop(Master, R, FA, State) ->
 
642
    receive
 
643
        {Master, {start, Header, Schema}} when State == schema ->
 
644
            Dir = FA#fallback_args.mnesia_dir,
 
645
            throw_bad_res(ok, mnesia_schema:opt_create_dir(true, Dir)),
 
646
            R2 = safe_apply(R, open_write, [R#restore.bup_data]),
 
647
            R3 = safe_apply(R2, write, [R2#restore.bup_data, [Header]]),
 
648
            BupSchema = [schema2bup(S) || S <- Schema],
 
649
            R4 = safe_apply(R3, write, [R3#restore.bup_data, BupSchema]),
 
650
            Master ! {self(), ok},
 
651
            fallback_receiver_loop(Master, R4, FA, records);
 
652
 
 
653
        {Master, {records, Recs}} when State == records ->
 
654
            R2 = safe_apply(R, write, [R#restore.bup_data, Recs]),
 
655
            Master ! {self(), ok},
 
656
            fallback_receiver_loop(Master, R2, FA, records);
 
657
 
 
658
        {Master, swap} when State /= schema ->
 
659
            ?eval_debug_fun({?MODULE, fallback_receiver_loop, pre_swap}, []),
 
660
            safe_apply(R, commit_write, [R#restore.bup_data]),
 
661
            Bup = FA#fallback_args.fallback_bup,
 
662
            Tmp = FA#fallback_args.fallback_tmp,
 
663
            throw_bad_res(ok, file:rename(Tmp, Bup)),
 
664
            catch mnesia_lib:set(active_fallback, true),
 
665
            ?eval_debug_fun({?MODULE, fallback_receiver_loop, post_swap}, []),
 
666
            Master ! {self(), ok},
 
667
            fallback_receiver_loop(Master, R, FA, stop);
 
668
 
 
669
        {Master, stop} when State == stop ->
 
670
            stopped;
 
671
 
 
672
        Msg ->
 
673
            safe_apply(R, abort_write, [R#restore.bup_data]),
 
674
            Tmp = FA#fallback_args.fallback_tmp,
 
675
            file:delete(Tmp),
 
676
            throw({error, "Unexpected msg fallback_receiver_loop", Msg})
 
677
    end.
 
678
 
 
679
throw_bad_res(Expected, Expected) -> Expected;
 
680
throw_bad_res(_Expected, {error, Actual}) -> throw({error, Actual});
 
681
throw_bad_res(_Expected, Actual) -> throw({error, Actual}).
 
682
 
 
683
-record(local_tab, {name, storage_type, dets_args, open, close, add, record_name}).
 
684
 
 
685
tm_fallback_start(IgnoreFallback) ->
 
686
    mnesia_schema:lock_schema(),
 
687
    Res = do_fallback_start(fallback_exists(), IgnoreFallback),
 
688
    mnesia_schema: unlock_schema(),
 
689
    case Res of
 
690
        ok -> ok;
 
691
        {error, Reason} -> exit(Reason)
 
692
    end.
 
693
 
 
694
do_fallback_start(false, _IgnoreFallback) ->
 
695
    ok;
 
696
do_fallback_start(true, true) ->
 
697
    verbose("Ignoring fallback at startup, but leaving it active...~n", []),
 
698
    mnesia_lib:set(active_fallback, true),
 
699
    ok;
 
700
do_fallback_start(true, false) ->
 
701
    verbose("Starting from fallback...~n", []),
 
702
 
 
703
    Fname = fallback_bup(),
 
704
    Mod = mnesia_backup,
 
705
    Ets = ?ets_new_table(mnesia_local_tables, [set, public, {keypos, 2}]),
 
706
    case catch iterate(Mod, fun restore_tables/4, Fname, {start, Ets}) of
 
707
        {ok, Res} ->
 
708
            case Res of
 
709
                {local, _, LT} ->  %% Close the last file
 
710
                    (LT#local_tab.close)(LT);
 
711
                _ ->
 
712
                    ignore
 
713
            end,
 
714
            List = ?ets_match_object(Ets, '_'),
 
715
            Tabs = [L#local_tab.name || L <- List, L#local_tab.name /= schema],
 
716
            ?ets_delete_table(Ets),
 
717
            mnesia_lib:swap_tmp_files(Tabs),
 
718
            catch dets:close(schema),
 
719
            Tmp = mnesia_lib:tab2tmp(schema),
 
720
            Dat = mnesia_lib:tab2dat(schema),
 
721
            case file:rename(Tmp, Dat) of
 
722
                ok ->
 
723
                    file:delete(Fname),
 
724
                    ok;
 
725
                {error, Reason} ->
 
726
                    file:delete(Tmp),
 
727
                    {error, {"Cannot start from fallback. Rename error.", Reason}}
 
728
            end;
 
729
        {error, Reason} ->
 
730
            {error, {"Cannot start from fallback", Reason}};
 
731
        {'EXIT', Reason} ->
 
732
            {error, {"Cannot start from fallback", Reason}}
 
733
    end.
 
734
 
 
735
restore_tables(Recs, Header, Schema, {start, LocalTabs}) ->
 
736
    Dir = mnesia_lib:dir(),
 
737
    OldDir = filename:join([Dir, "OLD_DIR"]),
 
738
    mnesia_schema:purge_dir(OldDir, []),
 
739
    mnesia_schema:purge_dir(Dir, [fallback_name()]),
 
740
    init_dat_files(Schema, LocalTabs),
 
741
    State = {new, LocalTabs},
 
742
    restore_tables(Recs, Header, Schema, State);
 
743
restore_tables([Rec | Recs], Header, Schema, {new, LocalTabs}) ->
 
744
    Tab = element(1, Rec),
 
745
    case ?ets_lookup(LocalTabs, Tab) of
 
746
        [] ->
 
747
            State = {not_local, LocalTabs, Tab},
 
748
            restore_tables(Recs, Header, Schema, State);
 
749
        [L] when record(L, local_tab) ->
 
750
            (L#local_tab.open)(Tab, L),
 
751
            State = {local, LocalTabs, L},
 
752
            restore_tables([Rec | Recs], Header, Schema, State)
 
753
    end;
 
754
restore_tables([Rec | Recs], Header, Schema, S = {not_local, LocalTabs, PrevTab}) ->
 
755
    Tab = element(1, Rec),
 
756
    if
 
757
        Tab == PrevTab ->
 
758
            restore_tables(Recs, Header, Schema, S);
 
759
        true ->
 
760
            State = {new, LocalTabs},
 
761
            restore_tables([Rec | Recs], Header, Schema, State)
 
762
    end;
 
763
restore_tables([Rec | Recs], Header, Schema, State = {local, LocalTabs, L}) ->
 
764
    Tab = element(1, Rec),
 
765
    if
 
766
        Tab == L#local_tab.name ->
 
767
            Key = element(2, Rec),
 
768
            (L#local_tab.add)(Tab, Key, Rec, L),
 
769
            restore_tables(Recs, Header, Schema, State);
 
770
        true ->
 
771
            (L#local_tab.close)(L),
 
772
            NState = {new, LocalTabs},
 
773
            restore_tables([Rec | Recs], Header, Schema, NState)
 
774
    end;
 
775
restore_tables([], _Header, _Schema, State) ->
 
776
    State.
 
777
 
 
778
%% Creates all neccessary dat files and inserts
 
779
%% the table definitions in the schema table
 
780
%%
 
781
%% Returns a list of local_tab tuples for all local tables
 
782
init_dat_files(Schema, LocalTabs) ->
 
783
    Fname = mnesia_lib:tab2tmp(schema),
 
784
    Args = [{file, Fname}, {keypos, 2}, {type, set}],
 
785
    case dets:open_file(schema, Args) of % Assume schema lock
 
786
        {ok, _} ->
 
787
            create_dat_files(Schema, LocalTabs),
 
788
            dets:close(schema),
 
789
            LocalTab = #local_tab{name = schema,
 
790
                                  storage_type = disc_copies,
 
791
                                  dets_args = Args,
 
792
                                  open   = fun open_media/2,
 
793
                                  close  = fun close_media/1,
 
794
                                  add    = fun add_to_media/4,
 
795
                                  record_name = schema},
 
796
            ?ets_insert(LocalTabs, LocalTab);
 
797
        {error, Reason} ->
 
798
            throw({error, {"Cannot open file", schema, Args, Reason}})
 
799
    end.
 
800
 
 
801
create_dat_files([{schema, schema, TabDef} | Tail], LocalTabs) ->
 
802
    ok = dets:insert(schema, {schema, schema, TabDef}),
 
803
    create_dat_files(Tail, LocalTabs);
 
804
create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) ->
 
805
    Cs =  mnesia_schema:list2cs(TabDef),
 
806
    ok = dets:insert(schema, {schema, Tab, TabDef}),
 
807
    RecName = Cs#cstruct.record_name,
 
808
    case mnesia_lib:cs_to_storage_type(node(), Cs) of
 
809
        unknown ->
 
810
            cleanup_dat_file(Tab),
 
811
            create_dat_files(Tail, LocalTabs);
 
812
        disc_only_copies ->
 
813
            Fname = mnesia_lib:tab2tmp(Tab),
 
814
            Args = [{file, Fname}, {keypos, 2},
 
815
                    {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)}],
 
816
            case mnesia_lib:dets_sync_open(Tab, Args) of
 
817
                {ok, _} ->
 
818
                    mnesia_lib:dets_sync_close(Tab),
 
819
                    LocalTab = #local_tab{name = Tab,
 
820
                                          storage_type = disc_only_copies,
 
821
                                          dets_args = Args,
 
822
                                          open   = fun open_media/2,
 
823
                                          close  = fun close_media/1,
 
824
                                          add    = fun add_to_media/4,
 
825
                                          record_name = RecName},
 
826
                    ?ets_insert(LocalTabs, LocalTab),
 
827
                    create_dat_files(Tail, LocalTabs);
 
828
                {error, Reason} ->
 
829
                    throw({error, {"Cannot open file", Tab, Args, Reason}})
 
830
            end;
 
831
        ram_copies ->
 
832
            %% Create .DCD if needed in open_media in case any ram_copies
 
833
            %% are backed up.
 
834
            LocalTab = #local_tab{name = Tab,
 
835
                                  storage_type = ram_copies,
 
836
                                  dets_args = ignore,
 
837
                                  open   = fun open_media/2,
 
838
                                  close  = fun close_media/1,
 
839
                                  add    = fun add_to_media/4,
 
840
                                  record_name = RecName},
 
841
            ?ets_insert(LocalTabs, LocalTab),
 
842
            create_dat_files(Tail, LocalTabs);
 
843
        Storage ->
 
844
            %% Create DCD
 
845
            Fname = mnesia_lib:tab2dcd(Tab),
 
846
            file:delete(Fname),
 
847
            Log = mnesia_log:open_log(fallback_tab, mnesia_log:dcd_log_header(),
 
848
                                      Fname, false),
 
849
            LocalTab = #local_tab{name = Tab,
 
850
                                  storage_type = Storage,
 
851
                                  dets_args = ignore,
 
852
                                  open   = fun open_media/2,
 
853
                                  close  = fun close_media/1,
 
854
                                  add    = fun add_to_media/4,
 
855
                                  record_name = RecName},
 
856
            mnesia_log:close_log(Log),
 
857
            ?ets_insert(LocalTabs, LocalTab),
 
858
            create_dat_files(Tail, LocalTabs)
 
859
    end;
 
860
create_dat_files([{schema, Tab} | Tail], LocalTabs) ->
 
861
    cleanup_dat_file(Tab),
 
862
    create_dat_files(Tail, LocalTabs);
 
863
create_dat_files([], _LocalTabs) ->
 
864
    ok.
 
865
 
 
866
cleanup_dat_file(Tab) ->
 
867
    ok = dets:delete(schema, {schema, Tab}),
 
868
    mnesia_lib:cleanup_tmp_files([Tab]).
 
869
 
 
870
open_media(Tab, LT) ->
 
871
    case LT#local_tab.storage_type of
 
872
        disc_only_copies ->
 
873
            Args = LT#local_tab.dets_args,
 
874
            case mnesia_lib:dets_sync_open(Tab, Args) of
 
875
                {ok, _} -> ok;
 
876
                {error, Reason} ->
 
877
                    throw({error, {"Cannot open file", Tab, Args, Reason}})
 
878
            end;
 
879
        ram_copies ->
 
880
            %% Create .DCD as ram_copies backed up.
 
881
            FnameDCD = mnesia_lib:tab2dcd(Tab),
 
882
            file:delete(FnameDCD),
 
883
            Log = mnesia_log:open_log(fallback_tab,
 
884
                                      mnesia_log:dcd_log_header(),
 
885
                                      FnameDCD, false),
 
886
            mnesia_log:close_log(Log),
 
887
 
 
888
            %% Create .DCL
 
889
            Fname = mnesia_lib:tab2dcl(Tab),
 
890
            file:delete(Fname),
 
891
            mnesia_log:open_log({?MODULE,Tab},
 
892
                                mnesia_log:dcl_log_header(),
 
893
                                Fname, false, false,
 
894
                                read_write);
 
895
        _ ->
 
896
            Fname = mnesia_lib:tab2dcl(Tab),
 
897
            file:delete(Fname),
 
898
            mnesia_log:open_log({?MODULE,Tab},
 
899
                                mnesia_log:dcl_log_header(),
 
900
                                Fname, false, false,
 
901
                                read_write)
 
902
    end.
 
903
close_media(L) ->
 
904
    Tab = L#local_tab.name,
 
905
    case L#local_tab.storage_type of
 
906
        disc_only_copies ->
 
907
            mnesia_lib:dets_sync_close(Tab);
 
908
        _ ->
 
909
            mnesia_log:close_log({?MODULE,Tab})
 
910
    end.
 
911
 
 
912
add_to_media(Tab, Key, Rec, L) ->
 
913
    RecName = L#local_tab.record_name,
 
914
    case L#local_tab.storage_type of
 
915
        disc_only_copies ->
 
916
            case Rec of
 
917
                {Tab, Key} ->
 
918
                    ok = dets:delete(Tab, Key);
 
919
                (Rec) when Tab == RecName ->
 
920
                    ok = dets:insert(Tab, Rec);
 
921
                (Rec) ->
 
922
                    Rec2 = setelement(1, Rec, RecName),
 
923
                    ok = dets:insert(Tab, Rec2)
 
924
            end;
 
925
        _ ->
 
926
            Log = {?MODULE, Tab},
 
927
            case Rec of
 
928
                {Tab, Key} ->
 
929
                    mnesia_log:append(Log, {{Tab, Key}, {Tab, Key}, delete});
 
930
                (Rec) when Tab == RecName ->
 
931
                    mnesia_log:append(Log, {{Tab, Key}, Rec, write});
 
932
                (Rec) ->
 
933
                    Rec2 = setelement(1, Rec, RecName),
 
934
                    mnesia_log:append(Log, {{Tab, Key}, Rec2, write})
 
935
            end
 
936
    end.
 
937
 
 
938
uninstall_fallback() ->
 
939
    uninstall_fallback([{scope, global}]).
 
940
 
 
941
uninstall_fallback(Args) ->
 
942
    case check_fallback_args(Args, #fallback_args{}) of
 
943
        {ok, FA} ->
 
944
            do_uninstall_fallback(FA);
 
945
        {error, Reason} ->
 
946
            {error, Reason}
 
947
    end.
 
948
 
 
949
do_uninstall_fallback(FA) ->
 
950
    %% Ensure that we access the intended Mnesia
 
951
    %% directory. This function may not be called
 
952
    %% during startup since it will cause the
 
953
    %% application_controller to get into deadlock
 
954
    case mnesia_lib:ensure_loaded(?APPLICATION) of
 
955
        ok ->
 
956
            Pid = spawn_link(?MODULE, uninstall_fallback_master, [self(), FA]),
 
957
            receive
 
958
                {'EXIT', Pid, Reason} -> % if appl has trapped exit
 
959
                    {error, {'EXIT', Reason}};
 
960
                {Pid, Res} ->
 
961
                    Res
 
962
            end;
 
963
        {error, Reason} ->
 
964
            {error, Reason}
 
965
    end.
 
966
 
 
967
uninstall_fallback_master(ClientPid, FA) ->
 
968
    process_flag(trap_exit, true),
 
969
 
 
970
    FA2 = check_fallback_dir(ClientPid, FA), % May exit
 
971
    Bup = FA2#fallback_args.fallback_bup,
 
972
    case fallback_to_schema(Bup) of
 
973
        {ok, fallback, List} ->
 
974
            Cs = mnesia_schema:list2cs(List),
 
975
            case catch get_fallback_nodes(FA, Cs#cstruct.disc_copies) of
 
976
                Ns when list(Ns) ->
 
977
                    do_uninstall(ClientPid, Ns, FA);
 
978
                {error, Reason} ->
 
979
                    local_fallback_error(ClientPid, Reason)
 
980
            end;
 
981
        {error, Reason} ->
 
982
            local_fallback_error(ClientPid, Reason)
 
983
    end.
 
984
 
 
985
do_uninstall(ClientPid, Ns, FA) ->
 
986
    Args = [self(), FA],
 
987
    global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity),
 
988
    Pids = [spawn_link(N, ?MODULE, local_uninstall_fallback, Args) || N <- Ns],
 
989
    Res = do_uninstall(ClientPid, Pids, [], [], ok),
 
990
    global:del_lock({{mnesia_table_lock, schema}, self()}, Ns),
 
991
    ClientPid ! {self(), Res},
 
992
    unlink(ClientPid),
 
993
    exit(shutdown).
 
994
 
 
995
do_uninstall(ClientPid, [Pid | Pids], GoodPids, BadNodes, Res) ->
 
996
    receive
 
997
        %% {'EXIT', ClientPid, _} ->
 
998
        %% client_exit;
 
999
        {'EXIT', Pid, Reason} ->
 
1000
            BadNode = node(Pid),
 
1001
            BadRes = {error, {"Uninstall fallback", BadNode, Reason}},
 
1002
            do_uninstall(ClientPid, Pids, GoodPids, [BadNode | BadNodes], BadRes);
 
1003
        {Pid, {error, Reason}} ->
 
1004
            BadNode = node(Pid),
 
1005
            BadRes = {error, {"Uninstall fallback", BadNode, Reason}},
 
1006
            do_uninstall(ClientPid, Pids, GoodPids, [BadNode | BadNodes], BadRes);
 
1007
        {Pid, started} ->
 
1008
            do_uninstall(ClientPid, Pids, [Pid | GoodPids], BadNodes, Res)
 
1009
    end;
 
1010
do_uninstall(ClientPid, [], GoodPids, [], ok) ->
 
1011
    lists:foreach(fun(Pid) -> Pid ! {self(), do_uninstall} end, GoodPids),
 
1012
    rec_uninstall(ClientPid, GoodPids, ok);
 
1013
do_uninstall(_ClientPid, [], GoodPids, BadNodes, BadRes) ->
 
1014
    lists:foreach(fun(Pid) -> exit(Pid, shutdown) end, GoodPids),
 
1015
    {error, {node_not_running, BadNodes, BadRes}}.
 
1016
 
 
1017
local_uninstall_fallback(Master, FA) ->
 
1018
    %% Don't trap exit
 
1019
 
 
1020
    register(mnesia_fallback, self()),        % May exit
 
1021
    FA2 = check_fallback_dir(Master, FA), % May exit
 
1022
    Master ! {self(), started},
 
1023
 
 
1024
    receive
 
1025
        {Master, do_uninstall} ->
 
1026
            ?eval_debug_fun({?MODULE, uninstall_fallback2, pre_delete}, []),
 
1027
            catch mnesia_lib:set(active_fallback, false),
 
1028
            Tmp = FA2#fallback_args.fallback_tmp,
 
1029
            Bup = FA2#fallback_args.fallback_bup,
 
1030
            file:delete(Tmp),
 
1031
            Res =
 
1032
                case fallback_exists(Bup) of
 
1033
                    true -> file:delete(Bup);
 
1034
                    false -> ok
 
1035
                end,
 
1036
            ?eval_debug_fun({?MODULE, uninstall_fallback2, post_delete}, []),
 
1037
            Master ! {self(), Res},
 
1038
            unlink(Master),
 
1039
            exit(normal)
 
1040
    end.
 
1041
 
 
1042
rec_uninstall(ClientPid, [Pid | Pids], AccRes) ->
 
1043
    receive
 
1044
        %% {'EXIT', ClientPid, _} ->
 
1045
        %% exit(shutdown);
 
1046
        {'EXIT', Pid, R} ->
 
1047
            Reason = {node_not_running, {node(Pid), R}},
 
1048
            rec_uninstall(ClientPid, Pids, {error, Reason});
 
1049
        {Pid, ok} ->
 
1050
            rec_uninstall(ClientPid, Pids, AccRes);
 
1051
        {Pid, BadRes} ->
 
1052
            rec_uninstall(ClientPid, Pids, BadRes)
 
1053
    end;
 
1054
rec_uninstall(ClientPid, [], Res) ->
 
1055
    ClientPid ! {self(), Res},
 
1056
    unlink(ClientPid),
 
1057
    exit(normal).
 
1058
 
 
1059
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1060
%% Backup traversal
 
1061
 
 
1062
%% Iterate over a backup and produce a new backup.
 
1063
%% Fun(BackupItem, Acc) is applied for each BackupItem.
 
1064
%%
 
1065
%% Valid BackupItems are:
 
1066
%%
 
1067
%%   {schema, Tab}              Table to be deleted
 
1068
%%   {schema, Tab, CreateList}  Table to be created, CreateList may be empty
 
1069
%%   {schema, db_nodes, DbNodes}List of nodes, defaults to [node()] OLD
 
1070
%%   {schema, version, Version} Schema version                      OLD
 
1071
%%   {schema, cookie, Cookie}   Unique schema cookie                OLD
 
1072
%%   {Tab, Key}                 Oid for record to be deleted
 
1073
%%   Record                     Record to be inserted.
 
1074
%%
 
1075
%% The Fun must return a tuple {BackupItems, NewAcc}
 
1076
%% where BackupItems is a list of valid BackupItems and
 
1077
%% NewAcc is a new accumulator value. Once BackupItems
 
1078
%% that not are schema  related has been returned, no more schema
 
1079
%% items may be  returned. The schema related items must always be
 
1080
%% first in the backup.
 
1081
%%
 
1082
%% If TargetMod == read_only, no new backup will be created.
 
1083
%%
 
1084
%% Opening of the source media will be performed by
 
1085
%% to SourceMod:open_read(Source)
 
1086
%%
 
1087
%% Opening of the target media will be performed by
 
1088
%% to TargetMod:open_write(Target)
 
1089
traverse_backup(Source, Target, Fun, Acc) ->
 
1090
    Mod = mnesia_monitor:get_env(backup_module),
 
1091
    traverse_backup(Source, Mod, Target, Mod, Fun, Acc).
 
1092
 
 
1093
traverse_backup(Source, SourceMod, Target, TargetMod, Fun, Acc) ->
 
1094
    Args = [self(), Source, SourceMod, Target, TargetMod, Fun, Acc],
 
1095
    Pid = spawn_link(?MODULE, do_traverse_backup, Args),
 
1096
    receive
 
1097
        {'EXIT', Pid, Reason} ->
 
1098
            {error, {"Backup traversal crashed", Reason}};
 
1099
        {iter_done, Pid, Res} ->
 
1100
            Res
 
1101
    end.
 
1102
 
 
1103
do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) ->
 
1104
    process_flag(trap_exit, true),
 
1105
    Iter =
 
1106
        if
 
1107
            TargetMod /= read_only ->
 
1108
                case catch do_apply(TargetMod, open_write, [Target], Target) of
 
1109
                    {error, Error} ->
 
1110
                        unlink(ClientPid),
 
1111
                        ClientPid ! {iter_done, self(), {error, Error}},
 
1112
                        exit(Error);
 
1113
                    Else -> Else
 
1114
                end;
 
1115
            true ->
 
1116
                ignore
 
1117
        end,
 
1118
    A = {start, Fun, Acc, TargetMod, Iter},
 
1119
    Res =
 
1120
        case iterate(SourceMod, fun trav_apply/4, Source, A) of
 
1121
            {ok, {iter, _, Acc2, _, Iter2}} when TargetMod /= read_only ->
 
1122
                case catch do_apply(TargetMod, commit_write, [Iter2], Iter2) of
 
1123
                    {error, Reason} ->
 
1124
                        {error, Reason};
 
1125
                    _ ->
 
1126
                        {ok, Acc2}
 
1127
                end;
 
1128
            {ok, {iter, _, Acc2, _, _}} ->
 
1129
                {ok, Acc2};
 
1130
            {error, Reason} when TargetMod /= read_only->
 
1131
                catch do_apply(TargetMod, abort_write, [Iter], Iter),
 
1132
                {error, {"Backup traversal failed", Reason}};
 
1133
            {error, Reason} ->
 
1134
                {error, {"Backup traversal failed", Reason}}
 
1135
        end,
 
1136
    unlink(ClientPid),
 
1137
    ClientPid ! {iter_done, self(), Res}.
 
1138
 
 
1139
trav_apply(Recs, _Header, _Schema, {iter, Fun, Acc, Mod, Iter}) ->
 
1140
    {NewRecs, Acc2} = filter_foldl(Fun, Acc, Recs),
 
1141
    if
 
1142
        Mod /= read_only, NewRecs /= [] ->
 
1143
            Iter2 = do_apply(Mod, write, [Iter, NewRecs], Iter),
 
1144
            {iter, Fun, Acc2, Mod, Iter2};
 
1145
        true ->
 
1146
            {iter, Fun, Acc2, Mod, Iter}
 
1147
    end;
 
1148
trav_apply(Recs, Header, Schema, {start, Fun, Acc, Mod, Iter}) ->
 
1149
    Iter2 =
 
1150
        if
 
1151
            Mod /= read_only ->
 
1152
                do_apply(Mod, write, [Iter, [Header]], Iter);
 
1153
            true ->
 
1154
                Iter
 
1155
        end,
 
1156
    TravAcc = trav_apply(Schema, Header, Schema, {iter, Fun, Acc, Mod, Iter2}),
 
1157
    trav_apply(Recs, Header, Schema, TravAcc).
 
1158
 
 
1159
filter_foldl(Fun, Acc, [Head|Tail]) ->
 
1160
    case Fun(Head, Acc) of
 
1161
        {HeadItems, HeadAcc} when list(HeadItems) ->
 
1162
            {TailItems, TailAcc} = filter_foldl(Fun, HeadAcc, Tail),
 
1163
            {HeadItems ++ TailItems, TailAcc};
 
1164
        Other ->
 
1165
            throw({error, {"Fun must return a list", Other}})
 
1166
    end;
 
1167
filter_foldl(_Fun, Acc, []) ->
 
1168
    {[], Acc}.