1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-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(snmp_generic). 21 22%% Avoid warning for local function error/1 clashing with autoimported BIF. 23-compile({no_auto_import,[error/1]}). 24-export([variable_func/2, variable_func/3, variable_get/1, variable_set/2]). 25-export([table_func/2, table_func/4, 26 table_set_row/5, table_set_cols/3, table_set_cols/4, 27 table_row_exists/2, table_foreach/2, table_foreach/3, 28 table_try_row/4, table_get_row/2, table_get_row/3, 29 table_get_elements/3, table_get_elements/4, table_get_element/3, 30 table_set_element/4, table_set_elements/3, 31 table_next/2, handle_table_next/6, 32 table_try_make_consistent/3, table_max_col/2, 33 find_col/2, table_check_status/5, 34 table_find/3,split_index_to_keys/2, init_defaults/2, init_defaults/3, 35 table_info/1, 36 try_apply/2, get_own_indexes/2, table_create_rest/6, 37 handle_table_get/4, variable_inc/2, 38 get_status_col/2, get_table_info/2, get_index_types/1]). 39 40-include("STANDARD-MIB.hrl"). 41-include("snmp_types.hrl"). 42 43-define(VMODULE,"GENERIC"). 44-include("snmp_verbosity.hrl"). 45 46-ifndef(default_verbosity). 47-define(default_verbosity,silence). 48-endif. 49 50 51%%%----------------------------------------------------------------- 52%%% Generic functions for implementing software tables 53%%% and variables. 54%%%----------------------------------------------------------------- 55%% NameDb is {TableName, Db} where Db is volatile | persistent | mnesia 56 57%%------------------------------------------------------------------ 58%% Access functions to the database. 59%%------------------------------------------------------------------ 60variable_get({Name, mnesia}) -> 61 snmp_generic_mnesia:variable_get(Name); 62variable_get(NameDb) -> % ret {value, Val} | undefined 63 snmpa_local_db:variable_get(NameDb). 64variable_set({Name, mnesia}, Val) -> 65 snmp_generic_mnesia:variable_set(Name, Val); 66variable_set(NameDb, Val) -> % ret true 67 snmpa_local_db:variable_set(NameDb, Val). 68 69variable_inc({Name, mnesia}, N) -> 70 snmp_generic_mnesia:variable_inc(Name, N); 71variable_inc(NameDb, N) -> % ret true 72 snmpa_local_db:variable_inc(NameDb, N). 73 74%%----------------------------------------------------------------- 75%% Returns: {value, Val} | undefined 76%% 77%% snmpa_local_db overloads (for performance reasons? (mbj?)) 78%%----------------------------------------------------------------- 79table_get_element({Name, volatile}, RowIndex, Col) -> 80 snmpa_local_db:table_get_element({Name, volatile}, RowIndex, Col); 81table_get_element({Name, persistent}, RowIndex, Col) -> 82 snmpa_local_db:table_get_element({Name, persistent}, RowIndex, Col); 83table_get_element(NameDb, RowIndex, Col) -> 84 TableInfo = table_info(NameDb), 85 case handle_table_get(NameDb,RowIndex,[Col], 86 TableInfo#table_info.first_own_index) of 87 [{value, Val}] -> {value, Val}; 88 _ -> undefined 89 end. 90 91table_get_elements(NameDb, RowIndex, Cols) -> 92 TableInfo = snmp_generic:table_info(NameDb), 93 table_get_elements(NameDb, RowIndex, Cols, 94 TableInfo#table_info.first_own_index). 95 96%%---------------------------------------------------------------------- 97%% Returns: list of vals | undefined 98%%---------------------------------------------------------------------- 99table_get_elements({Name, mnesia}, RowIndex, Cols, FirstOwnIndex) -> 100 ?vtrace("table_get_elements(mnesia) -> entry with" 101 "~n Name: ~p" 102 "~n RowIndex: ~p" 103 "~n Cols: ~p" 104 "~n FirstOwnIndex: ~p", [Name, RowIndex, Cols, FirstOwnIndex]), 105 snmp_generic_mnesia:table_get_elements(Name, RowIndex, Cols, FirstOwnIndex); 106table_get_elements(NameDb, RowIndex, Cols, FirstOwnIndex) -> 107 ?vtrace("table_get_elements -> entry with" 108 "~n NameDb: ~p" 109 "~n RowIndex: ~p" 110 "~n Cols: ~p" 111 "~n FirstOwnIndex: ~p", [NameDb, RowIndex, Cols, FirstOwnIndex]), 112 snmpa_local_db:table_get_elements(NameDb, RowIndex, Cols, FirstOwnIndex). 113 114 115%% ret true 116table_set_element({Name,mnesia}, RowIndex, Col, NewVal) -> 117 snmp_generic_mnesia:table_set_elements(Name, RowIndex, 118 [{Col, NewVal}]); 119table_set_element(NameDb, RowIndex, Col, NewVal) -> 120 snmpa_local_db:table_set_elements(NameDb, RowIndex, [{Col, NewVal}]). 121 122table_set_elements({Name, mnesia}, RowIndex, Cols) -> 123 snmp_generic_mnesia:table_set_elements(Name, RowIndex, Cols); 124table_set_elements(NameDb, RowIndex, Cols) -> % ret true 125 snmpa_local_db:table_set_elements(NameDb, RowIndex, Cols). 126 127table_next({Name, mnesia}, RestOid) -> 128 snmp_generic_mnesia:table_next(Name, RestOid); 129table_next(NameDb, RestOid) -> % ret RRestOid | endOfTable 130 snmpa_local_db:table_next(NameDb, RestOid). 131table_max_col(NameDb, Col) -> % ret largest element in Col 132 % in the table NameDb. 133 snmpa_local_db:table_max_col(NameDb, Col). 134 135 136%%------------------------------------------------------------------ 137%% Theses functions could be in the MIB for simple 138%% variables or tables, i.e. vars without complex 139%% set-operations. If there are complex set op, an 140%% extra layer-function should be added, and that 141%% function should be in the MIB, and it can call these 142%% functions. 143%% The MIB functions just provide the table name, column 144%% and a list of the keys for the table. 145%%------------------------------------------------------------------ 146 147%%------------------------------------------------------------------ 148%% Variables 149%%------------------------------------------------------------------ 150%%------------------------------------------------------------------ 151%% This is the default function for variables. 152%%------------------------------------------------------------------ 153 154variable_func(new, NameDb) -> 155 case variable_get(NameDb) of 156 {value, _} -> ok; 157 undefined -> 158 #variable_info{defval = Defval} = variable_info(NameDb), 159 variable_set(NameDb, Defval) 160 end; 161 162variable_func(delete, _NameDb) -> 163 ok; 164 165variable_func(get, NameDb) -> 166 case variable_get(NameDb) of 167 {value, Val} -> {value, Val}; 168 _ -> genErr 169 end. 170 171variable_func(is_set_ok, _Val, _NameDb) -> 172 noError; 173variable_func(set, Val, NameDb) -> 174 case variable_set(NameDb, Val) of 175 true -> noError; 176 false -> commitFailed 177 end; 178variable_func(undo, _Val, _NameDb) -> 179 noError. 180 181%%------------------------------------------------------------------ 182%% Tables 183%% Assumes the RowStatus is the last column in the 184%% table. 185%%------------------------------------------------------------------ 186%%------------------------------------------------------------------ 187%% This is the default function for tables. 188%% 189%% NameDb is the name of the table (atom) 190%% RowIndex is a flat list of the indexes for the row. 191%% Col is the column number. 192%%------------------------------------------------------------------ 193%% Each database implements its own table_func 194%%------------------------------------------------------------------ 195table_func(Op, {Name, mnesia}) -> 196 snmp_generic_mnesia:table_func(Op, Name); 197 198table_func(Op, NameDb) -> 199 snmpa_local_db:table_func(Op, NameDb). 200 201table_func(Op, RowIndex, Cols, {Name, mnesia}) -> 202 snmp_generic_mnesia:table_func(Op, RowIndex, Cols, Name); 203 204table_func(Op, RowIndex, Cols, NameDb) -> 205 snmpa_local_db:table_func(Op, RowIndex, Cols, NameDb). 206 207%%---------------------------------------------------------------------- 208%% DB independent. 209%%---------------------------------------------------------------------- 210handle_table_get(NameDb, RowIndex, Cols, FirstOwnIndex) -> 211 case table_get_elements(NameDb, RowIndex, Cols, FirstOwnIndex) of 212 undefined -> 213 ?vdebug("handle_table_get -> undefined", []), 214 make_list(length(Cols), {noValue, noSuchInstance}); 215 Res -> 216 ?vtrace("handle_table_get -> Res: ~n ~p", [Res]), 217 validate_get(Cols, Res) 218 end. 219 220validate_get([_Col | Cols], [Res | Ress]) -> 221 NewVal = 222 case Res of 223 noinit -> {noValue, unSpecified}; 224 noacc -> {noAccess, unSpecified}; 225 Val -> {value, Val} 226 end, 227 [NewVal | validate_get(Cols, Ress)]; 228validate_get([], []) -> []. 229 230make_list(N, X) when N > 0 -> [X | make_list(N-1, X)]; 231make_list(_, _) -> []. 232 233table_foreach(Tab, Fun) -> 234 ?vdebug("apply fun to all in table ~w",[Tab]), 235 table_foreach(Tab, Fun, undefined, []). 236table_foreach(Tab, Fun, FOI) -> 237 ?vdebug("apply fun to all in table ~w",[Tab]), 238 table_foreach(Tab, Fun, FOI, []). 239table_foreach(Tab, Fun, FOI, Oid) -> 240 case table_next(Tab, Oid) of 241 endOfTable -> 242 ?vdebug("end of table",[]), 243 ok; 244 Oid -> 245 %% OOUPS, circular ref, major db fuckup 246 ?vinfo("cyclic reference: ~w -> ~w",[Oid,Oid]), 247 exit({cyclic_db_reference,Oid}); 248 NextOid -> 249 ?vtrace("get row for oid ~w",[NextOid]), 250 case table_get_row(Tab, NextOid, FOI) of 251 undefined -> ok; 252 Row -> 253 ?vtrace("row: ~w",[Row]), 254 Fun(NextOid, Row) 255 end, 256 table_foreach(Tab, Fun, FOI, NextOid) 257 end. 258 259%%------------------------------------------------------------------ 260%% Used to implement next, and to find next entry's 261%% keys in a table when not all of the keys are known. 262%% 263%% FirstCol is the first column in the search. 264%% LastCol is the last column. 265%% Col is the current column. 266%% If Col is less than FirstCol, (or not present), the 267%% search shall begin in the first row (no indexes) of 268%% column FirstCol. 269%% Returns: List of endOfTable | {NextOid, Value} 270%%------------------------------------------------------------------ 271handle_table_next(_NameDb, _RowIndex, [], _FirstCol, _FOI, _LastCol) -> 272 []; 273handle_table_next(NameDb, RowIndex, OrgCols, FirstCol, FOI, LastCol) -> 274 FirstVals = 275 case split_cols(OrgCols, FirstCol, LastCol) of 276 {[], Cols, LastCols} -> 277 []; 278 {FirstCols, Cols, LastCols} -> 279 handle_table_next(NameDb, [], FirstCols, FirstCol, FOI, LastCol) 280 end, 281 NextVals = 282 case table_next(NameDb, RowIndex) of 283 endOfTable -> 284 {NewCols, EndOfTabs} = make_new_cols(Cols, LastCol), 285 NewVals = 286 handle_table_next(NameDb, [], NewCols,FirstCol,FOI,LastCol), 287 lists:append(NewVals, EndOfTabs); 288 NextIndex -> 289 % We found next Row; check if all Cols are initialized. 290 Row = table_get_elements(NameDb, NextIndex, Cols, FOI), 291 check_all_initalized(Row,Cols,NameDb,NextIndex, 292 FirstCol, FOI, LastCol) 293 end, 294 lists:append([FirstVals, NextVals, LastCols]). 295 296%% Split into three parts A,B,C; A < FirstCol =< B =< LastCol < C 297split_cols([Col | Cols], FirstCol, LastCol) when Col < FirstCol -> 298 {A, B, C} = split_cols(Cols, FirstCol, LastCol), 299 {[FirstCol | A], B, C}; 300split_cols([Col | Cols], FirstCol, LastCol) when Col > LastCol -> 301 {A, B, C} = split_cols(Cols, FirstCol, LastCol), 302 {A, B, [endOfTable | C]}; 303split_cols([Col | Cols], FirstCol, LastCol) -> 304 {A, B, C} = split_cols(Cols, FirstCol, LastCol), 305 {A, [Col | B], C}; 306split_cols([], _FirstCol, _LastCol) -> 307 {[], [], []}. 308 309%% Add 1 to each col < lastcol. Otherwise make it into 310%% endOfTable. 311make_new_cols([Col | Cols], LastCol) when Col < LastCol -> 312 {NewCols, Ends} = make_new_cols(Cols, LastCol), 313 {[Col+1 | NewCols], Ends}; 314make_new_cols([_Col | Cols], LastCol) -> 315 {NewCols, Ends} = make_new_cols(Cols, LastCol), 316 {NewCols, [endOfTable | Ends]}; 317make_new_cols([], _LastCol) -> 318 {[], []}. 319 320check_all_initalized([noinit|Vals],[Col|Cols],Name,RowIndex, 321 FirstCol, FOI, LastCol) -> 322 [NextValForThisCol] = 323 handle_table_next(Name, RowIndex, [Col], FirstCol, FOI, LastCol), 324 [NextValForThisCol | 325 check_all_initalized(Vals, Cols, Name, RowIndex, FirstCol, FOI, LastCol)]; 326check_all_initalized([noacc|Vals],[Col|Cols],Name,RowIndex, 327 FirstCol, FOI, LastCol) -> 328 [NextValForThisCol] = 329 handle_table_next(Name, RowIndex, [Col], FirstCol, FOI, LastCol), 330 [NextValForThisCol | 331 check_all_initalized(Vals, Cols, Name, RowIndex, FirstCol, FOI, LastCol)]; 332check_all_initalized([Val | Vals], [Col | Cols], Name, RowIndex, 333 FirstCol, FOI, LastCol) -> 334 [{[Col | RowIndex], Val} | 335 check_all_initalized(Vals, Cols, Name, RowIndex, FirstCol, FOI, LastCol)]; 336check_all_initalized([], [], _Name, _RowIndex, _FirstCol, _FOI, _LastCol) -> 337 []. 338 339 340%%------------------------------------------------------------------ 341%% Implements is_set_ok. 342%%------------------------------------------------------------------ 343%% TryChangeStatusFunc is a function that will be 344%% called if the rowstatus column is changed. 345%% Arguments: (StatusVal, RowIndex, Cols) 346%% Two cases: 347%% 1) Last col is RowStatus - check status 348%% 2) No modification to RowStatus - check that row exists. 349%%------------------------------------------------------------------ 350table_try_row(_NameDb, _TryChangeStatusFunc, _RowIndex, []) -> {noError, 0}; 351table_try_row(NameDb, TryChangeStatusFunc, RowIndex, Cols) -> 352 #table_info{status_col = StatusCol} = table_info(NameDb), 353 case lists:keysearch(StatusCol, 1, Cols) of 354 {value, {StatusCol, Val}} -> 355 case table_check_status(NameDb, StatusCol, 356 Val, RowIndex, Cols) of 357 {noError, 0} -> 358 try_apply(TryChangeStatusFunc, [NameDb, Val, 359 RowIndex, Cols]); 360 Error -> Error 361 end; 362 _ -> 363 case table_row_exists(NameDb, RowIndex) of 364 true -> {noError, 0}; 365 false -> 366 [{ColNo, _Val}|_] = Cols, 367 {inconsistentName, ColNo} 368 end 369 end. 370 371%%------------------------------------------------------------------ 372%% table_check_status can be used by the is_set_ok 373%% procedure of all tables, to check the 374%% status variable, if present in Cols. 375%% table_check_status(NameDb, Col, Val, RowIndex, Cols) -> 376%% NameDb : the name of the table 377%% Col : the columnnumber of RowStatus 378%% Val : the value of the RowStatus Col 379%%------------------------------------------------------------------ 380 381%% Try to make the row active. Ok if status != notReady 382%% If it is notReady, make sure no row has value noinit. 383table_check_status(NameDb, Col, ?'RowStatus_active', RowIndex, Cols) -> 384 case table_get_row(NameDb, RowIndex) of 385 Row when is_tuple(Row) andalso 386 (element(Col, Row) =:= ?'RowStatus_notReady') -> 387 case is_any_noinit(Row, Cols) of 388 false -> {noError, 0}; 389 true -> {inconsistentValue, Col} 390 end; 391 undefined -> {inconsistentValue, Col}; 392 _Else -> {noError, 0} 393 end; 394 395%% Try to make the row inactive. Ok if status != notReady 396table_check_status(NameDb, Col, ?'RowStatus_notInService', RowIndex, Cols) -> 397 case table_get_row(NameDb, RowIndex) of 398 Row when is_tuple(Row) andalso 399 (element(Col, Row) =:= ?'RowStatus_notReady') -> 400 case is_any_noinit(Row, Cols) of 401 false -> {noError, 0}; 402 true -> {inconsistentValue, Col} 403 end; 404 undefined -> {inconsistentValue, Col}; 405 _Else -> {noError, 0} 406 end; 407 408%% Try to createAndGo 409%% Ok if values are provided, or default values can be used for 410%% all columns. 411table_check_status(NameDb, Col, ?'RowStatus_createAndGo', RowIndex, Cols) -> 412 case table_row_exists(NameDb, RowIndex) of 413 false -> 414 % it's ok to use snmpa_local_db:table_construct_row since it's 415 % side effect free and we only use the result temporary. 416 try snmpa_local_db:table_construct_row( 417 NameDb, RowIndex, ?'RowStatus_createAndGo', Cols) of 418 Row -> 419 case lists:member(noinit, tuple_to_list(Row)) of 420 false -> {noError, 0}; 421 _Found -> {inconsistentValue, Col} 422 end 423 catch 424 _:_E:_S -> 425 ?vtrace( 426 "failed construct row (createAndGo): " 427 "~n Error: ~p" 428 "~n Stack: ~p", 429 [_E, _S]), 430 {noCreation, Col} % Bad RowIndex 431 end; 432 true -> {inconsistentValue, Col} 433 end; 434 435%% Try to createAndWait - ok if row doesn't exist. 436table_check_status(NameDb, Col, ?'RowStatus_createAndWait', RowIndex, Cols) -> 437 case table_row_exists(NameDb, RowIndex) of 438 false -> 439 try snmpa_local_db:table_construct_row( 440 NameDb, RowIndex, ?'RowStatus_createAndGo', Cols) of 441 _Row -> 442 {noError, 0} 443 catch 444 _:_E:_S -> 445 ?vtrace( 446 "failed construct row (createAndWait): " 447 "~n Error: ~p" 448 "~n Stack: ~p", 449 [_E, _S]), 450 {noCreation, Col} % Bad RowIndex 451 end; 452 true -> {inconsistentValue, Col} 453 end; 454 455%% Try to destroy 456table_check_status(_NameDb, _Col, ?'RowStatus_destroy', _RowIndex, _Cols) -> 457 {noError, 0}; 458 459%% Otherwise, notReady. It isn't possible to set a row to notReady. 460table_check_status(_NameDb, Col, _, _RowIndex, _Cols) -> 461 {inconsistentValue, Col}. 462 463is_any_noinit(Row, Cols) -> 464 is_any_noinit(tuple_to_list(Row), Cols, 1). 465is_any_noinit([noinit | Vals], [{N, _Value} | Cols], N) -> 466 is_any_noinit(Vals, Cols, N+1); 467is_any_noinit([noinit | _Vals], _Cols, _N) -> 468 true; 469is_any_noinit([_ | Vals], [{N, _Value} | Cols], N) -> 470 is_any_noinit(Vals, Cols, N+1); 471is_any_noinit([_ | Vals], Cols, N) -> 472 is_any_noinit(Vals, Cols, N+1); 473is_any_noinit([], _, _) -> 474 false. 475 476%%------------------------------------------------------------------ 477%% Implements set. 478%% ChangedStatusFunc is a function that will be 479%% called if the rowstatus column is changed. 480%% The function is called *after* the row is created or 481%% otherwise modified, but *before* it is deleted. 482%% Arguments: (StatusVal, RowIndex, Cols) 483%% ConsFunc is a consistency-check function which will 484%% be called with the RowIndex of this row, if 485%% no operation on the row is made, when 486%% all columns are set, OR when row is createAndWait:ed. 487%% This is useful when the RowStatus 488%% could change, e.g. if the manager has provided all 489%% mandatory columns in this set operation. 490%% If it is nofunc, no function will be called after all 491%% sets. 492%%------------------------------------------------------------------ 493table_set_row(_NameDb, _, _, _RowIndex, []) -> {noError, 0}; 494table_set_row(NameDb, ChangedStatusFunc, ConsFunc, RowIndex, Cols) -> 495 #table_info{status_col = StatusCol} = table_info(NameDb), 496 case lists:keysearch(StatusCol, 1, Cols) of 497 {value, {StatusCol, Val}} -> 498 table_set_status(NameDb, RowIndex, Val, StatusCol, 499 Cols, ChangedStatusFunc, ConsFunc); 500 _ -> table_set_cols(NameDb, RowIndex, Cols, ConsFunc) 501 end. 502 503%%---------------------------------------------------------------------- 504%% Mnesia overloads for performance reasons. 505%%---------------------------------------------------------------------- 506table_set_status({Name, mnesia}, RowIndex, Status, StatusCol, Cols, 507 ChangedStatusFunc, ConsFunc) -> 508 snmp_generic_mnesia:table_set_status(Name, RowIndex, 509 Status, StatusCol, Cols, 510 ChangedStatusFunc, ConsFunc); 511 512table_set_status(NameDb,RowIndex, Status, StatusCol, Cols, 513 ChangedStatusFunc,ConsFunc) -> 514 snmpa_local_db:table_set_status(NameDb, RowIndex, 515 Status, StatusCol, Cols, 516 ChangedStatusFunc, ConsFunc). 517 518init_defaults(Defs, InitRow) -> 519 table_defaults(InitRow, Defs). 520init_defaults(Defs, InitRow, StartCol) -> 521 table_defaults(InitRow, StartCol, Defs). 522%%----------------------------------------------------------------- 523%% Get, from a list of Keys, the Keys defined in this table. 524%% (e.g. if INDEX { ifIndex, myOwnIndex }, the Keys is a list 525%% of two elements, and returned from this func is a list of 526%% the last of the two.) 527%%----------------------------------------------------------------- 528get_own_indexes(0, _Keys) -> []; 529get_own_indexes(1, Keys) -> Keys; 530get_own_indexes(Index, [_Key | Keys]) -> 531 get_own_indexes(Index - 1, Keys). 532 533%%----------------------------------------------------------------- 534%% Creates everything but the INDEX columns. 535%% Pre: The StatusColumn is present 536%% Four cases: 537%% 0) If a column is 'not-accessible' => use noacc 538%% 1) If no value is provided for the column and column is 539%% not StatusCol => use noinit 540%% 2) If column is not StatusCol, use the provided value 541%% 3) If column is StatusCol, use Status 542%%----------------------------------------------------------------- 543table_create_rest(Col, Max, _ , _ , [], _NoAcc) when Col > Max -> []; 544table_create_rest(Col,Max,StatusCol,Status,[{Col,_Val}|Defs],[Col|NoAccs]) -> 545 % case 0 546 [noacc | table_create_rest(Col+1, Max, StatusCol, Status, Defs, NoAccs)]; 547table_create_rest(Col,Max,StatusCol,Status,Defs,[Col|NoAccs]) -> 548 % case 0 549 [noacc | table_create_rest(Col+1, Max, StatusCol, Status, Defs, NoAccs)]; 550table_create_rest(StatCol, Max, StatCol, Status, [{_Col, _Val} |Defs], NoAccs) -> 551 % case 3 552 [Status | table_create_rest(StatCol+1, Max, StatCol, Status,Defs,NoAccs)]; 553table_create_rest(Col, Max, StatusCol, Status, [{Col, Val} |Defs],NoAccs) -> 554 % case 2 555 [Val | table_create_rest(Col+1, Max, StatusCol, Status,Defs,NoAccs)]; 556table_create_rest(StatCol, Max, StatCol, Status, Cols, NoAccs) -> 557 % case 3 558 [Status | table_create_rest(StatCol+1, Max, StatCol, Status, Cols, NoAccs)]; 559table_create_rest(Col, Max, StatusCol, Status, Cols, NoAccs) when Col =< Max-> 560 % case 1 561 [noinit | table_create_rest(Col+1, Max, StatusCol, Status, Cols, NoAccs)]. 562 563%%------------------------------------------------------------------ 564%% Sets default values to a row. 565%% InitRow is a list of values. 566%% Defs is a list of {Col, DefVal}, in Column order. 567%% Returns a new row (a list of values) with the same values as 568%% InitRow, except if InitRow has value noinit in a column, and 569%% the corresponing Col has a DefVal in Defs, then the DefVal 570%% will be the new value. 571%%------------------------------------------------------------------ 572table_defaults(InitRow, Defs) -> table_defaults(InitRow, 1, Defs). 573 574table_defaults([], _, _Defs) -> []; 575table_defaults([noinit | T], CurIndex, [{CurIndex, DefVal} | Defs]) -> 576 [DefVal | table_defaults(T, CurIndex+1, Defs)]; 577%% 'not-accessible' columns don't get a value 578table_defaults([noacc | T], CurIndex, [{CurIndex, _DefVal} | Defs]) -> 579 [noacc | table_defaults(T, CurIndex+1, Defs)]; 580table_defaults([Val | T], CurIndex, [{CurIndex, _DefVal} | Defs]) -> 581 [Val | table_defaults(T, CurIndex+1, Defs)]; 582table_defaults([Val | T], CurIndex, Defs) -> 583 [Val | table_defaults(T, CurIndex+1, Defs)]. 584 585 586%%------------------------------------------------------------------ 587%% table_set_cols/3,4 588%% can be used by the set procedure of all tables 589%% to set all columns in Cols, one at a time. 590%% ConsFunc is a check-consistency function, which will 591%% be called with the RowIndex of this row, when 592%% all columns are set. This is useful when the RowStatus 593%% could change, e.g. if the manager has provided all 594%% mandatory columns in this set operation. 595%% If ConsFunc is nofunc, no function will be called after all 596%% sets. 597%% Returns: {noError, 0} | {Error, Col} 598%%------------------------------------------------------------------ 599%% mnesia uses its own for performance reasons. 600%% ----------------------------------------------------------------- 601table_set_cols({Name,mnesia}, RowIndex, Cols, ConsFunc) -> 602 snmp_generic_mnesia:table_set_cols(Name, RowIndex,Cols,ConsFunc); 603table_set_cols(NameDb, RowIndex, Cols, ConsFunc) -> 604 case table_set_cols(NameDb, RowIndex, Cols) of 605 {noError, 0} -> try_apply(ConsFunc, [NameDb, RowIndex, Cols]); 606 Error -> Error 607 end. 608 609table_set_cols(_NameDb, _RowIndex, []) -> 610 {noError, 0}; 611table_set_cols(NameDb, RowIndex, [{Col, Val} | Cols]) -> 612 case catch table_set_element(NameDb, RowIndex, Col, Val) of 613 true -> 614 table_set_cols(NameDb, RowIndex, Cols); 615 X -> 616 user_err("snmp_generic:table_set_cols set ~w to" 617 " ~w returned ~w", 618 [{NameDb, RowIndex}, {Col, Val}, X]), 619 {undoFailed, Col} 620 end. 621 622%%------------------------------------------------------------------ 623%% This function splits RowIndex which is part 624%% of a OID, into a list of the indexes for the 625%% table. So a table with indexes {integer, octet string}, 626%% and a RowIndex [4,3,5,6,7], will be split into 627%% [4, [5,6,7]]. 628%%------------------------------------------------------------------ 629split_index_to_keys(Indexes, RowIndex) -> 630 collect_keys(Indexes, RowIndex). 631 632collect_keys([#asn1_type{bertype = 'INTEGER'} | Indexes], [IntKey | Keys]) -> 633 [IntKey | collect_keys(Indexes, Keys)]; 634collect_keys([#asn1_type{bertype = 'Unsigned32'} | Indexes], [IntKey | Keys]) -> 635 [IntKey | collect_keys(Indexes, Keys)]; 636collect_keys([#asn1_type{bertype = 'Counter32'} | Indexes], [IntKey | Keys]) -> 637 %% Should we allow this - counter in INDEX is strange! 638 [IntKey | collect_keys(Indexes, Keys)]; 639collect_keys([#asn1_type{bertype = 'TimeTicks'} | Indexes], [IntKey | Keys]) -> 640 %% Should we allow this - timeticks in INDEX is strange! 641 [IntKey | collect_keys(Indexes, Keys)]; 642collect_keys([#asn1_type{bertype = 'IpAddress'} | Indexes], 643 [A, B, C, D | Keys]) -> 644 [[A, B, C, D] | collect_keys(Indexes, Keys)]; 645%% Otherwise, check if it has constant size 646collect_keys([#asn1_type{lo = X, hi = X} | Indexes], Keys) 647 when is_integer(X) andalso (length(Keys) >= X) -> 648 {StrKey, Rest} = collect_length(X, Keys, []), 649 [StrKey | collect_keys(Indexes, Rest)]; 650collect_keys([#asn1_type{lo = X, hi = X} | _Indexes], Keys) 651 when is_integer(X) -> 652 exit({error, {size_mismatch, X, Keys}}); 653%% Otherwise, its a dynamic-length type => its a list 654%% OBJECT IDENTIFIER, OCTET STRING or BITS (or derivatives) 655%% Check if it is IMPLIED (only last element can be IMPLIED) 656collect_keys([#asn1_type{implied = true}], Keys) -> 657 [Keys]; 658collect_keys([_Type | Indexes], [Length | Keys]) when length(Keys) >= Length -> 659 {StrKey, Rest} = collect_length(Length, Keys, []), 660 [StrKey | collect_keys(Indexes, Rest)]; 661collect_keys([_Type | _Indexes], [Length | Keys]) -> 662 exit({error, {size_mismatch, Length, Keys}}); 663collect_keys([], []) -> []; 664collect_keys([], Keys) -> 665 exit({error, {bad_keys, Keys}}); 666collect_keys(_Any, Key) -> [Key]. 667 668collect_length(0, Rest, Rts) -> 669 {lists:reverse(Rts), Rest}; 670collect_length(N, [El | Rest], Rts) -> 671 collect_length(N-1, Rest, [El | Rts]). 672 673%%------------------------------------------------------------------ 674%% Checks if a certain row exists. 675%% Returns true or false. 676%%------------------------------------------------------------------ 677table_row_exists(NameDb, RowIndex) -> 678 case table_get_element(NameDb, RowIndex, 1) of 679 undefined -> false; 680 _ -> true 681 end. 682 683%%------------------------------------------------------------------ 684%% table_find(NameDb, Col, Value) 685%% Finds a row (if one exists) in table NameDb 686%% with column Col equals to Value. 687%% Returns the RowIndex of the row, or false 688%% if no row exists. 689%%------------------------------------------------------------------ 690table_find(NameDb, Col, Value) -> table_find(NameDb, Col, Value, []). 691table_find(NameDb, Col, Value, Indexes) -> 692 case table_next(NameDb, Indexes) of 693 endOfTable -> 694 false; 695 NewIndexes -> 696 case table_get_element(NameDb, NewIndexes, Col) of 697 {value, Value} -> NewIndexes; 698 _Else -> table_find(NameDb, Col, Value, NewIndexes) 699 end 700 end. 701 702 703%%------------------------------------------------------------------ 704%% find_col(Col, Cols) 705%% undefined if a Col for column Col doesn't exist. 706%% {value, Val} if a Col for Col with value Val exists. 707%%------------------------------------------------------------------ 708find_col(_Col, []) -> undefined; 709find_col(Col, [{Col, Val} | _T]) -> {value, Val}; 710find_col(Col, [_H | T]) -> find_col(Col, T). 711 712%%------------------------------------------------------------------ 713%% check_mandatory_cols(ListOfCols, Cols) 714%% {noError 0}if all columns in ListOfCols are present in Cols. 715%% {inconsistentValue 0} otherwise. (Index = 0. It's hard to tell 716%% which Col is wrong, when the problem is that one is missing!) 717%%------------------------------------------------------------------ 718% check_mandatory_cols([], _) -> {noError, 0}; 719% check_mandatory_cols(_, []) -> {inconsistentValue, 0}; 720% check_mandatory_cols([Col | Cols], [{Col, Val} | T]) -> 721% check_mandatory_cols(Cols, T); 722% check_mandatory_cols([Col | Cols], [{Col2, Val} | T]) -> 723% check_mandatory_cols([Col | Cols], T). 724 725 726try_apply(nofunc, _) -> {noError, 0}; 727try_apply(F, Args) -> maybe_verbose_apply(F, Args). 728 729maybe_verbose_apply(M, Args) -> 730 case get(verbosity) of 731 false -> 732 apply(M, Args); 733 _ -> 734 ?vlog("~n apply: ~w,~p~n", [M,Args]), 735 Res = apply(M,Args), 736 ?vlog("~n returned: ~p", [Res]), 737 Res 738 end. 739 740 741table_info({Name, _Db}) -> 742 case snmpa_symbolic_store:table_info(Name) of 743 {value, TI} -> 744 TI; 745 false -> 746 error({table_not_found, Name}) 747 end; 748table_info(Name) -> 749 case snmpa_symbolic_store:table_info(Name) of 750 {value, TI} -> 751 TI; 752 false -> 753 error({table_not_found, Name}) 754 end. 755 756variable_info({Name, _Db}) -> 757 case snmpa_symbolic_store:variable_info(Name) of 758 {value, VI} -> 759 VI; 760 false -> 761 error({variable_not_found, Name}) 762 end; 763variable_info(Name) -> 764 case snmpa_symbolic_store:variable_info(Name) of 765 {value, VI} -> 766 VI; 767 false -> 768 error({variable_not_found, Name}) 769 end. 770 771 772%%------------------------------------------------------------------ 773%% This function is a simple consistency check 774%% function which could be used by the user-defined 775%% table functions. 776%% Check if the row has all information needed to 777%% make row notInService (from notReady). This is 778%% a simple check, which just checks if some col 779%% in the row has the value 'noinit'. 780%% If it has the information, the status is changed 781%% to notInService. 782%%------------------------------------------------------------------ 783table_try_make_consistent(Name, RowIndex, _Cols) -> 784 TableInfo = table_info(Name), 785 case TableInfo#table_info.status_col of 786 StatusCol when is_integer(StatusCol) -> 787 {value, StatusVal} = table_get_element(Name, RowIndex, StatusCol), 788 table_try_make_consistent(Name, RowIndex, StatusVal, TableInfo); 789 _ -> 790 {noError, 0} 791 end. 792 793table_try_make_consistent(Name, RowIndex, ?'RowStatus_notReady', TableInfo) -> 794 %% this *should* be a generic function, 795 %% but since mnesia got its own try_mk_cons 796 %% and I don't have time to impl table_get_row 797 %% for mnesia I call snmpa_local_db: 798 Row = snmpa_local_db:table_get_row(Name, RowIndex), 799 case lists:member(noinit, tuple_to_list(Row)) of 800 true -> {noError, 0}; 801 false -> 802 case catch table_set_element(Name, RowIndex, 803 TableInfo#table_info.status_col, 804 ?'RowStatus_notInService') of 805 true -> {noError, 0}; 806 X -> 807 user_err("snmp_generic:table_try_make_consistent " 808 "set ~w to notInService returned ~w", 809 [{Name, RowIndex}, X]), 810 {commitFailed, TableInfo#table_info.status_col} 811 end 812 end; 813 814table_try_make_consistent(_Name, _RowIndex, _StatusVal, _TableInfo) -> 815 {noError, 0}. 816 817table_get_row({Name, mnesia}, RowIndex) -> 818 snmp_generic_mnesia:table_get_row(Name, RowIndex); 819table_get_row(NameDb, RowIndex) -> 820 snmpa_local_db:table_get_row(NameDb, RowIndex). 821 822table_get_row(NameDb, RowIndex, undefined) -> 823 table_get_row(NameDb, RowIndex); 824table_get_row({Name, mnesia}, RowIndex, FOI) -> 825 snmp_generic_mnesia:table_get_row(Name, RowIndex, FOI); 826table_get_row(NameDb, RowIndex, _FOI) -> 827 snmpa_local_db:table_get_row(NameDb, RowIndex). 828 829 830%%----------------------------------------------------------------- 831%% Purpose: These functions can be used by the user's instrum func 832%% to retrieve various table info. 833%%----------------------------------------------------------------- 834 835%%----------------------------------------------------------------- 836%% Description: 837%% Used by user's instrum func to check if mstatus column is 838%% modified. 839%%----------------------------------------------------------------- 840get_status_col(Name, Cols) -> 841 #table_info{status_col = StatusCol} = table_info(Name), 842 case lists:keysearch(StatusCol, 1, Cols) of 843 {value, {StatusCol, Val}} -> {ok, Val}; 844 _ -> false 845 end. 846 847 848%%----------------------------------------------------------------- 849%% Description: 850%% Used by user's instrum func to get the table info. Specific parts 851%% or all of it. If all is selected then the result will be a tagged 852%% list of values. 853%%----------------------------------------------------------------- 854get_table_info(Name, nbr_of_cols) -> 855 get_nbr_of_cols(Name); 856get_table_info(Name, defvals) -> 857 get_defvals(Name); 858get_table_info(Name, status_col) -> 859 get_status_col(Name); 860get_table_info(Name, not_accessible) -> 861 get_not_accessible(Name); 862get_table_info(Name, index_types) -> 863 get_index_types(Name); 864get_table_info(Name, first_accessible) -> 865 get_first_accessible(Name); 866get_table_info(Name, first_own_index) -> 867 get_first_own_index(Name); 868get_table_info(Name, all) -> 869 TableInfo = table_info(Name), 870 [{nbr_of_cols, TableInfo#table_info.nbr_of_cols}, 871 {defvals, TableInfo#table_info.defvals}, 872 {status_col, TableInfo#table_info.status_col}, 873 {not_accessible, TableInfo#table_info.not_accessible}, 874 {index_types, TableInfo#table_info.index_types}, 875 {first_accessible, TableInfo#table_info.first_accessible}, 876 {first_own_index, TableInfo#table_info.first_own_index}]. 877 878 879%%----------------------------------------------------------------- 880%% Description: 881%% Used by user's instrum func to get the index types. 882%%----------------------------------------------------------------- 883get_index_types(Name) -> 884 #table_info{index_types = IndexTypes} = table_info(Name), 885 IndexTypes. 886 887get_nbr_of_cols(Name) -> 888 #table_info{nbr_of_cols = NumberOfCols} = table_info(Name), 889 NumberOfCols. 890 891get_defvals(Name) -> 892 #table_info{defvals = DefVals} = table_info(Name), 893 DefVals. 894 895get_status_col(Name) -> 896 #table_info{status_col = StatusCol} = table_info(Name), 897 StatusCol. 898 899get_not_accessible(Name) -> 900 #table_info{not_accessible = NotAcc} = table_info(Name), 901 NotAcc. 902 903get_first_accessible(Name) -> 904 #table_info{first_accessible = FirstAcc} = table_info(Name), 905 FirstAcc. 906 907get_first_own_index(Name) -> 908 #table_info{first_own_index = FirstOwnIdx} = table_info(Name), 909 FirstOwnIdx. 910 911 912%%----------------------------------------------------------------- 913 914error(Reason) -> 915 throw({error, Reason}). 916 917user_err(F, A) -> 918 snmpa_error:user_err(F, A). 919