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