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