1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(snmpa_mib_data_ttln).
22
23%%%-----------------------------------------------------------------
24%%%
25%%%           THIS FILE IS JUST A PLACE HOLDER - IGNORE
26%%%
27%%%-----------------------------------------------------------------
28
29
30%%%-----------------------------------------------------------------
31%%%
32%%%        TTLN - TupleTreeListNodes
33%%%
34%%% This module implements the MIB internal data structures.
35%%% An MIB Data Structure consists of three items; an ets-table,
36%%% a tree and a list of registered subagents.
37%%% The subagent information is consequently duplicated. It resides
38%%% both in the tree and in the list.
39%%% The ets-table contains all data associated with each variable,
40%%% table, tableentry and tablecolumn in the MIB.
41%%% The tree contains information of the Oids in the MIB.
42%%%
43%%% When a mib is loaded, the tree is built from the plain list
44%%% in the binary file.
45%%%
46%%%-----------------------------------------------------------------
47
48-include("snmp_types.hrl").
49-include("snmp_debug.hrl").
50
51-define(VMODULE,"MDATA_TTLN").
52-include("snmp_verbosity.hrl").
53
54-behaviour(snmpa_mib_data).
55
56-define(MIB_DATA, snmpa_mib_data).
57-define(MIB_NODE, snmpa_mib_node).
58-define(MIB_TREE, snmpa_mib_tree).
59-define(DUMMY_TREE_GENERATION, 1).
60-define(DEFAULT_TREE, {tree,{undefined_node},internal}).
61
62
63%%%-----------------------------------------------------------------
64%%% Table of contents
65%%% =================
66%%% 1. Interface
67%%% 2. Implementation of tree access
68%%% 3. Tree building functions
69%%% 4. Tree merging
70%%% 5. Tree deletion routines
71%%% 6. Functions for subagent handling
72%%% 7. Misc functions
73%%%-----------------------------------------------------------------
74
75
76%%----------------------------------------------------------------------
77%% data_db is an database containing loaded mibs as:
78%%    {MibName = atom(), Symbolic = ?, FullFileName = string()}
79%%    it is either ets or mnesia
80%% tree_db is a database containing _one_ record with the tree!
81%% (the reason for this is part to get replication and part out of convenience)
82%% ref_tree is the root node, without any subagent.
83%% tree is the root node (same as ref_tree but with the subagents added).
84%% subagents is a list of {SAPid, Oid}
85%%----------------------------------------------------------------------
86-record(mib_data, {mib_db,  % table of #mib_info
87		   node_db, % table of #node_info
88		   tree_db, % table of #tree
89		   tree,    % The actual tree
90		   subagents = []}).
91
92-record(mib_info,  {name, symbolic, file_name}).
93-record(node_info, {oid, mib_name, me}).
94
95
96%% API
97-export([new/0, new/1, sync/1, close/1,
98	 load_mib/4, unload_mib/4, which_mibs/1, whereis_mib/2,
99	 info/1, info/2,
100	 dump/1, dump/2,
101	 backup/2,
102	 lookup/2, next/3, which_mib/2,
103	 register_subagent/3, unregister_subagent/2]).
104
105%% Internal exports
106-export([code_change/2]).
107
108
109%%-----------------------------------------------------------------
110%% A tree is represented as a N-tuple, where each element is a
111%% node. A node is:
112%% 1) {tree, Tree, Info} where Info can be {table, Id}, {table_entry, Id}
113%%                                        or perhaps 'internal'
114%% 2) undefined_node  (memory optimization (instead of {node, undefined}))
115%% 3) {node, Info} where Info can be {subagent, Pid}, {variable, Id},
116%%                                   {table_column, Id}
117%% Id is {MibName, MibEntry}
118%% The over all root is represented as {tree, Tree, internal}.
119%%
120%% tree() = {tree, nodes(), tree_info()}
121%% nodes() = [tree() | node() | undefined_node]
122%% node() = {node, node_info()}
123%% tree_info() = {table, Id} | {table_entry, Id} | internal
124%% node_info() = {subagent, Pid} | {variable, Id} | {table_colum, Id}
125%%-----------------------------------------------------------------
126
127-type tree_generation() :: non_neg_integer().
128-type tree()            :: #tree{}.
129-type tree_nodes()      :: [tree_node()].
130-type tree_node()       :: tree() |
131			   tree_node_elem() |
132			   tree_node_empty().
133-type tree_node_elem()  :: {node, tree_node_info()}.
134-type tree_node_info()  :: {subagent,     Pid :: pid()} |
135			   {variable,     Id  :: non_neg_integer()} |
136			   {table_column, Id  :: non_neg_integer()}.
137-type tree_node_empty() :: {undefined_node, N :: pos_integer()}.
138-type tree_info()       :: {table,       Id :: non_neg_integer()} |
139			   {table_entry, Id :: non_neg_integer()} |
140			   internal.
141
142
143%% This record is what is stored in the database. The 'tree' part
144%% is described above...
145-record(mtree,
146	{
147	  generation = ?DUMMY_TREE_GENERATION :: tree_generation(),
148	  root       = ?DEFAULT_TREE          :: tree()
149	}).
150
151-record(tree,
152	{
153	  %% The number of nodes is *not* actually the length of the
154	  %% nodes list. Since the undefined-node(s) can be collapsed
155	  %% into {undefined_node, N} we need to keep track of the
156	  %% actual size some other way (so that we dont have the
157	  %% traverse the nodes every time we want to check an index).
158	  num_nodes :: non_neg_integer(),
159	  nodes     :: tree_nodes(),
160	  tree_info :: tree_info()
161	}).
162
163
164
165
166%%%======================================================================
167%%% 1. Interface
168%%%======================================================================
169
170%%-----------------------------------------------------------------
171%% Func: new/0, new/1
172%% Returns: A representation of mib data.
173%%-----------------------------------------------------------------
174new() ->
175    new(ets).
176
177%% Where -> A list of nodes where the tables will be created
178new(Storage) ->
179    %% First we must check if there is already something to read
180    %% If a database already exists, then the tree structure has to be read
181    ?vtrace("open (mib) database",[]),
182    MibDb = snmpa_general_db:open(Storage, ?MIB_DATA,
183				  mib_info,
184				  record_info(fields, mib_info), set),
185    ?vtrace("open (mib) node database",[]),
186    NodeDb = snmpa_general_db:open(Storage, ?MIB_NODE,
187				   node_info,
188				   record_info(fields, node_info), set),
189    ?vtrace("open (mib) tree database",[]),
190    TreeDb = snmpa_general_db:open(Storage, ?MIB_TREE,
191				   tree,
192				   record_info(fields, mtree), set),
193    MTree =
194	case snmpa_general_db:read(TreeDb, ?DUMMY_TREE_GENERATION) of
195	    false ->
196		T = #mtree{},
197		snmpa_general_db:write(TreeDb, T),
198		T;
199	    {value, T} ->
200		T
201	end,
202    install_mibs(MibDb, NodeDb),
203    #mib_data{mib_db   = MibDb,
204	      node_db  = NodeDb,
205	      tree_db  = TreeDb,
206	      mtree    = MTree}.
207
208
209%%----------------------------------------------------------------------
210%% Returns: new mib data | {error, Reason}
211%%----------------------------------------------------------------------
212load_mib(MibData,FileName,MeOverride,TeOverride)
213  when is_record(MibData,mib_data) andalso is_list(FileName) ->
214    ?vlog("load mib file: ~p",[FileName]),
215    ActualFileName = filename:rootname(FileName, ".bin") ++ ".bin",
216    MibName = list_to_atom(filename:basename(FileName, ".bin")),
217    (catch do_load_mib(MibData, ActualFileName, MibName,
218                       MeOverride, TeOverride)).
219
220do_load_mib(MibData, ActualFileName, MibName, MeOverride, TeOverride) ->
221    ?vtrace("do_load_mib -> entry with"
222        "~n  ActualFileName: ~s"
223        "~n  MibName:        ~p",[ActualFileName, MibName]),
224    #mib_data{mib_db  = MibDb,
225	      node_db = NodeDb,
226	      %% tree_db = TreeDb,
227	      tree    = Tree} = MibData,
228    verify_not_loaded(MibDb, MibName),
229    ?vtrace("do_load_mib -> already loaded mibs:"
230	"~n   ~p",[loaded(MibDb)]),
231    Mib = do_read_mib(ActualFileName),
232    ?vtrace("do_load_mib -> read mib ~s",[Mib#mib.name]),
233    NonInternalMes =
234        lists:filter(fun(ME) -> maybe_drop_me(ME) end, Mib#mib.mes),
235    OldRoot = Tree#tree.root,
236    T = build_tree(NonInternalMes, MibName),
237    ?d("load_mib -> "
238	"~n   OldRoot: ~p"
239	"~n   T:       ~p", [OldRoot, T]),
240    case (catch merge_nodes(T, OldRoot)) of
241	{error_merge_nodes, Node1, Node2} ->
242	    ?vlog("error merging nodes:"
243		"~n~p~nand~n~p", [Node1,Node2]),
244	    {error, oid_conflict};
245	NewRoot when is_tuple(NewRoot) andalso (element(1,NewRoot) =:= tree) ->
246 	    ?d("load_mib -> "
247 		"~n   NewRoot: ~p", [NewRoot]),
248	    Symbolic = not lists:member(no_symbolic_info, Mib#mib.misc),
249	    case (catch check_notif_and_mes(TeOverride, MeOverride, Symbolic,
250					    Mib#mib.traps, NonInternalMes)) of
251		true ->
252		    install_mes(NodeDb, MibName, NonInternalMes),
253		    install_mib(MibDb, Symbolic, Mib,
254				MibName, ActualFileName, NonInternalMes),
255		    ?vtrace("installed mib ~s", [Mib#mib.name]),
256		    Tree2 = Tree#tree{root = NewRoot},
257		    %% snmpa_general_db:write(TreeDb, Tree2), %% Store later?
258		    {ok, MibData#mib_data{tree = Tree2}};
259		Else ->
260		    Else
261	    end
262    end.
263
264
265verify_not_loaded(Db, Name) ->
266    case snmpa_general_db:read(Db, Name) of
267        {value, #mib_info{name = Name}} ->
268            throw({error, 'already loaded'});
269        false ->
270            ok
271    end.
272
273do_read_mib(ActualFileName) ->
274    case snmp_misc:read_mib(ActualFileName) of
275        {error, Reason} ->
276            ?vlog("Failed reading mib file ~p with reason: ~p",
277                [ActualFileName,Reason]),
278            throw({error, Reason});
279        {ok, Mib} ->
280            Mib
281    end.
282
283%% The Tree DB is handled in a special way since it can be very large.
284sync(#mib_data{mib_db  = M,
285	       node_db = N,
286	       tree_db = T, tree = Tree, subagents = []}) ->
287    snmpa_general_db:sync(M),
288    snmpa_general_db:sync(N),
289    snmpa_general_db:write(T, Tree),
290    snmpa_general_db:sync(T);
291sync(#mib_data{mib_db  = M,
292	       node_db = N,
293	       tree_db = T, tree = Tree, subagents = SAs}) ->
294
295    snmpa_general_db:sync(M),
296    snmpa_general_db:sync(N),
297
298    %% Ouch. Since the subagent info is dynamic we do not
299    %% want to store the tree containing subagent info. So, we
300    %% have to create a tmp tree without those and store it.
301
302    case delete_subagents(Tree, SAs) of
303	{ok, TreeWithoutSAs} ->
304	    snmpa_general_db:write(T, TreeWithoutSAs),
305	    snmpa_general_db:sync(T);
306	Error ->
307	    Error
308    end.
309
310delete_subagents(Tree, []) ->
311    {ok, Tree};
312delete_subagents(Tree0, [{_, Oid}|SAs]) ->
313    case (catch delete_subagent(Tree0, Oid)) of
314	{tree, _Tree, _Info} = Tree1 ->
315	    delete_subagents(Tree1, SAs);
316	_Error ->
317	    {error, {'invalid oid', Oid}}
318    end.
319
320%%----------------------------------------------------------------------
321%% (OTP-3601)
322%%----------------------------------------------------------------------
323check_notif_and_mes(TeOverride,MeOverride,Symbolic,Traps,MEs) ->
324    ?vtrace("check notifications and mib entries",[]),
325    check_notifications(TeOverride,Symbolic,Traps),
326    check_mes(MeOverride,MEs).
327
328check_notifications(true, _Symbolic, _Traps) ->
329    ?vtrace("trapentry override = true => skip check",[]),
330    true;
331check_notifications(_, Symbolic, Traps) ->
332    check_notifications(Symbolic, Traps).
333
334check_notifications(true, Traps) ->
335    check_notifications(Traps);
336check_notifications(_, _) -> true.
337
338check_notifications([]) -> true;
339check_notifications([#trap{trapname = Key} = Trap | Traps]) ->
340    ?vtrace("check notification [trap] with Key: ~p",[Key]),
341    case snmpa_symbolic_store:get_notification(Key) of
342	{value, Trap} -> check_notifications(Traps);
343	{value,    _} -> throw({error, {'trap already defined', Key}});
344	undefined     -> check_notifications(Traps)
345    end;
346check_notifications([#notification{trapname = Key} = Notif | Traps]) ->
347    ?vtrace("check notification [notification] with Key: ~p",[Key]),
348    case snmpa_symbolic_store:get_notification(Key) of
349	{value, Notif} ->
350	    check_notifications(Traps);
351	{value,     _} ->
352	    throw({error, {'notification already defined', Key}});
353	undefined      ->
354	    check_notifications(Traps)
355    end;
356check_notifications([Crap | Traps]) ->
357    ?vlog("skipped check of: ~n~p",[Crap]),
358    check_notifications(Traps).
359
360check_mes(true,_) ->
361    ?vtrace("mibentry override = true => skip check",[]),
362    true;
363check_mes(_,MEs) ->
364    check_mes(MEs).
365
366check_mes([]) -> true;
367check_mes([#me{aliasname = Name, oid = Oid1} | MEs]) ->
368    ?vtrace("check mib entries with aliasname: ~p",[Name]),
369    case snmpa_symbolic_store:aliasname_to_oid(Name) of
370	{value, Oid1} ->
371	    check_mes(MEs);
372	{value, Oid2} ->
373	    ?vinfo("~n   expecting '~p'~n   but found '~p'",[Oid1, Oid2]),
374	    throw({error, {'mibentry already defined', Name}});
375	false ->
376	    check_mes(MEs)
377    end;
378check_mes([Crap | MEs]) ->
379    ?vlog("skipped check of: ~n~p",[Crap]),
380    check_mes(MEs).
381
382
383
384%%----------------------------------------------------------------------
385%% Returns: new mib data | {error, Reason}
386%%----------------------------------------------------------------------
387unload_mib(MibData, FileName, _, _) when is_list(FileName) ->
388    MibName = list_to_atom(filename:basename(FileName, ".bin")),
389    (catch do_unload_mib(MibData, MibName)).
390
391do_unload_mib(MibData, MibName) ->
392    ?vtrace("do_unload_mib -> entry with"
393	"~n   MibName: ~p", [MibName]),
394    #mib_data{mib_db  = MibDb,
395	      node_db = NodeDb,
396	      %% tree_db = TreeDb,
397	      tree    = Tree} = MibData,
398    #mib_info{symbolic = Symbolic} = verify_loaded(MibDb, MibName),
399    NewRoot = delete_mib_from_tree(MibName, Tree#tree.root),
400    MEs = uninstall_mes(NodeDb, MibName),
401    uninstall_mib(MibDb, Symbolic, MibName, MEs),
402    NewMibData = MibData#mib_data{tree = Tree#tree{root = NewRoot}},
403    {ok, NewMibData}.
404
405verify_loaded(Db, Name) ->
406    case snmpa_general_db:read(Db, Name) of
407        {value, MibInfo} ->
408            MibInfo;
409        false ->
410            throw({error, 'not loaded'})
411    end.
412
413
414close(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb}) ->
415    snmpa_general_db:close(MibDb),
416    snmpa_general_db:close(NodeDb),
417    snmpa_general_db:close(TreeDb),
418    ok.
419
420register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) ->
421    case insert_subagent(Oid, T#tree.root) of
422	{error, Reason} ->
423	    {error, Reason};
424	NewRootTree ->
425	    SAs = [{Pid, Oid} | MibData#mib_data.subagents],
426	    T2 = T#tree{root = NewRootTree},
427	    MibData#mib_data{tree = T2, subagents = SAs}
428    end.
429
430
431%%----------------------------------------------------------------------
432%% Purpose: Get a list of all loaded mibs
433%% Returns: [{Name, File}]
434%%----------------------------------------------------------------------
435
436which_mibs(#mib_data{mib_db = Db}) ->
437    Mibs = snmpa_general_db:tab2list(Db),
438    [{Name, File} || #mib_info{name = Name, file_name = File} <- Mibs].
439
440
441%%----------------------------------------------------------------------
442%% Purpose: Get a list of all loaded mibs
443%% Returns: [{Name, File}]
444%%----------------------------------------------------------------------
445
446whereis_mib(#mib_data{mib_db = Db}, Name) ->
447    case snmpa_general_db:read(Db, Name) of
448        {value, #mib_info{file_name = File}} ->
449	    {ok, File};
450	false ->
451	    {error, not_found}
452    end.
453
454
455%%----------------------------------------------------------------------
456%% Purpose: Deletes SA with Pid from all subtrees it handles.
457%% Returns: NewMibData.
458%%----------------------------------------------------------------------
459unregister_subagent(MibData, Pid) when is_pid(Pid) ->
460    SAs = MibData#mib_data.subagents,
461    case lists:keysearch(Pid, 1, SAs) of
462	false -> MibData;
463	{value, {Pid, Oid}} ->
464	    % we should never get an error since Oid is found in MibData.
465	    {ok, NewMibData, _DeletedSA} = unregister_subagent(MibData, Oid),
466	    % continue if the same Pid handles other mib subtrees.
467	    unregister_subagent(NewMibData, Pid)
468    end;
469
470%%----------------------------------------------------------------------
471%% Purpose: Deletes one unique subagent.
472%% Returns: {error, Reason} | {ok, NewMibData, DeletedSubagentPid}
473%%----------------------------------------------------------------------
474unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) ->
475    case catch delete_subagent(T#tree.root, Oid) of
476	{tree, Tree, Info} ->
477	    OldSAs = MibData#mib_data.subagents,
478	    {value, {Pid, _Oid}} = lists:keysearch(Oid, 2, OldSAs),
479	    SAs = lists:keydelete(Oid, 2, OldSAs),
480	    T2 = T#tree{root = {tree, Tree, Info}},
481	    {ok,
482	     MibData#mib_data{tree = T2, subagents = SAs},
483	     Pid};
484	_ ->
485	    {error, {'invalid oid', Oid}}
486    end.
487
488%%----------------------------------------------------------------------
489%% Purpose: To inpect memory usage, loaded mibs, registered subagents
490%%----------------------------------------------------------------------
491info(MibData) ->
492    ?vtrace("retrieve info",[]),
493    #mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb,
494	      tree = Tree, subagents = SAs} = MibData,
495    LoadedMibs = old_format(snmpa_general_db:tab2list(MibDb)),
496    TreeSize = snmp_misc:mem_size(Tree),
497    {memory, ProcSize} = erlang:process_info(self(),memory),
498    MibDbSize  = snmpa_general_db:info(MibDb,  memory),
499    NodeDbSize = snmpa_general_db:info(NodeDb, memory),
500    TreeDbSize = snmpa_general_db:info(TreeDb, memory),
501    [{loaded_mibs, LoadedMibs}, {subagents, SAs}, {tree_size_bytes, TreeSize},
502     {process_memory, ProcSize},
503     {db_memory, [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}]}].
504
505info(#mib_data{mib_db = MibDb}, loaded_mibs) ->
506    Mibs = snmpa_general_db:tab2list(MibDb),
507    [filename:rootname(FN, ".bin") || #mib_info{file_name = FN} <- Mibs];
508info(#mib_data{tree = Tree}, tree_size_bytes) ->
509    snmp_misc:mem_size(Tree);
510info(_, process_memory) ->
511    {memory, ProcSize} = erlang:process_info(self(),memory),
512    ProcSize;
513info(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb},
514     db_memory) ->
515    MibDbSize  = snmpa_general_db:info(MibDb,  memory),
516    NodeDbSize = snmpa_general_db:info(NodeDb, memory),
517    TreeDbSize = snmpa_general_db:info(TreeDb, memory),
518    [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}];
519info(#mib_data{subagents = SAs}, subagents) ->
520    SAs.
521
522old_format(LoadedMibs) ->
523    ?vtrace("convert mib info to old format",[]),
524    [{N,S,F} || #mib_info{name=N,symbolic=S,file_name=F} <- LoadedMibs].
525
526
527%%----------------------------------------------------------------------
528%% A total dump for debugging.
529%%----------------------------------------------------------------------
530dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}) ->
531    (catch io:format("MIB-tables:~n~p~n~n",
532		     [snmpa_general_db:tab2list(MibDb)])),
533    (catch io:format("MIB-entries:~n~p~n~n",
534		     [snmpa_general_db:tab2list(NodeDb)])),
535    (catch io:format("Tree:~n~p~n", [Tree])), % good luck reading it!
536    ok.
537
538dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, File) ->
539    case file:open(File,[write]) of
540	{ok, Fd} ->
541	    io:format(Fd,"~s~n",
542		      [snmp:date_and_time_to_string(snmp:date_and_time())]),
543	    (catch io:format(Fd,"MIB-tables:~n~p~n~n",
544			     [snmpa_general_db:tab2list(MibDb)])),
545	    (catch io:format(Fd, "MIB-entries:~n~p~n~n",
546			     [snmpa_general_db:tab2list(NodeDb)])),
547	    io:format(Fd,"Tree:~n~p~n", [Tree]), % good luck reading it!
548	    file:close(Fd),
549	    ok;
550	{error,Reason} ->
551	    ?vinfo("~n   Failed opening file '~s' for reason ~p",
552		   [File,Reason]),
553	    {error,Reason}
554    end.
555
556
557backup(#mib_data{mib_db = M, node_db = N, tree_db = T}, BackupDir) ->
558    MRes = snmpa_general_db:backup(M, BackupDir),
559    NRes = snmpa_general_db:backup(N, BackupDir),
560    TRes = snmpa_general_db:backup(T, BackupDir),
561    handle_backup_res([{mib_db, MRes}, {node_db, NRes}, {tree_db, TRes}]).
562
563handle_backup_res(Res) ->
564    handle_backup_res(Res, []).
565
566handle_backup_res([], []) ->
567    ok;
568handle_backup_res([], Err) ->
569    {error, lists:reverse(Err)};
570handle_backup_res([{_, ok}|Res], Err) ->
571    handle_backup_res(Res, Err);
572handle_backup_res([{Tag, {error, Reason}}|Res], Err) ->
573    handle_backup_res(Res, [{Tag, Reason}|Err]);
574handle_backup_res([{Tag, Error}|Res], Err) ->
575    handle_backup_res(Res, [{Tag, Error}|Err]).
576
577
578%%%======================================================================
579%%% 2. Implementation of tree access
580%%%    lookup and next.
581%%%======================================================================
582
583
584which_mib(#mib_data{tree = T} = D, Oid) ->
585    ?vtrace("which_mib -> entry with"
586	    "~n   Oid: ~p",[Oid]),
587    case (catch find_node(D, T#tree.root, Oid, [])) of
588	{variable, _ME, Mib} ->
589	    ?vtrace("which_mib -> variable:"
590		"~n   Mib: ~p", [Mib]),
591	    {ok, Mib};
592	{table, _EntryME, _, Mib} ->
593	    ?vtrace("which_mib -> table:"
594		"~n   Mib: ~p", [Mib]),
595	    {ok, Mib};
596	{subagent, SubAgentPid, _SANextOid} ->
597	    ?vtrace("which_mib -> subagent:"
598		"~n   SubAgentPid: ~p", [SubAgentPid]),
599	    {error, {subagent, SubAgentPid}};
600	{false, ErrorCode} ->
601	    ?vtrace("which_mib -> false:"
602		"~n   ErrorCode: ~p",[ErrorCode]),
603	    {error, ErrorCode};
604	false ->
605	    ?vtrace("which_mib -> false",[]),
606	    {error, noSuchObject};
607	{'EXIT', R} ->
608	    ?vtrace("which_mib -> exit:"
609		"~n   R:  ~p",[R]),
610	    {error, noSuchObject}
611    end.
612
613
614%%-----------------------------------------------------------------
615%% Func: lookup/2
616%% Purpose: Finds the mib entry corresponding to the Oid. If it is a
617%%          variable, the Oid must be <Oid for var>.0 and if it is
618%%          a table, Oid must be <table>.<entry>.<col>.<any>
619%% Returns: {variable, MibEntry} |
620%%          {table_column, MibEntry, TableEntryOid} |
621%%          {subagent, SubAgentPid, SAOid} |
622%%          {false, Reason}
623%%-----------------------------------------------------------------
624lookup(#mib_data{tree = T} = D, Oid) ->
625     ?vtrace("lookup -> entry with"
626 	"~n   Oid: ~p",[Oid]),
627    case (catch find_node(D, T#tree.root, Oid, [])) of
628	{variable, ME, _Mib} when is_record(ME, me) ->
629	    ?vtrace("lookup -> variable:"
630		"~n   ME: ~p",[ME]),
631	    {variable, ME};
632	{table, EntryME, {ColME, TableEntryOid}, _Mib} ->
633	    ?vtrace("lookup -> table:"
634		"~n   EntryME:          ~p"
635		"~n   ColME:            ~p"
636		"~n   RevTableEntryOid: ~p",
637		[EntryME, ColME, TableEntryOid]),
638	    MFA = EntryME#me.mfa,
639	    RetME = ColME#me{mfa = MFA},
640	    {table_column, RetME, TableEntryOid};
641	{subagent, SubAgentPid, SANextOid} ->
642	    ?vtrace("lookup -> subagent:"
643		"~n   SubAgentPid: ~p"
644		"~n   SANextOid:   ~p", [SubAgentPid, SANextOid]),
645	    {subagent, SubAgentPid, SANextOid};
646	{false, ErrorCode} ->
647	    ?vtrace("lookup -> false:"
648		"~n   ErrorCode: ~p",[ErrorCode]),
649	    {false, ErrorCode};
650	false ->
651	    ?vtrace("lookup -> false",[]),
652	    {false, noSuchObject};
653	{'EXIT', R} ->
654	    ?vtrace("lookup -> exit:"
655		"~n   R:  ~p",[R]),
656	    {false, noSuchObject}
657    end.
658
659
660find_node(D, {tree, Tree, {table, _}}, RestOfOid, RevOid) ->
661     ?vtrace("find_node(tree,table) -> entry with"
662 	"~n   RestOfOid: ~p"
663 	"~n   RevOid:    ~p",[RestOfOid, RevOid]),
664    find_node(D, {tree, Tree, internal}, RestOfOid, RevOid);
665find_node(D, {tree, Tree, {table_entry, _}}, RestOfOid, RevOid) ->
666     ?vtrace("find_node(tree,table_entry) -> entry with"
667 	"~n   RestOfOid: ~p"
668 	"~n   RevOid:    ~p",[RestOfOid, RevOid]),
669    #mib_data{node_db = Db} = D,
670    Oid = lists:reverse(RevOid),
671    case snmpa_general_db:read(Db, Oid) of
672	{value, #node_info{me = ME, mib_name = Mib}} ->
673	    case find_node(D, {tree, Tree, internal}, RestOfOid, RevOid) of
674		{false, ErrorCode} -> {false, ErrorCode};
675		Val -> {table, ME, Val, Mib}
676	    end;
677	false ->
678	    ?vinfo("find_node -> could not find table_entry ME with"
679		"~n   RevOid:    ~p"
680		"~n   when"
681		"~n   RestOfOid: ~p",
682		[RevOid, RestOfOid]),
683	    false
684    end;
685find_node(D, {tree, Tree, _Internal}, [Int | RestOfOid], RevOid) ->
686    ?vtrace("find_node(tree) -> entry with"
687	"~n   Int:       ~p"
688	"~n   RestOfOid: ~p"
689	"~n   RevOid:    ~p",[Int, RestOfOid, RevOid]),
690    find_node(D, element(Int+1, Tree), RestOfOid, [Int | RevOid]);
691find_node(D, {node, {table_column, _}}, RestOfOid, [ColInt | RevOid]) ->
692     ?vtrace("find_node(tree,table_column) -> entry with"
693 	"~n   RestOfOid: ~p"
694 	"~n   ColInt:    ~p"
695 	"~n   RevOid:    ~p",[RestOfOid, ColInt, RevOid]),
696    #mib_data{node_db = Db} = D,
697    Oid = lists:reverse([ColInt | RevOid]),
698    case snmpa_general_db:read(Db, Oid) of
699	{value, #node_info{me = ME}} ->
700	    {ME, lists:reverse(RevOid)};
701	false ->
702	    X = snmpa_general_db:read(Db, lists:reverse([ColInt | RevOid])),
703	    ?vinfo("find_node -> could not find table_column ME with"
704		"~n   RevOid: ~p"
705		"~n   trying  [~p|~p]"
706		"~n   X:      ~p",
707		[RevOid, [ColInt | RevOid], X]),
708	    false
709    end;
710find_node(D, {node, {variable, _MibName}}, [0], RevOid) ->
711     ?vtrace("find_node(tree,variable,[0]) -> entry with"
712 	"~n   RevOid:    ~p",[RevOid]),
713    #mib_data{node_db = Db} = D,
714    Oid = lists:reverse(RevOid),
715    %% {value, #node_info{me = ME}} = snmpa_general_db:read(Db, Oid),
716    case snmpa_general_db:read(Db, Oid) of
717	{value, #node_info{me = ME, mib_name = Mib}} ->
718	    {variable, ME, Mib};
719	false ->
720	    ?vinfo("find_node -> could not find variable ME with"
721		"~n   RevOid: ~p", [RevOid]),
722	    false
723    end;
724find_node(_D, {node, {variable, _MibName}}, [], _RevOid) ->
725    ?vtrace("find_node(tree,variable,[]) -> entry",[]),
726    {false, noSuchObject};
727find_node(_D, {node, {variable, _MibName}}, _, _RevOid) ->
728    ?vtrace("find_node(tree,variable) -> entry",[]),
729    {false, noSuchInstance};
730find_node(D, {node, subagent}, _RestOfOid, SARevOid) ->
731    ?vtrace("find_node(tree,subagent) -> entry with"
732 	"~n   SARevOid:    ~p",[SARevOid]),
733    #mib_data{subagents = SAs} = D,
734    SAOid = lists:reverse(SARevOid),
735    case lists:keysearch(SAOid, 2, SAs) of
736	{value, {SubAgentPid, SAOid}} ->
737	    {subagent, SubAgentPid, SAOid};
738	false ->
739	    ?vinfo("find_node -> could not find subagent with"
740		"~n   SAOid: ~p"
741		"~n   SAs:   ~p", [SAOid, SAs]),
742	    false
743    end;
744find_node(_D, Node, _RestOfOid, _RevOid) ->
745    ?vtrace("find_node -> failed:~n~p",[Node]),
746    {false, noSuchObject}.
747
748
749%%-----------------------------------------------------------------
750%% Func: next/3
751%% Purpose: Finds the lexicographically next oid.
752%% Returns: endOfMibView |
753%%          {subagent, SubAgentPid, SAOid} |
754%%          {variable, MibEntry, VarOid} |
755%%          {table, TableOid, TableRestOid, MibEntry}
756%% If a variable is returnes, it is in the MibView.
757%% If a table or subagent is returned, it *may* be in the MibView.
758%%-----------------------------------------------------------------
759next(#mib_data{tree = T} = D, Oid, MibView) ->
760    case catch next_node(D, T#tree.root, Oid, [], MibView) of
761	false -> endOfMibView;
762	Else -> Else
763    end.
764
765%%-----------------------------------------------------------------
766%% This function is used as long as we have any Oid left. Take
767%% one integer at a time from the Oid, and traverse the tree
768%% accordingly. When the Oid is empty, call find_next.
769%% Returns: {subagent, SubAgentPid, SAOid} |
770%%          false |
771%%          {variable, MibEntry, VarOid} |
772%%          {table, TableOid, TableRestOid, MibEntry}
773%%-----------------------------------------------------------------
774next_node(_D, undefined_node, _Oid, _RevOidSoFar, _MibView) ->
775    ?vtrace("next_node(undefined_node) -> entry", []),
776    false;
777
778next_node(_D, {tree, Tree, {table_entry, _Id}}, [Int | _Oid],
779	  _RevOidSoFar, _MibView)
780  when Int+1 > size(Tree) ->
781    ?vtrace("next_node(tree,table_entry) -> entry when not found whith"
782	"~n   Int:        ~p"
783	"~n   size(Tree): ~p", [Int, size(Tree)]),
784    false;
785next_node(D, {tree, Tree, {table_entry, _MibName}},
786	  Oid, RevOidSoFar, MibView) ->
787    ?vtrace("next_node(tree,table_entry) -> entry when"
788	"~n   size(Tree):  ~p"
789	"~n   Oid:         ~p"
790	"~n   RevOidSoFar: ~p"
791	"~n   MibView:     ~p", [size(Tree), Oid, RevOidSoFar, MibView]),
792    OidSoFar = lists:reverse(RevOidSoFar),
793    case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
794	true ->
795	    ?vdebug("next_node(tree,table_entry) -> not in mib view",[]),
796	    false;
797	_ ->
798	    #mib_data{node_db = Db} = D,
799	    case snmpa_general_db:read(Db, OidSoFar) of
800		false ->
801		    ?vinfo("next_node -> could not find table_entry with"
802			"~n   OidSoFar: ~p", [OidSoFar]),
803		    false;
804		{value, #node_info{me = ME}} ->
805		    ?vtrace("next_node(tree,table_entry) -> found: ~n   ~p",
806			[ME]),
807		    {table, OidSoFar, Oid, ME}
808	    end
809    end;
810
811next_node(D, {tree, Tree, _Info}, [Int | RestOfOid], RevOidSoFar, MibView)
812  when (Int < size(Tree)) andalso (Int >= 0) ->
813    ?vtrace("next_node(tree) -> entry when"
814	"~n   size(Tree):  ~p"
815	"~n   Int:         ~p"
816	"~n   RestOfOid:   ~p"
817	"~n   RevOidSoFar: ~p"
818	"~n   MibView:     ~p",
819	[size(Tree), Int, RestOfOid, RevOidSoFar, MibView]),
820    case next_node(D, element(Int+1,Tree),
821		   RestOfOid, [Int|RevOidSoFar], MibView) of
822	false ->
823	    find_next(D, {tree, Tree, _Info}, Int+1, RevOidSoFar, MibView);
824	Else ->
825	    Else
826    end;
827%% no solution
828next_node(D, {tree, Tree, _Info}, [], RevOidSoFar, MibView) ->
829    ?vtrace("next_node(tree,[]) -> entry when"
830	"~n   size(Tree):  ~p"
831	"~n   RevOidSoFar: ~p"
832	"~n   MibView:     ~p",
833	[size(Tree), RevOidSoFar, MibView]),
834    find_next(D, {tree, Tree, _Info}, 0, RevOidSoFar, MibView);
835next_node(_D, {tree, Tree, _Info}, _RestOfOid, _RevOidSoFar, _MibView) ->
836    ?vtrace("next_node(tree) -> entry when"
837	"~n   size(Tree):  ~p", [size(Tree)]),
838    false;
839
840next_node(D, {node, subagent}, Oid, RevOidSoFar, MibView) ->
841    ?vtrace("next_node(node,subagent) -> entry when"
842	"~n   Oid:         ~p"
843	"~n   RevOidSoFar: ~p"
844	"~n   MibView:     ~p",
845	[Oid, RevOidSoFar, MibView]),
846    OidSoFar = lists:reverse(RevOidSoFar),
847    case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
848	true ->
849	    false;
850	_ ->
851	    #mib_data{subagents = SAs} = D,
852	    case lists:keysearch(OidSoFar, 2, SAs) of
853		{value, {SubAgentPid, OidSoFar}} ->
854		    {subagent, SubAgentPid, OidSoFar};
855		_ ->
856		    ?vinfo("next_node -> could not find subagent with"
857			"~n   OidSoFar: ~p"
858			"~n   SAs:      ~p", [OidSoFar, SAs]),
859		    false
860	    end
861    end;
862
863next_node(D, {node, {variable, _MibName}}, [], RevOidSoFar, MibView) ->
864    ?vtrace("next_node(node,variable,[]) -> entry when"
865	"~n   RevOidSoFar: ~p"
866	"~n   MibView:     ~p",
867	[RevOidSoFar, MibView]),
868    OidSoFar = lists:reverse([0 | RevOidSoFar]),
869    case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
870	true ->
871	    #mib_data{node_db = Db} = D,
872	    case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
873		false ->
874		    ?vinfo("next_node -> could not find variable with"
875			"~n   RevOidSoFar: ~p", [RevOidSoFar]),
876		    false;
877		{value, #node_info{me = ME}} ->
878		    {variable, ME, OidSoFar}
879	    end;
880	_ ->
881	    false
882    end;
883
884next_node(_D, {node, {variable, _MibName}}, _Oid, _RevOidSoFar, _MibView) ->
885    ?vtrace("next_node(node,variable) -> entry", []),
886    false.
887
888%%-----------------------------------------------------------------
889%% This function is used to find the first leaf from where we
890%% are.
891%% Returns: {subagent, SubAgentPid, SAOid} |
892%%          false |
893%%          {variable, MibEntry, VarOid} |
894%%          {table, TableOid, TableRestOid, MibEntry}
895%% PRE: This function must always be called with a {internal, Tree}
896%%      node.
897%%-----------------------------------------------------------------
898find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView)
899  when Idx < size(Tree) ->
900    case find_next(D, element(Idx+1, Tree), 0, [Idx| RevOidSoFar], MibView) of
901	false ->
902	    find_next(D, {tree, Tree, internal}, Idx+1, RevOidSoFar, MibView);
903	Other ->
904	    Other
905    end;
906find_next(_D, {tree, _Tree, internal}, _Idx, _RevOidSoFar, _MibView) ->
907    false;
908find_next(_D, undefined_node, _Idx, _RevOidSoFar, _MibView) ->
909    false;
910find_next(D, {tree, Tree, {table, _MibName}}, Idx, RevOidSoFar, MibView) ->
911    find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView);
912find_next(D, {tree, _Tree, {table_entry, _MibName}}, _Index,
913	  RevOidSoFar, MibView) ->
914    OidSoFar = lists:reverse(RevOidSoFar),
915    case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
916	true ->
917	    false;
918	_ ->
919	    #mib_data{node_db = Db} = D,
920	    case snmpa_general_db:read(Db, OidSoFar) of
921		false ->
922		    ?vinfo("find_next -> could not find table_entry ME with"
923			"~n   OidSoFar: ~p", [OidSoFar]),
924		    false;
925		{value, #node_info{me = ME}} ->
926		    {table, OidSoFar, [], ME}
927	    end
928    end;
929find_next(D, {node, {variable, _MibName}}, _Idx, RevOidSoFar, MibView) ->
930    OidSoFar = lists:reverse([0 | RevOidSoFar]),
931    case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
932	true ->
933	    #mib_data{node_db = Db} = D,
934	    case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
935		false ->
936		    ?vinfo("find_next -> could not find variable with"
937			"~n   RevOidSoFar: ~p", [RevOidSoFar]),
938		    false;
939		{value, #node_info{me = ME}} ->
940		    {variable, ME, OidSoFar}
941	    end;
942	_ ->
943	    false
944    end;
945find_next(D, {node, subagent}, _Idx, RevOidSoFar, MibView) ->
946    OidSoFar = lists:reverse(RevOidSoFar),
947    case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
948	true ->
949	    false;
950	_ ->
951	    #mib_data{subagents = SAs} = D,
952	    case lists:keysearch(OidSoFar, 2, SAs) of
953		{value, {SubAgentPid, OidSoFar}} ->
954		    {subagent, SubAgentPid, OidSoFar};
955		false ->
956		    ?vinfo("find_node -> could not find subagent with"
957			"~n   OidSoFar: ~p"
958			"~n   SAs:      ~p", [OidSoFar, SAs]),
959		    false
960	    end
961    end.
962
963%%%======================================================================
964%%% 3. Tree building functions
965%%%    Used when loading mibs.
966%%%======================================================================
967
968build_tree(Mes, MibName) ->
969    ?d("build_tree -> "
970	"~n   Mes: ~p", [Mes]),
971    {ListTree, []}  = build_subtree([], Mes, MibName),
972    {tree, convert_tree(ListTree), internal}.
973
974%%----------------------------------------------------------------------
975%% Purpose: Builds the tree where all oids have prefix equal to LevelPrefix.
976%% Returns: {Tree, RestMes}
977%% RestMes are Mes that should not be in this subtree.
978%% The Tree is a temporary and simplified data structure that is easy to
979%% convert to the final tuple tree used by the MIB process.
980%% A Node is represented as in the final tree.
981%% The tree is not represented as a N-tuple, but as an Index-list.
982%% Example: Temporary: [{1, Node1}, {3, Node3}]
983%%          Final:     {Node1, undefined_node, Node3}
984%% Pre: Mes are sorted on oid.
985%%----------------------------------------------------------------------
986build_subtree(LevelPrefix, [Me | Mes], MibName) ->
987    ?vtrace("build subtree -> ~n"
988	   "   oid:         ~p~n"
989	   "   LevelPrefix: ~p~n"
990	   "   MibName:     ~p", [Me#me.oid, LevelPrefix, MibName]),
991    EType = Me#me.entrytype,
992    ?vtrace("build subtree -> EType = ~p",[EType]),
993    case in_subtree(LevelPrefix, Me) of
994	above ->
995	    ?vtrace("build subtree -> above",[]),
996	    {[], [Me|Mes]};
997	{node, Index} ->
998	    ?vtrace("build subtree -> node at ~p",[Index]),
999	    {Tree, RestMes} = build_subtree(LevelPrefix, Mes, MibName),
1000	    {[{Index, {node, {EType, MibName}}} | Tree], RestMes};
1001	{subtree, Index, NewLevelPrefix} ->
1002	    ?vtrace("build subtree -> subtree at"
1003		    "~n   ~w with ~w",
1004		   [Index, NewLevelPrefix]),
1005	    {BelowTree, RestMes} =
1006		build_subtree(NewLevelPrefix, Mes, MibName),
1007	    {CurTree, RestMes2} =
1008		build_subtree(LevelPrefix, RestMes, MibName),
1009	    {[{Index, {tree, BelowTree, {EType,MibName}}}| CurTree], RestMes2};
1010	{internal_subtree, Index, NewLevelPrefix} ->
1011	    ?vtrace("build subtree -> internal_subtree at"
1012		    "~n   ~w with ~w",
1013		   [Index,NewLevelPrefix]),
1014	    {BelowTree, RestMes} =
1015		build_subtree(NewLevelPrefix, [Me | Mes], MibName),
1016	    {CurTree, RestMes2} =
1017		build_subtree(LevelPrefix, RestMes, MibName),
1018	    {[{Index, {tree, BelowTree, internal}} | CurTree], RestMes2}
1019    end;
1020
1021build_subtree(_LevelPrefix, [], _MibName) ->
1022    ?vtrace("build subtree -> done", []),
1023    {[], []}.
1024
1025%%--------------------------------------------------
1026%% Purpose: Determine how/if/where Me should be inserted in subtree
1027%%          with LevelPrefix. This function does not build any tree, only
1028%%          determinses what should be done (by build subtree).
1029%% Returns:
1030%% above - Indicating that this ME should _not_ be in this subtree.
1031%% {node, Index} - yes, construct a node with index Index on this level
1032%% {internal_subtree, Index, NewLevelPrefix} - yes, there should be an
1033%%   internal subtree at this index.
1034%% {subtree, Index, NewLevelPrefix} - yes, construct a subtree with
1035%%   NewLevelPrefix and insert this on current level in position Index.
1036%%--------------------------------------------------
1037in_subtree(LevelPrefix, Me) ->
1038    case lists:prefix(LevelPrefix, Me#me.oid) of
1039	true when length(Me#me.oid) > length(LevelPrefix) ->
1040	    classify_how_in_subtree(LevelPrefix, Me);
1041	_ ->
1042	    above
1043    end.
1044
1045%%--------------------------------------------------
1046%% See comment about in_subtree/2.  This function takes care of all cases
1047%% where the ME really should be in _this_ subtree (not above).
1048%%--------------------------------------------------
1049classify_how_in_subtree(LevelPrefix, Me)
1050  when (length(Me#me.oid) =:= (length(LevelPrefix) + 1)) ->
1051    Oid = Me#me.oid,
1052    case node_or_subtree(Me#me.entrytype) of
1053	subtree ->
1054	    {subtree, lists:last(Oid), Oid};
1055	node ->
1056	    {node, lists:last(Oid)}
1057    end;
1058
1059classify_how_in_subtree(LevelPrefix, Me)
1060  when (length(Me#me.oid) > (length(LevelPrefix) + 1)) ->
1061    L1 = length(LevelPrefix) + 1,
1062    Oid = Me#me.oid,
1063    {internal_subtree, lists:nth(L1, Oid), lists:sublist(Oid, 1, L1)}.
1064
1065%%--------------------------------------------------
1066%% Determines how to treat different kinds om MEs in the tree building process.
1067%% Pre: all internal nodes have been removed.
1068%%--------------------------------------------------
1069node_or_subtree(table) -> subtree;
1070node_or_subtree(table_entry) -> subtree;
1071node_or_subtree(variable) -> node;
1072node_or_subtree(table_column) -> node.
1073
1074%%--------------------------------------------------
1075%% Purpose: (Recursively) Converts a temporary tree (see above) to a final tree.
1076%% If input is a ListTree, output is a TupleTree.
1077%% If input is a Node, output is the same Node.
1078%% Pre: All Indexes are >= 0.
1079%%--------------------------------------------------
1080convert_tree({Index, {tree, Tree, Info}}) when Index >= 0 ->
1081    L = lists:map(fun convert_tree/1, Tree),
1082    {Index, {tree, dict_list_to_tuple(L), Info}};
1083convert_tree({Index, {node, Info}}) when Index >= 0 ->
1084    {Index, {node, Info}};
1085convert_tree(Tree) when is_list(Tree) ->
1086    L = lists:map(fun convert_tree/1, Tree),
1087    dict_list_to_tuple(L).
1088
1089%%----------------------------------------------------------------------
1090%% Purpose: Converts a single level (that is non-recursively) from
1091%%          the temporary indexlist to the N-tuple.
1092%% Input: A list of {Index, Data}.
1093%% Output: A tuple where element Index is Data.
1094%%----------------------------------------------------------------------
1095dict_list_to_tuple(L) ->
1096    L2 = lists:keysort(1, L),
1097    list_to_tuple(integrate_indexes(0, L2)).
1098
1099%%----------------------------------------------------------------------
1100%% Purpose: Helper function for dict_list_to_tuple/1.
1101%%          Converts an indexlist to a N-list.
1102%% Input: A list of {Index, Data}.
1103%% Output: A (usually longer, never shorter) list where element Index is Data.
1104%% Example: [{1,hej}, {3, sven}] will give output
1105%% [undefined_node, hej, undefined_node, sven].
1106%% Initially CurIndex should be 0.
1107%%----------------------------------------------------------------------
1108integrate_indexes(CurIndex, [{CurIndex, Data} | T]) ->
1109    [Data | integrate_indexes(CurIndex + 1, T)];
1110integrate_indexes(_Index, []) ->
1111    [];
1112integrate_indexes(CurIndex, L) ->
1113    [undefined_node | integrate_indexes(CurIndex + 1, L)].
1114
1115%%%======================================================================
1116%%% 4. Tree merging
1117%%%    Used by: load mib, insert subagent.
1118%%%======================================================================
1119
1120%%----------------------------------------------------------------------
1121%% Arg: Two root nodes (that is to be merged).
1122%% Returns: A new root node where the nodes have been merger to one.
1123%%----------------------------------------------------------------------
1124merge_nodes(Same, Same) ->
1125    Same;
1126merge_nodes(Node, undefined_node) ->
1127    Node;
1128merge_nodes(undefined_node, Node) ->
1129    Node;
1130merge_nodes({tree, Tree1, internal}, {tree, Tree2, internal}) ->
1131    {tree, merge_levels(tuple_to_list(Tree1),tuple_to_list(Tree2)), internal};
1132merge_nodes(Node1, Node2) ->
1133    throw({error_merge_nodes, Node1, Node2}).
1134
1135%%----------------------------------------------------------------------
1136%% Arg: Two levels to be merged.
1137%%      Here, a level is represented as a list of nodes. A list is easier
1138%%      to extend than a tuple.
1139%% Returns: The resulting, merged level tuple.
1140%%----------------------------------------------------------------------
1141merge_levels(Level1, Level2) when length(Level1) =:= length(Level2) ->
1142    MergeNodes = fun(N1, N2) -> merge_nodes(N1, N2) end,
1143    list_to_tuple(snmp_misc:multi_map(MergeNodes, [Level1, Level2]));
1144merge_levels(Level1, Level2) when length(Level1) > length(Level2) ->
1145    merge_levels(Level1, Level2 ++
1146		 undefined_nodes_list(length(Level1) - length(Level2)));
1147merge_levels(Level1, Level2) when length(Level1) < length(Level2) ->
1148    merge_levels(Level2, Level1).
1149
1150undefined_nodes_list(N) -> lists:duplicate(N, undefined_node).
1151
1152
1153%%%======================================================================
1154%%% 5. Tree deletion routines
1155%%%    (for unload mib)
1156%%%======================================================================
1157
1158%%----------------------------------------------------------------------
1159%% Purpose:  Actually kicks of the tree reconstruction.
1160%% Returns: {list of removed MEs, NewTree}
1161%%----------------------------------------------------------------------
1162delete_mib_from_tree(MibName, {tree, Tree, internal}) ->
1163    case delete_tree(Tree, MibName) of
1164	[] ->
1165	    {tree, {undefined_node}, internal}; % reduce
1166	LevelList ->
1167	    {tree, list_to_tuple(LevelList), internal}
1168    end.
1169
1170%%----------------------------------------------------------------------
1171%% Purpose: Deletes all nodes associated to MibName from this level and
1172%%          all levels below.
1173%%          If the new level does not contain information (that is, no
1174%%          other mibs use it) anymore the empty list is returned.
1175%% Returns: {MEs, The new level represented as a list}
1176%%----------------------------------------------------------------------
1177delete_tree(Tree, MibName) when is_tuple(Tree) ->
1178    NewLevel = delete_nodes(tuple_to_list(Tree), MibName, []),
1179    case lists:filter(fun drop_undefined_nodes/1,NewLevel) of
1180	[] -> [];
1181	_A_perhaps_shorted_list ->
1182	    NewLevel  % some other mib needs this level
1183    end.
1184
1185%%----------------------------------------------------------------------
1186%% Purpose: Nodes belonging to MibName are removed from the tree.
1187%%          Recursively deletes sub trees to this node.
1188%% Returns: {MEs, NewNodesList}
1189%%----------------------------------------------------------------------
1190delete_nodes([], _MibName, AccNodes) ->
1191    lists:reverse(AccNodes);
1192
1193delete_nodes([{node, {variable, MibName}}|T], MibName, AccNodes) ->
1194    delete_nodes(T, MibName, [undefined_node | AccNodes]);
1195
1196delete_nodes([{node, {table_column, MibName}}|T], MibName, AccNodes) ->
1197    delete_nodes(T, MibName, [undefined_node | AccNodes]);
1198
1199delete_nodes([{tree, _Tree, {table, MibName}}|T], MibName, AccNodes) ->
1200    delete_nodes(T, MibName, [undefined_node | AccNodes]);
1201
1202delete_nodes([{tree, _Tree, {table_entry, MibName}}|T], MibName, AccNodes) ->
1203    delete_nodes(T, MibName, [undefined_node | AccNodes]);
1204
1205delete_nodes([{tree, Tree, Info}|T], MibName, AccNodes) ->
1206    case delete_tree(Tree, MibName) of
1207	[] -> % tree completely deleted
1208	    delete_nodes(T, MibName, [undefined_node | AccNodes]);
1209	LevelList ->
1210	    delete_nodes(T, MibName,
1211			 [{tree, list_to_tuple(LevelList), Info} | AccNodes])
1212    end;
1213
1214delete_nodes([NodeToKeep|T], MibName, AccNodes) ->
1215    delete_nodes(T, MibName, [NodeToKeep | AccNodes]).
1216
1217drop_undefined_nodes(undefined_node) -> false;
1218drop_undefined_nodes(_) -> true.
1219
1220
1221%%%======================================================================
1222%%% 6. Functions for subagent handling
1223%%%======================================================================
1224
1225%%----------------------------------------------------------------------
1226%% Returns: A new Root|{error, reason}
1227%%----------------------------------------------------------------------
1228insert_subagent(Oid, OldRoot) ->
1229    ListTree = build_tree_for_subagent(Oid),
1230    case catch convert_tree(ListTree) of
1231	{'EXIT', _Reason} ->
1232	    {error, 'cannot construct tree from oid'};
1233	Level when is_tuple(Level) ->
1234	    T = {tree, Level, internal},
1235	    case catch merge_nodes(T, OldRoot) of
1236		{error_merge_nodes, _Node1, _Node2} ->
1237		    {error, oid_conflict};
1238		NewRoot when is_tuple(NewRoot) andalso
1239			     (element(1, NewRoot) =:= tree) ->
1240		    NewRoot
1241	    end
1242    end.
1243
1244build_tree_for_subagent([Index]) ->
1245    [{Index, {node, subagent}}];
1246
1247build_tree_for_subagent([Index | T]) ->
1248    [{Index, {tree, build_tree_for_subagent(T), internal}}].
1249
1250%%----------------------------------------------------------------------
1251%% Returns: A new tree where the subagent at Oid (2nd arg) has been deleted.
1252%%----------------------------------------------------------------------
1253delete_subagent({tree, Tree, Info}, [Index]) ->
1254    {node, subagent} = element(Index+1, Tree),
1255    {tree, setelement(Index+1, Tree, undefined_node), Info};
1256delete_subagent({tree, Tree, Info}, [Index | TI]) ->
1257    {tree, setelement(Index+1, Tree,
1258		      delete_subagent(element(Index+1, Tree), TI)), Info}.
1259
1260%%%======================================================================
1261%%% 7. Misc functions
1262%%%======================================================================
1263
1264%%----------------------------------------------------------------------
1265%% Installs the mibs found in the database when starting the agent.
1266%% Basically calls the instrumentation functions for all non-internal
1267%% mib-entries
1268%%----------------------------------------------------------------------
1269install_mibs(MibDb, NodeDb) ->
1270    MibNames = loaded(MibDb),
1271    ?vtrace("install_mibs -> found following mibs in database: ~n"
1272	"~p", [MibNames]),
1273    install_mibs2(NodeDb, MibNames).
1274
1275install_mibs2(_, []) ->
1276    ok;
1277install_mibs2(NodeDb, [MibName|MibNames]) ->
1278    Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
1279    Nodes = snmpa_general_db:match_object(NodeDb, Pattern),
1280    MEs = [ME || #node_info{me = ME} <- Nodes],
1281    ?vtrace("install_mibs2 -> installing ~p MEs for mib ~p",
1282	[length(MEs),MibName]),
1283    NewF = fun(ME) -> call_instrumentation(ME, new) end,
1284    lists:foreach(NewF, MEs),
1285    install_mibs2(NodeDb, MibNames).
1286
1287
1288%%----------------------------------------------------------------------
1289%% Does all side effect stuff during load_mib.
1290%%----------------------------------------------------------------------
1291install_mib(Db, Symbolic, Mib, MibName, FileName, NonInternalMes) ->
1292    ?vdebug("install_mib -> entry with"
1293	    "~n   Symbolic: ~p"
1294	    "~n   MibName:  ~p"
1295	    "~n   FileName: ~p", [Symbolic, MibName, FileName]),
1296    Rec = #mib_info{name = MibName, symbolic = Symbolic, file_name = FileName},
1297    snmpa_general_db:write(Db, Rec),
1298    install_mib2(Symbolic, MibName, Mib),
1299    NewF = fun(ME) -> call_instrumentation(ME, new) end,
1300    lists:foreach(NewF, NonInternalMes).
1301
1302install_mib2(true, MibName, Mib) ->
1303    #mib{table_infos    = TabInfos,
1304	 variable_infos = VarInfos,
1305	 mes            = MEs,
1306	 asn1_types     = ASN1Types,
1307	 traps          = Traps} = Mib,
1308    snmpa_symbolic_store:add_table_infos(MibName, TabInfos),
1309    snmpa_symbolic_store:add_variable_infos(MibName, VarInfos),
1310    snmpa_symbolic_store:add_aliasnames(MibName, MEs),
1311    snmpa_symbolic_store:add_types(MibName, ASN1Types),
1312    SetF = fun(Trap) ->
1313		   snmpa_symbolic_store:set_notification(Trap, MibName)
1314	   end,
1315    lists:foreach(SetF, Traps);
1316install_mib2(_, _, _) ->
1317    ok.
1318
1319install_mes(_Db, _MibName, []) ->
1320    ok;
1321install_mes(Db, MibName, [ME|MEs]) ->
1322    Node = #node_info{oid = ME#me.oid, mib_name = MibName, me = ME},
1323    snmpa_general_db:write(Db, Node),
1324    install_mes(Db, MibName, MEs).
1325
1326
1327%%----------------------------------------------------------------------
1328%% Does all side effect stuff during unload_mib.
1329%%----------------------------------------------------------------------
1330uninstall_mib(Db, Symbolic, MibName, MEs) ->
1331    ?vtrace("uninstall_mib -> entry with"
1332	"~n   Db:       ~p"
1333	"~n   Symbolic: ~p"
1334	"~n   MibName:  ~p", [Db, Symbolic, MibName]),
1335    Res = snmpa_general_db:delete(Db, MibName),
1336    ?vtrace("uninstall_mib -> (mib) db delete result: ~p", [Res]),
1337    uninstall_mib2(Symbolic, MibName),
1338    DelF = fun(ME) -> call_instrumentation(ME, delete) end,
1339    lists:foreach(DelF, MEs).
1340
1341uninstall_mib2(true, MibName) ->
1342    snmpa_symbolic_store:delete_table_infos(MibName),
1343    snmpa_symbolic_store:delete_variable_infos(MibName),
1344    snmpa_symbolic_store:delete_aliasnames(MibName),
1345    snmpa_symbolic_store:delete_types(MibName),
1346    snmpa_symbolic_store:delete_notifications(MibName);
1347uninstall_mib2(_, _) ->
1348    ok.
1349
1350uninstall_mes(Db, MibName) ->
1351    Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
1352    snmpa_general_db:match_delete(Db, Pattern).
1353
1354
1355%%----------------------------------------------------------------------
1356%% Create a list of the names of all the loaded mibs
1357%%----------------------------------------------------------------------
1358loaded(Db) ->
1359    [N || #mib_info{name = N} <- snmpa_general_db:tab2list(Db)].
1360
1361
1362%%----------------------------------------------------------------------
1363%% Calls MFA-instrumentation with 'new' or 'delete' operation.
1364%%----------------------------------------------------------------------
1365call_instrumentation(#me{entrytype = variable, mfa={M,F,A}}, Operation) ->
1366    ?vtrace("call instrumentation with"
1367	    "~n   entrytype: variable"
1368	    "~n   MFA:       {~p,~p,~p}"
1369	    "~n   Operation: ~p",
1370	    [M,F,A,Operation]),
1371    catch apply(M, F, [Operation | A]);
1372call_instrumentation(#me{entrytype = table_entry, mfa={M,F,A}}, Operation) ->
1373    ?vtrace("call instrumentation with"
1374	    "~n   entrytype: table_entry"
1375	    "~n   MFA:       {~p,~p,~p}"
1376	    "~n   Operation: ~p",
1377	    [M,F,A,Operation]),
1378    catch apply(M, F, [Operation | A]);
1379call_instrumentation(_ShitME, _Operation) ->
1380    done.
1381
1382
1383maybe_drop_me(#me{entrytype = internal}) -> false;
1384maybe_drop_me(#me{entrytype = group}) -> false;
1385maybe_drop_me(#me{imported = true}) -> false;
1386maybe_drop_me(_) -> true.
1387
1388
1389%%----------------------------------------------------------------------
1390%% Code change functions
1391%%----------------------------------------------------------------------
1392
1393code_change(down, State) ->
1394    ?d("code_change(down) -> entry",[]),
1395    State;
1396
1397code_change(up, State) ->
1398    ?d("code_change(up)",[]),
1399    State;
1400
1401code_change(_Vsn, State) ->
1402    State.
1403
1404