1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2019. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(disk_log_SUITE).
21
22%%-define(debug, true).
23
24-ifdef(debug).
25-define(format(S, A), io:format(S, A)).
26-define(line, put(line, ?LINE), ).
27-define(privdir(_), "./disk_log_SUITE_priv").
28-define(datadir(_), "./disk_log_SUITE_data").
29-define(config(X,Y), foo).
30-define(t,test_server).
31-else.
32-include_lib("common_test/include/ct.hrl").
33-define(format(S, A), ok).
34-define(privdir(Conf), proplists:get_value(priv_dir, Conf)).
35-define(datadir(Conf), proplists:get_value(data_dir, Conf)).
36-endif.
37
38-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
39	 init_per_group/2,end_per_group/2,
40
41	 halt_int_inf/1,
42	 halt_int_sz_1/1, halt_int_sz_2/1,
43
44	 halt_int_ro/1, halt_ext_ro/1, wrap_int_ro/1,
45	 wrap_ext_ro/1, halt_trunc/1, halt_misc/1, halt_ro_alog/1,
46	 halt_ro_balog/1, halt_ro_crash/1,
47
48	 wrap_int_1/1, wrap_int_2/1, inc_wrap_file/1,
49
50	 halt_ext_inf/1,
51
52	 halt_ext_sz_1/1, halt_ext_sz_2/1,
53
54	 wrap_ext_1/1, wrap_ext_2/1,
55
56	 head_func/1, plain_head/1, one_header/1,
57
58	 wrap_notif/1, full_notif/1, trunc_notif/1, blocked_notif/1,
59
60	 new_idx_vsn/1,
61
62	 reopen/1,
63
64	 block_blocked/1, block_queue/1, block_queue2/1,
65
66	 unblock/1,
67
68	 open_overwrite/1, open_size/1, open_truncate/1, open_error/1,
69
70	 close_race/1, close_block/1, close_deadlock/1,
71
72	 error_repair/1, error_log/1, error_index/1,
73
74	 chunk/1,
75
76	 truncate/1,
77
78	 many_users/1,
79
80	 info_current/1,
81
82	 change_size_before/1, change_size_during/1,
83	 change_size_after/1, default_size/1, change_size2/1,
84	 change_size_truncate/1,
85
86	 change_attribute/1,
87
88	 dist_open/1, dist_error_open/1, dist_notify/1,
89	 dist_terminate/1, dist_accessible/1, dist_deadlock/1,
90         dist_open2/1, other_groups/1,
91
92         otp_6278/1, otp_10131/1, otp_16768/1]).
93
94-export([head_fun/1, hf/0, lserv/1,
95	 measure/0, init_m/1, xx/0, head_exit/0, slow_header/1]).
96
97-export([init_per_testcase/2, end_per_testcase/2]).
98
99-export([try_unblock/1]).
100
101-export([client/4]).
102
103%% error_logger
104-export([init/1,
105	 handle_event/2, handle_call/2, handle_info/2,
106	 terminate/2]).
107
108-include_lib("kernel/include/file.hrl").
109-include_lib("kernel/src/disk_log.hrl").
110
111%% TODO (old):
112%%   - global logs
113%%   - badarg
114%%   - force file:write fail (how?)
115%%   - kill logging proc while he is logging
116%%   - kill logging node while he is logging
117%%   - test chunk_step
118
119%% These are all tests, the list to be returned by all().
120-define(ALL_TESTS,
121	[halt_int, wrap_int, halt_ext, wrap_ext, read_mode, head,
122	 notif, new_idx_vsn, reopen, block, unblock, open, close,
123	 error, chunk, truncate, many_users, info, change_size,
124	 change_attribute, distribution, otp_6278, otp_10131, otp_16768]).
125
126%% These test cases should be skipped if the VxWorks card is
127%% configured without NFS cache.
128-define(SKIP_NO_CACHE,[distribution]).
129%% These tests should be skipped if the VxWorks card is configured *with*
130%% nfs cache.
131-define(SKIP_LARGE_CACHE,[inc_wrap_file, halt_ext, wrap_ext, read_mode,
132			  head, wrap_notif, open_size, error_log,
133                          error_index, chunk,
134			  change_size_before, change_size_during,
135			  change_size_after, default_size]).
136
137
138suite() ->
139    [{ct_hooks,[ts_install_cth]},
140     {timetrap,{minutes,2}}].
141
142all() ->
143    [{group, halt_int}, {group, wrap_int},
144     {group, halt_ext}, {group, wrap_ext},
145     {group, read_mode}, {group, head}, {group, notif},
146     new_idx_vsn, reopen, {group, block}, unblock,
147     {group, open}, {group, close}, {group, error}, chunk,
148     truncate, many_users, {group, info},
149     {group, change_size}, change_attribute,
150     {group, distribution}, otp_6278, otp_10131, otp_16768].
151
152groups() ->
153    [{halt_int, [], [halt_int_inf, {group, halt_int_sz}]},
154     {halt_int_sz, [], [halt_int_sz_1, halt_int_sz_2]},
155     {read_mode, [],
156      [halt_int_ro, halt_ext_ro, wrap_int_ro, wrap_ext_ro,
157       halt_trunc, halt_misc, halt_ro_alog, halt_ro_balog,
158       halt_ro_crash]},
159     {wrap_int, [], [wrap_int_1, wrap_int_2, inc_wrap_file]},
160     {halt_ext, [], [halt_ext_inf, {group, halt_ext_sz}]},
161     {halt_ext_sz, [], [halt_ext_sz_1, halt_ext_sz_2]},
162     {wrap_ext, [], [wrap_ext_1, wrap_ext_2]},
163     {head, [], [head_func, plain_head, one_header]},
164     {notif, [],
165      [wrap_notif, full_notif, trunc_notif, blocked_notif]},
166     {block, [], [block_blocked, block_queue, block_queue2]},
167     {open, [],
168      [open_overwrite, open_size, open_truncate, open_error]},
169     {close, [], [close_race, close_block, close_deadlock]},
170     {error, [], [error_repair, error_log, error_index]},
171     {info, [], [info_current]},
172     {change_size, [],
173      [change_size_before, change_size_during,
174       change_size_after, default_size, change_size2,
175       change_size_truncate]},
176     {distribution, [],
177      [dist_open, dist_error_open, dist_notify,
178       dist_terminate, dist_accessible, dist_deadlock,
179       dist_open2, other_groups]}].
180
181init_per_suite(Config) ->
182    Config.
183
184end_per_suite(_Config) ->
185    ok.
186
187init_per_group(_GroupName, Config) ->
188    Config.
189
190end_per_group(_GroupName, Config) ->
191    Config.
192
193
194
195init_per_testcase(_Case, Config) ->
196    Config.
197
198end_per_testcase(_Case, _Config) ->
199    ok.
200
201
202%% Test simple halt disk log, size infinity.
203halt_int_inf(Conf) when is_list(Conf) ->
204    Dir = ?privdir(Conf),
205    ok = disk_log:start(),
206    File = filename:join(Dir, "a.LOG"),
207    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
208			     {format,internal},
209			     {file, File}]),
210    simple_log(a),
211    ok = disk_log:close(a),
212    ok = file:delete(File).
213
214
215%% Test simple halt disk log, size defined.
216halt_int_sz_1(Conf) when is_list(Conf) ->
217    Dir = ?privdir(Conf),
218    File = filename:join(Dir, "a.LOG"),
219    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,18000},
220			     {format,internal},
221			     {file, File}]),
222    simple_log(a),
223    ok = disk_log:truncate(a),
224    [] = get_all_terms(a),
225    T1 = mk_bytes(10000),
226    T2 = mk_bytes(5000),
227    ok = disk_log:log(a, T1),
228    case get_all_terms(a) of
229	[T1] ->
230	    ok;
231	E1 ->
232	    test_server_fail({bad_terms, E1, [T1]})
233    end,
234    ok = disk_log:log(a, T2),
235    {error, {full, a}} = disk_log:log(a, T1),
236    ok = disk_log:alog(a, T1),
237    case get_all_terms(a) of
238	[T1, T2] ->
239	    ok;
240	E2 ->
241	    test_server_fail({bad_terms, E2, [T1, T2]})
242    end,
243    ok = disk_log:close(a),
244    ok = file:delete(File).
245
246%% Test simple halt disk log, size ~8192.
247halt_int_sz_2(Conf) when is_list(Conf) ->
248    Dir = ?privdir(Conf),
249    File1 = filename:join(Dir, "a.LOG"),
250    File2 = filename:join(Dir, "b.LOG"),
251    File3 = filename:join(Dir, "c.LOG"),
252    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,8191},
253			     {format,internal},
254			     {file, File1}]),
255    {ok, b} = disk_log:open([{name,b}, {type,halt}, {size,8192},
256			     {format,internal},
257			     {file, File2}]),
258    {ok, c} = disk_log:open([{name,c}, {type,halt}, {size,8193},
259			     {format,internal},
260			     {file, File3}]),
261    T1 = mk_bytes(8191-16), % 16 is size of header + magics for 1 item
262    T2 = mk_bytes(8192-16),
263    T3 = mk_bytes(8193-16),
264    ok = disk_log:log(a, T1),
265    ok = disk_log:log(b, T2),
266    ok = disk_log:log(c, T3),
267    case get_all_terms(a) of
268	[T1] ->
269	    ok;
270	E1 ->
271	    test_server_fail({bad_terms, E1, [T1]})
272    end,
273    case get_all_terms(b) of
274	[T2] ->
275	    ok;
276	E2 ->
277	    test_server_fail({bad_terms, E2, [T2]})
278    end,
279    case get_all_terms(c) of
280	[T3] ->
281	    ok;
282	E3 ->
283	    test_server_fail({bad_terms, E3, [T3]})
284    end,
285    ok = disk_log:truncate(a),
286    ok = disk_log:truncate(b),
287    {error, {full, a}} = disk_log:log(a, T2),
288    {error, {full, b}} = disk_log:log(b, T3),
289    [] = get_all_terms(a),
290    [] = get_all_terms(b),
291    ok = disk_log:close(a),
292    ok = disk_log:close(b),
293    ok = disk_log:close(c),
294    ok = file:delete(File1),
295    ok = file:delete(File2),
296    ok = file:delete(File3),
297    ok.
298
299
300%% Test simple halt disk log, read only, internal.
301halt_int_ro(Conf) when is_list(Conf) ->
302    Dir = ?privdir(Conf),
303    File = filename:join(Dir, "a.LOG"),
304
305    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
306			     {format,internal}, {file, File}]),
307    simple_log(a),
308    ok = disk_log:close(a),
309
310    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
311			     {format,internal}, {file, File},
312			     {mode,read_only}]),
313    T1 = "not allowed to write",
314    {error, {read_only_mode, a}} = disk_log:log(a, T1),
315    ok = disk_log:close(a),
316    ok = file:delete(File).
317
318%% Test simple halt disk log, read only, external.
319halt_ext_ro(Conf) when is_list(Conf) ->
320    Dir = ?privdir(Conf),
321    File = filename:join(Dir, "a.LOG"),
322    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
323			     {format,external}, {file, File}]),
324    xsimple_log(File, a),
325    ok = disk_log:close(a),
326    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
327			     {format,external}, {file, File},
328			     {mode,read_only}]),
329    T1 = "not allowed to write",
330    {error, {read_only_mode, a}}  = disk_log:blog(a, T1),
331    ok = disk_log:close(a),
332    ok = file:delete(File).
333
334%% Test simple wrap disk log, read only, internal.
335wrap_int_ro(Conf) when is_list(Conf) ->
336    Dir = ?privdir(Conf),
337    File = filename:join(Dir, "a.LOG"),
338    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
339			     {format,internal}, {file, File}]),
340    simple_log(a),
341    ok = disk_log:close(a),
342    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
343			     {format,internal}, {file, File}, {mode,read_only}]),
344    T1 = "not allowed to write",
345    {error, {read_only_mode, a}} = disk_log:log(a, T1),
346    ok = disk_log:close(a),
347    del(File, 4).
348
349%% Test simple wrap disk log, read only, external.
350wrap_ext_ro(Conf) when is_list(Conf) ->
351    Dir = ?privdir(Conf),
352    File = filename:join(Dir, "a.LOG"),
353    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
354			     {format,external}, {file, File}]),
355    x2simple_log(File ++ ".1", a),
356    ok = disk_log:close(a),
357    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
358			     {format,external}, {file, File},
359			     {mode,read_only}]),
360    T1 = "not allowed to write",
361    {error, {read_only_mode, a}}  = disk_log:blog(a, T1),
362    {error, {read_only_mode, a}}  = disk_log:inc_wrap_file(a),
363    ok = disk_log:close(a),
364    del(File, 4).
365
366%% Test truncation of halt disk log.
367halt_trunc(Conf) when is_list(Conf) ->
368    Dir = ?privdir(Conf),
369    File = filename:join(Dir, "a.LOG"),
370    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
371			     {format,internal}, {file, File}]),
372    simple_log(a),
373    ok = disk_log:close(a),
374    {error,{badarg,repair_read_only}} =
375	disk_log:open([{name,a}, {type,halt}, {size,infinity},
376		       {repair, truncate}, {format,internal},
377		       {file, File}, {mode,read_only}]),
378    ok = file:delete(File).
379
380%% Test truncation of halt disk log.
381halt_misc(Conf) when is_list(Conf) ->
382    Dir = ?privdir(Conf),
383    File = filename:join(Dir, "a.LOG"),
384    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
385			     {format,internal}, {file, File}]),
386    simple_log(a),
387    ok = disk_log:close(a),
388    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
389			     {format,internal}, {file, File},
390			     {mode,read_only}]),
391    T1 = "not allowed to write",
392    {error, {read_only_mode, a}} = disk_log:log(a, T1),
393    {error, {read_only_mode, a}} = disk_log:sync(a),
394    {error, {read_only_mode, a}} = disk_log:reopen(a, "b.LOG"),
395    {error, {read_only_mode, a}} =
396        disk_log:change_header(a, {head,header}),
397    {error, {read_only_mode, a}} =
398        disk_log:change_size(a, infinity),
399    ok = disk_log:close(a),
400    ok = file:delete(File).
401
402%% Test truncation of halt disk log, read only.
403halt_ro_alog(Conf) when is_list(Conf) ->
404    Dir = ?privdir(Conf),
405    File = filename:join(Dir, "a.LOG"),
406    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
407			     {format,internal}, {file, File}]),
408    simple_log(a),
409    ok = disk_log:close(a),
410    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
411			     {notify,true}, {format,internal},
412			     {file, File}, {mode,read_only}]),
413    T1 = "not allowed to write",
414    ok = disk_log:alog(a, T1),
415    ok = halt_ro_alog_wait_notify(a, T1),
416    ok = disk_log:close(a),
417    ok = file:delete(File).
418
419halt_ro_alog_wait_notify(Log, T) ->
420    Term = term_to_binary(T),
421    receive
422	{disk_log, _, Log,{read_only, [Term]}} ->
423	    ok;
424	Other ->
425	    Other
426    after 5000 ->
427	    failed
428    end.
429
430%% Test truncation of halt disk log, read only.
431halt_ro_balog(Conf) when is_list(Conf) ->
432    Dir = ?privdir(Conf),
433    File = filename:join(Dir, "a.LOG"),
434    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
435			     {format,internal}, {file, File}]),
436    simple_log(a),
437    ok = disk_log:close(a),
438    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
439			     {notify,true}, {format,external},
440			     {file, File}, {mode,read_only}]),
441    T1 = "not allowed to write",
442    ok = disk_log:balog(a, T1),
443    ok = halt_ro_balog_wait_notify(a, T1),
444    ok = disk_log:close(a),
445    ok = file:delete(File).
446
447halt_ro_balog_wait_notify(Log, T) ->
448    Term = list_to_binary(T),
449    receive
450	{disk_log, _, Log,{read_only, [Term]}} ->
451	    ok;
452	Other ->
453	    Other
454    after 5000 ->
455	    failed
456    end.
457
458%% Test truncation of halt disk log, read only, repair.
459halt_ro_crash(Conf) when is_list(Conf) ->
460    Dir = ?privdir(Conf),
461    File = filename:join(Dir, "a.LOG"),
462
463    file:delete(File),
464    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
465			     {format,internal},{file, File}]),
466    simple_log(a),
467    ok = disk_log:close(a),
468    crash(File, 10),
469    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
470			     {notify,true}, {format,internal},
471			     {file, File}, {mode,read_only}]),
472
473    Error1 = {error, {read_only_mode, a}} = disk_log:truncate(a),
474    "The disk log" ++ _ = format_error(Error1),
475
476    %% crash/1 sets the length of the first item to something big (2.5 kb).
477    %% In R6B, binary_to_term accepts garbage at the end of the binary,
478    %% which means that the first item is recognized!
479    %% This is how it was before R6B:
480    %% {C1,T1,15} = disk_log:chunk(a,start),
481    %% {C2,T2} = disk_log:chunk(a,C1),
482    {C1,_OneItem,7478} = disk_log:chunk(a,start),
483    {C2, [], 7} = disk_log:chunk(a,C1),
484    eof = disk_log:chunk(a,C2),
485    ok = disk_log:close(a),
486    ok = file:delete(File).
487
488
489
490
491
492%% Test wrap disk log, internal.
493wrap_int_1(Conf) when is_list(Conf) ->
494    Dir = ?privdir(Conf),
495    File = filename:join(Dir, "a.LOG"),
496    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
497			     {format,internal},
498			     {file, File}]),
499    [_] =
500        lists:filter(fun(P) -> disk_log:pid2name(P) =/= undefined end,
501                     erlang:processes()),
502    simple_log(a),
503    ok = disk_log:close(a),
504    del(File, 4),
505    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
506			     {format,internal},
507			     {file, File}]),
508    [] = get_all_terms(a),
509    T1 = mk_bytes(10000), % file 2
510    T2 = mk_bytes(5000),  % file 3
511    T3 = mk_bytes(4000),  % file 4
512    T4 = mk_bytes(2000),  % file 4
513    T5 = mk_bytes(5000),  % file 1
514    T6 = mk_bytes(5000),  % file 2
515    ok = disk_log:log(a, T1),
516    ok = disk_log:log(a, T2),
517    ok = disk_log:log(a, T3),
518    ok = disk_log:log_terms(a, [T4, T5, T6]),
519    case get_all_terms(a) of
520	[T2,T3,T4,T5,T6] ->
521	    ok;
522	E1 ->
523	    test_server_fail({bad_terms, E1, [T2,T3,T4,T5,T6]})
524    end,
525    ok = disk_log:close(a),
526    del(File, 4).
527
528%% Test wrap disk log, internal.
529wrap_int_2(Conf) when is_list(Conf) ->
530    Dir = ?privdir(Conf),
531    File1 = filename:join(Dir, "a.LOG"),
532    File2 = filename:join(Dir, "b.LOG"),
533    File3 = filename:join(Dir, "c.LOG"),
534    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8191,3}},
535			     {format,internal},
536			     {file, File1}]),
537    {ok, b} = disk_log:open([{name,b}, {type,wrap}, {size,{8192,3}},
538			     {format,internal},
539			     {file, File2}]),
540    {ok, c} = disk_log:open([{name,c}, {type,wrap}, {size,{8193,3}},
541			     {format,internal},
542			     {file, File3}]),
543    T1 = mk_bytes(8191-16), % 16 is size of header + magics for 1 item
544    T2 = mk_bytes(8192-16),
545    T3 = mk_bytes(8193-16),
546    ok = disk_log:log(a, T1),
547    ok = disk_log:log(b, T2),
548    ok = disk_log:log(c, T3),
549    case get_all_terms(a) of
550	[T1] ->
551	    ok;
552	E1 ->
553	    test_server_fail({bad_terms, E1, [T1]})
554    end,
555    case get_all_terms(b) of
556	[T2] ->
557	    ok;
558	E2 ->
559	    test_server_fail({bad_terms, E2, [T2]})
560    end,
561    case get_all_terms(c) of
562	[T3] ->
563	    ok;
564	E3 ->
565	    test_server_fail({bad_terms, E3, [T3]})
566    end,
567    ok = disk_log:close(a),
568    ok = disk_log:close(b),
569    ok = disk_log:close(c),
570    del(File1, 3),
571    del(File2, 3),
572    del(File3, 3).
573
574%% Test disk log, force a change to next file.
575inc_wrap_file(Conf) when is_list(Conf) ->
576    Dir = ?privdir(Conf),
577    File1 = filename:join(Dir, "a.LOG"),
578    File2 = filename:join(Dir, "b.LOG"),
579    File3 = filename:join(Dir, "c.LOG"),
580
581    %% Test that halt logs gets an error message
582    {ok, a} = disk_log:open([{name, a}, {type, halt},
583			     {format, internal},
584			     {file, File1}]),
585    ok = disk_log:log(a, "message one"),
586    {error, {halt_log, a}} = disk_log:inc_wrap_file(a),
587
588    %% test an internally formatted wrap log file
589    {ok, b} = disk_log:open([{name, b}, {type, wrap}, {size, {100,3}},
590			     {format, internal}, {head, 'thisisahead'},
591			     {file, File2}]),
592    ok = disk_log:log(b, "message one"),
593    ok = disk_log:inc_wrap_file(b),
594    ok = disk_log:log(b, "message two"),
595    ok = disk_log:inc_wrap_file(b),
596    ok = disk_log:log(b, "message three"),
597    ok = disk_log:inc_wrap_file(b),
598    ok = disk_log:log(b, "message four"),
599    T1 = get_all_terms(b),
600    ['thisisahead', "message two",
601     'thisisahead', "message three",
602     'thisisahead', "message four"] = T1,
603
604    %% test an externally formatted wrap log file
605    {ok, c} = disk_log:open([{name, c}, {type, wrap}, {size, {100,3}},
606			     {format,external}, {head,"this is a head "},
607			     {file, File3}]),
608    ok = disk_log:blog(c, "message one"),
609    ok = disk_log:inc_wrap_file(c),
610    ok = disk_log:blog(c, "message two"),
611    ok = disk_log:inc_wrap_file(c),
612    ok = disk_log:blog(c, "message three"),
613    ok = disk_log:inc_wrap_file(c),
614    ok = disk_log:blog(c, "message four"),
615    ok = disk_log:sync(c),
616    {ok, Fd31} = file:open(File3 ++ ".1", [read]),
617    {ok,"this is a head message four"} = file:read(Fd31, 200),
618    {ok, Fd32} = file:open(File3 ++ ".2", [read]),
619    {ok,"this is a head message two"} = file:read(Fd32, 200),
620    {ok, Fd33} = file:open(File3 ++ ".3", [read]),
621    {ok,"this is a head message three"} = file:read(Fd33, 200),
622    ok = file:close(Fd31),
623    ok = file:close(Fd32),
624    ok = file:close(Fd33),
625
626    ok = disk_log:close(a),
627    ok = disk_log:close(b),
628    ok = disk_log:close(c),
629    ok = file:delete(File1),
630    del(File2, 3),
631    del(File3, 3).
632
633
634
635
636%% Test halt disk log, external, infinity.
637halt_ext_inf(Conf) when is_list(Conf) ->
638    Dir = ?privdir(Conf),
639    File = filename:join(Dir, "a.LOG"),
640    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
641			     {format,external},
642			     {file, File}]),
643    xsimple_log(File, a),
644    ok = disk_log:close(a),
645    ok = file:delete(File).
646
647
648%% Test halt disk log, external, size defined.
649halt_ext_sz_1(Conf) when is_list(Conf) ->
650    Dir = ?privdir(Conf),
651    File = filename:join(Dir, "a.LOG"),
652    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,18000},
653			     {format,external},
654			     {file, File}]),
655    xsimple_log(File, a),
656    ok = disk_log:truncate(a),
657    [] = get_list(File, a),
658    {B1, T1} = x_mk_bytes(10000),
659    {B2, T2} = x_mk_bytes(5000),
660    {B3, T3} = x_mk_bytes(1000),
661    ok = disk_log:blog(a, B1),
662    case get_list(File, a) of
663	T1 ->
664	    ok;
665	E1 ->
666	    test_server_fail({bad_terms, E1, T1})
667    end,
668    ok = disk_log:blog(a, B2),
669    {error, {full, a}} = disk_log:blog_terms(a, [B3,B3,B1]),
670    ok = disk_log:balog(a, B1),
671    Tmp = T1 ++ T2 ++ T3 ++ T3,
672    case get_list(File, a) of
673	Tmp ->
674	    ok;
675	E2 ->
676	    test_server_fail({bad_terms, E2, Tmp})
677    end,
678    ok = disk_log:close(a),
679    ok = file:delete(File).
680
681%% Test halt disk log, external, size defined.
682halt_ext_sz_2(Conf) when is_list(Conf) ->
683    Dir = ?privdir(Conf),
684    File1 = filename:join(Dir, "a.LOG"),
685    File2 = filename:join(Dir, "b.LOG"),
686    File3 = filename:join(Dir, "c.LOG"),
687    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,8191},
688			     {format,external},
689			     {file, File1}]),
690    {ok, b} = disk_log:open([{name,b}, {type,halt}, {size,8192},
691			     {format,external},
692			     {file, File2}]),
693    {ok, c} = disk_log:open([{name,c}, {type,halt}, {size,8193},
694			     {format,external},
695			     {file, File3}]),
696    {B1, T1} = x_mk_bytes(8191),
697    {B2, T2} = x_mk_bytes(8192),
698    {B3, T3} = x_mk_bytes(8193),
699    ok = disk_log:blog(a, B1),
700    ok = disk_log:blog(b, B2),
701    ok = disk_log:blog(c, B3),
702    case get_list(File1, a) of
703	T1 ->
704	    ok;
705	E1 ->
706	    test_server_fail({bad_terms, E1, T1})
707    end,
708    case get_list(File2, b) of
709	T2 ->
710	    ok;
711	E2 ->
712	    test_server_fail({bad_terms, E2, T2})
713    end,
714    case get_list(File3, c) of
715	T3 ->
716	    ok;
717	E3 ->
718	    test_server_fail({bad_terms, E3, T3})
719    end,
720    ok = disk_log:truncate(a),
721    ok = disk_log:truncate(b),
722    {error, {full, a}} = disk_log:blog(a, B2),
723    Error1 = {error, {full, b}} = disk_log:blog(b, B3),
724    "The halt log" ++ _ = format_error(Error1),
725    true = info(b, full, false),
726    [] = get_list(File1, a),
727    [] = get_list(File2, b),
728    ok = disk_log:close(a),
729    ok = disk_log:close(b),
730    ok = disk_log:close(c),
731    ok = file:delete(File1),
732    ok = file:delete(File2),
733    ok = file:delete(File3),
734    ok.
735
736
737%% Test wrap disk log, external, size defined.
738wrap_ext_1(Conf) when is_list(Conf) ->
739    Dir = ?privdir(Conf),
740    File = filename:join(Dir, "a.LOG"),
741    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
742			     {format,external},
743			     {file, File}]),
744    x2simple_log(File ++ ".1", a),
745    ok = disk_log:close(a),
746    %%    del(File, 4),
747    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}},
748			     {format,external},
749			     {file, File}]),
750    {B1, _T1} = x_mk_bytes(10000), % file 2
751    {B2, T2} = x_mk_bytes(5000),  % file 3
752    {B3, T3} = x_mk_bytes(4000),  % file 4
753    {B4, T4} = x_mk_bytes(2000),  % file 4
754    {B5, T5} = x_mk_bytes(5000),  % file 1
755    {B6, T6} = x_mk_bytes(5000),  % file 2
756    ok = disk_log:blog(a, B1),
757    ok = disk_log:blog(a, B2),
758    ok = disk_log:blog(a, B3),
759    ok = disk_log:blog_terms(a, [B4, B5, B6]),
760    case get_list(File ++ ".3", a) of
761	T2 ->
762	    ok;
763	E2 ->
764	    test_server_fail({bad_terms, E2, T2})
765    end,
766    T34 = T3 ++ T4,
767    case get_list(File ++ ".4", a) of
768	T34 ->
769	    ok;
770	E34 ->
771	    test_server_fail({bad_terms, E34, T34})
772    end,
773    case get_list(File ++ ".1", a) of
774	T5 ->
775	    ok;
776	E5 ->
777	    test_server_fail({bad_terms, E5, T5})
778    end,
779    case get_list(File ++ ".2", a) of
780	T6 ->
781	    ok;
782	E6 ->
783	    test_server_fail({bad_terms, E6, T6})
784    end,
785    ok = disk_log:close(a),
786    del(File, 4).
787
788%% Test wrap disk log, external, size defined.
789wrap_ext_2(Conf) when is_list(Conf) ->
790    Dir = ?privdir(Conf),
791    File1 = filename:join(Dir, "a.LOG"),
792    File2 = filename:join(Dir, "b.LOG"),
793    File3 = filename:join(Dir, "c.LOG"),
794    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8191,3}},
795			     {format,external},
796			     {file, File1}]),
797    {ok, b} = disk_log:open([{name,b}, {type,wrap}, {size,{8192,3}},
798			     {format,external},
799			     {file, File2}]),
800    {ok, c} = disk_log:open([{name,c}, {type,wrap}, {size,{8193,3}},
801			     {format,external},
802			     {file, File3}]),
803    {B1, T1} = x_mk_bytes(8191),
804    {B2, T2} = x_mk_bytes(8192),
805    {B3, T3} = x_mk_bytes(8193),
806    ok = disk_log:blog(a, B1),
807    ok = disk_log:blog(b, B2),
808    ok = disk_log:blog(c, B3),
809    case get_list(File1 ++ ".1", a) of
810	T1 ->
811	    ok;
812	E1 ->
813	    test_server_fail({bad_terms, E1, T1})
814    end,
815    case get_list(File2 ++ ".1", b) of
816	T2 ->
817	    ok;
818	E2 ->
819	    test_server_fail({bad_terms, E2, T2})
820    end,
821    case get_list(File3 ++ ".1", c) of
822	T3 ->
823	    ok;
824	E3 ->
825	    test_server_fail({bad_terms, E3, T3})
826    end,
827    ok = disk_log:close(a),
828    ok = disk_log:close(b),
829    ok = disk_log:close(c),
830    del(File1, 3),
831    del(File2, 3),
832    del(File3, 3),
833    ok.
834
835simple_log(Log) ->
836    T1 = "hej",
837    T2 = hopp,
838    T3 = {tjena, 12},
839    T4 = mk_bytes(10000),
840    ok = disk_log:log(Log, T1),
841    ok = disk_log:log_terms(Log, [T2, T3]),
842    case get_all_terms(Log) of
843	[T1, T2, T3] ->
844	    ok;
845	E1 ->
846	    test_server_fail({bad_terms, E1, [T1, T2, T3]})
847    end,
848    ok = disk_log:log(a, T4),
849    case get_all_terms(Log) of
850	[T1, T2, T3, T4] ->
851	    ok;
852	E2 ->
853	    test_server_fail({bad_terms, E2, [T1, T2, T3, T4]})
854    end.
855
856xsimple_log(File, Log) ->
857    T1 = "hej",
858    T2 = list_to_binary("hopp"),
859    T3 = list_to_binary(["sena", list_to_binary("sejer")]),
860    T4 = list_to_binary(By = mk_bytes(10000)),
861    ok = disk_log:blog(Log, T1),
862    ok = disk_log:blog_terms(Log, [T2, T3]),
863    X = "hejhoppsenasejer",
864    X2 = get_list(File, Log),
865    case X2 of
866	X -> ok;
867	Z1 -> test_server_fail({bad_terms, Z1, X2})
868    end,
869    ok = disk_log:blog(Log, T4),
870    Tmp = get_list(File, Log),
871    case X ++ By of
872	Tmp -> ok;
873	Z2 -> test_server_fail({bad_terms, Z2, X ++ By})
874    end.
875
876x2simple_log(File, Log) ->
877    T1 = "hej",
878    T2 = list_to_binary("hopp"),
879    T3 = list_to_binary(["sena", list_to_binary("sejer")]),
880    T4 = list_to_binary(By = mk_bytes(1000)),
881    ok = disk_log:blog(Log, T1),
882    ok = disk_log:blog_terms(Log, [T2, T3]),
883    X = "hejhoppsenasejer",
884    X2 = get_list(File, Log),
885    case X2 of
886	X -> ok;
887	Z1 -> test_server_fail({bad_terms, Z1, X2})
888    end,
889    ok = disk_log:blog(Log, T4),
890    Tmp = get_list(File, Log),
891    case X ++ By of
892	Tmp -> ok;
893	Z2 -> test_server_fail({bad_terms, Z2, X ++ By})
894    end.
895
896x_mk_bytes(N) ->
897    X = lists:duplicate(N, $a),
898    {list_to_binary(X), X}.
899
900mk_bytes(N) when N > 4 ->
901    X = lists:duplicate(N-4, $a),
902    case byte_size(term_to_binary(X)) of
903        N -> X;
904        Z -> test_server_fail({bad_terms, Z, N})
905    end.
906
907get_list(File, Log) ->
908    ct:pal(?HI_VERBOSITY, "File ~p~n", [File]),
909    ok = disk_log:sync(Log),
910    {ok, B} = file:read_file(File),
911    binary_to_list(B).
912
913
914get_all_terms(Log, File, Type) ->
915    {ok, _Log} = disk_log:open([{name,Log}, {type,Type}, {size,infinity},
916				{format,internal}, {file, File},
917				{mode, read_only}]),
918    Ts = get_all_terms(Log),
919    ok = disk_log:close(Log),
920    Ts.
921
922get_all_terms(Log) ->
923    get_all_terms1(Log, start, []).
924
925get_all_terms1(Log, Cont, Res) ->
926    case disk_log:chunk(Log, Cont) of
927	{error, _R} ->
928	    test_server_fail({bad_chunk, Log, Cont});
929	{Cont2, Terms} ->
930	    get_all_terms1(Log, Cont2, Res ++ Terms);
931	eof ->
932	    Res
933    end.
934
935get_all_terms_and_bad(Log, File, Type) ->
936    {ok, _Log} = disk_log:open([{name,Log}, {type,Type}, {size,infinity},
937				{format,internal}, {file, File},
938				{mode, read_only}]),
939    Ts = get_all_terms_and_bad(Log),
940    ok = disk_log:close(Log),
941    Ts.
942
943get_all_terms_and_bad(Log) ->
944    read_only = info(Log, mode, foo),
945    get_all_terms_and_bad1(Log, start, [], 0).
946
947%%
948get_all_terms_and_bad1(Log, Cont, Res, Bad0) ->
949    case disk_log:chunk(Log, Cont) of
950	{Cont2, Terms} ->
951	    get_all_terms_and_bad1(Log, Cont2, Res ++ Terms, Bad0);
952	{Cont2, Terms, Bad} ->
953	    get_all_terms_and_bad1(Log, Cont2, Res ++ Terms, Bad0+Bad);
954	eof ->
955	    {Res, Bad0}
956    end.
957
958get_all_binary_terms_and_bad(Log, File, Type) ->
959    {ok, _Log} = disk_log:open([{name,Log}, {type,Type}, {size,infinity},
960				{format,internal}, {file, File},
961				{mode, read_only}]),
962    Ts = get_all_binary_terms_and_bad(Log),
963    ok = disk_log:close(Log),
964    Ts.
965
966get_all_binary_terms_and_bad(Log) ->
967    read_only = info(Log, mode, foo),
968    get_all_binary_terms_and_bad1(Log, start, [], 0).
969
970%%
971get_all_binary_terms_and_bad1(Log, Cont, Res, Bad0) ->
972    case disk_log:bchunk(Log, Cont) of
973	{Cont2, BinTerms} ->
974	    get_all_binary_terms_and_bad1(Log, Cont2, Res ++ BinTerms, Bad0);
975	{Cont2, BinTerms, Bad} ->
976	    get_all_binary_terms_and_bad1(Log, Cont2, Res ++ BinTerms,
977                                          Bad0+Bad);
978	eof ->
979	    {Res, Bad0}
980    end.
981
982del(File, 0) ->
983    file:delete(File ++ ".siz"),
984    file:delete(File ++ ".idx");
985del(File, N) ->
986    file:delete(File ++ "." ++ integer_to_list(N)),
987    del(File, N-1).
988
989test_server_fail(R) ->
990    exit({?MODULE, get(line), R}).
991
992xx() ->
993    File = "a.LOG",
994    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
995			     {format,internal}, {file, File}]),
996    W = xwr(a, 400),
997    disk_log:close(a),
998    %%    file:delete(File),
999    W.
1000
1001%% old: 6150
1002%% new: 5910
1003xwr(Log, BytesItem) ->
1004    NoW = 1000,
1005    Item1 = mk_bytes(BytesItem),
1006    Item2 = mk_bytes(BytesItem),
1007    Item3 = mk_bytes(BytesItem),
1008    Item4 = mk_bytes(BytesItem),
1009    Item5 = mk_bytes(BytesItem),
1010    Item6 = mk_bytes(BytesItem),
1011    Item7 = mk_bytes(BytesItem),
1012    Item8 = mk_bytes(BytesItem),
1013    Item9 = mk_bytes(BytesItem),
1014    Item0 = mk_bytes(BytesItem),
1015    Term = [Item1,Item2,Item3,Item4,Item5,Item6,Item7,Item8,Item9,Item0],
1016    {W, _} = timer:tc(?MODULE, wr, [Log, Term, NoW]),
1017    W/NoW.
1018
1019measure() ->
1020    proc_lib:start_link(?MODULE, init_m, [self()]).
1021
1022init_m(Par) ->
1023    process_flag(trap_exit, true),
1024    Res = m(),
1025    proc_lib:init_ack(Par, Res).
1026
1027m() ->
1028    {W10, R10, Rep10, C10} = m_halt_int(10),
1029    {W11, R11, Rep11, C11} = m_halt_int(100),
1030    {W12, R12, Rep12, C12} = m_halt_int(400),
1031    {W13, R13, Rep13, C13} = m_halt_int(1000),
1032    {W14, R14, Rep14, C14} = m_halt_int(10000),
1033    {W2, R2, Rep2, C2} = m_wrap_int(400),
1034    {W3, R3, Rep3, C3} = m_many_halt_int(10, 400),
1035    {W4, R4, Rep4, C4} = m_many_halt_int(20, 400),
1036    {W5, R5, Rep5, C5} = m_many_halt_int(10, 1000),
1037    {W6, R6, Rep6, C6} = m_many_halt_int(10, 10),
1038    {W7, R7, Rep7, C7} = m_many_halt_int(20, 10),
1039
1040    io:format("Type of log            mysec/write  mysec/read"
1041	      "  mysec/repair byte  cpu/write\n"),
1042    io:format("===========            ===========  =========="
1043	      "  =================  =========\n"),
1044    one_line("halt,int.inf. (10)", W10, R10, Rep10, C10),
1045    one_line("halt,int.inf. (100)", W11, R11, Rep11, C11),
1046    one_line("halt,int.inf. (400)", W12, R12, Rep12, C12),
1047    one_line("halt,int.inf. (1000)", W13, R13, Rep13, C13),
1048    one_line("halt,int.inf. (10000)", W14, R14, Rep14, C14),
1049    one_line("wrap,int.  4. (400)", W2, R2, Rep2, C2),
1050    one_line("halt,int.inf. (10,10)", W6, R6, Rep6, C6),
1051    one_line("halt,int.inf. (20,10)", W7, R7, Rep7, C7),
1052    one_line("halt,int.inf. (10,400)", W3, R3, Rep3, C3),
1053    one_line("halt,int.inf. (20,400)", W4, R4, Rep4, C4),
1054    one_line("halt,int.inf. (10,1000)", W5, R5, Rep5, C5),
1055    io:format("\n"),
1056    io:format("\tWrap log time depends on how often the log wraps, as this\n"),
1057    io:format("\tinvolves opening of new files, which costs alot."),
1058    io:format("\n").
1059
1060one_line(Txt, W, R, Rep, C) ->
1061    io:format("~.22s  ~.10w  ~.10w  ~.17w  ~.9w\n", [Txt, W, R, Rep, C]).
1062
1063m_halt_int(BytesItem) ->
1064    File = "a.LOG",
1065    {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity},
1066			     {format,internal}, {file, File}]),
1067    {T,W} = wr(a, BytesItem),
1068    R = r(a),
1069    [{_,P}] = ets:lookup(?DISK_LOG_NAME_TABLE, a),
1070    exit(P, kill),
1071    receive after 100 -> ok end,
1072    crash(File, 10),
1073    Sz = file_size(File),
1074    Start = start_times(),
1075    {repaired, a, {recovered, Rec}, {badbytes, Bad}} =
1076	disk_log:open([{name,a}, {type,halt}, {size,infinity},
1077		       {format,internal}, {file, File}]),
1078    {_,Rep} = end_times(Start),
1079    io:format("m_halt_int: Rep = ~p, Rec = ~p, Bad = ~p~n", [Rep, Rec, Bad]),
1080    disk_log:close(a),
1081    file:delete(File),
1082    {W,R,1000*Rep/Sz,T}.
1083
1084m_wrap_int(BytesItem) ->
1085    File = "a.LOG",
1086    {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{405*1000, 4}},
1087			     {format,internal}, {file, File}]),
1088    {T,W} = wr(a, BytesItem),
1089    R = r(a),
1090    [{_,P}] = ets:lookup(?DISK_LOG_NAME_TABLE, a),
1091    exit(P, kill),
1092    receive after 100 -> ok end,
1093    del(File, 4),
1094    {W,R,'n/a',T}.
1095
1096m_many_halt_int(NoClients, BytesItem) ->
1097    Name = 'log.LOG',
1098    File = "log.LOG",
1099    {ok, _} = disk_log:open([{name,Name}, {type,halt},
1100			     {size,infinity},
1101			     {format,internal}, {file,File}]),
1102    NoW = round(lists:max([lists:min([5000000/BytesItem/NoClients,
1103				      50000/NoClients]),
1104			   1000])),
1105    {T,W} = many_wr(NoClients, Name, NoW, BytesItem),
1106    ok = disk_log:close(Name),
1107    file:delete(File),
1108    {1000*W/NoW/NoClients,'n/a','n/a',1000*T/NoW/NoClients}.
1109
1110many_wr(NoClients, Log, NoW, BytesItem) ->
1111    Item = mk_bytes(BytesItem),
1112    Fun = fun(Name, _Pid, _I) -> disk_log:log(Name, Item) end,
1113    Start = start_times(),
1114    Pids = spawn_clients(NoClients, client, [self(), Log, NoW, Fun]),
1115    check_clients(Pids),
1116    end_times(Start).
1117
1118wr(Log, BytesItem) ->
1119    NoW = round(lists:max([lists:min([5000000/BytesItem,50000]),1000])),
1120    Item = mk_bytes(BytesItem),
1121    Start = start_times(),
1122    wr(Log, Item, NoW),
1123    {T,W} = end_times(Start),
1124    {1000*T/NoW, 1000*W/NoW}.
1125
1126wr(Log, _Item, 0) ->
1127    disk_log:sync(Log),
1128    ok;
1129wr(Log, Item, N) ->
1130    ok = disk_log:log(Log, Item),
1131    wr(Log, Item, N-1).
1132
1133r(_) ->
1134    nyi.
1135
1136start_times() ->
1137    {T1, _} = statistics(runtime),
1138    {W1, _} = statistics(wall_clock),
1139    {T1, W1}.
1140
1141end_times({T1,W1}) ->
1142    {T2, _} = statistics(runtime),
1143    {W2, _} = statistics(wall_clock),
1144    {T2-T1, W2-W1}.
1145
1146
1147%% Test head parameter.
1148head_func(Conf) when is_list(Conf) ->
1149    Dir = ?privdir(Conf),
1150    File = filename:join(Dir, "a.LOG"),
1151    ets:new(xxx, [named_table, set, public]),
1152    ets:insert(xxx, {wrapc, 0}),
1153    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
1154			     {size, {100,4}},
1155			     {head_func, {?MODULE, hf, []}}]),
1156    B = mk_bytes(60),
1157    disk_log:log(a, B),
1158    disk_log:alog(a, B),
1159    disk_log:alog(a, B),
1160    disk_log:log(a, B),
1161    H = [1,2,3],
1162    [{wrapc, 4}] = ets:lookup(xxx, wrapc),
1163    ets:delete(xxx),
1164    case get_all_terms(a) of
1165	[H,B,H,B,H,B,H,B] ->
1166	    ok;
1167	E1 ->
1168	    test_server_fail({bad_terms, E1,
1169			      [H,B,H,B,H,B,H,B]})
1170    end,
1171    8  = no_written_items(a),
1172    disk_log:close(a),
1173    del(File, 4),
1174
1175    %% invalid header function
1176    {error, {invalid_header, {_, {term}}}} =
1177	disk_log:open([{name, n}, {file, File}, {type, halt},
1178		       {format, external},
1179		       {head_func, {?MODULE, head_fun, [{term}]}}]),
1180    file:delete(File),
1181
1182    {error, {invalid_header, _}} =
1183	disk_log:open([{name, n}, {file, File}, {type, halt},
1184		       {format, external},
1185		       {head_func, {?MODULE, head_fun, [{ok,{term}}]}}]),
1186    file:delete(File),
1187
1188    {ok,n} =
1189	disk_log:open([{name, n}, {file, File}, {type, halt},
1190		       {format, external},
1191		       {head_func, {?MODULE, head_fun, [{ok,<<"head">>}]}}]),
1192    ok = disk_log:close(n),
1193    {ok,<<"head">>} = file:read_file(File),
1194    file:delete(File),
1195
1196    {ok,n} =
1197	disk_log:open([{name, n}, {file, File}, {type, halt},
1198		       {format, external},
1199		       {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]),
1200    ok = disk_log:close(n),
1201    {ok,<<"head">>} = file:read_file(File),
1202    file:delete(File),
1203
1204    Error1 = {error, {badarg, _}} =
1205	disk_log:open([{name, n}, {file, File}, {type, wrap},
1206		       {head_func, {tjo,hej,san}},{size, {100, 4}}]),
1207    "The argument " ++ _ = format_error(Error1),
1208
1209    Error2 = {error, {invalid_header, _}} =
1210	disk_log:open([{name, n}, {file, File}, {type, halt},
1211		       {head_func, {tjo,hej,[san]}}]),
1212    "The disk log header" ++ _ = format_error(Error2),
1213    file:delete(File).
1214
1215
1216head_fun(H) ->
1217    H.
1218
1219hf() ->
1220    ets:update_counter(xxx, wrapc, 1),
1221    {ok, [1,2,3]}.
1222
1223%% Test head parameter.
1224plain_head(Conf) when is_list(Conf) ->
1225    Dir = ?privdir(Conf),
1226    File = filename:join(Dir, "a.LOG"),
1227    H = [1,2,3],
1228    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
1229			     {size, {100,4}}, {head, H}]),
1230    %% This one is not "counted".
1231    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
1232			     {size, {100,4}}, {head, H}]),
1233    B = mk_bytes(60),
1234    disk_log:log(a, B),
1235    disk_log:alog(a, B),
1236    disk_log:alog(a, B),
1237    disk_log:log(a, B),
1238    case get_all_terms(a) of
1239	[H,B,H,B,H,B,H,B] ->
1240	    ok;
1241	E1 ->
1242	    test_server_fail({bad_terms, E1,
1243			      [H,B,H,B,H,B,H,B]})
1244    end,
1245    8 = no_written_items(a),
1246    ok = disk_log:close(a),
1247    {error, no_such_log} = disk_log:close(a),
1248    del(File, 4).
1249
1250
1251
1252%% Test that a header is just printed once in a log file.
1253one_header(Conf) when is_list(Conf) ->
1254    Dir = ?privdir(Conf),
1255    File = filename:join(Dir, "a.LOG"),
1256    H = [1,2,3],
1257    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
1258			     {size, {100,4}}, {head, H}]),
1259    B = mk_bytes(60),
1260    ok = disk_log:log(a, B),
1261    ok = disk_log:alog(a, B),
1262    ok = disk_log:alog(a, B),
1263    ok = disk_log:log(a, B),
1264    case get_all_terms(a) of
1265	[H,B,H,B,H,B,H,B] ->
1266	    ok;
1267	E1 ->
1268	    test_server_fail({bad_terms, E1,
1269			      [H,B,H,B,H,B,H,B]})
1270    end,
1271    8  = no_written_items(a),
1272    ok = disk_log:close(a),
1273    del(File, 4),
1274
1275    Fileb = filename:join(Dir, "b.LOG"),
1276    {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]),
1277    ok = disk_log:close(b),
1278    {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]),
1279    ok = disk_log:log(b, "first log"),
1280    ok = disk_log:alog(b, "second log"),
1281    ok = disk_log:close(b),
1282    {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]),
1283    ok = disk_log:alog(b, "3rd log"),
1284    ok = disk_log:log(b, "4th log"),
1285    case get_all_terms(b) of
1286	[H, "first log", "second log", "3rd log", "4th log"] ->
1287	    ok;
1288	E2 ->
1289	    test_server_fail({bad_terms, E2,
1290			      [H, "first log", "second log",
1291			       "3rd log", "4th log"]})
1292    end,
1293    2  = no_written_items(b),
1294    ok = disk_log:close(b),
1295    ok = file:delete(Fileb),
1296
1297    Filec = filename:join(Dir, "c.LOG"),
1298    H2 = "this is a header ",
1299    {ok, c} = disk_log:open([{name,c}, {format, external},
1300			     {file, Filec}, {head, H2}]),
1301    ok = disk_log:close(c),
1302    {ok, c} = disk_log:open([{name,c}, {format, external},
1303			     {file, Filec}, {head, H2}]),
1304    ok = disk_log:blog(c, "first log"),
1305    ok = disk_log:balog(c, "second log"),
1306    ok = disk_log:close(c),
1307    {ok, c} = disk_log:open([{name,c}, {format, external},
1308			     {file, Filec}, {head, H2}]),
1309    ok = disk_log:balog(c, "3rd log"),
1310    ok = disk_log:blog(c, "4th log"),
1311    ok = disk_log:sync(c),
1312    {ok, Fdc} = file:open(Filec, [read]),
1313    {ok,"this is a header first logsecond log3rd log4th log"} =
1314	file:read(Fdc, 200),
1315    ok = file:close(Fdc),
1316    2  = no_written_items(c),
1317    disk_log:close(c),
1318    ok = file:delete(Filec),
1319    ok.
1320
1321
1322
1323%% Test notify parameter, wrap.
1324wrap_notif(Conf) when is_list(Conf) ->
1325    Dir = ?privdir(Conf),
1326    File = filename:join(Dir, "a.LOG"),
1327    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
1328			     {size, {100,4}}, {notify, true}]),
1329    B = mk_bytes(60),
1330    disk_log:log(a, B),
1331    disk_log:alog(a, B),
1332    disk_log:alog(a, B),
1333    disk_log:log(a, B),
1334    disk_log:log(a, B),
1335    rec(3, {disk_log, node(), a, {wrap, 0}}),
1336    rec(1, {disk_log, node(), a, {wrap, 1}}),
1337    disk_log:close(a),
1338    del(File, 4).
1339
1340%% Test notify parameter, wrap, filled file.
1341full_notif(Conf) when is_list(Conf) ->
1342    Dir = ?privdir(Conf),
1343    File = filename:join(Dir, "a.LOG"),
1344    file:delete(File),
1345
1346    {ok, a} = disk_log:open([{name, a}, {file, File}, {type, halt},
1347			     {size, 100}, {notify, true}]),
1348    B = mk_bytes(60),
1349    disk_log:log(a, B),
1350    disk_log:alog(a, B),
1351    rec(1, {disk_log, node(), a, full}),
1352    disk_log:close(a),
1353    file:delete(File).
1354
1355%% Test notify parameter, wrap, truncated file.
1356trunc_notif(Conf) when is_list(Conf) ->
1357    Dir = ?privdir(Conf),
1358    File = filename:join(Dir, "a.LOG"),
1359    File2 = filename:join(Dir, "a.DUMP"),
1360    {ok, a} = disk_log:open([{name, a}, {file, File}, {type, halt},
1361			     {size, 100}, {notify, true}]),
1362    B = mk_bytes(60),
1363    disk_log:log(a, B),
1364    disk_log:truncate(a),
1365    rec(1, {disk_log, node(), a, {truncated, 1}}),
1366    disk_log:log(a, B),
1367    ok = disk_log:reopen(a, File2),
1368    rec(1, {disk_log, node(), a, {truncated, 1}}),
1369    disk_log:close(a),
1370    file:delete(File),
1371    file:delete(File2).
1372
1373%% Test notify parameters 'format_external' and 'blocked_log.
1374blocked_notif(Conf) when is_list(Conf) ->
1375    Dir = ?privdir(Conf),
1376    File = filename:join(Dir, "n.LOG"),
1377    No = 4,
1378    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1379			     {size, {100,No}}, {notify, true},
1380			     {format, external}]),
1381    B = mk_bytes(60),
1382    Error1 = {error,{format_external,n}} = disk_log:log(n, B),
1383    "The requested operation" ++ _ = format_error(Error1),
1384    ok = disk_log:blog(n, B),
1385    ok = disk_log:alog(n, B),
1386    rec(1, {disk_log, node(), n, {format_external, [term_to_binary(B)]}}),
1387    ok = disk_log:alog_terms(n, [B,B,B,B]),
1388    rec(1, {disk_log, node(), n, {format_external,
1389				  lists:map(fun term_to_binary/1, [B,B,B,B])}}),
1390    ok = disk_log:block(n, false),
1391    ok = disk_log:alog(n, B),
1392    rec(1, {disk_log, node(), n, {blocked_log, [term_to_binary(B)]}}),
1393    ok = disk_log:balog(n, B),
1394    rec(1, {disk_log, node(), n, {blocked_log, [list_to_binary(B)]}}),
1395    ok = disk_log:balog_terms(n, [B,B,B,B]),
1396    disk_log:close(n),
1397    rec(1, {disk_log, node(), n, {blocked_log,
1398				  lists:map(fun list_to_binary/1, [B,B,B,B])}}),
1399    del(File, No).
1400
1401
1402%% Test the new version of the .idx file.
1403new_idx_vsn(Conf) when is_list(Conf) ->
1404    DataDir = ?datadir(Conf),
1405    PrivDir = ?privdir(Conf),
1406    File = filename:join(PrivDir, "new_vsn.LOG"),
1407    Kurt = filename:join(PrivDir, "kurt.LOG"),
1408    Kurt2 = filename:join(PrivDir, "kurt2.LOG"),
1409
1410    %% Test that a wrap log file can have more than 255 files
1411    {ok, new_vsn} = disk_log:open([{file, File}, {name, new_vsn},
1412				   {type, wrap}, {size, {40, 270}}]),
1413    ok = log(new_vsn, 280),
1414    {ok, Bin} = file:read_file(add_ext(File, "idx")),
1415    <<0,0:32,2,10:32,1:64,1:64,_/binary>> = Bin,
1416    disk_log:close(new_vsn),
1417    del(File, 270),
1418
1419    %% convert a very old version (0) of wrap log file to the new format (2)
1420    copy_wrap_log("kurt.LOG", 4, DataDir, PrivDir),
1421
1422    {repaired, kurt, {recovered, 1}, {badbytes, 0}} =
1423	disk_log:open([{file, Kurt}, {name, kurt},
1424		       {type, wrap}, {size, {40, 4}}]),
1425    ok = disk_log:log(kurt, "this is a logged message number X"),
1426    ok = disk_log:log(kurt, "this is a logged message number Y"),
1427    {ok, BinK} = file:read_file(add_ext(Kurt, "idx")),
1428    <<0,0:32,2,2:32,1:64,1:64,1:64,1:64>> = BinK,
1429    {{40,4}, 2} = disk_log_1:read_size_file_version(Kurt),
1430    disk_log:close(kurt),
1431    del(Kurt, 4),
1432
1433    %% keep the old format (1)
1434    copy_wrap_log("kurt2.LOG", 4, DataDir, PrivDir),
1435
1436    {repaired, kurt2, {recovered, 1}, {badbytes, 0}} =
1437	disk_log:open([{file, Kurt2}, {name, kurt2},
1438		       {type, wrap}, {size, {40, 4}}]),
1439    ok = disk_log:log(kurt2, "this is a logged message number X"),
1440    ok = disk_log:log(kurt2, "this is a logged message number Y"),
1441    {ok, BinK2} = file:read_file(add_ext(Kurt2, "idx")),
1442    <<0,2:32,1:32,1:32,1:32,1:32>> = BinK2,
1443    {{40,4}, 1} = disk_log_1:read_size_file_version(Kurt2),
1444    disk_log:close(kurt2),
1445    del(Kurt2, 4),
1446
1447    ok.
1448
1449%% Test reopen/1 on halt and wrap logs.
1450reopen(Conf) when is_list(Conf) ->
1451
1452    Dir = ?privdir(Conf),
1453    File = filename:join(Dir, "n.LOG"),
1454    NewFile = filename:join(Dir, "nn.LOG"),
1455    B = mk_bytes(60),
1456
1457    file:delete(File),    % cleanup
1458    file:delete(NewFile), % cleanup
1459    Q = qlen(),
1460
1461    %% External halt log.
1462    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
1463			     {notify, true}, {head, "header"},
1464			     {size, infinity},{format, external}]),
1465    ok = disk_log:blog(n, B),
1466    ok = disk_log:breopen(n, NewFile, "head"),
1467    rec(1, {disk_log, node(), n, {truncated, 2}}),
1468    ok = disk_log:blog(n, B),
1469    ok = disk_log:blog(n, B),
1470    ok = disk_log:breopen(n, NewFile, "head"),
1471    rec(1, {disk_log, node(), n, {truncated, 3}}),
1472    ok = disk_log:close(n),
1473    {ok,BinaryFile} = file:read_file(File),
1474    "head" = binary_to_list(BinaryFile),
1475    file:delete(File),
1476    file:delete(NewFile),
1477
1478    %% Internal halt log.
1479    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
1480			     {notify, true}, {head, header},
1481			     {size, infinity}]),
1482    ok = disk_log:log(n, B),
1483    Error1 = {error, {same_file_name, n}} = disk_log:reopen(n, File),
1484    "Current and new" ++ _ = format_error(Error1),
1485    ok = disk_log:reopen(n, NewFile),
1486    rec(1, {disk_log, node(), n, {truncated, 2}}),
1487    ok = disk_log:log(n, B),
1488    ok = disk_log:log(n, B),
1489    ok = disk_log:reopen(n, NewFile),
1490    rec(1, {disk_log, node(), n, {truncated, 3}}),
1491    ok = disk_log:close(n),
1492    [header, _B, _B] = get_all_terms(nn, NewFile, halt),
1493    file:delete(File),
1494    file:delete(NewFile),
1495
1496    %% Internal wrap log.
1497    No = 4,
1498    del(File, No),	% cleanup
1499    del(NewFile, No),	% cleanup
1500
1501    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1502			     {notify, true},
1503			     {head, header}, {size, {100, No}}]),
1504    ok = disk_log:log(n, B),
1505    ok = disk_log:log_terms(n, [B,B,B]),
1506    %% Used to be one message, but now one per wrapped file.
1507    rec(3, {disk_log, node(), n, {wrap, 0}}),
1508    ok = disk_log:log_terms(n, [B]),
1509    rec(1, {disk_log, node(), n, {wrap, 2}}),
1510    ok = disk_log:log_terms(n, [B]),
1511    rec(1, {disk_log, node(), n, {wrap, 2}}),
1512    ok = disk_log:reopen(n, NewFile, new_header),
1513    rec(1, {disk_log, node(), n, {truncated, 8}}),
1514    ok = disk_log:log_terms(n, [B,B]),
1515    rec(1, {disk_log, node(), n, {wrap, 0}}),
1516    ok = disk_log:log_terms(n, [B]),
1517    rec(1, {disk_log, node(), n, {wrap, 0}}),
1518    ok = disk_log:close(n),
1519    [header, _, header, _, header, _, header, _] =
1520	get_all_terms(nn, NewFile, wrap),
1521    [new_header, _, header, _, header, _] = get_all_terms(n, File, wrap),
1522
1523    del(NewFile, No),
1524    file:delete(File ++ ".2"),
1525    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1526			     {notify, true},
1527			     {head, header}, {size, {100, No}}]),
1528    %% One file is missing...
1529    ok = disk_log:reopen(n, NewFile),
1530    rec(1, {disk_log, node(), n, {truncated, 6}}),
1531    ok = disk_log:close(n),
1532
1533    del(File, No),
1534    del(NewFile, No),
1535    Q = qlen(),
1536    ok.
1537
1538
1539%% Test block/1 on external and internal logs.
1540block_blocked(Conf) when is_list(Conf) ->
1541
1542    Dir = ?privdir(Conf),
1543    B = mk_bytes(60),
1544    Halt = join(Dir, "halt.LOG"),
1545
1546    %% External logs.
1547    file:delete(Halt), % cleanup
1548    {ok, halt} = disk_log:open([{name, halt}, {type, halt},
1549				{format, external}, {file, Halt}]),
1550    ok = disk_log:sync(halt),
1551    ok = disk_log:block(halt, false),
1552    Error1 = {error, {blocked_log, halt}} = disk_log:block(halt),
1553    "The blocked disk" ++ _ = format_error(Error1),
1554    {error, {blocked_log, halt}} = disk_log:sync(halt),
1555    {error, {blocked_log, halt}} = disk_log:truncate(halt),
1556    {error, {blocked_log, halt}} = disk_log:change_size(halt, infinity),
1557    {error, {blocked_log, halt}} =
1558        disk_log:change_notify(halt, self(), false),
1559    {error, {blocked_log, halt}} =
1560        disk_log:change_header(halt, {head, header}),
1561    {error, {blocked_log, halt}} = disk_log:reopen(halt, "foo"),
1562    ok = disk_log:close(halt),
1563
1564    {ok, halt} = disk_log:open([{name, halt}, {type, halt},
1565				{format, external}]),
1566    ok = disk_log:sync(halt),
1567    ok = disk_log:block(halt, true),
1568    {error, {blocked_log, halt}} = disk_log:blog(halt, B),
1569    {error, {blocked_log, halt}} = disk_log:blog(halt, B),
1570    {error, {blocked_log, halt}} = disk_log:block(halt),
1571    {error, {blocked_log, halt}} = disk_log:sync(halt),
1572    {error, {blocked_log, halt}} = disk_log:truncate(halt),
1573    {error, {blocked_log, halt}} = disk_log:change_size(halt, infinity),
1574    {error, {blocked_log, halt}} =
1575        disk_log:change_notify(halt, self(), false),
1576    {error, {blocked_log, halt}} =
1577        disk_log:change_header(halt, {head, header}),
1578    {error, {blocked_log, halt}} = disk_log:reopen(halt, "foo"),
1579
1580    ok = disk_log:unblock(halt),
1581    ok = disk_log:close(halt),
1582    file:delete(Halt),
1583
1584    %% Internal logs.
1585    File = filename:join(Dir, "n.LOG"),
1586    No = 4,
1587    del(File, No), % cleanup
1588    {ok, halt} = disk_log:open([{name, halt}, {file, File}, {type, wrap},
1589				{size, {100, No}}]),
1590    ok = disk_log:block(halt, true),
1591    eof = disk_log:chunk(halt, start),
1592    Error2 = {error, end_of_log} = disk_log:chunk_step(halt, start, 1),
1593    "An attempt" ++ _ = format_error(Error2),
1594    {error, {blocked_log, halt}} = disk_log:log(halt, B),
1595    {error, {blocked_log, halt}} = disk_log:inc_wrap_file(halt),
1596    ok = disk_log:unblock(halt),
1597    ok = disk_log:block(halt, false),
1598    {error, {blocked_log, halt}} = disk_log:log(halt, B),
1599    {error, {blocked_log, halt}} = disk_log:inc_wrap_file(halt),
1600    Parent = self(),
1601    Pid =
1602        spawn_link(fun() ->
1603                           {error, {blocked_log, halt}} =
1604                               disk_log:chunk(halt, start),
1605                           {error, {blocked_log, halt}} =
1606                               disk_log:chunk_step(halt, start, 1),
1607                           Parent ! {self(), stopped}
1608                   end),
1609    receive {Pid,stopped} -> ok end,
1610    ok = disk_log:close(halt),
1611    del(File, No).
1612
1613%% Run commands from the queue by unblocking.
1614block_queue(Conf) when is_list(Conf) ->
1615
1616    Dir = ?privdir(Conf),
1617    Q = qlen(),
1618    File = filename:join(Dir, "n.LOG"),
1619    No = 4,
1620    del(File, No), % cleanup
1621    B = mk_bytes(60),
1622
1623    Pid = spawn_link(?MODULE, lserv, [n]),
1624    {ok, n} = sync_do(Pid, {open, File}),
1625
1626    ok = disk_log:block(n, true),
1627    async_do(Pid, {blog, B}),
1628    ok = disk_log:unblock(n),
1629    ok = get_reply(),
1630    1 = no_written_items(n),
1631    Error1 = {error,{not_blocked,n}} = disk_log:unblock(n),
1632    "The disk log" ++ _ = format_error(Error1),
1633
1634    ok = disk_log:block(n, true),
1635    async_do(Pid, {balog, "one string"}),
1636    ok = disk_log:unblock(n),
1637    ok = get_reply(),
1638    2 = no_written_items(n),
1639
1640    ok = disk_log:block(n, true),
1641    async_do(Pid, sync),
1642    ok = disk_log:unblock(n),
1643    ok = get_reply(),
1644
1645    ok = disk_log:block(n, true),
1646    async_do(Pid, truncate),
1647    ok = disk_log:unblock(n),
1648    ok = get_reply(),
1649    0 = no_items(n),
1650
1651    ok = disk_log:block(n, true),
1652    async_do(Pid, {block, false}),
1653    ok = disk_log:unblock(n),
1654    ok = get_reply(),
1655    {error, {blocked_log, _}} = disk_log:blog(n, B),
1656    ok = sync_do(Pid, unblock),
1657
1658    ok = disk_log:block(n, true),
1659    async_do(Pid, {change_notify, Pid, true}),
1660    ok = disk_log:unblock(n),
1661    ok = get_reply(),
1662    [{_, true}] = owners(n),
1663
1664    ok = disk_log:block(n, true),
1665    async_do(Pid, {change_notify, Pid, false}),
1666    ok = disk_log:unblock(n),
1667    ok = get_reply(),
1668    [{_, false}] = owners(n),
1669
1670    ok = disk_log:block(n, true),
1671    async_do(Pid, {change_header, {head, header}}),
1672    ok = disk_log:unblock(n),
1673    {error, {badarg, head}} = get_reply(),
1674
1675    ok = disk_log:block(n, true),
1676    async_do(Pid, {change_size, 17}),
1677    ok = disk_log:unblock(n),
1678    {error, {badarg, size}} = get_reply(),
1679
1680    ok = disk_log:block(n, true),
1681    async_do(Pid, inc_wrap_file),
1682    ok = disk_log:unblock(n),
1683    ok = get_reply(),
1684
1685    ok = sync_do(Pid, close),
1686    del(File, No),
1687
1688    _Pid2 = spawn_link(?MODULE, lserv, [n]),
1689    {ok, n} = sync_do(Pid, {int_open, File}),
1690
1691    ok = disk_log:block(n, true),
1692    async_do(Pid, {chunk, start}),
1693    ok = disk_log:unblock(n),
1694    eof = get_reply(),
1695
1696    ok = disk_log:block(n, true),
1697    async_do(Pid, {chunk_step, start, 100}),
1698    ok = disk_log:unblock(n),
1699    {ok, _Cont} = get_reply(),
1700
1701    ok = disk_log:block(n, true),
1702    async_do(Pid, {log,a_term}),
1703    ok = disk_log:unblock(n),
1704    ok = get_reply(),
1705    1 = no_written_items(n),
1706
1707    ok = sync_do(Pid, close),
1708    sync_do(Pid, terminate),
1709    del(File, No),
1710
1711    %% Test of the queue. Three processes involved here. Pid1's block
1712    %% request is queued. Pid2's log requests are put in the queue.
1713    %% When unblock is executed, Pid1's block request is granted.
1714    %% Pid2's log requests are executed when Pid1 unblocks.
1715    %% (This example should show that the pair 'queue' and 'messages'
1716    %% in State does the trick - one does not need a "real" queue.)
1717    P0 = pps(),
1718    Name = n,
1719    Pid1 = spawn_link(?MODULE, lserv, [Name]),
1720    {ok, Name} = sync_do(Pid1, {int_open, File, {1000,2}}),
1721    Pid2 = spawn_link(?MODULE, lserv, [Name]),
1722    {ok, Name} = sync_do(Pid2, {int_open, File, {1000,2}}),
1723    ok = disk_log:block(Name),
1724    async_do(Pid1, {alog,{1,a}}),
1725    ok = get_reply(),
1726    async_do(Pid1, {alog,{2,b}}),
1727    ok = get_reply(),
1728    async_do(Pid1, {alog,{3,c}}),
1729    ok = get_reply(),
1730    async_do(Pid1, {alog,{4,d}}),
1731    ok = get_reply(),
1732    async_do(Pid1, block),
1733    async_do(Pid2, {alog,{5,e}}),
1734    ok = get_reply(),
1735    async_do(Pid2, {alog,{6,f}}),
1736    ok = get_reply(),
1737    ok = disk_log:unblock(Name),
1738    ok = get_reply(),
1739    async_do(Pid2, {alog,{7,g}}),
1740    ok = get_reply(),
1741    async_do(Pid2, {alog,{8,h}}),
1742    ok = get_reply(),
1743    async_do(Pid1, unblock),
1744    ok = get_reply(),
1745    ok = sync_do(Pid1, close),
1746    ok = sync_do(Pid2, close),
1747    sync_do(Pid1, terminate),
1748    sync_do(Pid2, terminate),
1749    Terms = get_all_terms(Name, File, wrap),
1750    true = [{1,a},{2,b},{3,c},{4,d},{5,e},{6,f},{7,g},{8,h}] == Terms,
1751    del(File, 2),
1752    Q = qlen(),
1753    check_pps(P0),
1754    ok.
1755
1756%% OTP-4880. Blocked processes did not get disk_log_stopped message.
1757block_queue2(Conf) when is_list(Conf) ->
1758    Q = qlen(),
1759    P0 = pps(),
1760    Dir = ?privdir(Conf),
1761    File = filename:join(Dir, "n.LOG"),
1762    No = 4,
1763
1764    %% log requests are queued, and processed when the log is closed
1765    Pid = spawn_link(?MODULE, lserv, [n]),
1766    {ok, n} = sync_do(Pid, {open, File}),
1767    ok = sync_do(Pid, block),
1768    %% Asynchronous stuff is ignored.
1769    ok = disk_log:balog_terms(n, [<<"foo">>,<<"bar">>]),
1770    ok = disk_log:balog_terms(n, [<<"more">>,<<"terms">>]),
1771    Parent = self(),
1772    Fun =
1773        fun() ->
1774                {error,no_such_log} = disk_log:sync(n),
1775                receive {disk_log, _, {error, disk_log_stopped}} -> ok end,
1776                Parent ! disk_log_stopped_ok
1777        end,
1778    spawn(Fun),
1779    timer:sleep(500),
1780    ok = sync_do(Pid, close),
1781    receive disk_log_stopped_ok -> ok end,
1782    sync_do(Pid, terminate),
1783    {ok,<<>>} = file:read_file(File ++ ".1"),
1784    del(File, No),
1785    Q = qlen(),
1786    check_pps(P0),
1787    ok.
1788
1789
1790%% Test unblock/1.
1791unblock(Conf) when is_list(Conf) ->
1792    Dir = ?privdir(Conf),
1793    File = filename:join(Dir, "n.LOG"),
1794    No = 1,
1795    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1796			     {size, {100,No}}, {notify, true},
1797			     {format, external}]),
1798    ok = disk_log:block(n),
1799    spawn_link(?MODULE, try_unblock, [n]),
1800    timer:sleep(100),
1801    disk_log:close(n),
1802    del(File, No).
1803
1804try_unblock(Log) ->
1805    Error = {error, {not_blocked_by_pid, n}} = disk_log:unblock(Log),
1806    "The disk log" ++ _ = format_error(Error).
1807
1808
1809%% Test open/1 when old files exist.
1810open_overwrite(Conf) when is_list(Conf) ->
1811
1812    Dir = ?privdir(Conf),
1813    File = filename:join(Dir, "n.LOG"),
1814    No = 4,
1815    del(File, No), % cleanup
1816
1817    %% read write
1818    First = "n.LOG.1",
1819    make_file(Dir, First, 8),
1820
1821    Error1 = {error, {not_a_log_file, _}} =
1822	disk_log:open([{name, n}, {file, File}, {type, wrap},
1823		       {format, internal}, {size, {100, No}}]),
1824    "The file" ++ _ = format_error(Error1),
1825    del(File, No),
1826
1827    make_file(Dir, First, 4),
1828
1829    {error, {not_a_log_file, _}} =
1830	disk_log:open([{name, n}, {file, File}, {type, wrap},
1831		       {format, internal}, {size, {100, No}}]),
1832    del(File, No),
1833
1834    make_file(Dir, First, 0),
1835
1836    {error, {not_a_log_file, _}} =
1837	disk_log:open([{name, n}, {file, File}, {type, wrap},
1838		       {format, internal}, {size, {100, No}}]),
1839    %% read only
1840    make_file(Dir, First, 6),
1841
1842    {error, {not_a_log_file, _}} =
1843	disk_log:open([{name, n}, {file, File}, {type, wrap},{mode, read_only},
1844		       {format, internal}, {size, {100, No}}]),
1845    del(File, No),
1846
1847    make_file(Dir, First, 0),
1848
1849    {error, {not_a_log_file, _}} =
1850	disk_log:open([{name, n}, {file, File},{type, wrap},
1851		       {mode, read_only}, {format, internal},
1852		       {size, {100, No}}]),
1853    del(File, No),
1854
1855    {error, _} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1856				{mode, read_only},
1857				{format, internal},{size, {100, No}}]),
1858
1859    file:delete(File),
1860    {ok,n} = disk_log:open([{name,n},{file,File},
1861			    {mode,read_write},{type,halt}]),
1862    ok = disk_log:close(n),
1863    ok = unwritable(File),
1864    {error, {file_error, File, _}} =
1865	disk_log:open([{name,n},{file,File},{mode,read_write},{type,halt}]),
1866    ok = writable(File),
1867    file:delete(File),
1868
1869    {ok,n} = disk_log:open([{name,n},{file,File},{format,external},
1870			    {mode,read_write},{type,halt}]),
1871    ok = disk_log:close(n),
1872    ok = unwritable(File),
1873    {error, {file_error, File, _}} =
1874        disk_log:open([{name,n},{file,File},{format,external},
1875                       {mode,read_write},{type,halt}]),
1876    ok = writable(File),
1877    file:delete(File),
1878
1879    ok.
1880
1881
1882make_file(Dir, File, N) ->
1883    {ok, F} = file:open(filename:join(Dir, File),
1884                        [raw, binary, read, write]),
1885    ok = file:truncate(F),
1886    case N of
1887	0 ->
1888	    true;
1889	_Else ->
1890	    ok = file:write(F, [lists:seq(1,N)])
1891    end,
1892    ok = file:close(F).
1893
1894%% Test open/1 option size.
1895open_size(Conf) when is_list(Conf) ->
1896
1897    Dir = ?privdir(Conf),
1898    File = filename:join(Dir, "n.LOG"),
1899
1900    No = 4,
1901    file:delete(File),
1902    del(File, No),	% cleanup
1903
1904    %% missing size option
1905    {error, {badarg, size}} =
1906	disk_log:open([{name, n}, {file, File}, {type, wrap},
1907		       {format, internal}]),
1908    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1909			     {format, internal},{size, {100, No}}]),
1910    B = mk_bytes(60),
1911    ok = disk_log:log_terms(n, [B, B, B, B]),
1912    ok = disk_log:sync(n),
1913    ok = disk_log:block(n),
1914
1915    %% size option does not match existing size file, read_only
1916    Error1 = {error, {size_mismatch, _, _}} =
1917	disk_log:open([{name, nn}, {file, File}, {type, wrap},
1918		       {mode, read_only}, {format, internal},
1919		       {size, {100, No + 1}}]),
1920    "The given size" ++ _ = format_error(Error1),
1921    {ok, nn} = disk_log:open([{name, nn}, {file, File}, {type, wrap},
1922			      {mode, read_only},
1923			      {format, internal},{size, {100, No}}]),
1924    [_, _, _, _] = get_all_terms1(nn, start, []),
1925    disk_log:close(nn),
1926
1927    ok = disk_log:unblock(n),
1928    ok = disk_log:close(n),
1929
1930    %% size option does not match existing size file, read_write
1931    {error, {size_mismatch, _, _}} =
1932	disk_log:open([{name, nn}, {file, File}, {type, wrap},
1933		       {format, internal}, {size, {100, No + 1}}]),
1934    %% size option does not match existing size file, truncating
1935    {ok, nn} =
1936	disk_log:open([{name, nn}, {file, File}, {type, wrap},
1937		       {repair, truncate}, {format, internal},
1938		       {size, {100, No + 1}}]),
1939    ok = disk_log:close(nn),
1940
1941    del(File, No),
1942    ok.
1943
1944
1945%% Test open/1 with {repair, truncate}.
1946open_truncate(Conf) when is_list(Conf) ->
1947
1948    Dir = ?privdir(Conf),
1949    File = filename:join(Dir, "n.LOG"),
1950    No = 4,
1951    del(File, No),	% cleanup
1952
1953    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1954			     {format, internal},{size, {100, No}}]),
1955    B = mk_bytes(60),
1956    ok = disk_log:log_terms(n, [B, B, B, B]),
1957    ok = disk_log:close(n),
1958    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1959			     {repair,truncate},
1960			     {format, internal},{size, {100, No}}]),
1961    ok = disk_log:close(n),
1962    [] = get_all_terms(n, File, wrap),
1963    del(File, No),
1964    ok.
1965
1966
1967%% Try some invalid open/1 options.
1968open_error(Conf) when is_list(Conf) ->
1969    Dir = ?privdir(Conf),
1970
1971    File = filename:join(Dir, "n.LOG"),
1972    No = 4,
1973    del(File, No),	% cleanup
1974
1975    {error, {badarg, name}} = disk_log:open([{file, File}]),
1976    {error, {badarg, file}} = disk_log:open([{name,{foo,bar}}]),
1977    {error, {badarg, [{foo,bar}]}} = disk_log:open([{foo,bar}]),
1978
1979    %% external logs, read_only.
1980    {error, {file_error, _, enoent}} =
1981	disk_log:open([{name, n}, {file, File}, {type, wrap},
1982		       {size, {100,No}},
1983		       {format, external}, {mode, read_only}]),
1984    Error5 = {error, {file_error, _, enoent}} =
1985	disk_log:open([{name, n}, {file, File}, {type, halt},
1986		       {size, 100},
1987		       {format, external}, {mode, read_only}]),
1988    true = lists:prefix("\"" ++ File, format_error(Error5)),
1989
1990    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
1991			     {format, external},{size, {100, No}}]),
1992    %% Already owner, ignored.
1993    {ok, n} =
1994	disk_log:open([{name, n}, {file, File}, {type, wrap},
1995		       {format, external}, {size, {100, No}}]),
1996    Error2 = {error, {name_already_open, n}} =
1997	disk_log:open([{name, n}, {file, another_file}, {type, wrap},
1998		       {format, external}, {size, {100, No}}]),
1999    "The disk log" ++ _ = format_error(Error2),
2000    Error1 = {error, {arg_mismatch, notify, false, true}} =
2001	disk_log:open([{name, n}, {file, File}, {type, wrap},
2002		       {format, external}, {size, {100, No}}, {notify, true}]),
2003    "The value" ++ _ = format_error(Error1),
2004    Error3 = {error, {open_read_write, n}} =
2005	disk_log:open([{name, n}, {file, File}, {type, wrap},
2006		       {mode, read_only},
2007		       {format, external}, {size, {100, No}}]),
2008    "The disk log" ++ _ = format_error(Error3),
2009    {error, {badarg, size}} =
2010        disk_log:open([{name, n}, {file, File}, {type, halt},
2011                       {format, external}, {size, {100, No}}]),
2012    {error, {arg_mismatch, type, wrap, halt}} =
2013	disk_log:open([{name, n}, {file, File}, {type, halt},
2014		       {format, external}]),
2015    {error, {arg_mismatch, format, external, internal}} =
2016	disk_log:open([{name, n}, {file, File}, {type, wrap},
2017		       {format, internal}, {size, {100, No}}]),
2018    {error, {arg_mismatch, repair, true, false}} =
2019	disk_log:open([{name, n}, {file, File}, {type, wrap},
2020		       {format, external}, {repair, false}]),
2021    {error, {size_mismatch, {100,4}, {1000,4}}} =
2022	disk_log:open([{name, n}, {file, File}, {type, wrap},
2023		       {format, external}, {size, {1000, No}}]),
2024    {error, {arg_mismatch, head, none, _}} =
2025	disk_log:open([{name, n}, {file, File}, {type, wrap},
2026		       {head, "header"},
2027		       {format, external}, {size, {100, No}}]),
2028    {error, {badarg, size}} =
2029	disk_log:open([{name, n}, {file, File}, {type, wrap},
2030		       {format, external}, {size, 100}]),
2031
2032    ok = disk_log:close(n),
2033
2034    {ok, n} =
2035	disk_log:open([{name, n}, {file, File}, {type, wrap},
2036		       {mode, read_only},
2037		       {format, external}, {size, {100, No}}]),
2038    Error4 = {error, {open_read_only, n}} =
2039	disk_log:open([{name, n}, {file, File}, {type, wrap},
2040		       {mode, read_write},
2041		       {format, external}, {size, {100, No}}]),
2042    "The disk log" ++ _ = format_error(Error4),
2043    ok = disk_log:close(n),
2044
2045    del(File, No).
2046
2047
2048%% Do something quickly after close/1.
2049close_race(Conf) when is_list(Conf) ->
2050    Dir = ?privdir(Conf),
2051    File = filename:join(Dir, "n.LOG"),
2052    No = 1,
2053    del(File, No),  % cleanup
2054    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2055			     {size, {100,No}}, {notify, true},
2056			     {format, internal}]),
2057    ok = disk_log:close(n),
2058    Error1 = {error, no_such_log} = disk_log:close(n),
2059    "There is no disk" ++ _ = format_error(Error1),
2060
2061    %% Pid1 blocks, Pid2 closes without being suspended.
2062    Pid1 = spawn_link(?MODULE, lserv, [n]),
2063    Pid2 = spawn_link(?MODULE, lserv, [n]),
2064    {ok, n} = sync_do(Pid1, {open, File}),
2065    {ok, n} = sync_do(Pid2, {open, File}),
2066    ok = sync_do(Pid1, block),
2067    [{_, false}, {_, false}] = sync_do(Pid1, owners),
2068    ok = sync_do(Pid2, close),
2069    [{_, false}] = sync_do(Pid1, owners),
2070    ok = sync_do(Pid1, close),
2071    sync_do(Pid1, terminate),
2072    sync_do(Pid2, terminate),
2073    {error, no_such_log} = disk_log:info(n),
2074
2075    %% Pid3 blocks, Pid3 closes. Pid4 should still be ablo to use log.
2076    Pid3 = spawn_link(?MODULE, lserv, [n]),
2077    Pid4 = spawn_link(?MODULE, lserv, [n]),
2078    {ok, n} = sync_do(Pid3, {open, File}),
2079    {ok, n} = sync_do(Pid4, {open, File}),
2080    ok = sync_do(Pid3, block),
2081    ok = sync_do(Pid3, close),
2082    [{_Pid4, false}] = sync_do(Pid4, owners),
2083    sync_do(Pid3, terminate),
2084    sync_do(Pid4, terminate),
2085    {error, no_such_log} = disk_log:info(n),
2086
2087    %% Pid5 blocks, Pid5 terminates. Pid6 should still be ablo to use log.
2088    Pid5 = spawn_link(?MODULE, lserv, [n]),
2089    Pid6 = spawn_link(?MODULE, lserv, [n]),
2090    {ok, n} = sync_do(Pid5, {open, File}),
2091    {ok, n} = sync_do(Pid6, {open, File}),
2092    ok = sync_do(Pid5, block),
2093    sync_do(Pid5, terminate),
2094    [{_Pid6, false}] = sync_do(Pid6, owners),
2095    sync_do(Pid6, terminate),
2096    {error, no_such_log} = disk_log:info(n),
2097    del(File, No),  % cleanup
2098    ok.
2099
2100%% Block, unblock, close, terminate.
2101close_block(Conf) when is_list(Conf) ->
2102
2103    Dir = ?privdir(Conf),
2104    File = filename:join(Dir, "n.LOG"),
2105    No = 1,
2106    del(File, No),	% cleanup
2107
2108    P0 = pps(),
2109    %% One of two owners terminates.
2110    Pid1 = spawn_link(?MODULE, lserv, [n]),
2111    Pid2 = spawn_link(?MODULE, lserv, [n]),
2112    {ok, n} = sync_do(Pid1, {open, File}),
2113    {ok, n} = sync_do(Pid2, {open, File}),
2114    [_, _] = sync_do(Pid1, owners),
2115    [_, _] = sync_do(Pid2, owners),
2116    0 = sync_do(Pid1, users),
2117    0 = sync_do(Pid2, users),
2118    sync_do(Pid1, terminate),
2119    [_] = sync_do(Pid2, owners),
2120    0 = sync_do(Pid2, users),
2121    sync_do(Pid2, terminate),
2122    {error, no_such_log} = disk_log:info(n),
2123    check_pps(P0),
2124
2125    %% Users terminate (no link...).
2126    Pid3 = spawn_link(?MODULE, lserv, [n]),
2127    Pid4 = spawn_link(?MODULE, lserv, [n]),
2128    {ok, n} = sync_do(Pid3, {open, File, none}),
2129    {ok, n} = sync_do(Pid4, {open, File, none}),
2130    [] = sync_do(Pid3, owners),
2131    [] = sync_do(Pid4, owners),
2132    2 = sync_do(Pid3, users),
2133    2 = sync_do(Pid4, users),
2134    sync_do(Pid3, terminate),
2135    [] = sync_do(Pid4, owners),
2136    2 = sync_do(Pid4, users),
2137    sync_do(Pid4, terminate),
2138    disk_log:close(n),
2139    disk_log:close(n),
2140    {error, no_such_log} = disk_log:info(n),
2141    check_pps(P0),
2142
2143    %% Blocking owner terminates.
2144    Pid5 = spawn_link(?MODULE, lserv, [n]),
2145    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2146			     {linkto, none},{size, {100,No}},
2147			     {format, external}]),
2148    {ok, n} = sync_do(Pid5, {open, File}),
2149    ok = sync_do(Pid5, block),
2150    {blocked, true} = status(n),
2151    [_] = owners(n),
2152    sync_do(Pid5, terminate),
2153    ok = status(n),
2154    [] = owners(n),
2155    1 = users(n),
2156    ok = disk_log:close(n),
2157    {error, no_such_log} = disk_log:info(n),
2158    check_pps(P0),
2159
2160    %% Blocking user terminates.
2161    Pid6 = spawn_link(?MODULE, lserv, [n]),
2162    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2163			     {size, {100,No}}, {format, external}]),
2164    {ok, n} = sync_do(Pid6, {open, File, none}),
2165    ok = sync_do(Pid6, block),
2166    {blocked, true} = status(n),
2167    [_] = owners(n),
2168    1 = users(n),
2169    sync_do(Pid6, terminate), % very silently...
2170    ok = status(n),
2171    [_] = owners(n),
2172    1 = users(n),
2173    ok = disk_log:close(n),
2174    [] = owners(n),
2175    1 = users(n),
2176    ok = disk_log:close(n),
2177    {error, no_such_log} = disk_log:info(n),
2178    check_pps(P0),
2179
2180    %% Blocking owner terminates.
2181    Pid7 = spawn_link(?MODULE, lserv, [n]),
2182    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2183			     {linkto, none},
2184			     {size, {100,No}}, {format, external}]),
2185    {ok, n} = sync_do(Pid7, {open, File}),
2186    ok = sync_do(Pid7, block),
2187    {blocked, true} = status(n),
2188    [_] = owners(n),
2189    1 = users(n),
2190    sync_do(Pid7, terminate),
2191    ok = status(n),
2192    [] = owners(n),
2193    1 = users(n),
2194    ok = disk_log:close(n),
2195    {error, no_such_log} = disk_log:info(n),
2196    check_pps(P0),
2197
2198    %% Two owners, the blocking one terminates.
2199    Pid8 = spawn_link(?MODULE, lserv, [n]),
2200    Pid9 = spawn_link(?MODULE, lserv, [n]),
2201    {ok, n} = sync_do(Pid8, {open, File}),
2202    {ok, n} = sync_do(Pid9, {open, File}),
2203    ok = sync_do(Pid8, block),
2204    {blocked, true} = status(n),
2205    sync_do(Pid8, terminate),
2206    ok = status(n),
2207    [_] = sync_do(Pid9, owners),
2208    0 = sync_do(Pid9, users),
2209    sync_do(Pid9, terminate),
2210    {error, no_such_log} = disk_log:info(n),
2211    check_pps(P0),
2212
2213    %% Blocking user closes.
2214    Pid10 = spawn_link(?MODULE, lserv, [n]),
2215    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2216			     {size, {100,No}}, {format, external}]),
2217    {ok, n} = sync_do(Pid10, {open, File, none}),
2218    ok = sync_do(Pid10, block),
2219    {blocked, true} = status(n),
2220    [_] = owners(n),
2221    1 = users(n),
2222    ok = sync_do(Pid10, close),
2223    ok = status(n),
2224    [_] = owners(n),
2225    0 = users(n),
2226    ok = disk_log:close(n),
2227    sync_do(Pid10, terminate),
2228    {error, no_such_log} = disk_log:info(n),
2229    check_pps(P0),
2230
2231    %% Blocking user unblocks and closes.
2232    Pid11 = spawn_link(?MODULE, lserv, [n]),
2233    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2234			     {size, {100,No}}, {format, external}]),
2235    {ok, n} = sync_do(Pid11, {open, File, none}),
2236    ok = sync_do(Pid11, block),
2237    {blocked, true} = status(n),
2238    [_] = owners(n),
2239    1 = users(n),
2240    ok = sync_do(Pid11, unblock),
2241    ok = sync_do(Pid11, close),
2242    ok = status(n),
2243    [_] = owners(n),
2244    0 = users(n),
2245    ok = disk_log:close(n),
2246    {error, no_such_log} = disk_log:info(n),
2247    sync_do(Pid11, terminate),
2248    check_pps(P0),
2249
2250    %% Blocking owner closes.
2251    Pid12 = spawn_link(?MODULE, lserv, [n]),
2252    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2253			     {linkto, none},
2254			     {size, {100,No}}, {format, external}]),
2255    {ok, n} = sync_do(Pid12, {open, File}),
2256    ok = sync_do(Pid12, block),
2257    {blocked, true} = status(n),
2258    [_] = owners(n),
2259    1 = users(n),
2260    ok = sync_do(Pid12, close),
2261    ok = status(n),
2262    [] = owners(n),
2263    1 = users(n),
2264    ok = disk_log:close(n),
2265    {error, no_such_log} = disk_log:info(n),
2266    sync_do(Pid12, terminate),
2267    check_pps(P0),
2268
2269    %% Blocking owner unblocks and closes.
2270    Pid13 = spawn_link(?MODULE, lserv, [n]),
2271    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2272			     {linkto, none},
2273			     {size, {100,No}}, {format, external}]),
2274    {ok, n} = sync_do(Pid13, {open, File}),
2275    ok = sync_do(Pid13, block),
2276    {blocked, true} = status(n),
2277    [_] = owners(n),
2278    1 = users(n),
2279    ok = sync_do(Pid13, unblock),
2280    ok = sync_do(Pid13, close),
2281    ok = status(n),
2282    [] = owners(n),
2283    1 = users(n),
2284    ok = disk_log:close(n),
2285    {error, no_such_log} = disk_log:info(n),
2286    sync_do(Pid13, terminate),
2287    check_pps(P0),
2288
2289    del(File, No),	% cleanup
2290    ok.
2291
2292%% OTP-4745. Deadlock with just an ordinary log could happen.
2293close_deadlock(Conf) when is_list(Conf) ->
2294    true = is_alive(),
2295
2296    PrivDir = ?privdir(Conf),
2297
2298    F1 = filename:join(PrivDir, "a.LOG"),
2299    file:delete(F1),
2300    Self = self(),
2301
2302    %% One process opens the log at the same time as another process
2303    %% closes the log. Used to always cause deadlock before OTP-4745.
2304    Name = a,
2305    Fun = fun() -> open_close(Self, Name, F1) end,
2306    P = spawn(Fun),
2307    receive {P, Name} -> ok end,
2308    {ok, L} = disk_log:open([{name,Name},{file,F1}]),
2309    ok = disk_log:close(L),
2310    receive {P, done} -> ok end,
2311    file:delete(F1),
2312
2313    %% One process opens the log at the same time as another process
2314    %% closes the log due to file error while truncating.
2315    %% This test is time dependent, but does not fail when it does not
2316    %% "work". When it works, as it seems to do right now :), the
2317    %% disk_log_server gets {error, no_such_log}, receives the EXIT
2318    %% message caused by truncate, and tries to open the log again.
2319    No = 4,
2320    LDir = F1 ++ ".2",
2321    file:del_dir(LDir),
2322    del(F1, No),
2323    ok = file:make_dir(LDir),
2324    Fun2 = fun() -> open_truncate(Self, Name, F1, No) end,
2325    P2 = spawn(Fun2),
2326    receive {P2, Name} -> ok end,
2327    {ok, L} = disk_log:open([{name, Name}, {file, F1}, {type, wrap},
2328			     {format, external}]),
2329    %% Note: truncate causes the disk log process to terminate. One
2330    %% cannot say if open above happened before, after, or during the
2331    %% termination. The link to the owner is removed before termination.
2332    case disk_log:close(L) of
2333	ok -> ok;
2334	{error,no_such_log} ->
2335	    ok
2336    end,
2337    receive {P2, done} -> ok end,
2338    del(F1, No),
2339    file:del_dir(LDir),
2340
2341    %% To the same thing, this time using distributed logs.
2342    %% (Does not seem to work very well, unfortunately.)
2343    FunD = fun() -> open_close_dist(Self, Name, F1) end,
2344    PD = spawn(FunD),
2345    receive {PD, Name} -> ok end,
2346    {[_], []} = disk_log:open([{name,Name},{file,F1},
2347			       {distributed,[node()]}]),
2348    ok = disk_log:close(L),
2349    receive {PD, done} -> ok end,
2350    file:delete(F1),
2351
2352    ok.
2353
2354open_close(Pid, Name, File) ->
2355    {ok, L} = disk_log:open([{name,Name},{file,File}]),
2356    Pid ! {self(), Name},
2357    ok = disk_log:close(L),
2358    Pid ! {self(), done}.
2359
2360open_truncate(Pid, Name, File, No) ->
2361    {ok, L} = disk_log:open([{name, Name}, {file, File}, {type, wrap},
2362                             {format, external},{size, {100, No}}]),
2363    Pid ! {self(), Name},
2364    {error, {file_error, _, _}} = disk_log:truncate(L),
2365    %% The file has been closed, the disklog process has terminated.
2366    Pid ! {self(), done}.
2367
2368open_close_dist(Pid, Name, File) ->
2369    {[{_,{ok,L}}], []} = disk_log:open([{name,Name},{file,File},
2370                                        {distributed,[node()]}]),
2371    Pid ! {self(), Name},
2372    ok = disk_log:close(L),
2373    Pid ! {self(), done}.
2374
2375async_do(Pid, Req) ->
2376    Pid ! {self(), Req},
2377    %% make sure the request is queued
2378    timer:sleep(100).
2379
2380get_reply() ->
2381    receive Reply ->
2382	    Reply
2383    end.
2384
2385sync_do(Pid, Req) ->
2386    Pid ! {self(), Req},
2387    receive
2388        Reply when Req =:= terminate ->
2389            timer:sleep(500),
2390            Reply;
2391	Reply ->
2392	    Reply
2393    end.
2394
2395lserv(Log) ->
2396    receive
2397	{From, {open, File}} ->
2398	    From ! disk_log:open([{name, Log}, {file, File}, {type, wrap},
2399				  {size, {100,1}}, {format, external}]);
2400	{From, {open, File, LinkTo}} ->
2401	    From ! disk_log:open([{name, Log}, {file, File}, {type, wrap},
2402				  {linkto, LinkTo}, {size, {100,1}},
2403				  {format, external}]);
2404	{From, {int_open, File}} ->
2405	    From ! disk_log:open([{name, Log}, {file, File}, {type, wrap},
2406				  {size, {100,1}}]);
2407	{From, {int_open, File, Size}} ->
2408	    From ! disk_log:open([{name, Log}, {file, File}, {type, wrap},
2409				  {size, Size}]);
2410	{From, {dist_open, File, Node}} ->
2411	    From ! disk_log:open([{name, Log}, {file, File}, {type, wrap},
2412				  {size, {100,1}}, {distributed, [Node]}]);
2413	{From, {dist_open, File, LinkTo, Node}} ->
2414	    From ! disk_log:open([{name, Log}, {file, File}, {type, wrap},
2415				  {linkto, LinkTo}, {size, {100,1}},
2416				  {distributed, [Node]}]);
2417	{From, block} ->
2418	    From ! disk_log:block(Log);
2419	{From, {block, Bool}} ->
2420	    From ! disk_log:block(Log, Bool);
2421	{From, unblock} ->
2422	    From ! disk_log:unblock(Log);
2423	{From, close} ->
2424	    From ! disk_log:close(Log);
2425	{From, owners} ->
2426	    From ! owners(Log);
2427	{From, users} ->
2428	    From ! users(Log);
2429	{From, sync} ->
2430	    From ! disk_log:sync(Log);
2431	{From, truncate} ->
2432	    From ! disk_log:truncate(Log);
2433	{From, terminate} ->
2434	    From ! terminated,
2435	    exit(normal);
2436	{From, {log, B}} ->
2437	    From ! disk_log:log(Log, B);
2438	{From, {blog, B}} ->
2439	    From ! disk_log:blog(Log, B);
2440	{From, {alog, B}} ->
2441	    From ! disk_log:alog(Log, B);
2442	{From, {balog, B}} ->
2443	    From ! disk_log:balog(Log, B);
2444	{From, {change_notify, Pid, Bool}} ->
2445	    From ! disk_log:change_notify(Log, Pid, Bool);
2446	{From, {change_header, Header}} ->
2447	    From ! disk_log:change_header(Log, Header);
2448	{From, {change_size, Size}} ->
2449	    From ! disk_log:change_size(Log, Size);
2450	{From, inc_wrap_file} ->
2451	    From ! disk_log:inc_wrap_file(Log);
2452	{From, {chunk, Cont}} ->
2453	    From ! disk_log:chunk(Log, Cont);
2454	{From, {chunk_step, Cont, N}} ->
2455	    From ! disk_log:chunk_step(Log, Cont, N);
2456	Any ->
2457	    io:format("invalid request ~p~n", [Any]),
2458	    exit(abnormal)
2459    end,
2460    lserv(Log).
2461
2462
2463%% Error while repairing.
2464error_repair(Conf) when is_list(Conf) ->
2465    %% not all error situations are covered by this test
2466
2467    DataDir = ?datadir(Conf),
2468    PrivDir = ?privdir(Conf),
2469
2470    File = filename:join(PrivDir, "n.LOG"),
2471    No = 4,
2472    file:delete(File),
2473    del(File, No),	% cleanup
2474
2475    %% kurt.LOG is not closed and has four logged items, one is recovered
2476    copy_wrap_log("kurt.LOG", "n.LOG", No, DataDir, PrivDir),
2477    {repaired,n,{recovered,1},{badbytes,0}} =
2478	disk_log:open([{name, n}, {file, File}, {type, wrap}, {size,{40,No}}]),
2479    1 = cur_cnt(n),
2480    53 = curb(n),
2481    4 = no_items(n),
2482    ok = disk_log:close(n),
2483
2484    %% temporary repair file cannot be created
2485    copy_wrap_log("kurt.LOG", "n.LOG", No, DataDir, PrivDir),
2486    Dir = File ++ ".4" ++ ".TMP",
2487    ok = file:make_dir(Dir),
2488    P0 = pps(),
2489    {error, {file_error, _, _}} =
2490	disk_log:open([{name, n}, {file, File}, {type, wrap}, {size,{40,4}}]),
2491    check_pps(P0),
2492    del(File, No),
2493    ok = file:del_dir(Dir),
2494
2495    error_logger:add_report_handler(?MODULE, self()),
2496    %% repair a file
2497    P1 = pps(),
2498    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2499			     {format, internal}, {size, {40,No}}]),
2500    ok = disk_log:log_terms(n, [{this,is}]), % first file full
2501    ok = disk_log:log_terms(n, [{some,terms}]), % second file full
2502    ok = disk_log:close(n),
2503    BadFile = add_ext(File, 2), % current file
2504    set_opened(BadFile),
2505    crash(BadFile, 28), % the binary is now invalid
2506    {repaired,n,{recovered,0},{badbytes,26}} =
2507	disk_log:open([{name, n}, {file, File}, {type, wrap},
2508		       {format, internal}, {size, {40,No}}]),
2509    ok = disk_log:close(n),
2510    check_pps(P1),
2511    del(File, No),
2512    receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
2513    after 1000 -> ct:fail(failed) end,
2514
2515    %% yet another repair
2516    P2 = pps(),
2517    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2518			     {format, internal}, {size, {4000,No}}]),
2519    ok = disk_log:log_terms(n, [{this,is},{some,terms}]),
2520    ok = disk_log:close(n),
2521    BadFile2 = add_ext(File, 1), % current file
2522    set_opened(BadFile2),
2523    crash(BadFile2, 51), % the second binary is now invalid
2524    {repaired,n,{recovered,1},{badbytes,26}} =
2525	disk_log:open([{name, n}, {file, File}, {type, wrap},
2526		       {format, internal}, {size, {4000,No}}]),
2527    ok = disk_log:close(n),
2528    check_pps(P2),
2529    del(File, No),
2530    receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
2531    after 1000 -> ct:fail(failed) end,
2532
2533    %% Repair, large term
2534    Big = term_to_binary(lists:duplicate(66000,$a)),
2535    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2536			     {format, internal}, {size, {40,No}}]),
2537    ok = disk_log:log_terms(n, [Big]),
2538    ok = disk_log:close(n),
2539    set_opened(add_ext(File, 1)),
2540    {repaired,n,{recovered,1},{badbytes,0}} =
2541	disk_log:open([{name, n}, {file, File}, {type, wrap},
2542		       {format, internal}, {size, {40,No}}]),
2543    {_, [Got]} = disk_log:chunk(n, start),
2544    ok = disk_log:close(n),
2545    Got = Big,
2546    del(File, No),
2547    receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
2548    after 1000 -> ct:fail(failed) end,
2549
2550    %% A term a little smaller than a chunk, then big terms.
2551    BigSmall = mk_bytes(1024*64-8-12),
2552    file:delete(File),
2553    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2554			     {format, internal}]),
2555    ok = disk_log:log_terms(n, [BigSmall, Big, Big]),
2556    ok = disk_log:close(n),
2557    set_opened(File),
2558    FileSize = file_size(File),
2559    crash(File, FileSize-byte_size(Big)-4),
2560    Error1 = {error, {need_repair, _}} =
2561        disk_log:open([{name, n}, {file, File}, {repair, false},
2562                       {type, halt}, {format, internal}]),
2563    "The disk log" ++ _ = format_error(Error1),
2564    {repaired,n,{recovered,2},{badbytes,132013}} =
2565        disk_log:open([{name, n}, {file, File}, {repair, true},
2566                       {type, halt}, {format, internal}]),
2567    ok = disk_log:close(n),
2568    file:delete(File),
2569    receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
2570    after 1000 -> ct:fail(failed) end,
2571
2572    %% The header is recovered.
2573    {ok,n} =
2574	disk_log:open([{name, n}, {file, File}, {type, halt},
2575		       {format, internal},
2576		       {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]),
2577    ok = disk_log:log_terms(n, [list,'of',terms]),
2578    ["head",list,'of',terms] = get_all_terms(n),
2579    ok = disk_log:close(n),
2580    set_opened(File),
2581    crash(File, 30),
2582    {repaired,n,{recovered,3},{badbytes,16}} =
2583        disk_log:open([{name, n}, {file, File}, {type, halt},
2584		       {format, internal},{repair,true}, {quiet, true},
2585		       {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]),
2586    ["head",'of',terms] = get_all_terms(n),
2587    ok = disk_log:close(n),
2588    error_logger:delete_report_handler(?MODULE),
2589    file:delete(File),
2590    {messages, []} = process_info(self(), messages),
2591
2592    ok.
2593
2594set_opened(File) ->
2595    {ok, Fd} = file:open(File, [raw, binary, read, write]),
2596    ok = file:write(Fd, [?LOGMAGIC, ?OPENED]),
2597    ok = file:close(Fd).
2598
2599%% Error while repairing.
2600error_log(Conf) when is_list(Conf) ->
2601    Dir = ?privdir(Conf),
2602
2603    File = filename:join(Dir, "n.LOG"),
2604    No = 4,
2605    file:delete(File),
2606    del(File, No),	% cleanup
2607    LDir = File ++ ".2",
2608
2609    Q = qlen(),
2610    %% dummy just to get all processes "above" disk_log going
2611    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2612			     {format, external},{size, {100, No}}]),
2613    ok = disk_log:close(n),
2614    del(File, No),
2615
2616    %% inc_wrap_file fails, the external log is not terminated
2617    P0 = pps(),
2618    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2619			     {format, external},{size, {100, No}}]),
2620    ok = file:make_dir(LDir),
2621    {error, {file_error, _, _}} = disk_log:inc_wrap_file(n),
2622    timer:sleep(500),
2623    ok = disk_log:close(n),
2624    del(File, No),
2625
2626    %% inc_wrap_file fails, the internal log is not terminated, ./File.2/ exists
2627    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2628			     {format, internal},{size, {100, No}}]),
2629    {error, {file_error, _, _}} = disk_log:inc_wrap_file(n),
2630    ok = disk_log:close(n),
2631    del(File, No),
2632
2633    %% truncate fails, the log is terminated, ./File.2/ exists
2634    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2635			     {format, external},{size, {100, No}}]),
2636    {error, {file_error, _, _}} = disk_log:truncate(n),
2637    check_pps(P0),
2638    del(File, No),
2639
2640    %% OTP-4880.
2641    %% reopen (rename) fails, the log is terminated, ./File.2/ exists
2642    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2643			     {format, external},{size, 100000}]),
2644    {error, {file_error, _, eisdir}} = disk_log:reopen(n, LDir),
2645    check_pps(P0),
2646    file:delete(File),
2647
2648    B = mk_bytes(60),
2649
2650    %% OTP-4880. reopen a wrap log, rename fails
2651    File2 = filename:join(Dir, "n.LOG2"),
2652    {ok, n} = disk_log:open([{name, n}, {file, File2}, {type, wrap},
2653			     {format, external},{size, {100, No}}]),
2654    ok = disk_log:blog_terms(n, [B,B,B]),
2655    {error, {file_error, _, eisdir}} = disk_log:reopen(n, File),
2656    {error, no_such_log} = disk_log:close(n),
2657    del(File2, No),
2658    del(File, No),
2659
2660    %% log, external wrap log, ./File.2/ exists
2661    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2662			     {format, external},{size, {100, No}}]),
2663    {error, {file_error, _, _}} = disk_log:blog_terms(n, [B,B,B]),
2664    ok = disk_log:close(n),
2665    del(File, No),
2666
2667    %% log, internal wrap log, ./File.2/ exists
2668    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2669			     {format, internal},{size, {100, No}}]),
2670    {error, {file_error, _, _}} = disk_log:log_terms(n, [B,B,B]),
2671    ok = disk_log:close(n),
2672    del(File, No),
2673
2674    ok = file:del_dir(LDir),
2675
2676    %% can't remove file when changing size
2677    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2678			     {format, internal},{size, {100, No}}]),
2679    ok = disk_log:log_terms(n, [B,B,B,B]),
2680    ok = disk_log:change_size(n, {100, No-2}),
2681    Three = File ++ ".3",
2682    ok = file:delete(Three),
2683    ok = file:make_dir(Three),
2684    {error, {file_error, _, _}} = disk_log:log_terms(n, [B,B,B]),
2685    timer:sleep(500),
2686    ok = disk_log:close(n),
2687    ok = file:del_dir(Three),
2688    del(File, No),
2689    Q = qlen(),
2690    ok.
2691
2692%% Test chunk and chunk_step.
2693chunk(Conf) when is_list(Conf) ->
2694    %% See also halt_ro_crash/1 above.
2695
2696    Dir = ?privdir(Conf),
2697    File = filename:join(Dir, "n.LOG"),
2698    No = 4,
2699    B = mk_bytes(60),
2700    BB = mk_bytes(64000), % 64 kB chunks
2701    del(File, No),% cleanup
2702
2703    %% Make sure chunk_step skips the rest of the binary.
2704    %% OTP-3716. This was a bug...
2705    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2706			     {format, internal}, {size, {50,No}}]),
2707    %% 1, 2 and 3 on file one, 4 on file two.
2708    ok = disk_log:log_terms(n, [1,2,3,4]),
2709    {I1, [1]} = disk_log:chunk(n, start, 1),
2710    [{node,Node}] = disk_log:chunk_info(I1),
2711    Node = node(),
2712    Error1 = {error, {no_continuation, foobar}} =
2713        disk_log:chunk_info(foobar),
2714    "The term" ++ _ = format_error(Error1),
2715    {ok, I2} = disk_log:chunk_step(n, I1, 1),
2716    {error, {badarg, continuation}} = disk_log:chunk_step(n, foobar, 1),
2717    {I3, [4]} = disk_log:chunk(n, I2, 1),
2718    {ok, I4} = disk_log:chunk_step(n, I3, -1),
2719    {_, [1]} = disk_log:chunk(n, I4, 1),
2720    {error, {badarg, continuation}} = disk_log:bchunk(n, 'begin'),
2721    {Ib1, [Bin1,Bin2]} = disk_log:bchunk(n, start, 2),
2722    1 = binary_to_term(Bin1),
2723    2 = binary_to_term(Bin2),
2724    {ok, Ib2} = disk_log:chunk_step(n, Ib1, 1),
2725    {Ib3, [Bin3]} = disk_log:bchunk(n, Ib2, 1),
2726    4 = binary_to_term(Bin3),
2727    {ok, Ib4} = disk_log:chunk_step(n, Ib3, -1),
2728    {_, [Bin4]} = disk_log:bchunk(n, Ib4, 1),
2729    1 = binary_to_term(Bin4),
2730    {Ib5, [Bin1, Bin2, Bin17]} = disk_log:bchunk(n, start),
2731    3 = binary_to_term(Bin17),
2732    {Ib6, [Bin3]} = disk_log:bchunk(n, Ib5, infinity),
2733    eof = disk_log:bchunk(n, Ib6, infinity),
2734    ok = disk_log:close(n),
2735    del(File, No), % cleanup
2736
2737    %% external log, cannot read chunks
2738    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2739			     {format, external}, {size, {100,No}}]),
2740    {error, {badarg, continuation}} = disk_log:chunk(n, 'begin'),
2741    {error, {format_external, n}} = disk_log:chunk(n, start),
2742    Error2 = {error, {not_internal_wrap, n}} =
2743        disk_log:chunk_step(n, start, 1),
2744    "The requested" ++ _ = format_error(Error2),
2745    ok = disk_log:close(n),
2746    del(File, No),
2747
2748    %% wrap, read_write
2749    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2750			     {format, internal}, {size, {100,No}}]),
2751    ok = disk_log:log_terms(n, [B,B,B,B]),
2752    {C1, [_]} = disk_log:chunk(n, start),
2753    {C2, [_]} = disk_log:chunk(n, C1),
2754    {C3, [_]} = disk_log:chunk(n, C2),
2755    {C4, [_]} = disk_log:chunk(n, C3, 1),
2756    eof = disk_log:chunk(n, C4),
2757    {C5, [_]} = disk_log:chunk(n, start),
2758    {ok, C6} = disk_log:chunk_step(n, C5, 1),
2759    {C7, [_]} = disk_log:chunk(n, C6),
2760    {ok, C8} = disk_log:chunk_step(n, C7, 1),
2761    {_, [_]} = disk_log:chunk(n, C8),
2762    ok = disk_log:close(n),
2763
2764    %% wrap, read_only
2765    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2766			     {mode, read_only},
2767			     {format, internal}, {size, {100,No}}]),
2768    {CC1, [_]} = disk_log:chunk(n, start),
2769    {CC2, [_]} = disk_log:chunk(n, CC1),
2770    {CC3, [_]} = disk_log:chunk(n, CC2),
2771    {CC4, [_]} = disk_log:chunk(n, CC3, 1),
2772    eof = disk_log:chunk(n, CC4),
2773    {CC5, [_]} = disk_log:chunk(n, start),
2774    {ok, CC6} = disk_log:chunk_step(n, CC5, 1),
2775    {CC7, [_]} = disk_log:chunk(n, CC6),
2776    {ok, CC8} = disk_log:chunk_step(n, CC7, 1),
2777    {_, [_]} = disk_log:chunk(n, CC8),
2778    ok = disk_log:close(n),
2779
2780    %% OTP-3716. A bug: {Error, List} and {Error, List, Bad} could be
2781    %% returned from chunk/2.
2782    %% Magic bytes not OK.
2783    %% File header (8 bytes) OK, item header not OK.
2784    InvalidFile = add_ext(File, 1),
2785    crash(InvalidFile, 15),
2786    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2787			     {mode, read_only},
2788			     {format, internal}, {size, {100,No}}]),
2789    {_, [], 61} = disk_log:chunk(n, start),
2790    ok = disk_log:close(n),
2791    %% read_write...
2792    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2793			     {format, internal}, {size, {100,No}}]),
2794    Error3 = {error, {corrupt_log_file, Culprit}} =
2795        disk_log:chunk(n, start),
2796    "The disk log file" ++ _ = format_error(Error3),
2797    Culprit = InvalidFile,
2798    ok = disk_log:close(n),
2799    del(File, No),
2800
2801    %% Two wrap log files, writing the second one, then reading the first
2802    %% one, where a bogus term resides.
2803    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2804			     {format, internal}, {size, {40,No}}]),
2805    ok = disk_log:log_terms(n, [{this,is}]), % first file full
2806    ok = disk_log:log_terms(n, [{some,terms}]), % second file full
2807    2 = curf(n),
2808    BadFile = add_ext(File, 1),
2809    crash(BadFile, 28), % the _binary_ is now invalid
2810    {error, {corrupt_log_file, BFile}} = disk_log:chunk(n, start, 1),
2811    BadFile = BFile,
2812    ok = disk_log:close(n),
2813    %% The same, with a halt log.
2814    file:delete(File), % cleanup
2815    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2816			     {format, internal}]),
2817    ok = disk_log:log_terms(n, [{this,is}]),
2818    ok = disk_log:sync(n),
2819    crash(File, 28), % the _binary_ is now invalid
2820    {error, {corrupt_log_file, File2}} = disk_log:chunk(n, start, 1),
2821    crash(File, 10),
2822    {error,{corrupt_log_file,_}} = disk_log:bchunk(n, start, 1),
2823    true = File == File2,
2824    ok = disk_log:close(n),
2825    del(File, No),
2826
2827    %% halt, read_write
2828    file:delete(File), % cleanup
2829    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2830			     {format, internal}]),
2831    ok = disk_log:log_terms(n, [BB,BB,BB,BB]),
2832    {D1, [Ch1]} = disk_log:chunk(n, start, 1),
2833    Ch1 = BB,
2834    {D2, [Ch2]} = disk_log:chunk(n, D1, 1),
2835    Ch2 = BB,
2836    {D3, [Ch3]} = disk_log:chunk(n, D2, 1),
2837    Ch3 = BB,
2838    {D4, [Ch4]} = disk_log:chunk(n, D3, 1),
2839    Ch4 = BB,
2840    eof = disk_log:chunk(n, D4),
2841    ok = disk_log:close(n),
2842
2843    %% halt, read_only
2844    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2845			     {format, internal},{mode,read_only}]),
2846    {E1, [Ch5]} = disk_log:chunk(n, start, 1),
2847    Ch5 = BB,
2848    {E2, [Ch6]} = disk_log:chunk(n, E1, 1),
2849    Ch6 = BB,
2850    {E3, [Ch7]} = disk_log:chunk(n, E2, 1),
2851    Ch7 = BB,
2852    {E4, [Ch8]} = disk_log:chunk(n, E3, 1),
2853    Ch8 = BB,
2854    eof = disk_log:chunk(n, E4),
2855    ok = disk_log:close(n),
2856    file:delete(File), % cleanup
2857
2858    %% More than 64 kB term.
2859    BBB = term_to_binary(lists:duplicate(66000,$a)),
2860    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2861			     {format, internal}]),
2862    ok = disk_log:log_terms(n, [BBB]),
2863    {F1, [BBB1]} = disk_log:chunk(n, start),
2864    BBB1 = BBB,
2865    eof = disk_log:chunk(n, F1),
2866    ok = disk_log:close(n),
2867    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2868			     {format, internal}, {mode, read_only}]),
2869    {F1r, [BBB2]} = disk_log:chunk(n, start),
2870    BBB2 = BBB,
2871    eof = disk_log:chunk(n, F1r),
2872    ok = disk_log:close(n),
2873
2874    truncate(File, 8192),
2875    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2876			     {format, internal}]),
2877    {error, {corrupt_log_file, _}} = disk_log:chunk(n, start),
2878    ok = disk_log:close(n),
2879    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2880			     {format, internal}, {mode, read_only}]),
2881    {K1, [], 8176} = disk_log:chunk(n, start),
2882    eof = disk_log:chunk(n, K1),
2883    ok = disk_log:close(n),
2884    file:delete(File), % cleanup
2885
2886    %% OTP-3716. A bug: eof in the middle of the last element is not ok.
2887    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2888			     {format, internal}]),
2889    ok = disk_log:log_terms(n, [B,BB]),
2890    ok = disk_log:close(n),
2891    truncate(File, 80),
2892    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2893			     {format, internal}]),
2894    {G1, [_]} = disk_log:chunk(n, start, 1),
2895    {error, {corrupt_log_file, _}} = disk_log:chunk(n, G1, 1),
2896    ok = disk_log:close(n),
2897    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2898			     {format, internal}, {mode, read_only}]),
2899    {G1r, [_]} = disk_log:chunk(n, start, 1),
2900    {_, [], 4} = disk_log:chunk(n, G1r, 1),
2901    ok = disk_log:close(n),
2902    file:delete(File), % cleanup
2903
2904    %% Opening a wrap log read-only. The second of four terms is destroyed.
2905    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2906			     {format, internal}, {size, {4000,No}}]),
2907    ok = disk_log:log_terms(n,
2908			    [{this,is},{some,terms},{on,a},{wrap,file}]),
2909    ok = disk_log:close(n),
2910    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
2911			     {format, internal}, {mode, read_only}]),
2912    CrashFile = add_ext(File, 1),
2913    crash(CrashFile, 51), % the binary term {some,terms} is now bad
2914    {H1, [{this,is}], 18} = disk_log:chunk(n, start, 10),
2915    {H2, [{on,a},{wrap,file}]} = disk_log:chunk(n, H1),
2916    eof = disk_log:chunk(n, H2),
2917    ok = disk_log:close(n),
2918    del(File, No),
2919
2920    %% The same as last, but with a halt log.
2921    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2922			     {format, internal}, {mode, read_write}]),
2923    ok = disk_log:alog_terms(n, [{this,is},{some,terms}]),
2924    ok = disk_log:log_terms(n, [{on,a},{halt,file}]),
2925    ok = disk_log:close(n),
2926    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2927			     {format, internal}, {mode, read_only}]),
2928    crash(File, 51), % the binary term {some,terms} is now bad
2929    {J1, [{this,is}], 18} = disk_log:chunk(n, start, 10),
2930    {J2, [{on,a},{halt,file}]} = disk_log:chunk(n, J1),
2931    eof = disk_log:chunk(n, J2),
2932    ok = disk_log:close(n),
2933    file:delete(File),
2934
2935    %% OTP-7641. Same as last one, but the size of the bad term is
2936    %% less than ?HEADERSz (8) bytes.
2937    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2938			     {format, internal}, {mode, read_write}]),
2939    ok = disk_log:alog_terms(n, [{this,is},{s}]),
2940    ok = disk_log:log_terms(n, [{on,a},{halt,file}]),
2941    ok = disk_log:close(n),
2942    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2943			     {format, internal}, {mode, read_only}]),
2944    crash(File, 44), % the binary term {s} is now bad
2945    {J11, [{this,is}], 7} = disk_log:chunk(n, start, 10),
2946    {J21, [{on,a},{halt,file}]} = disk_log:chunk(n, J11),
2947    eof = disk_log:chunk(n, J21),
2948    ok = disk_log:close(n),
2949    file:delete(File),
2950
2951    %% Minimal MD5-proctected term, and maximal unprotected term.
2952    %% A chunk ends in the middle of the MD5-sum.
2953    MD5term = mk_bytes(64*1024-8),
2954    NotMD5term = mk_bytes((64*1024-8)-1),
2955    Term2 = mk_bytes((64*1024-8)-16),
2956    MD5L = [MD5term,NotMD5term,Term2,MD5term,MD5term,NotMD5term],
2957    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2958			     {format, internal}]),
2959    ok = disk_log:log_terms(n, MD5L),
2960    true = MD5L == get_all_terms(n),
2961    ok = disk_log:close(n),
2962    true = MD5L == get_all_terms(n, File, halt),
2963    crash(File, 21), % the MD5-sum of the first term is now bad
2964    true = {tl(MD5L),64*1024-8} == get_all_terms_and_bad(n, File, halt),
2965    {_,64*1024-8} = get_all_binary_terms_and_bad(n, File, halt),
2966    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt},
2967			     {format, internal}]),
2968    {error, {corrupt_log_file, _}} = disk_log:chunk(n, start),
2969    ok = disk_log:close(n),
2970    file:delete(File),
2971
2972    %% A file with "old" terms (magic word is MAGICINT).
2973    DataDir = ?datadir(Conf),
2974    OldTermsFileOrig = filename:join(DataDir, "old_terms.LOG"),
2975    OldTermsFile = filename:join(Dir, "old_terms.LOG"),
2976    copy_file(OldTermsFileOrig, OldTermsFile),
2977    {[_,_,_,_],0} = get_all_terms_and_bad(n, OldTermsFile, halt),
2978    {ok, n} = disk_log:open([{name, n}, {file, OldTermsFile},
2979			     {type, halt}, {format, internal}]),
2980    [_,_,_,_] = get_all_terms(n),
2981    ok = disk_log:close(n),
2982    file:delete(OldTermsFile),
2983
2984    ok.
2985
2986%% OTP-5558. Keep the contents of index files after disk crash.
2987error_index(Conf) when is_list(Conf) ->
2988    Dir = ?privdir(Conf),
2989
2990    File = filename:join(Dir, "n.LOG"),
2991    IdxFile = File ++ ".idx",
2992    No = 4,
2993    file:delete(File),
2994    del(File, No),	% cleanup
2995
2996    Args = [{name,n},{type,wrap},{size,{100,No}},{file,File}],
2997    {ok, n} = disk_log:open(Args),
2998    ok = disk_log:close(n),
2999    Q = qlen(),
3000    P0 = pps(),
3001    ok = file:write_file(IdxFile, <<"abc">>),
3002    {error, {invalid_index_file, _}} = disk_log:open(Args),
3003    {error, {invalid_index_file, _}} = disk_log:open(Args),
3004    {error, {invalid_index_file, _}} = disk_log:open(Args),
3005
3006    del(File, No),
3007    check_pps(P0),
3008    true = (Q == qlen()),
3009    ok.
3010
3011%% Test truncate/1 on halt and wrap logs.
3012truncate(Conf) when is_list(Conf) ->
3013    Dir = ?privdir(Conf),
3014
3015    Q = qlen(),
3016    Halt = join(Dir, "halt.LOG"),
3017    %% Halt logs.
3018
3019    file:delete(Halt), % cleanup
3020    {ok, halt} = disk_log:open([{name, halt}, {type, halt}, {file, Halt},
3021				{head, header}, {notify, true}]),
3022    infinity = sz(halt),
3023    ok = disk_log:truncate(halt, tjohej),
3024    rec(1, {disk_log, node(), halt, {truncated, 1}}),
3025    ok = disk_log:change_size(halt, 10000),
3026    10000 = sz(halt),
3027    disk_log:close(halt),
3028    [tjohej] = get_all_terms(halt, Halt, halt),
3029    file:delete(Halt),
3030
3031    {ok, halt} = disk_log:open([{name, halt}, {type, halt}, {file, Halt},
3032				{head, header}, {notify, true}]),
3033    ok = disk_log:truncate(halt),
3034    rec(1, {disk_log, node(), halt, {truncated, 1}}),
3035    disk_log:close(halt),
3036    [header] = get_all_terms(halt, Halt, halt),
3037    file:delete(Halt),
3038
3039    {ok, halt} = disk_log:open([{name, halt}, {type, halt},
3040				{file, Halt}, {format, external},
3041				{head, "header"}, {notify, false}]),
3042    ok = disk_log:btruncate(halt, "apa"),
3043    disk_log:close(halt),
3044    3 = file_size(Halt),
3045    file:delete(Halt),
3046
3047    %% Wrap logs.
3048    File = filename:join(Dir, "n.LOG"),
3049    No = 4,
3050    B = mk_bytes(60),
3051    del(File, No),	% cleanup
3052
3053    %% Internal with header.
3054    Size = {100, No},
3055    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3056			     {head, header}, {notify, true},
3057			     {size, Size}]),
3058    ok = disk_log:log_terms(n, [B,B,B]),
3059    %% Used to be one message, but now one per wrapped file.
3060    rec(2, {disk_log, node(), n, {wrap, 0}}),
3061    ok = disk_log:truncate(n, apa),
3062    rec(1, {disk_log, node(), n, {truncated, 6}}),
3063    {0, 0} = no_overflows(n),
3064    23 = curb(n),
3065    1 = curf(n),
3066    1 = cur_cnt(n),
3067    true = (Size == sz(n)),
3068
3069    ok = disk_log:log_terms(n, [B, B]),
3070    rec(1, {disk_log, node(), n, {wrap, 0}}),
3071    ok = disk_log:close(n),
3072    [apa, _, header, _] = get_all_terms(n, File, wrap),
3073    del(File, No),
3074
3075    %% Internal without general header.
3076    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3077			     {notify, true},
3078			     {size, {100, No}}]),
3079    ok = disk_log:log_terms(n, [B,B,B]),
3080    rec(2, {disk_log, node(), n, {wrap, 0}}),
3081    ok = disk_log:truncate(n, apa),
3082    rec(1, {disk_log, node(), n, {truncated, 3}}),
3083    {0, 0} = no_overflows(n),
3084    23 = curb(n),
3085    1 = curf(n),
3086    1 = cur_cnt(n),
3087    true = (Size == sz(n)),
3088
3089    ok = disk_log:log_terms(n, [B, B]),
3090    rec(1, {disk_log, node(), n, {wrap, 0}}),
3091    ok = disk_log:close(n),
3092    [apa, _, _] = get_all_terms(n, File, wrap),
3093    del(File, No),
3094
3095    %% Internal without any header.
3096    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3097			     {notify, true},
3098			     {size, {100, No}}]),
3099    ok = disk_log:log_terms(n, [B,B,B]),
3100    rec(2, {disk_log, node(), n, {wrap, 0}}),
3101    ok = disk_log:truncate(n),
3102    rec(1, {disk_log, node(), n, {truncated, 3}}),
3103    {0, 0} = no_overflows(n),
3104    8 = curb(n),
3105    1 = curf(n),
3106    0 = cur_cnt(n),
3107    true = (Size == sz(n)),
3108
3109    ok = disk_log:log_terms(n, [B, B]),
3110    rec(1, {disk_log, node(), n, {wrap, 0}}),
3111    ok = disk_log:close(n),
3112    [_, _] = get_all_terms(n, File, wrap),
3113    del(File, No),
3114    Q = qlen(),
3115    ok.
3116
3117
3118%% Test many users logging and sync:ing at the same time.
3119many_users(Conf) when is_list(Conf) ->
3120    Dir = ?privdir(Conf),
3121    N = 100,
3122    NoClients = 10,
3123    Fun1 = fun(Name, Pid, I) -> disk_log:log(Name, {Pid, I}) end,
3124    Fun2 = fun(Name, Pid, I) -> ok = disk_log:log(Name, {Pid, I}),
3125				disk_log:sync(Name) end,
3126    {C1, T1} = many(Fun2, NoClients, N, halt, internal, infinity, Dir),
3127    true = lists:duplicate(NoClients, ok) == C1,
3128    true = length(T1) == N*NoClients,
3129    {C2, T2} = many(Fun1, NoClients, N, halt, internal, 1000, Dir),
3130    true = lists:duplicate(NoClients, {error, {full,"log.LOG"}}) == C2,
3131    true = length(T2) > 0,
3132    {C3, T3} = many(Fun2, NoClients, N, wrap, internal,
3133		    {300*NoClients,200}, Dir),
3134    true = lists:duplicate(NoClients, ok) == C3,
3135    true = length(T3) == N*NoClients,
3136    ok.
3137
3138many(Fun, NoClients, N, Type, Format, Size, Dir) ->
3139    Name = "log.LOG",
3140    File = filename:join(Dir, Name),
3141    del_files(Size, File),
3142    Q = qlen(),
3143    {ok, _} = disk_log:open([{name,Name}, {type,Type}, {size,Size},
3144			     {format,Format}, {file,File}]),
3145    Pids = spawn_clients(NoClients, client, [self(), Name, N, Fun]),
3146    Checked = check_clients(Pids),
3147    ok = disk_log:close(Name),
3148    Terms = get_all_terms(Name, File, Type),
3149    del_files(Size, File),
3150    Q = qlen(),
3151    {Checked, Terms}.
3152
3153spawn_clients(0, _F, _A) ->
3154    [];
3155spawn_clients(I, F, A) ->
3156    [spawn_link(?MODULE, F, A) | spawn_clients(I-1, F, A)].
3157
3158check_clients(Pids) ->
3159    lists:map(fun(Pid) -> receive {Pid, Reply} -> Reply end end, Pids).
3160
3161client(From, _Name, 0, _Fun) ->
3162    From ! {self(), ok};
3163client(From, Name, N, Fun) ->
3164    %% Fun is called N times.
3165    case Fun(Name, self(), N) of
3166	ok -> client(From, Name, N-1, Fun);
3167	Else -> From ! {self(), Else}
3168    end.
3169
3170del_files({_NoBytes,NoFiles}, File) ->
3171    del(File, NoFiles);
3172del_files(_Size, File) ->
3173    file:delete(File).
3174
3175
3176
3177
3178%% Test no_current_{bytes, items} as returned by info/0.
3179info_current(Conf) when is_list(Conf) ->
3180
3181    Dir = ?privdir(Conf),
3182    File = filename:join(Dir, "n.LOG"),
3183    No = 4,
3184    B = mk_bytes(60),
3185    BB = mk_bytes(160), % bigger than a single wrap log file
3186    SB = mk_bytes(10),  % much smaller than a single wrap log file
3187    del(File, No),% cleanup
3188
3189    Q = qlen(),
3190    %% Internal with header.
3191    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3192			     {head, header}, {size, {100,No}}]),
3193    {26, 1} = {curb(n), cur_cnt(n)},
3194    {1, 1}  = {no_written_items(n), no_items(n)},
3195    ok = disk_log:log(n, B),
3196    {94, 2} = {curb(n), cur_cnt(n)},
3197    {2, 2}  = {no_written_items(n), no_items(n)},
3198    ok = disk_log:close(n),
3199    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3200			     {notify, true},
3201			     {head, header}, {size, {100,No}}]),
3202    {94, 2} = {curb(n), cur_cnt(n)},
3203    {0, 2}  = {no_written_items(n), no_items(n)},
3204    ok = disk_log:log(n, B),
3205    rec(1, {disk_log, node(), n, {wrap, 0}}),
3206    {94, 2} = {curb(n), cur_cnt(n)},
3207    {2, 4}  = {no_written_items(n), no_items(n)},
3208    disk_log:inc_wrap_file(n),
3209    rec(1, {disk_log, node(), n, {wrap, 0}}),
3210    {26, 1} = {curb(n), cur_cnt(n)},
3211    {3, 4}  = {no_written_items(n), no_items(n)},
3212    ok = disk_log:log_terms(n, [B,B,B]),
3213    %% Used to be one message, but now one per wrapped file.
3214    rec(1, {disk_log, node(), n, {wrap, 0}}),
3215    rec(1, {disk_log, node(), n, {wrap, 2}}),
3216    {94, 2} = {curb(n), cur_cnt(n)},
3217    {8, 7}  = {no_written_items(n), no_items(n)},
3218    ok = disk_log:log_terms(n, [B]),
3219    rec(1, {disk_log, node(), n, {wrap, 2}}),
3220    ok = disk_log:log_terms(n, [B]),
3221    rec(1, {disk_log, node(), n, {wrap, 2}}),
3222    {94, 2} = {curb(n), cur_cnt(n)},
3223    {12, 7}  = {no_written_items(n), no_items(n)},
3224    ok = disk_log:log_terms(n, [BB,BB]),
3225    %% Used to be one message, but now one per wrapped file.
3226    rec(2, {disk_log, node(), n, {wrap, 2}}),
3227    {194, 2} = {curb(n), cur_cnt(n)},
3228    {16, 7}  = {no_written_items(n), no_items(n)},
3229    ok = disk_log:log_terms(n, [SB,SB,SB]),
3230    rec(1, {disk_log, node(), n, {wrap, 2}}),
3231    {80, 4} = {curb(n), cur_cnt(n)},
3232    {20, 9}  = {no_written_items(n), no_items(n)},
3233    ok = disk_log:close(n),
3234    del(File, No),
3235
3236    %% Internal without header.
3237    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3238			     {size, {100,No}}]),
3239    {8, 0} = {curb(n), cur_cnt(n)},
3240    {0, 0}  = {no_written_items(n), no_items(n)},
3241    ok = disk_log:log(n, B),
3242    {76, 1} = {curb(n), cur_cnt(n)},
3243    {1, 1}  = {no_written_items(n), no_items(n)},
3244    ok = disk_log:close(n),
3245    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3246			     {notify, true}, {size, {100,No}}]),
3247    {76, 1} = {curb(n), cur_cnt(n)},
3248    {0, 1}  = {no_written_items(n), no_items(n)},
3249    ok = disk_log:log(n, B),
3250    rec(1, {disk_log, node(), n, {wrap, 0}}),
3251    {76, 1} = {curb(n), cur_cnt(n)},
3252    {1, 2}  = {no_written_items(n), no_items(n)},
3253    disk_log:inc_wrap_file(n),
3254    rec(1, {disk_log, node(), n, {wrap, 0}}),
3255    {8, 0} = {curb(n), cur_cnt(n)},
3256    {1, 2}  = {no_written_items(n), no_items(n)},
3257    ok = disk_log:log_terms(n, [B,B,B]),
3258    %% Used to be one message, but now one per wrapped file.
3259    rec(1, {disk_log, node(), n, {wrap, 0}}),
3260    rec(1, {disk_log, node(), n, {wrap, 1}}),
3261    {76, 1} = {curb(n), cur_cnt(n)},
3262    {4, 4}  = {no_written_items(n), no_items(n)},
3263    ok = disk_log:log_terms(n, [B]),
3264    rec(1, {disk_log, node(), n, {wrap, 1}}),
3265    ok = disk_log:log_terms(n, [B]),
3266    rec(1, {disk_log, node(), n, {wrap, 1}}),
3267    {76, 1} = {curb(n), cur_cnt(n)},
3268    {6, 4}  = {no_written_items(n), no_items(n)},
3269    ok = disk_log:log_terms(n, [BB,BB]),
3270    %% Used to be one message, but now one per wrapped file.
3271    rec(2, {disk_log, node(), n, {wrap, 1}}),
3272    {176, 1} = {curb(n), cur_cnt(n)},
3273    {8, 4}  = {no_written_items(n), no_items(n)},
3274    ok = disk_log:log_terms(n, [SB,SB,SB]),
3275    rec(1, {disk_log, node(), n, {wrap, 1}}),
3276    {62, 3} = {curb(n), cur_cnt(n)},
3277    {11, 6}  = {no_written_items(n), no_items(n)},
3278    ok = disk_log:close(n),
3279    del(File, No),
3280
3281    %% External with header.
3282    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3283			     {format, external}, {head, "header"},
3284			     {size, {100,No}}]),
3285    {6, 1} = {curb(n), cur_cnt(n)},
3286    {1, 1}  = {no_written_items(n), no_items(n)},
3287    ok = disk_log:blog(n, B),
3288    {62, 2} = {curb(n), cur_cnt(n)},
3289    {2, 2}  = {no_written_items(n), no_items(n)},
3290    ok = disk_log:close(n),
3291    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3292			     {format, external}, {head, "header"},
3293			     {notify, true}, {size, {100,No}}]),
3294    {62, 2} = {curb(n), cur_cnt(n)},
3295    {0, 2}  = {no_written_items(n), no_items(n)},
3296    ok = disk_log:blog(n, B),
3297    rec(1, {disk_log, node(), n, {wrap, 0}}),
3298    {62, 2} = {curb(n), cur_cnt(n)},
3299    {2, 4}  = {no_written_items(n), no_items(n)},
3300    disk_log:inc_wrap_file(n),
3301    rec(1, {disk_log, node(), n, {wrap, 0}}),
3302    {6, 1} = {curb(n), cur_cnt(n)},
3303    {3, 4}  = {no_written_items(n), no_items(n)},
3304    ok = disk_log:blog_terms(n, [B,B,B]),
3305    %% Used to be one message, but now one per wrapped file.
3306    rec(1, {disk_log, node(), n, {wrap, 0}}),
3307    rec(1, {disk_log, node(), n, {wrap, 2}}),
3308    {62, 2} = {curb(n), cur_cnt(n)},
3309    {8, 7}  = {no_written_items(n), no_items(n)},
3310    ok = disk_log:blog_terms(n, [B]),
3311    rec(1, {disk_log, node(), n, {wrap, 2}}),
3312    ok = disk_log:blog_terms(n, [B]),
3313    rec(1, {disk_log, node(), n, {wrap, 2}}),
3314    {62, 2} = {curb(n), cur_cnt(n)},
3315    {12, 7}  = {no_written_items(n), no_items(n)},
3316    ok = disk_log:blog_terms(n, [BB,BB]),
3317    %% Used to be one message, but now one per wrapped file.
3318    rec(2, {disk_log, node(), n, {wrap, 2}}),
3319    {162, 2} = {curb(n), cur_cnt(n)},
3320    {16, 7}  = {no_written_items(n), no_items(n)},
3321    ok = disk_log:blog_terms(n, [SB,SB,SB]),
3322
3323    rec(1, {disk_log, node(), n, {wrap, 2}}),
3324    {24, 4} = {curb(n), cur_cnt(n)},
3325    {20, 9}  = {no_written_items(n), no_items(n)},
3326    ok = disk_log:close(n),
3327    del(File, No),
3328
3329    %% External without header.
3330    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3331			     {format, external}, {size, {100,No}}]),
3332    {0, 0} = {curb(n), cur_cnt(n)},
3333    {0, 0}  = {no_written_items(n), no_items(n)},
3334    ok = disk_log:blog(n, B),
3335    {56, 1} = {curb(n), cur_cnt(n)},
3336    {1, 1}  = {no_written_items(n), no_items(n)},
3337    ok = disk_log:close(n),
3338    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3339			     {notify, true},
3340			     {format, external}, {size, {100,No}}]),
3341    {56, 1} = {curb(n), cur_cnt(n)},
3342    {0, 1}  = {no_written_items(n), no_items(n)},
3343    ok = disk_log:blog(n, B),
3344    rec(1, {disk_log, node(), n, {wrap, 0}}),
3345    {56, 1} = {curb(n), cur_cnt(n)},
3346    {1, 2}  = {no_written_items(n), no_items(n)},
3347    disk_log:inc_wrap_file(n),
3348    rec(1, {disk_log, node(), n, {wrap, 0}}),
3349    {0, 0} = {curb(n), cur_cnt(n)},
3350    {1, 2}  = {no_written_items(n), no_items(n)},
3351    ok = disk_log:blog_terms(n, [B,B,B]),
3352    %% Used to be one message, but now one per wrapped file.
3353    rec(1, {disk_log, node(), n, {wrap, 0}}),
3354    rec(1, {disk_log, node(), n, {wrap, 1}}),
3355    {56, 1} = {curb(n), cur_cnt(n)},
3356    {4, 4}  = {no_written_items(n), no_items(n)},
3357    ok = disk_log:blog_terms(n, [B]),
3358    rec(1, {disk_log, node(), n, {wrap, 1}}),
3359    ok = disk_log:blog_terms(n, [B]),
3360    rec(1, {disk_log, node(), n, {wrap, 1}}),
3361    {56, 1} = {curb(n), cur_cnt(n)},
3362    {6, 4}  = {no_written_items(n), no_items(n)},
3363    ok = disk_log:blog_terms(n, [BB,BB]),
3364    %% Used to be one message, but now one per wrapped file.
3365    rec(2, {disk_log, node(), n, {wrap, 1}}),
3366    {156, 1} = {curb(n), cur_cnt(n)},
3367    {8, 4}  = {no_written_items(n), no_items(n)},
3368    ok = disk_log:blog_terms(n, [SB,SB,SB]),
3369    rec(1, {disk_log, node(), n, {wrap, 1}}),
3370    {18, 3} = {curb(n), cur_cnt(n)},
3371    {11, 6}  = {no_written_items(n), no_items(n)},
3372    ok = disk_log:close(n),
3373    del(File, No),
3374
3375    Q = qlen(),
3376    ok.
3377
3378
3379
3380change_size_before(doc) ->
3381    ["Change size of a wrap log file before we have reached "
3382     "to the file index corresponding to the new size"];
3383change_size_before(Conf) when is_list(Conf) ->
3384
3385    Log_1_1 = "first log  first message",
3386    Log_1_2 = "first log  second message",
3387    Log_2_1 = "second log  first message",
3388    Log_2_2 = "second log  second message",
3389    Log_3_1 = "third log  first message",
3390    Log_3_2 = "third log  second message",
3391    Log_4_1 = "fourth log  first message",
3392    Log_4_2 = "fourth log  second message",
3393    Log_5_1 = "fifth log  first message",
3394    Log_5_2 = "fifth log  second message",
3395    Log_1_2_1 = "first log  second round 1",
3396    Log_1_2_2 = "first log  second round 2",
3397
3398
3399    Dir = ?privdir(Conf),
3400    File = filename:join(Dir, "a.LOG"),
3401    del(File, 5),
3402    {ok, a} = disk_log:open([{name,a}, {file, File},
3403			     {type, wrap}, {size, {100,5}}]),
3404    disk_log:log(a, Log_1_1),
3405    disk_log:log(a, Log_1_2),
3406    disk_log:log(a, Log_2_1),
3407    disk_log:log(a, Log_2_2),
3408    disk_log:change_size(a, {100, 3}),
3409    [Log_1_1, Log_1_2,
3410     Log_2_1, Log_2_2] = get_all_terms(a),
3411    disk_log:log(a, Log_3_1),
3412    disk_log:log(a, Log_3_2),
3413    disk_log:log(a, Log_1_2_1),
3414    disk_log:log(a, Log_1_2_2),
3415    [Log_2_1, Log_2_2,
3416     Log_3_1, Log_3_2,
3417     Log_1_2_1, Log_1_2_2] = get_all_terms(a),
3418
3419    disk_log:close(a),
3420    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3421			     {size, {100,3}}]),
3422    [Log_2_1, Log_2_2,
3423     Log_3_1, Log_3_2,
3424     Log_1_2_1, Log_1_2_2] = get_all_terms(a),
3425    disk_log:close(a),
3426    del(File, 5),
3427
3428    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3429			     {size, {60,5}}, {format, external}]),
3430    disk_log:blog(a, Log_1_1),
3431    disk_log:blog(a, Log_1_2),
3432    disk_log:blog(a, Log_2_1),
3433    disk_log:blog(a, Log_2_2),
3434    disk_log:change_size(a, {60, 3}),
3435    ok = disk_log:sync(a),
3436    {ok, Fd1} = file:open(File ++ ".1", [read]),
3437    Log11_12 = Log_1_1 ++ Log_1_2,
3438    {ok,Log11_12} = file:read(Fd1, 200),
3439    ok = file:close(Fd1),
3440    {ok, Fd2} = file:open(File ++ ".2", [read]),
3441    Log21_22 = Log_2_1 ++ Log_2_2,
3442    {ok,Log21_22} = file:read(Fd2, 200),
3443    ok = file:close(Fd2),
3444    disk_log:blog(a, Log_3_1),
3445    disk_log:blog(a, Log_3_2),
3446    disk_log:blog(a, Log_1_2_1),
3447    disk_log:blog(a, Log_1_2_2),
3448    ok = disk_log:sync(a),
3449    {ok, Fd2a} = file:open(File ++ ".2", [read]),
3450    {ok,Log21_22} = file:read(Fd2a, 200),
3451    ok = file:close(Fd2a),
3452    {ok, Fd3a} = file:open(File ++ ".3", [read]),
3453    Log31_32 = Log_3_1 ++ Log_3_2,
3454    {ok,Log31_32} = file:read(Fd3a, 200),
3455    ok = file:close(Fd3a),
3456    {ok, Fd1a} = file:open(File ++ ".1", [read]),
3457    Log121_122 = Log_1_2_1 ++ Log_1_2_2,
3458    {ok,Log121_122} = file:read(Fd1a, 200),
3459    ok = file:close(Fd1a),
3460
3461    disk_log:close(a),
3462    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3463			     {size, {60,3}}, {format, external}]),
3464    {ok, Fd2b} = file:open(File ++ ".2", [read]),
3465    {ok,Log21_22} = file:read(Fd2b, 200),
3466    ok = file:close(Fd2b),
3467    {ok, Fd3b} = file:open(File ++ ".3", [read]),
3468    {ok,Log31_32} = file:read(Fd3b, 200),
3469    ok = file:close(Fd3b),
3470    {ok, Fd1b} = file:open(File ++ ".1", [read]),
3471    {ok,Log121_122} = file:read(Fd1b, 200),
3472    ok = file:close(Fd1b),
3473    disk_log:close(a),
3474    del(File, 5),
3475
3476    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]),
3477    disk_log:log(a, Log_1_1),
3478    disk_log:log(a, Log_1_2),
3479    disk_log:log(a, Log_2_1),
3480    disk_log:log(a, Log_2_2),
3481    disk_log:change_size(a, {60, 3}),
3482    [Log_1_1, Log_1_2,
3483     Log_2_1, Log_2_2] = get_all_terms(a),
3484    disk_log:log(a, Log_3_1),
3485    disk_log:log(a, Log_1_2_1),
3486    [Log_2_1, Log_2_2,
3487     Log_3_1,
3488     Log_1_2_1] = get_all_terms(a),
3489
3490    disk_log:close(a),
3491    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {60,3}}]),
3492    [Log_2_1, Log_2_2,
3493     Log_3_1,
3494     Log_1_2_1] = get_all_terms(a),
3495    disk_log:close(a),
3496    del(File, 5),
3497
3498    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {60, 3}}]),
3499    disk_log:log(a, Log_1_1),
3500    disk_log:log(a, Log_2_1),
3501    disk_log:change_size(a, {100, 5}),
3502    [Log_1_1,
3503     Log_2_1] = get_all_terms(a),
3504    disk_log:log(a, Log_2_2),
3505    disk_log:log(a, Log_3_1),
3506    disk_log:log(a, Log_3_2),
3507    disk_log:log(a, Log_4_1),
3508    disk_log:log(a, Log_4_2),
3509    disk_log:log(a, Log_5_1),
3510    disk_log:log(a, Log_5_2),
3511    disk_log:log(a, Log_1_2_1),
3512    [Log_2_1, Log_2_2,
3513     Log_3_1, Log_3_2,
3514     Log_4_1, Log_4_2,
3515     Log_5_1, Log_5_2,
3516     Log_1_2_1] = get_all_terms(a),
3517
3518    disk_log:close(a),
3519    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100, 5}}]),
3520    [Log_2_1, Log_2_2,
3521     Log_3_1, Log_3_2,
3522     Log_4_1, Log_4_2,
3523     Log_5_1, Log_5_2,
3524     Log_1_2_1] = get_all_terms(a),
3525    disk_log:close(a),
3526    del(File, 5).
3527
3528
3529
3530%% Change size of a wrap log file while logging to a file index
3531%% between the old and the new size.
3532change_size_during(Conf) when is_list(Conf) ->
3533
3534    Log_1_1 = "first log  first message",
3535    Log_1_2 = "first log  second message",
3536    Log_2_1 = "second log  first message",
3537    Log_2_2 = "second log  second message",
3538    Log_3_1 = "third log  first message",
3539    Log_3_2 = "third log  second message",
3540    Log_4_1 = "fourth log  first message",
3541    Log_4_2 = "fourth log  second message",
3542    Log_5_1 = "fifth log  first message",
3543    Log_5_2 = "fifth log  second message",
3544    Log_1_2_1 = "first log  second round 1",
3545    Log_1_2_2 = "first log  second round 2",
3546    Log_2_2_1 = "second log  second round 1",
3547    Log_2_2_2 = "second log  second round 2",
3548    Log_3_2_1 = "third log  second round 1",
3549    Log_3_2_2 = "third log  second round 2",
3550    Log_1_3_1 = "first log  third round 1",
3551    Log_1_3_2 = "first log  third round 2",
3552
3553    Dir = ?privdir(Conf),
3554    File = filename:join(Dir, "a.LOG"),
3555    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]),
3556    disk_log:log(a, Log_1_1),
3557    disk_log:log(a, Log_1_2),
3558    disk_log:log(a, Log_2_1),
3559    disk_log:log(a, Log_2_2),
3560    disk_log:log(a, Log_3_1),
3561    disk_log:log(a, Log_3_2),
3562    disk_log:log(a, Log_4_1),
3563    disk_log:log(a, Log_4_2),
3564    disk_log:log(a, Log_5_1),
3565    disk_log:log(a, Log_5_2),
3566    disk_log:log(a, Log_1_1),
3567    disk_log:log(a, Log_1_2),
3568    disk_log:log(a, Log_2_1),
3569    disk_log:log(a, Log_2_2),
3570    disk_log:log(a, Log_3_1),
3571    disk_log:log(a, Log_3_2),
3572    disk_log:log(a, Log_4_1),
3573    disk_log:log(a, Log_4_2),
3574    disk_log:change_size(a, {100, 3}),
3575    [Log_5_1, Log_5_2,
3576     Log_1_1, Log_1_2,
3577     Log_2_1, Log_2_2,
3578     Log_3_1, Log_3_2,
3579     Log_4_1, Log_4_2] = get_all_terms(a),
3580    disk_log:log(a, Log_1_2_1),
3581    disk_log:log(a, Log_1_2_2),
3582    [Log_2_1, Log_2_2,
3583     Log_3_1, Log_3_2,
3584     Log_4_1, Log_4_2,
3585     Log_1_2_1, Log_1_2_2] = get_all_terms(a),
3586
3587    disk_log:close(a),
3588    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]),
3589    [Log_2_1, Log_2_2,
3590     Log_3_1, Log_3_2,
3591     Log_4_1, Log_4_2,
3592     Log_1_2_1, Log_1_2_2] = get_all_terms(a),
3593    disk_log:log(a, Log_2_2_1),
3594    disk_log:log(a, Log_2_2_2),
3595    disk_log:log(a, Log_3_2_1),
3596    disk_log:log(a, Log_3_2_2),
3597    disk_log:log(a, Log_1_3_1),
3598    disk_log:log(a, Log_1_3_2),
3599    [Log_2_2_1, Log_2_2_2,
3600     Log_3_2_1, Log_3_2_2,
3601     Log_1_3_1, Log_1_3_2] = get_all_terms(a),
3602    disk_log:close(a),
3603    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]),
3604    [Log_2_2_1, Log_2_2_2,
3605     Log_3_2_1, Log_3_2_2,
3606     Log_1_3_1, Log_1_3_2] = get_all_terms(a),
3607    disk_log:close(a),
3608    del(File, 5),
3609
3610    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]),
3611    disk_log:log(a, Log_1_1),
3612    disk_log:log(a, Log_1_2),
3613    disk_log:log(a, Log_2_1),
3614    disk_log:log(a, Log_2_2),
3615    disk_log:log(a, Log_3_1),
3616    disk_log:log(a, Log_3_2),
3617    disk_log:log(a, Log_4_1),
3618    disk_log:log(a, Log_4_2),
3619    disk_log:log(a, Log_5_1),
3620    disk_log:log(a, Log_5_2),
3621    disk_log:log(a, Log_1_1),
3622    disk_log:log(a, Log_1_2),
3623    disk_log:log(a, Log_2_1),
3624    disk_log:log(a, Log_2_2),
3625    disk_log:log(a, Log_3_1),
3626    disk_log:log(a, Log_3_2),
3627    disk_log:log(a, Log_4_1),
3628    disk_log:log(a, Log_4_2),
3629    disk_log:log(a, Log_5_1),
3630    disk_log:log(a, Log_5_2),
3631    disk_log:change_size(a, {100, 3}),
3632    [Log_1_1, Log_1_2,
3633     Log_2_1, Log_2_2,
3634     Log_3_1, Log_3_2,
3635     Log_4_1, Log_4_2,
3636     Log_5_1, Log_5_2] = get_all_terms(a),
3637    disk_log:log(a, Log_1_2_1),
3638    disk_log:log(a, Log_1_2_2),
3639    disk_log:log(a, Log_2_2_1),
3640    disk_log:log(a, Log_2_2_2),
3641    disk_log:log(a, Log_3_2_1),
3642    disk_log:log(a, Log_3_2_2),
3643    disk_log:log(a, Log_1_3_1),
3644    disk_log:log(a, Log_1_3_2),
3645    [Log_2_2_1, Log_2_2_2,
3646     Log_3_2_1, Log_3_2_2,
3647     Log_1_3_1, Log_1_3_2] = get_all_terms(a),
3648
3649    disk_log:close(a),
3650    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]),
3651    [Log_2_2_1, Log_2_2_2,
3652     Log_3_2_1, Log_3_2_2,
3653     Log_1_3_1, Log_1_3_2] = get_all_terms(a),
3654    disk_log:close(a),
3655    del(File, 5).
3656
3657
3658%% Change size of a wrap log file before we have reached (on the
3659%% second round) to the file index corresponding to the new size.
3660change_size_after(Conf) when is_list(Conf) ->
3661
3662    Log_1_1 = "first log  first message",
3663    Log_1_2 = "first log  second message",
3664    Log_2_1 = "second log  first message",
3665    Log_2_2 = "second log  second message",
3666    Log_3_1 = "third log  first message",
3667    Log_3_2 = "third log  second message",
3668    Log_4_1 = "fourth log  first message",
3669    Log_4_2 = "fourth log  second message",
3670    Log_5_1 = "fifth log  first message",
3671    Log_5_2 = "fifth log  second message",
3672    Log_1_2_1 = "first log  second round 1",
3673    Log_1_2_2 = "first log  second round 2",
3674
3675    Dir = ?privdir(Conf),
3676    File = filename:join(Dir, "a.LOG"),
3677    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3678			     {size, {100,5}}]),
3679    disk_log:log(a, Log_1_1),
3680    disk_log:log(a, Log_1_2),
3681    disk_log:log(a, Log_2_1),
3682    disk_log:log(a, Log_2_2),
3683    disk_log:log(a, Log_3_1),
3684    disk_log:log(a, Log_3_2),
3685    disk_log:log(a, Log_4_1),
3686    disk_log:log(a, Log_4_2),
3687    disk_log:log(a, Log_5_1),
3688    disk_log:log(a, Log_5_2),
3689    disk_log:log(a, Log_1_1),
3690    disk_log:log(a, Log_1_2),
3691    disk_log:log(a, Log_2_1),
3692    disk_log:log(a, Log_2_2),
3693    disk_log:change_size(a, {100, 3}),
3694    [Log_3_1,Log_3_2,
3695     Log_4_1, Log_4_2,
3696     Log_5_1, Log_5_2,
3697     Log_1_1, Log_1_2,
3698     Log_2_1, Log_2_2] = get_all_terms(a),
3699    disk_log:log(a, Log_3_1),
3700    disk_log:log(a, Log_3_2),
3701    disk_log:log(a, Log_1_2_1),
3702    disk_log:log(a, Log_1_2_2),
3703    [Log_2_1, Log_2_2,
3704     Log_3_1, Log_3_2,
3705     Log_1_2_1, Log_1_2_2] = get_all_terms(a),
3706
3707    disk_log:close(a),
3708    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3709			     {size, {100,3}}]),
3710    [Log_2_1, Log_2_2,
3711     Log_3_1, Log_3_2,
3712     Log_1_2_1, Log_1_2_2] = get_all_terms(a),
3713    disk_log:close(a),
3714    del(File, 5),
3715
3716    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3717			     {size, {100,5}}]),
3718    disk_log:log(a, Log_1_1),
3719    disk_log:log(a, Log_1_2),
3720    disk_log:log(a, Log_2_1),
3721    disk_log:log(a, Log_2_2),
3722    disk_log:log(a, Log_3_1),
3723    disk_log:log(a, Log_3_2),
3724    disk_log:log(a, Log_4_1),
3725    disk_log:log(a, Log_4_2),
3726    disk_log:log(a, Log_5_1),
3727    disk_log:log(a, Log_5_2),
3728    disk_log:log(a, Log_1_1),
3729    disk_log:log(a, Log_1_2),
3730    disk_log:log(a, Log_2_1),
3731    disk_log:log(a, Log_2_2),
3732    disk_log:change_size(a, {60, 3}),
3733    [Log_3_1,Log_3_2,
3734     Log_4_1, Log_4_2,
3735     Log_5_1, Log_5_2,
3736     Log_1_1, Log_1_2,
3737     Log_2_1, Log_2_2] = get_all_terms(a),
3738    disk_log:log(a, Log_3_1),
3739    disk_log:log(a, Log_1_2_1),
3740    [Log_2_1, Log_2_2,
3741     Log_3_1,
3742     Log_1_2_1] = get_all_terms(a),
3743
3744    disk_log:close(a),
3745    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3746			     {size, {60,3}}]),
3747    [Log_2_1, Log_2_2,
3748     Log_3_1,
3749     Log_1_2_1] = get_all_terms(a),
3750    disk_log:close(a),
3751    del(File, 5).
3752
3753
3754
3755%% Open an existing wrap log without size option .
3756default_size(Conf) when is_list(Conf) ->
3757    Dir = ?privdir(Conf),
3758    File = filename:join(Dir, "a.LOG"),
3759    {error, {badarg, size}} = disk_log:open([{name,a}, {file, File},
3760                                                   {type, wrap}]),
3761
3762    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap},
3763                                   {size, {100,5}}]),
3764    disk_log:close(a),
3765
3766    {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}]),
3767    {100, 5} = disk_log_1:read_size_file(File),
3768    ok = disk_log:close(a),
3769    del(File, 5).
3770
3771%% Testing change_size/2 a bit more...
3772change_size2(Conf) when is_list(Conf) ->
3773
3774    Dir = ?privdir(Conf),
3775    File = filename:join(Dir, "n.LOG"),
3776    No = 4,
3777    del(File, No),	% cleanup
3778
3779    %% External halt.
3780    {ok, n} = disk_log:open([{name, n}, {file, File}, {size, 100000},
3781                                   {format, external}, {type, halt}]),
3782    B = mk_bytes(60), % 56 actually...
3783    ok = disk_log:blog_terms(n, [B,list_to_binary(B),B]),
3784    Error1 = {error, {new_size_too_small,n,168}} =
3785        disk_log:change_size(n, 167),
3786    "The current size" ++ _ = format_error(Error1),
3787    ok = disk_log:change_size(n, infinity),
3788    ok = disk_log:change_size(n, 168),
3789    ok = disk_log:close(n),
3790    file:delete(File), % cleanup
3791
3792    %% External wrap.
3793    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3794				   {size, {100,No}}, {notify, true},
3795				   {format, external}]),
3796    BB = mk_bytes(160),
3797    ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), % create all files
3798    %% Used to be one message, but now one per wrapped file.
3799    rec(3, {disk_log, node(), n, {wrap, 0}}),
3800    ok = disk_log:blog_terms(n, [BB, BB]),
3801    %% Used to be one message, but now one per wrapped file.
3802    rec(2, {disk_log, node(), n, {wrap, 1}}),
3803    ok = disk_log:change_size(n, {100, 2}),
3804    ok = disk_log:change_size(n, {100, 2}),
3805    {100, 2} = sz(n),
3806    ok = disk_log:balog_terms(n, [BB, BB]),
3807    ok = disk_log:balog_terms(n, [BB]),
3808    ok = disk_log:blog_terms(n, [BB]),
3809    %% Used to be one message, but now one per wrapped file.
3810    rec(4, {disk_log, node(), n, {wrap, 1}}),
3811    ok = disk_log:change_size(n, {100, 4}),
3812    ok = disk_log:close(n),
3813    del(File, No),
3814
3815    %% Internal wrap.
3816    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3817				   {size, {100,No}}, {notify, true},
3818				   {format, internal}]),
3819    ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), % create all files
3820    %% Used to be one message, but now one per wrapped file.
3821    rec(3, {disk_log, node(), n, {wrap, 0}}),
3822    ok = disk_log:blog_terms(n, [BB, BB]),
3823    %% Used to be one message, but now one per wrapped file.
3824    rec(2, {disk_log, node(), n, {wrap, 1}}),
3825    ok = disk_log:change_size(n, {100, 2}),
3826    {100, 2} = sz(n),
3827    ok = disk_log:blog_terms(n, [BB, BB, BB, BB]),
3828    %% Used to be one message, but now one per wrapped file.
3829    rec(4, {disk_log, node(), n, {wrap, 1}}),
3830    ok = disk_log:close(n),
3831    del(File, No).
3832
3833%% OTP-3484: truncating index file.
3834change_size_truncate(Conf) when is_list(Conf) ->
3835
3836    Dir = ?privdir(Conf),
3837    File = filename:join(Dir, "bert.LOG"),
3838    No = 3,
3839    B = mk_bytes(60),
3840
3841    %% The problem here is truncation of the index file. One cannot easily
3842    %% check that the index file is correctly updated, but print_index_file()
3843    %% can be used to follow the progress more closely.
3844
3845    %% Part 1.
3846    %% Change the size immediately after creating the log, while there
3847    %% are no log files. This used to write stuff a negative offset
3848    %% from the beginning of the file.
3849    del(File, No+1),
3850    {ok, bert} = disk_log:open([{name,bert}, {type,wrap}, {file, File},
3851				      {notify, true}, {size,{1000,255}}]),
3852    ok = disk_log:change_size(bert,{100,No}),
3853    ok = disk_log:blog(bert, B),
3854    ok = disk_log:blog(bert, B),
3855    rec(1, {disk_log, node(), bert, {wrap, 0}}),
3856    ok = disk_log:blog(bert, B),
3857    rec(1, {disk_log, node(), bert, {wrap, 0}}),
3858    3 = curf(bert),
3859    ok = disk_log:blog(bert, B),
3860    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3861    1 = curf(bert),
3862    ok = disk_log:blog(bert, B),
3863    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3864    ok = disk_log:blog(bert, B),
3865    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3866
3867    ok = disk_log:blog(bert, B),
3868    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3869    ok = disk_log:blog(bert, B),
3870    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3871    ok = disk_log:blog(bert, B),
3872    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3873
3874    %% Three items expected.
3875    %% disk_log_1:print_index_file("bert.LOG.idx"),
3876    3 = curf(bert),
3877    ok = disk_log:change_size(bert,{100,1}),
3878    ok = disk_log:blog(bert, B),
3879    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3880    %% Three items expected.
3881    %% disk_log_1:print_index_file("bert.LOG.idx"),
3882    ok = disk_log:blog(bert, B),
3883    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3884    ok = disk_log:blog(bert, B),
3885    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3886    %% One item expected.
3887    %% disk_log_1:print_index_file("bert.LOG.idx"),
3888
3889    ok = disk_log:blog(bert, B),
3890    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3891    ok = disk_log:close(bert),
3892    del(File, No),
3893
3894    %% Part 2.
3895    %% Change the size twice, the second time while the the effects of
3896    %% the first changed have not yet been handled. Finally close before
3897    %% the index file has been truncated.
3898
3899    del(File, No),
3900    {ok, bert} = disk_log:open([{name,bert}, {type,wrap}, {file, File},
3901				      {notify, true}, {size,{100,No}}]),
3902    ok = disk_log:blog(bert, B),
3903    ok = disk_log:blog(bert, B),
3904    rec(1, {disk_log, node(), bert, {wrap, 0}}),
3905    ok = disk_log:blog(bert, B),
3906    rec(1, {disk_log, node(), bert, {wrap, 0}}),
3907
3908    3 = curf(bert),
3909    ok = disk_log:change_size(bert,{100,No-1}),
3910
3911    ok = disk_log:blog(bert, B),
3912    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3913
3914    1 = curf(bert),
3915    ok = disk_log:change_size(bert,{100,No+1}),
3916
3917    %% Three items expected.
3918    %% disk_log_1:print_index_file("bert.LOG.idx"),
3919
3920    ok = disk_log:blog(bert, B),
3921    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3922
3923    %% Three items expected.
3924    %% disk_log_1:print_index_file("bert.LOG.idx"),
3925
3926    2 = curf(bert),
3927    ok = disk_log:change_size(bert,{100,1}),
3928
3929    %% Three items expected.
3930    %% disk_log_1:print_index_file("bert.LOG.idx"),
3931
3932    ok = disk_log:close(bert),
3933
3934    %% State: .siz is 1, current file is 2, index file size is 3...
3935
3936    {ok, bert} = disk_log:open([{name,bert}, {file, File},
3937				      {type,wrap}, {notify, true}]),
3938
3939    %% Three items expected.
3940    %% disk_log_1:print_index_file("bert.LOG.idx"),
3941
3942    2 = curf(bert),
3943    ok = disk_log:blog(bert, B),
3944    rec(1, {disk_log, node(), bert, {wrap, 1}}),
3945    ok = disk_log:close(bert),
3946
3947    {ok, bert} = disk_log:open([{name,bert}, {file, File},
3948				      {type,wrap}, {notify, true}]),
3949
3950    %% Two items expected.
3951    %% disk_log_1:print_index_file("bert.LOG.idx"),
3952
3953    1 = curf(bert),
3954    ok = disk_log:blog(bert, B),
3955    %% Expect {wrap 0}. Nothing lost now, last wrap notification
3956    %% reported one lost item.
3957    rec(1, {disk_log, node(), bert, {wrap, 0}}),
3958
3959    %% One item expected.
3960    %% disk_log_1:print_index_file("bert.LOG.idx"),
3961    ok = disk_log:close(bert),
3962
3963    del(File, No),
3964    ok.
3965
3966%% Change notify and head.
3967change_attribute(Conf) when is_list(Conf) ->
3968
3969    Dir = ?privdir(Conf),
3970    File = filename:join(Dir, "n.LOG"),
3971    No = 4,
3972    del(File, No),	% cleanup
3973    B = mk_bytes(60),
3974
3975    Q = qlen(),
3976
3977    %% test change_notify
3978    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
3979				   {size, {100,No}}]),
3980    {ok, n} = disk_log:open([{name, n}]), % ignored...
3981    ok = disk_log:log_terms(n, [B,B]),
3982    {error, {badarg, notify}} = disk_log:change_notify(n, self(), wrong),
3983    ok = disk_log:change_notify(n, self(), false),
3984    ok = disk_log:change_notify(n, self(), true),
3985    Error1 = {error, {not_owner, _}} =
3986        disk_log:change_notify(n, none, true),
3987    "The pid" ++ _ = format_error(Error1),
3988    2 = no_written_items(n),
3989    0 = users(n),
3990    Parent = self(),
3991    Pid = spawn(fun() -> disk_log:close(n), Parent ! {self(),done} end),
3992    receive {Pid, done} -> ok end,
3993    0 = users(n),
3994    1 = length(owners(n)),
3995
3996    %% test change_header
3997    {error, {badarg, head}} = disk_log:change_header(n, none),
3998    {error, {badarg, head}} =
3999	disk_log:change_header(n, {head_func, {1,2,3}}),
4000    ok = disk_log:change_header(n, {head, header}),
4001    ok = disk_log:log(n, B),
4002    rec(1, {disk_log, node(), n, {wrap, 0}}),
4003    4 = no_written_items(n),
4004    ok = disk_log:change_header(n, {head, none}),
4005    ok = disk_log:log(n, B),
4006    rec(1, {disk_log, node(), n, {wrap, 0}}),
4007    5 = no_written_items(n),
4008    ok = disk_log:change_header(n,
4009			     {head_func, {?MODULE, head_fun, [{ok,header}]}}),
4010    ok = disk_log:log(n, B),
4011    rec(1, {disk_log, node(), n, {wrap, 1}}),
4012    7 = no_written_items(n),
4013    ok = disk_log:close(n),
4014    {error, no_such_log} = disk_log:close(n),
4015    del(File, No),
4016    file:delete(File), % cleanup
4017    {ok, n} = disk_log:open([{name, n}, {file, File}, {format, external},
4018				   {type, halt}]),
4019    {error, {badarg, head}} = disk_log:change_header(n, {head, header}),
4020    ok = disk_log:change_header(n, {head, "header"}),
4021    ok = disk_log:close(n),
4022    file:delete(File),
4023
4024    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
4025				   {size, {100,No}}]),
4026    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
4027				   {size, {100,No}}]),
4028    ok = disk_log:change_notify(n, self(), true),
4029    ok = disk_log:change_header(n, {head, tjolahopp}),
4030    {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
4031				   {size, {100,No}}, {notify, true}]),
4032    ok = disk_log:close(n),
4033    {error, no_such_log} = disk_log:info(n),
4034    Q = qlen(),
4035    del(File, No).
4036
4037
4038%% Open a distributed log.
4039dist_open(Conf) when is_list(Conf) ->
4040    PrivDir = ?privdir(Conf),
4041    true = is_alive(),
4042
4043    Q = qlen(),
4044    File = filename:join(PrivDir, "n.LOG"),
4045    File1 = filename:join(PrivDir, "n1.LOG"),
4046    No = 3,
4047    file:delete(File),
4048    del(File, No),	% cleanup
4049    del(File1, No),	% cleanup
4050    B = mk_bytes(60),
4051
4052    PA = filename:dirname(code:which(?MODULE)),
4053    {ok, Node} = start_node(disk_log, "-pa " ++ PA),
4054    wait_for_ready_net(),
4055
4056    %% open non-distributed on this node:
4057    {ok,n} = disk_log:open([{name, n}, {file, File}, {type, halt},
4058                                  {distributed, []}]),
4059
4060    Error1 = {error, {halt_log, n}} = disk_log:inc_wrap_file(n),
4061    "The halt log" ++ _ = format_error(Error1),
4062    ok = disk_log:lclose(n),
4063    file:delete(File),
4064
4065    %% open distributed on this node:
4066    {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, halt},
4067				    {distributed, [node()]}]),
4068    %% the error message is ignored:
4069    ok = disk_log:inc_wrap_file(n),
4070    ok = disk_log:close(n),
4071    file:delete(File),
4072
4073    %% open a wrap log on this node, write something on this node
4074    {[_],[]} = disk_log:open([{name, n}, {file, File},
4075				    {type, wrap}, {size, {50, No}},
4076				    {distributed, [node()]}]),
4077    ok = disk_log:log(n, B),
4078    ok = disk_log:close(n),
4079
4080    %% open a wrap log on this node and aother node, write something
4081    {[_],[]} = disk_log:open([{name, n}, {file, File},
4082				    {type, wrap}, {size, {50, No}},
4083				    {distributed, [node()]}]),
4084    {[_],[]} = disk_log:open([{name, n}, {file, File1},
4085				    {type, wrap}, {size, {50, No}},
4086				    {distributed, [Node]}]),
4087    ok = disk_log:log(n, B),
4088    ok = rpc:call(Node, disk_log, log, [n, B]),
4089    ok = disk_log:close(n),
4090    del(File, No),
4091    del(File1, No),
4092    file:delete(File),
4093
4094    %% open a wrap log on this node and another node, use lclose
4095    {[_],[]} = disk_log:open([{name, n}, {file, File},
4096				    {type, wrap}, {size, {50, No}},
4097				    {distributed, [node()]}]),
4098    {[_],[]} = disk_log:open([{name, n}, {file, File},
4099				    {type, wrap}, {size, {50, No}},
4100				    {distributed, [node()]},
4101                                    {linkto,none}]),
4102    {[_],[]} = disk_log:open([{name, n}, {file, File1},
4103				    {type, wrap}, {size, {50, No}},
4104				    {distributed, [Node]}]),
4105    [_, _] = distributed(n),
4106    ok = disk_log:lclose(n, Node),
4107    [_] = distributed(n),
4108    ok = disk_log:lclose(n),
4109    ok = disk_log:lclose(n),
4110    {error, no_such_log} = disk_log:info(n),
4111    del(File, No),
4112    del(File1, No),
4113    file:delete(File),
4114
4115    %% open an invalid log file, and see how error are handled
4116    First = "n.LOG.1",
4117    make_file(PrivDir, First, 8),
4118
4119    {[], [_,_]} = disk_log:open([{name, n}, {file, File},
4120				       {type, wrap}, {size, {50, No}},
4121				       {distributed, [Node,node()]}]),
4122    del(File, No),
4123    file:delete(File),
4124
4125    %% open a wrap on one other node (not on this node)
4126    {[_],[]} = disk_log:open([{name, n}, {file, File},
4127				    {type, wrap}, {size, {50, No}},
4128				    {distributed, [Node]}]),
4129    ok = rpc:call(Node, disk_log, log, [n, B]),
4130    {error, no_such_log} = disk_log:lclose(n),
4131    ok = disk_log:close(n),
4132
4133    Q = qlen(),
4134
4135    {error, no_such_log} = disk_log:info(n),
4136    del(File, No),
4137    file:delete(File),
4138    stop_node(Node),
4139    ok.
4140
4141%% Open a log distributed and not distributed.
4142dist_error_open(Conf) when is_list(Conf) ->
4143    PrivDir = ?privdir(Conf),
4144    true = is_alive(),
4145
4146    Q = qlen(),
4147    File = filename:join(PrivDir, "bert.LOG"),
4148    File1 = filename:join(PrivDir, "bert1.LOG"),
4149    No = 3,
4150    file:delete(File),
4151    del(File, No),	% cleanup
4152    del(File1, No),	% cleanup
4153
4154    PA = filename:dirname(code:which(?MODULE)),
4155    {ok, Node} = start_node(disk_log, "-pa " ++ PA),
4156    wait_for_ready_net(),
4157
4158    %% open non-distributed on this node:
4159    {ok,n} = disk_log:open([{name, n}, {file, File},
4160				  {type, wrap}, {size, {50, No}}]),
4161
4162    %% trying to open distributed on this node (error):
4163    {[],[Error1={ENode,{error,{node_already_open,n}}}]} =
4164	disk_log:open([{name, n}, {file, File},
4165		       {type, wrap}, {size, {50, No}},
4166		       {distributed, [node()]}]),
4167    true =
4168        lists:prefix(lists:flatten(io_lib:format("~p: The distribution",
4169                                                 [ENode])),
4170                     format_error(Error1)),
4171    ok = disk_log:lclose(n),
4172
4173    %% open distributed on this node:
4174    {[_],[]} = disk_log:open([{name, n}, {file, File},
4175				    {type, wrap}, {size, {50, No}},
4176				    {distributed, [node()]}]),
4177
4178    %% trying to open non-distributed on this node (error):
4179    {_,{node_already_open,n}} =
4180	disk_log:open([{name, n}, {file, File},
4181		       {type, wrap}, {size, {50, No}}]),
4182
4183    ok = disk_log:close(n),
4184    Q = qlen(),
4185
4186    del(File, No),
4187    del(File1, No),
4188    file:delete(File),
4189    stop_node(Node),
4190    ok.
4191
4192%% Notification from other node.
4193dist_notify(Conf) when is_list(Conf) ->
4194    PrivDir = ?privdir(Conf),
4195    true = is_alive(),
4196
4197    File = filename:join(PrivDir, "bert.LOG"),
4198    File1 = filename:join(PrivDir, "bert1.LOG"),
4199    No = 3,
4200    B = mk_bytes(60),
4201    file:delete(File),
4202    file:delete(File1),
4203    del(File, No),	% cleanup
4204    del(File1, No),
4205
4206    PA = filename:dirname(code:which(?MODULE)),
4207    {ok, Node} = start_node(disk_log, "-pa " ++ PA),
4208    wait_for_ready_net(),
4209
4210    %% opening distributed on this node:
4211    {[_],[]} = disk_log:open([{name, n}, {file, File}, {notify, false},
4212				    {type, wrap}, {size, {50, No}},
4213				    {distributed, [node()]}]),
4214
4215    %% opening distributed on other node:
4216    {[_],[]} = disk_log:open([{name, n}, {file, File1},
4217				    {notify, true}, {linkto, self()},
4218				    {type, wrap}, {size, {50, No}},
4219				    {distributed, [Node]}]),
4220    disk_log:alog(n, B),
4221    disk_log:alog(n, B),
4222    ok = disk_log:sync(n),
4223    rec(1, {disk_log, Node, n, {wrap, 0}}),
4224    ok = disk_log:close(n),
4225
4226    del(File, No),
4227    del(File1, No),
4228    file:delete(File),
4229    stop_node(Node),
4230    ok.
4231
4232%% Terminating nodes with distributed logs.
4233dist_terminate(Conf) when is_list(Conf) ->
4234    Dir = ?privdir(Conf),
4235    true = is_alive(),
4236
4237    File = filename:join(Dir, "n.LOG"),
4238    File1 = filename:join(Dir, "n1.LOG"),
4239    No = 1,
4240    del(File, No),	% cleanup
4241    del(File1, No),	% cleanup
4242
4243    PA = filename:dirname(code:which(?MODULE)),
4244    {ok, Node} = start_node(disk_log, "-pa " ++ PA),
4245    wait_for_ready_net(),
4246
4247    %% Distributed versions of two of the situations in close_block(/1.
4248
4249    %% One of two owners terminates.
4250    Pid1 = spawn_link(?MODULE, lserv, [n]),
4251    Pid2 = spawn_link(?MODULE, lserv, [n]),
4252    {[{_, {ok, n}}], []} = sync_do(Pid1, {dist_open, File, node()}),
4253    {[{_, {ok, n}}], []} = sync_do(Pid2, {dist_open, File1, Node}),
4254    [_] = sync_do(Pid1, owners),
4255    [_] = sync_do(Pid2, owners),
4256    0 = sync_do(Pid1, users),
4257    0 = sync_do(Pid2, users),
4258    sync_do(Pid1, terminate),
4259    [_] = sync_do(Pid2, owners),
4260    0 = sync_do(Pid2, users),
4261    sync_do(Pid2, terminate),
4262    {error, no_such_log} = disk_log:info(n),
4263
4264    %% Users terminate (no link...).
4265    Pid3 = spawn_link(?MODULE, lserv, [n]),
4266    Pid4 = spawn_link(?MODULE, lserv, [n]),
4267    {[{_, {ok, n}}], []} =
4268	sync_do(Pid3, {dist_open, File, none, node()}),
4269    {[{_, {ok, n}}], []} =
4270	sync_do(Pid4, {dist_open, File1, none, Node}),
4271    [] = sync_do(Pid3, owners),
4272    [] = sync_do(Pid4, owners),
4273    1 = sync_do(Pid3, users),
4274    1 = sync_do(Pid4, users),
4275    sync_do(Pid3, terminate),
4276    [] = sync_do(Pid4, owners),
4277    1 = sync_do(Pid4, users),
4278    sync_do(Pid4, terminate),
4279    ok = disk_log:close(n), % closing all nodes
4280    {error, no_such_log} = disk_log:info(n),
4281
4282    del(File, No),
4283    del(File1, No),
4284    stop_node(Node),
4285    ok.
4286
4287%% Accessible logs on nodes.
4288dist_accessible(Conf) when is_list(Conf) ->
4289    PrivDir = ?privdir(Conf),
4290
4291    true = is_alive(),
4292
4293    F1 = filename:join(PrivDir, "a.LOG"),
4294    file:delete(F1),
4295    F2 = filename:join(PrivDir, "b.LOG"),
4296    file:delete(F2),
4297    F3 = filename:join(PrivDir, "c.LOG"),
4298    file:delete(F3),
4299    F4 = filename:join(PrivDir, "d.LOG"),
4300    file:delete(F1),
4301    F5 = filename:join(PrivDir, "e.LOG"),
4302    file:delete(F2),
4303    F6 = filename:join(PrivDir, "f.LOG"),
4304    file:delete(F3),
4305
4306    {[],[]} = disk_log:accessible_logs(),
4307    {ok, a} = disk_log:open([{name, a}, {type, halt}, {file, F1}]),
4308    {[a],[]} = disk_log:accessible_logs(),
4309    {ok, b} = disk_log:open([{name, b}, {type, halt}, {file, F2}]),
4310    {[a,b],[]} = disk_log:accessible_logs(),
4311    {ok, c} = disk_log:open([{name, c}, {type, halt}, {file, F3}]),
4312    {[a,b,c],[]} = disk_log:accessible_logs(),
4313
4314    PA = filename:dirname(code:which(?MODULE)),
4315    {ok, Node} = start_node(disk_log, "-pa " ++ PA),
4316    wait_for_ready_net(),
4317
4318    {[_],[]} = disk_log:open([{name, a}, {file, F4}, {type, halt},
4319				    {distributed, [Node]}]),
4320    {[a,b,c],[]} = disk_log:accessible_logs(),
4321    {[],[a]} = rpc:call(Node, disk_log, accessible_logs, []),
4322    {[_],[]} = disk_log:open([{name, b}, {file, F5}, {type, halt},
4323				    {distributed, [Node]}]),
4324    {[],[a,b]} = rpc:call(Node, disk_log, accessible_logs, []),
4325    {[_],[]} = disk_log:open([{name, c}, {file, F6}, {type, halt},
4326				    {distributed, [Node]}]),
4327    {[],[a,b,c]} = rpc:call(Node, disk_log, accessible_logs, []),
4328    {[a,b,c],[]} = disk_log:accessible_logs(),
4329    ok = disk_log:close(a),
4330    {[b,c],[a]} = disk_log:accessible_logs(),
4331    ok = disk_log:close(b),
4332    {[c],[a,b]} = disk_log:accessible_logs(),
4333    ok = disk_log:close(b),
4334    {[c],[a]} = disk_log:accessible_logs(),
4335    {[],[a,c]} = rpc:call(Node, disk_log, accessible_logs, []),
4336    ok = disk_log:close(c),
4337    {[],[a,c]} = disk_log:accessible_logs(),
4338    ok = disk_log:close(c),
4339    {[],[a]} = disk_log:accessible_logs(),
4340    {[],[a]} = rpc:call(Node, disk_log, accessible_logs, []),
4341    ok = disk_log:close(a),
4342    {[],[]} = disk_log:accessible_logs(),
4343    {[],[]} = rpc:call(Node, disk_log, accessible_logs, []),
4344
4345    file:delete(F1),
4346    file:delete(F2),
4347    file:delete(F3),
4348    file:delete(F4),
4349    file:delete(F5),
4350    file:delete(F6),
4351
4352    stop_node(Node),
4353    ok.
4354
4355%% OTP-4405. Deadlock between two nodes could happen.
4356dist_deadlock(Conf) when is_list(Conf) ->
4357    PrivDir = ?privdir(Conf),
4358
4359    true = is_alive(),
4360
4361    F1 = filename:join(PrivDir, "a.LOG"),
4362    file:delete(F1),
4363    F2 = filename:join(PrivDir, "b.LOG"),
4364    file:delete(F2),
4365
4366    PA = filename:dirname(code:which(?MODULE)),
4367    {ok, Node1} = start_node(disk_log_node1, "-pa " ++ PA),
4368    {ok, Node2} = start_node(disk_log_node2, "-pa " ++ PA),
4369    wait_for_ready_net(),
4370
4371    Self = self(),
4372    Fun1 = fun() -> dist_dl(Node2, a, F1, Self) end,
4373    Fun2 = fun() -> dist_dl(Node1, b, F2, Self) end,
4374    P1 = spawn(Node1, Fun1),
4375    P2 = spawn(Node2, Fun2),
4376    receive {P1, a} -> ok end,
4377    receive {P2, b} -> ok end,
4378
4379    stop_node(Node1),
4380    stop_node(Node2),
4381
4382    file:delete(F1),
4383    file:delete(F2),
4384    ok.
4385
4386dist_dl(Node, Name, File, Pid) ->
4387    {[{Node,{ok,Log}}], []} =
4388	disk_log:open([{name,Name},{file,File},{distributed,[Node]}]),
4389    timer:sleep(50), % give the nodes chance to exchange pg2 information
4390    ok = disk_log:close(Log),
4391    Pid ! {self(), Name},
4392    ok.
4393
4394%% OTP-4480. Opening several logs simultaneously.
4395dist_open2(Conf) when is_list(Conf) ->
4396    true = is_alive(),
4397    {ok, _Pg2} = pg2:start(),
4398
4399    dist_open2_1(Conf, 0),
4400    dist_open2_1(Conf, 100),
4401
4402    dist_open2_2(Conf, 0),
4403    dist_open2_2(Conf, 100),
4404
4405    PrivDir = ?privdir(Conf),
4406    Log = n,
4407
4408    %% Open a log three times (very fast). Two of the opening
4409    %% processes will be put on hold (pending). The first one failes
4410    %% to open the log. The second one succeeds, and the third one is
4411    %% attached.
4412    P0 = pps(),
4413    File0 = "n.LOG",
4414    File = filename:join(PrivDir, File0),
4415    make_file(PrivDir, File0, 8),
4416
4417    Parent = self(),
4418    F1 = fun() -> R = disk_log:open([{name, Log}, {file, File},
4419                                     {type, halt}, {format,internal},
4420                                     {distributed, [node()]}]),
4421                  Parent ! {self(), R}
4422         end,
4423    F2 = fun() -> R = disk_log:open([{name, Log}, {file, File},
4424                                     {type, halt}, {format,external},
4425                                     {distributed, [node()]}]),
4426                  Parent ! {self(), R},
4427                  timer:sleep(300)
4428         end,
4429    Pid1 = spawn(F1),
4430    timer:sleep(10),
4431    Pid2 = spawn(F2),
4432    Pid3 = spawn(F2),
4433
4434    receive {Pid1,R1} -> {[],[_]} = R1 end,
4435    receive {Pid2,R2} -> {[_],[]} = R2 end,
4436    receive {Pid3,R3} -> {[_],[]} = R3 end,
4437
4438    timer:sleep(500),
4439    file:delete(File),
4440    check_pps(P0),
4441
4442    %% This time the first process has a naughty head_func. This test
4443    %% does not add very much. Perhaps it should be removed. However,
4444    %% a head_func like this is why it's necessary to have an separate
4445    %% process calling disk_log:internal_open: the server cannot wait
4446    %% for the reply, but the call must be monitored, and this is what
4447    %% is accomplished by having a proxy process.
4448    F3 = fun() ->
4449                 R = disk_log:open([{name,Log},{file,File},
4450                                    {format,internal},
4451                                    {head_func,{?MODULE,head_exit,[]}},
4452                                    {type,halt}, {linkto,none}]),
4453                 Parent ! {self(), R}
4454         end,
4455    F4 = fun() ->
4456                 R = disk_log:open([{name,Log},{file,File},
4457                                    {format,internal},
4458                                    {type,halt}]),
4459                 Parent ! {self(), R}
4460         end,
4461    Pid4 = spawn(F3),
4462    timer:sleep(10),
4463    Pid5 = spawn(F4),
4464    Pid6 = spawn(F4),
4465    %% The timing is crucial here.
4466    R = case receive {Pid4,R4} -> R4 end of
4467                  {error, no_such_log} ->
4468                      R5 = receive {Pid5, R5a} -> R5a end,
4469                      R6 = receive {Pid6, R6a} -> R6a end,
4470                      case {R5, R6} of
4471                          {{repaired, _, _, _}, {ok, Log}} -> ok;
4472                          {{ok, Log}, {repaired, _, _, _}} -> ok;
4473                          _ -> test_server_fail({bad_replies, R5, R6})
4474                      end,
4475                      ok;
4476                  {ok, Log} -> % uninteresting case
4477                      receive {Pid5,_R5} -> ok end,
4478                      receive {Pid6,_R6} -> ok end,
4479                      {comment,
4480                       "Timing dependent test did not check anything."}
4481              end,
4482
4483    timer:sleep(100),
4484    {error, no_such_log} = disk_log:close(Log),
4485    file:delete(File),
4486    check_pps(P0),
4487
4488    No = 2,
4489    Log2 = n2,
4490    File2 = filename:join(PrivDir, "b.LOG"),
4491    file:delete(File2),
4492    del(File, No),
4493
4494    %% If a client takes a long time when writing the header, other
4495    %% processes should be able to attach to other log without having to
4496    %% wait.
4497
4498    {ok,Log} =
4499        disk_log:open([{name,Log},{file,File},{type,wrap},{size,{100,No}}]),
4500    Pid = spawn(fun() ->
4501                        receive {HeadPid, start} -> ok end,
4502                        {ok,Log2} = disk_log:open([{name,Log2},{file,File2},
4503                                                   {type,halt}]),
4504                        HeadPid ! {self(), done}
4505                end),
4506    HeadFunc = {?MODULE, slow_header, [Pid]},
4507    ok = disk_log:change_header(Log, {head_func, HeadFunc}),
4508    ok = disk_log:inc_wrap_file(Log), % header is written
4509
4510    timer:sleep(100),
4511    ok = disk_log:close(Log),
4512
4513    file:delete(File2),
4514    del(File, No),
4515    check_pps(P0),
4516
4517    R.
4518
4519dist_open2_1(Conf, Delay) ->
4520    Dir = ?privdir(Conf),
4521    File = filename:join(Dir, "n.LOG"),
4522    Log = n,
4523
4524    A0 = [{name,Log},{file,File},{type,halt}],
4525    create_opened_log(File, A0),
4526    P0 = pps(),
4527
4528    Log2 = log2,
4529    File2 = "log2.LOG",
4530    file:delete(File2),
4531    {ok,Log2} = disk_log:open([{name,Log2},{file,File2},{type,halt}]),
4532
4533    Parent = self(),
4534    F = fun() ->
4535                R = disk_log:open(A0),
4536                timer:sleep(Delay),
4537                Parent ! {self(), R}
4538        end,
4539    Pid1 = spawn(F),
4540    timer:sleep(10),
4541    Pid2 = spawn(F),
4542    Pid3 = spawn(F),
4543    {error, no_such_log} = disk_log:log(Log, term), % is repairing now
4544    0 = qlen(),
4545
4546    %% The file is already open, so this will not take long.
4547    {ok,Log2} = disk_log:open([{name,Log2},{file,File2},{type,halt}]),
4548    0 = qlen(), % still repairing
4549    ok = disk_log:close(Log2),
4550    {error, no_such_log} = disk_log:close(Log2),
4551    file:delete(File2),
4552
4553    receive {Pid1,R1} -> {repaired,_,_,_} = R1 end,
4554    receive {Pid2,R2} -> {ok,_} = R2 end,
4555    receive {Pid3,R3} -> {ok,_} = R3 end,
4556    timer:sleep(500),
4557    {error, no_such_log} = disk_log:info(Log),
4558
4559    file:delete(File),
4560    check_pps(P0),
4561
4562    ok.
4563
4564dist_open2_2(Conf, Delay) ->
4565    Dir = ?privdir(Conf),
4566    File = filename:join(Dir, "n.LOG"),
4567    Log = n,
4568
4569    PA = filename:dirname(code:which(?MODULE)),
4570    {ok, Node1} = start_node(disk_log_node2, "-pa " ++ PA),
4571    wait_for_ready_net(),
4572    P0 = pps(),
4573
4574    A0 = [{name,Log},{file,File},{type,halt}],
4575    create_opened_log(File, A0),
4576
4577    Log2 = log2,
4578    File2 = "log2.LOG",
4579    file:delete(File2),
4580    {[{Node1,{ok,Log2}}],[]} =
4581        disk_log:open([{name,Log2},{file,File2},{type,halt},
4582                       {distributed,[Node1]}]),
4583
4584    Parent = self(),
4585    F = fun() ->
4586                %% It would be nice to slow down the repair. head_func
4587                %% cannot be used since it is not called when repairing.
4588                R = disk_log:open([{distributed,[Node1]} | A0]),
4589                timer:sleep(Delay),
4590                Parent ! {self(), R}
4591        end,
4592    %% And {priority, ...} probably has no effect either.
4593    Pid1 = spawn_opt(F, [{priority, low}]),
4594    %% timer:sleep(1), % no guarantee that Pid1 will return {repaired, ...}
4595    Pid2 = spawn_opt(F, [{priority, low}]),
4596    {error, no_such_log} =
4597        disk_log:log(Log, term), % maybe repairing now
4598    0 = qlen(),
4599
4600    %% The file is already open, so this will not take long.
4601    {[{Node1,{ok,Log2}}],[]} =
4602        disk_log:open([{name,Log2},{file,File2},{type,halt},
4603                       {distributed,[Node1]}]),
4604    0 = qlen(), % probably still repairing
4605    ok = disk_log:close(Log2),
4606    file:delete(File2),
4607
4608    receive {Pid1,R1} -> R1 end,
4609    receive {Pid2,R2} -> R2 end,
4610    case {R1, R2} of
4611	      {{[{Node1,{repaired,_,_,_}}],[]},
4612	       {[{Node1,{ok,Log}}],[]}}         -> ok;
4613	      {{[{Node1,{ok,Log}}],[]},
4614	       {[{Node1,{repaired,_,_,_}}],[]}} -> ok
4615	  end,
4616
4617    check_pps(P0),
4618    stop_node(Node1),
4619    file:delete(File),
4620    ok.
4621
4622head_exit() ->
4623    process_flag(trap_exit, false), % Don't do like this!
4624    spawn_link(fun() -> exit(helfel) end),
4625    {ok,"123"}.
4626
4627slow_header(Pid) ->
4628    Pid ! {self(), start},
4629    receive {Pid, done} -> ok end,
4630    {ok, <<>>}.
4631
4632create_opened_log(File, Args) ->
4633    Log = n,
4634    file:delete(File),
4635    {ok, Log} = disk_log:open(Args),
4636    log_terms(Log, 400000),
4637    ok = disk_log:close(Log),
4638    mark(File, ?OPENED),
4639    ok.
4640
4641log_terms(_Log, 0) ->
4642    ok;
4643log_terms(Log, N) when N > 100 ->
4644    Terms = [{term,I} || I <- lists:seq(N-99, N)],
4645    ok = disk_log:log_terms(Log, Terms),
4646    log_terms(Log, N-100);
4647log_terms(Log, N) ->
4648    ok = disk_log:log(Log, {term, N}),
4649    log_terms(Log, N-1).
4650
4651%% OTP-5810. Cope with pg2 groups that are not disk logs.
4652other_groups(Conf) when is_list(Conf) ->
4653    true = is_alive(),
4654    PrivDir = ?privdir(Conf),
4655
4656    File = filename:join(PrivDir, "n.LOG"),
4657    file:delete(File),
4658
4659    {[],[]} = disk_log:accessible_logs(),
4660    {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, halt},
4661				    {distributed, [node()]}]),
4662    {[],[n]} = disk_log:accessible_logs(),
4663    Group = grupp,
4664    pg2:create(Group),
4665    ok = pg2:join(Group, self()),
4666    {[],[n]} = disk_log:accessible_logs(),
4667    [_] =
4668        lists:filter(fun(P) -> disk_log:pid2name(P) =/= undefined end,
4669                     erlang:processes()),
4670    pg2:delete(Group),
4671    {[],[n]} = disk_log:accessible_logs(),
4672    ok = disk_log:close(n),
4673    {[],[]} = disk_log:accessible_logs(),
4674    file:delete(File),
4675
4676    ok.
4677
4678%% OTP-6278. open/1 creates no status or crash report.
4679otp_6278(Conf) when is_list(Conf) ->
4680    Dir = ?privdir(Conf),
4681    File = filename:join(Dir, "no_such_dir/no_such_file"),
4682    error_logger:add_report_handler(?MODULE, self()),
4683    {error, {file_error, _, _}} =
4684        disk_log:open([{name,n},{file,File}]),
4685    receive
4686	{crash_report,_Pid,Report} ->
4687	    io:format("Unexpected: ~p\n", [Report]),
4688	    ct:fail(failed)
4689    after 1000 ->
4690            ok
4691    end,
4692    error_logger:delete_report_handler(?MODULE).
4693
4694%% OTP-10131. head_func type.
4695otp_10131(Conf) when is_list(Conf) ->
4696    Dir = ?privdir(Conf),
4697    Log = otp_10131,
4698    File = filename:join(Dir, lists:concat([Log, ".LOG"])),
4699    HeadFunc = {?MODULE, head_fun, [{ok,"head"}]},
4700    {ok, Log} = disk_log:open([{name,Log},{file,File},
4701                               {head_func, HeadFunc}]),
4702    HeadFunc = info(Log, head, undef),
4703    HeadFunc2 = {?MODULE, head_fun, [{ok,"head2"}]},
4704    ok = disk_log:change_header(Log, {head_func, HeadFunc2}),
4705    HeadFunc2 = info(Log, head, undef),
4706    ok = disk_log:close(Log),
4707    ok.
4708
4709%% OTP-16768. Bad number of items with truncate/1. ERL-1312, ERL-1313.
4710otp_16768(Conf) when is_list(Conf) ->
4711    Dir = ?privdir(Conf),
4712    Log = otp_16768,
4713    File = filename:join(Dir, Log),
4714    Header = <<"123456789\n">>,
4715    head_count(Log, File, Header, external, 25),
4716    head_count(Log, File, none, external, 20),
4717    head_count(Log, File, Header, internal, 30),
4718    head_count(Log, File, none, internal, 20),
4719    ok.
4720
4721head_count(Log, File, Header, Format, Expected) ->
4722    del(File, 10),
4723    Content = <<"1234567890123456789\n">>,
4724    HeaderSize = case Header of
4725                     none -> 0;
4726                     _ -> byte_size(Header)
4727                 end,
4728    %% 5 files for the external format, more for the internal format
4729    MaxSizePerFile = HeaderSize + (5 * byte_size(Content)) - 1,
4730    {ok, Log} = disk_log:open([{file, File},
4731                               {name, Log},
4732                               {format, Format},
4733                               {head, Header},
4734                               {size, {MaxSizePerFile, 999}},
4735                               {type, wrap}
4736                              ]),
4737    ok = disk_log:truncate(Log),
4738    lists:foreach(fun(_I) -> disk_log:blog(Log, Content) end,
4739                  lists:seq(1, 20)),
4740    DiskLogInfo = disk_log:info(Log),
4741    Expected = proplists:get_value(no_items, DiskLogInfo),
4742    ok = disk_log:close(Log).
4743
4744mark(FileName, What) ->
4745    {ok,Fd} = file:open(FileName, [raw, binary, read, write]),
4746    {ok,_} = file:position(Fd, 4),
4747    ok = file:write(Fd, What),
4748    ok = file:close(Fd).
4749
4750crash(File, Where) ->
4751    {ok, Fd} = file:open(File, [read,write]),
4752    file:position(Fd, Where),
4753    ok = file:write(Fd, [10]),
4754    ok = file:close(Fd).
4755
4756unwritable(Fname) ->
4757    {ok, Info} = file:read_file_info(Fname),
4758    Mode = Info#file_info.mode - 8#00200,
4759    file:write_file_info(Fname, Info#file_info{mode = Mode}).
4760
4761writable(Fname) ->
4762    {ok, Info} = file:read_file_info(Fname),
4763    Mode = Info#file_info.mode bor 8#00200,
4764    file:write_file_info(Fname, Info#file_info{mode = Mode}).
4765
4766truncate(File, Where) ->
4767    {ok, Fd} = file:open(File, [read,write]),
4768    file:position(Fd, Where),
4769    ok = file:truncate(Fd),
4770    ok = file:close(Fd).
4771
4772file_size(File) ->
4773    {ok, F} = file:read_file_info(File),
4774    F#file_info.size.
4775
4776copy_wrap_log(FromName, N, FromDir, ToDir) ->
4777    copy_wrap_log(FromName, FromName, N, FromDir, ToDir).
4778
4779copy_wrap_log(FromName, ToName, N, FromDir, ToDir) ->
4780    Fun = fun(E) ->
4781	     From = join(FromDir, io_lib:format("~s.~p", [FromName, E])),
4782	     To = join(ToDir, io_lib:format("~s.~p", [ToName, E])),
4783	     case file:read_file_info(From) of
4784                 {ok, _FileInfo} ->
4785                     copy_file(From, To);
4786                 _Else ->
4787                     ok
4788             end
4789	  end,
4790    Exts = [idx, siz | lists:seq(1, N)],
4791    lists:foreach(Fun, Exts).
4792
4793-define(BUFSIZE, 8192).
4794
4795copy_file(Src, Dest) ->
4796    %% io:format("copying from ~p to ~p~n", [Src, Dest]),
4797    {ok, InFd} = file:open(Src, [raw, binary, read]),
4798    {ok, OutFd} = file:open(Dest, [raw, binary, write]),
4799    ok = copy_file1(InFd, OutFd),
4800    file:close(InFd),
4801    file:close(OutFd),
4802    ok = file:change_mode(Dest, 8#0666).
4803
4804copy_file1(InFd, OutFd) ->
4805    case file:read(InFd, ?BUFSIZE) of
4806        {ok, Bin} ->
4807            ok = file:write(OutFd, Bin),
4808            copy_file1(InFd, OutFd);
4809        eof  ->
4810            ok
4811    end.
4812
4813
4814join(A, B) ->
4815    filename:nativename(filename:join(A, B)).
4816
4817add_ext(Name, Ext) ->
4818    lists:concat([Name, ".", Ext]).
4819
4820log(_Name, 0) ->
4821    ok;
4822log(Name, N) ->
4823    ok = disk_log:log(Name, "this is a logged message number " ++
4824                      integer_to_list(N)),
4825    log(Name, N-1).
4826
4827format_error(E) ->
4828    lists:flatten(disk_log:format_error(E)).
4829
4830check_pps({Ports0,Procs0} = P0) ->
4831    case pps() of
4832        P0 ->
4833            ok;
4834        _ ->
4835            timer:sleep(500),
4836            case pps() of
4837                P0 ->
4838                    ok;
4839                {Ports1,Procs1} = P1 ->
4840		    case {Ports1 -- Ports0, Procs1 -- Procs0} of
4841			{[], []} -> ok;
4842			{PortsDiff,ProcsDiff} ->
4843			    io:format("failure, got ~p~n, expected ~p\n", [P1, P0]),
4844			    show("Old port", Ports0 -- Ports1),
4845			    show("New port", PortsDiff),
4846			    show("Old proc", Procs0 -- Procs1),
4847			    show("New proc", ProcsDiff),
4848			    ct:fail(failed)
4849		    end
4850	    end
4851    end.
4852
4853show(_S, []) ->
4854    ok;
4855show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) ->
4856    io:format("~s: ~w (~w), ~w: ~p~n",
4857              [S, Pid, proc_reg_name(Name), InitCall,
4858               erlang:process_info(Pid)]),
4859    show(S, Pids);
4860show(S, [{Port, _}|Ports]) when is_port(Port)->
4861    io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]),
4862    show(S, Ports).
4863
4864pps() ->
4865    timer:sleep(100),
4866    {port_list(), process_list()}.
4867
4868port_list() ->
4869    [{P,safe_second_element(erlang:port_info(P, name))} ||
4870        P <- erlang:ports()].
4871
4872process_list() ->
4873    [{P,process_info(P, registered_name),
4874      safe_second_element(process_info(P, initial_call))} ||
4875        P <- processes(), erlang:is_process_alive(P)].
4876
4877proc_reg_name({registered_name, Name}) -> Name;
4878proc_reg_name([]) -> no_reg_name.
4879
4880safe_second_element({_,Info}) -> Info;
4881safe_second_element(Other) -> Other.
4882
4883
4884qlen() ->
4885    {_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())),
4886    N.
4887
4888owners(Log) ->
4889%%     io:format("owners ~p~n", [info(Log, owners, -1)]),
4890    info(Log, owners, -1).
4891users(Log) ->
4892%%     io:format("users ~p~n", [info(Log, users, -1)]),
4893    info(Log, users, -1).
4894status(Log) ->
4895%%     io:format("status ~p~n", [info(Log, status, -1)]),
4896    info(Log, status, -1).
4897distributed(Log) ->
4898%%     io:format("distributed ~p~n", [info(Log, distributed, -1)]),
4899    info(Log, distributed, -1).
4900no_items(Log) ->
4901%%     io:format("no_items ~p~n", [info(Log, no_items, -1)]),
4902    info(Log, no_items, -1).
4903no_written_items(Log) ->
4904%%     io:format("no_written_items ~p~n", [info(Log, no_written_items, -1)]),
4905    info(Log, no_written_items, -1).
4906sz(Log) ->
4907%%     io:format("sz ~p~n", [info(Log, size, -1)]),
4908    info(Log, size, -1).
4909curb(Log) ->
4910%%     io:format("curb ~p~n", [info(Log, no_current_bytes, -1)]),
4911    info(Log, no_current_bytes, -1).
4912curf(Log) ->
4913%%     io:format("curf ~p~n", [info(Log, current_file, -1)]),
4914    info(Log, current_file, -1).
4915cur_cnt(Log) ->
4916%%     io:format("cur_cnt ~p~n", [info(Log, no_current_items, -1)]),
4917    info(Log, no_current_items, -1).
4918no_overflows(Log) ->
4919%%     io:format("no_overflows ~p~n", [info(Log, no_overflows, -1)]),
4920    info(Log, no_overflows, -1).
4921
4922info(Log, What, Undef) ->
4923    case lists:keysearch(What, 1, disk_log:info(Log)) of
4924        {value, {What, Value}} -> Value;
4925        false -> Undef
4926    end.
4927
4928rec(0, _) ->
4929     ok;
4930rec(N, Msg) ->
4931    receive
4932	Msg ->
4933	    rec(N-1, Msg)
4934    after 100 ->
4935	    test_server_fail({no_msg, N, Msg})
4936    end.
4937
4938%% Copied from global_SUITE.erl.
4939-define(UNTIL(Seq), loop_until_true(fun() -> Seq end)).
4940
4941loop_until_true(Fun) ->
4942    case Fun() of
4943	true ->
4944	    ok;
4945	_ ->
4946	    timer:sleep(1000),
4947	    loop_until_true(Fun)
4948    end.
4949
4950wait_for_ready_net() ->
4951    Nodes = lists:sort([node() | nodes()]),
4952    ?UNTIL(begin
4953               lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and
4954               lists:all(fun(N) ->
4955                                 LNs = rpc:call(N, erlang, nodes, []),
4956                                 Nodes =:= lists:sort([N | LNs])
4957                         end, Nodes)
4958           end).
4959
4960get_known(Node) ->
4961    case catch gen_server:call({global_name_server,Node}, get_known) of
4962        {'EXIT', _} ->
4963            [list, without, nodenames];
4964        Known ->
4965            lists:sort([Node | Known])
4966    end.
4967
4968%% Copied from erl_distribution_SUITE.erl:
4969start_node(Name, Param) ->
4970    test_server:start_node(Name, slave, [{args, Param}]).
4971
4972stop_node(Node) ->
4973    test_server:stop_node(Node).
4974
4975%% from(H, [H | T]) -> T;
4976%% from(H, [_ | T]) -> from(H, T);
4977%% from(_H, []) -> [].
4978
4979
4980%%-----------------------------------------------------------------
4981%% The error_logger handler used.
4982%% (Copied from stdlib/test/proc_lib_SUITE.erl.)
4983%%-----------------------------------------------------------------
4984init(Tester) ->
4985    {ok, Tester}.
4986
4987handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) ->
4988    Tester ! {crash_report, Pid, Report},
4989    {ok, Tester};
4990handle_event({info_msg, _GL, {Pid, F,A}}, Tester) ->
4991    Tester ! {info_msg, Pid, F, A},
4992    {ok, Tester};
4993handle_event(_Event, State) ->
4994    {ok, State}.
4995
4996handle_info(_, State) ->
4997    {ok, State}.
4998
4999handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
5000
5001terminate(_Reason, State) ->
5002    State.
5003