aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/manager/snmpm_server.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/src/manager/snmpm_server.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/src/manager/snmpm_server.erl')
-rw-r--r--lib/snmp/src/manager/snmpm_server.erl3117
1 files changed, 3117 insertions, 0 deletions
diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl
new file mode 100644
index 0000000000..30aacc0ec3
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_server.erl
@@ -0,0 +1,3117 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_server).
+
+%%----------------------------------------------------------------------
+%% This module implements a simple SNMP manager for Erlang.
+%%
+%%----------------------------------------------------------------------
+
+%% User interface
+-export([start_link/0, stop/0,
+ is_started/0,
+
+ load_mib/1, unload_mib/1,
+
+ register_user/4, register_user_monitor/4, unregister_user/1,
+
+ sync_get/4, sync_get/5, sync_get/6,
+ async_get/4, async_get/5, async_get/6,
+ sync_get_next/4, sync_get_next/5, sync_get_next/6,
+ async_get_next/4, async_get_next/5, async_get_next/6,
+ sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8,
+ async_get_bulk/6, async_get_bulk/7, async_get_bulk/8,
+ sync_set/4, sync_set/5, sync_set/6,
+ async_set/4, async_set/5, async_set/6,
+ cancel_async_request/2,
+
+ %% discovery/2, discovery/3, discovery/4, discovery/5, discovery/6,
+
+ %% system_info_updated/2,
+ get_log_type/0, set_log_type/1,
+
+ reconfigure/0,
+
+ info/0,
+ verbosity/1, verbosity/2
+
+ ]).
+
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+%% GCT exports
+-export([gct_init/1, gct/2]).
+
+
+-include("snmpm_internal.hrl").
+-include("snmp_debug.hrl").
+-include("snmp_types.hrl").
+-include("STANDARD-MIB.hrl").
+-include("SNMP-FRAMEWORK-MIB.hrl").
+-include("snmp_verbosity.hrl").
+
+
+%%----------------------------------------------------------------------
+
+-define(SERVER, ?MODULE).
+
+-define(SYNC_GET_TIMEOUT, 5000).
+-define(SYNC_SET_TIMEOUT, 5000).
+-define(DEFAULT_ASYNC_EXPIRE, 5000).
+-define(EXTRA_INFO, undefined).
+
+-define(SNMP_AGENT_PORT, 161).
+
+
+-ifdef(snmp_debug).
+-define(GS_START_LINK(Args),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, Args,
+ [{debug,[trace]}])).
+-else.
+-define(GS_START_LINK(Args),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, Args, [])).
+-endif.
+
+
+
+%%----------------------------------------------------------------------
+
+-record(state,
+ {parent,
+ gct,
+ note_store,
+ note_store_ref,
+ net_if,
+ net_if_mod,
+ net_if_ref,
+ req, %% ???? Last request id in outgoing message
+ oid, %% ???? Last oid in request outgoing message
+ mini_mib
+ }
+ ).
+
+%% The active state is to ensure that nothing unpleasant happens
+%% during (after) a code_change. At the initial start of the
+%% application, this process (GCT) will make one run and then
+%% deactivate (unless some async request has been issued in the
+%% meantime).
+-record(gct, {parent, state = active, timeout}).
+
+-record(request,
+ {id,
+ user_id,
+ reg_type,
+ target,
+ addr,
+ port,
+ type,
+ data,
+ ref,
+ mon,
+ from,
+ discovery = false,
+ expire = infinity % When shall the request expire (time in ms)
+ }
+ ).
+
+-record(monitor,
+ {id,
+ mon,
+ proc
+ }
+ ).
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+
+start_link() ->
+ ?d("start_link -> entry", []),
+ Args = [],
+ ?GS_START_LINK(Args).
+
+stop() ->
+ call(stop).
+
+is_started() ->
+ case (catch call(is_started, 1000)) of
+ Bool when ((Bool =:= true) orelse (Bool =:= false)) ->
+ Bool;
+ _ ->
+ false
+ end.
+
+load_mib(MibFile) when is_list(MibFile) ->
+ call({load_mib, MibFile}).
+
+unload_mib(Mib) when is_list(Mib) ->
+ call({unload_mib, Mib}).
+
+
+register_user(UserId, UserMod, UserData, DefaultAgentConfig) ->
+ snmpm_config:register_user(UserId, UserMod, UserData, DefaultAgentConfig).
+
+register_user_monitor(Id, Module, Data, DefaultAgentConfig) ->
+ case register_user(Id, Module, Data, DefaultAgentConfig) of
+ ok ->
+ case call({monitor_user, Id, self()}) of
+ ok ->
+ ok;
+ Error ->
+ unregister_user(Id),
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+unregister_user(UserId) ->
+ call({unregister_user, UserId}).
+
+
+%% -- [sync] get --
+
+sync_get(UserId, TargetName, CtxName, Oids) ->
+ sync_get(UserId, TargetName, CtxName, Oids,
+ ?SYNC_GET_TIMEOUT).
+
+sync_get(UserId, TargetName, CtxName, Oids, Timeout) ->
+ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO).
+
+sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo)
+ when is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ call({sync_get, self(), UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}).
+
+%% -- [async] get --
+
+async_get(UserId, TargetName, CtxName, Oids) ->
+ async_get(UserId, TargetName, CtxName, Oids,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_get(UserId, TargetName, CtxName, Oids, Expire) ->
+ async_get(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO).
+
+async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo)
+ when (is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) andalso (Expire >= 0)) ->
+ call({async_get, self(), UserId, TargetName, CtxName, Oids, Expire,
+ ExtraInfo}).
+
+%% -- [sync] get-next --
+
+sync_get_next(UserId, TargetName, CtxName, Oids) ->
+ sync_get_next(UserId, TargetName, CtxName, Oids, ?SYNC_GET_TIMEOUT,
+ ?EXTRA_INFO).
+
+sync_get_next(UserId, TargetName, CtxName, Oids, Timeout) ->
+ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO).
+
+sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo)
+ when is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ call({sync_get_next, self(), UserId, TargetName, CtxName, Oids, Timeout,
+ ExtraInfo}).
+
+%% -- [async] get-next --
+
+async_get_next(UserId, TargetName, CtxName, Oids) ->
+ async_get_next(UserId, TargetName, CtxName, Oids,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_get_next(UserId, TargetName, CtxName, Oids, Expire) ->
+ async_get_next(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO).
+
+async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo)
+ when (is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) andalso (Expire >= 0)) ->
+ call({async_get_next, self(), UserId, TargetName, CtxName, Oids,
+ Expire, ExtraInfo}).
+
+%% -- [sync] get-bulk --
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) ->
+ sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ ?SYNC_GET_TIMEOUT, ?EXTRA_INFO).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout) ->
+ sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ Timeout, ?EXTRA_INFO).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout,
+ ExtraInfo)
+ when is_list(TargetName) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ call({sync_get_bulk, self(), UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}).
+
+%% -- [async] get-bulk --
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire) ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ Expire, ?EXTRA_INFO).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire,
+ ExtraInfo)
+ when is_list(TargetName) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) ->
+ call({async_get_bulk, self(), UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo}).
+
+%% -- [sync] set --
+
+%% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value}
+sync_set(UserId, TargetName, CtxName, VarsAndVals) ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals,
+ ?SYNC_SET_TIMEOUT, ?EXTRA_INFO).
+
+sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout) ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals,
+ Timeout, ?EXTRA_INFO).
+
+sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo)
+ when is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Timeout) ->
+ call({sync_set, self(), UserId, TargetName,
+ CtxName, VarsAndVals, Timeout, ExtraInfo}).
+
+%% -- [async] set --
+
+async_set(UserId, TargetName, CtxName, VarsAndVals) ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_set(UserId, TargetName, CtxName, VarsAndVals, Expire) ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals,
+ Expire, ?EXTRA_INFO).
+
+async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo)
+ when (is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Expire) andalso (Expire >= 0)) ->
+ call({async_set, self(), UserId, TargetName,
+ CtxName, VarsAndVals, Expire, ExtraInfo}).
+
+
+cancel_async_request(UserId, ReqId) ->
+ call({cancel_async_request, UserId, ReqId}).
+
+
+%% discovery(UserId, BAddr) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, [],
+%% ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Config) when is_list(Config) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, Config,
+%% ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO);
+
+%% discovery(UserId, BAddr, Expire) when is_integer(Expire) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, [], Expire, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Config, Expire) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, Config, Expire, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Port, Config, Expire) ->
+%% discovery(UserId, BAddr, Port, Config, Expire, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Port, Config, Expire, ExtraInfo) ->
+%% call({discovery, self(), UserId, BAddr, Port, Config, Expire, ExtraInfo}).
+
+
+verbosity(Verbosity) ->
+ case ?vvalidate(Verbosity) of
+ Verbosity ->
+ call({verbosity, Verbosity});
+ _ ->
+ {error, {invalid_verbosity, Verbosity}}
+ end.
+
+info() ->
+ call(info).
+
+verbosity(net_if = Ref, Verbosity) ->
+ verbosity2(Ref, Verbosity);
+verbosity(note_store = Ref, Verbosity) ->
+ verbosity2(Ref, Verbosity).
+
+verbosity2(Ref, Verbosity) ->
+ case ?vvalidate(Verbosity) of
+ Verbosity ->
+ call({verbosity, Ref, Verbosity});
+ _ ->
+ {error, {invalid_verbosity, Verbosity}}
+ end.
+
+%% Target -> all | server | net_if
+%% system_info_updated(Target, What) ->
+%% call({system_info_updated, Target, What}).
+
+get_log_type() ->
+ call(get_log_type).
+
+set_log_type(NewType) ->
+ call({set_log_type, NewType}).
+
+reconfigure() ->
+ call(reconfigure).
+
+
+%%----------------------------------------------------------------------
+%% Options: List of
+%% {community, String ("public" is default}
+%% {mibs, List of Filenames}
+%% {trap_udp, integer() (default 5000)}
+%% {conf_dir, string()}
+%% {log_dir, string()}
+%% {db_dir, string()}
+%% {db_repair, true | false}
+%%----------------------------------------------------------------------
+init(_) ->
+ ?d("init -> entry", []),
+ case (catch do_init()) of
+ {ok, State} ->
+ {ok, State};
+ {error, Reason} ->
+ {stop, Reason}
+ end.
+
+
+%% Put all config stuff in a snmpm_config module/process.
+%% Tables should be protected so that it is cheap to
+%% read. Writing has to go through the interface...
+
+do_init() ->
+ process_flag(trap_exit, true),
+ {ok, Prio} = snmpm_config:system_info(prio),
+ process_flag(priority, Prio),
+
+ {ok, Verbosity} = snmpm_config:system_info(server_verbosity),
+ put(sname, mse),
+ put(verbosity, Verbosity),
+ ?vlog("starting", []),
+
+ %% Start the garbage collector timer process
+ {ok, Timeout} = snmpm_config:system_info(server_timeout),
+ {ok, GCT} = gct_start(Timeout),
+
+ %% -- Create request table --
+ ets:new(snmpm_request_table,
+ [set, protected, named_table, {keypos, #request.id}]),
+
+ %% -- Create monitor table --
+ ets:new(snmpm_monitor_table,
+ [set, protected, named_table, {keypos, #monitor.id}]),
+
+ %% -- Start the note-store and net-if processes --
+ {NoteStore, NoteStoreRef} = do_init_note_store(Prio),
+ {NetIf, NetIfModule, NetIfRef} = do_init_net_if(NoteStore),
+
+ MiniMIB = snmpm_config:make_mini_mib(),
+ State = #state{mini_mib = MiniMIB,
+ gct = GCT,
+ note_store = NoteStore,
+ note_store_ref = NoteStoreRef,
+ net_if = NetIf,
+ net_if_mod = NetIfModule,
+ net_if_ref = NetIfRef},
+ ?vlog("started", []),
+ {ok, State}.
+
+
+do_init_note_store(Prio) ->
+ ?vdebug("try start note store", []),
+ {ok, Verbosity} = snmpm_config:system_info(note_store_verbosity),
+ {ok, Timeout} = snmpm_config:system_info(note_store_timeout),
+ Opts = [{sname, mns},
+ {verbosity, Verbosity},
+ {timeout, Timeout}],
+ case snmpm_misc_sup:start_note_store(Prio, Opts) of
+ {ok, Pid} ->
+ ?vtrace("do_init_note_store -> Pid: ~p", [Pid]),
+ Ref = erlang:monitor(process, Pid),
+ {Pid, Ref};
+ {error, Reason} ->
+ ?vlog("failed starting note-store - Reason: "
+ "~n Reason: ~p"
+ "~n", [Reason]),
+ throw({error, {failed_starting_note_store, Reason}})
+ end.
+
+do_init_net_if(NoteStore) ->
+ ?vdebug("try start net if", []),
+ {ok, NetIfModule} = snmpm_config:system_info(net_if_module),
+ case snmpm_misc_sup:start_net_if(NetIfModule, NoteStore) of
+ {ok, Pid} ->
+ ?vtrace("do_init_net_if -> Pid: ~p", [Pid]),
+ Ref = erlang:monitor(process, Pid),
+ {Pid, NetIfModule, Ref};
+ {error, Reason} ->
+ ?vlog("failed starting net-if - Reason: "
+ "~n Reason: ~p"
+ "~n", [Reason]),
+ throw({error, {failed_starting_net_if, Reason}})
+ end.
+
+%% ---------------------------------------------------------------------
+%% ---------------------------------------------------------------------
+
+handle_call({monitor_user, Id, Pid}, _From, State) when is_pid(Pid) ->
+ ?vlog("received monitor_user request for ~w [~w]", [Id, Pid]),
+ Reply =
+ case ets:lookup(snmpm_monitor_table, Id) of
+ [#monitor{proc = Pid}] ->
+ ?vdebug("already monitored", []),
+ ok;
+
+ [#monitor{proc = OtherPid}] ->
+ ?vinfo("already registered to ~w", [OtherPid]),
+ {error, {already_monitored, OtherPid}};
+
+ [] ->
+ Ref = erlang:monitor(process, Pid),
+ ?vtrace("monitor ref: ~w", [Ref]),
+ Mon = #monitor{id = Id, mon = Ref, proc = Pid},
+ ets:insert(snmpm_monitor_table, Mon),
+ ok
+ end,
+ {reply, Reply, State};
+
+handle_call({unregister_user, UserId}, _From, State) ->
+ ?vlog("received request to unregister user ~p", [UserId]),
+
+ %% 1) If this user is monitored, then demonitor
+ ?vtrace("handle_call(unregister_user) -> maybe demonitor", []),
+ case ets:lookup(snmpm_monitor_table, UserId) of
+ [] ->
+ ok;
+ [#monitor{mon = M}] ->
+ maybe_demonitor(M), % This is really overkill (meybe_), but...
+ ok
+ end,
+
+ %% 2) Delete all outstanding requests from this user
+ ?vtrace("handle_call(unregister_user) -> "
+ "delete all outstanding requests for user", []),
+ Pat = #request{user_id = UserId,
+ id = '$1', ref = '$2', mon = '$3', _ = '_'},
+ Match = ets:match(snmpm_request_table, Pat),
+ ?vtrace("handle_call(unregister_user) -> Match: ~p", [Match]),
+ F1 = fun([ReqId, Ref, MonRef]) ->
+ ets:delete(snmpm_request_table, ReqId),
+ cancel_timer(Ref),
+ maybe_demonitor(MonRef),
+ ok
+ end,
+ lists:foreach(F1, Match),
+
+ %% 3) Unregister all agents registered by this user
+ ?vdebug("handle_call(unregister_user) -> "
+ "unregister all agents registered by user", []),
+ Agents = snmpm_config:which_agents(UserId),
+ ?vtrace("handle_call(unregister_user) -> Agents: ~p", [Agents]),
+ F2 = fun(TargetName) ->
+ snmpm_config:unregister_agent(UserId, TargetName)
+ end,
+ lists:foreach(F2, Agents),
+
+ %% 4) Unregister the user
+ ?vdebug("handle_call(unregister_user) -> unregister user", []),
+ Reply = snmpm_config:unregister_user(UserId),
+ ?vtrace("handle_call(unregister_user) -> Reply: ~p", [Reply]),
+ {reply, Reply, State};
+
+
+%% We will reply to this request later, when the reply comes in from the
+%% agent, or when the timeout hits (unless we get an error now).
+handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo},
+ From, State) ->
+ ?vlog("received sync_get [~p] request", [CtxName]),
+ case (catch handle_sync_get(Pid,
+ UserId, TargetName, CtxName, Oids,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) ->
+ ?vlog("received sync_get_next [~p] request", [CtxName]),
+ case (catch handle_sync_get_next(Pid,
+ UserId, TargetName, CtxName, Oids,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+%% Check agent version? This op not in v1
+handle_call({sync_get_bulk, Pid, UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo},
+ From, State) ->
+ ?vlog("received sync_get_bulk [~p] request", [CtxName]),
+ case (catch handle_sync_get_bulk(Pid,
+ UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({sync_set, Pid, UserId, TargetName,
+ CtxName, VarsAndVals, Timeout, ExtraInfo},
+ From, State) ->
+ ?vlog("received sync_set [~p] request", [CtxName]),
+ case (catch handle_sync_set(Pid,
+ UserId, TargetName, CtxName, VarsAndVals,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({async_get, Pid, UserId, TargetName,
+ CtxName, Oids, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_get [~p] request", [CtxName]),
+ Reply = (catch handle_async_get(Pid, UserId, TargetName, CtxName, Oids,
+ Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+handle_call({async_get_next, Pid, UserId, TargetName,
+ CtxName, Oids, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_get_next [~p] request", [CtxName]),
+ Reply = (catch handle_async_get_next(Pid, UserId, TargetName, CtxName,
+ Oids, Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+%% Check agent version? This op not in v1
+handle_call({async_get_bulk, Pid, UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_get_bulk [~p] request", [CtxName]),
+ Reply = (catch handle_async_get_bulk(Pid,
+ UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids,
+ Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+handle_call({async_set, Pid, UserId, TargetName,
+ CtxName, VarsAndVals, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_set [~p] request", [CtxName]),
+ Reply = (catch handle_async_set(Pid, UserId, TargetName, CtxName,
+ VarsAndVals, Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+handle_call({cancel_async_request, UserId, ReqId}, _From, State) ->
+ ?vlog("received cancel_async_request request", []),
+ Reply = (catch handle_cancel_async_request(UserId, ReqId, State)),
+ {reply, Reply, State};
+
+
+%% handle_call({discovery, Pid, UserId, BAddr, Port, Config, Expire, ExtraInfo},
+%% _From, State) ->
+%% ?vlog("received discovery request", []),
+%% Reply = (catch handle_discovery(Pid, UserId, BAddr, Port, Config,
+%% Expire, ExtraInfo, State)),
+%% {reply, Reply, State};
+
+
+handle_call({load_mib, Mib}, _From, State) ->
+ ?vlog("received load_mib request", []),
+ case snmpm_config:load_mib(Mib) of
+ ok ->
+ MiniMIB = snmpm_config:make_mini_mib(),
+ {reply, ok, State#state{mini_mib = MiniMIB}};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({unload_mib, Mib}, _From, State) ->
+ ?vlog("received unload_mib request", []),
+ case snmpm_config:unload_mib(Mib) of
+ ok ->
+ MiniMIB = snmpm_config:make_mini_mib(),
+ {reply, ok, State#state{mini_mib = MiniMIB}};
+ Error ->
+ {reply, Error, State}
+ end;
+
+handle_call({verbosity, Verbosity}, _From, State) ->
+ ?vlog("received verbosity request", []),
+ put(verbosity, Verbosity),
+ {reply, ok, State};
+
+handle_call({verbosity, net_if, Verbosity}, _From,
+ #state{net_if = Pid, net_if_mod = Mod} = State) ->
+ ?vlog("received net_if verbosity request", []),
+ Mod:verbosity(Pid, Verbosity),
+ {reply, ok, State};
+
+handle_call({verbosity, note_store, Verbosity}, _From,
+ #state{note_store = Pid} = State) ->
+ ?vlog("received note_store verbosity request", []),
+ snmp_note_store:verbosity(Pid, Verbosity),
+ {reply, ok, State};
+
+handle_call(reconfigure, _From, State) ->
+ ?vlog("received reconfigure request", []),
+ Reply = {error, not_implemented},
+ {reply, Reply, State};
+
+handle_call(info, _From, State) ->
+ ?vlog("received info request", []),
+ Reply = get_info(State),
+ {reply, Reply, State};
+
+handle_call(is_started, _From, State) ->
+ ?vlog("received is_started request", []),
+ IsStarted = is_started(State),
+ {reply, IsStarted, State};
+
+%% handle_call({system_info_updated, Target, What}, _From, State) ->
+%% ?vlog("received system_info_updated request: "
+%% "~n Target: ~p"
+%% "~n What: ~p", [Target, What]),
+%% Reply = handle_system_info_updated(State, Target, What),
+%% {reply, Reply, State};
+
+handle_call(get_log_type, _From, State) ->
+ ?vlog("received get_log_type request", []),
+ Reply = handle_get_log_type(State),
+ {reply, Reply, State};
+
+handle_call({set_log_type, NewType}, _From, State) ->
+ ?vlog("received set_log_type request: "
+ "~n NewType: ~p", [NewType]),
+ Reply = handle_set_log_type(State, NewType),
+ {reply, Reply, State};
+
+handle_call(stop, _From, State) ->
+ ?vlog("received stop request", []),
+ {stop, normal, ok, State};
+
+
+handle_call(Req, _From, State) ->
+ warning_msg("received unknown request: ~n~p", [Req]),
+ {reply, {error, unknown_request}, State}.
+
+
+handle_cast(Msg, State) ->
+ warning_msg("received unknown message: ~n~p", [Msg]),
+ {noreply, State}.
+
+
+handle_info({sync_timeout, ReqId, From}, State) ->
+ ?vlog("received sync_timeout [~w] message", [ReqId]),
+ handle_sync_timeout(ReqId, From, State),
+ {noreply, State};
+
+
+handle_info({snmp_error, Pdu, Reason}, State) ->
+ ?vlog("received snmp_error message", []),
+ handle_snmp_error(Pdu, Reason, State),
+ {noreply, State};
+
+handle_info({snmp_error, Reason, Addr, Port}, State) ->
+ ?vlog("received snmp_error message", []),
+ handle_snmp_error(Addr, Port, -1, Reason, State),
+ {noreply, State};
+
+handle_info({snmp_error, ReqId, Reason, Addr, Port}, State) ->
+ ?vlog("received snmp_error message", []),
+ handle_snmp_error(Addr, Port, ReqId, Reason, State),
+ {noreply, State};
+
+%% handle_info({snmp_error, ReqId, Pdu, Reason, Addr, Port}, State) ->
+%% ?vlog("received snmp_error message", []),
+%% handle_snmp_error(Pdu, ReqId, Reason, Addr, Port, State),
+%% {noreply, State};
+
+
+handle_info({snmp_pdu, Pdu, Addr, Port}, State) ->
+ ?vlog("received snmp_pdu message", []),
+ handle_snmp_pdu(Pdu, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info({snmp_trap, Trap, Addr, Port}, State) ->
+ ?vlog("received snmp_trap message", []),
+ handle_snmp_trap(Trap, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info({snmp_inform, Ref, Pdu, Addr, Port}, State) ->
+ ?vlog("received snmp_inform message", []),
+ handle_snmp_inform(Ref, Pdu, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info({snmp_report, {ok, Pdu}, Addr, Port}, State) ->
+ handle_snmp_report(Pdu, Addr, Port, State),
+ {noreply, State};
+
+handle_info({snmp_report, {error, ReqId, Info, Pdu}, Addr, Port}, State) ->
+ handle_snmp_report(ReqId, Pdu, Info, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info(gc_timeout, #state{gct = GCT} = State) ->
+ ?vlog("received gc_timeout message", []),
+ handle_gc(GCT),
+ {noreply, State};
+
+
+handle_info({'DOWN', _MonRef, process, Pid, _Reason},
+ #state{note_store = NoteStore,
+ net_if = Pid} = State) ->
+ ?vlog("received 'DOWN' message regarding net_if", []),
+ {NetIf, _, Ref} = do_init_net_if(NoteStore),
+ {noreply, State#state{net_if = NetIf, net_if_ref = Ref}};
+
+
+handle_info({'DOWN', _MonRef, process, Pid, _Reason},
+ #state{note_store = Pid,
+ net_if = NetIf,
+ net_if_mod = Mod} = State) ->
+ ?vlog("received 'DOWN' message regarding note_store", []),
+ {ok, Prio} = snmpm_config:system_info(prio),
+ {NoteStore, Ref} = do_init_note_store(Prio),
+ Mod:note_store(NetIf, NoteStore),
+ {noreply, State#state{note_store = NoteStore, note_store_ref = Ref}};
+
+
+handle_info({'DOWN', MonRef, process, Pid, Reason}, State) ->
+ ?vlog("received 'DOWN' message (~w) from ~w "
+ "~n Reason: ~p", [MonRef, Pid, Reason]),
+ handle_down(MonRef),
+ {noreply, State};
+
+
+handle_info({'EXIT', Pid, Reason}, #state{gct = Pid} = State) ->
+ ?vlog("received 'EXIT' message from the GCT (~w) process: "
+ "~n ~p", [Pid, Reason]),
+ {ok, Timeout} = snmpm_config:system_info(server_timeout),
+ {ok, GCT} = gct_start(Timeout),
+ {noreply, State#state{gct = GCT}};
+
+
+handle_info(Info, State) ->
+ warning_msg("received unknown info: ~n~p", [Info]),
+ {noreply, State}.
+
+
+%%----------------------------------------------------------
+%% Code change
+%%----------------------------------------------------------
+
+% downgrade
+code_change({down, _Vsn}, #state{gct = Pid} = State, _Extra) ->
+ ?d("code_change(down) -> entry", []),
+ gct_code_change(Pid),
+ {ok, State};
+
+% upgrade
+code_change(_Vsn, #state{gct = Pid} = State0, _Extra) ->
+ ?d("code_change(up) -> entry", []),
+ gct_code_change(Pid),
+ MiniMIB = snmpm_config:make_mini_mib(),
+ State = State0#state{mini_mib = MiniMIB},
+ {ok, State}.
+
+
+%%----------------------------------------------------------
+%% Terminate
+%%----------------------------------------------------------
+
+terminate(Reason, #state{gct = GCT}) ->
+ ?vdebug("terminate: ~p",[Reason]),
+ gct_stop(GCT),
+ snmpm_misc_sup:stop_note_store(),
+ snmpm_misc_sup:stop_net_if(),
+ ok.
+
+
+%%----------------------------------------------------------------------
+%%
+%%----------------------------------------------------------------------
+
+handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo,
+ From, State) ->
+ ?vtrace("handle_sync_get -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_get -> send a ~p message", [Vsn]),
+ ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port,
+ ExtraInfo, State),
+ ?vdebug("handle_sync_get -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_get -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+ Error ->
+ ?vinfo("failed retrieving agent data for get:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout,
+ ExtraInfo, From, State) ->
+ ?vtrace("handle_sync_get_next -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_get_next -> send a ~p message", [Vsn]),
+ ReqId = send_get_next_request(Oids, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_sync_get_next -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_get_next -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_next,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-next:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_sync_get_bulk(Pid, UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids, Timeout,
+ ExtraInfo, From, State) ->
+ ?vtrace("handle_sync_get_bulk -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n NonRep: ~p"
+ "~n MaxRep: ~p"
+ "~n Oids: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids,
+ Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_get_bulk -> send a ~p message", [Vsn]),
+ ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port,
+ NonRep, MaxRep, ExtraInfo, State),
+ ?vdebug("handle_sync_get_bulk -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_get_bulk -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_bulk,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-bulk:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout,
+ ExtraInfo, From, State) ->
+ ?vtrace("handle_sync_set -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n VarsAndVals: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_set -> send a ~p message", [Vsn]),
+ ReqId = send_set_request(VarsAndVals, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_sync_set -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_set -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = set,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+
+ Error ->
+ ?vinfo("failed retrieving agent data for set:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo,
+ State) ->
+ ?vtrace("handle_async_get -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_get -> send a ~p message", [Vsn]),
+ ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port,
+ ExtraInfo, State),
+ ?vdebug("handle_async_get -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get,
+ data = MsgData,
+ expire = t() + Expire},
+
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire,
+ ExtraInfo, State) ->
+ ?vtrace("handle_async_get_next -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_get_next -> send a ~p message", [Vsn]),
+ ReqId = send_get_next_request(Oids, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_async_get_next -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_next,
+ data = MsgData,
+ expire = t() + Expire},
+
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-next:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_get_bulk(Pid, UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids, Expire,
+ ExtraInfo, State) ->
+ ?vtrace("handle_async_get_bulk -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n NonRep: ~p"
+ "~n MaxRep: ~p"
+ "~n Oids: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_get_bulk -> send a ~p message", [Vsn]),
+ ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port,
+ NonRep, MaxRep, ExtraInfo, State),
+ ?vdebug("handle_async_get_bulk -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_bulk,
+ data = MsgData,
+ expire = t() + Expire},
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-bulk:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire,
+ ExtraInfo, State) ->
+ ?vtrace("handle_async_set -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n VarsAndVals: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, VarsAndVals, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_set -> send a ~p message", [Vsn]),
+ ReqId = send_set_request(VarsAndVals, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_async_set -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = set,
+ data = MsgData,
+ expire = t() + Expire},
+
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for set:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_cancel_async_request(UserId, ReqId, _State) ->
+ ?vtrace("handle_cancel_async_request -> entry with"
+ "~n UserId: ~p"
+ "~n ReqId: ~p", [UserId, ReqId]),
+ case ets:lookup(snmpm_request_table, ReqId) of
+ [#request{user_id = UserId,
+ ref = Ref}] ->
+ ?vdebug("handle_cancel_async_request -> demonitor and cancel timer"
+ "~n Ref: ~p", [Ref]),
+ cancel_timer(Ref),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+ [#request{user_id = OtherUserId}] ->
+ ?vinfo("handle_cancel_async_request -> Not request owner"
+ "~n OtherUserId: ~p", [OtherUserId]),
+ {error, {not_owner, OtherUserId}};
+
+ [] ->
+ ?vlog("handle_cancel_async_request -> not found", []),
+ {error, not_found}
+ end.
+
+
+%% handle_system_info_updated(#state{net_if = Pid, net_if_mod = Mod} = _State,
+%% net_if = _Target, What) ->
+%% case (catch Mod:system_info_updated(Pid, What)) of
+%% {'EXIT', _} ->
+%% {error, not_supported};
+%% Else ->
+%% Else
+%% end;
+%% handle_system_info_updated(_State, Target, What) ->
+%% {error, {bad_target, Target, What}}.
+
+handle_get_log_type(#state{net_if = Pid, net_if_mod = Mod}) ->
+ case (catch Mod:get_log_type(Pid)) of
+ {'EXIT', _} ->
+ {error, not_supported};
+ Else ->
+ Else
+ end.
+
+handle_set_log_type(#state{net_if = Pid, net_if_mod = Mod}, NewType) ->
+ case (catch Mod:set_log_type(Pid, NewType)) of
+ {'EXIT', _} ->
+ {error, not_supported};
+ Else ->
+ Else
+ end.
+
+
+%% handle_discovery(Pid, UserId, BAddr, Port, Config, Expire, ExtraInfo, State) ->
+%% ?vtrace("handle_discovery -> entry with"
+%% "~n Pid: ~p"
+%% "~n UserId: ~p"
+%% "~n BAddr: ~p"
+%% "~n Port: ~p"
+%% "~n Config: ~p"
+%% "~n Expire: ~p",
+%% [Pid, UserId, BAddr, Port, Config, Expire]),
+%% case agent_data(default, default, "", Config) of
+%% {ok, Addr, Port, Vsn, MsgData} ->
+%% ?vtrace("handle_discovery -> send a ~p disco message", [Vsn]),
+%% ReqId = send_discovery(Vsn, MsgData, BAddr, Port, ExtraInfo,
+%% State),
+%% ?vdebug("handle_discovery -> ReqId: ~p", [ReqId]),
+%% MonRef = erlang:monitor(process, Pid),
+%% ?vtrace("handle_discovery -> MonRef: ~p", [MonRef]),
+%% Req = #request{id = ReqId,
+%% user_id = UserId,
+%% target = TargetName,
+%% addr = BAddr,
+%% port = Port,
+%% type = get,
+%% data = MsgData,
+%% mon = MonRef,
+%% discovery = true,
+%% expire = t() + Expire},
+%% ets:insert(snmpm_request_table, Req),
+%% gct_activate(State#state.gct),
+%% {ok, ReqId};
+
+%% Error ->
+%% ?vinfo("failed retrieving agent data for discovery (get):"
+%% "~n BAddr: ~p"
+%% "~n Port: ~p"
+%% "~n Error: ~p", [BAddr, Port, Error]),
+%% Error
+%% end.
+
+
+handle_sync_timeout(ReqId, From, State) ->
+ ?vtrace("handle_sync_timeout -> entry with"
+ "~n ReqId: ~p"
+ "~n From: ~p", [ReqId, From]),
+ case ets:lookup(snmpm_request_table, ReqId) of
+ [#request{mon = MonRef, from = From} = Req0] ->
+ ?vdebug("handle_sync_timeout -> "
+ "deliver reply (timeout) and demonitor: "
+ "~n Monref: ~p"
+ "~n From: ~p", [MonRef, From]),
+ gen_server:reply(From, {error, {timeout, ReqId}}),
+ maybe_demonitor(MonRef),
+
+ %%
+ %% Instead of deleting the request record now,
+ %% we leave it to the gc. But for that to work
+ %% we must update the expire value (which for
+ %% sync requests is infinity).
+ %%
+
+ Req = Req0#request{ref = undefined,
+ mon = undefined,
+ from = undefined,
+ expire = t()},
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ ok;
+ _ ->
+ ok
+ end.
+
+
+handle_snmp_error(#pdu{request_id = ReqId} = Pdu, Reason, State) ->
+
+ ?vtrace("handle_snmp_error -> entry with"
+ "~n Reason: ~p"
+ "~n Pdu: ~p", [Reason, Pdu]),
+
+ case ets:lookup(snmpm_request_table, ReqId) of
+
+ %% Failed async request
+ [#request{user_id = UserId,
+ from = undefined,
+ ref = undefined,
+ mon = MonRef,
+ discovery = Disco}] ->
+
+ ?vdebug("handle_snmp_error -> "
+ "found corresponding request: "
+ "~n failed async request"
+ "~n UserId: ~p"
+ "~n ModRef: ~p"
+ "~n Disco: ~p", [UserId, MonRef, Disco]),
+
+ maybe_demonitor(MonRef),
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ handle_error(UserId, UserMod, Reason, ReqId,
+ UserData, State),
+ maybe_delete(Disco, ReqId);
+ _ ->
+ %% reply to outstanding request, for which there is no
+ %% longer any owner (the user has unregistered).
+ %% Therefor send it to the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason, ReqId,
+ DefData, State),
+ maybe_delete(Disco, ReqId);
+ _ ->
+ error_msg("failed retreiving the default user "
+ "info handling error [~w]: "
+ "~n~w", [ReqId, Reason])
+ end
+ end;
+
+
+ %% Failed sync request
+ %%
+ [#request{ref = Ref, mon = MonRef, from = From}] ->
+
+ ?vdebug("handle_snmp_error -> "
+ "found corresponding request: "
+ "~n failed sync request"
+ "~n Ref: ~p"
+ "~n ModRef: ~p"
+ "~n From: ~p", [Ref, MonRef, From]),
+
+ Remaining =
+ case (catch cancel_timer(Ref)) of
+ Rem when is_integer(Rem) ->
+ Rem;
+ _ ->
+ 0
+ end,
+
+ ?vtrace("handle_snmp_error -> Remaining: ~p", [Remaining]),
+
+ maybe_demonitor(MonRef),
+ Reply = {error, {send_failed, ReqId, Reason}},
+ ?vtrace("handle_snmp_error -> deliver (error-) reply",[]),
+ gen_server:reply(From, Reply),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+
+ %% A very old reply, see if this agent is handled by
+ %% a user. In that case send it there, else to the
+ %% default user.
+ _ ->
+
+ ?vdebug("handle_snmp_error -> no request?", []),
+
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ _ ->
+ error_msg("failed retreiving the default "
+ "user info handling error [~w]: "
+ "~n~w",[ReqId, Reason])
+ end
+ end;
+
+handle_snmp_error(CrapError, Reason, _State) ->
+ error_msg("received crap (snmp) error =>"
+ "~n~p~n~p", [CrapError, Reason]),
+ ok.
+
+handle_snmp_error(Addr, Port, ReqId, Reason, State) ->
+
+ ?vtrace("handle_snmp_error -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n ReqId: ~p"
+ "~n Reason: ~p", [Addr, Port, ReqId, Reason]),
+
+ case snmpm_config:get_agent_user_id(Addr, Port) of
+ {ok, UserId} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ handle_error(UserId, UserMod, Reason, ReqId,
+ UserData, State);
+ _Error ->
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ _Error ->
+ error_msg("failed retreiving the default user "
+ "info handling snmp error "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, ReqId, Reason])
+ end
+ end;
+ _Error ->
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ _Error ->
+ error_msg("failed retreiving the default user "
+ "info handling snmp error "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, ReqId, Reason])
+ end
+ end.
+
+
+handle_error(_UserId, Mod, Reason, ReqId, Data, _State) ->
+ ?vtrace("handle_error -> entry when"
+ "~n Mod: ~p", [Mod]),
+ F = fun() -> (catch Mod:handle_error(ReqId, Reason, Data)) end,
+ handle_callback(F),
+ ok.
+
+
+handle_snmp_pdu(#pdu{type = 'get-response', request_id = ReqId} = Pdu,
+ Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_pdu(get-response) -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Pdu: ~p", [Addr, Port, Pdu]),
+
+ case ets:lookup(snmpm_request_table, ReqId) of
+
+ %% Reply to a async request or
+ %% possibly a late reply to a sync request
+ %% (ref is also undefined)
+ [#request{user_id = UserId,
+ reg_type = RegType,
+ target = Target,
+ from = undefined,
+ ref = undefined,
+ mon = MonRef,
+ discovery = Disco}] ->
+
+ ?vdebug("handle_snmp_pdu(get-response) -> "
+ "found corresponding request: "
+ "~n reply to async request or late reply to sync request"
+ "~n UserId: ~p"
+ "~n ModRef: ~p"
+ "~n Disco: ~p", [UserId, MonRef, Disco]),
+
+ maybe_demonitor(MonRef),
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpResponse = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ handle_pdu(UserId, UserMod,
+ RegType, Target, Addr, Port,
+ ReqId, SnmpResponse, UserData, State),
+ maybe_delete(Disco, ReqId);
+ _Error ->
+ %% reply to outstanding request, for which there is no
+ %% longer any owner (the user has unregistered).
+ %% Therefor send it to the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_pdu(DefUserId, DefMod,
+ RegType, Target, Addr, Port,
+ ReqId, SnmpResponse, DefData, State),
+ maybe_delete(Disco, ReqId);
+ Error ->
+ error_msg("failed retreiving the default user "
+ "info handling pdu from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port, Error, Pdu])
+ end
+ end;
+
+
+ %% Reply to a sync request
+ %%
+ [#request{ref = Ref, mon = MonRef, from = From}] ->
+
+ ?vdebug("handle_snmp_pdu(get-response) -> "
+ "found corresponding request: "
+ "~n reply to sync request"
+ "~n Ref: ~p"
+ "~n ModRef: ~p"
+ "~n From: ~p", [Ref, MonRef, From]),
+
+ Remaining =
+ case (catch cancel_timer(Ref)) of
+ Rem when is_integer(Rem) ->
+ Rem;
+ _ ->
+ 0
+ end,
+
+ ?vtrace("handle_snmp_pdu(get-response) -> Remaining: ~p",
+ [Remaining]),
+
+ maybe_demonitor(MonRef),
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpReply = {EStatus, EIndex, Varbinds2},
+ Reply = {ok, SnmpReply, Remaining},
+ ?vtrace("handle_snmp_pdu(get-response) -> deliver reply",[]),
+ gen_server:reply(From, Reply),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+
+ %% A very old reply, see if this agent is handled by
+ %% a user. In that case send it there, else to the
+ %% default user.
+ _ ->
+
+ ?vdebug("handle_snmp_pdu(get-response) -> "
+ "no corresponding request: "
+ "~n a very old reply", []),
+
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpInfo = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:get_agent_user_id(Addr, Port) of
+ {ok, UserId} ->
+ %% A very late reply or a reply to a request
+ %% that has been cancelled.
+ %%
+ ?vtrace("handle_snmp_pdu(get-response) -> "
+ "a very late reply:"
+ "~n UserId: ~p",[UserId]),
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ Reason = {unexpected_pdu, SnmpInfo},
+ handle_error(UserId, UserMod, Reason, ReqId,
+ UserData, State);
+ _Error ->
+ %% Ouch, found an agent but not it's user!!
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ Reason = {unexpected_pdu, SnmpInfo},
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ Error ->
+ error_msg("failed retreiving the default "
+ "user info handling (old) "
+ "pdu from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error, Pdu])
+ end
+ end;
+
+ {error, _} ->
+ %% No agent, so either this agent has been
+ %% unregistered, or this is a very late reply
+ %% to a request (possibly a discovery), which
+ %% has since been cancelled (either because of
+ %% a timeout or that the user has unregistered
+ %% itself (and with it all it's requests)).
+ %% No way to know which.
+ %%
+ ?vtrace("handle_snmp_pdu(get-response) -> "
+ "no agent info found", []),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ pdu, ignore,
+ SnmpInfo, DefData, State);
+ Error ->
+ error_msg("failed retreiving the default user "
+ "info handling (old) pdu when no user "
+ "found from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error, Pdu])
+ end
+ end
+ end;
+
+handle_snmp_pdu(CrapPdu, Addr, Port, _State) ->
+ error_msg("received crap (snmp) Pdu from ~w:~w =>"
+ "~p", [Addr, Port, CrapPdu]),
+ ok.
+
+
+handle_pdu(_UserId, Mod, target_name = _RegType, TargetName, _Addr, _Port,
+ ReqId, SnmpResponse, Data, _State) ->
+ ?vtrace("handle_pdu(target_name) -> entry when"
+ "~n Mod: ~p", [Mod]),
+ F = fun() ->
+ (catch Mod:handle_pdu(TargetName, ReqId, SnmpResponse, Data))
+ end,
+ handle_callback(F),
+ ok;
+handle_pdu(_UserId, Mod, addr_port = _RegType, _TargetName, Addr, Port,
+ ReqId, SnmpResponse, Data, _State) ->
+ ?vtrace("handle_pdu(addr_port) -> entry when"
+ "~n Mod: ~p", [Mod]),
+ F = fun() ->
+ (catch Mod:handle_pdu(Addr, Port, ReqId, SnmpResponse, Data))
+ end,
+ handle_callback(F),
+ ok.
+
+
+handle_agent(UserId, Mod, Addr, Port, Type, Ref, SnmpInfo, Data, State) ->
+ ?vtrace("handle_agent -> entry when"
+ "~n UserId: ~p"
+ "~n Type: ~p"
+ "~n Mod: ~p", [UserId, Type, Mod]),
+ F = fun() ->
+ do_handle_agent(UserId, Mod, Addr, Port,
+ Type, Ref, SnmpInfo, Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+do_handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ Type, Ref,
+ SnmpInfo, DefData, State) ->
+ ?vdebug("do_handle_agent -> entry when"
+ "~n DefUserId: ~p", [DefUserId]),
+ case (catch DefMod:handle_agent(Addr, Port, Type, SnmpInfo, DefData)) of
+ {'EXIT', {undef, _}} when Type =:= pdu ->
+ %% Maybe, still on the old API
+ ?vdebug("do_handle_agent -> maybe still on the old api", []),
+ case (catch DefMod:handle_agent(Addr, Port, SnmpInfo, DefData)) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ TargetName = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, addr_port},
+ {address, Addr},
+ {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config2) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+ {register, UserId2, TargetName, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p",
+ [UserId2, TargetName, Config]),
+ Config2 = ensure_present([{address, Addr}, {port, Port}],
+ Config),
+ Config3 = [{reg_type, target_name} | Config2],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config3) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+ _Ignore ->
+ ?vdebug("do_handle_agent -> ignore", []),
+ ok
+ end;
+
+ {'EXIT', {undef, _}} ->
+ %% If the user does not implement the new API (but the
+ %% old), then this clause catches all non-pdu handle_agent
+ %% calls. These calls was previously never made,so we make
+ %% a best-effert call (using reg-type target_name) to the
+ %% various callback functions, and leave it to the user to
+ %% figure out
+
+ %% Backward compatibillity crap
+ RegType = target_name,
+ Target = mk_target_name(Addr, Port, default_agent_config()),
+ case Type of
+ report ->
+ SnmpInform = SnmpInfo,
+ handle_report(DefUserId, DefMod,
+ RegType, Target, Addr, Port,
+ SnmpInform, DefData, State);
+
+ inform ->
+ SnmpInform = SnmpInfo,
+ handle_inform(DefUserId, DefMod, Ref,
+ RegType, Target, Addr, Port,
+ SnmpInform, DefData, State);
+
+ trap ->
+ SnmpTrapInfo = SnmpInfo,
+ handle_trap(DefUserId, DefMod,
+ RegType, Target, Addr, Port,
+ SnmpTrapInfo, DefData, State);
+
+ _ ->
+ error_msg("failed delivering ~w info to default user - "
+ "regarding agent "
+ "<~p,~p>: ~n~w", [Type, Addr, Port, SnmpInfo])
+ end;
+
+ {register, UserId2, TargetName, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p",
+ [UserId2, TargetName, Config]),
+ Config2 = ensure_present([{address, Addr}, {port, Port}], Config),
+ Config3 = [{reg_type, target_name} | Config2],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config3) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+
+ _Ignore ->
+ ?vdebug("do_handle_agent -> ignore", []),
+ ok
+
+ end.
+
+ensure_present([], Config) ->
+ Config;
+ensure_present([{Key, _Val} = Elem|Ensure], Config) ->
+ case lists:keymember(Key, 1, Config) of
+ false ->
+ ensure_present(Ensure, [Elem|Config]);
+ true ->
+ ensure_present(Ensure, Config)
+ end.
+
+
+%% Retrieve user info for this agent.
+%% If this is an unknown agent, then use the default user
+handle_snmp_trap(#trappdu{enterprise = Enteprise,
+ generic_trap = Generic,
+ specific_trap = Spec,
+ time_stamp = Timestamp,
+ varbinds = Varbinds} = Trap,
+ Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_trap [trappdu] -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Trap: ~p", [Addr, Port, Trap]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpTrapInfo = {Enteprise, Generic, Spec, Timestamp, Varbinds2},
+ do_handle_snmp_trap(SnmpTrapInfo, Addr, Port, State);
+
+handle_snmp_trap(#pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Trap,
+ Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_trap [pdu] -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Trap: ~p", [Addr, Port, Trap]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpTrapInfo = {EStatus, EIndex, Varbinds2},
+ do_handle_snmp_trap(SnmpTrapInfo, Addr, Port, State);
+
+handle_snmp_trap(CrapTrap, Addr, Port, _State) ->
+ error_msg("received crap (snmp) trap from ~w:~w =>"
+ "~p", [Addr, Port, CrapTrap]),
+ ok.
+
+do_handle_snmp_trap(SnmpTrapInfo, Addr, Port, State) ->
+ case snmpm_config:get_agent_user_info(Addr, Port) of
+ {ok, UserId, Target, RegType} ->
+ ?vtrace("handle_snmp_trap -> found user: ~p", [UserId]),
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port,
+ SnmpTrapInfo, Data, State);
+
+ Error1 ->
+ %% User no longer exists, unregister agent
+ ?vlog("[trap] failed retreiving user info for "
+ "user ~p: "
+ "~n ~p", [UserId, Error1]),
+ case snmpm_config:unregister_agent(UserId, Target) of
+ ok ->
+ %% Try use the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ trap, ignore,
+ SnmpTrapInfo, DefData, State);
+ Error2 ->
+ error_msg("failed retreiving the default "
+ "user info handling report from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port,
+ Error2, SnmpTrapInfo])
+ end;
+ Error3 ->
+ %% Failed unregister agent,
+ %% now its getting messy...
+ warning_msg("failed unregister agent ~p <~p,~p> "
+ "belonging to non-existing "
+ "user ~p, handling trap: "
+ "~n Error: ~w"
+ "~n Trap info: ~w",
+ [Target, Addr, Port, UserId,
+ Error3, SnmpTrapInfo])
+ end
+ end;
+
+ Error4 ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[trap] failed retreiving user id for agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error4]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ trap, ignore,
+ SnmpTrapInfo, DefData, State);
+ Error5 ->
+ error_msg("failed retreiving "
+ "the default user info handling trap from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error5, SnmpTrapInfo])
+ end
+ end,
+ ok.
+
+
+handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port, SnmpTrapInfo, Data, State) ->
+ ?vtrace("handle_trap -> entry with"
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ F = fun() ->
+ do_handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port,
+ SnmpTrapInfo, Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+
+do_handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port, SnmpTrapInfo, Data, _State) ->
+ ?vdebug("do_handle_trap -> entry with"
+ "~n UserId: ~p", [UserId]),
+ HandleTrap =
+ case RegType of
+ target_name ->
+ fun() -> Mod:handle_trap(Target, SnmpTrapInfo, Data) end;
+ addr_port ->
+ fun() -> Mod:handle_trap(Addr, Port, SnmpTrapInfo, Data) end
+ end,
+
+ case (catch HandleTrap()) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_trap -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ Target2 = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, target_name},
+ {address, Addr}, {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling trap "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ {register, UserId2, Target2, Config} ->
+ ?vtrace("do_handle_trap -> register: "
+ "~n UserId2: ~p"
+ "~n Target2: ~p"
+ "~n Config: ~p", [UserId2, Target2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Config2 = [{reg_type, target_name} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling trap "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ unregister ->
+ ?vtrace("do_handle_trap -> unregister", []),
+ case snmpm_config:unregister_agent(UserId,
+ Addr, Port) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed unregistering agent "
+ "handling trap "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ _Ignore ->
+ ?vtrace("do_handle_trap -> ignore", []),
+ ok
+ end.
+
+
+handle_snmp_inform(Ref,
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu, Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_inform -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Pdu: ~p", [Addr, Port, Pdu]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpInform = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:get_agent_user_info(Addr, Port) of
+ {ok, UserId, Target, RegType} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ ?vdebug("[inform] callback handle_inform with: "
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port,
+ SnmpInform, Data, State);
+ Error1 ->
+ %% User no longer exists, unregister agent
+ case snmpm_config:unregister_agent(UserId, Target) of
+ ok ->
+ %% Try use the default user
+ ?vlog("[inform] failed retreiving user "
+ "info for user ~p:"
+ "~n ~p", [UserId, Error1]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ inform, Ref,
+ SnmpInform, DefData, State);
+ Error2 ->
+ error_msg("failed retreiving the default "
+ "user info handling inform from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port,
+ Error2, Pdu])
+ end;
+ Error3 ->
+ %% Failed unregister agent,
+ %% now its getting messy...
+ warning_msg("failed unregister agent ~p <~p,~p> "
+ "~n belonging to non-existing "
+ "user ~p, handling inform: "
+ "~n Error: ~w"
+ "~n Pdu: ~w",
+ [Target, Addr, Port, UserId,
+ Error3, Pdu])
+ end
+ end;
+
+ Error4 ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[inform] failed retreiving user id for agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error4]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ inform, Ref,
+ SnmpInform, DefData, State);
+ Error5 ->
+ error_msg("failed retreiving "
+ "the default user info handling inform from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error5, Pdu])
+ end
+ end,
+ ok;
+
+handle_snmp_inform(_Ref, CrapInform, Addr, Port, _State) ->
+ error_msg("received crap (snmp) inform from ~w:~w =>"
+ "~p", [Addr, Port, CrapInform]),
+ ok.
+
+handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port, SnmpInform, Data, State) ->
+ ?vtrace("handle_inform -> entry with"
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ F = fun() ->
+ do_handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port, SnmpInform,
+ Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+do_handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port, SnmpInform, Data, State) ->
+ ?vdebug("do_handle_inform -> entry with"
+ "~n UserId: ~p", [UserId]),
+ HandleInform =
+ case RegType of
+ target_name ->
+ fun() -> Mod:handle_inform(Target, SnmpInform, Data) end;
+ addr_port ->
+ fun() -> Mod:handle_inform(Addr, Port, SnmpInform, Data) end
+ end,
+
+ Rep =
+ case (catch HandleInform()) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_inform -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Target2 = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, target_name},
+ {address, Addr}, {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling inform "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ {register, UserId2, Target2, Config} ->
+ ?vtrace("do_handle_inform -> register: "
+ "~n UserId2: ~p"
+ "~n Target2: ~p"
+ "~n Config: ~p", [UserId2, Target2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Config2 = [{reg_type, target_name} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling inform "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ unregister ->
+ ?vtrace("do_handle_inform -> unregister", []),
+ case snmpm_config:unregister_agent(UserId,
+ Addr, Port) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed unregistering agent "
+ "handling inform "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ reply
+ end;
+ no_reply ->
+ ?vtrace("do_handle_inform -> no_reply", []),
+ no_reply;
+ _Ignore ->
+ ?vtrace("do_handle_inform -> ignore", []),
+ reply
+ end,
+ handle_inform_response(Rep, Ref, Addr, Port, State),
+ ok.
+
+
+handle_inform_response(_, ignore, _Addr, _Port, _State) ->
+ ignore;
+handle_inform_response(no_reply, _Ref, _Addr, _Port, _State) ->
+ no_reply;
+handle_inform_response(_, Ref, Addr, Port,
+ #state{net_if = Pid, net_if_mod = Mod}) ->
+ ?vdebug("handle_inform -> response", []),
+ (catch Mod:inform_response(Pid, Ref, Addr, Port)).
+
+handle_snmp_report(#pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu, Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_report -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Pdu: ~p", [Addr, Port, Pdu]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpReport = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:get_agent_user_info(Addr, Port) of
+ {ok, UserId, Target, RegType} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ ?vdebug("[report] callback handle_report with: "
+ "~n ~p"
+ "~n ~p"
+ "~n ~p"
+ "~n ~p", [UserId, Mod, Target, SnmpReport]),
+ handle_report(UserId, Mod,
+ RegType, Target, Addr, Port,
+ SnmpReport, Data, State);
+ Error1 ->
+ %% User no longer exists, unregister agent
+ ?vlog("[report] failed retreiving user info "
+ "for user ~p:"
+ " ~n ~p", [UserId, Error1]),
+ case snmpm_config:unregister_agent(UserId, Target) of
+ ok ->
+ %% Try use the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ report, ignore,
+ SnmpReport, DefData, State);
+
+ Error2 ->
+ error_msg("failed retreiving the default "
+ "user info handling report from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port,
+ Error2, Pdu])
+ end;
+ Error3 ->
+ %% Failed unregister agent,
+ %% now its getting messy...
+ warning_msg("failed unregister agent ~p <~p,~p> "
+ "belonging to non-existing "
+ "user ~p, handling report: "
+ "~n Error: ~w"
+ "~n Report: ~w",
+ [Target, Addr, Port, UserId,
+ Error3, Pdu])
+ end
+ end;
+
+ Error4 ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[report] failed retreiving user id for agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error4]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ report, ignore,
+ SnmpReport, DefData, State);
+ Error5 ->
+ error_msg("failed retreiving "
+ "the default user info handling report from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error5, Pdu])
+ end
+ end,
+ ok;
+
+handle_snmp_report(CrapReport, Addr, Port, _State) ->
+ error_msg("received crap (snmp) report from ~w:~w =>"
+ "~p", [Addr, Port, CrapReport]),
+ ok.
+
+%% This could be from a failed get-request, so we might have a user
+%% waiting for a reply here. If there is, we handle this as an failed
+%% get-response (except for tha data which is different). Otherwise,
+%% we handle it as an error (reported via the handle_error callback
+%% function).
+handle_snmp_report(ReqId,
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ {ReportReason, Info} = Rep,
+ Addr, Port, State)
+ when is_integer(ReqId) ->
+
+ ?vtrace("handle_snmp_report -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n ReqId: ~p"
+ "~n Rep: ~p"
+ "~n Pdu: ~p", [Addr, Port, ReqId, Rep, Pdu]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpReport = {EStatus, EIndex, Varbinds2},
+ Reason = {ReportReason, Info, SnmpReport},
+
+ %% Check if there is someone waiting for this request
+
+ case ets:lookup(snmpm_request_table, ReqId) of
+
+ [#request{from = From,
+ ref = Ref,
+ mon = MonRef}] when (From =/= undefined) andalso
+ (Ref =/= undefined) ->
+
+ ?vdebug("handle_snmp_report -> "
+ "found corresponding request: "
+ "~n reply to sync request"
+ "~n Ref: ~p"
+ "~n ModRef: ~p"
+ "~n From: ~p", [Ref, MonRef, From]),
+
+ Remaining =
+ case (catch cancel_timer(Ref)) of
+ Rem when is_integer(Rem) ->
+ Rem;
+ _ ->
+ 0
+ end,
+
+ ?vtrace("handle_snmp_pdu(get-response) -> Remaining: ~p",
+ [Remaining]),
+
+ maybe_demonitor(MonRef),
+
+ Reply = {error, Reason},
+ ?vtrace("handle_snmp_report -> deliver reply",[]),
+ gen_server:reply(From, Reply),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+ _ ->
+ %% Either not a sync request or no such request. Either
+ %% way, this is error info, so handle it as such.
+
+ case snmpm_config:get_agent_user_id(Addr, Port) of
+ {ok, UserId} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ ?vdebug("[report] callback handle_error with: "
+ "~n ~p"
+ "~n ~p"
+ "~n ~p", [UserId, Mod, Reason]),
+ handle_error(UserId, Mod, Reason, ReqId,
+ Data, State);
+ Error ->
+ %% Oh crap, use the default user
+ ?vlog("[report] failed retreiving user info for "
+ "user ~p:"
+ " ~n ~p", [UserId, Error]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ Error ->
+ error_msg("failed retreiving the "
+ "default user "
+ "info handling report from "
+ "<~p,~p>: ~n~w~n~w~n~w",
+ [Addr, Port, Error,
+ ReqId, Reason])
+ end
+ end;
+ Error ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[report] failed retreiving user id for "
+ "agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason, ReqId,
+ DefData, State);
+ Error ->
+ error_msg("failed retreiving "
+ "the default user info handling "
+ "report from "
+ "<~p,~p>: ~n~w~n~w~n~w",
+ [Addr, Port, Error, ReqId, Reason])
+ end
+ end
+ end,
+ ok;
+
+handle_snmp_report(CrapReqId, CrapReport, CrapInfo, Addr, Port, _State) ->
+ error_msg("received crap (snmp) report from ~w:~w =>"
+ "~n~p~n~p~n~p", [Addr, Port, CrapReqId, CrapReport, CrapInfo]),
+ ok.
+
+
+handle_report(UserId, Mod, RegType, Target, Addr, Port,
+ SnmpReport, Data, State) ->
+ ?vtrace("handle_report -> entry with"
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ F = fun() ->
+ do_handle_report(UserId, Mod, RegType, Target, Addr, Port,
+ SnmpReport, Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+do_handle_report(UserId, Mod,
+ RegType, Target, Addr, Port, SnmpReport, Data, _State) ->
+ ?vdebug("do_handle_report -> entry with"
+ "~n UserId: ~p", [UserId]),
+ HandleReport =
+ case RegType of
+ target_name ->
+ fun() -> Mod:handle_report(Target, SnmpReport, Data) end;
+ addr_port ->
+ fun() -> Mod:handle_report(Addr, Port, SnmpReport, Data) end
+ end,
+
+ case (catch HandleReport()) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_report -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Target2 = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, target_name},
+ {address, Addr}, {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling report "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ {register, UserId2, Target2, Config} ->
+ ?vtrace("do_handle_report -> register: "
+ "~n UserId2: ~p"
+ "~n Target2: ~p"
+ "~n Config: ~p", [UserId2, Target2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Config2 = [{reg_type, target_name} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling report "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ unregister ->
+ ?vtrace("do_handle_trap -> unregister", []),
+ case snmpm_config:unregister_agent(UserId,
+ Addr, Port) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed unregistering agent "
+ "handling report "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ _Ignore ->
+ ?vtrace("do_handle_report -> ignore", []),
+ ok
+ end.
+
+
+handle_callback(F) ->
+ V = get(verbosity),
+ erlang:spawn(
+ fun() ->
+ put(sname, msew),
+ put(verbosity, V),
+ F()
+ end).
+
+
+handle_down(MonRef) ->
+ (catch do_handle_down(MonRef)).
+
+do_handle_down(MonRef) ->
+ %% Clear out all requests from this client
+ handle_down_requests_cleanup(MonRef),
+
+ %%
+ %% Check also if this was a monitored user, and if so
+ %% unregister all agents registered by this user, and
+ %% finally unregister the user itself
+ %%
+ handle_down_user_cleanup(MonRef),
+
+ ok.
+
+
+handle_down_requests_cleanup(MonRef) ->
+ Pat = #request{id = '$1', ref = '$2', mon = MonRef, _ = '_'},
+ Match = ets:match(snmpm_request_table, Pat),
+ Fun = fun([Id, Ref]) ->
+ ?vtrace("delete request: ~p", [Id]),
+ ets:delete(snmpm_request_table, Id),
+ cancel_timer(Ref),
+ ok
+ end,
+ lists:foreach(Fun, Match).
+
+handle_down_user_cleanup(MonRef) ->
+ Pat = #monitor{id = '$1', mon = MonRef, _ = '_'},
+ Match = ets:match(snmpm_monitor_table, Pat),
+ Fun = fun([Id]) ->
+ Agents = snmpm_config:which_agents(Id),
+ lists:foreach(
+ fun({Addr,Port}) ->
+ %% ** Previous format **
+ %% Just in case this happens during code upgrade
+ ?vtrace("unregister agent of monitored user "
+ "~w: <~w,~w>", [Id,Addr,Port]),
+ snmpm_config:unregister_agent(Id, Addr, Port);
+ (TargetName) ->
+ ?vtrace("unregister agent of monitored user "
+ "~w: ~p", [Id,TargetName]),
+ snmpm_config:unregister_agent(Id, TargetName)
+ end,
+ Agents),
+ ?vtrace("unregister monitored user: ~w", [Id]),
+ ets:delete(snmpm_monitor_table, Id),
+ snmpm_config:unregister_user(Id),
+ ok
+ end,
+ lists:foreach(Fun, Match).
+
+cancel_timer(undefined) ->
+ ok;
+cancel_timer(Ref) ->
+ (catch erlang:cancel_timer(Ref)).
+
+handle_gc(GCT) ->
+ ets:safe_fixtable(snmpm_request_table, true),
+ case do_gc(ets:first(snmpm_request_table), t()) of
+ 0 ->
+ gct_deactivate(GCT);
+ _ ->
+ ok
+ end,
+ ets:safe_fixtable(snmpm_request_table, false).
+
+
+
+%% We are deleting at the same time as we are traversing the table!!!
+do_gc('$end_of_table', _) ->
+ ets:info(snmpm_request_table, size);
+do_gc(Key, Now) ->
+ Next = ets:next(snmpm_request_table, Key),
+ case ets:lookup(snmpm_request_table, Key) of
+ [#request{expire = BestBefore}] when (BestBefore < Now) ->
+ ets:delete(snmpm_request_table, Key);
+ _ ->
+ ok
+ end,
+ do_gc(Next, Now).
+
+
+
+%%----------------------------------------------------------------------
+%%
+%%----------------------------------------------------------------------
+
+send_get_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo,
+ #state{net_if = NetIf,
+ net_if_mod = Mod,
+ mini_mib = MiniMIB}) ->
+ Pdu = make_pdu(get, Oids, MiniMIB),
+ ?vtrace("send_get_request -> send get-request:"
+ "~n Mod: ~p"
+ "~n NetIf: ~p"
+ "~n Pdu: ~p"
+ "~n Vsn: ~p"
+ "~n MsgData: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Mod, NetIf, Pdu, Vsn, MsgData, Addr, Port]),
+ (catch Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo)),
+ Pdu#pdu.request_id.
+
+send_get_next_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo,
+ #state{mini_mib = MiniMIB,
+ net_if = NetIf,
+ net_if_mod = Mod}) ->
+ Pdu = make_pdu(get_next, Oids, MiniMIB),
+ Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+ Pdu#pdu.request_id.
+
+send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port,
+ NonRep, MaxRep, ExtraInfo,
+ #state{mini_mib = MiniMIB,
+ net_if = NetIf,
+ net_if_mod = Mod}) ->
+ Pdu = make_pdu(bulk, {NonRep, MaxRep, Oids}, MiniMIB),
+ Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+ Pdu#pdu.request_id.
+
+send_set_request(VarsAndVals, Vsn, MsgData, Addr, Port, ExtraInfo,
+ #state{mini_mib = MiniMIB,
+ net_if = NetIf,
+ net_if_mod = Mod}) ->
+ Pdu = make_pdu(set, VarsAndVals, MiniMIB),
+ Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+ Pdu#pdu.request_id.
+
+%% send_discovery(Vsn, MsgData, Addr, Port, ExtraInfo,
+%% #state{net_if = NetIf,
+%% net_if_mod = Mod}) ->
+%% Pdu = make_discovery_pdu(),
+%% Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+%% Pdu#pdu.request_id.
+
+
+
+%%----------------------------------------------------------------------
+%%
+%%----------------------------------------------------------------------
+
+%% make_discovery_pdu() ->
+%% Oids = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance],
+%% make_pdu_impl(get, Oids).
+
+make_pdu(set, VarsAndVals, MiniMIB) ->
+ VBs = [var_and_value_to_varbind(VAV, MiniMIB) || VAV <- VarsAndVals],
+ make_pdu_impl(set, VBs);
+
+make_pdu(bulk, {NonRepeaters, MaxRepetitions, Oids}, MiniMIB) ->
+ Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
+ #pdu{type = 'get-bulk-request',
+ request_id = request_id(),
+ error_status = NonRepeaters,
+ error_index = MaxRepetitions,
+ varbinds = [make_vb(Foid) || Foid <- Foids]};
+
+make_pdu(Op, Oids, MiniMIB) ->
+ Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
+ make_pdu_impl(Op, Foids).
+
+
+make_pdu_impl(get, Oids) ->
+ #pdu{type = 'get-request',
+ request_id = request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = [make_vb(Oid) || Oid <- Oids]};
+
+make_pdu_impl(get_next, Oids) ->
+ #pdu{type = 'get-next-request',
+ request_id = request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = [make_vb(Oid) || Oid <- Oids]};
+
+make_pdu_impl(set, Varbinds) ->
+ #pdu{type = 'set-request',
+ request_id = request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = Varbinds}.
+
+
+fix_vbs_BITS(Varbinds) ->
+ [fix_vb_BITS(Varbind) || Varbind <- Varbinds].
+
+fix_vb_BITS(#varbind{oid = Oid,
+ variabletype = 'OCTET STRING' = _Type,
+ value = Value} = Varbind) ->
+ %% BITS are encoded as OCTET STRING, so this could be a BITS
+ %% check with the MiniMIB
+ case type_of_oid(Oid) of
+ {error, _} ->
+ Varbind;
+ {ok, NewType = 'BITS'} ->
+ NewValue = snmp_pdus:octet_str_to_bits(Value),
+ Varbind#varbind{variabletype = NewType,
+ value = NewValue};
+ _ ->
+ Varbind
+ end;
+fix_vb_BITS(Vb) ->
+ Vb.
+
+type_of_oid(Oid) ->
+ Oid2 = case lists:reverse(Oid) of
+ [0|T] ->
+ lists:reverse(T);
+ _ ->
+ Oid
+ end,
+ snmpm_config:oid_to_type(Oid2).
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Unnesting of oids like [myTable, 3, 4, "hej", 45] to
+%% [1,2,3,3,4,104,101,106,45]
+%%----------------------------------------------------------------------
+
+flatten_oid([A|T], MiniMIB) when is_atom(A) ->
+ Oid = [alias2oid(A, MiniMIB)|T],
+ check_is_pure_oid(lists:flatten(Oid));
+flatten_oid(Oid, _) when is_list(Oid) ->
+ check_is_pure_oid(lists:flatten(Oid));
+flatten_oid(Shit, _) ->
+ throw({error, {invalid_oid, Shit}}).
+
+check_is_pure_oid([]) -> [];
+check_is_pure_oid([X | T]) when is_integer(X) andalso (X >= 0) ->
+ [X | check_is_pure_oid(T)];
+check_is_pure_oid([X | _T]) ->
+ throw({error, {invalid_oid, X}}).
+
+
+var_and_value_to_varbind({Oid, Type, Value}, MiniMIB) ->
+ Oid2 = flatten_oid(Oid, MiniMIB),
+ #varbind{oid = Oid2,
+ variabletype = char_to_type(Type),
+ value = Value};
+var_and_value_to_varbind({Oid, Value}, MiniMIB) ->
+ Oid2 = flatten_oid(Oid, MiniMIB),
+ #varbind{oid = Oid2,
+ variabletype = oid2type(Oid2, MiniMIB),
+ value = Value}.
+
+char_to_type(i) ->
+ 'INTEGER';
+char_to_type(u) ->
+ 'Unsigned32';
+char_to_type(g) -> % Gauge, Gauge32
+ 'Unsigned32';
+char_to_type(b) ->
+ 'BITS';
+char_to_type(ip) ->
+ 'IpAddress';
+char_to_type(ia) ->
+ 'IpAddress';
+char_to_type(op) ->
+ 'Opaque';
+char_to_type(c32) ->
+ 'Counter32';
+char_to_type(c64) ->
+ 'Counter64';
+char_to_type(tt) ->
+ 'TimeTicks';
+char_to_type(o) ->
+ 'OBJECT IDENTIFIER';
+char_to_type(s) ->
+ 'OCTET STRING';
+char_to_type(C) ->
+ throw({error, {invalid_value_type, C}}).
+
+
+alias2oid(AliasName, MiniMIB) when is_atom(AliasName) ->
+ case lists:keysearch(AliasName, 2, MiniMIB) of
+ {value, {Oid, _Aliasname, _Type}} ->
+ Oid;
+ false ->
+ throw({error, {unknown_aliasname, AliasName}})
+ end.
+
+oid2type(Oid, MiniMIB) ->
+ Oid2 = case lists:reverse(Oid) of
+ [0|T] ->
+ lists:reverse(T);
+ _ ->
+ Oid
+ end,
+ oid2type(Oid2, MiniMIB, utter_nonsense).
+
+oid2type(_Oid, [], utter_nonsense) ->
+ throw({error, no_type});
+oid2type(_Oid, [], Type) ->
+ Type;
+oid2type(Oid, [{Oid2, _, Type}|MiniMIB], Res) when (Oid2 =< Oid) ->
+ case lists:prefix(Oid2, Oid) of
+ true ->
+ oid2type(Oid, MiniMIB, Type); % A better guess
+ false ->
+ oid2type(Oid, MiniMIB, Res)
+ end;
+oid2type(_Oid, _MiniMIB, utter_nonsense) ->
+ throw({error, no_type});
+oid2type(_Oid, _MiniMIB, Type) ->
+ Type.
+
+
+make_vb(Oid) ->
+ #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
+
+
+%%----------------------------------------------------------------------
+
+request_id() ->
+ snmpm_mpd:next_req_id().
+
+
+%%----------------------------------------------------------------------
+
+agent_data(TargetName, CtxName) ->
+ agent_data(TargetName, CtxName, []).
+
+agent_data(TargetName, CtxName, Config) ->
+ case snmpm_config:agent_info(TargetName, all) of
+ {ok, Info} ->
+ {value, {_, Version}} = lists:keysearch(version, 1, Info),
+ MsgData =
+ case Version of
+ v3 ->
+ DefSecModel = agent_data_item(sec_model, Info),
+ DefSecName = agent_data_item(sec_name, Info),
+ DefSecLevel = agent_data_item(sec_level, Info),
+
+ EngineId = agent_data_item(engine_id, Info),
+
+ SecModel = agent_data_item(sec_model,
+ Config,
+ DefSecModel),
+ SecName = agent_data_item(sec_name,
+ Config,
+ DefSecName),
+ SecLevel = agent_data_item(sec_level,
+ Config,
+ DefSecLevel),
+
+ {SecModel, SecName, mk_sec_level_flag(SecLevel),
+ EngineId, CtxName, TargetName};
+ _ ->
+ DefComm = agent_data_item(community, Info),
+ DefSecModel = agent_data_item(sec_model, Info),
+
+ Comm = agent_data_item(community,
+ Config,
+ DefComm),
+ SecModel = agent_data_item(sec_model,
+ Config,
+ DefSecModel),
+
+ {Comm, SecModel}
+ end,
+ Addr = agent_data_item(address, Info),
+ Port = agent_data_item(port, Info),
+ RegType = agent_data_item(reg_type, Info),
+ {ok, RegType, Addr, Port, version(Version), MsgData};
+ Error ->
+ Error
+ end.
+
+agent_data_item(Item, Info) ->
+ {value, {_, Val}} = lists:keysearch(Item, 1, Info),
+ Val.
+
+agent_data_item(Item, Info, Default) ->
+ case lists:keysearch(Item, 1, Info) of
+ {value, {_, Val}} ->
+ Val;
+ false ->
+ Default
+ end.
+
+
+version(v1) ->
+ 'version-1';
+version(v2) ->
+ 'version-2';
+version(v3) ->
+ 'version-3'.
+
+
+%%-----------------------------------------------------------------
+%% Convert the SecurityLevel into a flag value used by snmpm_mpd
+%%-----------------------------------------------------------------
+mk_sec_level_flag(?'SnmpSecurityLevel_noAuthNoPriv') -> 0;
+mk_sec_level_flag(?'SnmpSecurityLevel_authNoPriv') -> 1;
+mk_sec_level_flag(?'SnmpSecurityLevel_authPriv') -> 3.
+
+
+%%----------------------------------------------------------------------
+%% Request Garbage Collector timer
+%%----------------------------------------------------------------------
+
+gct_start(Timeout) ->
+ ?vdebug("start gc timer process (~p)", [Timeout]),
+ State = #gct{parent = self(), timeout = Timeout},
+ proc_lib:start_link(?MODULE, gct_init, [State]).
+
+gct_stop(GCT) ->
+ GCT ! {stop, self()}.
+
+gct_activate(GCT) ->
+ GCT ! {activate, self()}.
+
+gct_deactivate(GCT) ->
+ GCT ! {deactivate, self()}.
+
+gct_code_change(GCT) ->
+ GCT ! {code_change, self()}.
+
+gct_init(#gct{parent = Parent, timeout = Timeout} = State) ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ gct(State, Timeout).
+
+gct(#gct{parent = Parent, state = active} = State, Timeout) ->
+ T = t(),
+ receive
+ {stop, Parent} ->
+ ok;
+
+ %% This happens when a new request is received.
+ {activate, Parent} ->
+ ?MODULE:gct(State, new_timeout(Timeout, T));
+
+ {deactivate, Parent} ->
+ %% Timeout is of no consequence in the idle state,
+ %% but just to be sure
+ NewTimeout = State#gct.timeout,
+ ?MODULE:gct(State#gct{state = idle}, NewTimeout);
+
+ {code_change, Parent} ->
+ %% Let the server take care of this
+ exit(normal);
+
+ {'EXIT', Parent, _Reason} ->
+ ok;
+
+ _ -> % Crap
+ ?MODULE:gct(State, Timeout)
+
+ after Timeout ->
+ Parent ! gc_timeout,
+ NewTimeout = State#gct.timeout,
+ ?MODULE:gct(State, NewTimeout)
+ end;
+
+gct(#gct{parent = Parent, state = idle} = State, Timeout) ->
+ receive
+ {stop, Parent} ->
+ ok;
+
+ {deactivate, Parent} ->
+ ?MODULE:gct(State, Timeout);
+
+ {activate, Parent} ->
+ NewTimeout = State#gct.timeout,
+ ?MODULE:gct(State#gct{state = active}, NewTimeout);
+
+ {code_change, Parent} ->
+ %% Let the server take care of this
+ exit(normal);
+
+ {'EXIT', Parent, _Reason} ->
+ ok;
+
+ _ -> % Crap
+ ?MODULE:gct(State, Timeout)
+
+ after Timeout ->
+ ?MODULE:gct(State, Timeout)
+ end.
+
+new_timeout(T1, T2) ->
+ case T1 - (t() - T2) of
+ T when (T > 0) ->
+ T;
+ _ ->
+ 0
+ end.
+
+
+%%----------------------------------------------------------------------
+
+maybe_delete(false, ReqId) ->
+ ets:delete(snmpm_request_table, ReqId);
+maybe_delete(true, _) ->
+ ok.
+
+maybe_demonitor(undefined) ->
+ ok;
+maybe_demonitor(MonRef) ->
+ erlang:demonitor(MonRef).
+
+%% Time in milli seconds
+t() ->
+ {A,B,C} = erlang:now(),
+ A*1000000000+B*1000+(C div 1000).
+
+mk_target_name(Addr, Port, Config) ->
+ snmpm_config:mk_target_name(Addr, Port, Config).
+
+default_agent_config() ->
+ case snmpm_config:agent_info() of
+ {ok, Config} ->
+ Config;
+ _ ->
+ []
+ end.
+
+
+%%----------------------------------------------------------------------
+
+is_started(#state{net_if = _Pid, net_if_mod = _Mod}) ->
+ %% Mod:is_started(Pid) and snmpm_config:is_started().
+ case snmpm_config:is_started() of
+ true ->
+ true;
+ _ ->
+ false
+ end.
+
+
+%%----------------------------------------------------------------------
+
+call(Req) ->
+ call(Req, infinity).
+
+call(Req, To) ->
+ gen_server:call(?SERVER, Req, To).
+
+%% cast(Msg) ->
+%% gen_server:cast(?SERVER, Msg).
+
+%% info_msg(F, A) ->
+%% ?snmpm_info("Server: " ++ F, A).
+
+warning_msg(F, A) ->
+ ?snmpm_warning("Server: " ++ F, A).
+
+error_msg(F, A) ->
+ ?snmpm_error("Server: " ++ F, A).
+
+
+%%----------------------------------------------------------------------
+
+get_info(#state{gct = GCT,
+ net_if = NI, net_if_mod = NIMod,
+ note_store = NS}) ->
+ Info = [{server, server_info(GCT)},
+ {config, config_info()},
+ {net_if, net_if_info(NI, NIMod)},
+ {note_store, note_store_info(NS)},
+ {stats_counters, get_stats_counters()}],
+ Info.
+
+server_info(GCT) ->
+ ProcSize = proc_mem(self()),
+ GCTSz = proc_mem(GCT),
+ RTSz = tab_size(snmpm_request_table),
+ MTSz = tab_size(snmpm_monitor_table),
+ [{process_memory, [{server, ProcSize}, {gct, GCTSz}]},
+ {db_memory, [{request, RTSz}, {monitor, MTSz}]}].
+
+proc_mem(P) when is_pid(P) ->
+ case (catch erlang:process_info(P, memory)) of
+ {memory, Sz} when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end;
+proc_mem(_) ->
+ undefined.
+
+tab_size(T) ->
+ case (catch ets:info(T, memory)) of
+ Sz when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end.
+
+config_info() ->
+ case (catch snmpm_config:info()) of
+ Info when is_list(Info) ->
+ Info;
+ E ->
+ [{error, E}]
+ end.
+
+get_stats_counters() ->
+ lists:sort(snmpm_config:get_stats_counters()).
+
+
+net_if_info(Pid, Mod) ->
+ case (catch Mod:info(Pid)) of
+ Info when is_list(Info) ->
+ Info;
+ E ->
+ [{error, E}]
+ end.
+
+note_store_info(Pid) ->
+ case (catch snmp_note_store:info(Pid)) of
+ Info when is_list(Info) ->
+ Info;
+ E ->
+ [{error, E}]
+ end.
+
+
+%%----------------------------------------------------------------------
+
+
+%%----------------------------------------------------------------------
+%% Debug
+%%----------------------------------------------------------------------
+
+% sz(L) when is_list(L) ->
+% length(lists:flatten(L));
+% sz(B) when is_binary(B) ->
+% size(B).
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
+