aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/snmp_generic.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/agent/snmp_generic.erl')
-rw-r--r--lib/snmp/src/agent/snmp_generic.erl891
1 files changed, 891 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl
new file mode 100644
index 0000000000..508aa090d9
--- /dev/null
+++ b/lib/snmp/src/agent/snmp_generic.erl
@@ -0,0 +1,891 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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(snmp_generic).
+
+-export([variable_func/2, variable_func/3, variable_get/1, variable_set/2]).
+-export([table_func/2, table_func/4,
+ table_set_row/5, table_set_cols/3, table_set_cols/4,
+ table_row_exists/2, table_foreach/2, table_foreach/3,
+ table_try_row/4, table_get_row/2, table_get_row/3,
+ table_get_elements/3, table_get_elements/4, table_get_element/3,
+ table_set_element/4, table_set_elements/3,
+ table_next/2, handle_table_next/6,
+ table_try_make_consistent/3, table_max_col/2,
+ find_col/2, table_check_status/5,
+ table_find/3,split_index_to_keys/2, init_defaults/2, init_defaults/3,
+ table_info/1,
+ try_apply/2, get_own_indexes/2, table_create_rest/6,
+ handle_table_get/4, variable_inc/2,
+ get_status_col/2, get_table_info/2, get_index_types/1]).
+
+-include("STANDARD-MIB.hrl").
+-include("snmp_types.hrl").
+
+-define(VMODULE,"GENERIC").
+-include("snmp_verbosity.hrl").
+
+-ifndef(default_verbosity).
+-define(default_verbosity,silence).
+-endif.
+
+
+%%%-----------------------------------------------------------------
+%%% Generic functions for implementing software tables
+%%% and variables.
+%%%-----------------------------------------------------------------
+%% NameDb is {TableName, Db} where Db is volatile | persistent | mnesia
+
+%%------------------------------------------------------------------
+%% Access functions to the database.
+%%------------------------------------------------------------------
+variable_get({Name, mnesia}) ->
+ snmp_generic_mnesia:variable_get(Name);
+variable_get(NameDb) -> % ret {value, Val} | undefined
+ snmpa_local_db:variable_get(NameDb).
+variable_set({Name, mnesia}, Val) ->
+ snmp_generic_mnesia:variable_set(Name, Val);
+variable_set(NameDb, Val) -> % ret true
+ snmpa_local_db:variable_set(NameDb, Val).
+
+variable_inc({Name, mnesia}, N) ->
+ snmp_generic_mnesia:variable_inc(Name, N);
+variable_inc(NameDb, N) -> % ret true
+ snmpa_local_db:variable_inc(NameDb, N).
+
+%%-----------------------------------------------------------------
+%% Returns: {value, Val} | undefined
+%%
+%% snmpa_local_db overloads (for performance reasons? (mbj?))
+%%-----------------------------------------------------------------
+table_get_element({Name, volatile}, RowIndex, Col) ->
+ snmpa_local_db:table_get_element({Name, volatile}, RowIndex, Col);
+table_get_element({Name, persistent}, RowIndex, Col) ->
+ snmpa_local_db:table_get_element({Name, persistent}, RowIndex, Col);
+table_get_element(NameDb, RowIndex, Col) ->
+ TableInfo = table_info(NameDb),
+ case handle_table_get(NameDb,RowIndex,[Col],
+ TableInfo#table_info.first_own_index) of
+ [{value, Val}] -> {value, Val};
+ _ -> undefined
+ end.
+
+table_get_elements(NameDb, RowIndex, Cols) ->
+ TableInfo = snmp_generic:table_info(NameDb),
+ table_get_elements(NameDb, RowIndex, Cols,
+ TableInfo#table_info.first_own_index).
+
+%%----------------------------------------------------------------------
+%% Returns: list of vals | undefined
+%%----------------------------------------------------------------------
+table_get_elements({Name, mnesia}, RowIndex, Cols, FirstOwnIndex) ->
+ ?vtrace("table_get_elements(mnesia) -> entry with"
+ "~n Name: ~p"
+ "~n RowIndex: ~p"
+ "~n Cols: ~p"
+ "~n FirstOwnIndex: ~p", [Name, RowIndex, Cols, FirstOwnIndex]),
+ snmp_generic_mnesia:table_get_elements(Name, RowIndex, Cols, FirstOwnIndex);
+table_get_elements(NameDb, RowIndex, Cols, FirstOwnIndex) ->
+ ?vtrace("table_get_elements -> entry with"
+ "~n NameDb: ~p"
+ "~n RowIndex: ~p"
+ "~n Cols: ~p"
+ "~n FirstOwnIndex: ~p", [NameDb, RowIndex, Cols, FirstOwnIndex]),
+ snmpa_local_db:table_get_elements(NameDb, RowIndex, Cols, FirstOwnIndex).
+
+
+%% ret true
+table_set_element({Name,mnesia}, RowIndex, Col, NewVal) ->
+ snmp_generic_mnesia:table_set_elements(Name, RowIndex,
+ [{Col, NewVal}]);
+table_set_element(NameDb, RowIndex, Col, NewVal) ->
+ snmpa_local_db:table_set_elements(NameDb, RowIndex, [{Col, NewVal}]).
+
+table_set_elements({Name, mnesia}, RowIndex, Cols) ->
+ snmp_generic_mnesia:table_set_elements(Name, RowIndex, Cols);
+table_set_elements(NameDb, RowIndex, Cols) -> % ret true
+ snmpa_local_db:table_set_elements(NameDb, RowIndex, Cols).
+
+table_next({Name, mnesia}, RestOid) ->
+ snmp_generic_mnesia:table_next(Name, RestOid);
+table_next(NameDb, RestOid) -> % ret RRestOid | endOfTable
+ snmpa_local_db:table_next(NameDb, RestOid).
+table_max_col(NameDb, Col) -> % ret largest element in Col
+ % in the table NameDb.
+ snmpa_local_db:table_max_col(NameDb, Col).
+
+
+%%------------------------------------------------------------------
+%% Theses functions could be in the MIB for simple
+%% variables or tables, i.e. vars without complex
+%% set-operations. If there are complex set op, an
+%% extra layer-function should be added, and that
+%% function should be in the MIB, and it can call these
+%% functions.
+%% The MIB functions just provide the table name, column
+%% and a list of the keys for the table.
+%%------------------------------------------------------------------
+
+%%------------------------------------------------------------------
+%% Variables
+%%------------------------------------------------------------------
+%%------------------------------------------------------------------
+%% This is the default function for variables.
+%%------------------------------------------------------------------
+
+variable_func(new, NameDb) ->
+ case variable_get(NameDb) of
+ {value, _} -> ok;
+ undefined ->
+ #variable_info{defval = Defval} = variable_info(NameDb),
+ variable_set(NameDb, Defval)
+ end;
+
+variable_func(delete, _NameDb) ->
+ ok;
+
+variable_func(get, NameDb) ->
+ case variable_get(NameDb) of
+ {value, Val} -> {value, Val};
+ _ -> genErr
+ end.
+
+variable_func(is_set_ok, _Val, _NameDb) ->
+ noError;
+variable_func(set, Val, NameDb) ->
+ case variable_set(NameDb, Val) of
+ true -> noError;
+ false -> commitFailed
+ end;
+variable_func(undo, _Val, _NameDb) ->
+ noError.
+
+%%------------------------------------------------------------------
+%% Tables
+%% Assumes the RowStatus is the last column in the
+%% table.
+%%------------------------------------------------------------------
+%%------------------------------------------------------------------
+%% This is the default function for tables.
+%%
+%% NameDb is the name of the table (atom)
+%% RowIndex is a flat list of the indexes for the row.
+%% Col is the column number.
+%%------------------------------------------------------------------
+%% Each database implements its own table_func
+%%------------------------------------------------------------------
+table_func(Op, {Name, mnesia}) ->
+ snmp_generic_mnesia:table_func(Op, Name);
+
+table_func(Op, NameDb) ->
+ snmpa_local_db:table_func(Op, NameDb).
+
+table_func(Op, RowIndex, Cols, {Name, mnesia}) ->
+ snmp_generic_mnesia:table_func(Op, RowIndex, Cols, Name);
+
+table_func(Op, RowIndex, Cols, NameDb) ->
+ snmpa_local_db:table_func(Op, RowIndex, Cols, NameDb).
+
+%%----------------------------------------------------------------------
+%% DB independent.
+%%----------------------------------------------------------------------
+handle_table_get(NameDb, RowIndex, Cols, FirstOwnIndex) ->
+ case table_get_elements(NameDb, RowIndex, Cols, FirstOwnIndex) of
+ undefined ->
+ ?vdebug("handle_table_get -> undefined", []),
+ make_list(length(Cols), {noValue, noSuchInstance});
+ Res ->
+ ?vtrace("handle_table_get -> Res: ~n ~p", [Res]),
+ validate_get(Cols, Res)
+ end.
+
+validate_get([_Col | Cols], [Res | Ress]) ->
+ NewVal =
+ case Res of
+ noinit -> {noValue, unSpecified};
+ noacc -> {noAccess, unSpecified};
+ Val -> {value, Val}
+ end,
+ [NewVal | validate_get(Cols, Ress)];
+validate_get([], []) -> [].
+
+make_list(N, X) when N > 0 -> [X | make_list(N-1, X)];
+make_list(_, _) -> [].
+
+table_foreach(Tab, Fun) ->
+ ?vdebug("apply fun to all in table ~w",[Tab]),
+ table_foreach(Tab, Fun, undefined, []).
+table_foreach(Tab, Fun, FOI) ->
+ ?vdebug("apply fun to all in table ~w",[Tab]),
+ table_foreach(Tab, Fun, FOI, []).
+table_foreach(Tab, Fun, FOI, Oid) ->
+ case table_next(Tab, Oid) of
+ endOfTable ->
+ ?vdebug("end of table",[]),
+ ok;
+ Oid ->
+ %% OOUPS, circular ref, major db fuckup
+ ?vinfo("cyclic reference: ~w -> ~w",[Oid,Oid]),
+ exit({cyclic_db_reference,Oid});
+ NextOid ->
+ ?vtrace("get row for oid ~w",[NextOid]),
+ case table_get_row(Tab, NextOid, FOI) of
+ undefined -> ok;
+ Row ->
+ ?vtrace("row: ~w",[Row]),
+ Fun(NextOid, Row)
+ end,
+ table_foreach(Tab, Fun, FOI, NextOid)
+ end.
+
+%%------------------------------------------------------------------
+%% Used to implement next, and to find next entry's
+%% keys in a table when not all of the keys are known.
+%%
+%% FirstCol is the first column in the search.
+%% LastCol is the last column.
+%% Col is the current column.
+%% If Col is less than FirstCol, (or not present), the
+%% search shall begin in the first row (no indexes) of
+%% column FirstCol.
+%% Returns: List of endOfTable | {NextOid, Value}
+%%------------------------------------------------------------------
+handle_table_next(_NameDb, _RowIndex, [], _FirstCol, _FOI, _LastCol) ->
+ [];
+handle_table_next(NameDb, RowIndex, OrgCols, FirstCol, FOI, LastCol) ->
+ FirstVals =
+ case split_cols(OrgCols, FirstCol, LastCol) of
+ {[], Cols, LastCols} ->
+ [];
+ {FirstCols, Cols, LastCols} ->
+ handle_table_next(NameDb, [], FirstCols, FirstCol, FOI, LastCol)
+ end,
+ NextVals =
+ case table_next(NameDb, RowIndex) of
+ endOfTable ->
+ {NewCols, EndOfTabs} = make_new_cols(Cols, LastCol),
+ NewVals =
+ handle_table_next(NameDb, [], NewCols,FirstCol,FOI,LastCol),
+ lists:append(NewVals, EndOfTabs);
+ NextIndex ->
+ % We found next Row; check if all Cols are initialized.
+ Row = table_get_elements(NameDb, NextIndex, Cols, FOI),
+ check_all_initalized(Row,Cols,NameDb,NextIndex,
+ FirstCol, FOI, LastCol)
+ end,
+ lists:append([FirstVals, NextVals, LastCols]).
+
+%% Split into three parts A,B,C; A < FirstCol =< B =< LastCol < C
+split_cols([Col | Cols], FirstCol, LastCol) when Col < FirstCol ->
+ {A, B, C} = split_cols(Cols, FirstCol, LastCol),
+ {[FirstCol | A], B, C};
+split_cols([Col | Cols], FirstCol, LastCol) when Col > LastCol ->
+ {A, B, C} = split_cols(Cols, FirstCol, LastCol),
+ {A, B, [endOfTable | C]};
+split_cols([Col | Cols], FirstCol, LastCol) ->
+ {A, B, C} = split_cols(Cols, FirstCol, LastCol),
+ {A, [Col | B], C};
+split_cols([], _FirstCol, _LastCol) ->
+ {[], [], []}.
+
+%% Add 1 to each col < lastcol. Otherwise make it into
+%% endOfTable.
+make_new_cols([Col | Cols], LastCol) when Col < LastCol ->
+ {NewCols, Ends} = make_new_cols(Cols, LastCol),
+ {[Col+1 | NewCols], Ends};
+make_new_cols([_Col | Cols], LastCol) ->
+ {NewCols, Ends} = make_new_cols(Cols, LastCol),
+ {NewCols, [endOfTable | Ends]};
+make_new_cols([], _LastCol) ->
+ {[], []}.
+
+check_all_initalized([noinit|Vals],[Col|Cols],Name,RowIndex,
+ FirstCol, FOI, LastCol) ->
+ [NextValForThisCol] =
+ handle_table_next(Name, RowIndex, [Col], FirstCol, FOI, LastCol),
+ [NextValForThisCol |
+ check_all_initalized(Vals, Cols, Name, RowIndex, FirstCol, FOI, LastCol)];
+check_all_initalized([noacc|Vals],[Col|Cols],Name,RowIndex,
+ FirstCol, FOI, LastCol) ->
+ [NextValForThisCol] =
+ handle_table_next(Name, RowIndex, [Col], FirstCol, FOI, LastCol),
+ [NextValForThisCol |
+ check_all_initalized(Vals, Cols, Name, RowIndex, FirstCol, FOI, LastCol)];
+check_all_initalized([Val | Vals], [Col | Cols], Name, RowIndex,
+ FirstCol, FOI, LastCol) ->
+ [{[Col | RowIndex], Val} |
+ check_all_initalized(Vals, Cols, Name, RowIndex, FirstCol, FOI, LastCol)];
+check_all_initalized([], [], _Name, _RowIndex, _FirstCol, _FOI, _LastCol) ->
+ [].
+
+
+%%------------------------------------------------------------------
+%% Implements is_set_ok.
+%%------------------------------------------------------------------
+%% TryChangeStatusFunc is a function that will be
+%% called if the rowstatus column is changed.
+%% Arguments: (StatusVal, RowIndex, Cols)
+%% Two cases:
+%% 1) Last col is RowStatus - check status
+%% 2) No modification to RowStatus - check that row exists.
+%%------------------------------------------------------------------
+table_try_row(_NameDb, _TryChangeStatusFunc, _RowIndex, []) -> {noError, 0};
+table_try_row(NameDb, TryChangeStatusFunc, RowIndex, Cols) ->
+ #table_info{status_col = StatusCol} = table_info(NameDb),
+ case lists:keysearch(StatusCol, 1, Cols) of
+ {value, {StatusCol, Val}} ->
+ case table_check_status(NameDb, StatusCol,
+ Val, RowIndex, Cols) of
+ {noError, 0} ->
+ try_apply(TryChangeStatusFunc, [NameDb, Val,
+ RowIndex, Cols]);
+ Error -> Error
+ end;
+ _ ->
+ case table_row_exists(NameDb, RowIndex) of
+ true -> {noError, 0};
+ false ->
+ [{ColNo, _Val}|_] = Cols,
+ {inconsistentName, ColNo}
+ end
+ end.
+
+%%------------------------------------------------------------------
+%% table_check_status can be used by the is_set_ok
+%% procedure of all tables, to check the
+%% status variable, if present in Cols.
+%% table_check_status(NameDb, Col, Val, RowIndex, Cols) ->
+%% NameDb : the name of the table
+%% Col : the columnnumber of RowStatus
+%% Val : the value of the RowStatus Col
+%%------------------------------------------------------------------
+
+%% Try to make the row active. Ok if status != notReady
+%% If it is notReady, make sure no row has value noinit.
+table_check_status(NameDb, Col, ?'RowStatus_active', RowIndex, Cols) ->
+ case table_get_row(NameDb, RowIndex) of
+ Row when is_tuple(Row) andalso
+ (element(Col, Row) =:= ?'RowStatus_notReady') ->
+ case is_any_noinit(Row, Cols) of
+ false -> {noError, 0};
+ true -> {inconsistentValue, Col}
+ end;
+ undefined -> {inconsistentValue, Col};
+ _Else -> {noError, 0}
+ end;
+
+%% Try to make the row inactive. Ok if status != notReady
+table_check_status(NameDb, Col, ?'RowStatus_notInService', RowIndex, Cols) ->
+ case table_get_row(NameDb, RowIndex) of
+ Row when is_tuple(Row) andalso
+ (element(Col, Row) =:= ?'RowStatus_notReady') ->
+ case is_any_noinit(Row, Cols) of
+ false -> {noError, 0};
+ true -> {inconsistentValue, Col}
+ end;
+ undefined -> {inconsistentValue, Col};
+ _Else -> {noError, 0}
+ end;
+
+%% Try to createAndGo
+%% Ok if values are provided, or default values can be used for
+%% all columns.
+table_check_status(NameDb, Col, ?'RowStatus_createAndGo', RowIndex, Cols) ->
+ case table_row_exists(NameDb, RowIndex) of
+ false ->
+ % it's ok to use snmpa_local_db:table_construct_row since it's
+ % side effect free and we only use the result temporary.
+ case catch snmpa_local_db:table_construct_row(
+ NameDb, RowIndex, ?'RowStatus_createAndGo', Cols) of
+ {'EXIT', _} ->
+ {noCreation, Col}; % Bad RowIndex
+ Row ->
+ case lists:member(noinit, tuple_to_list(Row)) of
+ false -> {noError, 0};
+ _Found -> {inconsistentValue, Col}
+ end
+ end;
+ true -> {inconsistentValue, Col}
+ end;
+
+%% Try to createAndWait - ok if row doesn't exist.
+table_check_status(NameDb, Col, ?'RowStatus_createAndWait', RowIndex, Cols) ->
+ case table_row_exists(NameDb, RowIndex) of
+ false ->
+ case catch snmpa_local_db:table_construct_row(
+ NameDb, RowIndex, ?'RowStatus_createAndGo', Cols) of
+ {'EXIT', _} ->
+ {noCreation, Col}; % Bad RowIndex
+ _Row ->
+ {noError, 0}
+ end;
+ true -> {inconsistentValue, Col}
+ end;
+
+%% Try to destroy
+table_check_status(_NameDb, _Col, ?'RowStatus_destroy', _RowIndex, _Cols) ->
+ {noError, 0};
+
+%% Otherwise, notReady. It isn't possible to set a row to notReady.
+table_check_status(_NameDb, Col, _, _RowIndex, _Cols) ->
+ {inconsistentValue, Col}.
+
+is_any_noinit(Row, Cols) ->
+ is_any_noinit(tuple_to_list(Row), Cols, 1).
+is_any_noinit([noinit | Vals], [{N, _Value} | Cols], N) ->
+ is_any_noinit(Vals, Cols, N+1);
+is_any_noinit([noinit | _Vals], _Cols, _N) ->
+ true;
+is_any_noinit([_ | Vals], [{N, _Value} | Cols], N) ->
+ is_any_noinit(Vals, Cols, N+1);
+is_any_noinit([_ | Vals], Cols, N) ->
+ is_any_noinit(Vals, Cols, N+1);
+is_any_noinit([], _, _) ->
+ false.
+
+%%------------------------------------------------------------------
+%% Implements set.
+%% ChangedStatusFunc is a function that will be
+%% called if the rowstatus column is changed.
+%% The function is called *after* the row is created or
+%% otherwise modified, but *before* it is deleted.
+%% Arguments: (StatusVal, RowIndex, Cols)
+%% ConsFunc is a consistency-check function which will
+%% be called with the RowIndex of this row, if
+%% no operation on the row is made, when
+%% all columns are set, OR when row is createAndWait:ed.
+%% This is useful when the RowStatus
+%% could change, e.g. if the manager has provided all
+%% mandatory columns in this set operation.
+%% If it is nofunc, no function will be called after all
+%% sets.
+%%------------------------------------------------------------------
+table_set_row(_NameDb, _, _, _RowIndex, []) -> {noError, 0};
+table_set_row(NameDb, ChangedStatusFunc, ConsFunc, RowIndex, Cols) ->
+ #table_info{status_col = StatusCol} = table_info(NameDb),
+ case lists:keysearch(StatusCol, 1, Cols) of
+ {value, {StatusCol, Val}} ->
+ table_set_status(NameDb, RowIndex, Val, StatusCol,
+ Cols, ChangedStatusFunc, ConsFunc);
+ _ -> table_set_cols(NameDb, RowIndex, Cols, ConsFunc)
+ end.
+
+%%----------------------------------------------------------------------
+%% Mnesia overloads for performance reasons.
+%%----------------------------------------------------------------------
+table_set_status({Name, mnesia}, RowIndex, Status, StatusCol, Cols,
+ ChangedStatusFunc, ConsFunc) ->
+ snmp_generic_mnesia:table_set_status(Name, RowIndex,
+ Status, StatusCol, Cols,
+ ChangedStatusFunc, ConsFunc);
+
+table_set_status(NameDb,RowIndex, Status, StatusCol, Cols,
+ ChangedStatusFunc,ConsFunc) ->
+ snmpa_local_db:table_set_status(NameDb, RowIndex,
+ Status, StatusCol, Cols,
+ ChangedStatusFunc, ConsFunc).
+
+init_defaults(Defs, InitRow) ->
+ table_defaults(InitRow, Defs).
+init_defaults(Defs, InitRow, StartCol) ->
+ table_defaults(InitRow, StartCol, Defs).
+%%-----------------------------------------------------------------
+%% Get, from a list of Keys, the Keys defined in this table.
+%% (e.g. if INDEX { ifIndex, myOwnIndex }, the Keys is a list
+%% of two elements, and returned from this func is a list of
+%% the last of the two.)
+%%-----------------------------------------------------------------
+get_own_indexes(0, _Keys) -> [];
+get_own_indexes(1, Keys) -> Keys;
+get_own_indexes(Index, [_Key | Keys]) ->
+ get_own_indexes(Index - 1, Keys).
+
+%%-----------------------------------------------------------------
+%% Creates everything but the INDEX columns.
+%% Pre: The StatusColumn is present
+%% Four cases:
+%% 0) If a column is 'not-accessible' => use noacc
+%% 1) If no value is provided for the column and column is
+%% not StatusCol => use noinit
+%% 2) If column is not StatusCol, use the provided value
+%% 3) If column is StatusCol, use Status
+%%-----------------------------------------------------------------
+table_create_rest(Col, Max, _ , _ , [], _NoAcc) when Col > Max -> [];
+table_create_rest(Col,Max,StatusCol,Status,[{Col,_Val}|Defs],[Col|NoAccs]) ->
+ % case 0
+ [noacc | table_create_rest(Col+1, Max, StatusCol, Status, Defs, NoAccs)];
+table_create_rest(Col,Max,StatusCol,Status,Defs,[Col|NoAccs]) ->
+ % case 0
+ [noacc | table_create_rest(Col+1, Max, StatusCol, Status, Defs, NoAccs)];
+table_create_rest(StatCol, Max, StatCol, Status, [{_Col, _Val} |Defs], NoAccs) ->
+ % case 3
+ [Status | table_create_rest(StatCol+1, Max, StatCol, Status,Defs,NoAccs)];
+table_create_rest(Col, Max, StatusCol, Status, [{Col, Val} |Defs],NoAccs) ->
+ % case 2
+ [Val | table_create_rest(Col+1, Max, StatusCol, Status,Defs,NoAccs)];
+table_create_rest(StatCol, Max, StatCol, Status, Cols, NoAccs) ->
+ % case 3
+ [Status | table_create_rest(StatCol+1, Max, StatCol, Status, Cols, NoAccs)];
+table_create_rest(Col, Max, StatusCol, Status, Cols, NoAccs) when Col =< Max->
+ % case 1
+ [noinit | table_create_rest(Col+1, Max, StatusCol, Status, Cols, NoAccs)].
+
+%%------------------------------------------------------------------
+%% Sets default values to a row.
+%% InitRow is a list of values.
+%% Defs is a list of {Col, DefVal}, in Column order.
+%% Returns a new row (a list of values) with the same values as
+%% InitRow, except if InitRow has value noinit in a column, and
+%% the corresponing Col has a DefVal in Defs, then the DefVal
+%% will be the new value.
+%%------------------------------------------------------------------
+table_defaults(InitRow, Defs) -> table_defaults(InitRow, 1, Defs).
+
+table_defaults([], _, _Defs) -> [];
+table_defaults([noinit | T], CurIndex, [{CurIndex, DefVal} | Defs]) ->
+ [DefVal | table_defaults(T, CurIndex+1, Defs)];
+%% 'not-accessible' columns don't get a value
+table_defaults([noacc | T], CurIndex, [{CurIndex, _DefVal} | Defs]) ->
+ [noacc | table_defaults(T, CurIndex+1, Defs)];
+table_defaults([Val | T], CurIndex, [{CurIndex, _DefVal} | Defs]) ->
+ [Val | table_defaults(T, CurIndex+1, Defs)];
+table_defaults([Val | T], CurIndex, Defs) ->
+ [Val | table_defaults(T, CurIndex+1, Defs)].
+
+
+%%------------------------------------------------------------------
+%% table_set_cols/3,4
+%% can be used by the set procedure of all tables
+%% to set all columns in Cols, one at a time.
+%% ConsFunc is a check-consistency function, which will
+%% be called with the RowIndex of this row, when
+%% all columns are set. This is useful when the RowStatus
+%% could change, e.g. if the manager has provided all
+%% mandatory columns in this set operation.
+%% If ConsFunc is nofunc, no function will be called after all
+%% sets.
+%% Returns: {noError, 0} | {Error, Col}
+%%------------------------------------------------------------------
+%% mnesia uses its own for performance reasons.
+%% -----------------------------------------------------------------
+table_set_cols({Name,mnesia}, RowIndex, Cols, ConsFunc) ->
+ snmp_generic_mnesia:table_set_cols(Name, RowIndex,Cols,ConsFunc);
+table_set_cols(NameDb, RowIndex, Cols, ConsFunc) ->
+ case table_set_cols(NameDb, RowIndex, Cols) of
+ {noError, 0} -> try_apply(ConsFunc, [NameDb, RowIndex, Cols]);
+ Error -> Error
+ end.
+
+table_set_cols(_NameDb, _RowIndex, []) ->
+ {noError, 0};
+table_set_cols(NameDb, RowIndex, [{Col, Val} | Cols]) ->
+ case catch table_set_element(NameDb, RowIndex, Col, Val) of
+ true ->
+ table_set_cols(NameDb, RowIndex, Cols);
+ X ->
+ user_err("snmp_generic:table_set_cols set ~w to"
+ " ~w returned ~w",
+ [{NameDb, RowIndex}, {Col, Val}, X]),
+ {undoFailed, Col}
+ end.
+
+%%------------------------------------------------------------------
+%% This function splits RowIndex which is part
+%% of a OID, into a list of the indexes for the
+%% table. So a table with indexes {integer, octet string},
+%% and a RowIndex [4,3,5,6,7], will be split into
+%% [4, [5,6,7]].
+%%------------------------------------------------------------------
+split_index_to_keys(Indexes, RowIndex) ->
+ collect_keys(Indexes, RowIndex).
+
+collect_keys([#asn1_type{bertype = 'INTEGER'} | Indexes], [IntKey | Keys]) ->
+ [IntKey | collect_keys(Indexes, Keys)];
+collect_keys([#asn1_type{bertype = 'Unsigned32'} | Indexes], [IntKey | Keys]) ->
+ [IntKey | collect_keys(Indexes, Keys)];
+collect_keys([#asn1_type{bertype = 'Counter32'} | Indexes], [IntKey | Keys]) ->
+ %% Should we allow this - counter in INDEX is strange!
+ [IntKey | collect_keys(Indexes, Keys)];
+collect_keys([#asn1_type{bertype = 'TimeTicks'} | Indexes], [IntKey | Keys]) ->
+ %% Should we allow this - timeticks in INDEX is strange!
+ [IntKey | collect_keys(Indexes, Keys)];
+collect_keys([#asn1_type{bertype = 'IpAddress'} | Indexes],
+ [A, B, C, D | Keys]) ->
+ [[A, B, C, D] | collect_keys(Indexes, Keys)];
+%% Otherwise, check if it has constant size
+collect_keys([#asn1_type{lo = X, hi = X} | Indexes], Keys)
+ when is_integer(X) andalso (length(Keys) >= X) ->
+ {StrKey, Rest} = collect_length(X, Keys, []),
+ [StrKey | collect_keys(Indexes, Rest)];
+collect_keys([#asn1_type{lo = X, hi = X} | _Indexes], Keys)
+ when is_integer(X) ->
+ exit({error, {size_mismatch, X, Keys}});
+%% Otherwise, its a dynamic-length type => its a list
+%% OBJECT IDENTIFIER, OCTET STRING or BITS (or derivatives)
+%% Check if it is IMPLIED (only last element can be IMPLIED)
+collect_keys([#asn1_type{implied = true}], Keys) ->
+ [Keys];
+collect_keys([_Type | Indexes], [Length | Keys]) when length(Keys) >= Length ->
+ {StrKey, Rest} = collect_length(Length, Keys, []),
+ [StrKey | collect_keys(Indexes, Rest)];
+collect_keys([_Type | _Indexes], [Length | Keys]) ->
+ exit({error, {size_mismatch, Length, Keys}});
+collect_keys([], []) -> [];
+collect_keys([], Keys) ->
+ exit({error, {bad_keys, Keys}});
+collect_keys(_Any, Key) -> [Key].
+
+collect_length(0, Rest, Rts) ->
+ {lists:reverse(Rts), Rest};
+collect_length(N, [El | Rest], Rts) ->
+ collect_length(N-1, Rest, [El | Rts]).
+
+%%------------------------------------------------------------------
+%% Checks if a certain row exists.
+%% Returns true or false.
+%%------------------------------------------------------------------
+table_row_exists(NameDb, RowIndex) ->
+ case table_get_element(NameDb, RowIndex, 1) of
+ undefined -> false;
+ _ -> true
+ end.
+
+%%------------------------------------------------------------------
+%% table_find(NameDb, Col, Value)
+%% Finds a row (if one exists) in table NameDb
+%% with column Col equals to Value.
+%% Returns the RowIndex of the row, or false
+%% if no row exists.
+%%------------------------------------------------------------------
+table_find(NameDb, Col, Value) -> table_find(NameDb, Col, Value, []).
+table_find(NameDb, Col, Value, Indexes) ->
+ case table_next(NameDb, Indexes) of
+ endOfTable ->
+ false;
+ NewIndexes ->
+ case table_get_element(NameDb, NewIndexes, Col) of
+ {value, Value} -> NewIndexes;
+ _Else -> table_find(NameDb, Col, Value, NewIndexes)
+ end
+ end.
+
+
+%%------------------------------------------------------------------
+%% find_col(Col, Cols)
+%% undefined if a Col for column Col doesn't exist.
+%% {value, Val} if a Col for Col with value Val exists.
+%%------------------------------------------------------------------
+find_col(_Col, []) -> undefined;
+find_col(Col, [{Col, Val} | _T]) -> {value, Val};
+find_col(Col, [_H | T]) -> find_col(Col, T).
+
+%%------------------------------------------------------------------
+%% check_mandatory_cols(ListOfCols, Cols)
+%% {noError 0}if all columns in ListOfCols are present in Cols.
+%% {inconsistentValue 0} otherwise. (Index = 0. It's hard to tell
+%% which Col is wrong, when the problem is that one is missing!)
+%%------------------------------------------------------------------
+% check_mandatory_cols([], _) -> {noError, 0};
+% check_mandatory_cols(_, []) -> {inconsistentValue, 0};
+% check_mandatory_cols([Col | Cols], [{Col, Val} | T]) ->
+% check_mandatory_cols(Cols, T);
+% check_mandatory_cols([Col | Cols], [{Col2, Val} | T]) ->
+% check_mandatory_cols([Col | Cols], T).
+
+
+try_apply(nofunc, _) -> {noError, 0};
+try_apply(F, Args) -> apply(F, Args).
+
+table_info({Name, _Db}) ->
+ case snmpa_symbolic_store:table_info(Name) of
+ {value, TI} ->
+ TI;
+ false ->
+ error({table_not_found, Name})
+ end;
+table_info(Name) ->
+ case snmpa_symbolic_store:table_info(Name) of
+ {value, TI} ->
+ TI;
+ false ->
+ error({table_not_found, Name})
+ end.
+
+variable_info({Name, _Db}) ->
+ case snmpa_symbolic_store:variable_info(Name) of
+ {value, VI} ->
+ VI;
+ false ->
+ error({variable_not_found, Name})
+ end;
+variable_info(Name) ->
+ case snmpa_symbolic_store:variable_info(Name) of
+ {value, VI} ->
+ VI;
+ false ->
+ error({variable_not_found, Name})
+ end.
+
+
+%%------------------------------------------------------------------
+%% This function is a simple consistency check
+%% function which could be used by the user-defined
+%% table functions.
+%% Check if the row has all information needed to
+%% make row notInService (from notReady). This is
+%% a simple check, which just checks if some col
+%% in the row has the value 'noinit'.
+%% If it has the information, the status is changed
+%% to notInService.
+%%------------------------------------------------------------------
+table_try_make_consistent(Name, RowIndex, _Cols) ->
+ TableInfo = table_info(Name),
+ case TableInfo#table_info.status_col of
+ StatusCol when is_integer(StatusCol) ->
+ {value, StatusVal} = table_get_element(Name, RowIndex, StatusCol),
+ table_try_make_consistent(Name, RowIndex, StatusVal, TableInfo);
+ _ ->
+ {noError, 0}
+ end.
+
+table_try_make_consistent(Name, RowIndex, ?'RowStatus_notReady', TableInfo) ->
+ %% this *should* be a generic function,
+ %% but since mnesia got its own try_mk_cons
+ %% and I don't have time to impl table_get_row
+ %% for mnesia I call snmpa_local_db:
+ Row = snmpa_local_db:table_get_row(Name, RowIndex),
+ case lists:member(noinit, tuple_to_list(Row)) of
+ true -> {noError, 0};
+ false ->
+ case catch table_set_element(Name, RowIndex,
+ TableInfo#table_info.status_col,
+ ?'RowStatus_notInService') of
+ true -> {noError, 0};
+ X ->
+ user_err("snmp_generic:table_try_make_consistent "
+ "set ~w to notInService returned ~w",
+ [{Name, RowIndex}, X]),
+ {commitFailed, TableInfo#table_info.status_col}
+ end
+ end;
+
+table_try_make_consistent(_Name, _RowIndex, _StatusVal, _TableInfo) ->
+ {noError, 0}.
+
+table_get_row({Name, mnesia}, RowIndex) ->
+ snmp_generic_mnesia:table_get_row(Name, RowIndex);
+table_get_row(NameDb, RowIndex) ->
+ snmpa_local_db:table_get_row(NameDb, RowIndex).
+
+table_get_row(NameDb, RowIndex, undefined) ->
+ table_get_row(NameDb, RowIndex);
+table_get_row({Name, mnesia}, RowIndex, FOI) ->
+ snmp_generic_mnesia:table_get_row(Name, RowIndex, FOI);
+table_get_row(NameDb, RowIndex, _FOI) ->
+ snmpa_local_db:table_get_row(NameDb, RowIndex).
+
+
+%%-----------------------------------------------------------------
+%% Purpose: These functions can be used by the user's instrum func
+%% to retrieve various table info.
+%%-----------------------------------------------------------------
+
+%%-----------------------------------------------------------------
+%% Description:
+%% Used by user's instrum func to check if mstatus column is
+%% modified.
+%%-----------------------------------------------------------------
+get_status_col(Name, Cols) ->
+ #table_info{status_col = StatusCol} = table_info(Name),
+ case lists:keysearch(StatusCol, 1, Cols) of
+ {value, {StatusCol, Val}} -> {ok, Val};
+ _ -> false
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Description:
+%% Used by user's instrum func to get the table info. Specific parts
+%% or all of it. If all is selected then the result will be a tagged
+%% list of values.
+%%-----------------------------------------------------------------
+get_table_info(Name, nbr_of_cols) ->
+ get_nbr_of_cols(Name);
+get_table_info(Name, defvals) ->
+ get_defvals(Name);
+get_table_info(Name, status_col) ->
+ get_status_col(Name);
+get_table_info(Name, not_accessible) ->
+ get_not_accessible(Name);
+get_table_info(Name, index_types) ->
+ get_index_types(Name);
+get_table_info(Name, first_accessible) ->
+ get_first_accessible(Name);
+get_table_info(Name, first_own_index) ->
+ get_first_own_index(Name);
+get_table_info(Name, all) ->
+ TableInfo = table_info(Name),
+ [{nbr_of_cols, TableInfo#table_info.nbr_of_cols},
+ {defvals, TableInfo#table_info.defvals},
+ {status_col, TableInfo#table_info.status_col},
+ {not_accessible, TableInfo#table_info.not_accessible},
+ {index_types, TableInfo#table_info.index_types},
+ {first_accessible, TableInfo#table_info.first_accessible},
+ {first_own_index, TableInfo#table_info.first_own_index}].
+
+
+%%-----------------------------------------------------------------
+%% Description:
+%% Used by user's instrum func to get the index types.
+%%-----------------------------------------------------------------
+get_index_types(Name) ->
+ #table_info{index_types = IndexTypes} = table_info(Name),
+ IndexTypes.
+
+get_nbr_of_cols(Name) ->
+ #table_info{nbr_of_cols = NumberOfCols} = table_info(Name),
+ NumberOfCols.
+
+get_defvals(Name) ->
+ #table_info{defvals = DefVals} = table_info(Name),
+ DefVals.
+
+get_status_col(Name) ->
+ #table_info{status_col = StatusCol} = table_info(Name),
+ StatusCol.
+
+get_not_accessible(Name) ->
+ #table_info{not_accessible = NotAcc} = table_info(Name),
+ NotAcc.
+
+get_first_accessible(Name) ->
+ #table_info{first_accessible = FirstAcc} = table_info(Name),
+ FirstAcc.
+
+get_first_own_index(Name) ->
+ #table_info{first_own_index = FirstOwnIdx} = table_info(Name),
+ FirstOwnIdx.
+
+
+%%-----------------------------------------------------------------
+
+error(Reason) ->
+ throw({error, Reason}).
+
+user_err(F, A) ->
+ snmpa_error:user_err(F, A).