1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2013-2016. 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 {Cont2, Data} -> 295 F = fun(Arg) -> 296 dets_backup(Arg, Cont2, ID, B) 297 end, 298 {Data, F}; 299 '$end_of_table' -> 300 dets:close(B), 301 end_of_input; 302 Error -> 303 Error 304 end. 305 306 307%%---------------------------------------------------------------------- 308 309%% user_err(F, A) -> 310%% snmpa_error:user_err(F, A). 311