aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2013-05-28 17:14:13 +0200
committerMicael Karlberg <[email protected]>2013-05-28 17:14:13 +0200
commit1fadb8a6744b282659cecba754b9678ef9761d8c (patch)
tree4ce5c743c58ffb3ad765429591deae55264f5d98 /lib/snmp/src/agent
parenta9244ce9be2d73397ed71fb9d7d5986c5f70e202 (diff)
parent3ffa5b5c87518b6579f48b935cb67c7eb22b10b4 (diff)
downloadotp-1fadb8a6744b282659cecba754b9678ef9761d8c.tar.gz
otp-1fadb8a6744b282659cecba754b9678ef9761d8c.tar.bz2
otp-1fadb8a6744b282659cecba754b9678ef9761d8c.zip
Merge branch 'bmk/snmp/agent/mib_server_data_callback/OTP-11101' into bmk/snmp/snmp424_integration/r16
Conflicts: lib/snmp/doc/src/notes.xml
Diffstat (limited to 'lib/snmp/src/agent')
-rw-r--r--lib/snmp/src/agent/depend.mk4
-rw-r--r--lib/snmp/src/agent/modules.mk10
-rw-r--r--lib/snmp/src/agent/snmpa.erl16
-rw-r--r--lib/snmp/src/agent/snmpa_mib.erl194
-rw-r--r--lib/snmp/src/agent/snmpa_mib_data.erl1386
-rw-r--r--lib/snmp/src/agent/snmpa_mib_data_ttln.erl1402
-rw-r--r--lib/snmp/src/agent/snmpa_mib_data_tttn.erl1371
7 files changed, 2988 insertions, 1395 deletions
diff --git a/lib/snmp/src/agent/depend.mk b/lib/snmp/src/agent/depend.mk
index ea9261e266..096182a626 100644
--- a/lib/snmp/src/agent/depend.mk
+++ b/lib/snmp/src/agent/depend.mk
@@ -96,6 +96,10 @@ $(EBIN)/snmpa_mib.$(EMULATOR): \
$(EBIN)/snmpa_mib_data.$(EMULATOR): \
snmpa_mib_data.erl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmpa_mib_data_tttn.$(EMULATOR): \
+ snmpa_mib_data_tttn.erl \
../misc/snmp_debug.hrl \
../misc/snmp_verbosity.hrl \
../../include/snmp_types.hrl
diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk
index 33ab41b434..da9f6d6b4c 100644
--- a/lib/snmp/src/agent/modules.mk
+++ b/lib/snmp/src/agent/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2013. 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
@@ -21,15 +21,19 @@ BEHAVIOUR_MODULES = \
snmpa_authentication_service \
snmpa_discovery_handler \
snmpa_error_report \
+ snmpa_mib_data \
snmpa_network_interface \
snmpa_network_interface_filter \
snmpa_notification_delivery_info_receiver \
snmpa_notification_filter \
snmpa_set_mechanism
+# snmpa is "plain" interface module but also defines some agent specific types
+# and therefor must be compiled before the modules that use them...
+# snmpa_mib_data_ttln
MODULES = \
- $(BEHAVIOUR_MODULES) \
snmpa \
+ $(BEHAVIOUR_MODULES) \
snmpa_acm \
snmpa_agent \
snmpa_agent_sup \
@@ -42,7 +46,7 @@ MODULES = \
snmpa_general_db \
snmpa_local_db \
snmpa_mib \
- snmpa_mib_data \
+ snmpa_mib_data_tttn \
snmpa_mib_lib \
snmpa_misc_sup \
snmpa_mpd \
diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl
index b45a47ec6b..785276c73d 100644
--- a/lib/snmp/src/agent/snmpa.erl
+++ b/lib/snmp/src/agent/snmpa.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. 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
@@ -111,13 +111,27 @@
-export([print_mib_info/0, print_mib_tables/0, print_mib_variables/0]).
+-export_type([
+ me/0
+ ]).
+
+
+
-include("snmpa_atl.hrl").
-include("snmpa_internal.hrl").
+-include_lib("snmp/include/snmp_types.hrl"). % type of me needed.
-define(DISCO_EXTRA_INFO, undefined).
%%-----------------------------------------------------------------
+%% Types
+%%-----------------------------------------------------------------
+
+-type me() :: #me{}.
+
+
+%%-----------------------------------------------------------------
%% This utility function is used to convert an old SNMP application
%% config (prior to snmp-4.0) to a SNMP agent config (as of
%% snmp-4.0).
diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl
index 575a018c0c..1bd7398a69 100644
--- a/lib/snmp/src/agent/snmpa_mib.erl
+++ b/lib/snmp/src/agent/snmpa_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013. 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
@@ -18,7 +18,6 @@
%%
-module(snmpa_mib).
-%% c(snmpa_mib).
%%%-----------------------------------------------------------------
%%% This module implements a MIB server.
@@ -75,12 +74,14 @@
%% Internal Data structures
%%
%% State
-%% data - is the MIB data (defined in snmpa_mib_data)
+%% data - is the MIB data (defined in mib_data module)
%% meo - mib entry override
%% teo - trap (notification) entry override
%%-----------------------------------------------------------------
--record(state, {data, meo, teo, backup,
- cache, cache_tmr, cache_autogc, cache_gclimit, cache_age}).
+-record(state,
+ {data, meo, teo, backup,
+ cache, cache_tmr, cache_autogc, cache_gclimit, cache_age,
+ data_mod}).
@@ -224,9 +225,9 @@ info(MibServer, Type) ->
call(MibServer, {info, Type}).
dump(MibServer) ->
- call(MibServer, dump).
+ dump(MibServer, io).
-dump(MibServer, File) when is_list(File) ->
+dump(MibServer, File) when (File =:= io) orelse is_list(File) ->
call(MibServer, {dump, File}).
backup(MibServer, BackupDir) when is_list(BackupDir) ->
@@ -289,13 +290,15 @@ do_init(Prio, Mibs, Opts) ->
MeOverride = get_me_override(Opts),
TeOverride = get_te_override(Opts),
MibStorage = get_mib_storage(Opts),
- Data = snmpa_mib_data:new(MibStorage),
+ MibDataMod = get_data_mod(Opts),
+ Data = MibDataMod:new(MibStorage),
?vtrace("init -> mib data created",[]),
- case (catch mib_operations(load_mib, Mibs, Data,
+ case (catch mib_operations(MibDataMod,
+ load_mib, Mibs, Data,
MeOverride, TeOverride, true)) of
{ok, Data2} ->
?vdebug("started",[]),
- snmpa_mib_data:sync(Data2),
+ MibDataMod:sync(Data2),
?vdebug("mib data synced",[]),
{ok, #state{data = Data2,
teo = TeOverride,
@@ -304,7 +307,8 @@ do_init(Prio, Mibs, Opts) ->
cache_tmr = CacheGcTimer,
cache_autogc = CacheAutoGC,
cache_gclimit = CacheGcLimit,
- cache_age = CacheAge}};
+ cache_age = CacheAge,
+ data_mod = MibDataMod}};
{'aborted at', Mib, _NewData, Reason} ->
?vinfo("failed loading mib ~p: ~p",[Mib,Reason]),
{error, {Mib, Reason}}
@@ -315,32 +319,34 @@ do_init(Prio, Mibs, Opts) ->
%% Returns: {ok, NewMibData} | {'aborted at', Mib, NewData, Reason}
%% Args: Operation is load_mib | unload_mib.
%%----------------------------------------------------------------------
-mib_operations(Operation, Mibs, Data, MeOverride, TeOverride) ->
- mib_operations(Operation, Mibs, Data, MeOverride, TeOverride, false).
+mib_operations(Mod, Operation, Mibs, Data, MeOverride, TeOverride) ->
+ mib_operations(Mod, Operation, Mibs, Data, MeOverride, TeOverride, false).
-mib_operations(_Operation, [], Data, _MeOverride, _TeOverride, _Force) ->
+mib_operations(_Mod, _Operation, [], Data, _MeOverride, _TeOverride, _Force) ->
{ok, Data};
-mib_operations(Operation, [Mib|Mibs], Data0, MeOverride, TeOverride, Force) ->
+mib_operations(Mod, Operation, [Mib|Mibs], Data0, MeOverride, TeOverride, Force) ->
?vtrace("mib operations ~p on"
- "~n Mibs: ~p"
- "~n with "
- "~n MeOverride: ~p"
- "~n TeOverride: ~p"
- "~n Force: ~p", [Operation,Mibs,MeOverride,TeOverride,Force]),
- Data = mib_operation(Operation, Mib, Data0, MeOverride, TeOverride, Force),
- mib_operations(Operation, Mibs, Data, MeOverride, TeOverride, Force).
-
-mib_operation(Operation, Mib, Data0, MeOverride, TeOverride, Force)
+ "~n Mibs: ~p"
+ "~n with "
+ "~n MeOverride: ~p"
+ "~n TeOverride: ~p"
+ "~n Force: ~p",
+ [Operation, Mibs, MeOverride, TeOverride, Force]),
+ Data = mib_operation(Mod,
+ Operation, Mib, Data0, MeOverride, TeOverride, Force),
+ mib_operations(Mod, Operation, Mibs, Data, MeOverride, TeOverride, Force).
+
+mib_operation(Mod, Operation, Mib, Data0, MeOverride, TeOverride, Force)
when is_list(Mib) ->
?vtrace("mib operation on mib ~p", [Mib]),
- case apply(snmpa_mib_data, Operation, [Data0,Mib,MeOverride,TeOverride]) of
- {error, 'already loaded'} when (Operation =:= load_mib) andalso
+ case apply(Mod, Operation, [Data0, Mib, MeOverride, TeOverride]) of
+ {error, already_loaded} when (Operation =:= load_mib) andalso
(Force =:= true) ->
?vlog("ignore mib ~p -> already loaded", [Mib]),
Data0;
- {error, 'not loaded'} when (Operation =:= unload_mib) andalso
- (Force =:= true) ->
+ {error, not_loaded} when (Operation =:= unload_mib) andalso
+ (Force =:= true) ->
?vlog("ignore mib ~p -> not loaded", [Mib]),
Data0;
{error, Reason} ->
@@ -350,7 +356,7 @@ mib_operation(Operation, Mib, Data0, MeOverride, TeOverride, Force)
{ok, Data} ->
Data
end;
-mib_operation(_Op, Mib, Data, _MeOverride, _TeOverride, _Force) ->
+mib_operation(_Mod, _Op, Mib, Data, _MeOverride, _TeOverride, _Force) ->
throw({'aborted at', Mib, Data, bad_mibname}).
@@ -395,15 +401,15 @@ handle_call({update_cache_opts, Key, Value}, _From, State) ->
{reply, Result, NewState};
handle_call({lookup, Oid}, _From,
- #state{data = Data, cache = Cache} = State) ->
+ #state{data = Data, cache = Cache, data_mod = Mod} = State) ->
?vlog("lookup ~p", [Oid]),
Key = {lookup, Oid},
{Reply, NewState} =
case maybe_cache_lookup(Cache, Key) of
?NO_CACHE ->
- {snmpa_mib_data:lookup(Data, Oid), State};
+ {Mod:lookup(Data, Oid), State};
[] ->
- Rep = snmpa_mib_data:lookup(Data, Oid),
+ Rep = Mod:lookup(Data, Oid),
ets:insert(Cache, {Key, Rep, timestamp()}),
{Rep, maybe_start_cache_gc_timer(State)};
[{Key, Rep, _}] ->
@@ -414,22 +420,23 @@ handle_call({lookup, Oid}, _From,
?vdebug("lookup -> Reply: ~p", [Reply]),
{reply, Reply, NewState};
-handle_call({which_mib, Oid}, _From, #state{data = Data} = State) ->
+handle_call({which_mib, Oid}, _From,
+ #state{data = Data, data_mod = Mod} = State) ->
?vlog("which_mib ~p",[Oid]),
- Reply = snmpa_mib_data:which_mib(Data, Oid),
+ Reply = Mod:which_mib(Data, Oid),
?vdebug("which_mib: ~p",[Reply]),
{reply, Reply, State};
handle_call({next, Oid, MibView}, _From,
- #state{data = Data, cache = Cache} = State) ->
+ #state{data = Data, cache = Cache, data_mod = Mod} = State) ->
?vlog("next ~p [~p]", [Oid, MibView]),
Key = {next, Oid, MibView},
{Reply, NewState} =
case maybe_cache_lookup(Cache, Key) of
?NO_CACHE ->
- {snmpa_mib_data:next(Data, Oid, MibView), State};
+ {Mod:next(Data, Oid, MibView), State};
[] ->
- Rep = snmpa_mib_data:next(Data, Oid, MibView),
+ Rep = Mod:next(Data, Oid, MibView),
ets:insert(Cache, {Key, Rep, timestamp()}),
{Rep, maybe_start_cache_gc_timer(State)};
[{Key, Rep, _}] ->
@@ -441,89 +448,99 @@ handle_call({next, Oid, MibView}, _From,
{reply, Reply, NewState};
handle_call({load_mibs, Mibs}, _From,
- #state{data = Data,
- teo = TeOverride,
- meo = MeOverride,
- cache = Cache} = State) ->
+ #state{data = Data,
+ teo = TeOverride,
+ meo = MeOverride,
+ cache = Cache,
+ data_mod = Mod} = State) ->
?vlog("load mibs ~p",[Mibs]),
%% Invalidate cache
NewCache = maybe_invalidate_cache(Cache),
- {NData,Reply} =
- case (catch mib_operations(load_mib, Mibs, Data,
+ {NData, Reply} =
+ case (catch mib_operations(Mod, load_mib, Mibs, Data,
MeOverride, TeOverride)) of
{'aborted at', Mib, NewData, Reason} ->
?vlog("aborted at ~p for reason ~p",[Mib,Reason]),
- {NewData,{error, {'load aborted at', Mib, Reason}}};
+ {NewData, {error, {'load aborted at', Mib, Reason}}};
{ok, NewData} ->
- {NewData,ok}
+ {NewData, ok}
end,
- snmpa_mib_data:sync(NData),
+ Mod:sync(NData),
{reply, Reply, State#state{data = NData, cache = NewCache}};
handle_call({unload_mibs, Mibs}, _From,
- #state{data = Data,
- teo = TeOverride,
- meo = MeOverride,
- cache = Cache} = State) ->
+ #state{data = Data,
+ teo = TeOverride,
+ meo = MeOverride,
+ cache = Cache,
+ data_mod = Mod} = State) ->
?vlog("unload mibs ~p",[Mibs]),
%% Invalidate cache
NewCache = maybe_invalidate_cache(Cache),
%% Unload mib(s)
- {NData,Reply} =
- case (catch mib_operations(unload_mib, Mibs, Data,
+ {NData, Reply} =
+ case (catch mib_operations(Mod, unload_mib, Mibs, Data,
MeOverride, TeOverride)) of
{'aborted at', Mib, NewData, Reason} ->
- ?vlog("aborted at ~p for reason ~p",[Mib,Reason]),
+ ?vlog("aborted at ~p for reason ~p", [Mib,Reason]),
{NewData, {error, {'unload aborted at', Mib, Reason}}};
{ok, NewData} ->
- {NewData,ok}
+ {NewData, ok}
end,
- snmpa_mib_data:sync(NData),
+ Mod:sync(NData),
{reply, Reply, State#state{data = NData, cache = NewCache}};
-handle_call(which_mibs, _From, #state{data = Data} = State) ->
+handle_call(which_mibs, _From, #state{data = Data, data_mod = Mod} = State) ->
?vlog("which mibs",[]),
- Reply = snmpa_mib_data:which_mibs(Data),
+ Reply = Mod:which_mibs(Data),
{reply, Reply, State};
-handle_call({whereis_mib, Mib}, _From, #state{data = Data} = State) ->
+handle_call({whereis_mib, Mib}, _From,
+ #state{data = Data,
+ data_mod = Mod} = State) ->
?vlog("whereis mib: ~p",[Mib]),
- Reply = snmpa_mib_data:whereis_mib(Data, Mib),
+ Reply = Mod:whereis_mib(Data, Mib),
{reply, Reply, State};
handle_call({register_subagent, Oid, Pid}, _From,
- #state{data = Data, cache = Cache} = State) ->
+ #state{data = Data,
+ cache = Cache,
+ data_mod = Mod} = State) ->
?vlog("register subagent ~p, ~p",[Oid,Pid]),
%% Invalidate cache
NewCache = maybe_invalidate_cache(Cache),
- case snmpa_mib_data:register_subagent(Data, Oid, Pid) of
+ case Mod:register_subagent(Data, Oid, Pid) of
{error, Reason} ->
?vlog("registration failed: ~p",[Reason]),
{reply, {error, Reason}, State#state{cache = NewCache}};
- NewData ->
+ {ok, NewData} ->
{reply, ok, State#state{data = NewData, cache = NewCache}}
end;
handle_call({unregister_subagent, OidOrPid}, _From,
- #state{data = Data, cache = Cache} = State) ->
+ #state{data = Data,
+ cache = Cache,
+ data_mod = Mod} = State) ->
?vlog("unregister subagent ~p",[OidOrPid]),
%% Invalidate cache
NewCache = maybe_invalidate_cache(Cache),
- case snmpa_mib_data:unregister_subagent(Data, OidOrPid) of
+ case Mod:unregister_subagent(Data, OidOrPid) of
+ {ok, NewData} ->
+ {reply, ok, State#state{data = NewData, cache = NewCache}};
{ok, NewData, DeletedSubagentPid} ->
{reply, {ok, DeletedSubagentPid}, State#state{data = NewData,
cache = NewCache}};
{error, Reason} ->
?vlog("unregistration failed: ~p",[Reason]),
- {reply, {error, Reason}, State#state{cache = NewCache}};
- NewData ->
- {reply, ok, State#state{data = NewData, cache = NewCache}}
+ {reply, {error, Reason}, State#state{cache = NewCache}}
end;
-handle_call(info, _From, #state{data = Data, cache = Cache} = State) ->
+handle_call(info, _From, #state{data = Data,
+ cache = Cache,
+ data_mod = Mod} = State) ->
?vlog("info",[]),
Reply =
- case (catch snmpa_mib_data:info(Data)) of
+ case (catch Mod:info(Data)) of
Info when is_list(Info) ->
[{cache, size_cache(Cache)} | Info];
E ->
@@ -531,10 +548,12 @@ handle_call(info, _From, #state{data = Data, cache = Cache} = State) ->
end,
{reply, Reply, State};
-handle_call({info, Type}, _From, #state{data = Data} = State) ->
+handle_call({info, Type}, _From,
+ #state{data = Data,
+ data_mod = Mod} = State) ->
?vlog("info ~p",[Type]),
Reply =
- case (catch snmpa_mib_data:info(Data, Type)) of
+ case (catch Mod:info(Data, Type)) of
Info when is_list(Info) ->
Info;
E ->
@@ -542,21 +561,19 @@ handle_call({info, Type}, _From, #state{data = Data} = State) ->
end,
{reply, Reply, State};
-handle_call(dump, _From, State) ->
- ?vlog("dump",[]),
- Reply = snmpa_mib_data:dump(State#state.data),
- {reply, Reply, State};
-
-handle_call({dump, File}, _From, #state{data = Data} = State) ->
+handle_call({dump, File}, _From,
+ #state{data = Data, data_mod = Mod} = State) ->
?vlog("dump on ~s",[File]),
- Reply = snmpa_mib_data:dump(Data, File),
+ Reply = Mod:dump(Data, File),
{reply, Reply, State};
%% This check (that there is no backup already in progress) is also
%% done in the master agent process, but just in case a user issues
%% a backup call to this process directly, we add a similar check here.
handle_call({backup, BackupDir}, From,
- #state{backup = undefined, data = Data} = State) ->
+ #state{backup = undefined,
+ data = Data,
+ data_mod = Mod} = State) ->
?vlog("backup to ~s", [BackupDir]),
Pid = self(),
V = get(verbosity),
@@ -568,7 +585,7 @@ handle_call({backup, BackupDir}, From,
put(sname, ambs),
put(verbosity, V),
Dir = filename:join([BackupDir]),
- Reply = snmpa_mib_data:backup(Data, Dir),
+ Reply = Mod:backup(Data, Dir),
Pid ! {backup_done, Reply},
unlink(Pid)
end),
@@ -637,8 +654,8 @@ handle_info(Info, State) ->
warning_msg("received unknown info: ~n~p", [Info]),
{noreply, State}.
-terminate(_Reason, #state{data = Data}) ->
- catch snmpa_mib_data:close(Data),
+terminate(_Reason, #state{data = Data, data_mod = Mod}) ->
+ catch Mod:close(Data),
ok.
@@ -655,6 +672,11 @@ terminate(_Reason, #state{data = Data}) ->
%% S2 = {state, Data, MEO, TEO, B},
%% {ok, S2};
+code_change({down, Vsn}, #state{data = Data0, data_mod = Mod} = State, Extra) ->
+ Data = Mod:code_change(down, Vsn, Extra, Data0),
+ {ok, State#state{data = Data}};
+
+
%% %% upgrade
%% %%
%% code_change(_Vsn, S1, upgrade_from_pre_4_12) ->
@@ -663,8 +685,9 @@ terminate(_Reason, #state{data = Data}) ->
%% S2 = #state{data = Data, meo = MEO, teo = TEO, backup = B, cache = Cache},
%% {ok, S2};
-code_change(_Vsn, State, _Extra) ->
- {ok, State}.
+code_change(Vsn, #state{data = Data0, data_mod = Mod} = State, Extra) ->
+ Data = Mod:code_change(up, Vsn, Extra, Data0),
+ {ok, State#state{data = Data}}.
%%-----------------------------------------------------------------
@@ -683,6 +706,9 @@ get_te_override(Options) ->
get_mib_storage(Options) ->
get_opt(mib_storage, Options, ets).
+get_data_mod(Options) ->
+ get_opt(data_module, Options, snmpa_mib_data_tttn).
+
get_cacheopt_autogc(Cache, CacheOpts) ->
IsValid = fun(AutoGC) when ((AutoGC =:= true) orelse
(AutoGC =:= false)) ->
diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl
index b80d85d2ee..ffacdd5b3a 100644
--- a/lib/snmp/src/agent/snmpa_mib_data.erl
+++ b/lib/snmp/src/agent/snmpa_mib_data.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013. 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
@@ -18,1338 +18,110 @@
%%
-module(snmpa_mib_data).
-%%%-----------------------------------------------------------------
-%%% This module implements the MIB internal data structures.
-%%% An MIB Data Structure consists of three items; an ets-table,
-%%% a tree and a list of registered subagents.
-%%% The subagent information is consequently duplicated. It resides
-%%% both in the tree and in the list.
-%%% The ets-table contains all data associated with each variable,
-%%% table, tableentry and tablecolumn in the MIB.
-%%% The tree contains information of the Oids in the MIB.
-%%%
-%%% When a mib is loaded, the tree is built from the plain list
-%%% in the binary file.
-%%%-----------------------------------------------------------------
--include("snmp_types.hrl").
--include("snmp_debug.hrl").
-
--define(VMODULE,"MDATA").
--include("snmp_verbosity.hrl").
-
--define(MIB_DATA,snmpa_mib_data).
--define(MIB_NODE,snmpa_mib_node).
--define(MIB_TREE,snmpa_mib_tree).
--define(DUMMY_TREE_GENERATION,1).
--define(DEFAULT_TREE,{tree,{undefined_node},internal}).
-%%-define(DUMMY_TREE_DB,dummy_tree_db).
-%%-define(DUMMY_TREE_DB_INIT,{?DUMMY_TREE_DB,?DEFAULT_TREE}).
-
+-include_lib("snmp/include/snmp_types.hrl").
%%%-----------------------------------------------------------------
-%%% Table of contents
-%%% =================
-%%% 1. Interface
-%%% 2. Implementation of tree access
-%%% 3. Tree building functions
-%%% 4. Tree merging
-%%% 5. Tree deletion routines
-%%% 6. Functions for subagent handling
-%%% 7. Misc functions
+%%% This is the behaviour for the MIB server backend internal
+%%% data storage.
%%%-----------------------------------------------------------------
+%% These types should really be defined elsewhere...
+-export_type([
+ mib_storage/0,
+ mib_storage_dir/0,
+ mib_storage_action/0,
-%%----------------------------------------------------------------------
-%% data_db is an database containing loaded mibs as:
-%% {MibName = atom(), Symbolic = ?, FullFileName = string()}
-%% it is either ets or mnesia
-%% tree_db is a database containing _one_ record with the tree!
-%% (the reason for this is part to get replication and part out of convenience)
-%% ref_tree is the root node, without any subagent.
-%% tree is the root node (same as ref_tree but with the subagents added).
-%% subagents is a list of {SAPid, Oid}
-%%----------------------------------------------------------------------
--record(mib_data, {mib_db, % table of #mib_info
- node_db, % table of #node_info
- tree_db, % table of #tree
- tree, % The actual tree
- subagents = []}).
-
--record(mib_info, {name, symbolic, file_name}).
--record(node_info, {oid, mib_name, me}).
-
-
-%% API
--export([new/0, new/1, sync/1, close/1,
- load_mib/4, unload_mib/4, which_mibs/1, whereis_mib/2,
- info/1, info/2,
- dump/1, dump/2,
- backup/2,
- lookup/2, next/3, which_mib/2,
- register_subagent/3, unregister_subagent/2]).
-
-%% Internal exports
--export([code_change/2]).
-
-
-%%-----------------------------------------------------------------
-%% A tree is represented as a N-tuple, where each element is a
-%% node. A node is:
-%% 1) {tree, Tree, Info} where Info can be {table, Id}, {table_entry, Id}
-%% or perhaps 'internal'
-%% 2) undefined_node (memory optimization (instead of {node, undefined}))
-%% 3) {node, Info} where Info can be {subagent, Pid}, {variable, Id},
-%% {table_column, Id}
-%% Id is {MibName, MibEntry}
-%% The over all root is represented as {tree, Tree, internal}.
-%%
-%% tree() = {tree, nodes(), tree_info()}
-%% nodes() = {tree() | node() | undefined_node, ...}
-%% node() = {node, node_info()}
-%% tree_info() = {table, Id} | {table_entry, Id} | internal
-%% node_info() = {subagent, Pid} | {variable, Id} | {table_colum, Id}
-%%-----------------------------------------------------------------
-
-%% This record is what is stored in the database. The 'tree' part
-%% is described above...
--record(tree,{generation = ?DUMMY_TREE_GENERATION, root = ?DEFAULT_TREE}).
-
-
-%%%======================================================================
-%%% 1. Interface
-%%%======================================================================
-
-%%-----------------------------------------------------------------
-%% Func: new/0, new/1
-%% Returns: A representation of mib data.
-%%-----------------------------------------------------------------
-new() ->
- new(ets).
-
-%% Where -> A list of nodes where the tables will be created
-new(Storage) ->
- %% First we must check if there is already something to read
- %% If a database already exists, then the tree structure has to be read
- ?vtrace("open (mib) database",[]),
- MibDb = snmpa_general_db:open(Storage, ?MIB_DATA,
- mib_info,
- record_info(fields,mib_info), set),
- ?vtrace("open (mib) node database",[]),
- NodeDb = snmpa_general_db:open(Storage, ?MIB_NODE,
- node_info,
- record_info(fields,node_info), set),
- ?vtrace("open (mib) tree database",[]),
- TreeDb = snmpa_general_db:open(Storage, ?MIB_TREE,
- tree,
- record_info(fields,tree), set),
- Tree =
- case snmpa_general_db:read(TreeDb, ?DUMMY_TREE_GENERATION) of
- false ->
- T = #tree{},
- snmpa_general_db:write(TreeDb, T),
- T;
- {value, T} ->
- T
- end,
- install_mibs(MibDb, NodeDb),
- #mib_data{mib_db = MibDb,
- node_db = NodeDb,
- tree_db = TreeDb,
- tree = Tree}.
-
-
-%%----------------------------------------------------------------------
-%% Returns: new mib data | {error, Reason}
-%%----------------------------------------------------------------------
-load_mib(MibData,FileName,MeOverride,TeOverride)
- when is_record(MibData,mib_data) andalso is_list(FileName) ->
- ?vlog("load mib file: ~p",[FileName]),
- ActualFileName = filename:rootname(FileName, ".bin") ++ ".bin",
- MibName = list_to_atom(filename:basename(FileName, ".bin")),
- (catch do_load_mib(MibData, ActualFileName, MibName,
- MeOverride, TeOverride)).
-
-do_load_mib(MibData, ActualFileName, MibName, MeOverride, TeOverride) ->
- ?vtrace("do_load_mib -> entry with"
- "~n ActualFileName: ~s"
- "~n MibName: ~p",[ActualFileName, MibName]),
- #mib_data{mib_db = MibDb,
- node_db = NodeDb,
- %% tree_db = TreeDb,
- tree = Tree} = MibData,
- verify_not_loaded(MibDb, MibName),
- ?vtrace("do_load_mib -> already loaded mibs:"
- "~n ~p",[loaded(MibDb)]),
- Mib = do_read_mib(ActualFileName),
- ?vtrace("do_load_mib -> read mib ~s",[Mib#mib.name]),
- NonInternalMes =
- lists:filter(fun(ME) -> maybe_drop_me(ME) end, Mib#mib.mes),
- OldRoot = Tree#tree.root,
- T = build_tree(NonInternalMes, MibName),
- ?d("load_mib -> "
- "~n OldRoot: ~p"
- "~n T: ~p", [OldRoot, T]),
- case (catch merge_nodes(T, OldRoot)) of
- {error_merge_nodes, Node1, Node2} ->
- ?vlog("error merging nodes:"
- "~n~p~nand~n~p", [Node1,Node2]),
- {error, oid_conflict};
- NewRoot when is_tuple(NewRoot) andalso (element(1,NewRoot) =:= tree) ->
- ?d("load_mib -> "
- "~n NewRoot: ~p", [NewRoot]),
- Symbolic = not lists:member(no_symbolic_info, Mib#mib.misc),
- case (catch check_notif_and_mes(TeOverride, MeOverride, Symbolic,
- Mib#mib.traps, NonInternalMes)) of
- true ->
- install_mes(NodeDb, MibName, NonInternalMes),
- install_mib(MibDb, Symbolic, Mib,
- MibName, ActualFileName, NonInternalMes),
- ?vtrace("installed mib ~s", [Mib#mib.name]),
- Tree2 = Tree#tree{root = NewRoot},
- %% snmpa_general_db:write(TreeDb, Tree2), %% Store later?
- {ok, MibData#mib_data{tree = Tree2}};
- Else ->
- Else
- end
- end.
-
-
-verify_not_loaded(Db, Name) ->
- case snmpa_general_db:read(Db, Name) of
- {value, #mib_info{name = Name}} ->
- throw({error, 'already loaded'});
- false ->
- ok
- end.
-
-do_read_mib(ActualFileName) ->
- case snmp_misc:read_mib(ActualFileName) of
- {error, Reason} ->
- ?vlog("Failed reading mib file ~p with reason: ~p",
- [ActualFileName,Reason]),
- throw({error, Reason});
- {ok, Mib} ->
- Mib
- end.
-
-%% The Tree DB is handled in a special way since it can be very large.
-sync(#mib_data{mib_db = M,
- node_db = N,
- tree_db = T, tree = Tree, subagents = []}) ->
- snmpa_general_db:sync(M),
- snmpa_general_db:sync(N),
- snmpa_general_db:write(T, Tree),
- snmpa_general_db:sync(T);
-sync(#mib_data{mib_db = M,
- node_db = N,
- tree_db = T, tree = Tree, subagents = SAs}) ->
-
- snmpa_general_db:sync(M),
- snmpa_general_db:sync(N),
-
- %% Ouch. Since the subagent info is dynamic we do not
- %% want to store the tree containing subagent info. So, we
- %% have to create a tmp tree without those and store it.
-
- case delete_subagents(Tree, SAs) of
- {ok, TreeWithoutSAs} ->
- snmpa_general_db:write(T, TreeWithoutSAs),
- snmpa_general_db:sync(T);
- Error ->
- Error
- end.
-
-delete_subagents(Tree, []) ->
- {ok, Tree};
-delete_subagents(Tree0, [{_, Oid}|SAs]) ->
- case (catch delete_subagent(Tree0, Oid)) of
- {tree, _Tree, _Info} = Tree1 ->
- delete_subagents(Tree1, SAs);
- _Error ->
- {error, {'invalid oid', Oid}}
- end.
-
-%%----------------------------------------------------------------------
-%% (OTP-3601)
-%%----------------------------------------------------------------------
-check_notif_and_mes(TeOverride,MeOverride,Symbolic,Traps,MEs) ->
- ?vtrace("check notifications and mib entries",[]),
- check_notifications(TeOverride,Symbolic,Traps),
- check_mes(MeOverride,MEs).
-
-check_notifications(true, _Symbolic, _Traps) ->
- ?vtrace("trapentry override = true => skip check",[]),
- true;
-check_notifications(_, Symbolic, Traps) ->
- check_notifications(Symbolic, Traps).
-
-check_notifications(true, Traps) ->
- check_notifications(Traps);
-check_notifications(_, _) -> true.
-
-check_notifications([]) -> true;
-check_notifications([#trap{trapname = Key} = Trap | Traps]) ->
- ?vtrace("check notification [trap] with Key: ~p",[Key]),
- case snmpa_symbolic_store:get_notification(Key) of
- {value, Trap} -> check_notifications(Traps);
- {value, _} -> throw({error, {'trap already defined', Key}});
- undefined -> check_notifications(Traps)
- end;
-check_notifications([#notification{trapname = Key} = Notif | Traps]) ->
- ?vtrace("check notification [notification] with Key: ~p",[Key]),
- case snmpa_symbolic_store:get_notification(Key) of
- {value, Notif} ->
- check_notifications(Traps);
- {value, _} ->
- throw({error, {'notification already defined', Key}});
- undefined ->
- check_notifications(Traps)
- end;
-check_notifications([Crap | Traps]) ->
- ?vlog("skipped check of: ~n~p",[Crap]),
- check_notifications(Traps).
-
-check_mes(true,_) ->
- ?vtrace("mibentry override = true => skip check",[]),
- true;
-check_mes(_,MEs) ->
- check_mes(MEs).
-
-check_mes([]) -> true;
-check_mes([#me{aliasname = Name, oid = Oid1} | MEs]) ->
- ?vtrace("check mib entries with aliasname: ~p",[Name]),
- case snmpa_symbolic_store:aliasname_to_oid(Name) of
- {value, Oid1} ->
- check_mes(MEs);
- {value, Oid2} ->
- ?vinfo("~n expecting '~p'~n but found '~p'",[Oid1, Oid2]),
- throw({error, {'mibentry already defined', Name}});
- false ->
- check_mes(MEs)
- end;
-check_mes([Crap | MEs]) ->
- ?vlog("skipped check of: ~n~p",[Crap]),
- check_mes(MEs).
-
-
-
-%%----------------------------------------------------------------------
-%% Returns: new mib data | {error, Reason}
-%%----------------------------------------------------------------------
-unload_mib(MibData, FileName, _, _) when is_list(FileName) ->
- MibName = list_to_atom(filename:basename(FileName, ".bin")),
- (catch do_unload_mib(MibData, MibName)).
-
-do_unload_mib(MibData, MibName) ->
- ?vtrace("do_unload_mib -> entry with"
- "~n MibName: ~p", [MibName]),
- #mib_data{mib_db = MibDb,
- node_db = NodeDb,
- %% tree_db = TreeDb,
- tree = Tree} = MibData,
- #mib_info{symbolic = Symbolic} = verify_loaded(MibDb, MibName),
- NewRoot = delete_mib_from_tree(MibName, Tree#tree.root),
- MEs = uninstall_mes(NodeDb, MibName),
- uninstall_mib(MibDb, Symbolic, MibName, MEs),
- NewMibData = MibData#mib_data{tree = Tree#tree{root = NewRoot}},
- {ok, NewMibData}.
-
-verify_loaded(Db, Name) ->
- case snmpa_general_db:read(Db, Name) of
- {value, MibInfo} ->
- MibInfo;
- false ->
- throw({error, 'not loaded'})
- end.
-
-
-close(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb}) ->
- snmpa_general_db:close(MibDb),
- snmpa_general_db:close(NodeDb),
- snmpa_general_db:close(TreeDb),
- ok.
-
-register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) ->
- case insert_subagent(Oid, T#tree.root) of
- {error, Reason} ->
- {error, Reason};
- NewRootTree ->
- SAs = [{Pid, Oid} | MibData#mib_data.subagents],
- T2 = T#tree{root = NewRootTree},
- MibData#mib_data{tree = T2, subagents = SAs}
- end.
-
-
-%%----------------------------------------------------------------------
-%% Purpose: Get a list of all loaded mibs
-%% Returns: [{Name, File}]
-%%----------------------------------------------------------------------
-
-which_mibs(#mib_data{mib_db = Db}) ->
- Mibs = snmpa_general_db:tab2list(Db),
- [{Name, File} || #mib_info{name = Name, file_name = File} <- Mibs].
-
-
-%%----------------------------------------------------------------------
-%% Purpose: Get a list of all loaded mibs
-%% Returns: [{Name, File}]
-%%----------------------------------------------------------------------
-
-whereis_mib(#mib_data{mib_db = Db}, Name) ->
- case snmpa_general_db:read(Db, Name) of
- {value, #mib_info{file_name = File}} ->
- {ok, File};
- false ->
- {error, not_found}
- end.
-
-
-%%----------------------------------------------------------------------
-%% Purpose: Deletes SA with Pid from all subtrees it handles.
-%% Returns: NewMibData.
-%%----------------------------------------------------------------------
-unregister_subagent(MibData, Pid) when is_pid(Pid) ->
- SAs = MibData#mib_data.subagents,
- case lists:keysearch(Pid, 1, SAs) of
- false -> MibData;
- {value, {Pid, Oid}} ->
- % we should never get an error since Oid is found in MibData.
- {ok, NewMibData, _DeletedSA} = unregister_subagent(MibData, Oid),
- % continue if the same Pid handles other mib subtrees.
- unregister_subagent(NewMibData, Pid)
- end;
-
-%%----------------------------------------------------------------------
-%% Purpose: Deletes one unique subagent.
-%% Returns: {error, Reason} | {ok, NewMibData, DeletedSubagentPid}
-%%----------------------------------------------------------------------
-unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) ->
- case catch delete_subagent(T#tree.root, Oid) of
- {tree, Tree, Info} ->
- OldSAs = MibData#mib_data.subagents,
- {value, {Pid, _Oid}} = lists:keysearch(Oid, 2, OldSAs),
- SAs = lists:keydelete(Oid, 2, OldSAs),
- T2 = T#tree{root = {tree, Tree, Info}},
- {ok,
- MibData#mib_data{tree = T2, subagents = SAs},
- Pid};
- _ ->
- {error, {'invalid oid', Oid}}
- end.
-
-%%----------------------------------------------------------------------
-%% Purpose: To inpect memory usage, loaded mibs, registered subagents
-%%----------------------------------------------------------------------
-info(MibData) ->
- ?vtrace("retrieve info",[]),
- #mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb,
- tree = Tree, subagents = SAs} = MibData,
- LoadedMibs = old_format(snmpa_general_db:tab2list(MibDb)),
- TreeSize = snmp_misc:mem_size(Tree),
- {memory, ProcSize} = erlang:process_info(self(),memory),
- MibDbSize = snmpa_general_db:info(MibDb, memory),
- NodeDbSize = snmpa_general_db:info(NodeDb, memory),
- TreeDbSize = snmpa_general_db:info(TreeDb, memory),
- [{loaded_mibs, LoadedMibs}, {subagents, SAs}, {tree_size_bytes, TreeSize},
- {process_memory, ProcSize},
- {db_memory, [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}]}].
-
-info(#mib_data{mib_db = MibDb}, loaded_mibs) ->
- Mibs = snmpa_general_db:tab2list(MibDb),
- [filename:rootname(FN, ".bin") || #mib_info{file_name = FN} <- Mibs];
-info(#mib_data{tree = Tree}, tree_size_bytes) ->
- snmp_misc:mem_size(Tree);
-info(_, process_memory) ->
- {memory, ProcSize} = erlang:process_info(self(),memory),
- ProcSize;
-info(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb},
- db_memory) ->
- MibDbSize = snmpa_general_db:info(MibDb, memory),
- NodeDbSize = snmpa_general_db:info(NodeDb, memory),
- TreeDbSize = snmpa_general_db:info(TreeDb, memory),
- [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}];
-info(#mib_data{subagents = SAs}, subagents) ->
- SAs.
-
-old_format(LoadedMibs) ->
- ?vtrace("convert mib info to old format",[]),
- [{N,S,F} || #mib_info{name=N,symbolic=S,file_name=F} <- LoadedMibs].
-
-
-%%----------------------------------------------------------------------
-%% A total dump for debugging.
-%%----------------------------------------------------------------------
-dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}) ->
- (catch io:format("MIB-tables:~n~p~n~n",
- [snmpa_general_db:tab2list(MibDb)])),
- (catch io:format("MIB-entries:~n~p~n~n",
- [snmpa_general_db:tab2list(NodeDb)])),
- (catch io:format("Tree:~n~p~n", [Tree])), % good luck reading it!
- ok.
-
-dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, File) ->
- case file:open(File,[write]) of
- {ok, Fd} ->
- io:format(Fd,"~s~n",
- [snmp:date_and_time_to_string(snmp:date_and_time())]),
- (catch io:format(Fd,"MIB-tables:~n~p~n~n",
- [snmpa_general_db:tab2list(MibDb)])),
- (catch io:format(Fd, "MIB-entries:~n~p~n~n",
- [snmpa_general_db:tab2list(NodeDb)])),
- io:format(Fd,"Tree:~n~p~n", [Tree]), % good luck reading it!
- file:close(Fd),
- ok;
- {error,Reason} ->
- ?vinfo("~n Failed opening file '~s' for reason ~p",
- [File,Reason]),
- {error,Reason}
- end.
-
-
-backup(#mib_data{mib_db = M, node_db = N, tree_db = T}, BackupDir) ->
- MRes = snmpa_general_db:backup(M, BackupDir),
- NRes = snmpa_general_db:backup(N, BackupDir),
- TRes = snmpa_general_db:backup(T, BackupDir),
- handle_backup_res([{mib_db, MRes}, {node_db, NRes}, {tree_db, TRes}]).
-
-handle_backup_res(Res) ->
- handle_backup_res(Res, []).
-
-handle_backup_res([], []) ->
- ok;
-handle_backup_res([], Err) ->
- {error, lists:reverse(Err)};
-handle_backup_res([{_, ok}|Res], Err) ->
- handle_backup_res(Res, Err);
-handle_backup_res([{Tag, {error, Reason}}|Res], Err) ->
- handle_backup_res(Res, [{Tag, Reason}|Err]);
-handle_backup_res([{Tag, Error}|Res], Err) ->
- handle_backup_res(Res, [{Tag, Error}|Err]).
-
-
-%%%======================================================================
-%%% 2. Implementation of tree access
-%%% lookup and next.
-%%%======================================================================
-
-
-which_mib(#mib_data{tree = T} = D, Oid) ->
- ?vtrace("which_mib -> entry with"
- "~n Oid: ~p",[Oid]),
- case (catch find_node(D, T#tree.root, Oid, [])) of
- {variable, _ME, Mib} ->
- ?vtrace("which_mib -> variable:"
- "~n Mib: ~p", [Mib]),
- {ok, Mib};
- {table, _EntryME, _, Mib} ->
- ?vtrace("which_mib -> table:"
- "~n Mib: ~p", [Mib]),
- {ok, Mib};
- {subagent, SubAgentPid, _SANextOid} ->
- ?vtrace("which_mib -> subagent:"
- "~n SubAgentPid: ~p", [SubAgentPid]),
- {error, {subagent, SubAgentPid}};
- {false, ErrorCode} ->
- ?vtrace("which_mib -> false:"
- "~n ErrorCode: ~p",[ErrorCode]),
- {error, ErrorCode};
- false ->
- ?vtrace("which_mib -> false",[]),
- {error, noSuchObject};
- {'EXIT', R} ->
- ?vtrace("which_mib -> exit:"
- "~n R: ~p",[R]),
- {error, noSuchObject}
- end.
-
-
-%%-----------------------------------------------------------------
-%% Func: lookup/2
-%% Purpose: Finds the mib entry corresponding to the Oid. If it is a
-%% variable, the Oid must be <Oid for var>.0 and if it is
-%% a table, Oid must be <table>.<entry>.<col>.<any>
-%% Returns: {variable, MibEntry} |
-%% {table_column, MibEntry, TableEntryOid} |
-%% {subagent, SubAgentPid, SAOid} |
-%% {false, Reason}
-%%-----------------------------------------------------------------
-lookup(#mib_data{tree = T} = D, Oid) ->
- ?vtrace("lookup -> entry with"
- "~n Oid: ~p",[Oid]),
- case (catch find_node(D, T#tree.root, Oid, [])) of
- {variable, ME, _Mib} when is_record(ME, me) ->
- ?vtrace("lookup -> variable:"
- "~n ME: ~p",[ME]),
- {variable, ME};
- {table, EntryME, {ColME, TableEntryOid}, _Mib} ->
- ?vtrace("lookup -> table:"
- "~n EntryME: ~p"
- "~n ColME: ~p"
- "~n RevTableEntryOid: ~p",
- [EntryME, ColME, TableEntryOid]),
- MFA = EntryME#me.mfa,
- RetME = ColME#me{mfa = MFA},
- {table_column, RetME, TableEntryOid};
- {subagent, SubAgentPid, SANextOid} ->
- ?vtrace("lookup -> subagent:"
- "~n SubAgentPid: ~p"
- "~n SANextOid: ~p", [SubAgentPid, SANextOid]),
- {subagent, SubAgentPid, SANextOid};
- {false, ErrorCode} ->
- ?vtrace("lookup -> false:"
- "~n ErrorCode: ~p",[ErrorCode]),
- {false, ErrorCode};
- false ->
- ?vtrace("lookup -> false",[]),
- {false, noSuchObject};
- {'EXIT', R} ->
- ?vtrace("lookup -> exit:"
- "~n R: ~p",[R]),
- {false, noSuchObject}
- end.
-
-
-find_node(D, {tree, Tree, {table, _}}, RestOfOid, RevOid) ->
- ?vtrace("find_node(tree,table) -> entry with"
- "~n RestOfOid: ~p"
- "~n RevOid: ~p",[RestOfOid, RevOid]),
- find_node(D, {tree, Tree, internal}, RestOfOid, RevOid);
-find_node(D, {tree, Tree, {table_entry, _}}, RestOfOid, RevOid) ->
- ?vtrace("find_node(tree,table_entry) -> entry with"
- "~n RestOfOid: ~p"
- "~n RevOid: ~p",[RestOfOid, RevOid]),
- #mib_data{node_db = Db} = D,
- Oid = lists:reverse(RevOid),
- case snmpa_general_db:read(Db, Oid) of
- {value, #node_info{me = ME, mib_name = Mib}} ->
- case find_node(D, {tree, Tree, internal}, RestOfOid, RevOid) of
- {false, ErrorCode} -> {false, ErrorCode};
- Val -> {table, ME, Val, Mib}
- end;
- false ->
- ?vinfo("find_node -> could not find table_entry ME with"
- "~n RevOid: ~p"
- "~n when"
- "~n RestOfOid: ~p",
- [RevOid, RestOfOid]),
- false
- end;
-find_node(D, {tree, Tree, _Internal}, [Int | RestOfOid], RevOid) ->
- ?vtrace("find_node(tree) -> entry with"
- "~n Int: ~p"
- "~n RestOfOid: ~p"
- "~n RevOid: ~p",[Int, RestOfOid, RevOid]),
- find_node(D, element(Int+1, Tree), RestOfOid, [Int | RevOid]);
-find_node(D, {node, {table_column, _}}, RestOfOid, [ColInt | RevOid]) ->
- ?vtrace("find_node(tree,table_column) -> entry with"
- "~n RestOfOid: ~p"
- "~n ColInt: ~p"
- "~n RevOid: ~p",[RestOfOid, ColInt, RevOid]),
- #mib_data{node_db = Db} = D,
- Oid = lists:reverse([ColInt | RevOid]),
- case snmpa_general_db:read(Db, Oid) of
- {value, #node_info{me = ME}} ->
- {ME, lists:reverse(RevOid)};
- false ->
- X = snmpa_general_db:read(Db, lists:reverse([ColInt | RevOid])),
- ?vinfo("find_node -> could not find table_column ME with"
- "~n RevOid: ~p"
- "~n trying [~p|~p]"
- "~n X: ~p",
- [RevOid, [ColInt | RevOid], X]),
- false
- end;
-find_node(D, {node, {variable, _MibName}}, [0], RevOid) ->
- ?vtrace("find_node(tree,variable,[0]) -> entry with"
- "~n RevOid: ~p",[RevOid]),
- #mib_data{node_db = Db} = D,
- Oid = lists:reverse(RevOid),
- %% {value, #node_info{me = ME}} = snmpa_general_db:read(Db, Oid),
- case snmpa_general_db:read(Db, Oid) of
- {value, #node_info{me = ME, mib_name = Mib}} ->
- {variable, ME, Mib};
- false ->
- ?vinfo("find_node -> could not find variable ME with"
- "~n RevOid: ~p", [RevOid]),
- false
- end;
-find_node(_D, {node, {variable, _MibName}}, [], _RevOid) ->
- ?vtrace("find_node(tree,variable,[]) -> entry",[]),
- {false, noSuchObject};
-find_node(_D, {node, {variable, _MibName}}, _, _RevOid) ->
- ?vtrace("find_node(tree,variable) -> entry",[]),
- {false, noSuchInstance};
-find_node(D, {node, subagent}, _RestOfOid, SARevOid) ->
- ?vtrace("find_node(tree,subagent) -> entry with"
- "~n SARevOid: ~p",[SARevOid]),
- #mib_data{subagents = SAs} = D,
- SAOid = lists:reverse(SARevOid),
- case lists:keysearch(SAOid, 2, SAs) of
- {value, {SubAgentPid, SAOid}} ->
- {subagent, SubAgentPid, SAOid};
- false ->
- ?vinfo("find_node -> could not find subagent with"
- "~n SAOid: ~p"
- "~n SAs: ~p", [SAOid, SAs]),
- false
- end;
-find_node(_D, Node, _RestOfOid, _RevOid) ->
- ?vtrace("find_node -> failed:~n~p",[Node]),
- {false, noSuchObject}.
-
-
-%%-----------------------------------------------------------------
-%% Func: next/3
-%% Purpose: Finds the lexicographically next oid.
-%% Returns: endOfMibView |
-%% {subagent, SubAgentPid, SAOid} |
-%% {variable, MibEntry, VarOid} |
-%% {table, TableOid, TableRestOid, MibEntry}
-%% If a variable is returnes, it is in the MibView.
-%% If a table or subagent is returned, it *may* be in the MibView.
-%%-----------------------------------------------------------------
-next(#mib_data{tree = T} = D, Oid, MibView) ->
- case catch next_node(D, T#tree.root, Oid, [], MibView) of
- false -> endOfMibView;
- Else -> Else
- end.
-
-%%-----------------------------------------------------------------
-%% This function is used as long as we have any Oid left. Take
-%% one integer at a time from the Oid, and traverse the tree
-%% accordingly. When the Oid is empty, call find_next.
-%% Returns: {subagent, SubAgentPid, SAOid} |
-%% false |
-%% {variable, MibEntry, VarOid} |
-%% {table, TableOid, TableRestOid, MibEntry}
-%%-----------------------------------------------------------------
-next_node(_D, undefined_node, _Oid, _RevOidSoFar, _MibView) ->
- ?vtrace("next_node(undefined_node) -> entry", []),
- false;
-
-next_node(_D, {tree, Tree, {table_entry, _Id}}, [Int | _Oid],
- _RevOidSoFar, _MibView)
- when Int+1 > size(Tree) ->
- ?vtrace("next_node(tree,table_entry) -> entry when not found whith"
- "~n Int: ~p"
- "~n size(Tree): ~p", [Int, size(Tree)]),
- false;
-next_node(D, {tree, Tree, {table_entry, _MibName}},
- Oid, RevOidSoFar, MibView) ->
- ?vtrace("next_node(tree,table_entry) -> entry when"
- "~n size(Tree): ~p"
- "~n Oid: ~p"
- "~n RevOidSoFar: ~p"
- "~n MibView: ~p", [size(Tree), Oid, RevOidSoFar, MibView]),
- OidSoFar = lists:reverse(RevOidSoFar),
- case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
- true ->
- ?vdebug("next_node(tree,table_entry) -> not in mib view",[]),
- false;
- _ ->
- #mib_data{node_db = Db} = D,
- case snmpa_general_db:read(Db, OidSoFar) of
- false ->
- ?vinfo("next_node -> could not find table_entry with"
- "~n OidSoFar: ~p", [OidSoFar]),
- false;
- {value, #node_info{me = ME}} ->
- ?vtrace("next_node(tree,table_entry) -> found: ~n ~p",
- [ME]),
- {table, OidSoFar, Oid, ME}
- end
- end;
-
-next_node(D, {tree, Tree, _Info}, [Int | RestOfOid], RevOidSoFar, MibView)
- when (Int < size(Tree)) andalso (Int >= 0) ->
- ?vtrace("next_node(tree) -> entry when"
- "~n size(Tree): ~p"
- "~n Int: ~p"
- "~n RestOfOid: ~p"
- "~n RevOidSoFar: ~p"
- "~n MibView: ~p",
- [size(Tree), Int, RestOfOid, RevOidSoFar, MibView]),
- case next_node(D, element(Int+1,Tree),
- RestOfOid, [Int|RevOidSoFar], MibView) of
- false ->
- find_next(D, {tree, Tree, _Info}, Int+1, RevOidSoFar, MibView);
- Else ->
- Else
- end;
-%% no solution
-next_node(D, {tree, Tree, _Info}, [], RevOidSoFar, MibView) ->
- ?vtrace("next_node(tree,[]) -> entry when"
- "~n size(Tree): ~p"
- "~n RevOidSoFar: ~p"
- "~n MibView: ~p",
- [size(Tree), RevOidSoFar, MibView]),
- find_next(D, {tree, Tree, _Info}, 0, RevOidSoFar, MibView);
-next_node(_D, {tree, Tree, _Info}, _RestOfOid, _RevOidSoFar, _MibView) ->
- ?vtrace("next_node(tree) -> entry when"
- "~n size(Tree): ~p", [size(Tree)]),
- false;
-
-next_node(D, {node, subagent}, Oid, RevOidSoFar, MibView) ->
- ?vtrace("next_node(node,subagent) -> entry when"
- "~n Oid: ~p"
- "~n RevOidSoFar: ~p"
- "~n MibView: ~p",
- [Oid, RevOidSoFar, MibView]),
- OidSoFar = lists:reverse(RevOidSoFar),
- case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
- true ->
- false;
- _ ->
- #mib_data{subagents = SAs} = D,
- case lists:keysearch(OidSoFar, 2, SAs) of
- {value, {SubAgentPid, OidSoFar}} ->
- {subagent, SubAgentPid, OidSoFar};
- _ ->
- ?vinfo("next_node -> could not find subagent with"
- "~n OidSoFar: ~p"
- "~n SAs: ~p", [OidSoFar, SAs]),
- false
- end
- end;
-
-next_node(D, {node, {variable, _MibName}}, [], RevOidSoFar, MibView) ->
- ?vtrace("next_node(node,variable,[]) -> entry when"
- "~n RevOidSoFar: ~p"
- "~n MibView: ~p",
- [RevOidSoFar, MibView]),
- OidSoFar = lists:reverse([0 | RevOidSoFar]),
- case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
- true ->
- #mib_data{node_db = Db} = D,
- case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
- false ->
- ?vinfo("next_node -> could not find variable with"
- "~n RevOidSoFar: ~p", [RevOidSoFar]),
- false;
- {value, #node_info{me = ME}} ->
- {variable, ME, OidSoFar}
- end;
- _ ->
- false
- end;
-
-next_node(_D, {node, {variable, _MibName}}, _Oid, _RevOidSoFar, _MibView) ->
- ?vtrace("next_node(node,variable) -> entry", []),
- false.
-
-%%-----------------------------------------------------------------
-%% This function is used to find the first leaf from where we
-%% are.
-%% Returns: {subagent, SubAgentPid, SAOid} |
-%% false |
-%% {variable, MibEntry, VarOid} |
-%% {table, TableOid, TableRestOid, MibEntry}
-%% PRE: This function must always be called with a {internal, Tree}
-%% node.
-%%-----------------------------------------------------------------
-find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView)
- when Idx < size(Tree) ->
- case find_next(D, element(Idx+1, Tree), 0, [Idx| RevOidSoFar], MibView) of
- false ->
- find_next(D, {tree, Tree, internal}, Idx+1, RevOidSoFar, MibView);
- Other ->
- Other
- end;
-find_next(_D, {tree, _Tree, internal}, _Idx, _RevOidSoFar, _MibView) ->
- false;
-find_next(_D, undefined_node, _Idx, _RevOidSoFar, _MibView) ->
- false;
-find_next(D, {tree, Tree, {table, _MibName}}, Idx, RevOidSoFar, MibView) ->
- find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView);
-find_next(D, {tree, _Tree, {table_entry, _MibName}}, _Index,
- RevOidSoFar, MibView) ->
- OidSoFar = lists:reverse(RevOidSoFar),
- case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
- true ->
- false;
- _ ->
- #mib_data{node_db = Db} = D,
- case snmpa_general_db:read(Db, OidSoFar) of
- false ->
- ?vinfo("find_next -> could not find table_entry ME with"
- "~n OidSoFar: ~p", [OidSoFar]),
- false;
- {value, #node_info{me = ME}} ->
- {table, OidSoFar, [], ME}
- end
- end;
-find_next(D, {node, {variable, _MibName}}, _Idx, RevOidSoFar, MibView) ->
- OidSoFar = lists:reverse([0 | RevOidSoFar]),
- case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
- true ->
- #mib_data{node_db = Db} = D,
- case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
- false ->
- ?vinfo("find_next -> could not find variable with"
- "~n RevOidSoFar: ~p", [RevOidSoFar]),
- false;
- {value, #node_info{me = ME}} ->
- {variable, ME, OidSoFar}
- end;
- _ ->
- false
- end;
-find_next(D, {node, subagent}, _Idx, RevOidSoFar, MibView) ->
- OidSoFar = lists:reverse(RevOidSoFar),
- case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
- true ->
- false;
- _ ->
- #mib_data{subagents = SAs} = D,
- case lists:keysearch(OidSoFar, 2, SAs) of
- {value, {SubAgentPid, OidSoFar}} ->
- {subagent, SubAgentPid, OidSoFar};
- false ->
- ?vinfo("find_node -> could not find subagent with"
- "~n OidSoFar: ~p"
- "~n SAs: ~p", [OidSoFar, SAs]),
- false
- end
- end.
-
-%%%======================================================================
-%%% 3. Tree building functions
-%%% Used when loading mibs.
-%%%======================================================================
-
-build_tree(Mes, MibName) ->
- ?d("build_tree -> "
- "~n Mes: ~p", [Mes]),
- {ListTree, []} = build_subtree([], Mes, MibName),
- {tree, convert_tree(ListTree), internal}.
-
-%%----------------------------------------------------------------------
-%% Purpose: Builds the tree where all oids have prefix equal to LevelPrefix.
-%% Returns: {Tree, RestMes}
-%% RestMes are Mes that should not be in this subtree.
-%% The Tree is a temporary and simplified data structure that is easy to
-%% convert to the final tuple tree used by the MIB process.
-%% A Node is represented as in the final tree.
-%% The tree is not represented as a N-tuple, but as an Index-list.
-%% Example: Temporary: [{1, Node1}, {3, Node3}]
-%% Final: {Node1, undefined_node, Node3}
-%% Pre: Mes are sorted on oid.
-%%----------------------------------------------------------------------
-build_subtree(LevelPrefix, [Me | Mes], MibName) ->
- ?vtrace("build subtree -> ~n"
- " oid: ~p~n"
- " LevelPrefix: ~p~n"
- " MibName: ~p", [Me#me.oid, LevelPrefix, MibName]),
- EType = Me#me.entrytype,
- ?vtrace("build subtree -> EType = ~p",[EType]),
- case in_subtree(LevelPrefix, Me) of
- above ->
- ?vtrace("build subtree -> above",[]),
- {[], [Me|Mes]};
- {node, Index} ->
- ?vtrace("build subtree -> node at ~p",[Index]),
- {Tree, RestMes} = build_subtree(LevelPrefix, Mes, MibName),
- {[{Index, {node, {EType, MibName}}} | Tree], RestMes};
- {subtree, Index, NewLevelPrefix} ->
- ?vtrace("build subtree -> subtree at"
- "~n ~w with ~w",
- [Index, NewLevelPrefix]),
- {BelowTree, RestMes} =
- build_subtree(NewLevelPrefix, Mes, MibName),
- {CurTree, RestMes2} =
- build_subtree(LevelPrefix, RestMes, MibName),
- {[{Index, {tree, BelowTree, {EType,MibName}}}| CurTree], RestMes2};
- {internal_subtree, Index, NewLevelPrefix} ->
- ?vtrace("build subtree -> internal_subtree at"
- "~n ~w with ~w",
- [Index,NewLevelPrefix]),
- {BelowTree, RestMes} =
- build_subtree(NewLevelPrefix, [Me | Mes], MibName),
- {CurTree, RestMes2} =
- build_subtree(LevelPrefix, RestMes, MibName),
- {[{Index, {tree, BelowTree, internal}} | CurTree], RestMes2}
- end;
-
-build_subtree(_LevelPrefix, [], _MibName) ->
- ?vtrace("build subtree -> done", []),
- {[], []}.
-
-%%--------------------------------------------------
-%% Purpose: Determine how/if/where Me should be inserted in subtree
-%% with LevelPrefix. This function does not build any tree, only
-%% determinses what should be done (by build subtree).
-%% Returns:
-%% above - Indicating that this ME should _not_ be in this subtree.
-%% {node, Index} - yes, construct a node with index Index on this level
-%% {internal_subtree, Index, NewLevelPrefix} - yes, there should be an
-%% internal subtree at this index.
-%% {subtree, Index, NewLevelPrefix} - yes, construct a subtree with
-%% NewLevelPrefix and insert this on current level in position Index.
-%%--------------------------------------------------
-in_subtree(LevelPrefix, Me) ->
- case lists:prefix(LevelPrefix, Me#me.oid) of
- true when length(Me#me.oid) > length(LevelPrefix) ->
- classify_how_in_subtree(LevelPrefix, Me);
- _ ->
- above
- end.
-
-%%--------------------------------------------------
-%% See comment about in_subtree/2. This function takes care of all cases
-%% where the ME really should be in _this_ subtree (not above).
-%%--------------------------------------------------
-classify_how_in_subtree(LevelPrefix, Me)
- when (length(Me#me.oid) =:= (length(LevelPrefix) + 1)) ->
- Oid = Me#me.oid,
- case node_or_subtree(Me#me.entrytype) of
- subtree ->
- {subtree, lists:last(Oid), Oid};
- node ->
- {node, lists:last(Oid)}
- end;
-
-classify_how_in_subtree(LevelPrefix, Me)
- when (length(Me#me.oid) > (length(LevelPrefix) + 1)) ->
- L1 = length(LevelPrefix) + 1,
- Oid = Me#me.oid,
- {internal_subtree, lists:nth(L1, Oid), lists:sublist(Oid, 1, L1)}.
-
-%%--------------------------------------------------
-%% Determines how to treat different kinds om MEs in the tree building process.
-%% Pre: all internal nodes have been removed.
-%%--------------------------------------------------
-node_or_subtree(table) -> subtree;
-node_or_subtree(table_entry) -> subtree;
-node_or_subtree(variable) -> node;
-node_or_subtree(table_column) -> node.
-
-%%--------------------------------------------------
-%% Purpose: (Recursively) Converts a temporary tree (see above) to a final tree.
-%% If input is a ListTree, output is a TupleTree.
-%% If input is a Node, output is the same Node.
-%% Pre: All Indexes are >= 0.
-%%--------------------------------------------------
-convert_tree({Index, {tree, Tree, Info}}) when Index >= 0 ->
- L = lists:map(fun convert_tree/1, Tree),
- {Index, {tree, dict_list_to_tuple(L), Info}};
-convert_tree({Index, {node, Info}}) when Index >= 0 ->
- {Index, {node, Info}};
-convert_tree(Tree) when is_list(Tree) ->
- L = lists:map(fun convert_tree/1, Tree),
- dict_list_to_tuple(L).
-
-%%----------------------------------------------------------------------
-%% Purpose: Converts a single level (that is non-recursively) from
-%% the temporary indexlist to the N-tuple.
-%% Input: A list of {Index, Data}.
-%% Output: A tuple where element Index is Data.
-%%----------------------------------------------------------------------
-dict_list_to_tuple(L) ->
- L2 = lists:keysort(1, L),
- list_to_tuple(integrate_indexes(0, L2)).
-
-%%----------------------------------------------------------------------
-%% Purpose: Helper function for dict_list_to_tuple/1.
-%% Converts an indexlist to a N-list.
-%% Input: A list of {Index, Data}.
-%% Output: A (usually longer, never shorter) list where element Index is Data.
-%% Example: [{1,hej}, {3, sven}] will give output
-%% [undefined_node, hej, undefined_node, sven].
-%% Initially CurIndex should be 0.
-%%----------------------------------------------------------------------
-integrate_indexes(CurIndex, [{CurIndex, Data} | T]) ->
- [Data | integrate_indexes(CurIndex + 1, T)];
-integrate_indexes(_Index, []) ->
- [];
-integrate_indexes(CurIndex, L) ->
- [undefined_node | integrate_indexes(CurIndex + 1, L)].
-
-%%%======================================================================
-%%% 4. Tree merging
-%%% Used by: load mib, insert subagent.
-%%%======================================================================
-
-%%----------------------------------------------------------------------
-%% Arg: Two root nodes (that is to be merged).
-%% Returns: A new root node where the nodes have been merger to one.
-%%----------------------------------------------------------------------
-merge_nodes(Same, Same) ->
- Same;
-merge_nodes(Node, undefined_node) ->
- Node;
-merge_nodes(undefined_node, Node) ->
- Node;
-merge_nodes({tree, Tree1, internal}, {tree, Tree2, internal}) ->
- {tree, merge_levels(tuple_to_list(Tree1),tuple_to_list(Tree2)), internal};
-merge_nodes(Node1, Node2) ->
- throw({error_merge_nodes, Node1, Node2}).
-
-%%----------------------------------------------------------------------
-%% Arg: Two levels to be merged.
-%% Here, a level is represented as a list of nodes. A list is easier
-%% to extend than a tuple.
-%% Returns: The resulting, merged level tuple.
-%%----------------------------------------------------------------------
-merge_levels(Level1, Level2) when length(Level1) =:= length(Level2) ->
- MergeNodes = fun(N1, N2) -> merge_nodes(N1, N2) end,
- list_to_tuple(snmp_misc:multi_map(MergeNodes, [Level1, Level2]));
-merge_levels(Level1, Level2) when length(Level1) > length(Level2) ->
- merge_levels(Level1, Level2 ++
- undefined_nodes_list(length(Level1) - length(Level2)));
-merge_levels(Level1, Level2) when length(Level1) < length(Level2) ->
- merge_levels(Level2, Level1).
-
-undefined_nodes_list(N) -> lists:duplicate(N, undefined_node).
-
-
-%%%======================================================================
-%%% 5. Tree deletion routines
-%%% (for unload mib)
-%%%======================================================================
-
-%%----------------------------------------------------------------------
-%% Purpose: Actually kicks of the tree reconstruction.
-%% Returns: {list of removed MEs, NewTree}
-%%----------------------------------------------------------------------
-delete_mib_from_tree(MibName, {tree, Tree, internal}) ->
- case delete_tree(Tree, MibName) of
- [] ->
- {tree, {undefined_node}, internal}; % reduce
- LevelList ->
- {tree, list_to_tuple(LevelList), internal}
- end.
-
-%%----------------------------------------------------------------------
-%% Purpose: Deletes all nodes associated to MibName from this level and
-%% all levels below.
-%% If the new level does not contain information (that is, no
-%% other mibs use it) anymore the empty list is returned.
-%% Returns: {MEs, The new level represented as a list}
-%%----------------------------------------------------------------------
-delete_tree(Tree, MibName) when is_tuple(Tree) ->
- NewLevel = delete_nodes(tuple_to_list(Tree), MibName, []),
- case lists:filter(fun drop_undefined_nodes/1,NewLevel) of
- [] -> [];
- _A_perhaps_shorted_list ->
- NewLevel % some other mib needs this level
- end.
-
-%%----------------------------------------------------------------------
-%% Purpose: Nodes belonging to MibName are removed from the tree.
-%% Recursively deletes sub trees to this node.
-%% Returns: {MEs, NewNodesList}
-%%----------------------------------------------------------------------
-delete_nodes([], _MibName, AccNodes) ->
- lists:reverse(AccNodes);
-
-delete_nodes([{node, {variable, MibName}}|T], MibName, AccNodes) ->
- delete_nodes(T, MibName, [undefined_node | AccNodes]);
-
-delete_nodes([{node, {table_column, MibName}}|T], MibName, AccNodes) ->
- delete_nodes(T, MibName, [undefined_node | AccNodes]);
-
-delete_nodes([{tree, _Tree, {table, MibName}}|T], MibName, AccNodes) ->
- delete_nodes(T, MibName, [undefined_node | AccNodes]);
-
-delete_nodes([{tree, _Tree, {table_entry, MibName}}|T], MibName, AccNodes) ->
- delete_nodes(T, MibName, [undefined_node | AccNodes]);
-
-delete_nodes([{tree, Tree, Info}|T], MibName, AccNodes) ->
- case delete_tree(Tree, MibName) of
- [] -> % tree completely deleted
- delete_nodes(T, MibName, [undefined_node | AccNodes]);
- LevelList ->
- delete_nodes(T, MibName,
- [{tree, list_to_tuple(LevelList), Info} | AccNodes])
- end;
-
-delete_nodes([NodeToKeep|T], MibName, AccNodes) ->
- delete_nodes(T, MibName, [NodeToKeep | AccNodes]).
-
-drop_undefined_nodes(undefined_node) -> false;
-drop_undefined_nodes(_) -> true.
-
-
-%%%======================================================================
-%%% 6. Functions for subagent handling
-%%%======================================================================
+ mib_view/0,
+ mib_view_elem/0,
+ mib_view_mask/0,
+ mib_view_inclusion/0
+ ]).
-%%----------------------------------------------------------------------
-%% Returns: A new Root|{error, reason}
-%%----------------------------------------------------------------------
-insert_subagent(Oid, OldRoot) ->
- ListTree = build_tree_for_subagent(Oid),
- case catch convert_tree(ListTree) of
- {'EXIT', _Reason} ->
- {error, 'cannot construct tree from oid'};
- Level when is_tuple(Level) ->
- T = {tree, Level, internal},
- case catch merge_nodes(T, OldRoot) of
- {error_merge_nodes, _Node1, _Node2} ->
- {error, oid_conflict};
- NewRoot when is_tuple(NewRoot) andalso
- (element(1, NewRoot) =:= tree) ->
- NewRoot
- end
- end.
+-type mib_storage() :: ets |
+ {ets, Dir :: mib_storage_dir()} |
+ {ets, Dir :: mib_storage_dir(), Action :: mib_storage_action()} |
+ dets |
+ {dets, Dir :: mib_storage_dir()} |
+ {dets, Dir :: mib_storage_dir(), Action :: mib_storage_action()} |
+ mnesia |
+ {mnesia, Nodes :: [node()]} |
+ {mnesia, Nodes :: [node()],
+ Action :: mib_storage_action()}.
-build_tree_for_subagent([Index]) ->
- [{Index, {node, subagent}}];
+-type mib_storage_dir() :: default | string().
+-type mib_storage_action() :: clear | keep.
-build_tree_for_subagent([Index | T]) ->
- [{Index, {tree, build_tree_for_subagent(T), internal}}].
+-type mib_view() :: [mib_view_elem()].
+-type mib_view_elem() :: {SubTree :: snmp:oid(),
+ Mask :: [non_neg_integer()],
+ Inclusion :: mib_view_inclusion()}.
+-type mib_view_mask() :: [non_neg_integer()].
+-type mib_view_inclusion() :: 1 | 2. % 1 = included, 2 = excluded
-%%----------------------------------------------------------------------
-%% Returns: A new tree where the subagent at Oid (2nd arg) has been deleted.
-%%----------------------------------------------------------------------
-delete_subagent({tree, Tree, Info}, [Index]) ->
- {node, subagent} = element(Index+1, Tree),
- {tree, setelement(Index+1, Tree, undefined_node), Info};
-delete_subagent({tree, Tree, Info}, [Index | TI]) ->
- {tree, setelement(Index+1, Tree,
- delete_subagent(element(Index+1, Tree), TI)), Info}.
+-type filename() :: file:filename().
-%%%======================================================================
-%%% 7. Misc functions
-%%%======================================================================
-%%----------------------------------------------------------------------
-%% Installs the mibs found in the database when starting the agent.
-%% Basically calls the instrumentation functions for all non-internal
-%% mib-entries
-%%----------------------------------------------------------------------
-install_mibs(MibDb, NodeDb) ->
- MibNames = loaded(MibDb),
- ?vtrace("install_mibs -> found following mibs in database: ~n"
- "~p", [MibNames]),
- install_mibs2(NodeDb, MibNames).
+-callback new(MibStorage :: mib_storage()) -> State :: term().
-install_mibs2(_, []) ->
- ok;
-install_mibs2(NodeDb, [MibName|MibNames]) ->
- Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
- Nodes = snmpa_general_db:match_object(NodeDb, Pattern),
- MEs = [ME || #node_info{me = ME} <- Nodes],
- ?vtrace("install_mibs2 -> installing ~p MEs for mib ~p",
- [length(MEs),MibName]),
- NewF = fun(ME) -> call_instrumentation(ME, new) end,
- lists:foreach(NewF, MEs),
- install_mibs2(NodeDb, MibNames).
-
-
-%%----------------------------------------------------------------------
-%% Does all side effect stuff during load_mib.
-%%----------------------------------------------------------------------
-install_mib(Db, Symbolic, Mib, MibName, FileName, NonInternalMes) ->
- ?vdebug("install_mib -> entry with"
- "~n Symbolic: ~p"
- "~n MibName: ~p"
- "~n FileName: ~p", [Symbolic, MibName, FileName]),
- Rec = #mib_info{name = MibName, symbolic = Symbolic, file_name = FileName},
- snmpa_general_db:write(Db, Rec),
- install_mib2(Symbolic, MibName, Mib),
- NewF = fun(ME) -> call_instrumentation(ME, new) end,
- lists:foreach(NewF, NonInternalMes).
+-callback close(State :: term()) -> ok.
-install_mib2(true, MibName, Mib) ->
- #mib{table_infos = TabInfos,
- variable_infos = VarInfos,
- mes = MEs,
- asn1_types = ASN1Types,
- traps = Traps} = Mib,
- snmpa_symbolic_store:add_table_infos(MibName, TabInfos),
- snmpa_symbolic_store:add_variable_infos(MibName, VarInfos),
- snmpa_symbolic_store:add_aliasnames(MibName, MEs),
- snmpa_symbolic_store:add_types(MibName, ASN1Types),
- SetF = fun(Trap) ->
- snmpa_symbolic_store:set_notification(Trap, MibName)
- end,
- lists:foreach(SetF, Traps);
-install_mib2(_, _, _) ->
- ok.
+-callback sync(State :: term()) -> ok.
-install_mes(_Db, _MibName, []) ->
- ok;
-install_mes(Db, MibName, [ME|MEs]) ->
- Node = #node_info{oid = ME#me.oid, mib_name = MibName, me = ME},
- snmpa_general_db:write(Db, Node),
- install_mes(Db, MibName, MEs).
+-callback load_mib(State :: term(), FileName :: string(),
+ MeOverride :: boolean(),
+ TeOverride :: boolean()) ->
+ {ok, NewState :: term()} | {error, Reason :: already_loaded | term()}.
+-callback unload_mib(State :: term(), FileName :: string(),
+ MeOverride :: boolean(),
+ TeOverride :: boolean()) ->
+ {ok, NewState :: term()} | {error, Reason :: not_loaded | term()}.
-%%----------------------------------------------------------------------
-%% Does all side effect stuff during unload_mib.
-%%----------------------------------------------------------------------
-uninstall_mib(Db, Symbolic, MibName, MEs) ->
- ?vtrace("uninstall_mib -> entry with"
- "~n Db: ~p"
- "~n Symbolic: ~p"
- "~n MibName: ~p", [Db, Symbolic, MibName]),
- Res = snmpa_general_db:delete(Db, MibName),
- ?vtrace("uninstall_mib -> (mib) db delete result: ~p", [Res]),
- uninstall_mib2(Symbolic, MibName),
- DelF = fun(ME) -> call_instrumentation(ME, delete) end,
- lists:foreach(DelF, MEs).
+-callback lookup(State :: term(), Oid :: snmp:oid()) ->
+ {false, Reason :: term()} |
+ {variable, MibEntry :: snmpa:me()} |
+ {table_column, MibEntry :: snmpa:me(), TableEntryOid :: snmp:oid()} |
+ {subagent, SubAgentPid :: pid(), SAOid :: snmp:oid()}.
-uninstall_mib2(true, MibName) ->
- snmpa_symbolic_store:delete_table_infos(MibName),
- snmpa_symbolic_store:delete_variable_infos(MibName),
- snmpa_symbolic_store:delete_aliasnames(MibName),
- snmpa_symbolic_store:delete_types(MibName),
- snmpa_symbolic_store:delete_notifications(MibName);
-uninstall_mib2(_, _) ->
- ok.
+-callback next(State :: term(), Oid :: snmp:oid(), MibView :: mib_view()) ->
+ endOfView | false |
+ {subagent, SubAgentPid :: pid(), SAOid :: snmp:oid()} |
+ {variable, MibEntry :: snmpa:me(), VarOid :: snmp:oid()} |
+ {table, TableOid :: snmp:oid(), TableRestOid :: snmp:oid(), MibEntry :: snmpa:me()}.
-uninstall_mes(Db, MibName) ->
- Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
- snmpa_general_db:match_delete(Db, Pattern).
+-callback register_subagent(State :: term(),
+ Oid :: snmp:oid(),
+ Pid :: pid()) ->
+ {ok, NewState :: term()} | {error, Reason :: term()}.
+-callback unregister_subagent(State :: term(),
+ PidOrOid :: pid() | snmp:oid()) ->
+ {ok, NewState :: term()} | % When second arg was a pid()
+ {ok, NewState :: term(), Pid :: pid()} | % When second arg was a oid()
+ {error, Reason :: term()}.
-%%----------------------------------------------------------------------
-%% Create a list of the names of all the loaded mibs
-%%----------------------------------------------------------------------
-loaded(Db) ->
- [N || #mib_info{name = N} <- snmpa_general_db:tab2list(Db)].
-
+-callback dump(State :: term(), Destination :: io | filename()) ->
+ ok | {error, Reason :: term()}.
-%%----------------------------------------------------------------------
-%% Calls MFA-instrumentation with 'new' or 'delete' operation.
-%%----------------------------------------------------------------------
-call_instrumentation(#me{entrytype = variable, mfa={M,F,A}}, Operation) ->
- ?vtrace("call instrumentation with"
- "~n entrytype: variable"
- "~n MFA: {~p,~p,~p}"
- "~n Operation: ~p",
- [M,F,A,Operation]),
- catch apply(M, F, [Operation | A]);
-call_instrumentation(#me{entrytype = table_entry, mfa={M,F,A}}, Operation) ->
- ?vtrace("call instrumentation with"
- "~n entrytype: table_entry"
- "~n MFA: {~p,~p,~p}"
- "~n Operation: ~p",
- [M,F,A,Operation]),
- catch apply(M, F, [Operation | A]);
-call_instrumentation(_ShitME, _Operation) ->
- done.
+-callback which_mib(State :: term(), Oid :: snmp:oid()) ->
+ {ok, Mib :: string()} | {error, Reason :: term()}.
+-callback which_mibs(State :: term()) ->
+ [{MibName :: atom(), Filename :: string()}].
-maybe_drop_me(#me{entrytype = internal}) -> false;
-maybe_drop_me(#me{entrytype = group}) -> false;
-maybe_drop_me(#me{imported = true}) -> false;
-maybe_drop_me(_) -> true.
+-callback whereis_mib(State :: term(), MibName :: atom()) ->
+ {ok, Filename :: string()} | {error, Reason :: term()}.
+-callback info(State :: term()) -> list().
-%%----------------------------------------------------------------------
-%% Code change functions
-%%----------------------------------------------------------------------
+-callback backup(State :: term(), BackupDir :: string()) ->
+ ok | {error, Reason :: term()}.
-code_change(down, State) ->
- ?d("code_change(down) -> entry",[]),
- State;
+-callback code_change(Direction :: up | down,
+ Vsn :: term(),
+ Extra :: term(),
+ State :: term()) ->
+ NewState :: term().
-code_change(up, State) ->
- ?d("code_change(up)",[]),
- State;
-code_change(_Vsn, State) ->
- State.
diff --git a/lib/snmp/src/agent/snmpa_mib_data_ttln.erl b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl
new file mode 100644
index 0000000000..d367e8f13f
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl
@@ -0,0 +1,1402 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013-2013. 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(snmpa_mib_data_ttln).
+
+%%%-----------------------------------------------------------------
+%%%
+%%% THIS FILE IS JUST A PLACE HOLDER - IGNORE
+%%%
+%%%-----------------------------------------------------------------
+
+
+%%%-----------------------------------------------------------------
+%%%
+%%% TTLN - TupleTreeListNodes
+%%%
+%%% This module implements the MIB internal data structures.
+%%% An MIB Data Structure consists of three items; an ets-table,
+%%% a tree and a list of registered subagents.
+%%% The subagent information is consequently duplicated. It resides
+%%% both in the tree and in the list.
+%%% The ets-table contains all data associated with each variable,
+%%% table, tableentry and tablecolumn in the MIB.
+%%% The tree contains information of the Oids in the MIB.
+%%%
+%%% When a mib is loaded, the tree is built from the plain list
+%%% in the binary file.
+%%%
+%%%-----------------------------------------------------------------
+
+-include("snmp_types.hrl").
+-include("snmp_debug.hrl").
+
+-define(VMODULE,"MDATA_TTLN").
+-include("snmp_verbosity.hrl").
+
+-behaviour(snmpa_mib_data).
+
+-define(MIB_DATA, snmpa_mib_data).
+-define(MIB_NODE, snmpa_mib_node).
+-define(MIB_TREE, snmpa_mib_tree).
+-define(DUMMY_TREE_GENERATION, 1).
+-define(DEFAULT_TREE, {tree,{undefined_node},internal}).
+
+
+%%%-----------------------------------------------------------------
+%%% Table of contents
+%%% =================
+%%% 1. Interface
+%%% 2. Implementation of tree access
+%%% 3. Tree building functions
+%%% 4. Tree merging
+%%% 5. Tree deletion routines
+%%% 6. Functions for subagent handling
+%%% 7. Misc functions
+%%%-----------------------------------------------------------------
+
+
+%%----------------------------------------------------------------------
+%% data_db is an database containing loaded mibs as:
+%% {MibName = atom(), Symbolic = ?, FullFileName = string()}
+%% it is either ets or mnesia
+%% tree_db is a database containing _one_ record with the tree!
+%% (the reason for this is part to get replication and part out of convenience)
+%% ref_tree is the root node, without any subagent.
+%% tree is the root node (same as ref_tree but with the subagents added).
+%% subagents is a list of {SAPid, Oid}
+%%----------------------------------------------------------------------
+-record(mib_data, {mib_db, % table of #mib_info
+ node_db, % table of #node_info
+ tree_db, % table of #tree
+ tree, % The actual tree
+ subagents = []}).
+
+-record(mib_info, {name, symbolic, file_name}).
+-record(node_info, {oid, mib_name, me}).
+
+
+%% API
+-export([new/0, new/1, sync/1, close/1,
+ load_mib/4, unload_mib/4, which_mibs/1, whereis_mib/2,
+ info/1, info/2,
+ dump/1, dump/2,
+ backup/2,
+ lookup/2, next/3, which_mib/2,
+ register_subagent/3, unregister_subagent/2]).
+
+%% Internal exports
+-export([code_change/2]).
+
+
+%%-----------------------------------------------------------------
+%% A tree is represented as a N-tuple, where each element is a
+%% node. A node is:
+%% 1) {tree, Tree, Info} where Info can be {table, Id}, {table_entry, Id}
+%% or perhaps 'internal'
+%% 2) undefined_node (memory optimization (instead of {node, undefined}))
+%% 3) {node, Info} where Info can be {subagent, Pid}, {variable, Id},
+%% {table_column, Id}
+%% Id is {MibName, MibEntry}
+%% The over all root is represented as {tree, Tree, internal}.
+%%
+%% tree() = {tree, nodes(), tree_info()}
+%% nodes() = [tree() | node() | undefined_node]
+%% node() = {node, node_info()}
+%% tree_info() = {table, Id} | {table_entry, Id} | internal
+%% node_info() = {subagent, Pid} | {variable, Id} | {table_colum, Id}
+%%-----------------------------------------------------------------
+
+-type tree_generation() :: non_neg_integer().
+-type tree() :: #tree{}.
+-type tree_nodes() :: [tree_node()].
+-type tree_node() :: tree() |
+ tree_node_elem() |
+ tree_node_empty().
+-type tree_node_elem() :: {node, tree_node_info()}.
+-type tree_node_info() :: {subagent, Pid :: pid()} |
+ {variable, Id :: non_neg_integer()} |
+ {table_column, Id :: non_neg_integer()}.
+-type tree_node_empty() :: {undefined_node, N :: pos_integer()}.
+-type tree_info() :: {table, Id :: non_neg_integer()} |
+ {table_entry, Id :: non_neg_integer()} |
+ internal.
+
+
+%% This record is what is stored in the database. The 'tree' part
+%% is described above...
+-record(mtree,
+ {
+ generation = ?DUMMY_TREE_GENERATION :: tree_generation(),
+ root = ?DEFAULT_TREE :: tree()
+ }).
+
+-record(tree,
+ {
+ %% The number of nodes is *not* actually the length of the
+ %% nodes list. Since the undefined-node(s) can be collapsed
+ %% into {undefined_node, N} we need to keep track of the
+ %% actual size some other way (so that we dont have the
+ %% traverse the nodes every time we want to check an index).
+ num_nodes :: non_neg_integer(),
+ nodes :: tree_nodes(),
+ tree_info :: tree_info()
+ }).
+
+
+
+
+%%%======================================================================
+%%% 1. Interface
+%%%======================================================================
+
+%%-----------------------------------------------------------------
+%% Func: new/0, new/1
+%% Returns: A representation of mib data.
+%%-----------------------------------------------------------------
+new() ->
+ new(ets).
+
+%% Where -> A list of nodes where the tables will be created
+new(Storage) ->
+ %% First we must check if there is already something to read
+ %% If a database already exists, then the tree structure has to be read
+ ?vtrace("open (mib) database",[]),
+ MibDb = snmpa_general_db:open(Storage, ?MIB_DATA,
+ mib_info,
+ record_info(fields, mib_info), set),
+ ?vtrace("open (mib) node database",[]),
+ NodeDb = snmpa_general_db:open(Storage, ?MIB_NODE,
+ node_info,
+ record_info(fields, node_info), set),
+ ?vtrace("open (mib) tree database",[]),
+ TreeDb = snmpa_general_db:open(Storage, ?MIB_TREE,
+ tree,
+ record_info(fields, mtree), set),
+ MTree =
+ case snmpa_general_db:read(TreeDb, ?DUMMY_TREE_GENERATION) of
+ false ->
+ T = #mtree{},
+ snmpa_general_db:write(TreeDb, T),
+ T;
+ {value, T} ->
+ T
+ end,
+ install_mibs(MibDb, NodeDb),
+ #mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ tree_db = TreeDb,
+ mtree = MTree}.
+
+
+%%----------------------------------------------------------------------
+%% Returns: new mib data | {error, Reason}
+%%----------------------------------------------------------------------
+load_mib(MibData,FileName,MeOverride,TeOverride)
+ when is_record(MibData,mib_data) andalso is_list(FileName) ->
+ ?vlog("load mib file: ~p",[FileName]),
+ ActualFileName = filename:rootname(FileName, ".bin") ++ ".bin",
+ MibName = list_to_atom(filename:basename(FileName, ".bin")),
+ (catch do_load_mib(MibData, ActualFileName, MibName,
+ MeOverride, TeOverride)).
+
+do_load_mib(MibData, ActualFileName, MibName, MeOverride, TeOverride) ->
+ ?vtrace("do_load_mib -> entry with"
+ "~n ActualFileName: ~s"
+ "~n MibName: ~p",[ActualFileName, MibName]),
+ #mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ %% tree_db = TreeDb,
+ tree = Tree} = MibData,
+ verify_not_loaded(MibDb, MibName),
+ ?vtrace("do_load_mib -> already loaded mibs:"
+ "~n ~p",[loaded(MibDb)]),
+ Mib = do_read_mib(ActualFileName),
+ ?vtrace("do_load_mib -> read mib ~s",[Mib#mib.name]),
+ NonInternalMes =
+ lists:filter(fun(ME) -> maybe_drop_me(ME) end, Mib#mib.mes),
+ OldRoot = Tree#tree.root,
+ T = build_tree(NonInternalMes, MibName),
+ ?d("load_mib -> "
+ "~n OldRoot: ~p"
+ "~n T: ~p", [OldRoot, T]),
+ case (catch merge_nodes(T, OldRoot)) of
+ {error_merge_nodes, Node1, Node2} ->
+ ?vlog("error merging nodes:"
+ "~n~p~nand~n~p", [Node1,Node2]),
+ {error, oid_conflict};
+ NewRoot when is_tuple(NewRoot) andalso (element(1,NewRoot) =:= tree) ->
+ ?d("load_mib -> "
+ "~n NewRoot: ~p", [NewRoot]),
+ Symbolic = not lists:member(no_symbolic_info, Mib#mib.misc),
+ case (catch check_notif_and_mes(TeOverride, MeOverride, Symbolic,
+ Mib#mib.traps, NonInternalMes)) of
+ true ->
+ install_mes(NodeDb, MibName, NonInternalMes),
+ install_mib(MibDb, Symbolic, Mib,
+ MibName, ActualFileName, NonInternalMes),
+ ?vtrace("installed mib ~s", [Mib#mib.name]),
+ Tree2 = Tree#tree{root = NewRoot},
+ %% snmpa_general_db:write(TreeDb, Tree2), %% Store later?
+ {ok, MibData#mib_data{tree = Tree2}};
+ Else ->
+ Else
+ end
+ end.
+
+
+verify_not_loaded(Db, Name) ->
+ case snmpa_general_db:read(Db, Name) of
+ {value, #mib_info{name = Name}} ->
+ throw({error, 'already loaded'});
+ false ->
+ ok
+ end.
+
+do_read_mib(ActualFileName) ->
+ case snmp_misc:read_mib(ActualFileName) of
+ {error, Reason} ->
+ ?vlog("Failed reading mib file ~p with reason: ~p",
+ [ActualFileName,Reason]),
+ throw({error, Reason});
+ {ok, Mib} ->
+ Mib
+ end.
+
+%% The Tree DB is handled in a special way since it can be very large.
+sync(#mib_data{mib_db = M,
+ node_db = N,
+ tree_db = T, tree = Tree, subagents = []}) ->
+ snmpa_general_db:sync(M),
+ snmpa_general_db:sync(N),
+ snmpa_general_db:write(T, Tree),
+ snmpa_general_db:sync(T);
+sync(#mib_data{mib_db = M,
+ node_db = N,
+ tree_db = T, tree = Tree, subagents = SAs}) ->
+
+ snmpa_general_db:sync(M),
+ snmpa_general_db:sync(N),
+
+ %% Ouch. Since the subagent info is dynamic we do not
+ %% want to store the tree containing subagent info. So, we
+ %% have to create a tmp tree without those and store it.
+
+ case delete_subagents(Tree, SAs) of
+ {ok, TreeWithoutSAs} ->
+ snmpa_general_db:write(T, TreeWithoutSAs),
+ snmpa_general_db:sync(T);
+ Error ->
+ Error
+ end.
+
+delete_subagents(Tree, []) ->
+ {ok, Tree};
+delete_subagents(Tree0, [{_, Oid}|SAs]) ->
+ case (catch delete_subagent(Tree0, Oid)) of
+ {tree, _Tree, _Info} = Tree1 ->
+ delete_subagents(Tree1, SAs);
+ _Error ->
+ {error, {'invalid oid', Oid}}
+ end.
+
+%%----------------------------------------------------------------------
+%% (OTP-3601)
+%%----------------------------------------------------------------------
+check_notif_and_mes(TeOverride,MeOverride,Symbolic,Traps,MEs) ->
+ ?vtrace("check notifications and mib entries",[]),
+ check_notifications(TeOverride,Symbolic,Traps),
+ check_mes(MeOverride,MEs).
+
+check_notifications(true, _Symbolic, _Traps) ->
+ ?vtrace("trapentry override = true => skip check",[]),
+ true;
+check_notifications(_, Symbolic, Traps) ->
+ check_notifications(Symbolic, Traps).
+
+check_notifications(true, Traps) ->
+ check_notifications(Traps);
+check_notifications(_, _) -> true.
+
+check_notifications([]) -> true;
+check_notifications([#trap{trapname = Key} = Trap | Traps]) ->
+ ?vtrace("check notification [trap] with Key: ~p",[Key]),
+ case snmpa_symbolic_store:get_notification(Key) of
+ {value, Trap} -> check_notifications(Traps);
+ {value, _} -> throw({error, {'trap already defined', Key}});
+ undefined -> check_notifications(Traps)
+ end;
+check_notifications([#notification{trapname = Key} = Notif | Traps]) ->
+ ?vtrace("check notification [notification] with Key: ~p",[Key]),
+ case snmpa_symbolic_store:get_notification(Key) of
+ {value, Notif} ->
+ check_notifications(Traps);
+ {value, _} ->
+ throw({error, {'notification already defined', Key}});
+ undefined ->
+ check_notifications(Traps)
+ end;
+check_notifications([Crap | Traps]) ->
+ ?vlog("skipped check of: ~n~p",[Crap]),
+ check_notifications(Traps).
+
+check_mes(true,_) ->
+ ?vtrace("mibentry override = true => skip check",[]),
+ true;
+check_mes(_,MEs) ->
+ check_mes(MEs).
+
+check_mes([]) -> true;
+check_mes([#me{aliasname = Name, oid = Oid1} | MEs]) ->
+ ?vtrace("check mib entries with aliasname: ~p",[Name]),
+ case snmpa_symbolic_store:aliasname_to_oid(Name) of
+ {value, Oid1} ->
+ check_mes(MEs);
+ {value, Oid2} ->
+ ?vinfo("~n expecting '~p'~n but found '~p'",[Oid1, Oid2]),
+ throw({error, {'mibentry already defined', Name}});
+ false ->
+ check_mes(MEs)
+ end;
+check_mes([Crap | MEs]) ->
+ ?vlog("skipped check of: ~n~p",[Crap]),
+ check_mes(MEs).
+
+
+
+%%----------------------------------------------------------------------
+%% Returns: new mib data | {error, Reason}
+%%----------------------------------------------------------------------
+unload_mib(MibData, FileName, _, _) when is_list(FileName) ->
+ MibName = list_to_atom(filename:basename(FileName, ".bin")),
+ (catch do_unload_mib(MibData, MibName)).
+
+do_unload_mib(MibData, MibName) ->
+ ?vtrace("do_unload_mib -> entry with"
+ "~n MibName: ~p", [MibName]),
+ #mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ %% tree_db = TreeDb,
+ tree = Tree} = MibData,
+ #mib_info{symbolic = Symbolic} = verify_loaded(MibDb, MibName),
+ NewRoot = delete_mib_from_tree(MibName, Tree#tree.root),
+ MEs = uninstall_mes(NodeDb, MibName),
+ uninstall_mib(MibDb, Symbolic, MibName, MEs),
+ NewMibData = MibData#mib_data{tree = Tree#tree{root = NewRoot}},
+ {ok, NewMibData}.
+
+verify_loaded(Db, Name) ->
+ case snmpa_general_db:read(Db, Name) of
+ {value, MibInfo} ->
+ MibInfo;
+ false ->
+ throw({error, 'not loaded'})
+ end.
+
+
+close(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb}) ->
+ snmpa_general_db:close(MibDb),
+ snmpa_general_db:close(NodeDb),
+ snmpa_general_db:close(TreeDb),
+ ok.
+
+register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) ->
+ case insert_subagent(Oid, T#tree.root) of
+ {error, Reason} ->
+ {error, Reason};
+ NewRootTree ->
+ SAs = [{Pid, Oid} | MibData#mib_data.subagents],
+ T2 = T#tree{root = NewRootTree},
+ MibData#mib_data{tree = T2, subagents = SAs}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Get a list of all loaded mibs
+%% Returns: [{Name, File}]
+%%----------------------------------------------------------------------
+
+which_mibs(#mib_data{mib_db = Db}) ->
+ Mibs = snmpa_general_db:tab2list(Db),
+ [{Name, File} || #mib_info{name = Name, file_name = File} <- Mibs].
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Get a list of all loaded mibs
+%% Returns: [{Name, File}]
+%%----------------------------------------------------------------------
+
+whereis_mib(#mib_data{mib_db = Db}, Name) ->
+ case snmpa_general_db:read(Db, Name) of
+ {value, #mib_info{file_name = File}} ->
+ {ok, File};
+ false ->
+ {error, not_found}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Deletes SA with Pid from all subtrees it handles.
+%% Returns: NewMibData.
+%%----------------------------------------------------------------------
+unregister_subagent(MibData, Pid) when is_pid(Pid) ->
+ SAs = MibData#mib_data.subagents,
+ case lists:keysearch(Pid, 1, SAs) of
+ false -> MibData;
+ {value, {Pid, Oid}} ->
+ % we should never get an error since Oid is found in MibData.
+ {ok, NewMibData, _DeletedSA} = unregister_subagent(MibData, Oid),
+ % continue if the same Pid handles other mib subtrees.
+ unregister_subagent(NewMibData, Pid)
+ end;
+
+%%----------------------------------------------------------------------
+%% Purpose: Deletes one unique subagent.
+%% Returns: {error, Reason} | {ok, NewMibData, DeletedSubagentPid}
+%%----------------------------------------------------------------------
+unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) ->
+ case catch delete_subagent(T#tree.root, Oid) of
+ {tree, Tree, Info} ->
+ OldSAs = MibData#mib_data.subagents,
+ {value, {Pid, _Oid}} = lists:keysearch(Oid, 2, OldSAs),
+ SAs = lists:keydelete(Oid, 2, OldSAs),
+ T2 = T#tree{root = {tree, Tree, Info}},
+ {ok,
+ MibData#mib_data{tree = T2, subagents = SAs},
+ Pid};
+ _ ->
+ {error, {'invalid oid', Oid}}
+ end.
+
+%%----------------------------------------------------------------------
+%% Purpose: To inpect memory usage, loaded mibs, registered subagents
+%%----------------------------------------------------------------------
+info(MibData) ->
+ ?vtrace("retrieve info",[]),
+ #mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb,
+ tree = Tree, subagents = SAs} = MibData,
+ LoadedMibs = old_format(snmpa_general_db:tab2list(MibDb)),
+ TreeSize = snmp_misc:mem_size(Tree),
+ {memory, ProcSize} = erlang:process_info(self(),memory),
+ MibDbSize = snmpa_general_db:info(MibDb, memory),
+ NodeDbSize = snmpa_general_db:info(NodeDb, memory),
+ TreeDbSize = snmpa_general_db:info(TreeDb, memory),
+ [{loaded_mibs, LoadedMibs}, {subagents, SAs}, {tree_size_bytes, TreeSize},
+ {process_memory, ProcSize},
+ {db_memory, [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}]}].
+
+info(#mib_data{mib_db = MibDb}, loaded_mibs) ->
+ Mibs = snmpa_general_db:tab2list(MibDb),
+ [filename:rootname(FN, ".bin") || #mib_info{file_name = FN} <- Mibs];
+info(#mib_data{tree = Tree}, tree_size_bytes) ->
+ snmp_misc:mem_size(Tree);
+info(_, process_memory) ->
+ {memory, ProcSize} = erlang:process_info(self(),memory),
+ ProcSize;
+info(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb},
+ db_memory) ->
+ MibDbSize = snmpa_general_db:info(MibDb, memory),
+ NodeDbSize = snmpa_general_db:info(NodeDb, memory),
+ TreeDbSize = snmpa_general_db:info(TreeDb, memory),
+ [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}];
+info(#mib_data{subagents = SAs}, subagents) ->
+ SAs.
+
+old_format(LoadedMibs) ->
+ ?vtrace("convert mib info to old format",[]),
+ [{N,S,F} || #mib_info{name=N,symbolic=S,file_name=F} <- LoadedMibs].
+
+
+%%----------------------------------------------------------------------
+%% A total dump for debugging.
+%%----------------------------------------------------------------------
+dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}) ->
+ (catch io:format("MIB-tables:~n~p~n~n",
+ [snmpa_general_db:tab2list(MibDb)])),
+ (catch io:format("MIB-entries:~n~p~n~n",
+ [snmpa_general_db:tab2list(NodeDb)])),
+ (catch io:format("Tree:~n~p~n", [Tree])), % good luck reading it!
+ ok.
+
+dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, File) ->
+ case file:open(File,[write]) of
+ {ok, Fd} ->
+ io:format(Fd,"~s~n",
+ [snmp:date_and_time_to_string(snmp:date_and_time())]),
+ (catch io:format(Fd,"MIB-tables:~n~p~n~n",
+ [snmpa_general_db:tab2list(MibDb)])),
+ (catch io:format(Fd, "MIB-entries:~n~p~n~n",
+ [snmpa_general_db:tab2list(NodeDb)])),
+ io:format(Fd,"Tree:~n~p~n", [Tree]), % good luck reading it!
+ file:close(Fd),
+ ok;
+ {error,Reason} ->
+ ?vinfo("~n Failed opening file '~s' for reason ~p",
+ [File,Reason]),
+ {error,Reason}
+ end.
+
+
+backup(#mib_data{mib_db = M, node_db = N, tree_db = T}, BackupDir) ->
+ MRes = snmpa_general_db:backup(M, BackupDir),
+ NRes = snmpa_general_db:backup(N, BackupDir),
+ TRes = snmpa_general_db:backup(T, BackupDir),
+ handle_backup_res([{mib_db, MRes}, {node_db, NRes}, {tree_db, TRes}]).
+
+handle_backup_res(Res) ->
+ handle_backup_res(Res, []).
+
+handle_backup_res([], []) ->
+ ok;
+handle_backup_res([], Err) ->
+ {error, lists:reverse(Err)};
+handle_backup_res([{_, ok}|Res], Err) ->
+ handle_backup_res(Res, Err);
+handle_backup_res([{Tag, {error, Reason}}|Res], Err) ->
+ handle_backup_res(Res, [{Tag, Reason}|Err]);
+handle_backup_res([{Tag, Error}|Res], Err) ->
+ handle_backup_res(Res, [{Tag, Error}|Err]).
+
+
+%%%======================================================================
+%%% 2. Implementation of tree access
+%%% lookup and next.
+%%%======================================================================
+
+
+which_mib(#mib_data{tree = T} = D, Oid) ->
+ ?vtrace("which_mib -> entry with"
+ "~n Oid: ~p",[Oid]),
+ case (catch find_node(D, T#tree.root, Oid, [])) of
+ {variable, _ME, Mib} ->
+ ?vtrace("which_mib -> variable:"
+ "~n Mib: ~p", [Mib]),
+ {ok, Mib};
+ {table, _EntryME, _, Mib} ->
+ ?vtrace("which_mib -> table:"
+ "~n Mib: ~p", [Mib]),
+ {ok, Mib};
+ {subagent, SubAgentPid, _SANextOid} ->
+ ?vtrace("which_mib -> subagent:"
+ "~n SubAgentPid: ~p", [SubAgentPid]),
+ {error, {subagent, SubAgentPid}};
+ {false, ErrorCode} ->
+ ?vtrace("which_mib -> false:"
+ "~n ErrorCode: ~p",[ErrorCode]),
+ {error, ErrorCode};
+ false ->
+ ?vtrace("which_mib -> false",[]),
+ {error, noSuchObject};
+ {'EXIT', R} ->
+ ?vtrace("which_mib -> exit:"
+ "~n R: ~p",[R]),
+ {error, noSuchObject}
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Func: lookup/2
+%% Purpose: Finds the mib entry corresponding to the Oid. If it is a
+%% variable, the Oid must be <Oid for var>.0 and if it is
+%% a table, Oid must be <table>.<entry>.<col>.<any>
+%% Returns: {variable, MibEntry} |
+%% {table_column, MibEntry, TableEntryOid} |
+%% {subagent, SubAgentPid, SAOid} |
+%% {false, Reason}
+%%-----------------------------------------------------------------
+lookup(#mib_data{tree = T} = D, Oid) ->
+ ?vtrace("lookup -> entry with"
+ "~n Oid: ~p",[Oid]),
+ case (catch find_node(D, T#tree.root, Oid, [])) of
+ {variable, ME, _Mib} when is_record(ME, me) ->
+ ?vtrace("lookup -> variable:"
+ "~n ME: ~p",[ME]),
+ {variable, ME};
+ {table, EntryME, {ColME, TableEntryOid}, _Mib} ->
+ ?vtrace("lookup -> table:"
+ "~n EntryME: ~p"
+ "~n ColME: ~p"
+ "~n RevTableEntryOid: ~p",
+ [EntryME, ColME, TableEntryOid]),
+ MFA = EntryME#me.mfa,
+ RetME = ColME#me{mfa = MFA},
+ {table_column, RetME, TableEntryOid};
+ {subagent, SubAgentPid, SANextOid} ->
+ ?vtrace("lookup -> subagent:"
+ "~n SubAgentPid: ~p"
+ "~n SANextOid: ~p", [SubAgentPid, SANextOid]),
+ {subagent, SubAgentPid, SANextOid};
+ {false, ErrorCode} ->
+ ?vtrace("lookup -> false:"
+ "~n ErrorCode: ~p",[ErrorCode]),
+ {false, ErrorCode};
+ false ->
+ ?vtrace("lookup -> false",[]),
+ {false, noSuchObject};
+ {'EXIT', R} ->
+ ?vtrace("lookup -> exit:"
+ "~n R: ~p",[R]),
+ {false, noSuchObject}
+ end.
+
+
+find_node(D, {tree, Tree, {table, _}}, RestOfOid, RevOid) ->
+ ?vtrace("find_node(tree,table) -> entry with"
+ "~n RestOfOid: ~p"
+ "~n RevOid: ~p",[RestOfOid, RevOid]),
+ find_node(D, {tree, Tree, internal}, RestOfOid, RevOid);
+find_node(D, {tree, Tree, {table_entry, _}}, RestOfOid, RevOid) ->
+ ?vtrace("find_node(tree,table_entry) -> entry with"
+ "~n RestOfOid: ~p"
+ "~n RevOid: ~p",[RestOfOid, RevOid]),
+ #mib_data{node_db = Db} = D,
+ Oid = lists:reverse(RevOid),
+ case snmpa_general_db:read(Db, Oid) of
+ {value, #node_info{me = ME, mib_name = Mib}} ->
+ case find_node(D, {tree, Tree, internal}, RestOfOid, RevOid) of
+ {false, ErrorCode} -> {false, ErrorCode};
+ Val -> {table, ME, Val, Mib}
+ end;
+ false ->
+ ?vinfo("find_node -> could not find table_entry ME with"
+ "~n RevOid: ~p"
+ "~n when"
+ "~n RestOfOid: ~p",
+ [RevOid, RestOfOid]),
+ false
+ end;
+find_node(D, {tree, Tree, _Internal}, [Int | RestOfOid], RevOid) ->
+ ?vtrace("find_node(tree) -> entry with"
+ "~n Int: ~p"
+ "~n RestOfOid: ~p"
+ "~n RevOid: ~p",[Int, RestOfOid, RevOid]),
+ find_node(D, element(Int+1, Tree), RestOfOid, [Int | RevOid]);
+find_node(D, {node, {table_column, _}}, RestOfOid, [ColInt | RevOid]) ->
+ ?vtrace("find_node(tree,table_column) -> entry with"
+ "~n RestOfOid: ~p"
+ "~n ColInt: ~p"
+ "~n RevOid: ~p",[RestOfOid, ColInt, RevOid]),
+ #mib_data{node_db = Db} = D,
+ Oid = lists:reverse([ColInt | RevOid]),
+ case snmpa_general_db:read(Db, Oid) of
+ {value, #node_info{me = ME}} ->
+ {ME, lists:reverse(RevOid)};
+ false ->
+ X = snmpa_general_db:read(Db, lists:reverse([ColInt | RevOid])),
+ ?vinfo("find_node -> could not find table_column ME with"
+ "~n RevOid: ~p"
+ "~n trying [~p|~p]"
+ "~n X: ~p",
+ [RevOid, [ColInt | RevOid], X]),
+ false
+ end;
+find_node(D, {node, {variable, _MibName}}, [0], RevOid) ->
+ ?vtrace("find_node(tree,variable,[0]) -> entry with"
+ "~n RevOid: ~p",[RevOid]),
+ #mib_data{node_db = Db} = D,
+ Oid = lists:reverse(RevOid),
+ %% {value, #node_info{me = ME}} = snmpa_general_db:read(Db, Oid),
+ case snmpa_general_db:read(Db, Oid) of
+ {value, #node_info{me = ME, mib_name = Mib}} ->
+ {variable, ME, Mib};
+ false ->
+ ?vinfo("find_node -> could not find variable ME with"
+ "~n RevOid: ~p", [RevOid]),
+ false
+ end;
+find_node(_D, {node, {variable, _MibName}}, [], _RevOid) ->
+ ?vtrace("find_node(tree,variable,[]) -> entry",[]),
+ {false, noSuchObject};
+find_node(_D, {node, {variable, _MibName}}, _, _RevOid) ->
+ ?vtrace("find_node(tree,variable) -> entry",[]),
+ {false, noSuchInstance};
+find_node(D, {node, subagent}, _RestOfOid, SARevOid) ->
+ ?vtrace("find_node(tree,subagent) -> entry with"
+ "~n SARevOid: ~p",[SARevOid]),
+ #mib_data{subagents = SAs} = D,
+ SAOid = lists:reverse(SARevOid),
+ case lists:keysearch(SAOid, 2, SAs) of
+ {value, {SubAgentPid, SAOid}} ->
+ {subagent, SubAgentPid, SAOid};
+ false ->
+ ?vinfo("find_node -> could not find subagent with"
+ "~n SAOid: ~p"
+ "~n SAs: ~p", [SAOid, SAs]),
+ false
+ end;
+find_node(_D, Node, _RestOfOid, _RevOid) ->
+ ?vtrace("find_node -> failed:~n~p",[Node]),
+ {false, noSuchObject}.
+
+
+%%-----------------------------------------------------------------
+%% Func: next/3
+%% Purpose: Finds the lexicographically next oid.
+%% Returns: endOfMibView |
+%% {subagent, SubAgentPid, SAOid} |
+%% {variable, MibEntry, VarOid} |
+%% {table, TableOid, TableRestOid, MibEntry}
+%% If a variable is returnes, it is in the MibView.
+%% If a table or subagent is returned, it *may* be in the MibView.
+%%-----------------------------------------------------------------
+next(#mib_data{tree = T} = D, Oid, MibView) ->
+ case catch next_node(D, T#tree.root, Oid, [], MibView) of
+ false -> endOfMibView;
+ Else -> Else
+ end.
+
+%%-----------------------------------------------------------------
+%% This function is used as long as we have any Oid left. Take
+%% one integer at a time from the Oid, and traverse the tree
+%% accordingly. When the Oid is empty, call find_next.
+%% Returns: {subagent, SubAgentPid, SAOid} |
+%% false |
+%% {variable, MibEntry, VarOid} |
+%% {table, TableOid, TableRestOid, MibEntry}
+%%-----------------------------------------------------------------
+next_node(_D, undefined_node, _Oid, _RevOidSoFar, _MibView) ->
+ ?vtrace("next_node(undefined_node) -> entry", []),
+ false;
+
+next_node(_D, {tree, Tree, {table_entry, _Id}}, [Int | _Oid],
+ _RevOidSoFar, _MibView)
+ when Int+1 > size(Tree) ->
+ ?vtrace("next_node(tree,table_entry) -> entry when not found whith"
+ "~n Int: ~p"
+ "~n size(Tree): ~p", [Int, size(Tree)]),
+ false;
+next_node(D, {tree, Tree, {table_entry, _MibName}},
+ Oid, RevOidSoFar, MibView) ->
+ ?vtrace("next_node(tree,table_entry) -> entry when"
+ "~n size(Tree): ~p"
+ "~n Oid: ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p", [size(Tree), Oid, RevOidSoFar, MibView]),
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ ?vdebug("next_node(tree,table_entry) -> not in mib view",[]),
+ false;
+ _ ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, OidSoFar) of
+ false ->
+ ?vinfo("next_node -> could not find table_entry with"
+ "~n OidSoFar: ~p", [OidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ ?vtrace("next_node(tree,table_entry) -> found: ~n ~p",
+ [ME]),
+ {table, OidSoFar, Oid, ME}
+ end
+ end;
+
+next_node(D, {tree, Tree, _Info}, [Int | RestOfOid], RevOidSoFar, MibView)
+ when (Int < size(Tree)) andalso (Int >= 0) ->
+ ?vtrace("next_node(tree) -> entry when"
+ "~n size(Tree): ~p"
+ "~n Int: ~p"
+ "~n RestOfOid: ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [size(Tree), Int, RestOfOid, RevOidSoFar, MibView]),
+ case next_node(D, element(Int+1,Tree),
+ RestOfOid, [Int|RevOidSoFar], MibView) of
+ false ->
+ find_next(D, {tree, Tree, _Info}, Int+1, RevOidSoFar, MibView);
+ Else ->
+ Else
+ end;
+%% no solution
+next_node(D, {tree, Tree, _Info}, [], RevOidSoFar, MibView) ->
+ ?vtrace("next_node(tree,[]) -> entry when"
+ "~n size(Tree): ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [size(Tree), RevOidSoFar, MibView]),
+ find_next(D, {tree, Tree, _Info}, 0, RevOidSoFar, MibView);
+next_node(_D, {tree, Tree, _Info}, _RestOfOid, _RevOidSoFar, _MibView) ->
+ ?vtrace("next_node(tree) -> entry when"
+ "~n size(Tree): ~p", [size(Tree)]),
+ false;
+
+next_node(D, {node, subagent}, Oid, RevOidSoFar, MibView) ->
+ ?vtrace("next_node(node,subagent) -> entry when"
+ "~n Oid: ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [Oid, RevOidSoFar, MibView]),
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ false;
+ _ ->
+ #mib_data{subagents = SAs} = D,
+ case lists:keysearch(OidSoFar, 2, SAs) of
+ {value, {SubAgentPid, OidSoFar}} ->
+ {subagent, SubAgentPid, OidSoFar};
+ _ ->
+ ?vinfo("next_node -> could not find subagent with"
+ "~n OidSoFar: ~p"
+ "~n SAs: ~p", [OidSoFar, SAs]),
+ false
+ end
+ end;
+
+next_node(D, {node, {variable, _MibName}}, [], RevOidSoFar, MibView) ->
+ ?vtrace("next_node(node,variable,[]) -> entry when"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [RevOidSoFar, MibView]),
+ OidSoFar = lists:reverse([0 | RevOidSoFar]),
+ case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
+ true ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
+ false ->
+ ?vinfo("next_node -> could not find variable with"
+ "~n RevOidSoFar: ~p", [RevOidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ {variable, ME, OidSoFar}
+ end;
+ _ ->
+ false
+ end;
+
+next_node(_D, {node, {variable, _MibName}}, _Oid, _RevOidSoFar, _MibView) ->
+ ?vtrace("next_node(node,variable) -> entry", []),
+ false.
+
+%%-----------------------------------------------------------------
+%% This function is used to find the first leaf from where we
+%% are.
+%% Returns: {subagent, SubAgentPid, SAOid} |
+%% false |
+%% {variable, MibEntry, VarOid} |
+%% {table, TableOid, TableRestOid, MibEntry}
+%% PRE: This function must always be called with a {internal, Tree}
+%% node.
+%%-----------------------------------------------------------------
+find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView)
+ when Idx < size(Tree) ->
+ case find_next(D, element(Idx+1, Tree), 0, [Idx| RevOidSoFar], MibView) of
+ false ->
+ find_next(D, {tree, Tree, internal}, Idx+1, RevOidSoFar, MibView);
+ Other ->
+ Other
+ end;
+find_next(_D, {tree, _Tree, internal}, _Idx, _RevOidSoFar, _MibView) ->
+ false;
+find_next(_D, undefined_node, _Idx, _RevOidSoFar, _MibView) ->
+ false;
+find_next(D, {tree, Tree, {table, _MibName}}, Idx, RevOidSoFar, MibView) ->
+ find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView);
+find_next(D, {tree, _Tree, {table_entry, _MibName}}, _Index,
+ RevOidSoFar, MibView) ->
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ false;
+ _ ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, OidSoFar) of
+ false ->
+ ?vinfo("find_next -> could not find table_entry ME with"
+ "~n OidSoFar: ~p", [OidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ {table, OidSoFar, [], ME}
+ end
+ end;
+find_next(D, {node, {variable, _MibName}}, _Idx, RevOidSoFar, MibView) ->
+ OidSoFar = lists:reverse([0 | RevOidSoFar]),
+ case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
+ true ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
+ false ->
+ ?vinfo("find_next -> could not find variable with"
+ "~n RevOidSoFar: ~p", [RevOidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ {variable, ME, OidSoFar}
+ end;
+ _ ->
+ false
+ end;
+find_next(D, {node, subagent}, _Idx, RevOidSoFar, MibView) ->
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ false;
+ _ ->
+ #mib_data{subagents = SAs} = D,
+ case lists:keysearch(OidSoFar, 2, SAs) of
+ {value, {SubAgentPid, OidSoFar}} ->
+ {subagent, SubAgentPid, OidSoFar};
+ false ->
+ ?vinfo("find_node -> could not find subagent with"
+ "~n OidSoFar: ~p"
+ "~n SAs: ~p", [OidSoFar, SAs]),
+ false
+ end
+ end.
+
+%%%======================================================================
+%%% 3. Tree building functions
+%%% Used when loading mibs.
+%%%======================================================================
+
+build_tree(Mes, MibName) ->
+ ?d("build_tree -> "
+ "~n Mes: ~p", [Mes]),
+ {ListTree, []} = build_subtree([], Mes, MibName),
+ {tree, convert_tree(ListTree), internal}.
+
+%%----------------------------------------------------------------------
+%% Purpose: Builds the tree where all oids have prefix equal to LevelPrefix.
+%% Returns: {Tree, RestMes}
+%% RestMes are Mes that should not be in this subtree.
+%% The Tree is a temporary and simplified data structure that is easy to
+%% convert to the final tuple tree used by the MIB process.
+%% A Node is represented as in the final tree.
+%% The tree is not represented as a N-tuple, but as an Index-list.
+%% Example: Temporary: [{1, Node1}, {3, Node3}]
+%% Final: {Node1, undefined_node, Node3}
+%% Pre: Mes are sorted on oid.
+%%----------------------------------------------------------------------
+build_subtree(LevelPrefix, [Me | Mes], MibName) ->
+ ?vtrace("build subtree -> ~n"
+ " oid: ~p~n"
+ " LevelPrefix: ~p~n"
+ " MibName: ~p", [Me#me.oid, LevelPrefix, MibName]),
+ EType = Me#me.entrytype,
+ ?vtrace("build subtree -> EType = ~p",[EType]),
+ case in_subtree(LevelPrefix, Me) of
+ above ->
+ ?vtrace("build subtree -> above",[]),
+ {[], [Me|Mes]};
+ {node, Index} ->
+ ?vtrace("build subtree -> node at ~p",[Index]),
+ {Tree, RestMes} = build_subtree(LevelPrefix, Mes, MibName),
+ {[{Index, {node, {EType, MibName}}} | Tree], RestMes};
+ {subtree, Index, NewLevelPrefix} ->
+ ?vtrace("build subtree -> subtree at"
+ "~n ~w with ~w",
+ [Index, NewLevelPrefix]),
+ {BelowTree, RestMes} =
+ build_subtree(NewLevelPrefix, Mes, MibName),
+ {CurTree, RestMes2} =
+ build_subtree(LevelPrefix, RestMes, MibName),
+ {[{Index, {tree, BelowTree, {EType,MibName}}}| CurTree], RestMes2};
+ {internal_subtree, Index, NewLevelPrefix} ->
+ ?vtrace("build subtree -> internal_subtree at"
+ "~n ~w with ~w",
+ [Index,NewLevelPrefix]),
+ {BelowTree, RestMes} =
+ build_subtree(NewLevelPrefix, [Me | Mes], MibName),
+ {CurTree, RestMes2} =
+ build_subtree(LevelPrefix, RestMes, MibName),
+ {[{Index, {tree, BelowTree, internal}} | CurTree], RestMes2}
+ end;
+
+build_subtree(_LevelPrefix, [], _MibName) ->
+ ?vtrace("build subtree -> done", []),
+ {[], []}.
+
+%%--------------------------------------------------
+%% Purpose: Determine how/if/where Me should be inserted in subtree
+%% with LevelPrefix. This function does not build any tree, only
+%% determinses what should be done (by build subtree).
+%% Returns:
+%% above - Indicating that this ME should _not_ be in this subtree.
+%% {node, Index} - yes, construct a node with index Index on this level
+%% {internal_subtree, Index, NewLevelPrefix} - yes, there should be an
+%% internal subtree at this index.
+%% {subtree, Index, NewLevelPrefix} - yes, construct a subtree with
+%% NewLevelPrefix and insert this on current level in position Index.
+%%--------------------------------------------------
+in_subtree(LevelPrefix, Me) ->
+ case lists:prefix(LevelPrefix, Me#me.oid) of
+ true when length(Me#me.oid) > length(LevelPrefix) ->
+ classify_how_in_subtree(LevelPrefix, Me);
+ _ ->
+ above
+ end.
+
+%%--------------------------------------------------
+%% See comment about in_subtree/2. This function takes care of all cases
+%% where the ME really should be in _this_ subtree (not above).
+%%--------------------------------------------------
+classify_how_in_subtree(LevelPrefix, Me)
+ when (length(Me#me.oid) =:= (length(LevelPrefix) + 1)) ->
+ Oid = Me#me.oid,
+ case node_or_subtree(Me#me.entrytype) of
+ subtree ->
+ {subtree, lists:last(Oid), Oid};
+ node ->
+ {node, lists:last(Oid)}
+ end;
+
+classify_how_in_subtree(LevelPrefix, Me)
+ when (length(Me#me.oid) > (length(LevelPrefix) + 1)) ->
+ L1 = length(LevelPrefix) + 1,
+ Oid = Me#me.oid,
+ {internal_subtree, lists:nth(L1, Oid), lists:sublist(Oid, 1, L1)}.
+
+%%--------------------------------------------------
+%% Determines how to treat different kinds om MEs in the tree building process.
+%% Pre: all internal nodes have been removed.
+%%--------------------------------------------------
+node_or_subtree(table) -> subtree;
+node_or_subtree(table_entry) -> subtree;
+node_or_subtree(variable) -> node;
+node_or_subtree(table_column) -> node.
+
+%%--------------------------------------------------
+%% Purpose: (Recursively) Converts a temporary tree (see above) to a final tree.
+%% If input is a ListTree, output is a TupleTree.
+%% If input is a Node, output is the same Node.
+%% Pre: All Indexes are >= 0.
+%%--------------------------------------------------
+convert_tree({Index, {tree, Tree, Info}}) when Index >= 0 ->
+ L = lists:map(fun convert_tree/1, Tree),
+ {Index, {tree, dict_list_to_tuple(L), Info}};
+convert_tree({Index, {node, Info}}) when Index >= 0 ->
+ {Index, {node, Info}};
+convert_tree(Tree) when is_list(Tree) ->
+ L = lists:map(fun convert_tree/1, Tree),
+ dict_list_to_tuple(L).
+
+%%----------------------------------------------------------------------
+%% Purpose: Converts a single level (that is non-recursively) from
+%% the temporary indexlist to the N-tuple.
+%% Input: A list of {Index, Data}.
+%% Output: A tuple where element Index is Data.
+%%----------------------------------------------------------------------
+dict_list_to_tuple(L) ->
+ L2 = lists:keysort(1, L),
+ list_to_tuple(integrate_indexes(0, L2)).
+
+%%----------------------------------------------------------------------
+%% Purpose: Helper function for dict_list_to_tuple/1.
+%% Converts an indexlist to a N-list.
+%% Input: A list of {Index, Data}.
+%% Output: A (usually longer, never shorter) list where element Index is Data.
+%% Example: [{1,hej}, {3, sven}] will give output
+%% [undefined_node, hej, undefined_node, sven].
+%% Initially CurIndex should be 0.
+%%----------------------------------------------------------------------
+integrate_indexes(CurIndex, [{CurIndex, Data} | T]) ->
+ [Data | integrate_indexes(CurIndex + 1, T)];
+integrate_indexes(_Index, []) ->
+ [];
+integrate_indexes(CurIndex, L) ->
+ [undefined_node | integrate_indexes(CurIndex + 1, L)].
+
+%%%======================================================================
+%%% 4. Tree merging
+%%% Used by: load mib, insert subagent.
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Arg: Two root nodes (that is to be merged).
+%% Returns: A new root node where the nodes have been merger to one.
+%%----------------------------------------------------------------------
+merge_nodes(Same, Same) ->
+ Same;
+merge_nodes(Node, undefined_node) ->
+ Node;
+merge_nodes(undefined_node, Node) ->
+ Node;
+merge_nodes({tree, Tree1, internal}, {tree, Tree2, internal}) ->
+ {tree, merge_levels(tuple_to_list(Tree1),tuple_to_list(Tree2)), internal};
+merge_nodes(Node1, Node2) ->
+ throw({error_merge_nodes, Node1, Node2}).
+
+%%----------------------------------------------------------------------
+%% Arg: Two levels to be merged.
+%% Here, a level is represented as a list of nodes. A list is easier
+%% to extend than a tuple.
+%% Returns: The resulting, merged level tuple.
+%%----------------------------------------------------------------------
+merge_levels(Level1, Level2) when length(Level1) =:= length(Level2) ->
+ MergeNodes = fun(N1, N2) -> merge_nodes(N1, N2) end,
+ list_to_tuple(snmp_misc:multi_map(MergeNodes, [Level1, Level2]));
+merge_levels(Level1, Level2) when length(Level1) > length(Level2) ->
+ merge_levels(Level1, Level2 ++
+ undefined_nodes_list(length(Level1) - length(Level2)));
+merge_levels(Level1, Level2) when length(Level1) < length(Level2) ->
+ merge_levels(Level2, Level1).
+
+undefined_nodes_list(N) -> lists:duplicate(N, undefined_node).
+
+
+%%%======================================================================
+%%% 5. Tree deletion routines
+%%% (for unload mib)
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Purpose: Actually kicks of the tree reconstruction.
+%% Returns: {list of removed MEs, NewTree}
+%%----------------------------------------------------------------------
+delete_mib_from_tree(MibName, {tree, Tree, internal}) ->
+ case delete_tree(Tree, MibName) of
+ [] ->
+ {tree, {undefined_node}, internal}; % reduce
+ LevelList ->
+ {tree, list_to_tuple(LevelList), internal}
+ end.
+
+%%----------------------------------------------------------------------
+%% Purpose: Deletes all nodes associated to MibName from this level and
+%% all levels below.
+%% If the new level does not contain information (that is, no
+%% other mibs use it) anymore the empty list is returned.
+%% Returns: {MEs, The new level represented as a list}
+%%----------------------------------------------------------------------
+delete_tree(Tree, MibName) when is_tuple(Tree) ->
+ NewLevel = delete_nodes(tuple_to_list(Tree), MibName, []),
+ case lists:filter(fun drop_undefined_nodes/1,NewLevel) of
+ [] -> [];
+ _A_perhaps_shorted_list ->
+ NewLevel % some other mib needs this level
+ end.
+
+%%----------------------------------------------------------------------
+%% Purpose: Nodes belonging to MibName are removed from the tree.
+%% Recursively deletes sub trees to this node.
+%% Returns: {MEs, NewNodesList}
+%%----------------------------------------------------------------------
+delete_nodes([], _MibName, AccNodes) ->
+ lists:reverse(AccNodes);
+
+delete_nodes([{node, {variable, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{node, {table_column, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{tree, _Tree, {table, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{tree, _Tree, {table_entry, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{tree, Tree, Info}|T], MibName, AccNodes) ->
+ case delete_tree(Tree, MibName) of
+ [] -> % tree completely deleted
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+ LevelList ->
+ delete_nodes(T, MibName,
+ [{tree, list_to_tuple(LevelList), Info} | AccNodes])
+ end;
+
+delete_nodes([NodeToKeep|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [NodeToKeep | AccNodes]).
+
+drop_undefined_nodes(undefined_node) -> false;
+drop_undefined_nodes(_) -> true.
+
+
+%%%======================================================================
+%%% 6. Functions for subagent handling
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Returns: A new Root|{error, reason}
+%%----------------------------------------------------------------------
+insert_subagent(Oid, OldRoot) ->
+ ListTree = build_tree_for_subagent(Oid),
+ case catch convert_tree(ListTree) of
+ {'EXIT', _Reason} ->
+ {error, 'cannot construct tree from oid'};
+ Level when is_tuple(Level) ->
+ T = {tree, Level, internal},
+ case catch merge_nodes(T, OldRoot) of
+ {error_merge_nodes, _Node1, _Node2} ->
+ {error, oid_conflict};
+ NewRoot when is_tuple(NewRoot) andalso
+ (element(1, NewRoot) =:= tree) ->
+ NewRoot
+ end
+ end.
+
+build_tree_for_subagent([Index]) ->
+ [{Index, {node, subagent}}];
+
+build_tree_for_subagent([Index | T]) ->
+ [{Index, {tree, build_tree_for_subagent(T), internal}}].
+
+%%----------------------------------------------------------------------
+%% Returns: A new tree where the subagent at Oid (2nd arg) has been deleted.
+%%----------------------------------------------------------------------
+delete_subagent({tree, Tree, Info}, [Index]) ->
+ {node, subagent} = element(Index+1, Tree),
+ {tree, setelement(Index+1, Tree, undefined_node), Info};
+delete_subagent({tree, Tree, Info}, [Index | TI]) ->
+ {tree, setelement(Index+1, Tree,
+ delete_subagent(element(Index+1, Tree), TI)), Info}.
+
+%%%======================================================================
+%%% 7. Misc functions
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Installs the mibs found in the database when starting the agent.
+%% Basically calls the instrumentation functions for all non-internal
+%% mib-entries
+%%----------------------------------------------------------------------
+install_mibs(MibDb, NodeDb) ->
+ MibNames = loaded(MibDb),
+ ?vtrace("install_mibs -> found following mibs in database: ~n"
+ "~p", [MibNames]),
+ install_mibs2(NodeDb, MibNames).
+
+install_mibs2(_, []) ->
+ ok;
+install_mibs2(NodeDb, [MibName|MibNames]) ->
+ Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
+ Nodes = snmpa_general_db:match_object(NodeDb, Pattern),
+ MEs = [ME || #node_info{me = ME} <- Nodes],
+ ?vtrace("install_mibs2 -> installing ~p MEs for mib ~p",
+ [length(MEs),MibName]),
+ NewF = fun(ME) -> call_instrumentation(ME, new) end,
+ lists:foreach(NewF, MEs),
+ install_mibs2(NodeDb, MibNames).
+
+
+%%----------------------------------------------------------------------
+%% Does all side effect stuff during load_mib.
+%%----------------------------------------------------------------------
+install_mib(Db, Symbolic, Mib, MibName, FileName, NonInternalMes) ->
+ ?vdebug("install_mib -> entry with"
+ "~n Symbolic: ~p"
+ "~n MibName: ~p"
+ "~n FileName: ~p", [Symbolic, MibName, FileName]),
+ Rec = #mib_info{name = MibName, symbolic = Symbolic, file_name = FileName},
+ snmpa_general_db:write(Db, Rec),
+ install_mib2(Symbolic, MibName, Mib),
+ NewF = fun(ME) -> call_instrumentation(ME, new) end,
+ lists:foreach(NewF, NonInternalMes).
+
+install_mib2(true, MibName, Mib) ->
+ #mib{table_infos = TabInfos,
+ variable_infos = VarInfos,
+ mes = MEs,
+ asn1_types = ASN1Types,
+ traps = Traps} = Mib,
+ snmpa_symbolic_store:add_table_infos(MibName, TabInfos),
+ snmpa_symbolic_store:add_variable_infos(MibName, VarInfos),
+ snmpa_symbolic_store:add_aliasnames(MibName, MEs),
+ snmpa_symbolic_store:add_types(MibName, ASN1Types),
+ SetF = fun(Trap) ->
+ snmpa_symbolic_store:set_notification(Trap, MibName)
+ end,
+ lists:foreach(SetF, Traps);
+install_mib2(_, _, _) ->
+ ok.
+
+install_mes(_Db, _MibName, []) ->
+ ok;
+install_mes(Db, MibName, [ME|MEs]) ->
+ Node = #node_info{oid = ME#me.oid, mib_name = MibName, me = ME},
+ snmpa_general_db:write(Db, Node),
+ install_mes(Db, MibName, MEs).
+
+
+%%----------------------------------------------------------------------
+%% Does all side effect stuff during unload_mib.
+%%----------------------------------------------------------------------
+uninstall_mib(Db, Symbolic, MibName, MEs) ->
+ ?vtrace("uninstall_mib -> entry with"
+ "~n Db: ~p"
+ "~n Symbolic: ~p"
+ "~n MibName: ~p", [Db, Symbolic, MibName]),
+ Res = snmpa_general_db:delete(Db, MibName),
+ ?vtrace("uninstall_mib -> (mib) db delete result: ~p", [Res]),
+ uninstall_mib2(Symbolic, MibName),
+ DelF = fun(ME) -> call_instrumentation(ME, delete) end,
+ lists:foreach(DelF, MEs).
+
+uninstall_mib2(true, MibName) ->
+ snmpa_symbolic_store:delete_table_infos(MibName),
+ snmpa_symbolic_store:delete_variable_infos(MibName),
+ snmpa_symbolic_store:delete_aliasnames(MibName),
+ snmpa_symbolic_store:delete_types(MibName),
+ snmpa_symbolic_store:delete_notifications(MibName);
+uninstall_mib2(_, _) ->
+ ok.
+
+uninstall_mes(Db, MibName) ->
+ Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
+ snmpa_general_db:match_delete(Db, Pattern).
+
+
+%%----------------------------------------------------------------------
+%% Create a list of the names of all the loaded mibs
+%%----------------------------------------------------------------------
+loaded(Db) ->
+ [N || #mib_info{name = N} <- snmpa_general_db:tab2list(Db)].
+
+
+%%----------------------------------------------------------------------
+%% Calls MFA-instrumentation with 'new' or 'delete' operation.
+%%----------------------------------------------------------------------
+call_instrumentation(#me{entrytype = variable, mfa={M,F,A}}, Operation) ->
+ ?vtrace("call instrumentation with"
+ "~n entrytype: variable"
+ "~n MFA: {~p,~p,~p}"
+ "~n Operation: ~p",
+ [M,F,A,Operation]),
+ catch apply(M, F, [Operation | A]);
+call_instrumentation(#me{entrytype = table_entry, mfa={M,F,A}}, Operation) ->
+ ?vtrace("call instrumentation with"
+ "~n entrytype: table_entry"
+ "~n MFA: {~p,~p,~p}"
+ "~n Operation: ~p",
+ [M,F,A,Operation]),
+ catch apply(M, F, [Operation | A]);
+call_instrumentation(_ShitME, _Operation) ->
+ done.
+
+
+maybe_drop_me(#me{entrytype = internal}) -> false;
+maybe_drop_me(#me{entrytype = group}) -> false;
+maybe_drop_me(#me{imported = true}) -> false;
+maybe_drop_me(_) -> true.
+
+
+%%----------------------------------------------------------------------
+%% Code change functions
+%%----------------------------------------------------------------------
+
+code_change(down, State) ->
+ ?d("code_change(down) -> entry",[]),
+ State;
+
+code_change(up, State) ->
+ ?d("code_change(up)",[]),
+ State;
+
+code_change(_Vsn, State) ->
+ State.
+
diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl
new file mode 100644
index 0000000000..c24cff43e5
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl
@@ -0,0 +1,1371 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2013. 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(snmpa_mib_data_tttn).
+
+%%%-----------------------------------------------------------------
+%%%
+%%% TTTN - TupleTreeTupleNodes
+%%%
+%%% This module implements the MIB internal data structures.
+%%% An MIB Data Structure consists of three items; an ets-table,
+%%% a tree and a list of registered subagents.
+%%% The subagent information is consequently duplicated. It resides
+%%% both in the tree and in the list.
+%%% The ets-table contains all data associated with each variable,
+%%% table, tableentry and tablecolumn in the MIB.
+%%% The tree contains information of the Oids in the MIB.
+%%%
+%%% When a mib is loaded, the tree is built from the plain list
+%%% in the binary file.
+%%%-----------------------------------------------------------------
+-include("snmp_types.hrl").
+-include("snmp_debug.hrl").
+
+-define(VMODULE,"MDATA_TTTN").
+-include("snmp_verbosity.hrl").
+
+-behaviour(snmpa_mib_data).
+
+-define(MIB_DATA, snmpa_mib_data).
+-define(MIB_NODE, snmpa_mib_node).
+-define(MIB_TREE, snmpa_mib_tree).
+-define(DUMMY_TREE_GENERATION, 1).
+-define(DEFAULT_TREE, {tree,{undefined_node},internal}).
+
+
+%%%-----------------------------------------------------------------
+%%% Table of contents
+%%% =================
+%%% 1. Interface
+%%% 2. Implementation of tree access
+%%% 3. Tree building functions
+%%% 4. Tree merging
+%%% 5. Tree deletion routines
+%%% 6. Functions for subagent handling
+%%% 7. Misc functions
+%%%-----------------------------------------------------------------
+
+
+%%----------------------------------------------------------------------
+%% data_db is an database containing loaded mibs as:
+%% {MibName = atom(), Symbolic = ?, FullFileName = string()}
+%% it is either ets or mnesia
+%% tree_db is a database containing _one_ record with the tree!
+%% (the reason for this is part to get replication and part out of convenience)
+%% ref_tree is the root node, without any subagent.
+%% tree is the root node (same as ref_tree but with the subagents added).
+%% subagents is a list of {SAPid, Oid}
+%%----------------------------------------------------------------------
+
+-record(mib_data, {mib_db, % table of #mib_info
+ node_db, % table of #node_info
+ tree_db, % table of #tree
+ tree, % The actual tree
+ subagents = []}).
+
+-record(mib_info, {name, symbolic, file_name}).
+-record(node_info, {oid, mib_name, me}).
+
+
+%% API
+-export([new/1,
+ close/1,
+ sync/1,
+ load_mib/4,
+ unload_mib/4,
+ lookup/2,
+ next/3,
+ register_subagent/3,
+ unregister_subagent/2,
+ dump/2,
+ which_mib/2, which_mibs/1,
+ whereis_mib/2,
+ info/1, info/2,
+ backup/2,
+ code_change/4]).
+
+
+%%-----------------------------------------------------------------
+%% A tree is represented as a N-tuple, where each element is a
+%% node. A node is:
+%% 1) {tree, Tree, Info} where Info can be {table, Id}, {table_entry, Id}
+%% or perhaps 'internal'
+%% 2) undefined_node (memory optimization (instead of {node, undefined}))
+%% 3) {node, Info} where Info can be {subagent, Pid}, {variable, Id},
+%% {table_column, Id}
+%% Id is {MibName, MibEntry}
+%% The over all root is represented as {tree, Tree, internal}.
+%%
+%% tree() = {tree, nodes(), tree_info()}
+%% nodes() = {tree() | node() | undefined_node, ...}
+%% node() = {node, node_info()}
+%% tree_info() = {table, Id} | {table_entry, Id} | internal
+%% node_info() = {subagent, Pid} | {variable, Id} | {table_colum, Id}
+%%-----------------------------------------------------------------
+
+%% This record is what is stored in the database. The 'tree' part
+%% is described above...
+-record(tree,{generation = ?DUMMY_TREE_GENERATION, root = ?DEFAULT_TREE}).
+
+
+%%%======================================================================
+%%% 1. Interface
+%%%======================================================================
+
+%%-----------------------------------------------------------------
+%% Func: new/0, new/1
+%% Returns: A representation of mib data.
+%%-----------------------------------------------------------------
+
+%% Where -> A list of nodes where the tables will be created
+new(Storage) ->
+ %% First we must check if there is already something to read
+ %% If a database already exists, then the tree structure has to be read
+ ?vtrace("open (mib) database",[]),
+ MibDb = snmpa_general_db:open(Storage, ?MIB_DATA,
+ mib_info,
+ record_info(fields,mib_info), set),
+ ?vtrace("open (mib) node database",[]),
+ NodeDb = snmpa_general_db:open(Storage, ?MIB_NODE,
+ node_info,
+ record_info(fields,node_info), set),
+ ?vtrace("open (mib) tree database",[]),
+ TreeDb = snmpa_general_db:open(Storage, ?MIB_TREE,
+ tree,
+ record_info(fields,tree), set),
+ Tree =
+ case snmpa_general_db:read(TreeDb, ?DUMMY_TREE_GENERATION) of
+ false ->
+ T = #tree{},
+ snmpa_general_db:write(TreeDb, T),
+ T;
+ {value, T} ->
+ T
+ end,
+ install_mibs(MibDb, NodeDb),
+ #mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ tree_db = TreeDb,
+ tree = Tree}.
+
+
+%%----------------------------------------------------------------------
+%% Returns: new mib data | {error, Reason}
+%%----------------------------------------------------------------------
+load_mib(MibData,FileName,MeOverride,TeOverride)
+ when is_record(MibData,mib_data) andalso is_list(FileName) ->
+ ?vlog("load mib file: ~p",[FileName]),
+ ActualFileName = filename:rootname(FileName, ".bin") ++ ".bin",
+ MibName = list_to_atom(filename:basename(FileName, ".bin")),
+ (catch do_load_mib(MibData, ActualFileName, MibName,
+ MeOverride, TeOverride)).
+
+do_load_mib(MibData, ActualFileName, MibName, MeOverride, TeOverride) ->
+ ?vtrace("do_load_mib -> entry with"
+ "~n ActualFileName: ~s"
+ "~n MibName: ~p",[ActualFileName, MibName]),
+ #mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ %% tree_db = TreeDb,
+ tree = Tree} = MibData,
+ verify_not_loaded(MibDb, MibName),
+ ?vtrace("do_load_mib -> already loaded mibs:"
+ "~n ~p",[loaded(MibDb)]),
+ Mib = do_read_mib(ActualFileName),
+ ?vtrace("do_load_mib -> read mib ~s",[Mib#mib.name]),
+ NonInternalMes =
+ lists:filter(fun(ME) -> maybe_drop_me(ME) end, Mib#mib.mes),
+ OldRoot = Tree#tree.root,
+ T = build_tree(NonInternalMes, MibName),
+ ?d("load_mib -> "
+ "~n OldRoot: ~p"
+ "~n T: ~p", [OldRoot, T]),
+ case (catch merge_nodes(T, OldRoot)) of
+ {error_merge_nodes, Node1, Node2} ->
+ ?vlog("error merging nodes:"
+ "~n~p~nand~n~p", [Node1,Node2]),
+ {error, oid_conflict};
+ NewRoot when is_tuple(NewRoot) andalso (element(1,NewRoot) =:= tree) ->
+ ?d("load_mib -> "
+ "~n NewRoot: ~p", [NewRoot]),
+ Symbolic = not lists:member(no_symbolic_info, Mib#mib.misc),
+ case (catch check_notif_and_mes(TeOverride, MeOverride, Symbolic,
+ Mib#mib.traps, NonInternalMes)) of
+ true ->
+ install_mes(NodeDb, MibName, NonInternalMes),
+ install_mib(MibDb, Symbolic, Mib,
+ MibName, ActualFileName, NonInternalMes),
+ ?vtrace("installed mib ~s", [Mib#mib.name]),
+ Tree2 = Tree#tree{root = NewRoot},
+ %% snmpa_general_db:write(TreeDb, Tree2), %% Store later?
+ {ok, MibData#mib_data{tree = Tree2}};
+ Else ->
+ Else
+ end
+ end.
+
+
+verify_not_loaded(Db, Name) ->
+ case snmpa_general_db:read(Db, Name) of
+ {value, #mib_info{name = Name}} ->
+ throw({error, 'already loaded'});
+ false ->
+ ok
+ end.
+
+do_read_mib(ActualFileName) ->
+ case snmp_misc:read_mib(ActualFileName) of
+ {error, Reason} ->
+ ?vlog("Failed reading mib file ~p with reason: ~p",
+ [ActualFileName,Reason]),
+ throw({error, Reason});
+ {ok, Mib} ->
+ Mib
+ end.
+
+%% The Tree DB is handled in a special way since it can be very large.
+sync(#mib_data{mib_db = M,
+ node_db = N,
+ tree_db = T, tree = Tree, subagents = []}) ->
+ snmpa_general_db:sync(M),
+ snmpa_general_db:sync(N),
+ snmpa_general_db:write(T, Tree),
+ snmpa_general_db:sync(T);
+sync(#mib_data{mib_db = M,
+ node_db = N,
+ tree_db = T, tree = Tree, subagents = SAs}) ->
+
+ snmpa_general_db:sync(M),
+ snmpa_general_db:sync(N),
+
+ %% Ouch. Since the subagent info is dynamic we do not
+ %% want to store the tree containing subagent info. So, we
+ %% have to create a tmp tree without those and store it.
+
+ case delete_subagents(Tree, SAs) of
+ {ok, TreeWithoutSAs} ->
+ snmpa_general_db:write(T, TreeWithoutSAs),
+ snmpa_general_db:sync(T);
+ Error ->
+ Error
+ end.
+
+delete_subagents(Tree, []) ->
+ {ok, Tree};
+delete_subagents(Tree0, [{_, Oid}|SAs]) ->
+ case (catch delete_subagent(Tree0, Oid)) of
+ {tree, _Tree, _Info} = Tree1 ->
+ delete_subagents(Tree1, SAs);
+ _Error ->
+ {error, {'invalid oid', Oid}}
+ end.
+
+%%----------------------------------------------------------------------
+%% (OTP-3601)
+%%----------------------------------------------------------------------
+check_notif_and_mes(TeOverride,MeOverride,Symbolic,Traps,MEs) ->
+ ?vtrace("check notifications and mib entries",[]),
+ check_notifications(TeOverride,Symbolic,Traps),
+ check_mes(MeOverride,MEs).
+
+check_notifications(true, _Symbolic, _Traps) ->
+ ?vtrace("trapentry override = true => skip check",[]),
+ true;
+check_notifications(_, Symbolic, Traps) ->
+ check_notifications(Symbolic, Traps).
+
+check_notifications(true, Traps) ->
+ check_notifications(Traps);
+check_notifications(_, _) -> true.
+
+check_notifications([]) -> true;
+check_notifications([#trap{trapname = Key} = Trap | Traps]) ->
+ ?vtrace("check notification [trap] with Key: ~p",[Key]),
+ case snmpa_symbolic_store:get_notification(Key) of
+ {value, Trap} -> check_notifications(Traps);
+ {value, _} -> throw({error, {'trap already defined', Key}});
+ undefined -> check_notifications(Traps)
+ end;
+check_notifications([#notification{trapname = Key} = Notif | Traps]) ->
+ ?vtrace("check notification [notification] with Key: ~p",[Key]),
+ case snmpa_symbolic_store:get_notification(Key) of
+ {value, Notif} ->
+ check_notifications(Traps);
+ {value, _} ->
+ throw({error, {'notification already defined', Key}});
+ undefined ->
+ check_notifications(Traps)
+ end;
+check_notifications([Crap | Traps]) ->
+ ?vlog("skipped check of: ~n~p",[Crap]),
+ check_notifications(Traps).
+
+check_mes(true,_) ->
+ ?vtrace("mibentry override = true => skip check",[]),
+ true;
+check_mes(_,MEs) ->
+ check_mes(MEs).
+
+check_mes([]) -> true;
+check_mes([#me{aliasname = Name, oid = Oid1} | MEs]) ->
+ ?vtrace("check mib entries with aliasname: ~p",[Name]),
+ case snmpa_symbolic_store:aliasname_to_oid(Name) of
+ {value, Oid1} ->
+ check_mes(MEs);
+ {value, Oid2} ->
+ ?vinfo("~n expecting '~p'~n but found '~p'",[Oid1, Oid2]),
+ throw({error, {'mibentry already defined', Name}});
+ false ->
+ check_mes(MEs)
+ end;
+check_mes([Crap | MEs]) ->
+ ?vlog("skipped check of: ~n~p",[Crap]),
+ check_mes(MEs).
+
+
+
+%%----------------------------------------------------------------------
+%% Returns: new mib data | {error, Reason}
+%%----------------------------------------------------------------------
+unload_mib(MibData, FileName, _, _) when is_list(FileName) ->
+ MibName = list_to_atom(filename:basename(FileName, ".bin")),
+ (catch do_unload_mib(MibData, MibName)).
+
+do_unload_mib(MibData, MibName) ->
+ ?vtrace("do_unload_mib -> entry with"
+ "~n MibName: ~p", [MibName]),
+ #mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ %% tree_db = TreeDb,
+ tree = Tree} = MibData,
+ #mib_info{symbolic = Symbolic} = verify_loaded(MibDb, MibName),
+ NewRoot = delete_mib_from_tree(MibName, Tree#tree.root),
+ MEs = uninstall_mes(NodeDb, MibName),
+ uninstall_mib(MibDb, Symbolic, MibName, MEs),
+ NewMibData = MibData#mib_data{tree = Tree#tree{root = NewRoot}},
+ {ok, NewMibData}.
+
+verify_loaded(Db, Name) ->
+ case snmpa_general_db:read(Db, Name) of
+ {value, MibInfo} ->
+ MibInfo;
+ false ->
+ throw({error, 'not loaded'})
+ end.
+
+
+close(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb}) ->
+ snmpa_general_db:close(MibDb),
+ snmpa_general_db:close(NodeDb),
+ snmpa_general_db:close(TreeDb),
+ ok.
+
+register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) ->
+ case insert_subagent(Oid, T#tree.root) of
+ {error, Reason} ->
+ {error, Reason};
+ NewRootTree ->
+ SAs = [{Pid, Oid} | MibData#mib_data.subagents],
+ T2 = T#tree{root = NewRootTree},
+ {ok, MibData#mib_data{tree = T2, subagents = SAs}}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Get a list of all loaded mibs
+%% Returns: [{Name, File}]
+%%----------------------------------------------------------------------
+
+which_mibs(#mib_data{mib_db = Db}) ->
+ Mibs = snmpa_general_db:tab2list(Db),
+ [{Name, File} || #mib_info{name = Name, file_name = File} <- Mibs].
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Get a list of all loaded mibs
+%% Returns: [{Name, File}]
+%%----------------------------------------------------------------------
+
+whereis_mib(#mib_data{mib_db = Db}, Name) ->
+ case snmpa_general_db:read(Db, Name) of
+ {value, #mib_info{file_name = File}} ->
+ {ok, File};
+ false ->
+ {error, not_found}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Deletes SA with Pid from all subtrees it handles.
+%% Returns: NewMibData.
+%%----------------------------------------------------------------------
+unregister_subagent(#mib_data{subagents = SAs} = MibData, Pid)
+ when is_pid(Pid) ->
+ SAs = MibData#mib_data.subagents,
+ case lists:keysearch(Pid, 1, SAs) of
+ false ->
+ {ok, MibData};
+ {value, {Pid, Oid}} ->
+ % we should never get an error since Oid is found in MibData.
+ {ok, NewMibData, _DeletedSA} = unregister_subagent(MibData, Oid),
+ % continue if the same Pid handles other mib subtrees.
+ unregister_subagent(NewMibData, Pid)
+ end;
+
+%%----------------------------------------------------------------------
+%% Purpose: Deletes one unique subagent.
+%% Returns: {error, Reason} | {ok, NewMibData, DeletedSubagentPid}
+%%----------------------------------------------------------------------
+unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) ->
+ case (catch delete_subagent(T#tree.root, Oid)) of
+ {tree, Tree, Info} ->
+ OldSAs = MibData#mib_data.subagents,
+ {value, {Pid, _Oid}} = lists:keysearch(Oid, 2, OldSAs),
+ SAs = lists:keydelete(Oid, 2, OldSAs),
+ T2 = T#tree{root = {tree, Tree, Info}},
+ {ok,
+ MibData#mib_data{tree = T2, subagents = SAs},
+ Pid};
+ _ ->
+ {error, {invalid_oid, Oid}}
+ end.
+
+%%----------------------------------------------------------------------
+%% Purpose: To inpect memory usage, loaded mibs, registered subagents
+%%----------------------------------------------------------------------
+info(MibData) ->
+ ?vtrace("retrieve info",[]),
+ #mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb,
+ tree = Tree, subagents = SAs} = MibData,
+ LoadedMibs = old_format(snmpa_general_db:tab2list(MibDb)),
+ TreeSize = snmp_misc:mem_size(Tree),
+ {memory, ProcSize} = erlang:process_info(self(),memory),
+ MibDbSize = snmpa_general_db:info(MibDb, memory),
+ NodeDbSize = snmpa_general_db:info(NodeDb, memory),
+ TreeDbSize = snmpa_general_db:info(TreeDb, memory),
+ [{loaded_mibs, LoadedMibs}, {subagents, SAs}, {tree_size_bytes, TreeSize},
+ {process_memory, ProcSize},
+ {db_memory, [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}]}].
+
+info(#mib_data{mib_db = MibDb}, loaded_mibs) ->
+ Mibs = snmpa_general_db:tab2list(MibDb),
+ [filename:rootname(FN, ".bin") || #mib_info{file_name = FN} <- Mibs];
+info(#mib_data{tree = Tree}, tree_size_bytes) ->
+ snmp_misc:mem_size(Tree);
+info(_, process_memory) ->
+ {memory, ProcSize} = erlang:process_info(self(),memory),
+ ProcSize;
+info(#mib_data{mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb},
+ db_memory) ->
+ MibDbSize = snmpa_general_db:info(MibDb, memory),
+ NodeDbSize = snmpa_general_db:info(NodeDb, memory),
+ TreeDbSize = snmpa_general_db:info(TreeDb, memory),
+ [{mib,MibDbSize},{node,NodeDbSize},{tree,TreeDbSize}];
+info(#mib_data{subagents = SAs}, subagents) ->
+ SAs.
+
+old_format(LoadedMibs) ->
+ ?vtrace("convert mib info to old format",[]),
+ [{N,S,F} || #mib_info{name=N,symbolic=S,file_name=F} <- LoadedMibs].
+
+
+%%----------------------------------------------------------------------
+%% A total dump for debugging.
+%%----------------------------------------------------------------------
+
+dump(#mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ tree = Tree}, io) ->
+ (catch io:format("MIB-tables:~n~p~n~n",
+ [snmpa_general_db:tab2list(MibDb)])),
+ (catch io:format("MIB-entries:~n~p~n~n",
+ [snmpa_general_db:tab2list(NodeDb)])),
+ (catch io:format("Tree:~n~p~n", [Tree])), % good luck reading it!
+ ok;
+
+dump(#mib_data{mib_db = MibDb,
+ node_db = NodeDb,
+ tree = Tree}, File) ->
+ case file:open(File, [write]) of
+ {ok, Fd} ->
+ io:format(Fd,"~s~n",
+ [snmp:date_and_time_to_string(snmp:date_and_time())]),
+ (catch io:format(Fd,"MIB-tables:~n~p~n~n",
+ [snmpa_general_db:tab2list(MibDb)])),
+ (catch io:format(Fd, "MIB-entries:~n~p~n~n",
+ [snmpa_general_db:tab2list(NodeDb)])),
+ io:format(Fd,"Tree:~n~p~n", [Tree]), % good luck reading it!
+ file:close(Fd),
+ ok;
+ {error, Reason} ->
+ ?vinfo("~n Failed opening file '~s' for reason ~p",
+ [File, Reason]),
+ {error, Reason}
+ end.
+
+
+backup(#mib_data{mib_db = M, node_db = N, tree_db = T}, BackupDir) ->
+ MRes = snmpa_general_db:backup(M, BackupDir),
+ NRes = snmpa_general_db:backup(N, BackupDir),
+ TRes = snmpa_general_db:backup(T, BackupDir),
+ handle_backup_res([{mib_db, MRes}, {node_db, NRes}, {tree_db, TRes}]).
+
+handle_backup_res(Res) ->
+ handle_backup_res(Res, []).
+
+handle_backup_res([], []) ->
+ ok;
+handle_backup_res([], Err) ->
+ {error, lists:reverse(Err)};
+handle_backup_res([{_, ok}|Res], Err) ->
+ handle_backup_res(Res, Err);
+handle_backup_res([{Tag, {error, Reason}}|Res], Err) ->
+ handle_backup_res(Res, [{Tag, Reason}|Err]);
+handle_backup_res([{Tag, Error}|Res], Err) ->
+ handle_backup_res(Res, [{Tag, Error}|Err]).
+
+
+%%%======================================================================
+%%% 2. Implementation of tree access
+%%% lookup and next.
+%%%======================================================================
+
+
+which_mib(#mib_data{tree = T} = D, Oid) ->
+ ?vtrace("which_mib -> entry with"
+ "~n Oid: ~p",[Oid]),
+ case (catch find_node(D, T#tree.root, Oid, [])) of
+ {variable, _ME, Mib} ->
+ ?vtrace("which_mib -> variable:"
+ "~n Mib: ~p", [Mib]),
+ {ok, Mib};
+ {table, _EntryME, _, Mib} ->
+ ?vtrace("which_mib -> table:"
+ "~n Mib: ~p", [Mib]),
+ {ok, Mib};
+ {subagent, SubAgentPid, _SANextOid} ->
+ ?vtrace("which_mib -> subagent:"
+ "~n SubAgentPid: ~p", [SubAgentPid]),
+ {error, {subagent, SubAgentPid}};
+ {false, ErrorCode} ->
+ ?vtrace("which_mib -> false:"
+ "~n ErrorCode: ~p",[ErrorCode]),
+ {error, ErrorCode};
+ false ->
+ ?vtrace("which_mib -> false",[]),
+ {error, noSuchObject};
+ {'EXIT', R} ->
+ ?vtrace("which_mib -> exit:"
+ "~n R: ~p",[R]),
+ {error, noSuchObject}
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Func: lookup/2
+%% Purpose: Finds the mib entry corresponding to the Oid. If it is a
+%% variable, the Oid must be <Oid for var>.0 and if it is
+%% a table, Oid must be <table>.<entry>.<col>.<any>
+%% Returns: {variable, MibEntry} |
+%% {table_column, MibEntry, TableEntryOid} |
+%% {subagent, SubAgentPid, SAOid} |
+%% {false, Reason}
+%%-----------------------------------------------------------------
+lookup(#mib_data{tree = T} = D, Oid) ->
+ ?vtrace("lookup -> entry with"
+ "~n Oid: ~p",[Oid]),
+ case (catch find_node(D, T#tree.root, Oid, [])) of
+ {variable, ME, _Mib} when is_record(ME, me) ->
+ ?vtrace("lookup -> variable:"
+ "~n ME: ~p",[ME]),
+ {variable, ME};
+ {table, EntryME, {ColME, TableEntryOid}, _Mib} ->
+ ?vtrace("lookup -> table:"
+ "~n EntryME: ~p"
+ "~n ColME: ~p"
+ "~n RevTableEntryOid: ~p",
+ [EntryME, ColME, TableEntryOid]),
+ MFA = EntryME#me.mfa,
+ RetME = ColME#me{mfa = MFA},
+ {table_column, RetME, TableEntryOid};
+ {subagent, SubAgentPid, SANextOid} ->
+ ?vtrace("lookup -> subagent:"
+ "~n SubAgentPid: ~p"
+ "~n SANextOid: ~p", [SubAgentPid, SANextOid]),
+ {subagent, SubAgentPid, SANextOid};
+ {false, ErrorCode} ->
+ ?vtrace("lookup -> false:"
+ "~n ErrorCode: ~p",[ErrorCode]),
+ {false, ErrorCode};
+ false ->
+ ?vtrace("lookup -> false",[]),
+ {false, noSuchObject};
+ {'EXIT', R} ->
+ ?vtrace("lookup -> exit:"
+ "~n R: ~p",[R]),
+ {false, noSuchObject}
+ end.
+
+
+find_node(D, {tree, Tree, {table, _}}, RestOfOid, RevOid) ->
+ ?vtrace("find_node(tree,table) -> entry with"
+ "~n RestOfOid: ~p"
+ "~n RevOid: ~p",[RestOfOid, RevOid]),
+ find_node(D, {tree, Tree, internal}, RestOfOid, RevOid);
+find_node(D, {tree, Tree, {table_entry, _}}, RestOfOid, RevOid) ->
+ ?vtrace("find_node(tree,table_entry) -> entry with"
+ "~n RestOfOid: ~p"
+ "~n RevOid: ~p",[RestOfOid, RevOid]),
+ #mib_data{node_db = Db} = D,
+ Oid = lists:reverse(RevOid),
+ case snmpa_general_db:read(Db, Oid) of
+ {value, #node_info{me = ME, mib_name = Mib}} ->
+ case find_node(D, {tree, Tree, internal}, RestOfOid, RevOid) of
+ {false, ErrorCode} -> {false, ErrorCode};
+ Val -> {table, ME, Val, Mib}
+ end;
+ false ->
+ ?vinfo("find_node -> could not find table_entry ME with"
+ "~n RevOid: ~p"
+ "~n when"
+ "~n RestOfOid: ~p",
+ [RevOid, RestOfOid]),
+ false
+ end;
+find_node(D, {tree, Tree, _Internal}, [Int | RestOfOid], RevOid) ->
+ ?vtrace("find_node(tree) -> entry with"
+ "~n Int: ~p"
+ "~n RestOfOid: ~p"
+ "~n RevOid: ~p",[Int, RestOfOid, RevOid]),
+ find_node(D, element(Int+1, Tree), RestOfOid, [Int | RevOid]);
+find_node(D, {node, {table_column, _}}, RestOfOid, [ColInt | RevOid]) ->
+ ?vtrace("find_node(tree,table_column) -> entry with"
+ "~n RestOfOid: ~p"
+ "~n ColInt: ~p"
+ "~n RevOid: ~p",[RestOfOid, ColInt, RevOid]),
+ #mib_data{node_db = Db} = D,
+ Oid = lists:reverse([ColInt | RevOid]),
+ case snmpa_general_db:read(Db, Oid) of
+ {value, #node_info{me = ME}} ->
+ {ME, lists:reverse(RevOid)};
+ false ->
+ X = snmpa_general_db:read(Db, lists:reverse([ColInt | RevOid])),
+ ?vinfo("find_node -> could not find table_column ME with"
+ "~n RevOid: ~p"
+ "~n trying [~p|~p]"
+ "~n X: ~p",
+ [RevOid, [ColInt | RevOid], X]),
+ false
+ end;
+find_node(D, {node, {variable, _MibName}}, [0], RevOid) ->
+ ?vtrace("find_node(tree,variable,[0]) -> entry with"
+ "~n RevOid: ~p",[RevOid]),
+ #mib_data{node_db = Db} = D,
+ Oid = lists:reverse(RevOid),
+ %% {value, #node_info{me = ME}} = snmpa_general_db:read(Db, Oid),
+ case snmpa_general_db:read(Db, Oid) of
+ {value, #node_info{me = ME, mib_name = Mib}} ->
+ {variable, ME, Mib};
+ false ->
+ ?vinfo("find_node -> could not find variable ME with"
+ "~n RevOid: ~p", [RevOid]),
+ false
+ end;
+find_node(_D, {node, {variable, _MibName}}, [], _RevOid) ->
+ ?vtrace("find_node(tree,variable,[]) -> entry",[]),
+ {false, noSuchObject};
+find_node(_D, {node, {variable, _MibName}}, _, _RevOid) ->
+ ?vtrace("find_node(tree,variable) -> entry",[]),
+ {false, noSuchInstance};
+find_node(D, {node, subagent}, _RestOfOid, SARevOid) ->
+ ?vtrace("find_node(tree,subagent) -> entry with"
+ "~n SARevOid: ~p",[SARevOid]),
+ #mib_data{subagents = SAs} = D,
+ SAOid = lists:reverse(SARevOid),
+ case lists:keysearch(SAOid, 2, SAs) of
+ {value, {SubAgentPid, SAOid}} ->
+ {subagent, SubAgentPid, SAOid};
+ false ->
+ ?vinfo("find_node -> could not find subagent with"
+ "~n SAOid: ~p"
+ "~n SAs: ~p", [SAOid, SAs]),
+ false
+ end;
+find_node(_D, Node, _RestOfOid, _RevOid) ->
+ ?vtrace("find_node -> failed:~n~p",[Node]),
+ {false, noSuchObject}.
+
+
+%%-----------------------------------------------------------------
+%% Func: next/3
+%% Purpose: Finds the lexicographically next oid.
+%% Returns: endOfMibView |
+%% {subagent, SubAgentPid, SAOid} |
+%% {variable, MibEntry, VarOid} |
+%% {table, TableOid, TableRestOid, MibEntry}
+%% If a variable is returnes, it is in the MibView.
+%% If a table or subagent is returned, it *may* be in the MibView.
+%%-----------------------------------------------------------------
+next(#mib_data{tree = T} = D, Oid, MibView) ->
+ case catch next_node(D, T#tree.root, Oid, [], MibView) of
+ false -> endOfMibView;
+ Else -> Else
+ end.
+
+%%-----------------------------------------------------------------
+%% This function is used as long as we have any Oid left. Take
+%% one integer at a time from the Oid, and traverse the tree
+%% accordingly. When the Oid is empty, call find_next.
+%% Returns: {subagent, SubAgentPid, SAOid} |
+%% false |
+%% {variable, MibEntry, VarOid} |
+%% {table, TableOid, TableRestOid, MibEntry}
+%%-----------------------------------------------------------------
+next_node(_D, undefined_node, _Oid, _RevOidSoFar, _MibView) ->
+ ?vtrace("next_node(undefined_node) -> entry", []),
+ false;
+
+next_node(_D, {tree, Tree, {table_entry, _Id}}, [Int | _Oid],
+ _RevOidSoFar, _MibView)
+ when Int+1 > size(Tree) ->
+ ?vtrace("next_node(tree,table_entry) -> entry when not found whith"
+ "~n Int: ~p"
+ "~n size(Tree): ~p", [Int, size(Tree)]),
+ false;
+next_node(D, {tree, Tree, {table_entry, _MibName}},
+ Oid, RevOidSoFar, MibView) ->
+ ?vtrace("next_node(tree,table_entry) -> entry when"
+ "~n size(Tree): ~p"
+ "~n Oid: ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p", [size(Tree), Oid, RevOidSoFar, MibView]),
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ ?vdebug("next_node(tree,table_entry) -> not in mib view",[]),
+ false;
+ _ ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, OidSoFar) of
+ false ->
+ ?vinfo("next_node -> could not find table_entry with"
+ "~n OidSoFar: ~p", [OidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ ?vtrace("next_node(tree,table_entry) -> found: ~n ~p",
+ [ME]),
+ {table, OidSoFar, Oid, ME}
+ end
+ end;
+
+next_node(D, {tree, Tree, _Info}, [Int | RestOfOid], RevOidSoFar, MibView)
+ when (Int < size(Tree)) andalso (Int >= 0) ->
+ ?vtrace("next_node(tree) -> entry when"
+ "~n size(Tree): ~p"
+ "~n Int: ~p"
+ "~n RestOfOid: ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [size(Tree), Int, RestOfOid, RevOidSoFar, MibView]),
+ case next_node(D, element(Int+1,Tree),
+ RestOfOid, [Int|RevOidSoFar], MibView) of
+ false ->
+ find_next(D, {tree, Tree, _Info}, Int+1, RevOidSoFar, MibView);
+ Else ->
+ Else
+ end;
+%% no solution
+next_node(D, {tree, Tree, _Info}, [], RevOidSoFar, MibView) ->
+ ?vtrace("next_node(tree,[]) -> entry when"
+ "~n size(Tree): ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [size(Tree), RevOidSoFar, MibView]),
+ find_next(D, {tree, Tree, _Info}, 0, RevOidSoFar, MibView);
+next_node(_D, {tree, Tree, _Info}, _RestOfOid, _RevOidSoFar, _MibView) ->
+ ?vtrace("next_node(tree) -> entry when"
+ "~n size(Tree): ~p", [size(Tree)]),
+ false;
+
+next_node(D, {node, subagent}, Oid, RevOidSoFar, MibView) ->
+ ?vtrace("next_node(node,subagent) -> entry when"
+ "~n Oid: ~p"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [Oid, RevOidSoFar, MibView]),
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ false;
+ _ ->
+ #mib_data{subagents = SAs} = D,
+ case lists:keysearch(OidSoFar, 2, SAs) of
+ {value, {SubAgentPid, OidSoFar}} ->
+ {subagent, SubAgentPid, OidSoFar};
+ _ ->
+ ?vinfo("next_node -> could not find subagent with"
+ "~n OidSoFar: ~p"
+ "~n SAs: ~p", [OidSoFar, SAs]),
+ false
+ end
+ end;
+
+next_node(D, {node, {variable, _MibName}}, [], RevOidSoFar, MibView) ->
+ ?vtrace("next_node(node,variable,[]) -> entry when"
+ "~n RevOidSoFar: ~p"
+ "~n MibView: ~p",
+ [RevOidSoFar, MibView]),
+ OidSoFar = lists:reverse([0 | RevOidSoFar]),
+ case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
+ true ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
+ false ->
+ ?vinfo("next_node -> could not find variable with"
+ "~n RevOidSoFar: ~p", [RevOidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ {variable, ME, OidSoFar}
+ end;
+ _ ->
+ false
+ end;
+
+next_node(_D, {node, {variable, _MibName}}, _Oid, _RevOidSoFar, _MibView) ->
+ ?vtrace("next_node(node,variable) -> entry", []),
+ false.
+
+%%-----------------------------------------------------------------
+%% This function is used to find the first leaf from where we
+%% are.
+%% Returns: {subagent, SubAgentPid, SAOid} |
+%% false |
+%% {variable, MibEntry, VarOid} |
+%% {table, TableOid, TableRestOid, MibEntry}
+%% PRE: This function must always be called with a {internal, Tree}
+%% node.
+%%-----------------------------------------------------------------
+find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView)
+ when Idx < size(Tree) ->
+ case find_next(D, element(Idx+1, Tree), 0, [Idx| RevOidSoFar], MibView) of
+ false ->
+ find_next(D, {tree, Tree, internal}, Idx+1, RevOidSoFar, MibView);
+ Other ->
+ Other
+ end;
+find_next(_D, {tree, _Tree, internal}, _Idx, _RevOidSoFar, _MibView) ->
+ false;
+find_next(_D, undefined_node, _Idx, _RevOidSoFar, _MibView) ->
+ false;
+find_next(D, {tree, Tree, {table, _MibName}}, Idx, RevOidSoFar, MibView) ->
+ find_next(D, {tree, Tree, internal}, Idx, RevOidSoFar, MibView);
+find_next(D, {tree, _Tree, {table_entry, _MibName}}, _Index,
+ RevOidSoFar, MibView) ->
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ false;
+ _ ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, OidSoFar) of
+ false ->
+ ?vinfo("find_next -> could not find table_entry ME with"
+ "~n OidSoFar: ~p", [OidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ {table, OidSoFar, [], ME}
+ end
+ end;
+find_next(D, {node, {variable, _MibName}}, _Idx, RevOidSoFar, MibView) ->
+ OidSoFar = lists:reverse([0 | RevOidSoFar]),
+ case snmpa_acm:validate_mib_view(OidSoFar, MibView) of
+ true ->
+ #mib_data{node_db = Db} = D,
+ case snmpa_general_db:read(Db, lists:reverse(RevOidSoFar)) of
+ false ->
+ ?vinfo("find_next -> could not find variable with"
+ "~n RevOidSoFar: ~p", [RevOidSoFar]),
+ false;
+ {value, #node_info{me = ME}} ->
+ {variable, ME, OidSoFar}
+ end;
+ _ ->
+ false
+ end;
+find_next(D, {node, subagent}, _Idx, RevOidSoFar, MibView) ->
+ OidSoFar = lists:reverse(RevOidSoFar),
+ case snmpa_acm:is_definitely_not_in_mib_view(OidSoFar, MibView) of
+ true ->
+ false;
+ _ ->
+ #mib_data{subagents = SAs} = D,
+ case lists:keysearch(OidSoFar, 2, SAs) of
+ {value, {SubAgentPid, OidSoFar}} ->
+ {subagent, SubAgentPid, OidSoFar};
+ false ->
+ ?vinfo("find_node -> could not find subagent with"
+ "~n OidSoFar: ~p"
+ "~n SAs: ~p", [OidSoFar, SAs]),
+ false
+ end
+ end.
+
+%%%======================================================================
+%%% 3. Tree building functions
+%%% Used when loading mibs.
+%%%======================================================================
+
+build_tree(Mes, MibName) ->
+ ?d("build_tree -> "
+ "~n Mes: ~p", [Mes]),
+ {ListTree, []} = build_subtree([], Mes, MibName),
+ {tree, convert_tree(ListTree), internal}.
+
+%%----------------------------------------------------------------------
+%% Purpose: Builds the tree where all oids have prefix equal to LevelPrefix.
+%% Returns: {Tree, RestMes}
+%% RestMes are Mes that should not be in this subtree.
+%% The Tree is a temporary and simplified data structure that is easy to
+%% convert to the final tuple tree used by the MIB process.
+%% A Node is represented as in the final tree.
+%% The tree is not represented as a N-tuple, but as an Index-list.
+%% Example: Temporary: [{1, Node1}, {3, Node3}]
+%% Final: {Node1, undefined_node, Node3}
+%% Pre: Mes are sorted on oid.
+%%----------------------------------------------------------------------
+build_subtree(LevelPrefix, [Me | Mes], MibName) ->
+ ?vtrace("build subtree -> ~n"
+ " oid: ~p~n"
+ " LevelPrefix: ~p~n"
+ " MibName: ~p", [Me#me.oid, LevelPrefix, MibName]),
+ EType = Me#me.entrytype,
+ ?vtrace("build subtree -> EType = ~p",[EType]),
+ case in_subtree(LevelPrefix, Me) of
+ above ->
+ ?vtrace("build subtree -> above",[]),
+ {[], [Me|Mes]};
+ {node, Index} ->
+ ?vtrace("build subtree -> node at ~p",[Index]),
+ {Tree, RestMes} = build_subtree(LevelPrefix, Mes, MibName),
+ {[{Index, {node, {EType, MibName}}} | Tree], RestMes};
+ {subtree, Index, NewLevelPrefix} ->
+ ?vtrace("build subtree -> subtree at"
+ "~n ~w with ~w",
+ [Index, NewLevelPrefix]),
+ {BelowTree, RestMes} =
+ build_subtree(NewLevelPrefix, Mes, MibName),
+ {CurTree, RestMes2} =
+ build_subtree(LevelPrefix, RestMes, MibName),
+ {[{Index, {tree, BelowTree, {EType,MibName}}}| CurTree], RestMes2};
+ {internal_subtree, Index, NewLevelPrefix} ->
+ ?vtrace("build subtree -> internal_subtree at"
+ "~n ~w with ~w",
+ [Index,NewLevelPrefix]),
+ {BelowTree, RestMes} =
+ build_subtree(NewLevelPrefix, [Me | Mes], MibName),
+ {CurTree, RestMes2} =
+ build_subtree(LevelPrefix, RestMes, MibName),
+ {[{Index, {tree, BelowTree, internal}} | CurTree], RestMes2}
+ end;
+
+build_subtree(_LevelPrefix, [], _MibName) ->
+ ?vtrace("build subtree -> done", []),
+ {[], []}.
+
+%%--------------------------------------------------
+%% Purpose: Determine how/if/where Me should be inserted in subtree
+%% with LevelPrefix. This function does not build any tree, only
+%% determinses what should be done (by build subtree).
+%% Returns:
+%% above - Indicating that this ME should _not_ be in this subtree.
+%% {node, Index} - yes, construct a node with index Index on this level
+%% {internal_subtree, Index, NewLevelPrefix} - yes, there should be an
+%% internal subtree at this index.
+%% {subtree, Index, NewLevelPrefix} - yes, construct a subtree with
+%% NewLevelPrefix and insert this on current level in position Index.
+%%--------------------------------------------------
+in_subtree(LevelPrefix, Me) ->
+ case lists:prefix(LevelPrefix, Me#me.oid) of
+ true when length(Me#me.oid) > length(LevelPrefix) ->
+ classify_how_in_subtree(LevelPrefix, Me);
+ _ ->
+ above
+ end.
+
+%%--------------------------------------------------
+%% See comment about in_subtree/2. This function takes care of all cases
+%% where the ME really should be in _this_ subtree (not above).
+%%--------------------------------------------------
+classify_how_in_subtree(LevelPrefix, Me)
+ when (length(Me#me.oid) =:= (length(LevelPrefix) + 1)) ->
+ Oid = Me#me.oid,
+ case node_or_subtree(Me#me.entrytype) of
+ subtree ->
+ {subtree, lists:last(Oid), Oid};
+ node ->
+ {node, lists:last(Oid)}
+ end;
+
+classify_how_in_subtree(LevelPrefix, Me)
+ when (length(Me#me.oid) > (length(LevelPrefix) + 1)) ->
+ L1 = length(LevelPrefix) + 1,
+ Oid = Me#me.oid,
+ {internal_subtree, lists:nth(L1, Oid), lists:sublist(Oid, 1, L1)}.
+
+%%--------------------------------------------------
+%% Determines how to treat different kinds om MEs in the tree building process.
+%% Pre: all internal nodes have been removed.
+%%--------------------------------------------------
+node_or_subtree(table) -> subtree;
+node_or_subtree(table_entry) -> subtree;
+node_or_subtree(variable) -> node;
+node_or_subtree(table_column) -> node.
+
+%%--------------------------------------------------
+%% Purpose: (Recursively) Converts a temporary tree (see above) to a final tree.
+%% If input is a ListTree, output is a TupleTree.
+%% If input is a Node, output is the same Node.
+%% Pre: All Indexes are >= 0.
+%%--------------------------------------------------
+convert_tree({Index, {tree, Tree, Info}}) when Index >= 0 ->
+ L = lists:map(fun convert_tree/1, Tree),
+ {Index, {tree, dict_list_to_tuple(L), Info}};
+convert_tree({Index, {node, Info}}) when Index >= 0 ->
+ {Index, {node, Info}};
+convert_tree(Tree) when is_list(Tree) ->
+ L = lists:map(fun convert_tree/1, Tree),
+ dict_list_to_tuple(L).
+
+%%----------------------------------------------------------------------
+%% Purpose: Converts a single level (that is non-recursively) from
+%% the temporary indexlist to the N-tuple.
+%% Input: A list of {Index, Data}.
+%% Output: A tuple where element Index is Data.
+%%----------------------------------------------------------------------
+dict_list_to_tuple(L) ->
+ L2 = lists:keysort(1, L),
+ list_to_tuple(integrate_indexes(0, L2)).
+
+%%----------------------------------------------------------------------
+%% Purpose: Helper function for dict_list_to_tuple/1.
+%% Converts an indexlist to a N-list.
+%% Input: A list of {Index, Data}.
+%% Output: A (usually longer, never shorter) list where element Index is Data.
+%% Example: [{1,hej}, {3, sven}] will give output
+%% [undefined_node, hej, undefined_node, sven].
+%% Initially CurIndex should be 0.
+%%----------------------------------------------------------------------
+integrate_indexes(CurIndex, [{CurIndex, Data} | T]) ->
+ [Data | integrate_indexes(CurIndex + 1, T)];
+integrate_indexes(_Index, []) ->
+ [];
+integrate_indexes(CurIndex, L) ->
+ [undefined_node | integrate_indexes(CurIndex + 1, L)].
+
+%%%======================================================================
+%%% 4. Tree merging
+%%% Used by: load mib, insert subagent.
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Arg: Two root nodes (that is to be merged).
+%% Returns: A new root node where the nodes have been merger to one.
+%%----------------------------------------------------------------------
+merge_nodes(Same, Same) ->
+ Same;
+merge_nodes(Node, undefined_node) ->
+ Node;
+merge_nodes(undefined_node, Node) ->
+ Node;
+merge_nodes({tree, Tree1, internal}, {tree, Tree2, internal}) ->
+ {tree, merge_levels(tuple_to_list(Tree1),tuple_to_list(Tree2)), internal};
+merge_nodes(Node1, Node2) ->
+ throw({error_merge_nodes, Node1, Node2}).
+
+%%----------------------------------------------------------------------
+%% Arg: Two levels to be merged.
+%% Here, a level is represented as a list of nodes. A list is easier
+%% to extend than a tuple.
+%% Returns: The resulting, merged level tuple.
+%%----------------------------------------------------------------------
+merge_levels(Level1, Level2) when length(Level1) =:= length(Level2) ->
+ MergeNodes = fun(N1, N2) -> merge_nodes(N1, N2) end,
+ list_to_tuple(snmp_misc:multi_map(MergeNodes, [Level1, Level2]));
+merge_levels(Level1, Level2) when length(Level1) > length(Level2) ->
+ merge_levels(Level1, Level2 ++
+ undefined_nodes_list(length(Level1) - length(Level2)));
+merge_levels(Level1, Level2) when length(Level1) < length(Level2) ->
+ merge_levels(Level2, Level1).
+
+undefined_nodes_list(N) -> lists:duplicate(N, undefined_node).
+
+
+%%%======================================================================
+%%% 5. Tree deletion routines
+%%% (for unload mib)
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Purpose: Actually kicks of the tree reconstruction.
+%% Returns: {list of removed MEs, NewTree}
+%%----------------------------------------------------------------------
+delete_mib_from_tree(MibName, {tree, Tree, internal}) ->
+ case delete_tree(Tree, MibName) of
+ [] ->
+ {tree, {undefined_node}, internal}; % reduce
+ LevelList ->
+ {tree, list_to_tuple(LevelList), internal}
+ end.
+
+%%----------------------------------------------------------------------
+%% Purpose: Deletes all nodes associated to MibName from this level and
+%% all levels below.
+%% If the new level does not contain information (that is, no
+%% other mibs use it) anymore the empty list is returned.
+%% Returns: {MEs, The new level represented as a list}
+%%----------------------------------------------------------------------
+delete_tree(Tree, MibName) when is_tuple(Tree) ->
+ NewLevel = delete_nodes(tuple_to_list(Tree), MibName, []),
+ case lists:filter(fun drop_undefined_nodes/1,NewLevel) of
+ [] -> [];
+ _A_perhaps_shorted_list ->
+ NewLevel % some other mib needs this level
+ end.
+
+%%----------------------------------------------------------------------
+%% Purpose: Nodes belonging to MibName are removed from the tree.
+%% Recursively deletes sub trees to this node.
+%% Returns: {MEs, NewNodesList}
+%%----------------------------------------------------------------------
+delete_nodes([], _MibName, AccNodes) ->
+ lists:reverse(AccNodes);
+
+delete_nodes([{node, {variable, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{node, {table_column, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{tree, _Tree, {table, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{tree, _Tree, {table_entry, MibName}}|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+
+delete_nodes([{tree, Tree, Info}|T], MibName, AccNodes) ->
+ case delete_tree(Tree, MibName) of
+ [] -> % tree completely deleted
+ delete_nodes(T, MibName, [undefined_node | AccNodes]);
+ LevelList ->
+ delete_nodes(T, MibName,
+ [{tree, list_to_tuple(LevelList), Info} | AccNodes])
+ end;
+
+delete_nodes([NodeToKeep|T], MibName, AccNodes) ->
+ delete_nodes(T, MibName, [NodeToKeep | AccNodes]).
+
+drop_undefined_nodes(undefined_node) -> false;
+drop_undefined_nodes(_) -> true.
+
+
+%%%======================================================================
+%%% 6. Functions for subagent handling
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Returns: A new Root|{error, reason}
+%%----------------------------------------------------------------------
+insert_subagent(Oid, OldRoot) ->
+ ListTree = build_tree_for_subagent(Oid),
+ case catch convert_tree(ListTree) of
+ {'EXIT', _Reason} ->
+ {error, 'cannot construct tree from oid'};
+ Level when is_tuple(Level) ->
+ T = {tree, Level, internal},
+ case catch merge_nodes(T, OldRoot) of
+ {error_merge_nodes, _Node1, _Node2} ->
+ {error, oid_conflict};
+ NewRoot when is_tuple(NewRoot) andalso
+ (element(1, NewRoot) =:= tree) ->
+ NewRoot
+ end
+ end.
+
+build_tree_for_subagent([Index]) ->
+ [{Index, {node, subagent}}];
+
+build_tree_for_subagent([Index | T]) ->
+ [{Index, {tree, build_tree_for_subagent(T), internal}}].
+
+%%----------------------------------------------------------------------
+%% Returns: A new tree where the subagent at Oid (2nd arg) has been deleted.
+%%----------------------------------------------------------------------
+delete_subagent({tree, Tree, Info}, [Index]) ->
+ {node, subagent} = element(Index+1, Tree),
+ {tree, setelement(Index+1, Tree, undefined_node), Info};
+delete_subagent({tree, Tree, Info}, [Index | TI]) ->
+ {tree, setelement(Index+1, Tree,
+ delete_subagent(element(Index+1, Tree), TI)), Info}.
+
+%%%======================================================================
+%%% 7. Misc functions
+%%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Installs the mibs found in the database when starting the agent.
+%% Basically calls the instrumentation functions for all non-internal
+%% mib-entries
+%%----------------------------------------------------------------------
+install_mibs(MibDb, NodeDb) ->
+ MibNames = loaded(MibDb),
+ ?vtrace("install_mibs -> found following mibs in database: ~n"
+ "~p", [MibNames]),
+ install_mibs2(NodeDb, MibNames).
+
+install_mibs2(_, []) ->
+ ok;
+install_mibs2(NodeDb, [MibName|MibNames]) ->
+ Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
+ Nodes = snmpa_general_db:match_object(NodeDb, Pattern),
+ MEs = [ME || #node_info{me = ME} <- Nodes],
+ ?vtrace("install_mibs2 -> installing ~p MEs for mib ~p",
+ [length(MEs),MibName]),
+ NewF = fun(ME) -> call_instrumentation(ME, new) end,
+ lists:foreach(NewF, MEs),
+ install_mibs2(NodeDb, MibNames).
+
+
+%%----------------------------------------------------------------------
+%% Does all side effect stuff during load_mib.
+%%----------------------------------------------------------------------
+install_mib(Db, Symbolic, Mib, MibName, FileName, NonInternalMes) ->
+ ?vdebug("install_mib -> entry with"
+ "~n Symbolic: ~p"
+ "~n MibName: ~p"
+ "~n FileName: ~p", [Symbolic, MibName, FileName]),
+ Rec = #mib_info{name = MibName, symbolic = Symbolic, file_name = FileName},
+ snmpa_general_db:write(Db, Rec),
+ install_mib2(Symbolic, MibName, Mib),
+ NewF = fun(ME) -> call_instrumentation(ME, new) end,
+ lists:foreach(NewF, NonInternalMes).
+
+install_mib2(true, MibName, Mib) ->
+ #mib{table_infos = TabInfos,
+ variable_infos = VarInfos,
+ mes = MEs,
+ asn1_types = ASN1Types,
+ traps = Traps} = Mib,
+ snmpa_symbolic_store:add_table_infos(MibName, TabInfos),
+ snmpa_symbolic_store:add_variable_infos(MibName, VarInfos),
+ snmpa_symbolic_store:add_aliasnames(MibName, MEs),
+ snmpa_symbolic_store:add_types(MibName, ASN1Types),
+ SetF = fun(Trap) ->
+ snmpa_symbolic_store:set_notification(Trap, MibName)
+ end,
+ lists:foreach(SetF, Traps);
+install_mib2(_, _, _) ->
+ ok.
+
+install_mes(_Db, _MibName, []) ->
+ ok;
+install_mes(Db, MibName, [ME|MEs]) ->
+ Node = #node_info{oid = ME#me.oid, mib_name = MibName, me = ME},
+ snmpa_general_db:write(Db, Node),
+ install_mes(Db, MibName, MEs).
+
+
+%%----------------------------------------------------------------------
+%% Does all side effect stuff during unload_mib.
+%%----------------------------------------------------------------------
+uninstall_mib(Db, Symbolic, MibName, MEs) ->
+ ?vtrace("uninstall_mib -> entry with"
+ "~n Db: ~p"
+ "~n Symbolic: ~p"
+ "~n MibName: ~p", [Db, Symbolic, MibName]),
+ Res = snmpa_general_db:delete(Db, MibName),
+ ?vtrace("uninstall_mib -> (mib) db delete result: ~p", [Res]),
+ uninstall_mib2(Symbolic, MibName),
+ DelF = fun(ME) -> call_instrumentation(ME, delete) end,
+ lists:foreach(DelF, MEs).
+
+uninstall_mib2(true, MibName) ->
+ snmpa_symbolic_store:delete_table_infos(MibName),
+ snmpa_symbolic_store:delete_variable_infos(MibName),
+ snmpa_symbolic_store:delete_aliasnames(MibName),
+ snmpa_symbolic_store:delete_types(MibName),
+ snmpa_symbolic_store:delete_notifications(MibName);
+uninstall_mib2(_, _) ->
+ ok.
+
+uninstall_mes(Db, MibName) ->
+ Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'},
+ snmpa_general_db:match_delete(Db, Pattern).
+
+
+%%----------------------------------------------------------------------
+%% Create a list of the names of all the loaded mibs
+%%----------------------------------------------------------------------
+loaded(Db) ->
+ [N || #mib_info{name = N} <- snmpa_general_db:tab2list(Db)].
+
+
+%%----------------------------------------------------------------------
+%% Calls MFA-instrumentation with 'new' or 'delete' operation.
+%%----------------------------------------------------------------------
+call_instrumentation(#me{entrytype = variable, mfa={M,F,A}}, Operation) ->
+ ?vtrace("call instrumentation with"
+ "~n entrytype: variable"
+ "~n MFA: {~p,~p,~p}"
+ "~n Operation: ~p",
+ [M,F,A,Operation]),
+ catch apply(M, F, [Operation | A]);
+call_instrumentation(#me{entrytype = table_entry, mfa={M,F,A}}, Operation) ->
+ ?vtrace("call instrumentation with"
+ "~n entrytype: table_entry"
+ "~n MFA: {~p,~p,~p}"
+ "~n Operation: ~p",
+ [M,F,A,Operation]),
+ catch apply(M, F, [Operation | A]);
+call_instrumentation(_ShitME, _Operation) ->
+ done.
+
+
+maybe_drop_me(#me{entrytype = internal}) -> false;
+maybe_drop_me(#me{entrytype = group}) -> false;
+maybe_drop_me(#me{imported = true}) -> false;
+maybe_drop_me(_) -> true.
+
+
+%%----------------------------------------------------------------------
+%% Code change functions
+%%----------------------------------------------------------------------
+
+code_change(down, _Vsn, _Extra, State) ->
+ ?d("code_change(down) -> entry when"
+ "~n Vsn: ~p"
+ "~n Extra: ~p", [_Vsn, _Extra]),
+ State;
+
+code_change(up, _Vsn, _Extra, State) ->
+ ?d("code_change(up) -> entry when"
+ "~n Vsn: ~p"
+ "~n Extra: ~p", [_Vsn, _Extra]),
+ State.
+
+