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