%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(snmpa_mib_storage_dets).
-behaviour(snmpa_mib_storage).
%%%-----------------------------------------------------------------
%%% This module implements the snmpa_mib_storage behaviour.
%%% It uses dets for storage.
%%%-----------------------------------------------------------------
-export([
open/5,
close/1,
read/2,
write/2,
delete/1,
delete/2,
match_object/2,
match_delete/2,
tab2list/1,
info/1, info/2,
sync/1,
backup/2
]).
-define(VMODULE, "MS-DETS").
-include("snmp_verbosity.hrl").
-record(tab, {id, rec_name}).
%% ---------------------------------------------------------------
%% open
%%
%% Open or create a mib-storage (dets) table.
%%
%% Opts - A list of implementation dependent options
%% dets_open_options() = [dets_open_option()]
%% dets_open_option() = {dir, filename()} |
%% {action, keep | clear} |
%% {auto_save, default | pos_integer()} |
%% {repair, force | boolean()}
%%
%% ---------------------------------------------------------------
open(Name, RecName, _Fields, Type, Opts) ->
Dir = snmp_misc:get_option(dir, Opts),
Action = snmp_misc:get_option(action, Opts, keep),
AutoSave = snmp_misc:get_option(auto_save, Opts, default),
Repair = snmp_misc:get_option(repair, Opts, false),
File = dets_filename(Name, Dir),
OpenOpts = [{file, File},
{type, Type},
{keypos, 2},
{repair, Repair}] ++
case AutoSave of
default ->
[];
_ ->
[{auto_save, AutoSave}]
end,
case dets:open_file(Name, OpenOpts) of
{ok, ID} when (Action =:= keep) ->
{ok, #tab{id = ID, rec_name = RecName}};
{ok, ID} when (Action =:= clear) ->
dets:match_delete(ID, '_'),
{ok, #tab{id = ID, rec_name = RecName}};
{error, Reason} ->
{error, {dets_open, Reason}}
end.
dets_filename(Name, Dir) ->
Dir1 = dets_filename1(Dir),
Dir2 = string:strip(Dir1, right, $/),
io_lib:format("~s/~p.dat", [Dir2, Name]).
dets_filename1([]) -> ".";
dets_filename1(Dir) -> Dir.
%% ---------------------------------------------------------------
%% close
%%
%% Close the table.
%% ---------------------------------------------------------------
close(#tab{id = ID}) ->
?vtrace("close database ~p", [ID]),
dets:close(ID).
%% ---------------------------------------------------------------
%% read
%%
%% Retrieve a record from the database table.
%% ---------------------------------------------------------------
read(#tab{id = ID}, Key) ->
?vtrace("read from table ~p: ~p", [ID, Key]),
case dets:lookup(ID, Key) of
[Rec|_] -> {value, Rec};
_ -> false
end.
%% ---------------------------------------------------------------
%% write
%%
%% Write a record to the database table.
%% ---------------------------------------------------------------
write(#tab{id = ID, rec_name = RecName}, Rec)
when (is_tuple(Rec) andalso (element(1, Rec) =:= RecName)) ->
?vtrace("write to table ~p", [ID]),
dets:insert(ID, Rec).
%% ---------------------------------------------------------------
%% delete
%%
%% Delete the database table.
%% ---------------------------------------------------------------
delete(#tab{id = ID}) ->
?vtrace("delete database ~p", [ID]),
File = dets:info(ID, filename),
case dets:close(ID) of
ok ->
file:delete(File);
Error ->
Error
end.
%% ---------------------------------------------------------------
%% delete
%%
%% Delete a record from the database table.
%% ---------------------------------------------------------------
delete(#tab{id = ID}, Key) ->
?vtrace("delete from table ~p: ~p", [ID, Key]),
dets:delete(ID, Key).
%% ---------------------------------------------------------------
%% match_object
%%
%% Search the database table for records witch matches the pattern.
%% ---------------------------------------------------------------
match_object(#tab{id = ID}, Pattern) ->
?vtrace("match_object in ~p of ~p", [ID, Pattern]),
dets:match_object(ID, Pattern).
%% ---------------------------------------------------------------
%% match_delete
%%
%% Search the database table for records witch matches the
%% pattern and deletes them from the database table.
%% ---------------------------------------------------------------
match_delete(#tab{id = ID}, Pattern) ->
?vtrace("match_delete in ~p with pattern ~p", [ID, Pattern]),
Recs = dets:match_object(ID, Pattern),
dets:match_delete(ID, Pattern),
Recs.
%% ---------------------------------------------------------------
%% tab2list
%%
%% Return all records in the table in the form of a list.
%% ---------------------------------------------------------------
tab2list(#tab{id = ID} = Tab) ->
?vtrace("tab2list -> list of ~p", [ID]),
match_object(Tab, '_').
%% ---------------------------------------------------------------
%% info
%%
%% Retrieve implementation dependent mib-storage table
%% information.
%% ---------------------------------------------------------------
info(#tab{id = ID}) ->
?vtrace("info -> info of ~p", [ID]),
dets:info(ID).
info(TabId, all = _Item) ->
info(TabId);
info(#tab{id = ID}, memory = _Item) ->
?vtrace("info on ~p (~w)", [ID, _Item]),
dets:info(ID, file_size);
info(#tab{id = ID}, Item) ->
?vtrace("info on ~p (~w)", [ID, Item]),
dets:info(ID, Item).
%% ---------------------------------------------------------------
%% sync
%%
%% Dump mib-storage table to disc (if it has a disk component)
%% ---------------------------------------------------------------
sync(#tab{id = ID}) ->
?vtrace("sync -> sync ~p", [ID]),
dets:sync(ID).
%% ---------------------------------------------------------------
%% backup
%%
%% Make a backup copy of the mib-storage table.
%% ---------------------------------------------------------------
backup(#tab{id = ID}, BackupDir) ->
?vtrace("backup -> backup of ~p to ~p", [ID, BackupDir]),
case dets:info(ID, filename) of
undefined ->
{error, no_file};
Filename ->
case filename:dirname(Filename) of
BackupDir ->
{error, db_dir};
_ ->
Type = dets:info(ID, type),
KP = dets:info(ID, keypos),
dets_backup(ID,
filename:basename(Filename),
BackupDir, Type, KP)
end
end.
dets_backup(ID, Filename, BackupDir, Type, KP) ->
?vtrace("dets_backup -> entry with"
"~n ID: ~p"
"~n Filename: ~p"
"~n BackupDir: ~p"
"~n Type: ~p"
"~n KP: ~p", [ID, Filename, BackupDir, Type, KP]),
BackupFile = filename:join(BackupDir, Filename),
?vtrace("dets_backup -> "
"~n BackupFile: ~p", [BackupFile]),
Backup = list_to_atom(atom_to_list(ID) ++ "_backup"),
Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}],
case dets:open_file(Backup, Opts) of
{ok, B} ->
?vtrace("dets_backup -> create fun", []),
F = fun(Arg) ->
dets_backup(Arg, start, ID, B)
end,
dets:safe_fixtable(ID, true),
Res = dets:init_table(Backup, F, [{format, bchunk}]),
dets:safe_fixtable(ID, false),
?vtrace("dets_backup -> Res: ~p", [Res]),
Res;
Error ->
?vinfo("dets_backup -> open_file failed: "
"~n ~p", [Error]),
Error
end.
dets_backup(close, _Cont, _ID, B) ->
dets:close(B),
ok;
dets_backup(read, Cont1, ID, B) ->
case dets:bchunk(ID, Cont1) of
{Cont2, Data} ->
F = fun(Arg) ->
dets_backup(Arg, Cont2, ID, B)
end,
{Data, F};
'$end_of_table' ->
dets:close(B),
end_of_input;
Error ->
Error
end.
%%----------------------------------------------------------------------
%% user_err(F, A) ->
%% snmpa_error:user_err(F, A).