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