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