1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2019. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(snmpa_agent). 21 22-include_lib("kernel/include/file.hrl"). 23-include("snmpa_internal.hrl"). 24-include("snmp_types.hrl"). 25-include("snmp_debug.hrl"). 26-include("snmp_verbosity.hrl"). 27-include("SNMP-FRAMEWORK-MIB.hrl"). 28 29%% External exports 30-export([start_link/4, start_link/5, stop/1]). 31-export([subagent_get/3, subagent_get_next/3, 32 subagent_set/2, 33 load_mibs/3, unload_mibs/3, 34 which_mibs/1, whereis_mib/2, info/1, 35 register_subagent/3, unregister_subagent/2, 36 send_notification/3, 37 register_notification_filter/5, 38 unregister_notification_filter/2, 39 which_notification_filter/1, 40 get_net_if/1]). 41-export([ 42 discovery/6, 43 is_originating_discovery_enabled/0, 44 is_terminating_discovery_enabled/0, 45 terminating_discovery_stage2/0, 46 terminating_trigger_username/0 47 ]). 48-export([verbosity/2, dump_mibs/1, dump_mibs/2]). 49-export([validate_err/3, make_value_a_correct_value/3, 50 do_get/3, do_get/4, 51 get/2, get/3, get_next/2, get_next/3]). 52-export([mib_of/1, mib_of/2, me_of/1, me_of/2, 53 invalidate_mibs_cache/1, 54 which_mibs_cache_size/1, 55 enable_mibs_cache/1, disable_mibs_cache/1, 56 gc_mibs_cache/1, gc_mibs_cache/2, gc_mibs_cache/3, 57 enable_mibs_cache_autogc/1, disable_mibs_cache_autogc/1, 58 update_mibs_cache_age/2, 59 update_mibs_cache_gclimit/2]). 60-export([get_agent_mib_storage/0, db/1, 61 backup/2]). 62-export([get_log_type/1, set_log_type/2]). 63-export([get_request_limit/1, set_request_limit/2]). 64-export([invalidate_ca_cache/0]). 65-export([increment_counter/3]). 66-export([restart_worker/1, restart_set_worker/1, restart_notif_worker/1]). 67 68%% For backward compatibillity 69-export([send_trap/6, send_trap/7]). 70 71%% Internal exports 72-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 73 terminate/2, code_change/3, tr_var/2, tr_varbind/1, 74 handle_pdu/8, worker/4, worker_loop/2, 75 do_send_trap/7, do_send_trap/8]). 76%% <BACKWARD-COMPAT> 77-export([handle_pdu/7, 78 load_mibs/2, unload_mibs/2]). 79%% </BACKWARD-COMPAT> 80 81-include("snmpa_internal.hrl"). 82 83-ifndef(default_verbosity). 84-define(default_verbosity,silence). 85-endif. 86 87-define(empty_pdu_size, 21). 88 89-ifdef(snmp_extended_verbosity). 90-define(vt(F,A), ?vtrace(F, A)). 91-else. 92-define(vt(_F, _A), ok). 93-endif. 94 95-define(DISCO_TERMINATING_TRIGGER_USERNAME, ""). 96 97-ifdef(snmp_debug). 98-define(GS_START_LINK3(Prio, Parent, Ref, Opts), 99 gen_server:start_link(?MODULE, [Prio, Parent, Ref, Opts], 100 [{debug,[trace]}])). 101-define(GS_START_LINK4(Prio, Name, Parent, Ref, Opts), 102 gen_server:start_link({local, Name}, ?MODULE, 103 [Prio, Parent, Ref, Opts], 104 [{debug,[trace]}])). 105-else. 106-define(GS_START_LINK3(Prio, Parent, Ref, Opts), 107 gen_server:start_link(?MODULE, [Prio, Parent, Ref, Opts],[])). 108-define(GS_START_LINK4(Prio, Name, Parent, Ref, Opts), 109 gen_server:start_link({local, Name}, ?MODULE, 110 [Prio, Parent, Ref, Opts],[])). 111-endif. 112 113%% Increment this whenever a change is made to the worker interface 114-define(WORKER_INTERFACE_VERSION, 1). 115 116%% -- Utility macros for creating worker commands -- 117-define(mk_pdu_wreq(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra), 118 #wrequest{cmd = handle_pdu, 119 info = [{vsn, Vsn}, 120 {pdu, Pdu}, 121 {pdu_ms, PduMS}, 122 {acm_data, ACMData}, 123 {addr, Address}, 124 {gb_max_vbs, GbMaxVBs}, 125 {extra, Extra}]}). 126-define(mk_send_trap_wreq(TrapRec, NotifyName, ContextName, 127 Recv, Vbs, LocalEngineID, Extra), 128 #wrequest{cmd = send_trap, 129 info = [{trap_rec, TrapRec}, 130 {notify_name, NotifyName}, 131 {context_name, ContextName}, 132 {receiver, Recv}, 133 {varbinds, Vbs}, 134 {local_engine_id, LocalEngineID}, 135 {extra, Extra}]}). 136-define(mk_terminate_wreq(), #wrequest{cmd = terminate, info = []}). 137-define(mk_verbosity_wreq(V), #wrequest{cmd = verbosity, 138 info = [{verbosity, V}]}). 139 140 141-record(notification_filter, {id, mod, data}). 142-record(disco, 143 {from, rec, sender, target, engine_id, 144 sec_level, ctx, ivbs, stage, handler, extra}). 145 146%% This record is used when sending requests to the worker processes 147-record(wrequest, 148 { 149 version = ?WORKER_INTERFACE_VERSION, 150 cmd, 151 info 152 } 153 ). 154 155 156%%----------------------------------------------------------------- 157%% The agent is multi-threaded, i.e. each request is handled 158%% by a separate process. However, in the normal case, there 159%% is just one request handled at the time. In order to improve 160%% performance, there is always two worker processes alive. They are 161%% created at initialization time. There is always one worker 162%% dedicated to SET-handling. When a get*-request is received, 163%% it is sent to the worker, and the worker is marked as busy. 164%% If a request is received when the worker is busy, a new temporary 165%% worker is spawned. 166%% 167%% Code change 168%% =========== 169%% Note that the worker(s) execute the same module as the master 170%% agent. For code change we have two options - ignore the workers, 171%% or send them a code change message. 172%% 173%%----------------------------------------------------------------- 174 175-record(state, {type, 176 parent, 177 worker, 178 worker_state = ready, 179 set_worker, 180 notif_worker, 181 multi_threaded, 182 ref, 183 vsns, 184 nfilters = [], 185 note_store, 186 mib_server, %% Currently unused 187 net_if, %% Currently unused 188 net_if_mod, 189 backup, 190 disco, 191 mibs_cache_request, 192 gb_max_vbs}). 193 194 195%%%----------------------------------------------------------------- 196%%% This module implements the agent machinery; both for the master 197%%% agent and the subagents. 198%%%----------------------------------------------------------------- 199%%% Table of contents 200%%% ================= 201%%% 1. Interface 202%%% 2. Main loop 203%%% 3. GET REQUEST 204%%% 4. GET-NEXT REQUEST 205%%% 5. GET-BULK REQUEST 206%%% 6. SET REQUEST 207%%% 7. Misc functions 208%%%----------------------------------------------------------------- 209 210%%----------------------------------------------------------------- 211%% Parent is a Pid (of master_agent) or none 212%% Options is a list of Option, where Option is 213%% {mibs, Mibs} 214%% {net_if, NetIfModule} 215%% {priority, Prio} 216%% {verbosity, Verbosity} 217%% {multi_threaded, Bool} true means that SETs are serialized, 218%% while GETs are concurrent, even with a SET. 219%% {set_mechanism, SetModule} % undocumented feature 220%% 221%% The following options are now removed - they are not needed 222%% anymore when VACM is standard for authentication, and works 223%% with all versions, and trap sending is standardized too. 224%% {authentication_service, AuthModule} % undocumented feature 225%% {trap_mechanism, TrapModule} % undocumented feature 226%% Note: authentication_service is reintroduced for AXD301 (OTP-3324). 227%%----------------------------------------------------------------- 228start_link(Prio, Parent, Ref, Options) -> 229 ?d("start_link -> entry with" 230 "~n Prio: ~p" 231 "~n Parent: ~p" 232 "~n Ref: ~p" 233 "~n Options: ~p", [Prio, Parent, Ref, Options]), 234 ?GS_START_LINK3(Prio, Parent, Ref, Options). 235 236start_link(Prio, Name, Parent, Ref, Options) -> 237 ?d("start_link -> entry with" 238 "~n Prio: ~p" 239 "~n Name: ~p" 240 "~n Parent: ~p" 241 "~n Ref: ~p" 242 "~n Options: ~p", [Prio, Name, Parent, Ref, Options]), 243 ?GS_START_LINK4(Prio, Name, Parent, Ref, Options). 244 245stop(Agent) -> call(Agent, stop). 246 247restart_worker(Agent) -> 248 call(Agent, restart_worker). 249 250restart_set_worker(Agent) -> 251 call(Agent, restart_set_worker). 252 253restart_notif_worker(Agent) -> 254 call(Agent, restart_notif_worker). 255 256get_log_type(Agent) -> 257 call(Agent, get_log_type). 258 259set_log_type(Agent, NewType) -> 260 call(Agent, {set_log_type, NewType}). 261 262get_request_limit(Agent) -> 263 call(Agent, get_request_limit). 264 265set_request_limit(Agent, NewLimit) -> 266 call(Agent, {set_request_limit, NewLimit}). 267 268mib_of(Oid) when is_list(Oid) -> 269 mib_of(snmp_master_agent, Oid). 270 271mib_of(Agent, Oid) when is_list(Oid) -> 272 call(Agent, {mib_of, Oid}). 273 274me_of(Oid) when is_list(Oid) -> 275 me_of(snmp_master_agent, Oid). 276 277me_of(Agent, Oid) when is_list(Oid) -> 278 call(Agent, {me_of, Oid}). 279 280 281invalidate_mibs_cache(Agent) -> 282 call(Agent, {mibs_cache_request, invalidate_cache}). 283 284 285gc_mibs_cache(Agent) -> 286 call(Agent, {mibs_cache_request, gc_cache}). 287 288gc_mibs_cache(Agent, Age) -> 289 call(Agent, {mibs_cache_request, {gc_cache, Age}}). 290 291gc_mibs_cache(Agent, Age, GcLimit) -> 292 call(Agent, {mibs_cache_request, {gc_cache, Age, GcLimit}}). 293 294 295enable_mibs_cache(Agent) -> 296 call(Agent, {mibs_cache_request, enable_cache}). 297 298disable_mibs_cache(Agent) -> 299 call(Agent, {mibs_cache_request, disable_cache}). 300 301 302which_mibs_cache_size(Agent) -> 303 call(Agent, {mibs_cache_request, cache_size}). 304 305 306enable_mibs_cache_autogc(Agent) -> 307 call(Agent, {mibs_cache_request, enable_autogc}). 308 309disable_mibs_cache_autogc(Agent) -> 310 call(Agent, {mibs_cache_request, disable_autogc}). 311 312 313update_mibs_cache_gclimit(Agent, GcLimit) -> 314 call(Agent, {mibs_cache_request, {update_gclimit, GcLimit}}). 315 316 317update_mibs_cache_age(Agent, Age) -> 318 call(Agent, {mibs_cache_request, {update_age, Age}}). 319 320 321increment_counter(Counter, Initial, Max) -> 322 %% This is to make sure no one else increments our counter 323 Key = {Counter, self()}, 324 325 %% Counter data 326 Position = 2, 327 Increment = 1, 328 Threshold = Max, 329 SetValue = Initial, 330 UpdateOp = {Position, Increment, Threshold, SetValue}, 331 332 %% And now for the actual increment 333 Tab = snmp_agent_table, 334 case (catch ets:update_counter(Tab, Key, UpdateOp)) of 335 {'EXIT', {badarg, _}} -> 336 %% Oups, first time 337 ets:insert(Tab, {Key, Initial}), 338 Initial; 339 Next when is_integer(Next) -> 340 Next 341 end. 342 343 344init([Prio, Parent, Ref, Options]) -> 345 ?d("init -> entry with" 346 "~n Prio: ~p" 347 "~n Parent: ~p" 348 "~n Ref: ~p" 349 "~n Options: ~p", [Prio, Parent, Ref, Options]), 350 case (catch do_init(Prio, Parent, Ref, Options)) of 351 {ok, State} -> 352 ?vdebug("started",[]), 353 {ok, State}; 354 {error, Reason} -> 355 config_err("failed starting agent: ~n~p", [Reason]), 356 {stop, Reason} 357 end. 358 359do_init(Prio, Parent, Ref, Options) -> 360 process_flag(priority, Prio), 361 put(sname,short_name(Parent)), 362 put(verbosity,get_verbosity(Options)), 363 ?vlog("starting with: " 364 "~n Prio: ~p" 365 "~n Parent: ~p" 366 "~n Ref: ~p" 367 "~n Options: ~p",[Prio, Parent, Ref, Options]), 368 369 Mibs = get_mibs(Options), 370 371 SetModule = get_set_mechanism(Options), 372 put(set_module, SetModule), 373 ?vtrace("set-module: ~w", [SetModule]), 374 375 GetModule = get_get_mechanism(Options), 376 put(get_module, GetModule), 377 ?vtrace("get-module: ~w", [GetModule]), 378 379 %% OTP-3324. For AXD301. 380 AuthModule = get_authentication_service(Options), 381 put(auth_module, AuthModule), 382 ?vtrace("auth-module: ~w", [AuthModule]), 383 384 MultiT = get_multi_threaded(Options), 385 Vsns = get_versions(Options), 386 387 GbMaxVbs = get_gb_max_vbs(Options), 388 389 NS = start_note_store(Prio, Ref, Options), 390 {Type, NetIfPid, NetIfMod} = 391 start_net_if(Parent, Prio, Ref, Vsns, NS, Options), 392 393 MibPid = start_mib_server(Prio, Ref, Mibs, Options), 394 395 put(net_if, NetIfPid), 396 put(mibserver, MibPid), 397 process_flag(trap_exit, true), 398 {Worker, SetWorker, NotifWorker} = workers_start(MultiT), 399 {ok, #state{type = Type, 400 parent = Parent, 401 worker = Worker, 402 set_worker = SetWorker, 403 notif_worker = NotifWorker, 404 multi_threaded = MultiT, 405 ref = Ref, 406 vsns = Vsns, 407 note_store = NS, 408 net_if_mod = NetIfMod, 409 gb_max_vbs = GbMaxVbs}}. 410 411 412start_note_store(Prio, Ref, Options) -> 413 ?vdebug("start_note_store -> with Prio: ~p", [Prio]), 414 NsOpts = get_note_store_opt(Options), 415 416 ?vtrace("start_note_store -> NsOpts: ~p", [NsOpts]), 417 418 case (catch snmpa_misc_sup:start_note_store(Prio, Ref, NsOpts)) of 419 {ok, Pid} -> 420 ?vdebug("start_note_store -> Pid: ~p", [Pid]), 421 Pid; 422 {error, Reason} -> 423 ?vinfo("error starting note store: ~n~p",[Reason]), 424 throw({error, {note_store_error, Reason}}); 425 {'EXIT', Reason} -> 426 ?vinfo("exit starting note store: ~n~p",[Reason]), 427 throw({error, {note_store_exit, Reason}}); 428 Error -> 429 ?vinfo("failed starting note store: ~n~p",[Error]), 430 throw({error, {note_store_failed, Error}}) 431 end. 432 433 434start_net_if(none, Prio, Ref, Vsns, NoteStore, Options) -> 435 ?vdebug("start_net_if(none) -> with Prio: ~p", [Prio]), 436 NetIfOpts = get_net_if_opt(Options), 437 Verbosity = get_net_if_verbosity(NetIfOpts), 438 Mod = get_net_if_module(NetIfOpts), 439 NiOptions = get_net_if_options(NetIfOpts), 440 NiOpts = [{versions, Vsns}, 441 {verbosity, Verbosity} | NiOptions], 442 443 ?vtrace("start_net_if -> " 444 "~n Mod: ~p" 445 "~n NiOpts: ~p",[Mod, NiOpts]), 446 447 case (catch snmpa_misc_sup:start_net_if(Prio, NoteStore, Ref, self(), 448 Mod, NiOpts)) of 449 {ok, Pid} -> 450 ?vdebug("start_net_if -> Pid: ~p", [Pid]), 451 {master_agent, Pid, Mod}; 452 {error, Reason} -> 453 ?vinfo("error starting net if: ~n~p",[Reason]), 454 throw({error, {net_if_error, Reason}}); 455 {'EXIT', Reason} -> 456 ?vinfo("exit starting net if: ~n~p",[Reason]), 457 throw({error, {net_if_exit, Reason}}); 458 Error -> 459 ?vinfo("failed starting net if: ~n~p",[Error]), 460 throw({error, {net_if_failed, Error}}) 461 end; 462start_net_if(Parent, _Prio, _Ref, _Vsns, _NoteStore, _Options) 463 when is_pid(Parent) -> 464 ?vdebug("start_net_if(~p) -> subagent => ignore", [Parent]), 465 {subagent, undefined, undefined}. 466 467 468start_mib_server(Prio, Ref, Mibs, Options) -> 469 ?vdebug("start_mib_server -> with Prio: ~p", [Prio]), 470 MibStorage = get_mib_storage(Options), 471 MibsOpts = [{mib_storage, MibStorage} | 472 get_option(mib_server, Options, [])], 473 474 ?vtrace("start_mib_server -> " 475 "~n Mibs: ~p" 476 "~n MibsOpts: ~p", [Mibs, MibsOpts]), 477 478 case (catch snmpa_misc_sup:start_mib_server(Prio, Ref, Mibs, MibsOpts)) of 479 {ok, Pid} -> 480 ?vdebug("start_mib_server -> Pid: ~p", [Pid]), 481 Pid; 482 {error, Reason} -> 483 ?vinfo("error starting mib server: ~n~p",[Reason]), 484 throw({error, {mib_server_error, Reason}}); 485 {'EXIT', Reason} -> 486 ?vinfo("exit starting mib server: ~n~p",[Reason]), 487 throw({error, {mib_server_exit, Reason}}); 488 Error -> 489 ?vinfo("failed starting mib server: ~n~p",[Error]), 490 throw({error, {mib_server_failed, Error}}) 491 end. 492 493 494%%----------------------------------------------------------------- 495%% Purpose: We must calculate the length of an empty Pdu. This 496%% length is used to calculate the max pdu size allowed 497%% for each get-bulk-request. This size is 498%% dependent on the varbinds. It is calculated 499%% as EmptySize + 8. 8 comes from the fact that the 500%% maximum pdu size needs 31 bits which needs 5 * 7 bits to be 501%% expressed. One 7bit octet is already present in the 502%% empty pdu, leaving 4 more 7bit octets. The length is 503%% repeated twice, once for the varbinds, and once for the 504%% entire pdu; 2 * 4 = 8. 505%% Actually, this function is not used, we use a constant instead. 506%%----------------------------------------------------------------- 507%% Ret: 21 508%% empty_pdu() -> 509%% Pdu = #pdu{type = 'get-response', 510%% request_id = 1, 511%% error_status = noError, 512%% error_index = 0, 513%% varbinds = []}, 514%% length(snmp_pdus:enc_pdu(Pdu)) + 8. 515 516 517%%%-------------------------------------------------- 518%%% 1. Interface 519%%%-------------------------------------------------- 520%% Called by administrator (not subagent; deadlock could occur) 521register_subagent(Agent, SubTreeOid, SubagentPid) -> 522 call(Agent, {register_subagent, SubTreeOid, SubagentPid}). 523 524%% Called by administrator (not subagent; deadlock could occur) 525unregister_subagent(Agent, SubagentOidOrPid) -> 526 call(Agent, {unregister_subagent, SubagentOidOrPid}). 527 528 529%%----------------------------------------------------------------- 530%% These subagent_ functions either return a value, or exits 531%% with {nodedown, Node} | Reason. 532%%----------------------------------------------------------------- 533 534%% A proper spec for this would be something like this: 535%% But, there is now way to spec that a process *can* exit. 536%% -spec subagent_get(Agent, VBs, IsNotification) -> 537%% {noError, 0, NewVBs} | 538%% {ErrStatus, ErrIndex, []} | 539%% erlang:exit(Reason) when 540%% Agent :: pid(), 541%% VBs :: [snmp:varbind()], 542%% IsNotification :: boolean(), 543%% NewVBs :: [snmp:varbind()], 544%% ErrStatus :: snmp:error_status(), 545%% ErrIndex :: snmp:error_index(), 546%% Reason :: {nodedown, Node} | term(), 547%% Node :: atom(). 548 549subagent_get(SubAgent, VBs, IsNotification) -> 550 PduData = get_pdu_data(), 551 call(SubAgent, {subagent_get, VBs, PduData, IsNotification}). 552 553subagent_get_next(SubAgent, MibView, Varbinds) -> 554 PduData = get_pdu_data(), 555 call(SubAgent, {subagent_get_next, MibView, Varbinds, PduData}). 556 557subagent_set(SubAgent, Arguments) -> 558 PduData = get_pdu_data(), 559 call(SubAgent, {subagent_set, Arguments, PduData}). 560 561 562%% Called by administrator (not agent; deadlock would occur) 563%% <BACKWARD-COMPAT> 564load_mibs(Agent, Mibs) -> 565 load_mibs(Agent, Mibs, false). 566%% </BACKWARD-COMPAT> 567 568load_mibs(Agent, Mibs, Force) -> 569 call(Agent, {load_mibs, Mibs, Force}). 570 571%% Called by administrator (not agent; deadlock would occur) 572%% <BACKWARD-COMPAT> 573unload_mibs(Agent, Mibs) -> 574 unload_mibs(Agent, Mibs, false). 575%% </BACKWARD-COMPAT> 576 577unload_mibs(Agent, Mibs, Force) -> 578 call(Agent, {unload_mibs, Mibs, Force}). 579 580which_mibs(Agent) -> 581 call(Agent, which_mibs). 582 583whereis_mib(Agent, Mib) -> 584 call(Agent, {whereis_mib, Mib}). 585 586info(Agent) -> 587 call(Agent, info). 588 589 590get_net_if(Agent) -> 591 call(Agent, get_net_if). 592 593 594register_notification_filter(Agent, Id, Mod, Args, Where) 595 when (Where =:= first) orelse (Where =:= last) -> 596 case (catch verify_notification_filter_behaviour(Mod)) of 597 ok -> 598 call(Agent, {register_notification_filter, Id, Mod, Args, Where}); 599 Error -> 600 Error 601 end; 602register_notification_filter(Agent, Id, Mod, Args, {Loc, _Id} = Where) 603 when (Loc =:= insert_before) orelse (Loc =:= insert_after) -> 604 case (catch verify_notification_filter_behaviour(Mod)) of 605 ok -> 606 call(Agent, {register_notification_filter, Id, Mod, Args, Where}); 607 Error -> 608 Error 609 end. 610 611verify_notification_filter_behaviour(Mod) -> 612 snmp_misc:verify_behaviour(snmpa_notification_filter, Mod). 613 614unregister_notification_filter(Agent, Id) -> 615 call(Agent, {unregister_notification_filter, Id}). 616 617which_notification_filter(Agent) -> 618 call(Agent, which_notification_filter). 619 620 621send_notification(Agent, Notification, SendOpts) -> 622 Msg = {send_notif, Notification, SendOpts}, 623 maybe_call(Agent, Msg). 624 625%% <BACKWARD-COMPAT> 626send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> 627 ?d("send_trap -> entry with" 628 "~n self(): ~p" 629 "~n Agent: ~p [~p]" 630 "~n Trap: ~p" 631 "~n NotifyName: ~p" 632 "~n CtxName: ~p" 633 "~n Recv: ~p" 634 "~n Varbinds: ~p", 635 [self(), Agent, wis(Agent), 636 Trap, NotifyName, CtxName, Recv, Varbinds]), 637 SendOpts = [ 638 {receiver, Recv}, 639 {varbinds, Varbinds}, 640 {name, NotifyName}, 641 {context, CtxName}, 642 {extra, ?DEFAULT_NOTIF_EXTRA_INFO} 643 ], 644 send_notification(Agent, Trap, SendOpts). 645 646send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> 647 ?d("send_trap -> entry with" 648 "~n self(): ~p" 649 "~n Agent: ~p [~p]" 650 "~n Trap: ~p" 651 "~n NotifyName: ~p" 652 "~n CtxName: ~p" 653 "~n Recv: ~p" 654 "~n Varbinds: ~p" 655 "~n LocalEngineID: ~p", 656 [self(), Agent, wis(Agent), 657 Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID]), 658 SendOpts = [ 659 {receiver, Recv}, 660 {varbinds, Varbinds}, 661 {name, NotifyName}, 662 {context, CtxName}, 663 {extra, ?DEFAULT_NOTIF_EXTRA_INFO}, 664 {local_engine_id, LocalEngineID} 665 ], 666 send_notification(Agent, Trap, SendOpts). 667 668%% </BACKWARD-COMPAT> 669 670 671%% -- Discovery functions -- 672 673disco_opts() -> 674 case ets:lookup(snmp_agent_table, discovery) of 675 [] -> 676 []; 677 [{discovery, DiscoOptions}] -> 678 DiscoOptions 679 end. 680 681originating_disco_opts() -> 682 DiscoOpts = disco_opts(), 683 case lists:keysearch(originating, 1, DiscoOpts) of 684 {value, {originating, OrigDisco}} -> 685 OrigDisco; 686 _ -> 687 [] 688 end. 689 690is_originating_discovery_enabled() -> 691 OrigDisco = originating_disco_opts(), 692 case lists:keysearch(enable, 1, OrigDisco) of 693 {value, {enable, false}} -> 694 false; 695 _ -> 696 true 697 end. 698 699terminating_disco_opts() -> 700 DiscoOpts = disco_opts(), 701 case lists:keysearch(terminating, 1, DiscoOpts) of 702 {value, {terminating, TermDisco}} -> 703 TermDisco; 704 _ -> 705 [] 706 end. 707 708is_terminating_discovery_enabled() -> 709 TermDisco = terminating_disco_opts(), 710 case lists:keysearch(enable, 1, TermDisco) of 711 {value, {enable, false}} -> 712 false; 713 _ -> 714 true 715 end. 716 717terminating_discovery_stage2() -> 718 Default = discovery, 719 TermDisco = terminating_disco_opts(), 720 case lists:keysearch(stage2, 1, TermDisco) of 721 {value, {stage2, Stage2}} when ((Stage2 =:= discovery) orelse (Stage2 =:= plain)) -> 722 Stage2; 723 _ -> 724 Default 725 end. 726 727terminating_trigger_username() -> 728 Default = ?DISCO_TERMINATING_TRIGGER_USERNAME, 729 TermDisco = terminating_disco_opts(), 730 case lists:keysearch(trigger_username, 1, TermDisco) of 731 {value, {trigger_username, Trigger}} when is_list(Trigger) -> 732 Trigger; 733 _ -> 734 Default 735 end. 736 737 738discovery(TargetName, Notification, ContextName, Varbinds, 739 DiscoHandler, ExtraInfo) -> 740 case is_originating_discovery_enabled() of 741 true -> 742 Agent = snmp_master_agent, 743 call(Agent, 744 {discovery, 745 TargetName, Notification, ContextName, Varbinds, 746 DiscoHandler, ExtraInfo}); 747 false -> 748 {error, not_enabled} 749 end. 750 751wis(Pid) when is_pid(Pid) -> 752 Pid; 753wis(Atom) when is_atom(Atom) -> 754 whereis(Atom). 755 756 757forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds, 758 ExtraInfo) -> 759 Agent ! {forward_trap, TrapRecord, NotifyName, CtxName, Recv, Varbinds, 760 ExtraInfo}. 761 762 763%%----------------------------------------------------------------- 764%% Args: Vars = [Oid] 765%% Returns: [Value] 766%% Called from a program to get variables. Don't call this from 767%% an instrumentation function; deadlock can occur! 768%%----------------------------------------------------------------- 769get(Agent, Vars) -> 770 call(Agent, {get, Vars, ""}). 771 772get(Agent, Vars, Context) -> 773 call(Agent, {get, Vars, Context}). 774 775get_next(Agent, Vars) -> 776 call(Agent, {get_next, Vars, ""}). 777 778get_next(Agent, Vars, Context) -> 779 call(Agent, {get_next, Vars, Context}). 780 781 782%%----------------------------------------------------------------- 783 784backup(Agent, BackupDir) when is_list(BackupDir) -> 785 call(Agent, {backup, BackupDir}). 786 787 788%%----------------------------------------------------------------- 789%% Runtime debug support. 790%%----------------------------------------------------------------- 791dump_mibs(Agent) -> 792 call(Agent, dump_mibs). 793dump_mibs(Agent, File) when is_list(File) -> 794 call(Agent, {dump_mibs, File}). 795 796 797%%----------------------------------------------------------------- 798%% Runtime debug (verbosity) support. 799%%----------------------------------------------------------------- 800verbosity(net_if,Verbosity) -> 801 cast(snmp_master_agent,{net_if_verbosity,Verbosity}); 802verbosity(mib_server,Verbosity) -> 803 cast(snmp_master_agent,{mib_server_verbosity,Verbosity}); 804verbosity(note_store,Verbosity) -> 805 cast(snmp_master_agent,{note_store_verbosity,Verbosity}); 806verbosity(sub_agents,Verbosity) -> 807 cast(snmp_master_agent,{sub_agents_verbosity,Verbosity}); 808verbosity(master_agent,Verbosity) -> 809 cast(snmp_master_agent,{verbosity,Verbosity}); 810verbosity(Agent,{sub_agents,Verbosity}) -> 811 cast(Agent,{sub_agents_verbosity,Verbosity}); 812verbosity(Agent,Verbosity) -> 813 cast(Agent,{verbosity,Verbosity}). 814 815 816%%%-------------------------------------------------- 817 818get_agent_mib_storage() -> 819 ets:lookup_element(snmp_agent_table, agent_mib_storage, 2). 820 821db(Tab) -> 822 {Tab, get_agent_mib_storage()}. 823 824 825%%%-------------------------------------------------- 826%%% 2. Main loop 827%%%-------------------------------------------------- 828%% gen_server:reply(From, Reply) 829handle_info({discovery_response, Response}, S) -> 830 ?vdebug("handle_info(discovery_response) -> entry with" 831 "~n Response: ~p", [Response]), 832 NewS = handle_discovery_response(S, Response), 833 {noreply, NewS}; 834 835handle_info({snmp_pdu, Vsn, Pdu, PduMS, ACMData, Address, Extra}, S) -> 836 ?vdebug("handle_info(snmp_pdu) -> entry with" 837 "~n Vsn: ~p" 838 "~n Pdu: ~p" 839 "~n Address: ~p" 840 "~n Extra: ~p", [Vsn, Pdu, Address, Extra]), 841 842 NewS = handle_snmp_pdu(is_valid_pdu_type(Pdu#pdu.type), 843 Vsn, Pdu, PduMS, ACMData, Address, Extra, S), 844 845 {noreply, NewS}; 846 847handle_info(worker_available, S) -> 848 ?vdebug("worker available",[]), 849 {noreply, S#state{worker_state = ready}}; 850 851handle_info({send_notif, Notification, SendOpts}, S) -> 852 ?vlog("[handle_info] send notif request:" 853 "~n Notification: ~p" 854 "~n SendOpts: ~p", 855 [Notification, SendOpts]), 856 case (catch handle_send_trap(S, Notification, SendOpts)) of 857 {ok, NewS} -> 858 {noreply, NewS}; 859 {'EXIT', R} -> 860 ?vinfo("Trap not sent:~n ~p", [R]), 861 {noreply, S}; 862 _ -> 863 {noreply, S} 864 end; 865 866%% <BACKWARD-COMPAT> 867handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) -> 868 ?vlog("[handle_info] send trap request:" 869 "~n Trap: ~p" 870 "~n NotifyName: ~p" 871 "~n ContextName: ~p" 872 "~n Recv: ~p" 873 "~n Varbinds: ~p", 874 [Trap, NotifyName, ContextName, Recv, Varbinds]), 875 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 876 LocalEngineID = local_engine_id(S), 877 case (catch handle_send_trap(S, Trap, NotifyName, ContextName, 878 Recv, Varbinds, LocalEngineID, ExtraInfo)) of 879 {ok, NewS} -> 880 {noreply, NewS}; 881 {'EXIT', R} -> 882 ?vinfo("Trap not sent:~n ~p", [R]), 883 {noreply, S}; 884 _ -> 885 {noreply, S} 886 end; 887 888handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds, 889 LocalEngineID}, S) -> 890 ?vlog("[handle_info] send trap request:" 891 "~n Trap: ~p" 892 "~n NotifyName: ~p" 893 "~n ContextName: ~p" 894 "~n Recv: ~p" 895 "~n Varbinds: ~p" 896 "~n LocalEngineID: ~p", 897 [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]), 898 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 899 case (catch handle_send_trap(S, Trap, NotifyName, ContextName, 900 Recv, Varbinds, LocalEngineID, ExtraInfo)) of 901 {ok, NewS} -> 902 {noreply, NewS}; 903 {'EXIT', R} -> 904 ?vinfo("Trap not sent:~n ~p", [R]), 905 {noreply, S}; 906 _ -> 907 {noreply, S} 908 end; 909%% </BACKWARD-COMPAT> 910 911handle_info({forward_trap, TrapRecord, NotifyName, ContextName, 912 Recv, Varbinds, ExtraInfo}, S) -> 913 ?vlog("[handle_info] forward trap request:" 914 "~n TrapRecord: ~p" 915 "~n NotifyName: ~p" 916 "~n ContextName: ~p" 917 "~n Recv: ~p" 918 "~n Varbinds: ~p", 919 [TrapRecord, NotifyName, ContextName, Recv, Varbinds]), 920 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 921 case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName, 922 Recv, Varbinds, LocalEngineID, ExtraInfo)) of 923 {ok, NewS} -> 924 {noreply, NewS}; 925 {'EXIT', R} -> 926 ?vinfo("Trap not sent:~n ~p", [R]), 927 {noreply, S}; 928 _ -> 929 {noreply, S} 930 end; 931 932%% <BACKWARD-COMPAT> 933handle_info({forward_trap, TrapRecord, NotifyName, ContextName, 934 Recv, Varbinds}, S) -> 935 ?vlog("[handle_info] forward trap request:" 936 "~n TrapRecord: ~p" 937 "~n NotifyName: ~p" 938 "~n ContextName: ~p" 939 "~n Recv: ~p" 940 "~n Varbinds: ~p", 941 [TrapRecord, NotifyName, ContextName, Recv, Varbinds]), 942 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 943 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 944 case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName, 945 Recv, Varbinds, LocalEngineID, ExtraInfo)) of 946 {ok, NewS} -> 947 {noreply, NewS}; 948 {'EXIT', R} -> 949 ?vinfo("Trap not sent:~n ~p", [R]), 950 {noreply, S}; 951 _ -> 952 {noreply, S} 953 end; 954%% </BACKWARD-COMPAT> 955 956handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) -> 957 ?vlog("[handle_info] backup done:" 958 "~n Reply: ~p", [Reply]), 959 gen_server:reply(From, Reply), 960 {noreply, S#state{backup = undefined}}; 961 962 963handle_info(invalidate_ca_cache, S) -> 964 invalidate_ca_cache(), 965 {noreply, S}; 966 967 968%%----------------------------------------------------------------- 969%% If a process crashes, we first check to see if it was the mib, 970%% net-if or note-store. 971%% Otherwise, we check to see if it was a subagent. In this case 972%% we unregister the sa, and unlink us from the sa. 973%%----------------------------------------------------------------- 974handle_info({'EXIT', Pid, Reason}, #state{note_store = Pid} = S) -> 975 ?vlog("note store (~p) exited for reason ~n~p", [Pid, Reason]), 976 error_msg("note-store exited: ~n~p", [Reason]), 977 {stop, {note_store_exit, Reason}, S#state{note_store = undefined}}; 978handle_info({'EXIT', Pid, Reason}, #state{worker = Pid} = S) -> 979 ?vlog("worker (~p) exited -> create new ~n ~p", [Pid, Reason]), 980 NewWorker = worker_start(), 981 {noreply, S#state{worker = NewWorker}}; 982handle_info({'EXIT', Pid, Reason}, #state{set_worker = Pid} = S) -> 983 ?vlog("set-worker (~p) exited -> create new ~n ~p", [Pid, Reason]), 984 NewWorker = set_worker_start(), 985 {noreply, S#state{set_worker = NewWorker}}; 986handle_info({'EXIT', Pid, Reason}, #state{notif_worker = Pid} = S) -> 987 ?vlog("notif-worker (~p) exited -> create new ~n ~p", [Pid, Reason]), 988 NewWorker = notif_worker_start(), 989 {noreply, S#state{notif_worker = NewWorker}}; 990handle_info({'EXIT', Pid, Reason}, #state{parent = Pid} = S) -> 991 ?vlog("parent (~p) exited for reason ~n~p", [Pid,Reason]), 992 {stop, {parent_died, Reason}, S}; 993handle_info({'EXIT', Pid, Reason}, #state{backup = {Pid, From}} = S) -> 994 ?vlog("backup server (~p) exited for reason ~n~p", [Pid, Reason]), 995 case Reason of 996 normal -> 997 {noreply, S}; 998 _ -> 999 gen_server:reply(From, {error, Reason}), 1000 {noreply, S#state{backup = undefined}} 1001 end; 1002handle_info({'EXIT', Pid, Reason}, S) -> 1003 ?vlog("~p exited for reason ~p", [Pid, Reason]), 1004 Mib = get(mibserver), 1005 NetIf = get(net_if), 1006 case Pid of 1007 Mib -> 1008 error_msg("mib-server exited: ~n~p", [Reason]), 1009 {stop, {mib_server_exit, Reason}, S}; 1010 NetIf -> 1011 error_msg("net-if exited: ~n~p", [Reason]), 1012 {stop, {net_if_exit, Reason}, S}; 1013 _ -> 1014 %% Could be a sub-agent 1015 SAs = snmpa_mib:info(Mib, subagents), 1016 case lists:keysearch(Pid, 1, SAs) of 1017 {value, _} -> 1018 ?vlog("subagent exit", []), 1019 snmpa_mib:unregister_subagent(Mib, Pid), 1020 unlink(Pid); 1021 _ -> 1022 %% Otherwise it was probably a worker thread - ignore 1023 ok 1024 end, 1025 {noreply, S} 1026 end; 1027 1028handle_info({'DOWN', Ref, process, Pid, {mibs_cache_reply, Reply}}, 1029 #state{mibs_cache_request = {Pid, Ref, From}} = S) -> 1030 ?vlog("reply from the mibs cache request handler (~p): ~n~p", 1031 [Pid, Reply]), 1032 gen_server:reply(From, Reply), 1033 {noreply, S#state{mibs_cache_request = undefined}}; 1034 1035handle_info(Info, S) -> 1036 warning_msg("received unexpected info: ~n~p", [Info]), 1037 {noreply, S}. 1038 1039handle_call(restart_worker, _From, #state{worker = Pid} = S) -> 1040 if 1041 is_pid(Pid) -> 1042 ?vlog("[handle_call] restart worker ~p", [Pid]), 1043 exit(Pid, kill); 1044 true -> 1045 ?vlog("[handle_call] not multi-threaded => " 1046 "ignoring restart request", []), 1047 ok 1048 end, 1049 {reply, ok, S}; 1050handle_call(restart_set_worker, _From, #state{set_worker = Pid} = S) -> 1051 if 1052 is_pid(Pid) -> 1053 ?vlog("[handle_call] restart set worker: ~p", [Pid]), 1054 exit(Pid, kill); 1055 true -> 1056 ?vlog("[handle_call] not multi-threaded => " 1057 "ignoring restart request", []), 1058 ok 1059 end, 1060 {reply, ok, S}; 1061 1062handle_call({send_notif, Notification, SendOpts}, _From, S) -> 1063 ?vlog("[handle_call] send notif request:" 1064 "~n Notification: ~p" 1065 "~n SendOpts: ~p", 1066 [Notification, SendOpts]), 1067 case (catch handle_send_trap(S, Notification, SendOpts)) of 1068 {ok, NewS} -> 1069 {reply, ok, NewS}; 1070 {'EXIT', Reason} -> 1071 ?vinfo("Trap not sent:~n ~p", [Reason]), 1072 {reply, {error, {send_failed, Reason}}, S}; 1073 _ -> 1074 ?vinfo("Trap not sent", []), 1075 {reply, {error, send_failed}, S} 1076 end; 1077 1078%% <BACKWARD-COMPAT> 1079handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, 1080 _From, S) -> 1081 ?vlog("[handle_call] send trap request:" 1082 "~n Trap: ~p" 1083 "~n NotifyName: ~p" 1084 "~n ContextName: ~p" 1085 "~n Recv: ~p" 1086 "~n Varbinds: ~p", 1087 [Trap, NotifyName, ContextName, Recv, Varbinds]), 1088 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 1089 LocalEngineID = local_engine_id(S), 1090 case (catch handle_send_trap(S, Trap, NotifyName, ContextName, 1091 Recv, Varbinds, LocalEngineID, ExtraInfo)) of 1092 {ok, NewS} -> 1093 {reply, ok, NewS}; 1094 {'EXIT', Reason} -> 1095 ?vinfo("Trap not sent:~n ~p", [Reason]), 1096 {reply, {error, {send_failed, Reason}}, S}; 1097 _ -> 1098 ?vinfo("Trap not sent", []), 1099 {reply, {error, send_failed}, S} 1100 end; 1101 1102handle_call({send_trap, Trap, NotifyName, 1103 ContextName, Recv, Varbinds, LocalEngineID}, 1104 _From, S) -> 1105 ?vlog("[handle_call] send trap request:" 1106 "~n Trap: ~p" 1107 "~n NotifyName: ~p" 1108 "~n ContextName: ~p" 1109 "~n Recv: ~p" 1110 "~n Varbinds: ~p" 1111 "~n LocalEngineID: ~p", 1112 [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]), 1113 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 1114 case (catch handle_send_trap(S, Trap, NotifyName, ContextName, 1115 Recv, Varbinds, LocalEngineID, ExtraInfo)) of 1116 {ok, NewS} -> 1117 {reply, ok, NewS}; 1118 {'EXIT', Reason} -> 1119 ?vinfo("Trap not sent:~n ~p", [Reason]), 1120 {reply, {error, {send_failed, Reason}}, S}; 1121 _ -> 1122 ?vinfo("Trap not sent", []), 1123 {reply, {error, send_failed}, S} 1124 end; 1125%% </BACKWARD-COMPAT> 1126 1127handle_call({discovery, 1128 TargetName, Notification, ContextName, Vbs, DiscoHandler, 1129 ExtraInfo}, 1130 From, 1131 #state{disco = undefined} = S) -> 1132 ?vlog("[handle_call] initiate discovery process:" 1133 "~n TargetName: ~p" 1134 "~n Notification: ~p" 1135 "~n ContextName: ~p" 1136 "~n Vbs: ~p" 1137 "~n DiscoHandler: ~p" 1138 "~n ExtraInfo: ~p", 1139 [TargetName, Notification, ContextName, Vbs, 1140 DiscoHandler, ExtraInfo]), 1141 case handle_discovery(S, From, TargetName, 1142 Notification, ContextName, Vbs, DiscoHandler, 1143 ExtraInfo) of 1144 {ok, NewS} -> 1145 ?vtrace("[handle_call] first stage of discovery process initiated", 1146 []), 1147 {noreply, NewS}; 1148 {error, _} = Error -> 1149 {reply, Error, S} 1150 end; 1151handle_call({discovery, _TargetName, _Notification, _ContextName, _Vbs, _DiscoHandler, _ExtraInfo}, _From, 1152 #state{disco = DiscoData} = S) -> 1153 Reply = {error, {discovery_in_progress, DiscoData}}, 1154 {reply, Reply, S}; 1155handle_call({subagent_get, Varbinds, PduData, IsNotification}, _From, S) -> 1156 ?vlog("[handle_call] subagent get:" 1157 "~n Varbinds: ~p" 1158 "~n PduData: ~p", 1159 [Varbinds,PduData]), 1160 put_pdu_data(PduData), 1161 {reply, do_get(Varbinds, IsNotification), S}; 1162handle_call({subagent_get_next, MibView, Varbinds, PduData}, _From, S) -> 1163 ?vlog("[handle_call] subagent get-next:" 1164 "~n MibView: ~p" 1165 "~n Varbinds: ~p" 1166 "~n PduData: ~p", 1167 [MibView,Varbinds,PduData]), 1168 put_pdu_data(PduData), 1169 {reply, do_get_next(MibView, Varbinds), S}; 1170handle_call({subagent_set, Arguments, PduData}, _From, S) -> 1171 ?vlog("[handle_call] subagent set:" 1172 "~n Arguments: ~p" 1173 "~n PduData: ~p", 1174 [Arguments,PduData]), 1175 put_pdu_data(PduData), 1176 {reply, do_subagent_set(Arguments), S}; 1177 1178handle_call({get, Vars, Context}, _From, S) -> 1179 ?vlog("[handle_call] get:" 1180 "~n Vars: ~p" 1181 "~n Context: ~p", [Vars, Context]), 1182 put_pdu_data({undefined, undefined, undefined, undefined, Context}), 1183 case catch mapfoldl(fun ?MODULE:tr_var/2, [], 1, Vars) of 1184 {error, Reason} -> {reply, {error, Reason}, S}; 1185 {_, Varbinds} -> 1186 ?vdebug("Varbinds: ~p",[Varbinds]), 1187 Reply = 1188 case do_get(Varbinds, false) of 1189 {noError, 0, NewVarbinds} -> 1190 Vbs = lists:keysort(#varbind.org_index, NewVarbinds), 1191 [Value || #varbind{value = Value} <- Vbs]; 1192 {ErrorStatus, ErrIndex, _} -> 1193 N = lists:nth(ErrIndex, Vars), 1194 {error, {ErrorStatus, N}} 1195 end, 1196 {reply, Reply, S} 1197 end; 1198 1199handle_call({get_next, Vars, Context}, _From, S) -> 1200 ?vlog("[handle_call] get_next:" 1201 "~n Vars: ~p" 1202 "~n Context: ~p",[Vars, Context]), 1203 put_pdu_data({undefined, undefined, undefined, undefined, Context}), 1204 case catch mapfoldl(fun ?MODULE:tr_var/2, [], 1, Vars) of 1205 {error, Reason} -> {reply, {error, Reason}, S}; 1206 {_, Varbinds} -> 1207 ?vdebug("Varbinds: ~p",[Varbinds]), 1208 MibView = snmpa_acm:get_root_mib_view(), 1209 Reply = 1210 case do_get_next(MibView, Varbinds) of 1211 {noError, 0, NewVarbinds} -> 1212 Vbs = lists:keysort(#varbind.org_index, NewVarbinds), 1213 [{Oid,Val} || #varbind{oid = Oid, value = Val} <- Vbs]; 1214 {ErrorStatus, ErrIndex, _} -> 1215 N = lists:nth(ErrIndex, Vars), 1216 {error, {ErrorStatus, N}} 1217 end, 1218 {reply, Reply, S} 1219 end; 1220 1221handle_call({do_get, MibView, UnsortedVarbinds, IsNotification, PduData}, 1222 _From, S) -> 1223 ?vlog("[handle_call] do_get:" 1224 "~n MibView: ~p" 1225 "~n UnsortedVarbinds: ~p" 1226 "~n IsNotification: ~p" 1227 "~n PduData: ~p", 1228 [MibView, UnsortedVarbinds, IsNotification, PduData]), 1229 put_pdu_data(PduData), 1230 Reply = do_get(MibView, UnsortedVarbinds, IsNotification), 1231 {reply, Reply, S}; 1232 1233handle_call({register_subagent, SubTreeOid, SubagentPid}, _From, S) -> 1234 Reply = 1235 case snmpa_mib:register_subagent(get(mibserver), 1236 SubTreeOid, SubagentPid) of 1237 ok -> link(SubagentPid), ok; 1238 Error -> Error 1239 end, 1240 {reply, Reply, S}; 1241 1242handle_call({unregister_subagent, SubagentPid}, _From, S) 1243 when is_pid(SubagentPid) -> 1244 ?vlog("[handle_call] unregister subagent ~p", [SubagentPid]), 1245 Reply = snmpa_mib:unregister_subagent(get(mibserver), SubagentPid), 1246 unlink(SubagentPid), 1247 {reply, Reply, S}; 1248 1249handle_call({unregister_subagent, SubTreeOid}, _From, S) -> 1250 ?vlog("[handle_call] unregister subagent ~p", [SubTreeOid]), 1251 Reply = 1252 case snmpa_mib:unregister_subagent(get(mibserver), SubTreeOid) of 1253 {ok, DeletedSubagentPid} -> 1254 SAs = snmpa_mib:info(get(mibserver), subagents), 1255 case lists:keysearch(DeletedSubagentPid, 1, SAs) of 1256 {value, _} -> ok; 1257 _ -> unlink(DeletedSubagentPid) 1258 end, 1259 ok; 1260 Error -> 1261 Error 1262 end, 1263 {reply, Reply, S}; 1264 1265%% <BACKWARD-COMPAT> 1266handle_call({load_mibs, Mibs}, _From, S) -> 1267 ?vlog("load mibs ~p", [Mibs]), 1268 {reply, snmpa_mib:load_mibs(get(mibserver), Mibs), S}; 1269%% </BACKWARD-COMPAT> 1270 1271handle_call({load_mibs, Mibs, Force}, _From, S) -> 1272 ?vlog("[~w] load mibs ~p", [Force, Mibs]), 1273 {reply, snmpa_mib:load_mibs(get(mibserver), Mibs, Force), S}; 1274 1275%% <BACKWARD-COMPAT> 1276handle_call({unload_mibs, Mibs}, _From, S) -> 1277 ?vlog("unload mibs ~p", [Mibs]), 1278 {reply, snmpa_mib:unload_mibs(get(mibserver), Mibs), S}; 1279%% </BACKWARD-COMPAT> 1280 1281handle_call({unload_mibs, Mibs, Force}, _From, S) -> 1282 ?vlog("[~w] unload mibs ~p", [Force, Mibs]), 1283 {reply, snmpa_mib:unload_mibs(get(mibserver), Mibs, Force), S}; 1284 1285handle_call(which_mibs, _From, S) -> 1286 ?vlog("which mibs", []), 1287 {reply, snmpa_mib:which_mibs(get(mibserver)), S}; 1288 1289handle_call({whereis_mib, Mib}, _From, S) -> 1290 ?vlog("whereis mib ~p", [Mib]), 1291 {reply, snmpa_mib:whereis_mib(get(mibserver), Mib), S}; 1292 1293handle_call({mibs_cache_request, MibsCacheReq}, From, S) -> 1294 ?vlog("mibs_cache_request: ~p", [MibsCacheReq]), 1295 {MibsCacheWorker, Ref} = 1296 handle_mibs_cache_request(get(mibserver), MibsCacheReq), 1297 NewS = S#state{mibs_cache_request = {MibsCacheWorker, Ref, From}}, 1298 {noreply, NewS}; 1299 1300handle_call(info, _From, S) -> 1301 ?vlog("info", []), 1302 Vsns = S#state.vsns, 1303 Stats = get_stats_counters(), 1304 AI = agent_info(S), 1305 NI = net_if_info(S), 1306 NS = note_store_info(S), 1307 SS = symbolic_store_info(), 1308 LD = local_db_info(), 1309 MS = mib_server_info(), 1310 Info = [{vsns, Vsns}, 1311 {stats_counters, Stats}, 1312 {agent, AI}, 1313 {net_if, NI}, 1314 {note_store, NS}, 1315 {symbolic_store, SS}, 1316 {local_db, LD}, 1317 {mib_server, MS}], 1318 {reply, Info, S}; 1319 1320handle_call(get_net_if, _From, S) -> 1321 {reply, get(net_if), S}; 1322 1323%% Only accept a backup request if there is none already in progress 1324handle_call({backup, BackupDir}, From, #state{backup = undefined} = S) -> 1325 ?vlog("backup: ~p", [BackupDir]), 1326 Pid = self(), 1327 V = get(verbosity), 1328 MS = get(mibserver), 1329 BackupServer = 1330 erlang:spawn_link( 1331 fun() -> 1332 put(sname, abs), 1333 put(verbosity, V), 1334 Dir = filename:join([BackupDir]), 1335 Reply = handle_backup(Dir, MS), 1336 Pid ! {backup_done, Reply}, 1337 unlink(Pid) 1338 end), 1339 ?vtrace("backup server: ~p", [BackupServer]), 1340 {noreply, S#state{backup = {BackupServer, From}}}; 1341 1342handle_call({backup, _BackupDir}, _From, #state{backup = Backup} = S) -> 1343 ?vinfo("backup already in progress: ~p", [Backup]), 1344 {reply, {error, backup_in_progress}, S}; 1345 1346handle_call(dump_mibs, _From, S) -> 1347 Reply = snmpa_mib:dump(get(mibserver)), 1348 {reply, Reply, S}; 1349 1350handle_call({dump_mibs,File}, _From, S) -> 1351 Reply = snmpa_mib:dump(get(mibserver),File), 1352 {reply, Reply, S}; 1353 1354handle_call({register_notification_filter, Id, Mod, Data, Where}, _From, 1355 #state{nfilters = NFs} = S) -> 1356 ?vlog("register_notification_filter -> " 1357 "~n Id: ~p" 1358 "~n Mod: ~p" 1359 "~n Where: ~p", [Id, Mod, Where]), 1360 case lists:keymember(Id, 2, NFs) of 1361 true -> 1362 {reply, {error, {already_registered, Id}}, S}; 1363 false -> 1364 NF = #notification_filter{id = Id, mod = Mod, data = Data}, 1365 {Reply, NewNFs} = add_notification_filter(Where, NF, NFs), 1366 {reply, Reply, S#state{nfilters = NewNFs}} 1367 end; 1368 1369handle_call({unregister_notification_filter, Id}, _From, 1370 #state{nfilters = NFs} = S) -> 1371 ?vlog("unregister_notification_filter -> " 1372 "~n Id: ~p", [Id]), 1373 case lists:keydelete(Id, 2, NFs) of 1374 NFs -> 1375 {reply, {error, {not_found, Id}}, S}; 1376 NFs2 -> 1377 {reply, ok, S#state{nfilters = NFs2}} 1378 end; 1379 1380handle_call(which_notification_filter, _From, 1381 #state{nfilters = NFs} = S) -> 1382 ?vlog("which_notification_filter", []), 1383 {reply, [Id || #notification_filter{id = Id} <- NFs], S}; 1384 1385handle_call({mib_of, Oid}, _From, S) -> 1386 Reply = handle_mib_of(get(mibserver), Oid), 1387 {reply, Reply, S}; 1388 1389handle_call({me_of, Oid}, _From, S) -> 1390 Reply = handle_me_of(get(mibserver), Oid), 1391 {reply, Reply, S}; 1392 1393handle_call(get_log_type, _From, S) -> 1394 ?vlog("handle_call(get_log_type) -> entry with", []), 1395 Reply = handle_get_log_type(S), 1396 {reply, Reply, S}; 1397 1398handle_call({set_log_type, NewType}, _From, S) -> 1399 ?vlog("handle_call(set_log_type) -> entry with" 1400 "~n NewType: ~p", [NewType]), 1401 Reply = handle_set_log_type(S, NewType), 1402 {reply, Reply, S}; 1403 1404handle_call(get_request_limit, _From, S) -> 1405 ?vlog("handle_call(get_request_limit) -> entry with", []), 1406 Reply = handle_get_request_limit(S), 1407 {reply, Reply, S}; 1408 1409handle_call({set_request_limit, NewLimit}, _From, S) -> 1410 ?vlog("handle_call(set_request_limit) -> entry with" 1411 "~n NewLimit: ~p", [NewLimit]), 1412 Reply = handle_set_request_limit(S, NewLimit), 1413 {reply, Reply, S}; 1414 1415handle_call(stop, _From, S) -> 1416 {stop, normal, ok, S}; 1417 1418handle_call(Req, _From, S) -> 1419 warning_msg("received unknown request: ~n~p", [Req]), 1420 Reply = {error, {unknown, Req}}, 1421 {reply, Reply, S}. 1422 1423handle_cast({verbosity, Verbosity}, S) -> 1424 ?vlog("verbosity: ~p -> ~p",[get(verbosity), Verbosity]), 1425 put(verbosity,snmp_verbosity:validate(Verbosity)), 1426 case S#state.worker of 1427 Pid1 when is_pid(Pid1) -> Pid1 ! ?mk_verbosity_wreq(Verbosity); 1428 _ -> ok 1429 end, 1430 case S#state.set_worker of 1431 Pid2 when is_pid(Pid2) -> Pid2 ! ?mk_verbosity_wreq(Verbosity); 1432 _ -> ok 1433 end, 1434 case S#state.notif_worker of 1435 Pid3 when is_pid(Pid3) -> Pid3 ! ?mk_verbosity_wreq(Verbosity); 1436 _ -> ok 1437 end, 1438 {noreply, S}; 1439 1440handle_cast({sub_agents_verbosity,Verbosity}, S) -> 1441 ?vlog("sub_agents verbosity: ~p",[Verbosity]), 1442 subagents_verbosity(Verbosity), 1443 {noreply, S}; 1444 1445%% This should only happen if we are a master_agent 1446handle_cast({net_if_verbosity, Verbosity}, S) -> 1447 net_if_verbosity(get(net_if), Verbosity), 1448 {noreply, S}; 1449 1450handle_cast({mib_server_verbosity, Verbosity}, S) -> 1451 mib_server_verbosity(get(mibserver),Verbosity), 1452 {noreply, S}; 1453 1454handle_cast({note_store_verbosity, Verbosity}, #state{note_store = Pid} = S) -> 1455 note_store_verbosity(Pid,Verbosity), 1456 {noreply, S}; 1457 1458handle_cast(Msg, S) -> 1459 warning_msg("received unknown message: ~n~p", [Msg]), 1460 {noreply, S}. 1461 1462 1463terminate(shutdown, #state{worker = Worker, 1464 set_worker = SetWorker, 1465 notif_worker = NotifWorker, 1466 backup = Backup, 1467 ref = Ref}) -> 1468 %% Ordered shutdown - stop misc-workers, net_if, mib-server and note-store. 1469 backup_server_stop(Backup), 1470 worker_stop(Worker, 100), 1471 worker_stop(SetWorker, 100), 1472 worker_stop(NotifWorker, 100), 1473 snmpa_misc_sup:stop_net_if(Ref), 1474 snmpa_misc_sup:stop_mib_server(Ref); 1475terminate(_Reason, _S) -> 1476 %% We crashed! We will reuse net_if and mib if we get restarted. 1477 ok. 1478 1479 1480handle_mibs_cache_request(MibServer, Req) -> 1481 {MibsCacheWorker, MibsCacheRef} = 1482 spawn_monitor( 1483 fun() -> 1484 Reply = 1485 case Req of 1486 invalidate_cache -> 1487 snmpa_mib:invalidate_cache(MibServer); 1488 gc_cache -> 1489 snmpa_mib:gc_cache(MibServer); 1490 {gc_cache, Age} -> 1491 snmpa_mib:gc_cache(MibServer, Age); 1492 {gc_cache, Age, GcLimit} -> 1493 snmpa_mib:gc_cache(MibServer, Age, GcLimit); 1494 cache_size -> 1495 snmpa_mib:which_cache_size(MibServer); 1496 enable_cache -> 1497 snmpa_mib:enable_cache(MibServer); 1498 disable_cache -> 1499 snmpa_mib:disable_cache(MibServer); 1500 enable_autogc -> 1501 snmpa_mib:enable_cache_autogc(MibServer); 1502 disable_autogc -> 1503 snmpa_mib:disable_cache_autogc(MibServer); 1504 {update_gclimit, GcLimit} -> 1505 snmpa_mib:update_cache_gclimit(MibServer, 1506 GcLimit); 1507 {update_age, Age} -> 1508 snmpa_mib:update_cache_age(MibServer, Age); 1509 _ -> 1510 {error, {unknown_mibs_cache_request, Req}} 1511 end, 1512 exit({mibs_cache_reply, Reply}) 1513 end), 1514 {MibsCacheWorker, MibsCacheRef}. 1515 1516 1517%%----------------------------------------------------------------- 1518%% Code replacement 1519%% 1520%%----------------------------------------------------------------- 1521 1522%% Downgrade 1523%% 1524%% code_change({down, _Vsn}, S1, downgrade_to_pre_4_17_3) -> 1525%% #state{type = Type, 1526%% parent = Parent, 1527%% worker = Worker, 1528%% worker_state = WorkerState, 1529%% set_worker = SetWorker, 1530%% multi_threaded = MT, 1531%% ref = Ref, 1532%% vsns = Vsns, 1533%% nfilters = NF, 1534%% note_store = NoteStore, 1535%% mib_server = MS, 1536%% net_if = NetIf, 1537%% net_if_mod = NetIfMod, 1538%% backup = Backup, 1539%% disco = Disco, 1540%% mibs_cache_request = MCR} = S1, 1541%% S2 = {state, 1542%% type = Type, 1543%% parent = Parent, 1544%% worker = Worker, 1545%% worker_state = WorkerState, 1546%% set_worker = SetWorker, 1547%% multi_threaded = MT, 1548%% ref = Ref, 1549%% vsns = Vsns, 1550%% nfilters = NF, 1551%% note_store = NoteStore, 1552%% mib_server = MS, 1553%% net_if = NetIf, 1554%% net_if_mod = NetIfMod, 1555%% backup = Backup, 1556%% disco = Disco, 1557%% mibs_cache_request = MCR}, 1558%% {ok, S2}; 1559 1560%% %% Upgrade 1561%% %% 1562%% code_change(_Vsn, S1, upgrade_from_pre_4_17_3) -> 1563%% {state, 1564%% type = Type, 1565%% parent = Parent, 1566%% worker = Worker, 1567%% worker_state = WorkerState, 1568%% set_worker = SetWorker, 1569%% multi_threaded = MT, 1570%% ref = Ref, 1571%% vsns = Vsns, 1572%% nfilters = NF, 1573%% note_store = NoteStore, 1574%% mib_server = MS, 1575%% net_if = NetIf, 1576%% net_if_mod = NetIfMod, 1577%% backup = Backup, 1578%% disco = Disco, 1579%% mibs_cache_request = MCR} = S1, 1580%% S2 = #state{type = Type, 1581%% parent = Parent, 1582%% worker = Worker, 1583%% worker_state = WorkerState, 1584%% set_worker = SetWorker, 1585%% multi_threaded = MT, 1586%% ref = Ref, 1587%% vsns = Vsns, 1588%% nfilters = NF, 1589%% note_store = NoteStore, 1590%% mib_server = MS, 1591%% net_if = NetIf, 1592%% net_if_mod = NetIfMod, 1593%% backup = Backup, 1594%% disco = Disco, 1595%% mibs_cache_request = MCR, 1596%% gb_max_vbs = ?DEFAULT_GB_MAX_VBS}, 1597%% {ok, S2}; 1598 1599code_change(_Vsn, S, _Extra) -> 1600 {ok, S}. 1601 1602 1603%% workers_restart(#state{worker = W, set_worker = SW} = S) -> 1604%% Worker = worker_restart(W), 1605%% SetWorker = set_worker_restart(SW), 1606%% S#state{worker = Worker, 1607%% set_worker = SetWorker}. 1608 1609 1610%%----------------------------------------------------------------- 1611 1612backup_server_stop({Pid, _}) when is_pid(Pid) -> 1613 exit(Pid, kill); 1614backup_server_stop(_) -> 1615 ok. 1616 1617 1618workers_start(extended) -> 1619 ?vdebug("start worker, set-worker and notif-worker", []), 1620 {worker_start(), set_worker_start(), notif_worker_start()}; 1621workers_start(true) -> 1622 ?vdebug("start worker and set-worker", []), 1623 {worker_start(), set_worker_start(), undefined}; 1624workers_start(_) -> 1625 {undefined, undefined, undefined}. 1626 1627worker_start() -> 1628 worker_start(mw, true, get()). 1629 1630set_worker_start() -> 1631 worker_start(sw, false, [{master, self()} | get()]). 1632 1633notif_worker_start() -> 1634 worker_start(nw, false, [{master, self()} | get()]). 1635 1636worker_start(SName, Report, Dict) -> 1637 proc_lib:spawn_link(?MODULE, worker, [self(), SName, Report, Dict]). 1638 1639%% worker_stop(Pid) -> 1640%% worker_stop(Pid, infinity). 1641 1642worker_stop(Pid, Timeout) when is_pid(Pid) -> 1643 Pid ! ?mk_terminate_wreq(), 1644 receive 1645 {'EXIT', Pid, normal} -> 1646 ok 1647 after Timeout -> 1648 (catch exit(Pid, kill)), 1649 ok 1650 end; 1651worker_stop(_, _) -> 1652 ok. 1653 1654%% set_worker_restart(Pid) -> 1655%% worker_restart(Pid, [{master, self()} | get()]). 1656 1657%% worker_restart(Pid) -> 1658%% worker_restart(Pid, get()). 1659 1660%% worker_restart(Pid, Dict) when is_pid(Pid) -> 1661%% worker_stop(Pid), 1662%% worker_start(Dict); 1663%% worker_restart(Any, _Dict) -> 1664%% Any. 1665 1666 1667%%----------------------------------------------------------------- 1668 1669handle_backup(BackupDir, MibServer) -> 1670 ?vlog("handle_backup -> entry with" 1671 "~n BackupDir: ~p", [BackupDir]), 1672 case ets:lookup(snmp_agent_table, db_dir) of 1673 [{db_dir, BackupDir}] -> 1674 ?vinfo("handle_backup -> backup dir and db dir the same", []), 1675 {error, db_dir}; 1676 _ -> 1677 case file:read_file_info(BackupDir) of 1678 {ok, #file_info{type = directory}} -> 1679 ?vdebug("handle_backup -> backup dir ok", []), 1680 1681 VacmRes = (catch snmpa_vacm:backup(BackupDir)), 1682 ?vtrace("handle_backup -> " 1683 "~n VacmRes: ~p", [VacmRes]), 1684 1685 LdbRes = (catch snmpa_local_db:backup(BackupDir)), 1686 ?vtrace("handle_backup -> " 1687 "~n LdbRes: ~p", [LdbRes]), 1688 1689 MsRes = (catch snmpa_mib:backup(MibServer, BackupDir)), 1690 ?vtrace("handle_backup -> " 1691 "~n MsRes: ~p", [MsRes]), 1692 1693 SsRes = (catch snmpa_symbolic_store:backup(BackupDir)), 1694 ?vtrace("handle_backup -> " 1695 "~n SsRes: ~p", [SsRes]), 1696 handle_backup_res([{vacm, VacmRes}, 1697 {local_db, LdbRes}, 1698 {mib_server, MsRes}, 1699 {symbolic_store, SsRes}]); 1700 {ok, _} -> 1701 ?vinfo("handle_backup -> backup dir not a dir", []), 1702 {error, not_a_directory}; 1703 Error -> 1704 ?vinfo("handle_backup -> Error: ~p", [Error]), 1705 Error 1706 end 1707 end. 1708 1709 1710handle_backup_res(Results) -> 1711 handle_backup_res(Results, []). 1712 1713handle_backup_res([], []) -> 1714 ok; 1715handle_backup_res([], Acc) -> 1716 {error, lists:reverse(Acc)}; 1717handle_backup_res([{_, ok}|Results], Acc) -> 1718 handle_backup_res(Results, Acc); 1719handle_backup_res([{Who, {error, Reason}}|Results], Acc) -> 1720 handle_backup_res(Results, [{Who, Reason}|Acc]); 1721handle_backup_res([{Who, Crap}|Results], Acc) -> 1722 handle_backup_res(Results, [{Who, Crap}|Acc]). 1723 1724 1725%%----------------------------------------------------------------- 1726%% We must cheat to get the community string out of the ACM data, 1727%% because we (for some reason) support the function 1728%% snmpa:current_community(). 1729%%----------------------------------------------------------------- 1730cheat({community, _SecModel, Community, _TAddress}, Address, ContextName) -> 1731 {Community, Address, ContextName}; 1732cheat({community, _SecModel, Community, _TDomain, _TAddress}, 1733 Address, ContextName) -> 1734 {Community, Address, ContextName}; 1735cheat(_, Address, ContextName) -> 1736 {"", Address, ContextName}. 1737 1738 1739%% This function will either be in the context of the: 1740%% 1) master-agent 1741%% 2) set-worker 1742%% 3) user code 1743%% ( calling e.g. snmp_community_mib:snmpCommunityTable(set, ...) ) 1744invalidate_ca_cache() -> 1745 case get(master) of 1746 undefined -> % 1 or 3 1747 case get(auth_module) of 1748 undefined -> % 3 1749 %% Ouch, we are not running in the context of the 1750 %% master agent either. Check if we are on the 1751 %% master-agent node. If so, sent it there, 1752 case whereis(snmp_master_agent) of 1753 MasterAgent when is_pid(MasterAgent) -> 1754 case node(MasterAgent) =:= node() of 1755 true -> 1756 %% Ok, we are on the node running the 1757 %% master_agent process, so sent it there 1758 MasterAgent ! invalidate_ca_cache; 1759 false -> 1760 %% This is running on a sub-agent node, 1761 %% so skip it 1762 ok 1763 end; 1764 _ -> % Not on this node 1765 ok 1766 end; 1767 AuthMod -> % 1 1768 AuthMod:invalidate_ca_cache() 1769 end; 1770 Pid -> % 2 1771 Pid ! invalidate_ca_cache 1772 end. 1773 1774 1775%%----------------------------------------------------------------- 1776%% Threads and workers 1777%% 1778%%----------------------------------------------------------------- 1779 1780%% This functions spawns a temporary worker process, 1781%% that evaluates one request and then silently exits. 1782spawn_thread(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra) -> 1783 Dict = get(), 1784 Args = [Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra, Dict], 1785 proc_lib:spawn_link(?MODULE, handle_pdu, Args). 1786 1787spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs, 1788 LocalEngineID, ExtraInfo) -> 1789 Dict = get(), 1790 proc_lib:spawn_link(?MODULE, do_send_trap, 1791 [TrapRec, NotifyName, ContextName, 1792 Recv, Vbs, LocalEngineID, ExtraInfo, Dict]). 1793 1794do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 1795 LocalEngineID, Dict) -> 1796 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 1797 do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 1798 LocalEngineID, ExtraInfo, Dict). 1799do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 1800 LocalEngineID, ExtraInfo, Dict) -> 1801 lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), 1802 put(sname, trap_sender_short_name(get(sname))), 1803 ?vlog("starting",[]), 1804 snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 1805 LocalEngineID, ExtraInfo, get(net_if)). 1806 1807worker(Master, SName, Report, Dict) -> 1808 lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), 1809 put(sname, worker_short_name(get(sname), SName)), 1810 ?vlog("starting",[]), 1811 worker_loop(Master, Report). 1812 1813worker_loop(Master, Report) -> 1814 Res = 1815 receive 1816 #wrequest{cmd = handle_pdu, 1817 info = Info} = Req -> 1818 ?vtrace("worker_loop -> received handle_pdu request with" 1819 "~n Info: ~p", [Info]), 1820 Vsn = proplists:get_value(vsn, Info), 1821 Pdu = proplists:get_value(pdu, Info), 1822 PduMS = proplists:get_value(pdu_ms, Info), 1823 ACMData = proplists:get_value(acm_data, Info), 1824 Address = proplists:get_value(addr, Info), 1825 GbMaxVBs = proplists:get_value(gb_max_vbs, Info), 1826 Extra = proplists:get_value(extra, Info), 1827 HandlePduRes = 1828 try 1829 begin 1830 handle_pdu2(Vsn, Pdu, PduMS, ACMData, Address, 1831 GbMaxVBs, Extra) 1832 end 1833 catch 1834 C:E:S -> 1835 exit({worker_crash, Req, C, E, S}) 1836 end, 1837 worker_maybe_announce_available(Master, Report), 1838 HandlePduRes; % For debugging... 1839 1840 1841 #wrequest{cmd = send_trap, 1842 info = Info} = Req -> 1843 ?vtrace("worker_loop -> received send_trap request with" 1844 "~n Info: ~p", [Info]), 1845 TrapRec = proplists:get_value(trap_rec, Info), 1846 NotifyName = proplists:get_value(notify_name, Info), 1847 ContextName = proplists:get_value(context_name, Info), 1848 Recv = proplists:get_value(receiver, Info), 1849 Vbs = proplists:get_value(varbinds, Info), 1850 LocalEngineID = proplists:get_value(local_engine_id, Info), 1851 Extra = proplists:get_value(extra, Info), 1852 SendTrapRes = 1853 try 1854 begin 1855 snmpa_trap:send_trap(TrapRec, NotifyName, 1856 ContextName, Recv, Vbs, 1857 LocalEngineID, Extra, 1858 get(net_if)) 1859 end 1860 catch 1861 C:E:S -> 1862 exit({worker_crash, Req, C, E, S}) 1863 end, 1864 worker_maybe_announce_available(Master, Report), 1865 SendTrapRes; % For debugging... 1866 1867 1868 #wrequest{cmd = verbosity, 1869 info = Info} -> 1870 Verbosity = proplists:get_value(verbosity, Info), 1871 put(verbosity, snmp_verbosity:validate(Verbosity)); 1872 1873 1874 #wrequest{cmd = terminate} -> 1875 ?vtrace("worker_loop -> received terminate request", []), 1876 exit(normal); 1877 1878 1879 1880 1881 %% ************************************************************* 1882 %% 1883 %% Kept for backward compatibillity reasons 1884 %% 1885 %% ************************************************************* 1886 1887 {Vsn, Pdu, PduMS, ACMData, Address, Extra} -> 1888 ?vtrace("worker_loop -> received request", []), 1889 handle_pdu2(Vsn, Pdu, PduMS, ACMData, Address, 1890 ?DEFAULT_GB_MAX_VBS, Extra), 1891 Master ! worker_available; 1892 1893 %% We don't trap exits! 1894 {TrapRec, NotifyName, ContextName, Recv, Vbs} -> 1895 ?vtrace("worker_loop -> send trap:" 1896 "~n ~p", [TrapRec]), 1897 snmpa_trap:send_trap(TrapRec, NotifyName, 1898 ContextName, Recv, Vbs, get(net_if)), 1899 Master ! worker_available; 1900 1901 %% We don't trap exits! 1902 {send_trap, 1903 TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, 1904 ExtraInfo} -> 1905 ?vtrace("worker_loop -> send trap:" 1906 "~n ~p", [TrapRec]), 1907 snmpa_trap:send_trap(TrapRec, NotifyName, 1908 ContextName, Recv, Vbs, 1909 LocalEngineID, ExtraInfo, 1910 get(net_if)), 1911 Master ! worker_available; 1912 1913 {verbosity, Verbosity} -> 1914 put(verbosity, snmp_verbosity:validate(Verbosity)); 1915 1916 terminate -> 1917 exit(normal); 1918 1919 _X -> 1920 %% ignore 1921 ignore_unknown 1922 1923 after 30000 -> 1924 %% This is to assure that the worker process leaves a 1925 %% possibly old version of this module. 1926 ok 1927 end, 1928 ?vtrace("worker_loop -> wrap with" 1929 "~n ~p", [Res]), 1930 ?MODULE:worker_loop(Master, Report). 1931 1932worker_maybe_announce_available(Master, true) -> 1933 Master ! worker_available; 1934worker_maybe_announce_available(_Master, _Report) -> 1935 ok. 1936 1937 1938%%----------------------------------------------------------------- 1939%%----------------------------------------------------------------- 1940 1941handle_snmp_pdu(true, Vsn, Pdu, PduMS, ACMData, Address, Extra, 1942 #state{multi_threaded = false, 1943 gb_max_vbs = GbMaxVBs} = S) -> 1944 ?vtrace("handle_snmp_pdu -> single-thread agent",[]), 1945 handle_pdu2(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra), 1946 S; 1947handle_snmp_pdu(true, Vsn, #pdu{type = 'set-request'} = Pdu, PduMS, 1948 ACMData, Address, Extra, 1949 #state{set_worker = Worker} = S) -> 1950 ?vtrace("handle_snmp_pdu -> multi-thread agent: " 1951 "send set-request to main worker",[]), 1952 WRequest = ?mk_pdu_wreq(Vsn, Pdu, PduMS, ACMData, Address, infinity, Extra), 1953 Worker ! WRequest, 1954 S#state{worker_state = busy}; 1955handle_snmp_pdu(true, Vsn, Pdu, PduMS, 1956 ACMData, Address, Extra, 1957 #state{worker_state = busy, 1958 gb_max_vbs = GbMaxVBs} = S) -> 1959 ?vtrace("handle_snmp_pdu -> multi-thread agent: " 1960 "main worker busy - create new worker",[]), 1961 spawn_thread(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra), 1962 S; 1963handle_snmp_pdu(true, Vsn, Pdu, PduMS, ACMData, Address, Extra, 1964 #state{worker = Worker, 1965 gb_max_vbs = GbMaxVBs} = S) -> 1966 ?vtrace("handle_snmp_pdu -> multi-thread agent: " 1967 "send to main worker",[]), 1968 WRequest = ?mk_pdu_wreq(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra), 1969 Worker ! WRequest, 1970 S#state{worker_state = busy}; 1971handle_snmp_pdu(_, _Vsn, _Pdu, _PduMS, _ACMData, _Address, _Extra, S) -> 1972 S. 1973 1974 1975%% Called via the spawn_thread function 1976%% <BACKWARD-COMPAT> 1977handle_pdu(Vsn, Pdu, PduMS, ACMData, Address, Extra, Dict) -> 1978 handle_pdu(Vsn, Pdu, PduMS, ACMData, Address, ?DEFAULT_GB_MAX_VBS, Extra, 1979 Dict). 1980%% </BACKWARD-COMPAT> 1981handle_pdu(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra, Dict) -> 1982 lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), 1983 put(sname, pdu_handler_short_name(get(sname))), 1984 ?vlog("new worker starting",[]), 1985 handle_pdu2(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra). 1986 1987handle_pdu2(Vsn, Pdu, PduMS, ACMData, Address, GbMaxVBs, Extra) -> 1988 %% OTP-3324 1989 AuthMod = get(auth_module), 1990 case AuthMod:init_check_access(Pdu, ACMData) of 1991 {ok, MibView, ContextName} -> 1992 ?vlog("handle_pdu -> ok:" 1993 "~n MibView: ~p" 1994 "~n ContextName: ~p", [MibView, ContextName]), 1995 AgentData = cheat(ACMData, Address, ContextName), 1996 do_handle_pdu(MibView, Vsn, Pdu, PduMS, ACMData, AgentData, 1997 GbMaxVBs, Extra); 1998 {error, Reason} -> 1999 ?vlog("handle_pdu -> error:" 2000 "~n Reason: ~p", [Reason]), 2001 handle_acm_error(Vsn, Reason, Pdu, ACMData, Address, Extra); 2002 {discarded, Variable, Reason} -> 2003 ?vlog("handle_pdu -> discarded:" 2004 "~n Variable: ~p" 2005 "~n Reason: ~p", [Variable, Reason]), 2006 get(net_if) ! {discarded_pdu, Vsn, Pdu#pdu.request_id, 2007 ACMData, Variable, Extra} 2008 end. 2009 2010do_handle_pdu(MibView, Vsn, Pdu, PduMS, 2011 ACMData, {Community, Address, ContextName}, 2012 GbMaxVBs, Extra) -> 2013 2014 put(net_if_data, Extra), 2015 2016 RePdu = process_msg(MibView, Vsn, Pdu, PduMS, Community, 2017 Address, ContextName, GbMaxVBs), 2018 2019 ?vtrace("do_handle_pdu -> processed:" 2020 "~n RePdu: ~p", [RePdu]), 2021 NetIf = get(net_if), 2022 NetIf ! {snmp_response, Vsn, RePdu, 2023 RePdu#pdu.type, ACMData, Address, Extra}. 2024 2025 2026handle_acm_error(Vsn, Reason, Pdu, ACMData, Address, Extra) -> 2027 #pdu{type = Type, request_id = ReqId, varbinds = Vbs} = Pdu, 2028 RawErrStatus = snmpa_acm:error2status(Reason), 2029 case is_valid_pdu_type(Type) of 2030 true -> 2031 %% RawErrStatus can be authorizationError or genErr. If it is 2032 %% authorizationError, we'll have to do different things, 2033 %% depending on which SNMP version is used. 2034 %% v1 - noSuchName error 2035 %% v2 - GET: all variables 'noSuchObject' 2036 %% NEXT/BULK: all variables 'endOfMibView' 2037 %% SET: noAccess error 2038 %% v3 - authorizationError error 2039 %% 2040 %% NOTE: this procedure is not yet defined in the coex document! 2041 ?vdebug("~n Raw error status: ~w",[RawErrStatus]), 2042 Idx = case Vbs of 2043 [] -> 0; 2044 _ -> 1 2045 end, 2046 RePdu = 2047 if 2048 Vsn =:= 'version-1' -> 2049 ErrStatus = v2err_to_v1err(RawErrStatus), 2050 make_response_pdu(ReqId, ErrStatus, Idx, Vbs, Vbs); 2051 Vsn =:= 'version-3' -> 2052 make_response_pdu(ReqId, RawErrStatus, Idx, Vbs, Vbs); 2053 Type =:= 'get-request' -> % this is v2 2054 ReVbs = lists:map( 2055 fun(Vb) -> 2056 Vb#varbind{value=noSuchObject} 2057 end, 2058 Vbs), 2059 make_response_pdu(ReqId, noError, 0, Vbs, ReVbs); 2060 Type =:= 'set-request' -> 2061 make_response_pdu(ReqId, noAccess, Idx, Vbs, Vbs); 2062 true -> % next or bulk 2063 ReVbs = lists:map( 2064 fun(Vb) -> 2065 Vb#varbind{value=endOfMibView} 2066 end, 2067 Vbs), 2068 make_response_pdu(ReqId, noError, 0, Vbs, ReVbs) 2069 end, 2070 get(net_if) ! {snmp_response, Vsn, RePdu, 2071 'get-response', ACMData, Address, Extra}; 2072 false -> 2073 ?vdebug("~n Raw error status: ~w" 2074 "~n invalid pdu type: ~w", 2075 [RawErrStatus,Type]), 2076 ok 2077 end. 2078 2079get_send_opt(Key, Default, SendOpts) -> 2080 case lists:keysearch(Key, 1, SendOpts) of 2081 {value, {Key, Value}} -> 2082 Value; 2083 false -> 2084 Default 2085 end. 2086 2087handle_send_trap(S, Notification, SendOpts) -> 2088 NotifyName = get_send_opt(name, "", SendOpts), 2089 ContextName = get_send_opt(context, "", SendOpts), 2090 Recv = get_send_opt(receiver, no_receiver, SendOpts), 2091 Varbinds = get_send_opt(varbinds, [], SendOpts), 2092 ExtraInfo = get_send_opt(extra, ?DEFAULT_NOTIF_EXTRA_INFO, SendOpts), 2093 LocalEngineID = 2094 case lists:keysearch(local_engine_id, 1, SendOpts) of 2095 {value, {local_engine_id, Value}} -> 2096 Value; 2097 false -> 2098 local_engine_id(S) 2099 end, 2100 handle_send_trap(S, Notification, NotifyName, ContextName, Recv, Varbinds, 2101 LocalEngineID, ExtraInfo). 2102 2103handle_send_trap(#state{type = Type} = S, 2104 Notification, NotifyName, ContextName, Recv, Varbinds, 2105 LocalEngineID, ExtraInfo) -> 2106 ?vtrace("handle_send_trap -> entry with" 2107 "~n Agent type: ~p" 2108 "~n TrapName: ~p" 2109 "~n NotifyName: ~p" 2110 "~n ContextName: ~p" 2111 "~n LocalEngineID: ~p", 2112 [Type, Notification, NotifyName, ContextName, LocalEngineID]), 2113 try snmpa_trap:construct_trap(Notification, Varbinds) of 2114 {ok, TrapRecord, VarList} -> 2115 ?vtrace("handle_send_trap -> construction complete: " 2116 "~n TrapRecord: ~p" 2117 "~n VarList: ~p", 2118 [TrapRecord, VarList]), 2119 case Type of 2120 subagent -> 2121 ?vtrace("handle_send_trap -> [sub] forward trap",[]), 2122 maybe_forward_trap(S, TrapRecord, NotifyName, 2123 ContextName, Recv, VarList, ExtraInfo), 2124 {ok, S}; 2125 master_agent -> 2126 ?vtrace("handle_send_trap -> " 2127 "[master] handle send trap",[]), 2128 maybe_send_trap(S, TrapRecord, NotifyName, 2129 ContextName, Recv, VarList, 2130 LocalEngineID, ExtraInfo) 2131 end; 2132 error -> 2133 {error, failed_constructing_trap} 2134 catch 2135 C:E:Stack -> 2136 {error, {failed_constructing_trap, C, E, Stack}} 2137 end. 2138 2139 2140maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, 2141 TrapRec, NotifyName, ContextName, Recv, V, ExtraInfo) -> 2142 ?vtrace("maybe_forward_trap -> entry with" 2143 "~n NFs: ~p", [NFs]), 2144 case filter_notification(NFs, [], TrapRec) of 2145 {dont_send, [], Id} -> 2146 ?vdebug("trap not forwarded [filter ~p]", [Id]), 2147 {ok, S}; 2148 2149 {dont_send, Removed, Id} -> 2150 ?vdebug("trap not forwarded [filter ~p]", [Id]), 2151 NFs2 = del_notification_filter(Removed, NFs), 2152 {ok, S#state{nfilters = NFs2}}; 2153 2154 {send, [], TrapRec2} -> 2155 ?vtrace("maybe_forward_trap -> forward trap:" 2156 "~n ~p", [TrapRec2]), 2157 forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V, 2158 ExtraInfo), 2159 {ok, S}; 2160 2161 {send, Removed, TrapRec2} -> 2162 ?vtrace("maybe_forward_trap -> forward trap:" 2163 "~n ~p", [TrapRec2]), 2164 forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V, 2165 ExtraInfo), 2166 NFs2 = del_notification_filter(Removed, NFs), 2167 {ok, S#state{nfilters = NFs2}} 2168 end. 2169 2170 2171maybe_send_trap(#state{nfilters = NFs} = S, 2172 TrapRec, NotifyName, ContextName, Recv, Varbinds, 2173 LocalEngineID, ExtraInfo) -> 2174 ?vtrace("maybe_send_trap -> entry with" 2175 "~n NFs: ~p", [NFs]), 2176 case filter_notification(NFs, [], TrapRec) of 2177 {dont_send, [], Id} -> 2178 ?vdebug("trap not sent [filter ~p]",[Id]), 2179 {ok, S}; 2180 2181 {dont_send, Removed, Id} -> 2182 ?vdebug("trap not sent [filter ~p]",[Id]), 2183 NFs2 = del_notification_filter(Removed, NFs), 2184 {ok, S#state{nfilters = NFs2}}; 2185 2186 {send, [], TrapRec2} -> 2187 ?vtrace("maybe_send_trap -> send trap:" 2188 "~n ~p", [TrapRec2]), 2189 do_handle_send_trap(S, TrapRec2, 2190 NotifyName, ContextName, Recv, Varbinds, 2191 LocalEngineID, ExtraInfo); 2192 2193 {send, Removed, TrapRec2} -> 2194 ?vtrace("maybe_send_trap -> send trap:" 2195 "~n ~p", [TrapRec2]), 2196 NFs2 = del_notification_filter(Removed, NFs), 2197 do_handle_send_trap(S#state{nfilters = NFs2}, TrapRec2, 2198 NotifyName, ContextName, Recv, Varbinds, 2199 LocalEngineID, ExtraInfo) 2200 end. 2201 2202do_handle_send_trap(S, TrapRec, NotifyName, ContextName, Recv, Varbinds, 2203 LocalEngineID, ExtraInfo) -> 2204 Vbs = snmpa_trap:try_initialise_vars(get(mibserver), Varbinds), 2205 case S#state.type of 2206 subagent -> 2207 forward_trap(S#state.parent, TrapRec, NotifyName, ContextName, 2208 Recv, Vbs, ExtraInfo), 2209 {ok, S}; 2210 master_agent when S#state.multi_threaded =:= false -> 2211 ?vtrace("do_handle_send_trap -> send trap:" 2212 "~n TrapRec: ~p", [TrapRec]), 2213 snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, 2214 Recv, Vbs, LocalEngineID, ExtraInfo, 2215 get(net_if)), 2216 {ok, S}; 2217 master_agent when (S#state.multi_threaded =:= extended) -> 2218 %% Send to main worker 2219 ?vtrace("do_handle_send_trap -> send to notif-worker",[]), 2220 S#state.notif_worker ! ?mk_send_trap_wreq(TrapRec, NotifyName, 2221 ContextName, Recv, Vbs, 2222 LocalEngineID, ExtraInfo), 2223 {ok, S}; 2224 master_agent when S#state.worker_state =:= busy -> 2225 %% Main worker busy => create new worker 2226 ?vtrace("do_handle_send_trap -> main worker busy: " 2227 "spawn a trap sender", []), 2228 spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs, 2229 LocalEngineID, ExtraInfo), 2230 {ok, S}; 2231 master_agent -> 2232 %% Send to main worker 2233 ?vtrace("do_handle_send_trap -> send to main worker", []), 2234 S#state.worker ! ?mk_send_trap_wreq(TrapRec, NotifyName, 2235 ContextName, Recv, Vbs, 2236 LocalEngineID, ExtraInfo), 2237 {ok, S#state{worker_state = busy}} 2238 end. 2239 2240 2241filter_notification([], RemoveNFs, Notif) -> 2242 ?vtrace("filter_notification -> done when" 2243 "~n RemoveNFs: ~p" 2244 "~n Notif: ~p", [RemoveNFs, Notif]), 2245 {send, RemoveNFs, Notif}; 2246filter_notification([NF|NFs], RemoveNFs, Notif0) -> 2247 ?vtrace("filter_notification -> entry with" 2248 "~n NF: ~p" 2249 "~n RemoveNFs: ~p" 2250 "~n Notif0: ~p", [NF, RemoveNFs, Notif0]), 2251 case do_filter_notification(NF, Notif0) of 2252 {dont_send, Id} -> 2253 {dont_send, RemoveNFs, Id}; 2254 {send, Notif} -> 2255 filter_notification(NFs, RemoveNFs, Notif); 2256 {error, Id} -> 2257 filter_notification(NFs, [Id|RemoveNFs], Notif0) 2258 end. 2259 2260do_filter_notification(#notification_filter{id = Id, mod = Mod, data = Data}, 2261 Notif) -> 2262 case (catch Mod:handle_notification(Notif, Data)) of 2263 dont_send -> 2264 {dont_send, Id}; 2265 send -> 2266 {send, Notif}; 2267 {send, NewNotif} -> 2268 {send, NewNotif}; 2269 Else -> 2270 user_err("notification filter ~p removed: ~n~p", [Id, Else]), 2271 {error, Id} 2272 end. 2273 2274 2275add_notification_filter(first, NewNF, NFs) -> 2276 {ok, [NewNF | NFs]}; 2277add_notification_filter(last, NewNF, NFs) -> 2278 {ok, lists:append(NFs, [NewNF])}; 2279add_notification_filter(Where, NewNF, NFs) -> 2280 case add_nf(Where, NewNF, NFs, []) of 2281 {ok, NFs2} -> 2282 {ok, NFs2}; 2283 Error -> 2284 {Error, NFs} 2285 end. 2286 2287add_nf({_Loc, Id}, _NewNF, [], _Acc) -> 2288 {error, {not_found, Id}}; 2289add_nf({insert_before, Id}, NewNF, [NF|NFs], Acc) 2290 when Id =:= NF#notification_filter.id -> 2291 {ok, lists:reverse(Acc) ++ [NewNF,NF|NFs]}; 2292add_nf({insert_after, Id}, NewNF, [NF|NFs], Acc) 2293 when Id =:= NF#notification_filter.id -> 2294 {ok, lists:reverse(Acc) ++ [NF,NewNF|NFs]}; 2295add_nf(Where, NewNF, [NF|NFs], Acc) -> 2296 add_nf(Where, NewNF, NFs, [NF|Acc]). 2297 2298 2299del_notification_filter(IDs, NFs) -> 2300 Fun = fun(Id, NFilters) -> lists:keydelete(Id, 2, NFilters) end, 2301 lists:foldl(Fun, NFs, IDs). 2302 2303 2304handle_discovery(#state{type = master_agent} = S, From, 2305 TargetName, Notification, ContextName, Varbinds, 2306 DiscoHandler, ExtraInfo) -> 2307 ?vtrace("handle_discovery -> entry with" 2308 "~n TargetName: ~p" 2309 "~n Notification: ~p" 2310 "~n ContextName: ~p" 2311 "~n Varbinds: ~p", 2312 [TargetName, Notification, ContextName, Varbinds]), 2313 try snmpa_trap:construct_trap(Notification, Varbinds) of 2314 {ok, Record, InitVars} -> 2315 ?vtrace("handle_discovery -> trap construction complete: " 2316 "~n Record: ~p" 2317 "~n InitVars: ~p", [Record, InitVars]), 2318 send_discovery(S, From, TargetName, 2319 Record, ContextName, InitVars, 2320 DiscoHandler, ExtraInfo); 2321 error -> 2322 {error, failed_constructing_notification} 2323 catch 2324 C:E:Stack -> 2325 {error, {failed_constructing_trap, C, E, Stack}} 2326 end; 2327handle_discovery(_S, _From, 2328 _TargetName, _Notification, _ContextName, _Varbinds, 2329 _DiscoHandler, _ExtraInfo) -> 2330 {error, only_master_discovery}. 2331 2332%% We ignore if the master agent is multi-threaded or not. 2333%% 2334send_discovery(S, From, 2335 TargetName, Record, ContextName, InitVars, 2336 DiscoHandler, ExtraInfo) -> 2337 case snmpa_trap:send_discovery(TargetName, Record, ContextName, 2338 InitVars, get(net_if), ExtraInfo) of 2339 {ok, Sender, SecLevel} -> 2340 Disco = #disco{from = From, 2341 rec = Record, 2342 sender = Sender, 2343 target = TargetName, 2344 sec_level = SecLevel, 2345 ctx = ContextName, 2346 ivbs = InitVars, 2347 stage = 1, 2348 handler = DiscoHandler, 2349 extra = ExtraInfo}, 2350 {ok, S#state{disco = Disco}}; 2351 Error -> 2352 ?vlog("send_discovery -> failed sending discovery: " 2353 "~n Error: ~p", [Error]), 2354 Error 2355 end. 2356 2357 2358handle_discovery_response(#state{disco = #disco{from = From}} = S, 2359 {error, _} = Error) -> 2360 ?vlog("handle_discovery_response -> entry with" 2361 "~n From: ~p" 2362 "~n Error: ~p", [From, Error]), 2363 gen_server:reply(From, Error), 2364 S#state{disco = undefined}; 2365 2366handle_discovery_response(#state{disco = #disco{target = TargetName, 2367 stage = 1, 2368 extra = ExtraInfo} = Disco} = S, 2369 {ok, _Pdu, ManagerEngineId}) 2370 when is_record(Disco, disco) -> 2371 ?vlog("handle_discovery_response(1) -> entry with" 2372 "~n TargetName: ~p" 2373 "~n ManagerEngineId: ~p", [TargetName, ManagerEngineId]), 2374 %% This is end of stage 1. 2375 %% So, first we need to update the database with the EngineId of the 2376 %% manager and then deside if we should continue with stage 2. E.g. 2377 %% establish authenticated communication. 2378 case snmp_target_mib:set_target_engine_id(TargetName, ManagerEngineId) of 2379 true when Disco#disco.sec_level =:= ?'SnmpSecurityLevel_noAuthNoPriv' -> 2380 %% Ok, we are done 2381 From = Disco#disco.from, 2382 Handler = Disco#disco.handler, 2383 Reply = 2384 case handle_discovery_stage1_finish(Handler, 2385 TargetName, 2386 ManagerEngineId, 2387 ExtraInfo) of 2388 {ok, _} -> 2389 {ok, ManagerEngineId}; 2390 Error -> 2391 Error 2392 end, 2393 gen_server:reply(From, Reply), 2394 S#state{disco = undefined}; 2395 2396 true when Disco#disco.sec_level =/= ?'SnmpSecurityLevel_noAuthNoPriv' -> 2397 %% Ok, time for stage 2 2398 %% Send the same inform again, 2399 %% this time we have the proper EngineId 2400 2401 From = Disco#disco.from, 2402 Handler = Disco#disco.handler, 2403 2404 case handle_discovery_stage1_finish(Handler, 2405 TargetName, 2406 ManagerEngineId, 2407 ExtraInfo) of 2408 {ok, NewExtraInfo} -> 2409 ?vdebug("handle_discovery_response(1) -> " 2410 "we are done with stage 1 - " 2411 "continue with stage 2", []), 2412 #disco{rec = Record, 2413 ctx = ContextName, 2414 ivbs = InitVars} = Disco, 2415 case snmpa_trap:send_discovery(TargetName, 2416 Record, 2417 ContextName, 2418 InitVars, 2419 get(net_if), 2420 ExtraInfo) of 2421 {ok, Sender, _SecLevel} -> 2422 ?vdebug("handle_discovery_response(1) -> " 2423 "stage 2 trap sent", []), 2424 Disco2 = Disco#disco{sender = Sender, 2425 engine_id = ManagerEngineId, 2426 stage = 2, 2427 extra = NewExtraInfo}, 2428 S#state{disco = Disco2}; 2429 Error -> 2430 ?vlog("handle_discovery_response(1) -> " 2431 "failed sending stage 2 trap: " 2432 "~n ~p", [Error]), 2433 error_msg("failed sending second " 2434 "discovery message: " 2435 "~n ~p", [Error]), 2436 Reply = {error, {second_send_failed, Error}}, 2437 gen_server:reply(From, Reply), 2438 S#state{disco = undefined} 2439 end; 2440 {error, Reason} = Error -> 2441 ?vlog("handle_discovery_response(1) -> " 2442 "stage 1 finish failed: " 2443 "~n ~p", [Reason]), 2444 gen_server:reply(From, Error), 2445 S#state{disco = undefined} 2446 end; 2447 false -> 2448 ?vinfo("handle_discovery_response(1) -> " 2449 "failed setting doscovered engine-id - " 2450 "inform the user", []), 2451 From = Disco#disco.from, 2452 Reason = {failed_setting_engine_id, TargetName, ManagerEngineId}, 2453 gen_server:reply(From, {error, Reason}), 2454 S#state{disco = undefined} 2455 2456 end; 2457 2458handle_discovery_response(#state{disco = #disco{from = From, 2459 engine_id = EngineID, 2460 stage = 2}} = S, 2461 {ok, _Pdu}) -> 2462 ?vlog("handle_discovery_response(2) -> entry with" 2463 "~n From: ~p", [From]), 2464 gen_server:reply(From, {ok, EngineID}), 2465 S#state{disco = undefined}; 2466 2467handle_discovery_response(#state{disco = #disco{from = From}} = S, Crap) -> 2468 Reason = {invalid_response, Crap}, 2469 gen_server:reply(From, {error, Reason}), 2470 S#state{disco = undefined}; 2471 2472handle_discovery_response(S, Crap) -> 2473 warning_msg("Received unexpected discovery response: ~p", [Crap]), 2474 S. 2475 2476handle_discovery_stage1_finish(Handler, TargetName, ManagerEngineID, 2477 ExtraInfo) -> 2478 case (catch Handler:stage1_finish(TargetName, ManagerEngineID, 2479 ExtraInfo)) of 2480 ignore -> 2481 ?vtrace("handle_discovery_stage1_finish -> " 2482 "we are done - [ignore] inform the user", []), 2483 {ok, ExtraInfo}; 2484 2485 {ok, UsmEntry} when is_tuple(UsmEntry) -> 2486 ?vtrace("handle_discovery_stage1_finish -> " 2487 "received usm entry - attempt to add it", []), 2488 case add_usm_users([UsmEntry]) of 2489 ok -> 2490 {ok, ExtraInfo}; 2491 Error -> 2492 Error 2493 end; 2494 2495 {ok, UsmEntry, NewExtraInfo} when is_tuple(UsmEntry) -> 2496 ?vtrace("handle_discovery_stage1_finish -> " 2497 "received usm entry - attempt to add it", []), 2498 case add_usm_users([UsmEntry]) of 2499 ok -> 2500 {ok, NewExtraInfo}; 2501 Error -> 2502 Error 2503 end; 2504 2505 {ok, UsmEntries} when is_list(UsmEntries) -> 2506 ?vtrace("handle_discovery_stage1_finish -> " 2507 "received usm entries - attempt to add them", []), 2508 case add_usm_users(UsmEntries) of 2509 ok -> 2510 {ok, ExtraInfo}; 2511 Error -> 2512 Error 2513 end; 2514 2515 {ok, UsmEntries, NewExtraInfo} when is_list(UsmEntries) -> 2516 ?vtrace("handle_discovery_stage1_finish -> " 2517 "received usm entries - attempt to add them", []), 2518 case add_usm_users(UsmEntries) of 2519 ok -> 2520 {ok, NewExtraInfo}; 2521 Error -> 2522 Error 2523 end; 2524 2525 {'EXIT', Reason} -> 2526 ?vlog("handle_discovery_stage1_finish -> stage 1 function exited: " 2527 "~n ~p", [Reason]), 2528 {error, {finish_exit, Reason, ManagerEngineID}}; 2529 2530 {error, Reason} -> 2531 ?vlog("handle_discovery_stage1_finish -> stage 1 function error: " 2532 "~n ~p", [Reason]), 2533 {error, {finish_error, Reason, ManagerEngineID}}; 2534 2535 Unknown -> 2536 ?vlog("handle_discovery_stage1_finish -> stage 1 function unknown: " 2537 "~n ~p", [Unknown]), 2538 {error, {finish_failed, Unknown, ManagerEngineID}} 2539 end. 2540 2541add_usm_users([]) -> 2542 ok; 2543add_usm_users([UsmEntry|UsmEntries]) when is_tuple(UsmEntry) -> 2544 ?vtrace("add_usm_users -> attempt to add entry (~w)", 2545 [element(1, UsmEntry)]), 2546 case snmp_user_based_sm_mib:add_user(UsmEntry) of 2547 {ok, _} -> 2548 add_usm_users(UsmEntries); 2549 {error, Reason} -> 2550 ?vlog("add_usm_users -> failed adding usm entry: " 2551 "~n ~p", [Reason]), 2552 {error, {failed_adding_entry, Reason, UsmEntry}} 2553 end. 2554 2555 2556handle_me_of(MibServer, Oid) -> 2557 case snmpa_mib:lookup(MibServer, Oid) of 2558 {variable, ME} -> 2559 {ok, ME}; 2560 {table_column, ME, _TableEntryOid} -> 2561 {ok, ME}; 2562 {subagent, SubAgentPid, _SANextOid} -> 2563 {error, {subagent, SubAgentPid}}; 2564 {false, Reason} -> 2565 {error, Reason}; 2566 Else -> 2567 {error, Else} 2568 end. 2569 2570 2571handle_mib_of(MibServer, Oid) -> 2572 snmpa_mib:which_mib(MibServer, Oid). 2573 2574 2575%%----------------------------------------------------------------- 2576%% Func: process_msg/7 2577%% Returns: RePdu 2578%%----------------------------------------------------------------- 2579process_msg( 2580 MibView, Vsn, Pdu, PduMS, Community, 2581 SourceAddress, ContextName, GbMaxVBs) -> 2582 #pdu{request_id = ReqId} = Pdu, 2583 put( 2584 snmp_address, 2585 case SourceAddress of 2586 {Domain, _} when is_atom(Domain) -> 2587 SourceAddress; 2588 {Ip, Port} when is_integer(Port) -> 2589 %% Legacy transport domain 2590 {tuple_to_list(Ip), Port} 2591 end), 2592 put(snmp_request_id, ReqId), 2593 put(snmp_community, Community), 2594 put(snmp_context, ContextName), 2595 ?vtrace("process ~p",[Pdu#pdu.type]), 2596 process_pdu(Pdu, PduMS, Vsn, MibView, GbMaxVBs). 2597 2598process_pdu(#pdu{type='get-request', request_id = ReqId, varbinds=Vbs}, 2599 _PduMS, Vsn, MibView, _GbMaxVBs) -> 2600 ?vtrace("get ~p",[ReqId]), 2601 OrigRes = do_get(MibView, Vbs, false), 2602 Res = get_err(OrigRes), 2603 {ErrStatus, ErrIndex, ResVarbinds} = 2604 if 2605 Vsn =:= 'version-1' -> validate_get_v1(Res); 2606 true -> Res 2607 end, 2608 if 2609 (ErrStatus =/= noError) -> 2610 ?vlog("get final result: " 2611 "~n Error status: ~p" 2612 "~n Error index: ~p" 2613 "~n when" 2614 "~n Original Result: " 2615 "~n ~p", [ErrStatus, ErrIndex, OrigRes]); 2616 true -> 2617 ?vtrace("get final result: " 2618 "~n Error status: ~p" 2619 "~n Error index: ~p" 2620 "~n Varbinds: ~p", 2621 [ErrStatus, ErrIndex, ResVarbinds]) 2622 end, 2623 ResponseVarbinds = lists:keysort(#varbind.org_index, ResVarbinds), 2624 ?vtrace("response varbinds: " 2625 "~n ~p", [ResponseVarbinds]), 2626 make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, ResponseVarbinds); 2627 2628process_pdu(#pdu{type = 'get-next-request', request_id = ReqId, varbinds = Vbs}, 2629 _PduMS, Vsn, MibView, _GbMaxVBs) -> 2630 ?vtrace("process get-next-request -> entry with" 2631 "~n ReqId: ~p" 2632 "~n Vbs: ~p" 2633 "~n MibView: ~p", [ReqId, Vbs, MibView]), 2634 OrigRes = do_get_next(MibView, Vbs), 2635 Res = get_err(OrigRes), 2636 {ErrStatus, ErrIndex, ResVarbinds} = 2637 if 2638 Vsn =:= 'version-1' -> validate_next_v1(Res, MibView); 2639 true -> Res 2640 end, 2641 if 2642 (ErrStatus =/= noError) -> 2643 ?vlog("get-next final result: " 2644 "~n Error status: ~p" 2645 "~n Error index: ~p" 2646 "~n when" 2647 "~n Original Result: " 2648 "~n ~p", [ErrStatus, ErrIndex, OrigRes]); 2649 true -> 2650 ?vtrace("get-next final result:" 2651 "~n Error status: ~p" 2652 "~n Error index: ~p" 2653 "~n Varbinds: ~p", [ErrStatus, ErrIndex, ResVarbinds]) 2654 end, 2655 ResponseVarbinds = lists:keysort(#varbind.org_index, ResVarbinds), 2656 ?vtrace("get-next final result -> response varbinds: " 2657 "~n ~p", [ResponseVarbinds]), 2658 make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, ResponseVarbinds); 2659 2660process_pdu(#pdu{type = 'get-bulk-request', 2661 request_id = ReqId, 2662 varbinds = Vbs, 2663 error_status = NonRepeaters, 2664 error_index = MaxRepetitions}, 2665 PduMS, _Vsn, MibView, GbMaxVBs) -> 2666 OrigRes = do_get_bulk(MibView, NonRepeaters, MaxRepetitions, PduMS, Vbs, 2667 GbMaxVBs), 2668 {ErrStatus, ErrIndex, ResponseVarbinds} = get_err(OrigRes), 2669 if 2670 (ErrStatus =/= noError) -> 2671 ?vlog("get-bulk final result: " 2672 "~n Error Status: ~p" 2673 "~n Error Index: ~p" 2674 "~n when" 2675 "~n Original Result: " 2676 "~n ~p", [ErrStatus, ErrIndex, OrigRes]); 2677 true -> 2678 ?vtrace("get-bulk final result: " 2679 "~n Error status: ~p" 2680 "~n Error index: ~p" 2681 "~n Response Varbinds: ~p", 2682 [ErrStatus, ErrIndex, ResponseVarbinds]) 2683 end, 2684 make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, ResponseVarbinds); 2685 2686process_pdu(#pdu{type = 'set-request', request_id = ReqId, varbinds = Vbs}, 2687 _PduMS, Vsn, MibView, _GbMaxVbs) -> 2688 OrigRes = do_set(MibView, Vbs), 2689 {ErrStatus, ErrIndex} = 2690 if 2691 Vsn =:= 'version-1' -> validate_err(v2_to_v1, OrigRes); 2692 true -> OrigRes 2693 end, 2694 if 2695 (ErrStatus =/= noError) -> 2696 ?vlog("set final result: " 2697 "~n Error Status: ~p" 2698 "~n Error Index: ~p" 2699 "~n when" 2700 "~n Original Result: " 2701 "~n ~p", [ErrStatus, ErrIndex, OrigRes]); 2702 true -> 2703 ?vtrace("set final result: " 2704 "~n Error Status: ~p" 2705 "~n Error Index: ~p", [ErrStatus, ErrIndex]) 2706 end, 2707 make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, Vbs). 2708 2709 2710%%----------------------------------------------------------------- 2711%% Transform a value == noSuchInstance | noSuchObject or a 2712%% Counter64 type to a noSuchName error for the whole pdu. 2713%% Args: {Error, Index, Vbs} 2714%% Returns: {NewError, NewIndex, NewVbs} 2715%%----------------------------------------------------------------- 2716validate_get_v1({noError, _, ResponseVarbinds}) -> 2717 case validate_get_v1_2(ResponseVarbinds) of 2718 true -> {noError, 0, ResponseVarbinds}; 2719 {Error, Index} -> {Error, Index, []} % dummy vbs 2720 end; 2721validate_get_v1({ErrStatus, ErrIndex, ResponseVarbinds}) -> 2722 {v2err_to_v1err(ErrStatus), ErrIndex, ResponseVarbinds}. 2723 2724validate_get_v1_2([Vb | Vbs]) 2725 when ((Vb#varbind.value =/= noSuchInstance) andalso 2726 (Vb#varbind.value =/= noSuchObject) andalso 2727 (Vb#varbind.variabletype =/= 'Counter64')) -> 2728 validate_get_v1_2(Vbs); 2729validate_get_v1_2([Vb | _Vbs]) -> 2730 {noSuchName, Vb#varbind.org_index}; 2731validate_get_v1_2([]) -> 2732 true. 2733 2734%%----------------------------------------------------------------- 2735%% Transform a value == endOfMibView to a noSuchName for the 2736%% whole pdu, and do another get-next for any Counter64 value. 2737%% Args: {Error, Index, Vbs} 2738%% Returns: {NewError, NewIndex, NewVbs} 2739%%----------------------------------------------------------------- 2740validate_next_v1({noError, _, ResponseVarbinds}, MibView) -> 2741 case validate_next_v1_2(ResponseVarbinds, MibView, []) of 2742 {true, NVbs} -> {noError, 0, NVbs}; 2743 {Error, Index} -> {Error, Index, []} % dummy vbs 2744 end; 2745validate_next_v1({ErrStatus, ErrIndex, ResponseVarbinds}, _MibView) -> 2746 {v2err_to_v1err(ErrStatus), ErrIndex, ResponseVarbinds}. 2747 2748validate_next_v1_2([Vb | _Vbs], _MibView, _Res) 2749 when Vb#varbind.value =:= endOfMibView -> 2750 {noSuchName, Vb#varbind.org_index}; 2751validate_next_v1_2([Vb | Vbs], MibView, Res) 2752 when Vb#varbind.variabletype =:= 'Counter64' -> 2753 case validate_next_v1( do_get_next(MibView, [mk_next_oid(Vb)]), MibView) of 2754 {noError, 0, [NVb]} -> 2755 validate_next_v1_2(Vbs, MibView, [NVb | Res]); 2756 {Error, Index, _OrgVb} -> 2757 {Error, Index} 2758 end; 2759validate_next_v1_2([Vb | Vbs], MibView, Res) -> 2760 validate_next_v1_2(Vbs, MibView, [Vb | Res]); 2761validate_next_v1_2([], _MibView, Res) -> 2762 {true, Res}. 2763 2764%%----------------------------------------------------------------- 2765%% Optimization. When we get to a Counter64 object that is a table 2766%% column, we'll try to find the next instance. This will be the 2767%% next row in the table, which is a Counter64 value as well. This 2768%% means that we will loop through the entire table, until we find 2769%% a column that isn't a Counter64 column. We can optimze this by 2770%% adding 1 to the column-no in the oid of this instance. 2771%% If the table is implemented by a subagent this does not help, 2772%% we'll call that subagent many times. But it shouldn't be any 2773%% problems. 2774%%----------------------------------------------------------------- 2775mk_next_oid(Vb) -> 2776 case snmpa_mib:lookup(get(mibserver), Oid = Vb#varbind.oid) of 2777 {table_column, _MibEntry, TableEntryOid} -> 2778 [Col | _] = Oid -- TableEntryOid, 2779 Vb#varbind{oid = TableEntryOid ++ [Col+1]}; 2780 _ -> 2781 Vb 2782 end. 2783 2784%%%----------------------------------------------------------------- 2785%%% 3. GET REQUEST 2786%%% -------------- 2787%%% According to RFC1157, section 4.1.2 and RFC1905, section 4.2.1. 2788%%% In rfc1157:4.1.2 it isn't specified if noSuchName should be 2789%%% returned even if some other varbind generates a genErr. 2790%%% In rfc1905:4.2.1 this is not a problem since exceptions are 2791%%% used, and thus a genErr will be returned anyway. 2792%%%----------------------------------------------------------------- 2793 2794%%----------------------------------------------------------------- 2795%% Func: do_get/2 2796%% Purpose: Handles all VBs in a request that is inside the 2797%% mibview (local). 2798%% Returns: {noError, 0, ListOfNewVarbinds} | 2799%% {ErrorStatus, ErrorIndex, []} 2800%%----------------------------------------------------------------- 2801 2802do_get(UnsortedVarbinds, IsNotification) -> 2803 Extra = get(net_if_data), 2804 GetModule = get(get_module), 2805 GetModule:do_get(UnsortedVarbinds, IsNotification, Extra). 2806 2807 2808%%----------------------------------------------------------------- 2809%% Func: do_get/3 2810%% Purpose: do_get handles "getRequests". 2811%% Pre: incoming varbinds have type == 'NULL', value == unSpecified 2812%% Returns: {noError, 0, ListOfNewVarbinds} | 2813%% {ErrorStatus, ErrorIndex, []} 2814%%----------------------------------------------------------------- 2815 2816%% If this function is called from a worker-process (or other process), 2817%% we *may* need to tunnel into the master-agent and let it do the work. 2818 2819do_get(MibView, UnsortedVarbinds, IsNotification) -> 2820 Extra = get(net_if_data), 2821 GetModule = get(get_module), 2822 GetModule:do_get(MibView, UnsortedVarbinds, IsNotification, Extra). 2823 2824do_get(MibView, UnsortedVarbinds, IsNotification, ForceMaster) -> 2825 case (whereis(snmp_master_agent) =:= self()) of 2826 false when (ForceMaster =:= true) -> 2827 PduData = get_pdu_data(), 2828 call(snmp_master_agent, 2829 {do_get, MibView, UnsortedVarbinds, IsNotification, PduData}); 2830 _ -> 2831 do_get(MibView, UnsortedVarbinds, IsNotification) 2832 end. 2833 2834 2835 2836%%%----------------------------------------------------------------- 2837%%% 4. GET-NEXT REQUEST 2838%%% -------------- 2839%%% According to RFC1157, section 4.1.3 and RFC1905, section 4.2.2. 2840%%%----------------------------------------------------------------- 2841%%----------------------------------------------------------------- 2842%% Func: do_get_next/2 2843%% Purpose: do_get_next handles "getNextRequests". 2844%% Note: Even if it is SNMPv1, a varbind's value can be 2845%% endOfMibView. This is converted to noSuchName in process_pdu. 2846%% Returns: {noError, 0, ListOfNewVarbinds} | 2847%% {ErrorStatus, ErrorIndex, []} 2848%% Note2: ListOfNewVarbinds is not sorted in any order!!! 2849%% Alg: First, the variables are sorted in OID order. 2850%% 2851%% Second, next in the MIB is performed for each OID, and 2852%% the result is collected as: if next oid is a variable, 2853%% perform a get to retrieve its value; if next oid is in a 2854%% table, save this value and continue until we get an oid 2855%% outside this table. Then perform get_next on the table, 2856%% and continue with all endOfTables and the oid outside the 2857%% table; if next oid is an subagent, save this value and 2858%% continue as in the table case. 2859%% 2860%% Third, each response is checked for endOfMibView, or (for 2861%% subagents) that the Oid returned has the correct prefix. 2862%% (This is necessary since an SA can be registered under many 2863%% separated subtrees, and if the last variable in the first 2864%% subtree is requested in a next, the SA will return the first 2865%% variable in the second subtree. This might be working, since 2866%% there may be a variable in between these subtrees.) For each 2867%% of these, a new get-next is performed, one at a time. 2868%% This alg. might be optimised in several ways. The most 2869%% striking one is that the same SA might be called several 2870%% times, when one time should be enough. But it isn't clear 2871%% that this really matters, since many nexts across the same 2872%% subagent must be considered to be very rare. 2873%%----------------------------------------------------------------- 2874 2875 2876do_get_next(MibView, UnsortedVarbinds) -> 2877 Extra = get(net_if_data), 2878 GetModule = get(get_module), 2879 GetModule:do_get_next(MibView, UnsortedVarbinds, Extra). 2880 2881 2882 2883%%%----------------------------------------------------------------- 2884%%% 5. GET-BULK REQUEST 2885%%% 2886%%% In order to prevent excesses in reply sizes there are two 2887%%% preventive methods in place. One is to check that the encode 2888%%% size does not exceed Max PDU size (this is mentioned in the 2889%%% standard). The other is a simple VBs limit. That is, the 2890%%% resulting response cannot contain more then this number of VBs. 2891%%%----------------------------------------------------------------- 2892 2893do_get_bulk(MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs) -> 2894 Extra = get(net_if_data), 2895 GetModule = get(get_module), 2896 GetModule:do_get_bulk(MibView, NonRepeaters, MaxRepetitions, 2897 PduMS, Varbinds, GbMaxVBs, 2898 Extra). 2899 2900 2901 2902%%%-------------------------------------------------- 2903%%% 6. SET REQUEST 2904%%%-------------------------------------------------- 2905%% return: {ErrStatus, ErrIndex} 2906%% where ErrIndex is an index in Varbinds list (not org_index (user-functions 2907%% doesn't see org_index)). 2908do_set(MibView, UnsortedVarbinds) -> 2909 SetModule = get(set_module), 2910 ?vtrace("set module: ~p",[SetModule]), 2911 apply(SetModule, do_set, [MibView, UnsortedVarbinds]). 2912 2913do_subagent_set(Arguments) -> 2914 SetModule = get(set_module), 2915 apply(SetModule, do_subagent_set, [Arguments]). 2916 2917 2918 2919%%%----------------------------------------------------------------- 2920%%% 7. Misc functions 2921%%%----------------------------------------------------------------- 2922 2923make_response_pdu(ReqId, ErrStatus, ErrIndex, OrgVarbinds, _ResponseVarbinds) 2924 when ErrIndex =/= 0 -> 2925 #pdu{type = 'get-response', request_id = ReqId, error_status = ErrStatus, 2926 error_index = ErrIndex, varbinds = OrgVarbinds}; 2927make_response_pdu(ReqId, ErrStatus, ErrIndex, _OrgVarbinds, ResponseVarbinds) -> 2928 #pdu{type = 'get-response', request_id = ReqId, error_status = ErrStatus, 2929 error_index = ErrIndex, varbinds = ResponseVarbinds}. 2930 2931%% Valid errormsgs for different operations. 2932validate_err(consistency_check, {'EXIT', _Reason}, _) -> 2933 {genErr, 0}; 2934validate_err(consistency_check, X, _) -> 2935 X; 2936 2937validate_err(is_set_ok, noError, _) -> noError; 2938validate_err(is_set_ok, noCreation, _) -> noCreation; 2939validate_err(is_set_ok, inconsistentValue, _) -> inconsistentValue; 2940validate_err(is_set_ok, resourceUnavailable, _) -> resourceUnavailable; 2941validate_err(is_set_ok, inconsistentName, _) -> inconsistentName; 2942validate_err(is_set_ok, badValue, _) -> badValue; 2943validate_err(is_set_ok, wrongValue, _) -> wrongValue; 2944validate_err(is_set_ok, noSuchName, _) -> noSuchName; 2945validate_err(is_set_ok, noAccess, _) -> noAccess; 2946validate_err(is_set_ok, notWritable, _) -> notWritable; 2947validate_err(is_set_ok, genErr, _) -> genErr; 2948validate_err(is_set_ok, X, Mfa) -> 2949 user_err("~w with is_set_ok, returned: ~w. Using genErr.", 2950 [Mfa, X]), 2951 genErr; 2952 2953validate_err(set, commitFailed, _) -> commitFailed; 2954validate_err(set, undoFailed, _) -> undoFailed; 2955validate_err(set, noError, _) -> noError; 2956validate_err(set, genErr, _) -> genErr; 2957validate_err(set, X, Mfa) -> 2958 user_err("~w with set, returned: ~w. Using genErr.", 2959 [Mfa, X]), 2960 genErr; 2961 2962validate_err(undo, undoFailed, _) -> undoFailed; 2963validate_err(undo, noError, _) -> noError; 2964validate_err(undo, genErr, _) -> genErr; 2965validate_err(undo, X, Mfa) -> 2966 user_err("~w with undo, returned: ~w. Using genErr.", 2967 [Mfa, X]), 2968 genErr; 2969 2970validate_err(table_is_set_ok, {Err, Idx}, Mfa) when is_integer(Idx) -> 2971 {validate_err(is_set_ok, Err, Mfa), Idx}; 2972validate_err(table_is_set_ok, X, Mfa) -> 2973 user_err("~w with is_set_ok (table), returned: ~w. Using genErr.", 2974 [Mfa, X]), 2975 {genErr, 0}; 2976 2977validate_err(row_is_set_ok, {Err, Idx}, _) when is_integer(Idx) -> 2978 {Err, Idx}; 2979validate_err(row_is_set_ok, {_Err, {false, BadCol}}, Mfa) -> 2980 user_err("~w with is_set_ok (table), returned bad column: " 2981 "~w. Using genErr.", [Mfa, BadCol]), 2982 {genErr, 0}; 2983 2984validate_err(table_undo, {Err, Idx}, Mfa) when is_integer(Idx) -> 2985 {validate_err(undo, Err, Mfa), Idx}; 2986validate_err(table_undo, X, Mfa) -> 2987 user_err("~w with undo (table), returned: ~w. Using genErr.", 2988 [Mfa, X]), 2989 {genErr, 0}; 2990 2991validate_err(row_undo, {Err, Idx}, _) when is_integer(Idx) -> 2992 {Err, Idx}; 2993validate_err(row_undo, {_Err, {false, BadCol}}, Mfa) -> 2994 user_err("~w with undo (table), returned bad column: " 2995 "~w. Using genErr.", [Mfa, BadCol]), 2996 {genErr, 0}; 2997 2998validate_err(table_set, {Err, Idx}, Mfa) when is_integer(Idx) -> 2999 {validate_err(set, Err, Mfa), Idx}; 3000validate_err(table_set, X, Mfa) -> 3001 user_err("~w with set (table), returned: ~w. Using genErr.", 3002 [Mfa, X]), 3003 {genErr, 0}; 3004 3005validate_err(row_set, {Err, Idx}, _) when is_integer(Idx) -> 3006 {Err, Idx}; 3007validate_err(row_set, {_Err, {false, BadCol}}, Mfa) -> 3008 user_err("~w with set (table), returned bad column: " 3009 "~w. Using genErr.", [Mfa, BadCol]), 3010 {genErr, 0}; 3011 3012validate_err(table_next, {Err, Idx}, _Mfa) when is_integer(Idx) -> 3013 {Err, Idx}; 3014validate_err(table_next, {_Err, {false, BadCol}}, Mfa) -> 3015 user_err("~w with get_next, returned bad column: " 3016 "~w. Using genErr.", [Mfa, BadCol]), 3017 {genErr, 0}. 3018 3019validate_err(v2_to_v1, {V2Err, Index}) -> 3020 {v2err_to_v1err(V2Err), Index}; 3021validate_err(v2_to_v1, _) -> 3022 {genErr, 0}. 3023 3024get_err({ErrC, ErrI, Vbs}) -> 3025 {get_err_i(ErrC), ErrI, Vbs}. 3026 3027get_err_i(noError) -> noError; 3028get_err_i(tooBig) -> tooBig; % OTP-9700 3029get_err_i(ES) -> ?vtrace("convert ErrorStatus '~p' to 'genErr'", [ES]), genErr. 3030 3031v2err_to_v1err(noError) -> noError; 3032v2err_to_v1err(noAccess) -> noSuchName; 3033v2err_to_v1err(noCreation) -> noSuchName; 3034v2err_to_v1err(notWritable) -> noSuchName; 3035v2err_to_v1err(wrongLength) -> badValue; 3036v2err_to_v1err(wrongEncoding) -> badValue; 3037v2err_to_v1err(wrongType) -> badValue; 3038v2err_to_v1err(wrongValue) -> badValue; 3039v2err_to_v1err(inconsistentValue) -> badValue; 3040v2err_to_v1err(inconsistentName) -> noSuchName; 3041v2err_to_v1err(noSuchName) -> noSuchName; 3042v2err_to_v1err(badValue) -> badValue; 3043v2err_to_v1err(authorizationError) -> noSuchName; 3044%% genErr | resourceUnavailable | undoFailed | commitFailed -> genErr 3045v2err_to_v1err(_Error) -> genErr. 3046 3047%%----------------------------------------------------------------- 3048%% transforms a (hopefully correct) return value ((perhaps) from a 3049%% mib-function) to a typed and guaranteed correct return value. 3050%% An incorrect return value is transformed to {error, genErr}. 3051%% A correct return value is on the form: 3052%% {error, <error-msg>} | {value, <variable-type>, <value>} 3053%%----------------------------------------------------------------- 3054make_value_a_correct_value({value, Val}, Asn1, Mfa) 3055 when Asn1#asn1_type.bertype =:= 'INTEGER' -> 3056 check_integer(Val, Asn1, Mfa); 3057 3058make_value_a_correct_value({value, Val}, Asn1, Mfa) 3059 when Asn1#asn1_type.bertype =:= 'Counter32' -> 3060 check_integer(Val, Asn1, Mfa); 3061 3062make_value_a_correct_value({value, Val}, Asn1, Mfa) 3063 when Asn1#asn1_type.bertype =:= 'Unsigned32' -> 3064 check_integer(Val, Asn1, Mfa); 3065 3066make_value_a_correct_value({value, Val}, Asn1, Mfa) 3067 when Asn1#asn1_type.bertype =:= 'TimeTicks' -> 3068 check_integer(Val, Asn1, Mfa); 3069 3070make_value_a_correct_value({value, Val}, Asn1, Mfa) 3071 when Asn1#asn1_type.bertype =:= 'Counter64' -> 3072 check_integer(Val, Asn1, Mfa); 3073 3074make_value_a_correct_value({value, Val}, Asn1, Mfa) 3075 when (Asn1#asn1_type.bertype =:= 'BITS') andalso is_list(Val) -> 3076 {value,Kibbles} = snmp_misc:assq(kibbles,Asn1#asn1_type.assocList), 3077 case snmp_misc:bits_to_int(Val,Kibbles) of 3078 error -> 3079 wrongValue(Val, Mfa); 3080 Int -> 3081 make_value_a_correct_value({value,Int},Asn1,Mfa) 3082 end; 3083 3084make_value_a_correct_value({value, Val}, Asn1, Mfa) 3085 when (Asn1#asn1_type.bertype =:= 'BITS') andalso is_integer(Val) -> 3086 {value,Kibbles} = snmp_misc:assq(kibbles,Asn1#asn1_type.assocList), 3087 {_Kibble,BitNo} = lists:last(Kibbles), 3088 case (1 bsl (BitNo+1)) of 3089 X when Val < X -> 3090 {value,'BITS',Val}; 3091 _Big -> 3092 wrongValue(Val, Mfa) 3093 end; 3094 3095make_value_a_correct_value({value, String}, 3096 #asn1_type{bertype = 'OCTET STRING', 3097 hi = Hi, lo = Lo}, Mfa) -> 3098 check_octet_string(String, Hi, Lo, Mfa, 'OCTET STRING'); 3099 3100make_value_a_correct_value({value, String}, 3101 #asn1_type{bertype = 'IpAddress', 3102 hi = Hi, lo = Lo}, Mfa) -> 3103 check_octet_string(String, Hi, Lo, Mfa, 'IpAddress'); 3104 3105make_value_a_correct_value({value, Oid}, 3106 #asn1_type{bertype = 'OBJECT IDENTIFIER'}, 3107 _Mfa) -> 3108 case snmp_misc:is_oid(Oid) of 3109 true -> {value, 'OBJECT IDENTIFIER', Oid}; 3110 _Else -> {error, wrongType} 3111 end; 3112 3113make_value_a_correct_value({value, Val}, Asn1, _Mfa) 3114 when Asn1#asn1_type.bertype =:= 'Opaque' -> 3115 if is_list(Val) -> {value, 'Opaque', Val}; 3116 true -> {error, wrongType} 3117 end; 3118 3119make_value_a_correct_value({noValue, noSuchObject}, _ASN1Type, _Mfa) -> 3120 {value, noValue, noSuchObject}; 3121make_value_a_correct_value({noValue, noSuchInstance}, _ASN1Type, _Mfa) -> 3122 {value, noValue, noSuchInstance}; 3123make_value_a_correct_value({noValue, noSuchName}, _ASN1Type, _Mfa) -> 3124 %% Transform this into a v2 value. It is converted to noSuchName 3125 %% later if it was v1. If it was v2, we use noSuchInstance. 3126 {value, noValue, noSuchInstance}; 3127%% For backwards compatibility only - we really shouldn't allow this; 3128%% it makes no sense to return unSpecified for a variable! But we did 3129%% allow it previously. -- We transform unSpecified to noSuchInstance 3130%% (OTP-3303). 3131make_value_a_correct_value({noValue, unSpecified}, _ASN1Type, _Mfa) -> 3132 {value, noValue, noSuchInstance}; 3133make_value_a_correct_value(genErr, _ASN1Type, _MFA) -> 3134 {error, genErr}; 3135 3136make_value_a_correct_value(_WrongVal, _ASN1Type, undef) -> 3137 {error, genErr}; 3138 3139make_value_a_correct_value(WrongVal, ASN1Type, Mfa) -> 3140 user_err("Got ~w from ~w. (~w) Using genErr", 3141 [WrongVal, Mfa, ASN1Type]), 3142 {error, genErr}. 3143 3144check_integer(Val, Asn1, Mfa) -> 3145 case Asn1#asn1_type.assocList of 3146 undefined -> check_size(Val, Asn1, Mfa); 3147 Alist -> 3148 case snmp_misc:assq(enums, Alist) of 3149 {value, Enums} -> check_enums(Val, Asn1, Enums, Mfa); 3150 false -> check_size(Val, Asn1, Mfa) 3151 end 3152 end. 3153 3154check_octet_string(String, Hi, Lo, Mfa, Type) -> 3155 Len = (catch length(String)), % it might not be a list 3156 case snmp_misc:is_string(String) of 3157 true when Lo =:= undefined -> {value, Type, String}; 3158 true when Len =< Hi, Len >= Lo -> 3159 {value, Type, String}; 3160 true -> 3161 wrongLength(String, Mfa); 3162 _Else -> 3163 wrongType(String, Mfa) 3164 end. 3165 3166check_size(Val, #asn1_type{lo = Lo, hi = Hi, bertype = Type}, Mfa) 3167 when is_integer(Val) -> 3168 ?vtrace("check size of integer: " 3169 "~n Value: ~p" 3170 "~n Upper limit: ~p" 3171 "~n Lower limit: ~p" 3172 "~n BER-type: ~p", 3173 [Val,Hi,Lo,Type]), 3174 if 3175 (Lo =:= undefined) andalso (Hi =:= undefined) -> {value, Type, Val}; 3176 (Lo =:= undefined) andalso is_integer(Hi) andalso (Val =< Hi) -> 3177 {value, Type, Val}; 3178 is_integer(Lo) andalso (Val >= Lo) andalso (Hi =:= undefined) -> 3179 {value, Type, Val}; 3180 is_integer(Lo) andalso is_integer(Hi) andalso (Val >= Lo) andalso (Val =< Hi) -> 3181 {value, Type, Val}; 3182 true -> 3183 wrongValue(Val, Mfa) 3184 end; 3185check_size(Val, _, Mfa) -> 3186 wrongType(Val, Mfa). 3187 3188check_enums(Val, Asn1, Enums, Mfa) -> 3189 Association = 3190 if 3191 is_integer(Val) -> lists:keysearch(Val, 2, Enums); 3192 is_atom(Val) -> lists:keysearch(Val, 1, Enums); 3193 true -> {error, wrongType} 3194 end, 3195 case Association of 3196 {value, {_AliasIntName, Val2}} -> 3197 {value, Asn1#asn1_type.bertype, Val2}; 3198 false -> 3199 wrongValue(Val, Mfa); 3200 {error, wrongType} -> 3201 wrongType(Val, Mfa) 3202 end. 3203 3204wrongLength(Val, Mfa) -> 3205 report_err(Val, Mfa, wrongLength). 3206 3207wrongValue(Val, Mfa) -> 3208 report_err(Val, Mfa, wrongValue). 3209 3210wrongType(Val, Mfa) -> 3211 report_err(Val, Mfa, wrongType). 3212 3213report_err(_Val, undef, Err) -> 3214 {error, Err}; 3215report_err(Val, Mfa, Err) -> 3216 user_err("Got ~p from ~w. Using ~w", [Val, Mfa, Err]), 3217 {error, Err}. 3218 3219 3220is_valid_pdu_type('get-request') -> true; 3221is_valid_pdu_type('get-next-request') -> true; 3222is_valid_pdu_type('get-bulk-request') -> true; 3223is_valid_pdu_type('set-request') -> true; 3224is_valid_pdu_type(_) -> false. 3225 3226get_pdu_data() -> 3227 {get(net_if_data), 3228 get(snmp_request_id), 3229 get(snmp_address), 3230 get(snmp_community), 3231 get(snmp_context)}. 3232 3233put_pdu_data({Extra, ReqId, Address, Community, ContextName}) -> 3234 {put(net_if_data, Extra), 3235 put(snmp_address, Address), 3236 put(snmp_request_id, ReqId), 3237 put(snmp_community, Community), 3238 put(snmp_context, ContextName)}. 3239 3240tr_var(Oid, Idx) -> 3241 case snmp_misc:is_oid(Oid) of 3242 true -> 3243 {#varbind{oid = Oid, value = unSpecified, org_index = Idx}, 3244 Idx+1}; 3245 false -> throw({error, {bad_oid, Oid}}) 3246 end. 3247 3248tr_varbind(#varbind{value = Value}) -> Value. 3249 3250mapfoldl(F, Eas, Accu0, [Hd|Tail]) -> 3251 {R,Accu1} = apply(F, [Hd,Accu0|Eas]), 3252 {Accu2,Rs} = mapfoldl(F, Eas, Accu1, Tail), 3253 {Accu2,[R|Rs]}; 3254mapfoldl(_F, _Eas, Accu, []) -> {Accu,[]}. 3255 3256 3257 3258%% --------------------------------------------------------------------- 3259 3260short_name(none) -> ma; 3261short_name(_Pid) -> sa. 3262 3263worker_short_name(ma, mw) -> mamw; 3264worker_short_name(ma, sw) -> masw; 3265worker_short_name(ma, nw) -> manw; 3266worker_short_name(_, _) -> saw. 3267 3268trap_sender_short_name(ma) -> mats; 3269trap_sender_short_name(_) -> sats. 3270 3271pdu_handler_short_name(ma) -> maph; 3272pdu_handler_short_name(_) -> saph. 3273 3274 3275mib_server_verbosity(Pid,Verbosity) when is_pid(Pid) -> 3276 snmpa_mib:verbosity(Pid,Verbosity); 3277mib_server_verbosity(_Pid,_Verbosity) -> 3278 ok. 3279 3280note_store_verbosity(Pid,Verbosity) when is_pid(Pid) -> 3281 snmp_note_store:verbosity(Pid,Verbosity); 3282note_store_verbosity(_Pid,_Verbosity) -> 3283 ok. 3284 3285subagents_verbosity(V) -> 3286 subagents_verbosity(catch snmpa_mib:info(get(mibserver),subagents),V). 3287 3288subagents_verbosity([],_V) -> 3289 ok; 3290subagents_verbosity([{Pid,_Oid}|T],V) -> 3291 catch verbosity(Pid,V), %% On the agent 3292 catch verbosity(Pid,{subagents,V}), %% and it's subagents 3293 subagents_verbosity(T,V); 3294subagents_verbosity(_,_V) -> 3295 ok. 3296 3297 3298%% --------------------------------------------------------------------- 3299 3300local_engine_id(#state{type = master_agent}) -> 3301 ?DEFAULT_LOCAL_ENGINE_ID; 3302local_engine_id(_) -> 3303 %% subagent - 3304 %% we don't need this now, eventually the trap send 3305 %% request will reach the master-agent and then it 3306 %% will look up the proper engine id. 3307 ignore. 3308 3309 3310%% --------------------------------------------------------------------- 3311 3312handle_get_log_type(#state{net_if_mod = Mod}) 3313 when Mod =/= undefined -> 3314 case (catch Mod:get_log_type(get(net_if))) of 3315 {'EXIT', _} -> 3316 {error, not_supported}; 3317 Else -> 3318 Else 3319 end; 3320handle_get_log_type(_) -> 3321 {error, not_supported}. 3322 3323handle_set_log_type(#state{net_if_mod = Mod}, NewType) 3324 when Mod =/= undefined -> 3325 case (catch Mod:set_log_type(get(net_if), NewType)) of 3326 {'EXIT', _} -> 3327 {error, not_supported}; 3328 Else -> 3329 Else 3330 end; 3331handle_set_log_type(_, _) -> 3332 {error, not_supported}. 3333 3334 3335handle_get_request_limit(#state{net_if_mod = Mod}) 3336 when Mod =/= undefined -> 3337 case (catch Mod:get_request_limit(get(net_if))) of 3338 {'EXIT', _} -> 3339 {error, not_supported}; 3340 Else -> 3341 Else 3342 end; 3343handle_get_request_limit(_) -> 3344 {error, not_supported}. 3345 3346handle_set_request_limit(#state{net_if_mod = Mod}, NewLimit) 3347 when Mod =/= undefined -> 3348 case (catch Mod:set_request_limit(get(net_if), NewLimit)) of 3349 {'EXIT', _} -> 3350 {error, not_supported}; 3351 Else -> 3352 Else 3353 end; 3354handle_set_request_limit(_, _) -> 3355 {error, not_supported}. 3356 3357 3358agent_info(#state{worker = W, set_worker = SW, notif_worker = NW}) -> 3359 case (catch get_agent_info(W, SW, NW)) of 3360 Info when is_list(Info) -> 3361 Info; 3362 E -> 3363 [{error, E}] 3364 end. 3365 3366get_agent_info(W, SW, NW) -> 3367 MASz = proc_mem(self()), 3368 ATSz = tab_mem(snmp_agent_table), 3369 CCSz = tab_mem(snmp_community_cache), 3370 VacmSz = tab_mem(snmpa_vacm), 3371 [{process_memory, 3372 [{master_agent, MASz}] ++ 3373 process_memory(worker, W) ++ 3374 process_memory(set_worker, SW) ++ 3375 process_memory(notif_worker, NW)}, 3376 {db_memory, 3377 [{agent, ATSz}, 3378 {community_cache, CCSz}, 3379 {vacm, VacmSz}]}]. 3380 3381process_memory(Tag, P) when is_pid(P) -> 3382 [{Tag, proc_mem(P)}]; 3383process_memory(_, _) -> 3384 []. 3385 3386proc_mem(P) when is_pid(P) -> 3387 case (catch erlang:process_info(P, memory)) of 3388 {memory, Sz} when is_integer(Sz) -> 3389 Sz; 3390 _ -> 3391 undefined 3392 end; 3393proc_mem(_) -> 3394 undefined. 3395 3396tab_mem(T) -> 3397 case (catch ets:info(T, memory)) of 3398 Sz when is_integer(Sz) -> 3399 Sz; 3400 _ -> 3401 undefined 3402 end. 3403 3404net_if_info(#state{net_if_mod = Mod}) when Mod =/= undefined -> 3405 case (catch Mod:info(get(net_if))) of 3406 Info when is_list(Info) -> 3407 Info; 3408 E -> 3409 [{error, E}] 3410 end; 3411net_if_info(_) -> 3412 %% This could be a result of a code upgrade 3413 %% Make best effert 3414 [{process_memory, proc_mem(get(net_if))}]. 3415 3416note_store_info(#state{note_store = NS}) -> 3417 case (catch snmp_note_store:info(NS)) of 3418 Info when is_list(Info) -> 3419 Info; 3420 E -> 3421 [{error, E}] 3422 end. 3423 3424symbolic_store_info() -> 3425 case (catch snmpa_symbolic_store:info()) of 3426 Info when is_list(Info) -> 3427 Info; 3428 E -> 3429 [{error, E}] 3430 end. 3431 3432local_db_info() -> 3433 case (catch snmpa_local_db:info()) of 3434 Info when is_list(Info) -> 3435 Info; 3436 E -> 3437 [{error, E}] 3438 end. 3439 3440mib_server_info() -> 3441 case (catch snmpa_mib:info(get(mibserver))) of 3442 Info when is_list(Info) -> 3443 Info; 3444 E -> 3445 [{error, E}] 3446 end. 3447 3448get_stats_counters() -> 3449 Counters = snmpa_mpd:counters(), 3450 get_stats_counters(Counters, []). 3451 3452get_stats_counters([], Acc) -> 3453 lists:reverse(Acc); 3454get_stats_counters([Counter|Counters], Acc) -> 3455 case ets:lookup(snmp_agent_table, Counter) of 3456 [CounterVal] -> 3457 get_stats_counters(Counters, [CounterVal|Acc]); 3458 _ -> 3459 get_stats_counters(Counters, Acc) 3460 end. 3461 3462 3463%% --------------------------------------------------------------------- 3464 3465%% info_msg(F, A) -> 3466%% ?snmpa_info(F, A). 3467 3468warning_msg(F, A) -> 3469 ?snmpa_warning(F, A). 3470 3471error_msg(F, A) -> 3472 ?snmpa_error(F, A). 3473 3474%% --- 3475 3476config_err(F, A) -> 3477 snmpa_error:config_err(F, A). 3478 3479user_err(F, A) -> 3480 snmpa_error:user_err(F, A). 3481 3482 3483%% --------------------------------------------------------------------- 3484 3485maybe_call(Server, Req) -> 3486 case (wis(Server) =:= self()) of 3487 false -> 3488 call(Server, Req); 3489 true -> 3490 Server ! Req 3491 end. 3492 3493call(Server, Req) -> 3494 gen_server:call(Server, Req, infinity). 3495 3496cast(Server, Msg) -> 3497 gen_server:cast(Server, Msg). 3498 3499 3500%% --------------------------------------------------------------------- 3501 3502get_verbosity(Opts) -> 3503 get_option(verbosity, Opts, ?default_verbosity). 3504 3505get_mibs(Opts) -> 3506 get_option(mibs, Opts, []). 3507 3508get_mib_storage(Opts) -> 3509 get_option(mib_storage, Opts). 3510 3511get_set_mechanism(Opts) -> 3512 get_option(set_mechanism, Opts, snmpa_set). 3513 3514get_get_mechanism(Opts) -> 3515 get_option(get_mechanism, Opts, snmpa_get). 3516 3517get_authentication_service(Opts) -> 3518 get_option(authentication_service, Opts, snmpa_acm). 3519 3520get_multi_threaded(Opts) -> 3521 get_option(multi_threaded, Opts, false). 3522 3523get_versions(Opts) -> 3524 get_option(versions, Opts, [v1,v2,v3]). 3525 3526get_gb_max_vbs(Opts) -> 3527 get_option(gb_max_vbs, Opts, infinity). 3528 3529get_note_store_opt(Opts) -> 3530 get_option(note_store, Opts, []). 3531 3532get_net_if_opt(Opts) -> 3533 get_option(net_if, Opts, []). 3534 3535get_net_if_verbosity(Opts) -> 3536 get_option(verbosity, Opts, silence). 3537 3538get_net_if_module(Opts) -> 3539 get_option(module, Opts, snmpa_net_if). 3540 3541get_net_if_options(Opts) -> 3542 get_option(options, Opts, []). 3543 3544 3545net_if_verbosity(Pid,Verbosity) when is_pid(Pid) -> 3546 Pid ! {verbosity,Verbosity}; 3547net_if_verbosity(_Pid,_Verbosity) -> 3548 ok. 3549 3550 3551get_option(Key, Opts) -> 3552 snmp_misc:get_option(Key, Opts). 3553 3554get_option(Key, Opts, Default) -> 3555 snmp_misc:get_option(Key, Opts, Default). 3556 3557 3558%% --------------------------------------------------------------------- 3559 3560 3561%% i(F) -> 3562%% i(F, []). 3563 3564%% i(F, A) -> 3565%% io:format("~p: " ++ F ++ "~n", [?MODULE|A]). 3566 3567 3568 3569