PageRenderTime 75ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/kernel/src/disk_log.erl

https://github.com/bsmr-erlang/otp
Erlang | 1999 lines | 1714 code | 175 blank | 110 comment | 5 complexity | 7da78e130d35ad49cca8b749a6ac4ac1 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 1997-2018. All Rights Reserved.
  5. %%
  6. %% Licensed under the Apache License, Version 2.0 (the "License");
  7. %% you may not use this file except in compliance with the License.
  8. %% You may obtain a copy of the License at
  9. %%
  10. %% http://www.apache.org/licenses/LICENSE-2.0
  11. %%
  12. %% Unless required by applicable law or agreed to in writing, software
  13. %% distributed under the License is distributed on an "AS IS" BASIS,
  14. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. %% See the License for the specific language governing permissions and
  16. %% limitations under the License.
  17. %%
  18. %% %CopyrightEnd%
  19. %%
  20. -module(disk_log).
  21. %% Efficient file based log - process part
  22. -export([start/0, istart_link/1,
  23. log/2, log_terms/2, blog/2, blog_terms/2,
  24. alog/2, alog_terms/2, balog/2, balog_terms/2,
  25. close/1, lclose/1, lclose/2, sync/1, open/1,
  26. truncate/1, truncate/2, btruncate/2,
  27. reopen/2, reopen/3, breopen/3, inc_wrap_file/1, change_size/2,
  28. change_notify/3, change_header/2,
  29. chunk/2, chunk/3, bchunk/2, bchunk/3, chunk_step/3, chunk_info/1,
  30. block/1, block/2, unblock/1, info/1, format_error/1,
  31. accessible_logs/0]).
  32. %% Internal exports
  33. -export([init/2, internal_open/2,
  34. system_continue/3, system_terminate/4, system_code_change/4]).
  35. %% To be used by disk_log_h.erl (not (yet) in Erlang/OTP) only.
  36. -export([ll_open/1, ll_close/1, do_log/2, do_sync/1, do_info/2]).
  37. %% To be used by wrap_log_reader only.
  38. -export([ichunk_end/2]).
  39. %% To be used for debugging only:
  40. -export([pid2name/1]).
  41. -export_type([continuation/0]).
  42. -type dlog_state_error() :: 'ok' | {'error', term()}.
  43. -record(state, {queue = [],
  44. messages = [],
  45. parent,
  46. server,
  47. cnt = 0 :: non_neg_integer(),
  48. args,
  49. error_status = ok :: dlog_state_error(),
  50. cache_error = ok %% cache write error after timeout
  51. }).
  52. -include("disk_log.hrl").
  53. -define(failure(Error, Function, Arg),
  54. {{failed, Error}, [{?MODULE, Function, Arg}]}).
  55. %%-define(PROFILE(C), C).
  56. -define(PROFILE(C), void).
  57. -compile({inline,[{log_loop,6},{log_end_sync,2},{replies,2},{rflat,1}]}).
  58. %%%----------------------------------------------------------------------
  59. %%% Contract type specifications
  60. %%%----------------------------------------------------------------------
  61. -opaque continuation() :: #continuation{}.
  62. -type file_error() :: term(). % XXX: refine
  63. -type invalid_header() :: term(). % XXX: refine
  64. %%%----------------------------------------------------------------------
  65. %%% API
  66. %%%----------------------------------------------------------------------
  67. %%-----------------------------------------------------------------
  68. %% This module implements the API, and the processes for each log.
  69. %% There is one process per log.
  70. %%-----------------------------------------------------------------
  71. -type open_error_rsn() :: 'no_such_log'
  72. | {'badarg', term()}
  73. | {'size_mismatch', CurrentSize :: dlog_size(),
  74. NewSize :: dlog_size()}
  75. | {'arg_mismatch', OptionName :: dlog_optattr(),
  76. CurrentValue :: term(), Value :: term()}
  77. | {'name_already_open', Log :: log()}
  78. | {'open_read_write', Log :: log()}
  79. | {'open_read_only', Log :: log()}
  80. | {'need_repair', Log :: log()}
  81. | {'not_a_log_file', FileName :: file:filename()}
  82. | {'invalid_index_file', FileName :: file:filename()}
  83. | {'invalid_header', invalid_header()}
  84. | {'file_error', file:filename(), file_error()}
  85. | {'node_already_open', Log :: log()}.
  86. -type dist_error_rsn() :: 'nodedown' | open_error_rsn().
  87. -type ret() :: {'ok', Log :: log()}
  88. | {'repaired', Log :: log(),
  89. {'recovered', Rec :: non_neg_integer()},
  90. {'badbytes', Bad :: non_neg_integer()}}.
  91. -type open_ret() :: ret() | {'error', open_error_rsn()}.
  92. -type dist_open_ret() :: {[{node(), ret()}],
  93. [{node(), {'error', dist_error_rsn()}}]}.
  94. -spec open(ArgL) -> open_ret() | dist_open_ret() when
  95. ArgL :: dlog_options().
  96. open(A) ->
  97. disk_log_server:open(check_arg(A, #arg{options = A})).
  98. -type log_error_rsn() :: 'no_such_log' | 'nonode' | {'read_only_mode', log()}
  99. | {'format_external', log()} | {'blocked_log', log()}
  100. | {'full', log()} | {'invalid_header', invalid_header()}
  101. | {'file_error', file:filename(), file_error()}.
  102. -spec log(Log, Term) -> ok | {error, Reason :: log_error_rsn()} when
  103. Log :: log(),
  104. Term :: term().
  105. log(Log, Term) ->
  106. req(Log, {log, internal, [term_to_binary(Term)]}).
  107. -spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when
  108. Log :: log(),
  109. Bytes :: iodata().
  110. blog(Log, Bytes) ->
  111. req(Log, {log, external, [ensure_binary(Bytes)]}).
  112. -spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when
  113. Log :: log(),
  114. TermList :: [term()].
  115. log_terms(Log, Terms) ->
  116. Bs = terms2bins(Terms),
  117. req(Log, {log, internal, Bs}).
  118. -spec blog_terms(Log, BytesList) ->
  119. ok | {error, Reason :: log_error_rsn()} when
  120. Log :: log(),
  121. BytesList :: [iodata()].
  122. blog_terms(Log, Bytess) ->
  123. Bs = ensure_binary_list(Bytess),
  124. req(Log, {log, external, Bs}).
  125. -type notify_ret() :: 'ok' | {'error', 'no_such_log'}.
  126. -spec alog(Log, Term) -> notify_ret() when
  127. Log :: log(),
  128. Term :: term().
  129. alog(Log, Term) ->
  130. notify(Log, {alog, internal, [term_to_binary(Term)]}).
  131. -spec alog_terms(Log, TermList) -> notify_ret() when
  132. Log :: log(),
  133. TermList :: [term()].
  134. alog_terms(Log, Terms) ->
  135. Bs = terms2bins(Terms),
  136. notify(Log, {alog, internal, Bs}).
  137. -spec balog(Log, Bytes) -> notify_ret() when
  138. Log :: log(),
  139. Bytes :: iodata().
  140. balog(Log, Bytes) ->
  141. notify(Log, {alog, external, [ensure_binary(Bytes)]}).
  142. -spec balog_terms(Log, ByteList) -> notify_ret() when
  143. Log :: log(),
  144. ByteList :: [iodata()].
  145. balog_terms(Log, Bytess) ->
  146. Bs = ensure_binary_list(Bytess),
  147. notify(Log, {alog, external, Bs}).
  148. -type close_error_rsn() ::'no_such_log' | 'nonode'
  149. | {'file_error', file:filename(), file_error()}.
  150. -spec close(Log) -> 'ok' | {'error', close_error_rsn()} when
  151. Log :: log().
  152. close(Log) ->
  153. req(Log, close).
  154. -type lclose_error_rsn() :: 'no_such_log'
  155. | {'file_error', file:filename(), file_error()}.
  156. -spec lclose(Log) -> 'ok' | {'error', lclose_error_rsn()} when
  157. Log :: log().
  158. lclose(Log) ->
  159. lclose(Log, node()).
  160. -spec lclose(Log, Node) -> 'ok' | {'error', lclose_error_rsn()} when
  161. Log :: log(),
  162. Node :: node().
  163. lclose(Log, Node) ->
  164. lreq(Log, close, Node).
  165. -type trunc_error_rsn() :: 'no_such_log' | 'nonode'
  166. | {'read_only_mode', log()}
  167. | {'blocked_log', log()}
  168. | {'invalid_header', invalid_header()}
  169. | {'file_error', file:filename(), file_error()}.
  170. -spec truncate(Log) -> 'ok' | {'error', trunc_error_rsn()} when
  171. Log :: log().
  172. truncate(Log) ->
  173. req(Log, {truncate, none, truncate, 1}).
  174. -spec truncate(Log, Head) -> 'ok' | {'error', trunc_error_rsn()} when
  175. Log :: log(),
  176. Head :: term().
  177. truncate(Log, Head) ->
  178. req(Log, {truncate, {ok, term_to_binary(Head)}, truncate, 2}).
  179. -spec btruncate(Log, BHead) -> 'ok' | {'error', trunc_error_rsn()} when
  180. Log :: log(),
  181. BHead :: iodata().
  182. btruncate(Log, Head) ->
  183. req(Log, {truncate, {ok, ensure_binary(Head)}, btruncate, 2}).
  184. -type reopen_error_rsn() :: no_such_log
  185. | nonode
  186. | {read_only_mode, log()}
  187. | {blocked_log, log()}
  188. | {same_file_name, log()} |
  189. {invalid_index_file, file:filename()}
  190. | {invalid_header, invalid_header()}
  191. | {'file_error', file:filename(), file_error()}.
  192. -spec reopen(Log, File) -> 'ok' | {'error', reopen_error_rsn()} when
  193. Log :: log(),
  194. File :: file:filename().
  195. reopen(Log, NewFile) ->
  196. req(Log, {reopen, NewFile, none, reopen, 2}).
  197. -spec reopen(Log, File, Head) -> 'ok' | {'error', reopen_error_rsn()} when
  198. Log :: log(),
  199. File :: file:filename(),
  200. Head :: term().
  201. reopen(Log, NewFile, NewHead) ->
  202. req(Log, {reopen, NewFile, {ok, term_to_binary(NewHead)}, reopen, 3}).
  203. -spec breopen(Log, File, BHead) -> 'ok' | {'error', reopen_error_rsn()} when
  204. Log :: log(),
  205. File :: file:filename(),
  206. BHead :: iodata().
  207. breopen(Log, NewFile, NewHead) ->
  208. req(Log, {reopen, NewFile, {ok, ensure_binary(NewHead)}, breopen, 3}).
  209. -type inc_wrap_error_rsn() :: 'no_such_log' | 'nonode'
  210. | {'read_only_mode', log()}
  211. | {'blocked_log', log()} | {'halt_log', log()}
  212. | {'invalid_header', invalid_header()}
  213. | {'file_error', file:filename(), file_error()}.
  214. -spec inc_wrap_file(Log) -> 'ok' | {'error', inc_wrap_error_rsn()} when
  215. Log :: log().
  216. inc_wrap_file(Log) ->
  217. req(Log, inc_wrap_file).
  218. -spec change_size(Log, Size) -> 'ok' | {'error', Reason} when
  219. Log :: log(),
  220. Size :: dlog_size(),
  221. Reason :: no_such_log | nonode | {read_only_mode, Log}
  222. | {blocked_log, Log}
  223. | {new_size_too_small, Log, CurrentSize :: pos_integer()}
  224. | {badarg, size}
  225. | {file_error, file:filename(), file_error()}.
  226. change_size(Log, NewSize) ->
  227. req(Log, {change_size, NewSize}).
  228. -spec change_notify(Log, Owner, Notify) -> 'ok' | {'error', Reason} when
  229. Log :: log(),
  230. Owner :: pid(),
  231. Notify :: boolean(),
  232. Reason :: no_such_log | nonode | {blocked_log, Log}
  233. | {badarg, notify} | {not_owner, Owner}.
  234. change_notify(Log, Pid, NewNotify) ->
  235. req(Log, {change_notify, Pid, NewNotify}).
  236. -spec change_header(Log, Header) -> 'ok' | {'error', Reason} when
  237. Log :: log(),
  238. Header :: {head, dlog_head_opt()}
  239. | {head_func, MFA :: {atom(), atom(), list()}},
  240. Reason :: no_such_log | nonode | {read_only_mode, Log}
  241. | {blocked_log, Log} | {badarg, head}.
  242. change_header(Log, NewHead) ->
  243. req(Log, {change_header, NewHead}).
  244. -type sync_error_rsn() :: 'no_such_log' | 'nonode' | {'read_only_mode', log()}
  245. | {'blocked_log', log()}
  246. | {'file_error', file:filename(), file_error()}.
  247. -spec sync(Log) -> 'ok' | {'error', sync_error_rsn()} when
  248. Log :: log().
  249. sync(Log) ->
  250. req(Log, sync).
  251. -type block_error_rsn() :: 'no_such_log' | 'nonode' | {'blocked_log', log()}.
  252. -spec block(Log) -> 'ok' | {'error', block_error_rsn()} when
  253. Log :: log().
  254. block(Log) ->
  255. block(Log, true).
  256. -spec block(Log, QueueLogRecords) -> 'ok' | {'error', block_error_rsn()} when
  257. Log :: log(),
  258. QueueLogRecords :: boolean().
  259. block(Log, QueueLogRecords) ->
  260. req(Log, {block, QueueLogRecords}).
  261. -type unblock_error_rsn() :: 'no_such_log' | 'nonode'
  262. | {'not_blocked', log()}
  263. | {'not_blocked_by_pid', log()}.
  264. -spec unblock(Log) -> 'ok' | {'error', unblock_error_rsn()} when
  265. Log :: log().
  266. unblock(Log) ->
  267. req(Log, unblock).
  268. -spec format_error(Error) -> io_lib:chars() when
  269. Error :: term().
  270. format_error(Error) ->
  271. do_format_error(Error).
  272. -type dlog_info() :: {name, Log :: log()}
  273. | {file, File :: file:filename()}
  274. | {type, Type :: dlog_type()}
  275. | {format, Format :: dlog_format()}
  276. | {size, Size :: dlog_size()}
  277. | {mode, Mode :: dlog_mode()}
  278. | {owners, [{pid(), Notify :: boolean()}]}
  279. | {users, Users :: non_neg_integer()}
  280. | {status, Status ::
  281. ok | {blocked, QueueLogRecords :: boolean()}}
  282. | {node, Node :: node()}
  283. | {distributed, Dist :: local | [node()]}
  284. | {head, Head :: none
  285. | {head, term()}
  286. | (MFA :: {atom(), atom(), list()})}
  287. | {no_written_items, NoWrittenItems ::non_neg_integer()}
  288. | {full, Full :: boolean}
  289. | {no_current_bytes, non_neg_integer()}
  290. | {no_current_items, non_neg_integer()}
  291. | {no_items, non_neg_integer()}
  292. | {current_file, pos_integer()}
  293. | {no_overflows, {SinceLogWasOpened :: non_neg_integer(),
  294. SinceLastInfo :: non_neg_integer()}}.
  295. -spec info(Log) -> InfoList | {'error', no_such_log} when
  296. Log :: log(),
  297. InfoList :: [dlog_info()].
  298. info(Log) ->
  299. sreq(Log, info).
  300. -spec pid2name(Pid) -> {'ok', Log} | 'undefined' when
  301. Pid :: pid(),
  302. Log :: log().
  303. pid2name(Pid) ->
  304. disk_log_server:start(),
  305. case ets:lookup(?DISK_LOG_PID_TABLE, Pid) of
  306. [] -> undefined;
  307. [{_Pid, Log}] -> {ok, Log}
  308. end.
  309. %% This function Takes 3 args, a Log, a Continuation and N.
  310. %% It retuns a {Cont2, ObjList} | eof | {error, Reason}
  311. %% The initial continuation is the atom 'start'
  312. -type chunk_error_rsn() :: no_such_log
  313. | {format_external, log()}
  314. | {blocked_log, log()}
  315. | {badarg, continuation}
  316. | {not_internal_wrap, log()}
  317. | {corrupt_log_file, FileName :: file:filename()}
  318. | {file_error, file:filename(), file_error()}.
  319. -type chunk_ret() :: {Continuation2 :: continuation(), Terms :: [term()]}
  320. | {Continuation2 :: continuation(),
  321. Terms :: [term()],
  322. Badbytes :: non_neg_integer()}
  323. | eof
  324. | {error, Reason :: chunk_error_rsn()}.
  325. -spec chunk(Log, Continuation) -> chunk_ret() when
  326. Log :: log(),
  327. Continuation :: start | continuation().
  328. chunk(Log, Cont) ->
  329. chunk(Log, Cont, infinity).
  330. -spec chunk(Log, Continuation, N) -> chunk_ret() when
  331. Log :: log(),
  332. Continuation :: start | continuation(),
  333. N :: pos_integer() | infinity.
  334. chunk(Log, Cont, infinity) ->
  335. %% There cannot be more than ?MAX_CHUNK_SIZE terms in a chunk.
  336. ichunk(Log, Cont, ?MAX_CHUNK_SIZE);
  337. chunk(Log, Cont, N) when is_integer(N), N > 0 ->
  338. ichunk(Log, Cont, N).
  339. ichunk(Log, start, N) ->
  340. R = sreq(Log, {chunk, 0, [], N}),
  341. ichunk_end(R, Log);
  342. ichunk(Log, More, N) when is_record(More, continuation) ->
  343. R = req2(More#continuation.pid,
  344. {chunk, More#continuation.pos, More#continuation.b, N}),
  345. ichunk_end(R, Log);
  346. ichunk(_Log, _, _) ->
  347. {error, {badarg, continuation}}.
  348. ichunk_end({C, R}, Log) when is_record(C, continuation) ->
  349. ichunk_end(R, read_write, Log, C, 0);
  350. ichunk_end({C, R, Bad}, Log) when is_record(C, continuation) ->
  351. ichunk_end(R, read_only, Log, C, Bad);
  352. ichunk_end(R, _Log) ->
  353. R.
  354. %% Create the terms on the client's heap, not the server's.
  355. %% The list of binaries is reversed.
  356. ichunk_end(R, Mode, Log, C, Bad) ->
  357. case catch bins2terms(R, []) of
  358. {'EXIT', _} ->
  359. RR = lists:reverse(R),
  360. ichunk_bad_end(RR, Mode, Log, C, Bad, []);
  361. Ts when Bad > 0 ->
  362. {C, Ts, Bad};
  363. Ts when Bad =:= 0 ->
  364. {C, Ts}
  365. end.
  366. bins2terms([], L) ->
  367. L;
  368. bins2terms([B | Bs], L) ->
  369. bins2terms(Bs, [binary_to_term(B) | L]).
  370. ichunk_bad_end([B | Bs], Mode, Log, C, Bad, A) ->
  371. case catch binary_to_term(B) of
  372. {'EXIT', _} when read_write =:= Mode ->
  373. InfoList = info(Log),
  374. {value, {file, FileName}} = lists:keysearch(file, 1, InfoList),
  375. File = case C#continuation.pos of
  376. Pos when is_integer(Pos) -> FileName; % halt log
  377. {FileNo, _} -> add_ext(FileName, FileNo) % wrap log
  378. end,
  379. {error, {corrupt_log_file, File}};
  380. {'EXIT', _} when read_only =:= Mode ->
  381. Reread = lists:foldl(fun(Bin, Sz) ->
  382. Sz + byte_size(Bin) + ?HEADERSZ
  383. end, 0, Bs),
  384. NewPos = case C#continuation.pos of
  385. Pos when is_integer(Pos) -> Pos-Reread;
  386. {FileNo, Pos} -> {FileNo, Pos-Reread}
  387. end,
  388. NewBad = Bad + byte_size(B),
  389. {C#continuation{pos = NewPos, b = []}, lists:reverse(A), NewBad};
  390. T ->
  391. ichunk_bad_end(Bs, Mode, Log, C, Bad, [T | A])
  392. end.
  393. -type bchunk_ret() :: {Continuation2 :: continuation(),
  394. Binaries :: [binary()]}
  395. | {Continuation2 :: continuation(),
  396. Binaries :: [binary()],
  397. Badbytes :: non_neg_integer()}
  398. | eof
  399. | {error, Reason :: chunk_error_rsn()}.
  400. -spec bchunk(Log, Continuation) -> bchunk_ret() when
  401. Log :: log(),
  402. Continuation :: start | continuation().
  403. bchunk(Log, Cont) ->
  404. bchunk(Log, Cont, infinity).
  405. -spec bchunk(Log, Continuation, N) -> bchunk_ret() when
  406. Log :: log(),
  407. Continuation :: start | continuation(),
  408. N :: pos_integer() | infinity.
  409. bchunk(Log, Cont, infinity) ->
  410. %% There cannot be more than ?MAX_CHUNK_SIZE terms in a chunk.
  411. bichunk(Log, Cont, ?MAX_CHUNK_SIZE);
  412. bchunk(Log, Cont, N) when is_integer(N), N > 0 ->
  413. bichunk(Log, Cont, N).
  414. bichunk(Log, start, N) ->
  415. R = sreq(Log, {chunk, 0, [], N}),
  416. bichunk_end(R);
  417. bichunk(_Log, #continuation{pid = Pid, pos = Pos, b = B}, N) ->
  418. R = req2(Pid, {chunk, Pos, B, N}),
  419. bichunk_end(R);
  420. bichunk(_Log, _, _) ->
  421. {error, {badarg, continuation}}.
  422. bichunk_end({C = #continuation{}, R}) ->
  423. {C, lists:reverse(R)};
  424. bichunk_end({C = #continuation{}, R, Bad}) ->
  425. {C, lists:reverse(R), Bad};
  426. bichunk_end(R) ->
  427. R.
  428. -spec chunk_step(Log, Continuation, Step) ->
  429. {'ok', any()} | {'error', Reason} when
  430. Log :: log(),
  431. Continuation :: start | continuation(),
  432. Step :: integer(),
  433. Reason :: no_such_log | end_of_log | {format_external, Log}
  434. | {blocked_log, Log} | {badarg, continuation}
  435. | {file_error, file:filename(), file_error()}.
  436. chunk_step(Log, Cont, N) when is_integer(N) ->
  437. ichunk_step(Log, Cont, N).
  438. ichunk_step(Log, start, N) ->
  439. sreq(Log, {chunk_step, 0, N});
  440. ichunk_step(_Log, More, N) when is_record(More, continuation) ->
  441. req2(More#continuation.pid, {chunk_step, More#continuation.pos, N});
  442. ichunk_step(_Log, _, _) ->
  443. {error, {badarg, continuation}}.
  444. -spec chunk_info(Continuation) -> InfoList | {error, Reason} when
  445. Continuation :: continuation(),
  446. InfoList :: [{node, Node :: node()}, ...],
  447. Reason :: {no_continuation, Continuation}.
  448. chunk_info(More = #continuation{}) ->
  449. [{node, node(More#continuation.pid)}];
  450. chunk_info(BadCont) ->
  451. {error, {no_continuation, BadCont}}.
  452. -spec accessible_logs() -> {[LocalLog], [DistributedLog]} when
  453. LocalLog :: log(),
  454. DistributedLog :: log().
  455. accessible_logs() ->
  456. disk_log_server:accessible_logs().
  457. istart_link(Server) ->
  458. {ok, proc_lib:spawn_link(disk_log, init, [self(), Server])}.
  459. %% Only for backwards compatibility, could probably be removed.
  460. -spec start() -> 'ok'.
  461. start() ->
  462. disk_log_server:start().
  463. internal_open(Pid, A) ->
  464. req2(Pid, {internal_open, A}).
  465. %%% ll_open() and ll_close() are used by disk_log_h.erl, a module not
  466. %%% (yet) in Erlang/OTP.
  467. %% -spec ll_open(dlog_options()) -> {'ok', Res :: _, #log{}, Cnt :: _} | Error.
  468. ll_open(A) ->
  469. case check_arg(A, #arg{options = A}) of
  470. {ok, L} -> do_open(L);
  471. Error -> Error
  472. end.
  473. %% -> closed | throw(Error)
  474. ll_close(Log) ->
  475. close_disk_log2(Log).
  476. check_arg([], Res) ->
  477. Ret = case Res#arg.head of
  478. none ->
  479. {ok, Res};
  480. _ ->
  481. case check_head(Res#arg.head, Res#arg.format) of
  482. {ok, Head} ->
  483. {ok, Res#arg{head = Head}};
  484. Error ->
  485. Error
  486. end
  487. end,
  488. if %% check result
  489. Res#arg.name =:= 0 ->
  490. {error, {badarg, name}};
  491. Res#arg.file =:= none ->
  492. case catch lists:concat([Res#arg.name, ".LOG"]) of
  493. {'EXIT',_} -> {error, {badarg, file}};
  494. FName -> check_arg([], Res#arg{file = FName})
  495. end;
  496. Res#arg.repair =:= truncate, Res#arg.mode =:= read_only ->
  497. {error, {badarg, repair_read_only}};
  498. Res#arg.type =:= halt, is_tuple(Res#arg.size) ->
  499. {error, {badarg, size}};
  500. Res#arg.type =:= wrap ->
  501. {OldSize, Version} =
  502. disk_log_1:read_size_file_version(Res#arg.file),
  503. check_wrap_arg(Ret, OldSize, Version);
  504. true ->
  505. Ret
  506. end;
  507. check_arg([{file, F} | Tail], Res) when is_list(F) ->
  508. check_arg(Tail, Res#arg{file = F});
  509. check_arg([{file, F} | Tail], Res) when is_atom(F) ->
  510. check_arg(Tail, Res#arg{file = F});
  511. check_arg([{linkto, Pid} |Tail], Res) when is_pid(Pid) ->
  512. check_arg(Tail, Res#arg{linkto = Pid});
  513. check_arg([{linkto, none} |Tail], Res) ->
  514. check_arg(Tail, Res#arg{linkto = none});
  515. check_arg([{name, Name}|Tail], Res) ->
  516. check_arg(Tail, Res#arg{name = Name});
  517. check_arg([{repair, true}|Tail], Res) ->
  518. check_arg(Tail, Res#arg{repair = true});
  519. check_arg([{repair, false}|Tail], Res) ->
  520. check_arg(Tail, Res#arg{repair = false});
  521. check_arg([{repair, truncate}|Tail], Res) ->
  522. check_arg(Tail, Res#arg{repair = truncate});
  523. check_arg([{size, Int}|Tail], Res) when is_integer(Int), Int > 0 ->
  524. check_arg(Tail, Res#arg{size = Int});
  525. check_arg([{size, infinity}|Tail], Res) ->
  526. check_arg(Tail, Res#arg{size = infinity});
  527. check_arg([{size, {MaxB,MaxF}}|Tail], Res) when is_integer(MaxB),
  528. is_integer(MaxF),
  529. MaxB > 0, MaxB =< ?MAX_BYTES,
  530. MaxF > 0, MaxF < ?MAX_FILES ->
  531. check_arg(Tail, Res#arg{size = {MaxB, MaxF}});
  532. check_arg([{type, wrap}|Tail], Res) ->
  533. check_arg(Tail, Res#arg{type = wrap});
  534. check_arg([{type, halt}|Tail], Res) ->
  535. check_arg(Tail, Res#arg{type = halt});
  536. check_arg([{format, internal}|Tail], Res) ->
  537. check_arg(Tail, Res#arg{format = internal});
  538. check_arg([{format, external}|Tail], Res) ->
  539. check_arg(Tail, Res#arg{format = external});
  540. check_arg([{distributed, []}|Tail], Res) ->
  541. check_arg(Tail, Res#arg{distributed = false});
  542. check_arg([{distributed, Nodes}|Tail], Res) when is_list(Nodes) ->
  543. check_arg(Tail, Res#arg{distributed = {true, Nodes}});
  544. check_arg([{notify, true}|Tail], Res) ->
  545. check_arg(Tail, Res#arg{notify = true});
  546. check_arg([{notify, false}|Tail], Res) ->
  547. check_arg(Tail, Res#arg{notify = false});
  548. check_arg([{head_func, HeadFunc}|Tail], Res) ->
  549. check_arg(Tail, Res#arg{head = {head_func, HeadFunc}});
  550. check_arg([{head, Term}|Tail], Res) ->
  551. check_arg(Tail, Res#arg{head = {head, Term}});
  552. check_arg([{mode, read_only}|Tail], Res) ->
  553. check_arg(Tail, Res#arg{mode = read_only});
  554. check_arg([{mode, read_write}|Tail], Res) ->
  555. check_arg(Tail, Res#arg{mode = read_write});
  556. check_arg([{quiet, Boolean}|Tail], Res) when is_boolean(Boolean) ->
  557. check_arg(Tail, Res#arg{quiet = Boolean});
  558. check_arg(Arg, _) ->
  559. {error, {badarg, Arg}}.
  560. check_wrap_arg({ok, Res}, {0,0}, _Version) when Res#arg.size =:= infinity ->
  561. {error, {badarg, size}};
  562. check_wrap_arg({ok, Res}, OldSize, Version) when Res#arg.size =:= infinity ->
  563. NewRes = Res#arg{size = OldSize},
  564. check_wrap_arg({ok, NewRes}, OldSize, Version);
  565. check_wrap_arg({ok, Res}, {0,0}, Version) ->
  566. {ok, Res#arg{version = Version}};
  567. check_wrap_arg({ok, Res}, OldSize, Version) when OldSize =:= Res#arg.size ->
  568. {ok, Res#arg{version = Version}};
  569. check_wrap_arg({ok, Res}, _OldSize, Version) when Res#arg.repair =:= truncate,
  570. is_tuple(Res#arg.size) ->
  571. {ok, Res#arg{version = Version}};
  572. check_wrap_arg({ok, Res}, OldSize, _Version) when is_tuple(Res#arg.size) ->
  573. {error, {size_mismatch, OldSize, Res#arg.size}};
  574. check_wrap_arg({ok, _Res}, _OldSize, _Version) ->
  575. {error, {badarg, size}};
  576. check_wrap_arg(Ret, _OldSize, _Version) ->
  577. Ret.
  578. %%%-----------------------------------------------------------------
  579. %%% Server functions
  580. %%%-----------------------------------------------------------------
  581. init(Parent, Server) ->
  582. ?PROFILE(ep:do()),
  583. process_flag(trap_exit, true),
  584. loop(#state{parent = Parent, server = Server}).
  585. loop(#state{messages = []}=State) ->
  586. receive
  587. Message ->
  588. handle(Message, State)
  589. end;
  590. loop(#state{messages = [M | Ms]}=State) ->
  591. handle(M, State#state{messages = Ms}).
  592. handle({From, write_cache}, S) when From =:= self() ->
  593. case catch do_write_cache(get(log)) of
  594. ok ->
  595. loop(S);
  596. Error ->
  597. loop(S#state{cache_error = Error})
  598. end;
  599. handle({From, {log, Format, B}}=Message, S) ->
  600. case get(log) of
  601. #log{mode = read_only}=L ->
  602. reply(From, {error, {read_only_mode, L#log.name}}, S);
  603. #log{status = ok, format=external}=L when Format =:= internal ->
  604. reply(From, {error, {format_external, L#log.name}}, S);
  605. #log{status = ok, format=LogFormat} ->
  606. log_loop(S, From, [B], [], iolist_size(B), LogFormat);
  607. #log{status = {blocked, false}}=L ->
  608. reply(From, {error, {blocked_log, L#log.name}}, S);
  609. #log{blocked_by = From}=L ->
  610. reply(From, {error, {blocked_log, L#log.name}}, S);
  611. _ ->
  612. enqueue(Message, S)
  613. end;
  614. handle({alog, Format, B}=Message, S) ->
  615. case get(log) of
  616. #log{mode = read_only} ->
  617. notify_owners({read_only,B}),
  618. loop(S);
  619. #log{status = ok, format = external} when Format =:= internal ->
  620. notify_owners({format_external, B}),
  621. loop(S);
  622. #log{status = ok, format=LogFormat} ->
  623. log_loop(S, [], [B], [], iolist_size(B), LogFormat);
  624. #log{status = {blocked, false}} ->
  625. notify_owners({blocked_log, B}),
  626. loop(S);
  627. _ ->
  628. enqueue(Message, S)
  629. end;
  630. handle({From, {block, QueueLogRecs}}=Message, S) ->
  631. case get(log) of
  632. #log{status = ok}=L ->
  633. do_block(From, QueueLogRecs, L),
  634. reply(From, ok, S);
  635. #log{status = {blocked, false}}=L ->
  636. reply(From, {error, {blocked_log, L#log.name}}, S);
  637. #log{blocked_by = From}=L ->
  638. reply(From, {error, {blocked_log, L#log.name}}, S);
  639. _ ->
  640. enqueue(Message, S)
  641. end;
  642. handle({From, unblock}, S) ->
  643. case get(log) of
  644. #log{status = ok}=L ->
  645. reply(From, {error, {not_blocked, L#log.name}}, S);
  646. #log{blocked_by = From}=L ->
  647. S2 = do_unblock(L, S),
  648. reply(From, ok, S2);
  649. L ->
  650. reply(From, {error, {not_blocked_by_pid, L#log.name}}, S)
  651. end;
  652. handle({From, sync}=Message, S) ->
  653. case get(log) of
  654. #log{mode = read_only}=L ->
  655. reply(From, {error, {read_only_mode, L#log.name}}, S);
  656. #log{status = ok, format=LogFormat} ->
  657. log_loop(S, [], [], [From], 0, LogFormat);
  658. #log{status = {blocked, false}}=L ->
  659. reply(From, {error, {blocked_log, L#log.name}}, S);
  660. #log{blocked_by = From}=L ->
  661. reply(From, {error, {blocked_log, L#log.name}}, S);
  662. _ ->
  663. enqueue(Message, S)
  664. end;
  665. handle({From, {truncate, Head, F, A}}=Message, S) ->
  666. case get(log) of
  667. #log{mode = read_only}=L ->
  668. reply(From, {error, {read_only_mode, L#log.name}}, S);
  669. #log{status = ok} when S#state.cache_error =/= ok ->
  670. loop(cache_error(S, [From]));
  671. #log{status = ok}=L ->
  672. H = merge_head(Head, L#log.head),
  673. case catch do_trunc(L, H) of
  674. ok ->
  675. erase(is_full),
  676. notify_owners({truncated, S#state.cnt}),
  677. N = if Head =:= none -> 0; true -> 1 end,
  678. reply(From, ok, (state_ok(S))#state{cnt = N});
  679. Error ->
  680. do_exit(S, From, Error, ?failure(Error, F, A))
  681. end;
  682. #log{status = {blocked, false}}=L ->
  683. reply(From, {error, {blocked_log, L#log.name}}, S);
  684. #log{blocked_by = From}=L ->
  685. reply(From, {error, {blocked_log, L#log.name}}, S);
  686. _ ->
  687. enqueue(Message, S)
  688. end;
  689. handle({From, {chunk, Pos, B, N}}=Message, S) ->
  690. case get(log) of
  691. #log{status = ok} when S#state.cache_error =/= ok ->
  692. loop(cache_error(S, [From]));
  693. #log{status = ok}=L ->
  694. R = do_chunk(L, Pos, B, N),
  695. reply(From, R, S);
  696. #log{blocked_by = From}=L ->
  697. R = do_chunk(L, Pos, B, N),
  698. reply(From, R, S);
  699. #log{status = {blocked, false}}=L ->
  700. reply(From, {error, {blocked_log, L#log.name}}, S);
  701. _L ->
  702. enqueue(Message, S)
  703. end;
  704. handle({From, {chunk_step, Pos, N}}=Message, S) ->
  705. case get(log) of
  706. #log{status = ok} when S#state.cache_error =/= ok ->
  707. loop(cache_error(S, [From]));
  708. #log{status = ok}=L ->
  709. R = do_chunk_step(L, Pos, N),
  710. reply(From, R, S);
  711. #log{blocked_by = From}=L ->
  712. R = do_chunk_step(L, Pos, N),
  713. reply(From, R, S);
  714. #log{status = {blocked, false}}=L ->
  715. reply(From, {error, {blocked_log, L#log.name}}, S);
  716. _ ->
  717. enqueue(Message, S)
  718. end;
  719. handle({From, {change_notify, Pid, NewNotify}}=Message, S) ->
  720. case get(log) of
  721. #log{status = ok}=L ->
  722. case do_change_notify(L, Pid, NewNotify) of
  723. {ok, L1} ->
  724. put(log, L1),
  725. reply(From, ok, S);
  726. Error ->
  727. reply(From, Error, S)
  728. end;
  729. #log{status = {blocked, false}}=L ->
  730. reply(From, {error, {blocked_log, L#log.name}}, S);
  731. #log{blocked_by = From}=L ->
  732. reply(From, {error, {blocked_log, L#log.name}}, S);
  733. _ ->
  734. enqueue(Message, S)
  735. end;
  736. handle({From, {change_header, NewHead}}=Message, S) ->
  737. case get(log) of
  738. #log{mode = read_only}=L ->
  739. reply(From, {error, {read_only_mode, L#log.name}}, S);
  740. #log{status = ok, format = Format}=L ->
  741. case check_head(NewHead, Format) of
  742. {ok, Head} ->
  743. put(log, L#log{head = mk_head(Head, Format)}),
  744. reply(From, ok, S);
  745. Error ->
  746. reply(From, Error, S)
  747. end;
  748. #log{status = {blocked, false}}=L ->
  749. reply(From, {error, {blocked_log, L#log.name}}, S);
  750. #log{blocked_by = From}=L ->
  751. reply(From, {error, {blocked_log, L#log.name}}, S);
  752. _ ->
  753. enqueue(Message, S)
  754. end;
  755. handle({From, {change_size, NewSize}}=Message, S) ->
  756. case get(log) of
  757. #log{mode = read_only}=L ->
  758. reply(From, {error, {read_only_mode, L#log.name}}, S);
  759. #log{status = ok}=L ->
  760. case check_size(L#log.type, NewSize) of
  761. ok ->
  762. case catch do_change_size(L, NewSize) of % does the put
  763. ok ->
  764. reply(From, ok, S);
  765. {big, CurSize} ->
  766. reply(From,
  767. {error,
  768. {new_size_too_small, L#log.name, CurSize}},
  769. S);
  770. Else ->
  771. reply(From, Else, state_err(S, Else))
  772. end;
  773. not_ok ->
  774. reply(From, {error, {badarg, size}}, S)
  775. end;
  776. #log{status = {blocked, false}}=L ->
  777. reply(From, {error, {blocked_log, L#log.name}}, S);
  778. #log{blocked_by = From}=L ->
  779. reply(From, {error, {blocked_log, L#log.name}}, S);
  780. _ ->
  781. enqueue(Message, S)
  782. end;
  783. handle({From, inc_wrap_file}=Message, S) ->
  784. case get(log) of
  785. #log{mode = read_only}=L ->
  786. reply(From, {error, {read_only_mode, L#log.name}}, S);
  787. #log{type = halt}=L ->
  788. reply(From, {error, {halt_log, L#log.name}}, S);
  789. #log{status = ok} when S#state.cache_error =/= ok ->
  790. loop(cache_error(S, [From]));
  791. #log{status = ok}=L ->
  792. case catch do_inc_wrap_file(L) of
  793. {ok, L2, Lost} ->
  794. put(log, L2),
  795. notify_owners({wrap, Lost}),
  796. reply(From, ok, S#state{cnt = S#state.cnt-Lost});
  797. {error, Error, L2} ->
  798. put(log, L2),
  799. reply(From, Error, state_err(S, Error))
  800. end;
  801. #log{status = {blocked, false}}=L ->
  802. reply(From, {error, {blocked_log, L#log.name}}, S);
  803. #log{blocked_by = From}=L ->
  804. reply(From, {error, {blocked_log, L#log.name}}, S);
  805. _ ->
  806. enqueue(Message, S)
  807. end;
  808. handle({From, {reopen, NewFile, Head, F, A}}, S) ->
  809. case get(log) of
  810. #log{mode = read_only}=L ->
  811. reply(From, {error, {read_only_mode, L#log.name}}, S);
  812. #log{status = ok} when S#state.cache_error =/= ok ->
  813. loop(cache_error(S, [From]));
  814. #log{status = ok, filename = NewFile}=L ->
  815. reply(From, {error, {same_file_name, L#log.name}}, S);
  816. #log{status = ok}=L ->
  817. case catch close_disk_log2(L) of
  818. closed ->
  819. File = L#log.filename,
  820. case catch rename_file(File, NewFile, L#log.type) of
  821. ok ->
  822. H = merge_head(Head, L#log.head),
  823. case do_open((S#state.args)#arg{name = L#log.name,
  824. repair = truncate,
  825. head = H,
  826. file = File}) of
  827. {ok, Res, L2, Cnt} ->
  828. put(log, L2#log{owners = L#log.owners,
  829. head = L#log.head,
  830. users = L#log.users}),
  831. notify_owners({truncated, S#state.cnt}),
  832. erase(is_full),
  833. case Res of
  834. {error, _} ->
  835. do_exit(S, From, Res,
  836. ?failure(Res, F, A));
  837. _ ->
  838. reply(From, ok, S#state{cnt = Cnt})
  839. end;
  840. Res ->
  841. do_exit(S, From, Res, ?failure(Res, F, A))
  842. end;
  843. Error ->
  844. do_exit(S, From, Error, ?failure(Error, reopen, 2))
  845. end;
  846. Error ->
  847. do_exit(S, From, Error, ?failure(Error, F, A))
  848. end;
  849. L ->
  850. reply(From, {error, {blocked_log, L#log.name}}, S)
  851. end;
  852. handle({Server, {internal_open, A}}, S) ->
  853. case get(log) of
  854. undefined ->
  855. case do_open(A) of % does the put
  856. {ok, Res, L, Cnt} ->
  857. put(log, opening_pid(A#arg.linkto, A#arg.notify, L)),
  858. reply(Server, Res, S#state{args=A, cnt=Cnt});
  859. Res ->
  860. do_fast_exit(S, Server, Res)
  861. end;
  862. L ->
  863. TestH = mk_head(A#arg.head, A#arg.format),
  864. case compare_arg(A#arg.options, S#state.args, TestH, L#log.head) of
  865. ok ->
  866. case add_pid(A#arg.linkto, A#arg.notify, L) of
  867. {ok, L1} ->
  868. put(log, L1),
  869. reply(Server, {ok, L#log.name}, S);
  870. Error ->
  871. reply(Server, Error, S)
  872. end;
  873. Error ->
  874. reply(Server, Error, S)
  875. end
  876. end;
  877. handle({From, close}, S) ->
  878. case do_close(From, S) of
  879. {stop, S1} ->
  880. do_exit(S1, From, ok, normal);
  881. {continue, S1} ->
  882. reply(From, ok, S1)
  883. end;
  884. handle({From, info}, S) ->
  885. reply(From, do_info(get(log), S#state.cnt), S);
  886. handle({'EXIT', From, Reason}, #state{parent=From}=S) ->
  887. %% Parent orders shutdown.
  888. _ = do_stop(S),
  889. exit(Reason);
  890. handle({'EXIT', From, Reason}, #state{server=From}=S) ->
  891. %% The server is gone.
  892. _ = do_stop(S),
  893. exit(Reason);
  894. handle({'EXIT', From, _Reason}, S) ->
  895. L = get(log),
  896. case is_owner(From, L) of
  897. {true, _Notify} ->
  898. case close_owner(From, L, S) of
  899. {stop, S1} ->
  900. _ = do_stop(S1),
  901. exit(normal);
  902. {continue, S1} ->
  903. loop(S1)
  904. end;
  905. false ->
  906. %% 'users' is not decremented.
  907. S1 = do_unblock(From, get(log), S),
  908. loop(S1)
  909. end;
  910. handle({system, From, Req}, S) ->
  911. sys:handle_system_msg(Req, From, S#state.parent, ?MODULE, [], S);
  912. handle(_, S) ->
  913. loop(S).
  914. enqueue(Message, #state{queue = Queue}=S) ->
  915. loop(S#state{queue = [Message | Queue]}).
  916. %% Collect further log and sync requests already in the mailbox or queued
  917. -define(MAX_LOOK_AHEAD, 64*1024).
  918. %% Inlined.
  919. log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz, _F) when CE =/= ok ->
  920. loop(cache_error(S, Pids));
  921. log_loop(#state{}=S, Pids, Bins, Sync, Sz, _F) when Sz > ?MAX_LOOK_AHEAD ->
  922. loop(log_end(S, Pids, Bins, Sync, Sz));
  923. log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz, F) ->
  924. receive
  925. Message ->
  926. log_loop(Message, Pids, Bins, Sync, Sz, F, S)
  927. after 0 ->
  928. loop(log_end(S, Pids, Bins, Sync, Sz))
  929. end;
  930. log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz, F) ->
  931. S1 = S#state{messages = Ms},
  932. log_loop(M, Pids, Bins, Sync, Sz, F, S1).
  933. %% Items logged after the last sync request found are sync:ed as well.
  934. log_loop({alog, internal, B}, Pids, Bins, Sync, Sz, internal=F, S) ->
  935. %% alog of terms allowed for the internal format only
  936. log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F);
  937. log_loop({alog, binary, B}, Pids, Bins, Sync, Sz, F, S) ->
  938. log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F);
  939. log_loop({From, {log, internal, B}}, Pids, Bins, Sync, Sz, internal=F, S) ->
  940. %% log of terms allowed for the internal format only
  941. log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F);
  942. log_loop({From, {log, binary, B}}, Pids, Bins, Sync, Sz, F, S) ->
  943. log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F);
  944. log_loop({From, sync}, Pids, Bins, Sync, Sz, F, S) ->
  945. log_loop(S, Pids, Bins, [From | Sync], Sz, F);
  946. log_loop(Message, Pids, Bins, Sync, Sz, _F, S) ->
  947. NS = log_end(S, Pids, Bins, Sync, Sz),
  948. handle(Message, NS).
  949. log_end(S, [], [], Sync, _Sz) ->
  950. log_end_sync(S, Sync);
  951. log_end(#state{cnt = Cnt}=S, Pids, Bins, Sync, Sz) ->
  952. case do_log(get(log), rflat(Bins), Sz) of
  953. N when is_integer(N) ->
  954. ok = replies(Pids, ok),
  955. S1 = (state_ok(S))#state{cnt = Cnt + N},
  956. log_end_sync(S1, Sync);
  957. {error, {error, {full, _Name}}, N} when Pids =:= [] ->
  958. log_end_sync(state_ok(S#state{cnt = Cnt + N}), Sync);
  959. {error, Error, N} ->
  960. ok = replies(Pids, Error),
  961. state_err(S#state{cnt = Cnt + N}, Error)
  962. end.
  963. %% Inlined.
  964. log_end_sync(S, []) ->
  965. S;
  966. log_end_sync(S, Sync) ->
  967. Res = do_sync(get(log)),
  968. ok = replies(Sync, Res),
  969. state_err(S, Res).
  970. %% Inlined.
  971. rflat([B]) -> B;
  972. rflat(B) -> rflat(B, []).
  973. rflat([B | Bs], L) ->
  974. rflat(Bs, B ++ L);
  975. rflat([], L) -> L.
  976. %% -> {ok, Log} | {error, Error}
  977. do_change_notify(L, Pid, Notify) ->
  978. case is_owner(Pid, L) of
  979. {true, Notify} ->
  980. {ok, L};
  981. {true, _OldNotify} when Notify =/= true, Notify =/= false ->
  982. {error, {badarg, notify}};
  983. {true, _OldNotify} ->
  984. Owners = lists:keydelete(Pid, 1, L#log.owners),
  985. L1 = L#log{owners = [{Pid, Notify} | Owners]},
  986. {ok, L1};
  987. false ->
  988. {error, {not_owner, Pid}}
  989. end.
  990. %% -> {stop, S} | {continue, S}
  991. do_close(Pid, S) ->
  992. L = get(log),
  993. case is_owner(Pid, L) of
  994. {true, _Notify} ->
  995. close_owner(Pid, L, S);
  996. false ->
  997. close_user(Pid, L, S)
  998. end.
  999. %% -> {stop, S} | {continue, S}
  1000. close_owner(Pid, L, S) ->
  1001. L1 = L#log{owners = lists:keydelete(Pid, 1, L#log.owners)},
  1002. put(log, L1),
  1003. S2 = do_unblock(Pid, get(log), S),
  1004. unlink(Pid),
  1005. do_close2(L1, S2).
  1006. %% -> {stop, S} | {continue, S}
  1007. close_user(Pid, #log{users=Users}=L, S) when Users > 0 ->
  1008. L1 = L#log{users = Users - 1},
  1009. put(log, L1),
  1010. S2 = do_unblock(Pid, get(log), S),
  1011. do_close2(L1, S2);
  1012. close_user(_Pid, _L, S) ->
  1013. {continue, S}.
  1014. do_close2(#log{users = 0, owners = []}, S) ->
  1015. {stop, S};
  1016. do_close2(_L, S) ->
  1017. {continue, S}.
  1018. %%-----------------------------------------------------------------
  1019. %% Callback functions for system messages handling.
  1020. %%-----------------------------------------------------------------
  1021. system_continue(_Parent, _, State) ->
  1022. loop(State).
  1023. -spec system_terminate(_, _, _, #state{}) -> no_return().
  1024. system_terminate(Reason, _Parent, _, State) ->
  1025. _ = do_stop(State),
  1026. exit(Reason).
  1027. %%-----------------------------------------------------------------
  1028. %% Temporay code for upgrade.
  1029. %%-----------------------------------------------------------------
  1030. system_code_change(State, _Module, _OldVsn, _Extra) ->
  1031. {ok, State}.
  1032. %%%----------------------------------------------------------------------
  1033. %%% Internal functions
  1034. %%%----------------------------------------------------------------------
  1035. -spec do_exit(#state{}, pid(), _, _) -> no_return().
  1036. do_exit(S, From, Message0, Reason) ->
  1037. R = do_stop(S),
  1038. Message = case S#state.cache_error of
  1039. Err when Err =/= ok -> Err;
  1040. _ when R =:= closed -> Message0;
  1041. _ when Message0 =:= ok -> R;
  1042. _ -> Message0
  1043. end,
  1044. _ = disk_log_server:close(self()),
  1045. ok = replies(From, Message),
  1046. ?PROFILE(ep:done()),
  1047. exit(Reason).
  1048. -spec do_fast_exit(#state{}, pid(), _) -> no_return().
  1049. do_fast_exit(S, Server, Message) ->
  1050. _ = do_stop(S),
  1051. Server ! {disk_log, self(), Message},
  1052. exit(normal).
  1053. %% -> closed | Error
  1054. do_stop(S) ->
  1055. proc_q(S#state.queue ++ S#state.messages),
  1056. close_disk_log(get(log)).
  1057. proc_q([{From, _R}|Tail]) when is_pid(From) ->
  1058. From ! {disk_log, self(), {error, disk_log_stopped}},
  1059. proc_q(Tail);
  1060. proc_q([_|T]) -> %% async stuff
  1061. proc_q(T);
  1062. proc_q([]) ->
  1063. ok.
  1064. %% -> log()
  1065. opening_pid(Pid, Notify, L) ->
  1066. {ok, L1} = add_pid(Pid, Notify, L),
  1067. L1.
  1068. %% -> {ok, log()} | Error
  1069. add_pid(Pid, Notify, L) when is_pid(Pid) ->
  1070. case is_owner(Pid, L) of
  1071. false ->
  1072. link(Pid),
  1073. {ok, L#log{owners = [{Pid, Notify} | L#log.owners]}};
  1074. {true, Notify} ->
  1075. %% {error, {pid_already_connected, L#log.name}};
  1076. {ok, L};
  1077. {true, CurNotify} when Notify =/= CurNotify ->
  1078. {error, {arg_mismatch, notify, CurNotify, Notify}}
  1079. end;
  1080. add_pid(_NotAPid, _Notify, L) ->
  1081. {ok, L#log{users = L#log.users + 1}}.
  1082. unblock_pid(#log{blocked_by = none}) ->
  1083. ok;
  1084. unblock_pid(#log{blocked_by = Pid}=L) ->
  1085. case is_owner(Pid, L) of
  1086. {true, _Notify} ->
  1087. ok;
  1088. false ->
  1089. unlink(Pid)
  1090. end.
  1091. %% -> true | false
  1092. is_owner(Pid, L) ->
  1093. case lists:keysearch(Pid, 1, L#log.owners) of
  1094. {value, {_Pid, Notify}} ->
  1095. {true, Notify};
  1096. false ->
  1097. false
  1098. end.
  1099. %% ok | throw(Error)
  1100. rename_file(File, NewFile, halt) ->
  1101. case file:rename(File, NewFile) of
  1102. ok ->
  1103. ok;
  1104. Else ->
  1105. file_error(NewFile, Else)
  1106. end;
  1107. rename_file(File, NewFile, wrap) ->
  1108. rename_file(wrap_file_extensions(File), File, NewFile, ok).
  1109. rename_file([Ext|Exts], File, NewFile0, Res) ->
  1110. NewFile = add_ext(NewFile0, Ext),
  1111. NRes = case file:rename(add_ext(File, Ext), NewFile) of
  1112. ok ->
  1113. Res;
  1114. Else ->
  1115. file_error(NewFile, Else)
  1116. end,
  1117. rename_file(Exts, File, NewFile0, NRes);
  1118. rename_file([], _File, _NewFiles, Res) -> Res.
  1119. file_error(FileName, {error, Error}) ->
  1120. {error, {file_error, FileName, Error}}.
  1121. %% "Old" error messages have been kept, arg_mismatch has been added.
  1122. %%-spec compare_arg(dlog_options(), #arg{},
  1123. compare_arg([], _A, none, _OrigHead) ->
  1124. % no header option given
  1125. ok;
  1126. compare_arg([], _A, Head, OrigHead) when Head =/= OrigHead ->
  1127. {error, {arg_mismatch, head, OrigHead, Head}};
  1128. compare_arg([], _A, _Head, _OrigHead) ->
  1129. ok;
  1130. compare_arg([{Attr, Val} | Tail], A, Head, OrigHead) ->
  1131. case compare_arg(Attr, Val, A) of
  1132. {not_ok, OrigVal} ->
  1133. {error, {arg_mismatch, Attr, OrigVal, Val}};
  1134. ok ->
  1135. compare_arg(Tail, A, Head, OrigHead);
  1136. Error ->
  1137. Error
  1138. end.
  1139. -spec compare_arg(atom(), _, #arg{}) ->
  1140. 'ok' | {'not_ok', _} | {'error', {atom(), _}}.
  1141. compare_arg(file, F, A) when F =/= A#arg.file ->
  1142. {error, {name_already_open, A#arg.name}};
  1143. compare_arg(mode, read_only, A) when A#arg.mode =:= read_write ->
  1144. {error, {open_read_write, A#arg.name}};
  1145. compare_arg(mode, read_write, A) when A#arg.mode =:= read_only ->
  1146. {error, {open_read_only, A#arg.name}};
  1147. compare_arg(type, T, A) when T =/= A#arg.type ->
  1148. {not_ok, A#arg.type};
  1149. compare_arg(format, F, A) when F =/= A#arg.format ->
  1150. {not_ok, A#arg.format};
  1151. compare_arg(repair, R, A) when R =/= A#arg.repair ->
  1152. %% not used, but check it anyway...
  1153. {not_ok, A#arg.repair};
  1154. compare_arg(_Attr, _Val, _A) ->
  1155. ok.
  1156. %% -> {ok, Res, log(), Cnt} | Error
  1157. do_open(A) ->
  1158. #arg{type = Type, format = Format, name = Name, head = Head0,
  1159. file = FName, repair = Repair, size = Size, mode = Mode,
  1160. quiet = Quiet, version = V} = A,
  1161. disk_log_1:set_quiet(Quiet),
  1162. Head = mk_head(Head0, Format),
  1163. case do_open2(Type, Format, Name, FName, Repair, Size, Mode, Head, V) of
  1164. {ok, Ret, Extra, FormatType, NoItems} ->
  1165. L = #log{name = Name, type = Type, format = Format,
  1166. filename = FName, size = Size,
  1167. format_type = FormatType, head = Head, mode = Mode,
  1168. version = V, extra = Extra},
  1169. {ok, Ret, L, NoItems};
  1170. Error ->
  1171. Error
  1172. end.
  1173. mk_head({head, Term}, internal) -> {ok, term_to_binary(Term)};
  1174. mk_head({head, Bytes}, external) -> {ok, ensure_binary(Bytes)};
  1175. mk_head(H, _) -> H.
  1176. terms2bins([T | Ts]) ->
  1177. [term_to_binary(T) | terms2bins(Ts)];
  1178. terms2bins([]) ->
  1179. [].
  1180. ensure_binary_list(Bs) ->
  1181. ensure_binary_list(Bs, Bs).
  1182. ensure_binary_list([B | Bs], Bs0) when is_binary(B) ->
  1183. ensure_binary_list(Bs, Bs0);
  1184. ensure_binary_list([], Bs0) ->
  1185. Bs0;
  1186. ensure_binary_list(_, Bs0) ->
  1187. make_binary_list(Bs0).
  1188. make_binary_list([B | Bs]) ->
  1189. [ensure_binary(B) | make_binary_list(Bs)];
  1190. make_binary_list([]) ->
  1191. [].
  1192. ensure_binary(Bytes) ->
  1193. iolist_to_binary(Bytes).
  1194. %%-----------------------------------------------------------------
  1195. %% Change size of the logs in runtime.
  1196. %%-----------------------------------------------------------------
  1197. %% -> ok | {big, CurSize} | throw(Error)
  1198. do_change_size(#log{type = halt}=L, NewSize) ->
  1199. Halt = L#log.extra,
  1200. CurB = Halt#halt.curB,
  1201. NewLog = L#log{extra = Halt#halt{size = NewSize}},
  1202. if
  1203. NewSize =:= infinity ->
  1204. erase(is_full),
  1205. put(log, NewLog),
  1206. ok;
  1207. CurB =< NewSize ->
  1208. erase(is_full),
  1209. put(log, NewLog),
  1210. ok;
  1211. true ->
  1212. {big, CurB}
  1213. end;
  1214. do_change_size(#log{type = wrap}=L, NewSize) ->
  1215. #log{extra = Extra, version = Version} = L,
  1216. {ok, Handle} = disk_log_1:change_size_wrap(Extra, NewSize, Version),
  1217. erase(is_full),
  1218. put(log, L#log{extra = Handle}),
  1219. ok.
  1220. %% -> {ok, Head} | Error; Head = none | {head, H} | {M,F,A}
  1221. check_head({head, none}, _Format) ->
  1222. {ok, none};
  1223. check_head({head_func, {M, F, A}}, _Format) when is_atom(M),
  1224. is_atom(F),
  1225. is_list(A) ->
  1226. {ok, {M, F, A}};
  1227. check_head({head, Head}, external) ->
  1228. case catch ensure_binary(Head) of
  1229. {'EXIT', _} ->
  1230. {error, {badarg, head}};
  1231. _ ->
  1232. {ok, {head, Head}}
  1233. end;
  1234. check_head({head, Term}, internal) ->
  1235. {ok, {head, Term}};
  1236. check_head(_Head, _Format) ->
  1237. {error, {badarg, head}}.
  1238. check_size(wrap, {NewMaxB,NewMaxF}) when
  1239. is_integer(NewMaxB), is_integer(NewMaxF),
  1240. NewMaxB > 0, NewMaxB =< ?MAX_BYTES, NewMaxF > 0, NewMaxF < ?MAX_FILES ->
  1241. ok;
  1242. check_size(halt, NewSize) when is_integer(NewSize), NewSize > 0 ->
  1243. ok;
  1244. check_size(halt, infinity) ->
  1245. ok;
  1246. check_size(_, _) ->
  1247. not_ok.
  1248. %%-----------------------------------------------------------------
  1249. %% Increment a wrap log.
  1250. %%-----------------------------------------------------------------
  1251. %% -> {ok, log(), Lost} | {error, Error, log()}
  1252. do_inc_wrap_file(L) ->
  1253. #log{format = Format, extra = Handle} = L,
  1254. case Format of
  1255. internal ->
  1256. case disk_log_1:mf_int_inc(Handle, L#log.head) of
  1257. {ok, Handle2, Lost} ->
  1258. {ok, L#log{extra = Handle2}, Lost};
  1259. {error, Error, Handle2} ->
  1260. {error, Error, L#log{extra = Handle2}}
  1261. end;
  1262. external ->
  1263. case disk_log_1:mf_ext_inc(Handle, L#log.head) of
  1264. {ok, Handle2, Lost} ->
  1265. {ok, L#log{extra = Handle2}, Lost};
  1266. {error, Error, Handle2} ->
  1267. {error, Error, L#log{extra = Handle2}}
  1268. end
  1269. end.
  1270. %%-----------------------------------------------------------------
  1271. %% Open a log file.
  1272. %%-----------------------------------------------------------------
  1273. %% -> {ok, Reply, log(), Cnt} | Error
  1274. %% Note: the header is always written, even if the log size is too small.
  1275. do_open2(halt, internal, Name, FName, Repair, Size, Mode, Head, _V) ->
  1276. case catch disk_log_1:int_open(FName, Repair, Mode, Head) of
  1277. {ok, {_Alloc, FdC, {NoItems, _NoBytes}, FileSize}} ->
  1278. Halt = #halt{fdc = FdC, curB = FileSize, size = Size},
  1279. {ok, {ok, Name}, Halt, halt_int, NoItems};
  1280. {repaired, FdC, Rec, Bad, FileSize} ->
  1281. Halt = #halt{fdc = FdC, curB = FileSize, size = Size},
  1282. {ok, {repaired, Name, {recovered, Rec}, {badbytes, Bad}},
  1283. Halt, halt_int, Rec};
  1284. Error ->
  1285. Error
  1286. end;
  1287. do_open2(wrap, internal, Name, FName, Repair, Size, Mode, Head, V) ->
  1288. {MaxB, MaxF} = Size,
  1289. case catch
  1290. disk_log_1:mf_int_open(FName, MaxB, MaxF, Repair, Mode, Head, V) of
  1291. {ok, Handle, Cnt} ->
  1292. {ok, {ok, Name}, Handle, wrap_int, Cnt};
  1293. {repaired, Handle, Rec, Bad, Cnt} ->
  1294. {ok, {repaired, Name, {recovered, Rec}, {badbytes, Bad}},
  1295. Handle, wrap_int, Cnt};
  1296. Error ->
  1297. Error
  1298. end;
  1299. do_open2(halt, external, Name, FName, Repair, Size, Mode, Head, _V) ->
  1300. case catch disk_log_1:ext_open(FName, Repair, Mode, Head) of
  1301. {ok, {_Alloc, FdC, {NoItems, _NoBytes}, FileSize}} ->
  1302. Halt = #halt{fdc = FdC, curB = FileSize, size = Size},
  1303. {ok, {ok, Name}, Halt, halt_ext, NoItems};
  1304. Error ->
  1305. Error
  1306. end;
  1307. do_open2(wrap, external, Name, FName, Repair, Size, Mode, Head, V) ->
  1308. {MaxB, MaxF} = Size,
  1309. case catch
  1310. disk_log_1:mf_ext_open(FName, MaxB, MaxF, Repair, Mode, Head, V) of
  1311. {ok, Handle, Cnt} ->
  1312. {ok, {ok, Name}, Handle, wrap_ext, Cnt};
  1313. Error ->
  1314. Error
  1315. end.
  1316. %% -> closed | Error
  1317. close_disk_log(undefined) ->
  1318. closed;
  1319. close_disk_log(L) ->
  1320. unblock_pid(L),
  1321. F = fun({Pid, _}) ->
  1322. unlink(Pid)
  1323. end,
  1324. lists:foreach(F, L#log.owners),
  1325. R = (catch close_disk_log2(L)),
  1326. erase(log),
  1327. R.
  1328. -spec close_disk_log2(#log{}) -> 'closed'. % | throw(Error)
  1329. close_disk_log2(L) ->
  1330. case L of
  1331. #log{format_type = halt_int, mode = Mode, extra = Halt} ->
  1332. disk_log_1:close(Halt#halt.fdc, L#log.filename, Mode);
  1333. #log{format_type = wrap_int, mode = Mode, extra = Handle} ->
  1334. disk_log_1:mf_int_close(Handle, Mode);
  1335. #log{format_type = halt_ext, extra = Halt} ->
  1336. disk_log_1:fclose(Halt#halt.fdc, L#log.filename);
  1337. #log{format_type = wrap_ext, mode = Mode, extra = Handle} ->
  1338. disk_log_1:mf_ext_close(Handle, Mode)
  1339. end,
  1340. closed.
  1341. do_format_error({error, Module, Error}) ->
  1342. Module:format_error(Error);
  1343. do_format_error({error, Reason}) ->
  1344. do_format_error(Reason);
  1345. do_format_error({Node, Error = {error, _Reason}}) ->
  1346. lists:append(io_lib:format("~p: ", [Node]), do_format_error(Error));
  1347. do_format_error({badarg, Arg}) ->
  1348. io_lib:format("The argument ~p is missing, not recognized or "
  1349. "not wellformed~n", [Arg]);
  1350. do_format_error({size_mismatch, OldSize, ArgSize}) ->
  1351. io_lib:format("The given size ~p does not match the size ~p found on "
  1352. "the disk log size file~n", [ArgSize, OldSize]);
  1353. do_format_error({read_only_mode, Log}) ->
  1354. io_lib:format("The disk log ~tp has been opened read-only, but the "
  1355. "requested operation needs read-write access~n", [Log]);
  1356. do_format_error({format_external, Log}) ->
  1357. io_lib:format("The requested operation can only be applied on internally "
  1358. "formatted disk logs, but ~tp is externally formatted~n",
  1359. [Log]);
  1360. do_format_error({blocked_log, Log}) ->
  1361. io_lib:format("The blocked disk log ~tp does not queue requests, or "
  1362. "the log has been blocked by the calling process~n", [Log]);
  1363. do_format_error({full, Log}) ->
  1364. io_lib:format("The halt log ~tp is full~n", [Log]);
  1365. do_format_error({not_blocked, Log}) ->
  1366. io_lib:format("The disk log ~tp is not blocked~n", [Log]);
  1367. do_format_error({not_owner, Pid}) ->
  1368. io_lib:format("The pid ~tp is not an owner of the disk log~n", [Pid]);
  1369. do_format_error({not_blocked_by_pid, Log}) ->
  1370. io_lib:format("The disk log ~tp is blocked, but only the blocking pid "
  1371. "can unblock a disk log~n", [Log]);
  1372. do_format_error({new_size_too_small, Log, CurrentSize}) ->
  1373. io_lib:format("The current size ~p of the halt log ~tp is greater than the "
  1374. "requested new size~n", [CurrentSize, Log]);
  1375. do_format_error({halt_log, Log}) ->
  1376. io_lib:format("The halt log ~tp cannot be wrapped~n", [Log]);
  1377. do_format_error({same_file_name, Log}) ->
  1378. io_lib:format("Current and new file name of the disk log ~tp "
  1379. "are the same~n", [Log]);
  1380. do_format_error({arg_mismatch, Option, FirstValue, ArgValue}) ->
  1381. io_lib:format("The value ~tp of the disk log option ~p does not match "
  1382. "the current value ~tp~n", [ArgValue, Option, FirstValue]);
  1383. do_format_error({name_already_open, Log}) ->
  1384. io_lib:format("The disk log ~tp has already opened another file~n", [Log]);
  1385. do_format_error({node_already_open, Log}) ->
  1386. io_lib:format("The distribution option of the disk log ~tp does not match "
  1387. "already open log~n", [Log]);
  1388. do_format_error({open_read_write, Log}) ->
  1389. io_lib:format("The disk log ~tp has already been opened read-write~n",
  1390. [Log]);
  1391. do_format_error({open_read_only, Log}) ->
  1392. io_lib:format("The disk log ~tp has already been opened read-only~n",
  1393. [Log]);
  1394. do_format_error({not_internal_wrap, Log}) ->
  1395. io_lib:format("The requested operation cannot be applied since ~tp is not "
  1396. "an internally formatted disk log~n", [Log]);
  1397. do_format_error(no_such_log) ->
  1398. io_lib:format("There is no disk log with the given name~n", []);
  1399. do_format_error(nonode) ->
  1400. io_lib:format("There seems to be no node up that can handle "
  1401. "the request~n", []);
  1402. do_format_error(nodedown) ->
  1403. io_lib:format("There seems to be no node up that can handle "
  1404. "the request~n", []);
  1405. do_format_error({corrupt_log_file, FileName}) ->
  1406. io_lib:format("The disk log file \"~ts\" contains corrupt data~n",
  1407. [FileName]);
  1408. do_format_error({need_repair, FileName}) ->
  1409. io_lib:format("The disk log file \"~ts\" has not been closed properly and "
  1410. "needs repair~n", [FileName]);
  1411. do_format_error({not_a_log_file, FileName}) ->
  1412. io_lib:format("The file \"~ts\" is not a wrap log file~n", [FileName]);
  1413. do_format_error({invalid_header, InvalidHeader}) ->
  1414. io_lib:format("The disk log header is not wellformed: ~p~n",
  1415. [InvalidHeader]);
  1416. do_format_error(end_of_log) ->
  1417. io_lib:format("An attempt was made to step outside a not yet "
  1418. "full wrap log~n", []);
  1419. do_format_error({invalid_index_file, FileName}) ->
  1420. io_lib:format("The wrap log index file \"~ts\" cannot be used~n",
  1421. [FileName]);
  1422. do_format_error({no_continuation, BadCont}) ->
  1423. io_lib:format("The term ~p is not a chunk continuation~n", [BadCont]);
  1424. do_format_error({file_error, FileName, Reason}) ->
  1425. io_lib:format("\"~ts\": ~tp~n", [FileName, file:format_error(Reason)]);
  1426. do_format_error(E) ->
  1427. io_lib:format("~tp~n", [E]).
  1428. do_info(L, Cnt) ->
  1429. #log{name = Name, type = Type, mode = Mode, filename = File,
  1430. extra = Extra, status = Status, owners = Owners, users = Users,
  1431. format = Format, head = Head} = L,
  1432. Size = case Type of
  1433. wrap ->
  1434. disk_log_1:get_wrap_size(Extra);
  1435. halt ->
  1436. Extra#halt.size
  1437. end,
  1438. Distribution =
  1439. case disk_log_server:get_log_pids(Name) of
  1440. {local, _Pid} ->
  1441. local;
  1442. {distributed, Pids} ->
  1443. [node(P) || P <- Pids];
  1444. undefined -> % "cannot happen"
  1445. []
  1446. end,
  1447. RW = case Type of
  1448. wrap when Mode =:= read_write ->
  1449. #handle{curB = CurB, curF = CurF,
  1450. cur_cnt = CurCnt, acc_cnt = AccCnt,
  1451. noFull = NoFull, accFull = AccFull} = Extra,
  1452. NewAccFull = AccFull + NoFull,
  1453. NewExtra = Extra#handle{noFull = 0, accFull = NewAccFull},
  1454. put(log, L#log{extra = NewExtra}),
  1455. [{no_current_bytes, CurB},
  1456. {no_current_items, CurCnt},
  1457. {no_items, Cnt},
  1458. {no_written_items, CurCnt + AccCnt},
  1459. {current_file, CurF},
  1460. {no_overflows, {NewAccFull, NoFull}}
  1461. ];
  1462. halt when Mode =:= read_write ->
  1463. IsFull = case get(is_full) of
  1464. undefined -> false;
  1465. _ -> true
  1466. end,
  1467. [{full, IsFull},
  1468. {no_written_items, Cnt}
  1469. ];
  1470. _ when Mode =:= read_only ->
  1471. []
  1472. end,
  1473. HeadL = case Mode of
  1474. read_write ->
  1475. [{head, Head}];
  1476. read_only ->
  1477. []
  1478. end,
  1479. Common = [{name, Name},
  1480. {file, File},
  1481. {type, Type},
  1482. {format, Format},
  1483. {size, Size},
  1484. {items, Cnt}, % kept for "backward compatibility" (undocumented)
  1485. {owners, Owners},
  1486. {users, Users}] ++
  1487. HeadL ++
  1488. [{mode, Mode},
  1489. {status, Status},
  1490. {node, node()},
  1491. {distributed, Distribution}
  1492. ],
  1493. Common ++ RW.
  1494. do_block(Pid, QueueLogRecs, L) ->
  1495. L2 = L#log{status = {blocked, QueueLogRecs}, blocked_by = Pid},
  1496. put(log, L2),
  1497. case is_owner(Pid, L2) of
  1498. {true, _Notify} ->
  1499. ok;
  1500. false ->
  1501. link(Pid)
  1502. end.
  1503. do_unblock(Pid, #log{blocked_by = Pid}=L, S) ->
  1504. do_unblock(L, S);
  1505. do_unblock(_Pid, _L, S) ->
  1506. S.
  1507. do_unblock(L, S) ->
  1508. unblock_pid(L),
  1509. L2 = L#log{blocked_by = none, status = ok},
  1510. put(log, L2),
  1511. %% Since the block request is synchronous, and the blocking
  1512. %% process is the only process that can unblock, all requests in
  1513. %% 'messages' will have been put in 'queue' before the unblock
  1514. %% request is granted.
  1515. [] = S#state.messages, % assertion
  1516. S#state{queue = [], messages = lists:reverse(S#state.queue)}.
  1517. -spec do_log(#log{}, [binary()]) -> integer() | {'error', _, integer()}.
  1518. do_log(L, B) ->
  1519. do_log(L, B, iolist_size(B)).
  1520. do_log(#log{type = halt}=L, B, BSz) ->
  1521. #log{format = Format, extra = Halt} = L,
  1522. #halt{curB = CurSize, size = Sz} = Halt,
  1523. {Bs, BSize} = logl(B, Format, BSz),
  1524. case get(is_full) of
  1525. true ->
  1526. {error, {error, {full, L#log.name}}, 0};
  1527. undefined when Sz =:= infinity; CurSize + BSize =< Sz ->
  1528. halt_write(Halt, L, B, Bs, BSize);
  1529. undefined ->
  1530. halt_write_full(L, B, Format, 0)
  1531. end;
  1532. do_log(#log{format_type = wrap_int}=L, B, _BSz) ->
  1533. case disk_log_1:mf_int_log(L#log.extra, B, L#log.head) of
  1534. {ok, Handle, Logged, Lost, Wraps} ->
  1535. notify_owners_wrap(Wraps),
  1536. put(log, L#log{extra = Handle}),
  1537. Logged - Lost;
  1538. {ok, Handle, Logged} ->
  1539. put(log, L#log{extra = Handle}),
  1540. Logged;
  1541. {error, Error, Handle, Logged, Lost} ->
  1542. put(log, L#log{extra = Handle}),
  1543. {error, Error, Logged - Lost}
  1544. end;
  1545. do_log(#log{format_type = wrap_ext}=L, B, _BSz) ->
  1546. case disk_log_1:mf_ext_log(L#log.extra, B, L#log.head) of
  1547. {ok, Handle, Logged, Lost, Wraps} ->
  1548. notify_owners_wrap(Wraps),
  1549. put(log, L#log{extra = Handle}),
  1550. Logged - Lost;
  1551. {ok, Handle, Logged} ->
  1552. put(log, L#log{extra = Handle}),
  1553. Logged;
  1554. {error, Error, Handle, Logged, Lost} ->
  1555. put(log, L#log{extra = Handle}),
  1556. {error, Error, Logged - Lost}
  1557. end.
  1558. logl(B, external, undefined) ->
  1559. {B, iolist_size(B)};
  1560. logl(B, external, Sz) ->
  1561. {B, Sz};
  1562. logl(B, internal, _Sz) ->
  1563. disk_log_1:logl(B).
  1564. halt_write_full(L, [Bin | Bins], Format, N) ->
  1565. B = [Bin],
  1566. {Bs, BSize} = logl(B, Format, undefined),
  1567. Halt = L#log.extra,
  1568. #halt{curB = CurSize, size = Sz} = Halt,
  1569. if
  1570. CurSize + BSize =< Sz ->
  1571. case halt_write(Halt, L, B, Bs, BSize) of
  1572. N1 when is_integer(N1) ->
  1573. halt_write_full(get(log), Bins, Format, N+N1);
  1574. Error ->
  1575. Error
  1576. end;
  1577. true ->
  1578. halt_write_full(L, [], Format, N)
  1579. end;
  1580. halt_write_full(L, _Bs, _Format, N) ->
  1581. put(is_full, true),
  1582. notify_owners(full),
  1583. {error, {error, {full, L#log.name}}, N}.
  1584. halt_write(Halt, L, B, Bs, BSize) ->
  1585. case disk_log_1:fwrite(Halt#halt.fdc, L#log.filename, Bs, BSize) of
  1586. {ok, NewFdC} ->
  1587. NCurB = Halt#halt.curB + BSize,
  1588. NewHalt = Halt#halt{fdc = NewFdC, curB = NCurB},
  1589. put(log, L#log{extra = NewHalt}),
  1590. length(B);
  1591. {Error, NewFdC} ->
  1592. put(log, L#log{extra = Halt#halt{fdc = NewFdC}}),
  1593. {error, Error, 0}
  1594. end.
  1595. %% -> ok | Error
  1596. do_write_cache(#log{filename = FName, type = halt, extra = Halt} = Log) ->
  1597. {Reply, NewFdC} = disk_log_1:write_cache(Halt#halt.fdc, FName),
  1598. put(log, Log#log{extra = Halt#halt{fdc = NewFdC}}),
  1599. Reply;
  1600. do_write_cache(#log{type = wrap, extra = Handle} = Log) ->
  1601. {Reply, NewHandle} = disk_log_1:mf_write_cache(Handle),
  1602. put(log, Log#log{extra = NewHandle}),
  1603. Reply.
  1604. %% -> ok | Error
  1605. do_sync(#log{filename = FName, type = halt, extra = Halt} = Log) ->
  1606. {Reply, NewFdC} = disk_log_1:sync(Halt#halt.fdc, FName),
  1607. put(log, Log#log{extra = Halt#halt{fdc = NewFdC}}),
  1608. Reply;
  1609. do_sync(#log{type = wrap, extra = Handle} = Log) ->
  1610. {Reply, NewHandle} = disk_log_1:mf_sync(Handle),
  1611. put(log, Log#log{extra = NewHandle}),
  1612. Reply.
  1613. %% -> ok | Error | throw(Error)
  1614. do_trunc(#log{type = halt}=L, Head) ->
  1615. #log{filename = FName, extra = Halt} = L,
  1616. FdC = Halt#halt.fdc,
  1617. {Reply1, FdC2} =
  1618. case L#log.format of
  1619. internal ->
  1620. disk_log_1:truncate(FdC, FName, Head);
  1621. external ->
  1622. case disk_log_1:truncate_at(FdC, FName, bof) of
  1623. {ok, NFdC} when Head =:= none ->
  1624. {ok, NFdC};
  1625. {ok, NFdC} ->
  1626. {ok, H} = Head,
  1627. disk_log_1:fwrite(NFdC, FName, H, byte_size(H));
  1628. R ->
  1629. R
  1630. end
  1631. end,
  1632. {Reply, NewHalt} =
  1633. case disk_log_1:position(FdC2, FName, cur) of
  1634. {ok, NewFdC, FileSize} when Reply1 =:= ok ->
  1635. {ok, Halt#halt{fdc = NewFdC, curB = FileSize}};
  1636. {Reply2, NewFdC} ->
  1637. {Reply2, Halt#halt{fdc = NewFdC}};
  1638. {ok, NewFdC, _} ->
  1639. {Reply1, Halt#halt{fdc = NewFdC}}
  1640. end,
  1641. put(log, L#log{extra = NewHalt}),
  1642. Reply;
  1643. do_trunc(#log{type = wrap}=L, Head) ->
  1644. Handle = L#log.extra,
  1645. OldHead = L#log.head,
  1646. {MaxB, MaxF} = disk_log_1:get_wrap_size(Handle),
  1647. ok = do_change_size(L, {MaxB, 1}),
  1648. NewLog = trunc_wrap((get(log))#log{head = Head}),
  1649. %% Just to remove all files with suffix > 1:
  1650. NewLog2 = trunc_wrap(NewLog),
  1651. NewHandle = (NewLog2#log.extra)#handle{noFull = 0, accFull = 0},
  1652. do_change_size(NewLog2#log{extra = NewHandle, head = OldHead},
  1653. {MaxB, MaxF}).
  1654. trunc_wrap(L) ->
  1655. case do_inc_wrap_file(L) of
  1656. {ok, L2, _Lost} ->
  1657. L2;
  1658. {error, Error, _L2} ->
  1659. throw(Error)
  1660. end.
  1661. do_chunk(#log{format_type = halt_int, extra = Halt} = L, Pos, B, N) ->
  1662. FdC = Halt#halt.fdc,
  1663. {NewFdC, Reply} =
  1664. case L#log.mode of
  1665. read_only ->
  1666. disk_log_1:chunk_read_only(FdC, L#log.filename, Pos, B, N);
  1667. read_write ->
  1668. disk_log_1:chunk(FdC, L#log.filename, Pos, B, N)
  1669. end,
  1670. put(log, L#log{extra = Halt#halt{fdc = NewFdC}}),
  1671. Reply;
  1672. do_chunk(#log{format_type = wrap_int, mode = read_only,
  1673. extra = Handle} = Log, Pos, B, N) ->
  1674. {NewHandle, Reply} = disk_log_1:mf_int_chunk_read_only(Handle, Pos, B, N),
  1675. put(log, Log#log{extra = NewHandle}),
  1676. Reply;
  1677. do_chunk(#log{format_type = wrap_int, extra = Handle} = Log, Pos, B, N) ->
  1678. {NewHandle, Reply} = disk_log_1:mf_int_chunk(Handle, Pos, B, N),
  1679. put(log, Log#log{extra = NewHandle}),
  1680. Reply;
  1681. do_chunk(Log, _Pos, _B, _) ->
  1682. {error, {format_external, Log#log.name}}.
  1683. do_chunk_step(#log{format_type = wrap_int, extra = Handle}, Pos, N) ->
  1684. disk_log_1:mf_int_chunk_step(Handle, Pos, N);
  1685. do_chunk_step(Log, _Pos, _N) ->
  1686. {error, {not_internal_wrap, Log#log.name}}.
  1687. %% Inlined.
  1688. replies(Pids, Reply) ->
  1689. M = {disk_log, self(), Reply},
  1690. send_reply(Pids, M).
  1691. send_reply(Pid, M) when is_pid(Pid) ->
  1692. Pid ! M,
  1693. ok;
  1694. send_reply([Pid | Pids], M) ->
  1695. Pid ! M,
  1696. send_reply(Pids, M);
  1697. send_reply([], _M) ->
  1698. ok.
  1699. reply(To, Reply, S) ->
  1700. To ! {disk_log, self(), Reply},
  1701. loop(S).
  1702. req(Log, R) ->
  1703. case disk_log_server:get_log_pids(Log) of
  1704. {local, Pid} ->
  1705. monitor_request(Pid, R);
  1706. undefined ->
  1707. {error, no_such_log};
  1708. {distributed, Pids} ->
  1709. multi_req({self(), R}, Pids)
  1710. end.
  1711. multi_req(Msg, Pids) ->
  1712. Refs =
  1713. lists:map(fun(Pid) ->
  1714. Ref = erlang:monitor(process, Pid),
  1715. Pid ! Msg,
  1716. {Pid, Ref}
  1717. end, Pids),
  1718. lists:foldl(fun({Pid, Ref}, Reply) ->
  1719. receive
  1720. {'DOWN', Ref, process, Pid, _Info} ->
  1721. Reply;
  1722. {disk_log, Pid, _Reply} ->
  1723. erlang:demonitor(Ref, [flush]),
  1724. ok
  1725. end
  1726. end, {error, nonode}, Refs).
  1727. sreq(Log, R) ->
  1728. case nearby_pid(Log, node()) of
  1729. undefined ->
  1730. {error, no_such_log};
  1731. Pid ->
  1732. monitor_request(Pid, R)
  1733. end.
  1734. %% Local req - always talk to log on Node
  1735. lreq(Log, R, Node) ->
  1736. case nearby_pid(Log, Node) of
  1737. Pid when is_pid(Pid), node(Pid) =:= Node ->
  1738. monitor_request(Pid, R);
  1739. _Else ->
  1740. {error, no_such_log}
  1741. end.
  1742. nearby_pid(Log, Node) ->
  1743. case disk_log_server:get_log_pids(Log) of
  1744. undefined ->
  1745. undefined;
  1746. {local, Pid} ->
  1747. Pid;
  1748. {distributed, Pids} ->
  1749. get_near_pid(Pids, Node)
  1750. end.
  1751. -spec get_near_pid([pid(),...], node()) -> pid().
  1752. get_near_pid([Pid | _], Node) when node(Pid) =:= Node -> Pid;
  1753. get_near_pid([Pid], _ ) -> Pid;
  1754. get_near_pid([_ | T], Node) -> get_near_pid(T, Node).
  1755. monitor_request(Pid, Req) ->
  1756. Ref = erlang:monitor(process, Pid),
  1757. Pid ! {self(), Req},
  1758. receive
  1759. {'DOWN', Ref, process, Pid, _Info} ->
  1760. {error, no_such_log};
  1761. {disk_log, Pid, Reply} when not is_tuple(Reply) orelse
  1762. element(2, Reply) =/= disk_log_stopped ->
  1763. erlang:demonitor(Ref, [flush]),
  1764. Reply
  1765. end.
  1766. req2(Pid, R) ->
  1767. monitor_request(Pid, R).
  1768. merge_head(none, Head) ->
  1769. Head;
  1770. merge_head(Head, _) ->
  1771. Head.
  1772. %% -> List of extensions of existing files (no dot included) | throw(FileError)
  1773. wrap_file_extensions(File) ->
  1774. {_CurF, _CurFSz, _TotSz, NoOfFiles} =
  1775. disk_log_1:read_index_file(File),
  1776. Fs = if
  1777. NoOfFiles >= 1 ->
  1778. lists:seq(1, NoOfFiles);
  1779. NoOfFiles =:= 0 ->
  1780. []
  1781. end,
  1782. Fun = fun(Ext) ->
  1783. case file:read_file_info(add_ext(File, Ext)) of
  1784. {ok, _} ->
  1785. true;
  1786. _Else ->
  1787. false
  1788. end
  1789. end,
  1790. lists:filter(Fun, ["idx", "siz" | Fs]).
  1791. add_ext(File, Ext) ->
  1792. lists:concat([File, ".", Ext]).
  1793. notify(Log, R) ->
  1794. case disk_log_server:get_log_pids(Log) of
  1795. undefined ->
  1796. {error, no_such_log};
  1797. {local, Pid} ->
  1798. Pid ! R,
  1799. ok;
  1800. {distributed, Pids} ->
  1801. lists:foreach(fun(Pid) -> Pid ! R end, Pids),
  1802. ok
  1803. end.
  1804. notify_owners_wrap([]) ->
  1805. ok;
  1806. notify_owners_wrap([N | Wraps]) ->
  1807. notify_owners({wrap, N}),
  1808. notify_owners_wrap(Wraps).
  1809. notify_owners(Note) ->
  1810. L = get(log),
  1811. Msg = {disk_log, node(), L#log.name, Note},
  1812. lists:foreach(fun({Pid, true}) -> Pid ! Msg;
  1813. (_) -> ok
  1814. end, L#log.owners).
  1815. cache_error(#state{cache_error=Error}=S, Pids) ->
  1816. ok = replies(Pids, Error),
  1817. state_err(S#state{cache_error = ok}, Error).
  1818. state_ok(S) ->
  1819. state_err(S, ok).
  1820. -spec state_err(#state{}, dlog_state_error()) -> #state{}.
  1821. state_err(S, Err) when S#state.error_status =:= Err -> S;
  1822. state_err(S, Err) ->
  1823. notify_owners({error_status, Err}),
  1824. S#state{error_status = Err}.