1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2013-2019. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(snmpa_mib_storage_dets).
21
22-behaviour(snmpa_mib_storage).
23
24
25%%%-----------------------------------------------------------------
26%%% This module implements the snmpa_mib_storage behaviour.
27%%% It uses dets for storage.
28%%%-----------------------------------------------------------------
29
30-export([
31	 open/5,
32	 close/1,
33	 read/2,
34	 write/2,
35	 delete/1,
36	 delete/2,
37	 match_object/2,
38	 match_delete/2,
39	 tab2list/1,
40	 info/1, info/2,
41	 sync/1,
42	 backup/2
43	]).
44
45
46-define(VMODULE, "MS-DETS").
47-include("snmp_verbosity.hrl").
48
49-record(tab, {id, rec_name}).
50
51
52%% ---------------------------------------------------------------
53%% open
54%%
55%% Open or create a mib-storage (dets) table.
56%%
57%% Opts - A list of implementation dependent options
58%%        dets_open_options() = [dets_open_option()]
59%%        dets_open_option()  = {dir,       filename()} |
60%%                              {action,    keep | clear} |
61%%                              {auto_save, default | pos_integer()} |
62%%                              {repair,    force | boolean()}
63%%
64%% ---------------------------------------------------------------
65
66open(Name, RecName, _Fields, Type, Opts) ->
67    Dir      = snmp_misc:get_option(dir,       Opts),
68    Action   = snmp_misc:get_option(action,    Opts, keep),
69    AutoSave = snmp_misc:get_option(auto_save, Opts, default),
70    Repair   = snmp_misc:get_option(repair,    Opts, false),
71    File     = dets_filename(Name, Dir),
72    OpenOpts = [{file,   File},
73		{type,   Type},
74		{keypos, 2},
75		{repair, Repair}] ++
76	case AutoSave of
77	    default ->
78		[];
79	    _ ->
80		[{auto_save, AutoSave}]
81	end,
82    case dets:open_file(Name, OpenOpts) of
83	{ok, ID} when (Action =:= keep) ->
84	    {ok, #tab{id = ID, rec_name = RecName}};
85	{ok, ID} when (Action =:= clear) ->
86	    dets:match_delete(ID, '_'),
87	    {ok, #tab{id = ID, rec_name = RecName}};
88	{error, Reason} ->
89	    {error, {dets_open, Reason}}
90    end.
91
92dets_filename(Name, Dir) ->
93    Dir1 = dets_filename1(Dir),
94    Dir2 = string:strip(Dir1, right, $/),
95    io_lib:format("~s/~p.dat", [Dir2, Name]).
96
97dets_filename1([])  -> ".";
98dets_filename1(Dir) -> Dir.
99
100
101%% ---------------------------------------------------------------
102%% close
103%%
104%% Close the table.
105%% ---------------------------------------------------------------
106
107close(#tab{id = ID}) ->
108    ?vtrace("close database ~p", [ID]),
109    dets:close(ID).
110
111
112%% ---------------------------------------------------------------
113%% read
114%%
115%% Retrieve a record from the database table.
116%% ---------------------------------------------------------------
117
118read(#tab{id = ID}, Key) ->
119    ?vtrace("read from table ~p: ~p", [ID, Key]),
120    case dets:lookup(ID, Key) of
121	[Rec|_] -> {value, Rec};
122	_ -> false
123    end.
124
125
126%% ---------------------------------------------------------------
127%% write
128%%
129%% Write a record to the database table.
130%% ---------------------------------------------------------------
131
132write(#tab{id = ID, rec_name = RecName}, Rec)
133  when (is_tuple(Rec) andalso (element(1, Rec) =:= RecName)) ->
134    ?vtrace("write to table ~p", [ID]),
135    dets:insert(ID, Rec).
136
137
138%% ---------------------------------------------------------------
139%% delete
140%%
141%% Delete the database table.
142%% ---------------------------------------------------------------
143
144delete(#tab{id = ID}) ->
145    ?vtrace("delete database ~p", [ID]),
146    File = dets:info(ID, filename),
147    case dets:close(ID) of
148	ok ->
149	    file:delete(File);
150	Error ->
151	    Error
152    end.
153
154
155%% ---------------------------------------------------------------
156%% delete
157%%
158%% Delete a record from the database table.
159%% ---------------------------------------------------------------
160
161delete(#tab{id = ID}, Key) ->
162    ?vtrace("delete from table ~p: ~p", [ID, Key]),
163    dets:delete(ID, Key).
164
165
166%% ---------------------------------------------------------------
167%% match_object
168%%
169%% Search the database table for records witch matches the pattern.
170%% ---------------------------------------------------------------
171
172match_object(#tab{id = ID}, Pattern) ->
173    ?vtrace("match_object in ~p of ~p", [ID, Pattern]),
174    dets:match_object(ID, Pattern).
175
176
177%% ---------------------------------------------------------------
178%% match_delete
179%%
180%% Search the database table for records witch matches the
181%% pattern and deletes them from the database table.
182%% ---------------------------------------------------------------
183
184match_delete(#tab{id = ID}, Pattern) ->
185    ?vtrace("match_delete in ~p with pattern ~p", [ID, Pattern]),
186    Recs = dets:match_object(ID, Pattern),
187    dets:match_delete(ID, Pattern),
188    Recs.
189
190
191%% ---------------------------------------------------------------
192%% tab2list
193%%
194%% Return all records in the table in the form of a list.
195%% ---------------------------------------------------------------
196
197tab2list(#tab{id = ID} = Tab) ->
198    ?vtrace("tab2list -> list of ~p", [ID]),
199    match_object(Tab, '_').
200
201
202%% ---------------------------------------------------------------
203%% info
204%%
205%% Retrieve implementation dependent mib-storage table
206%% information.
207%% ---------------------------------------------------------------
208
209info(#tab{id = ID}) ->
210    ?vtrace("info -> info of ~p", [ID]),
211    dets:info(ID).
212
213
214info(TabId, all = _Item) ->
215    info(TabId);
216info(#tab{id = ID}, memory = _Item) ->
217    ?vtrace("info on ~p (~w)", [ID, _Item]),
218    dets:info(ID, file_size);
219info(#tab{id = ID}, Item) ->
220    ?vtrace("info on ~p (~w)", [ID, Item]),
221    dets:info(ID, Item).
222
223
224%% ---------------------------------------------------------------
225%% sync
226%%
227%% Dump mib-storage table to disc (if it has a disk component)
228%% ---------------------------------------------------------------
229
230sync(#tab{id = ID}) ->
231    ?vtrace("sync -> sync ~p", [ID]),
232    dets:sync(ID).
233
234
235%% ---------------------------------------------------------------
236%% backup
237%%
238%% Make a backup copy of the mib-storage table.
239%% ---------------------------------------------------------------
240
241backup(#tab{id = ID}, BackupDir) ->
242    ?vtrace("backup -> backup of ~p to ~p", [ID, BackupDir]),
243    case dets:info(ID, filename) of
244	undefined ->
245	    {error, no_file};
246	Filename ->
247	    case filename:dirname(Filename) of
248		BackupDir ->
249		    {error, db_dir};
250		_ ->
251		    Type = dets:info(ID, type),
252		    KP   = dets:info(ID, keypos),
253		    dets_backup(ID,
254				filename:basename(Filename),
255				BackupDir, Type, KP)
256	    end
257    end.
258
259
260dets_backup(ID, Filename, BackupDir, Type, KP) ->
261    ?vtrace("dets_backup -> entry with"
262	    "~n   ID:        ~p"
263	    "~n   Filename:  ~p"
264	    "~n   BackupDir: ~p"
265	    "~n   Type:      ~p"
266	    "~n   KP:        ~p", [ID, Filename, BackupDir, Type, KP]),
267    BackupFile = filename:join(BackupDir, Filename),
268    ?vtrace("dets_backup -> "
269	    "~n   BackupFile: ~p", [BackupFile]),
270    Backup = list_to_atom(atom_to_list(ID) ++ "_backup"),
271    Opts   = [{file, BackupFile}, {type, Type}, {keypos, KP}],
272    case dets:open_file(Backup, Opts) of
273	{ok, B} ->
274	    ?vtrace("dets_backup -> create fun", []),
275	    F = fun(Arg) ->
276			dets_backup(Arg, start, ID, B)
277		end,
278	    dets:safe_fixtable(ID, true),
279	    Res = dets:init_table(Backup, F, [{format, bchunk}]),
280	    dets:safe_fixtable(ID, false),
281	    ?vtrace("dets_backup -> Res: ~p", [Res]),
282	    Res;
283	Error ->
284	    ?vinfo("dets_backup -> open_file failed: "
285		   "~n   ~p", [Error]),
286	    Error
287    end.
288
289dets_backup(close, _Cont, _ID, B) ->
290    dets:close(B),
291    ok;
292dets_backup(read, Cont1, ID, B) ->
293    case dets:bchunk(ID, Cont1) of
294        {error, _} = ERROR ->
295            ERROR;
296	'$end_of_table' ->
297	    dets:close(B),
298	    end_of_input;
299	{Cont2, Data} ->
300	    F = fun(Arg) ->
301			dets_backup(Arg, Cont2, ID, B)
302		end,
303	    {Data, F}
304    end.
305
306
307%%----------------------------------------------------------------------
308
309%% user_err(F, A) ->
310%%     snmpa_error:user_err(F, A).
311