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