From 6dfebed74f6faa92dc44dac291e5726e9ae08b8e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 26 Apr 2013 08:57:03 +0200 Subject: [snmp/agent] Preliminary change to the mib-server tree-type --- lib/snmp/src/agent/snmpa_mib_data.erl | 56 ++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index b80d85d2ee..e20884c0a3 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -104,15 +104,49 @@ %% The over all root is represented as {tree, Tree, internal}. %% %% tree() = {tree, nodes(), tree_info()} -%% nodes() = {tree() | node() | undefined_node, ...} +%% 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(tree,{generation = ?DUMMY_TREE_GENERATION, root = ?DEFAULT_TREE}). +-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() + }). + + %%%====================================================================== @@ -133,29 +167,29 @@ new(Storage) -> ?vtrace("open (mib) database",[]), MibDb = snmpa_general_db:open(Storage, ?MIB_DATA, mib_info, - record_info(fields,mib_info), set), + 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), + 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 = + record_info(fields, mtree), set), + MTree = case snmpa_general_db:read(TreeDb, ?DUMMY_TREE_GENERATION) of false -> - T = #tree{}, + 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, - tree = Tree}. + #mib_data{mib_db = MibDb, + node_db = NodeDb, + tree_db = TreeDb, + mtree = MTree}. %%---------------------------------------------------------------------- -- cgit v1.2.3 From 96a4c07061f260be41a14194596307f3829ac97e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 26 Apr 2013 22:03:59 +0200 Subject: [snmp/agent] New behaviour module (snmpa_mib_data) and two impl mods The old mib-server data module (snmpa_mib_data) has been changed into a behaviour module. The old implementation is now instead in a new module snmpa_mib_data_tree1. A new backend module has been added, still only very preliminary, snmpa_mib_data_tree2. --- lib/snmp/src/agent/modules.mk | 4 +- lib/snmp/src/agent/snmpa_mib.erl | 149 +-- lib/snmp/src/agent/snmpa_mib_data.erl | 1393 +-------------------------- lib/snmp/src/agent/snmpa_mib_data_tree1.erl | 1355 ++++++++++++++++++++++++++ lib/snmp/src/agent/snmpa_mib_data_tree2.erl | 1389 ++++++++++++++++++++++++++ 5 files changed, 2879 insertions(+), 1411 deletions(-) create mode 100644 lib/snmp/src/agent/snmpa_mib_data_tree1.erl create mode 100644 lib/snmp/src/agent/snmpa_mib_data_tree2.erl diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index 33ab41b434..e364675c2c 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -21,6 +21,7 @@ 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 \ @@ -42,7 +43,8 @@ MODULES = \ snmpa_general_db \ snmpa_local_db \ snmpa_mib \ - snmpa_mib_data \ + snmpa_mib_data_tree1 \ + snmpa_mib_data_tree2 \ snmpa_mib_lib \ snmpa_misc_sup \ snmpa_mpd \ diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl index 575a018c0c..a5e4a4cb73 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, + mib_data_mod}). @@ -289,9 +290,11 @@ do_init(Prio, Mibs, Opts) -> MeOverride = get_me_override(Opts), TeOverride = get_te_override(Opts), MibStorage = get_mib_storage(Opts), + MibDataMod = get_mib_data_mod(Otps), Data = snmpa_mib_data: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",[]), @@ -304,7 +307,8 @@ do_init(Prio, Mibs, Opts) -> cache_tmr = CacheGcTimer, cache_autogc = CacheAutoGC, cache_gclimit = CacheGcLimit, - cache_age = CacheAge}}; + cache_age = CacheAge, + mib_data_mib = MibDataMib}}; {'aborted at', Mib, _NewData, Reason} -> ?vinfo("failed loading mib ~p: ~p",[Mib,Reason]), {error, {Mib, Reason}} @@ -315,26 +319,28 @@ 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 + 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]), @@ -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, mib_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, mib_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, mib_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,15 +448,16 @@ 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, + mib_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, + 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]), @@ -457,20 +465,21 @@ handle_call({load_mibs, Mibs}, _From, {ok, NewData} -> {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, + mib_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, + 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]), @@ -478,25 +487,28 @@ handle_call({unload_mibs, Mibs}, _From, {ok, NewData} -> {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) -> ?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, + mib_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, + mib_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}}; @@ -505,11 +517,12 @@ handle_call({register_subagent, Oid, Pid}, _From, end; handle_call({unregister_subagent, OidOrPid}, _From, - #state{data = Data, cache = Cache} = State) -> + #state{data = Data, cache = Cache, + mib_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, DeletedSubagentPid} -> {reply, {ok, DeletedSubagentPid}, State#state{data = NewData, cache = NewCache}}; @@ -520,10 +533,11 @@ handle_call({unregister_subagent, OidOrPid}, _From, {reply, ok, State#state{data = NewData, cache = NewCache}} end; -handle_call(info, _From, #state{data = Data, cache = Cache} = State) -> +handle_call(info, _From, #state{data = Data, cache = Cache, + mib_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 +545,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, + mib_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 +558,23 @@ handle_call({info, Type}, _From, #state{data = Data} = State) -> end, {reply, Reply, State}; -handle_call(dump, _From, State) -> +handle_call(dump, _From, #state{mib_data_mod = Mod} = _State) -> ?vlog("dump",[]), - Reply = snmpa_mib_data:dump(State#state.data), + Reply = Mod:dump(State#state.data), {reply, Reply, State}; -handle_call({dump, File}, _From, #state{data = Data} = State) -> +handle_call({dump, File}, _From, + #state{data = Data, mib_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, mib_data_mod = Mod} = State) -> ?vlog("backup to ~s", [BackupDir]), Pid = self(), V = get(verbosity), @@ -568,7 +586,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 +655,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, mib_data_mod = Mod}) -> + catch Mod:close(Data), ok. @@ -663,8 +681,13 @@ 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({down, _Vsn}, #state{mib_data_mod = Mod} = State, _Extra) -> + Data = Mod:code_change(down, Data0), + {ok, State#state{data = Data}}; + +code_change(_Vsn, #state{mib_data_mod = Mod} = State, _Extra) -> + Data = Mod:code_change(up, Data0), + {ok, State#state{data = Data}}. %%----------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index e20884c0a3..2872ee2127 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 @@ -19,1371 +19,70 @@ -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. +%%% This is the behaviour for the MIB server backend internal +%%% data storage. %%%----------------------------------------------------------------- --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}). +-callback new(MibStorage :: mib_storage()) -> State :: term(). +-callback close(State :: term()) -> ok. -%%%----------------------------------------------------------------- -%%% 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 .0 and if it is -%% a table, Oid must be ... -%% 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). +-callback sync(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 load_mib(State :: term(), FileName :: string(), + MeOverride :: boolean(), + TeOverride :: boolean()) -> + {ok, NewState :: term()} | {error, already_loaded | Reason :: term()}. -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 unload_mib(State :: term(), FileName :: string(), + MeOverride :: boolean(), + TeOverride :: boolean()) -> + {ok, NewState :: term()} | {error, not_loaded | Reason :: term()}. +-callback lookup(State :: term(), Oid :: oid()) -> + {false, Reason :: term()} | + {variable, MibEntry :: me()} | + {table_column, MibEntry :: me(), TableEntryOid :: oid()} | + {subagent, SubAgentPid :: pid(), SAOid :: oid()}. -%%---------------------------------------------------------------------- -%% 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 next(State :: term(), Oid :: oid(), MibView :: mib_view()) -> + endOfView | false | + {subagent, SubAgentPid :: pid(), SAOid :: oid()} | + {variable, MibEntry :: me(), VarOid :: oid()} | + {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()} -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 register_subagent(State :: term(), Oid :: oid(), Pid :: pid()) -> + {error, Reason :: term()} | NewState :: term(). -uninstall_mes(Db, MibName) -> - Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'}, - snmpa_general_db:match_delete(Db, Pattern). +-callback unregister_subagent(State :: term(), + Pid :: pid() | Oid :: oid()) -> + {ok, NewState :: term()} | % When second arg wa a pid() + {ok, NewState :: term(), Pid :: pid()} | % When second arg was a oid() + {error, Reason :: term()}. +-callback dump(State :: term(), Filename :: string()) -> + ok | {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 print(State :: term()) -> ok. -%%---------------------------------------------------------------------- -%% 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 :: 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(). +-callback info(State :: term(), Item :: atom()) -> term(). -%%---------------------------------------------------------------------- -%% 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, 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_tree1.erl b/lib/snmp/src/agent/snmpa_mib_data_tree1.erl new file mode 100644 index 0000000000..b80d85d2ee --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_data_tree1.erl @@ -0,0 +1,1355 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(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}). + + +%%%----------------------------------------------------------------- +%%% 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} +%%----------------------------------------------------------------- + +%% 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 .0 and if it is +%% a table, Oid must be
... +%% 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_tree2.erl b/lib/snmp/src/agent/snmpa_mib_data_tree2.erl new file mode 100644 index 0000000000..e20884c0a3 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_data_tree2.erl @@ -0,0 +1,1389 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(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}). + + +%%%----------------------------------------------------------------- +%%% 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 .0 and if it is +%% a table, Oid must be
... +%% 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. + -- cgit v1.2.3 From 93358e09620964f8ea608e5832b8c77f558c5851 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Sun, 28 Apr 2013 17:32:54 +0200 Subject: [snmp/agent] Renamed the mib_data (backend) modules Renmed the two backend modules: snmpa_mib_data_tree1.erl -> snmpa_mib_data_tttn.erl snmpa_mib_data_tree2.erl -> snmpa_mib_data_ttln.erl TTTN - TupleTreeTupleNodes TTLN - TupleTreeListNodes --- lib/snmp/src/agent/modules.mk | 4 +- lib/snmp/src/agent/snmpa_mib_data_tree1.erl | 1355 -------------------------- lib/snmp/src/agent/snmpa_mib_data_tree2.erl | 1389 --------------------------- lib/snmp/src/agent/snmpa_mib_data_ttln.erl | 1389 +++++++++++++++++++++++++++ lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 1355 ++++++++++++++++++++++++++ 5 files changed, 2746 insertions(+), 2746 deletions(-) delete mode 100644 lib/snmp/src/agent/snmpa_mib_data_tree1.erl delete mode 100644 lib/snmp/src/agent/snmpa_mib_data_tree2.erl create mode 100644 lib/snmp/src/agent/snmpa_mib_data_ttln.erl create mode 100644 lib/snmp/src/agent/snmpa_mib_data_tttn.erl diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index e364675c2c..23f53a772c 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -43,8 +43,8 @@ MODULES = \ snmpa_general_db \ snmpa_local_db \ snmpa_mib \ - snmpa_mib_data_tree1 \ - snmpa_mib_data_tree2 \ + snmpa_mib_data_tttn \ + snmpa_mib_data_ttln \ snmpa_mib_lib \ snmpa_misc_sup \ snmpa_mpd \ diff --git a/lib/snmp/src/agent/snmpa_mib_data_tree1.erl b/lib/snmp/src/agent/snmpa_mib_data_tree1.erl deleted file mode 100644 index b80d85d2ee..0000000000 --- a/lib/snmp/src/agent/snmpa_mib_data_tree1.erl +++ /dev/null @@ -1,1355 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(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}). - - -%%%----------------------------------------------------------------- -%%% 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} -%%----------------------------------------------------------------- - -%% 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 .0 and if it is -%% a table, Oid must be
... -%% 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_tree2.erl b/lib/snmp/src/agent/snmpa_mib_data_tree2.erl deleted file mode 100644 index e20884c0a3..0000000000 --- a/lib/snmp/src/agent/snmpa_mib_data_tree2.erl +++ /dev/null @@ -1,1389 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(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}). - - -%%%----------------------------------------------------------------- -%%% 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 .0 and if it is -%% a table, Oid must be
... -%% 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_ttln.erl b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl new file mode 100644 index 0000000000..e20884c0a3 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl @@ -0,0 +1,1389 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(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}). + + +%%%----------------------------------------------------------------- +%%% 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 .0 and if it is +%% a table, Oid must be
... +%% 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..b80d85d2ee --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl @@ -0,0 +1,1355 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(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}). + + +%%%----------------------------------------------------------------- +%%% 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} +%%----------------------------------------------------------------- + +%% 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 .0 and if it is +%% a table, Oid must be
... +%% 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. + -- cgit v1.2.3 From 8f929fad475601ee3cb164a26db3a9065fa7f909 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Sun, 28 Apr 2013 20:23:44 +0200 Subject: [snmp/agent] Preliminary doc update regarding mib_data behaviour --- lib/snmp/doc/src/files.mk | 1 + lib/snmp/doc/src/notes.xml | 129 ----------------------- lib/snmp/doc/src/snmp_config.xml | 18 +++- lib/snmp/doc/src/snmpa_mib_data.xml | 201 ++++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 131 deletions(-) create mode 100644 lib/snmp/doc/src/snmpa_mib_data.xml diff --git a/lib/snmp/doc/src/files.mk b/lib/snmp/doc/src/files.mk index 61c91c9729..e215143b03 100644 --- a/lib/snmp/doc/src/files.mk +++ b/lib/snmp/doc/src/files.mk @@ -41,6 +41,7 @@ XML_AGENT_REF3_FILES = \ snmpa_error_io.xml \ snmpa_error_logger.xml \ snmpa_local_db.xml \ + snmpa_mib_data.xml \ snmpa_mpd.xml \ snmpa_network_interface.xml \ snmpa_network_interface_filter.xml \ diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 5222922848..b47d4237f5 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -1017,135 +1017,6 @@ -
- SNMP Development Toolkit 4.19 -

Version 4.19 supports code replacement in runtime from/to - version 4.18.

- -
- Improvements and new features - - - -

[compiler] Added support for textual convention - AGENT-CAPABILITIES and "full" support for textual - convention MODULE-COMPLIANCE, both defined by the SNMPv2-CONF - mib.

-

The reference and modules part(s) are - stored in the assocList of the mib-entry (me) - record. - Only handled if the option(s) agent_capabilities - and module_compliance (respectively) are provided to the - compiler.

-

See compile/2 - for more info.

-

For backward compatibillity, the MIBs provided with - this application are not compiled with these - options.

-

Own Id: OTP-8966

-
- - -

[agent] Added a "complete" set of (snmp) table and variable - print functions, for each mib handled by the SNMP (agent) - application. This will be usefull when debugging a running agent.

-

See - print_mib_info/0, - print_mib_tables/0 - and - print_mib_variables/0 - for more info.

-

Own Id: OTP-8977

-
- - -

[compiler] Added a MIB compiler (frontend) escript, - snmpc.

-

Own Id: OTP-9004

-
- -
-
- -
- Fixed Bugs and Malfunctions - - - -

[agent] For the table vacmAccessTable, - when performing the is_set_ok and set operation(s), - all values of the vacmAccessSecurityModel column was - incorrectly translated to any.

- -

Own Id: OTP-8980

-
- - -

[agent] When calling - snmp_view_based_acm_mib:reconfigure/1 - on a running node, the table vacmAccessTable was not properly - cleaned. - This meant that if some entries in the vacm.conf file was removed - (compared to the current config), - while others where modified and/or added, the removed entrie(s) - would still exist in the vacmAccessTable table.

-

Own Id: OTP-8981

-

Aux Id: Seq 11750

-
- -
-
- - -
- Incompatibilities -

-

-
- -
- - -
- SNMP Development Toolkit 4.18 -

Version 4.18 supports code replacement in runtime from/to - version 4.17.1 and 4.17.

- -
- Improvements and new features - - -

Prepared for R14B release.

-
-
-
- -
Fixed Bugs and Malfunctions -

-

- -
- -
- Incompatibilities -

-

-
-
- - + + + + + + -- cgit v1.2.3 From 39096a9e9183df34299914d0983b17815fdf2730 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Sun, 28 Apr 2013 20:25:09 +0200 Subject: [snmp/agent] Some minor mib_data behaviour changes --- lib/snmp/src/agent/snmpa_mib_data.erl | 4 +--- lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index 2872ee2127..599cba4b5b 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -61,11 +61,9 @@ {ok, NewState :: term(), Pid :: pid()} | % When second arg was a oid() {error, Reason :: term()}. --callback dump(State :: term(), Filename :: string()) -> +-callback dump(State :: term(), Destination :: io | filename()) -> ok | {error, Reason :: term()}. --callback print(State :: term()) -> ok. - -callback which_mib(State :: term(), Oid :: oid()) -> {ok, Mib :: string()} | {error, Reason :: term()}. diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl index b80d85d2ee..043dbaef6c 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl @@ -16,7 +16,7 @@ %% %% %CopyrightEnd% %% --module(snmpa_mib_data). +-module(snmpa_mib_data_tttn). %%%----------------------------------------------------------------- %%% This module implements the MIB internal data structures. @@ -479,16 +479,20 @@ old_format(LoadedMibs) -> %%---------------------------------------------------------------------- %% A total dump for debugging. %%---------------------------------------------------------------------- -dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}) -> +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. + ok; -dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, File) -> - case file:open(File,[write]) of +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())]), @@ -499,10 +503,10 @@ dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, File) -> io:format(Fd,"Tree:~n~p~n", [Tree]), % good luck reading it! file:close(Fd), ok; - {error,Reason} -> + {error, Reason} -> ?vinfo("~n Failed opening file '~s' for reason ~p", - [File,Reason]), - {error,Reason} + [File, Reason]), + {error, Reason} end. -- cgit v1.2.3 From 61ac3a2188af0f5d96ef9e1143845a12093acf4c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 30 Apr 2013 23:29:13 +0200 Subject: [snmp/agent] Some minor documentation additions --- lib/snmp/doc/src/snmpa_mib_data.xml | 40 +++++++++++++++++++++++++++++- lib/snmp/src/agent/snmpa_mib_data.erl | 2 +- lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml index 462112ac71..eeeb124d9d 100644 --- a/lib/snmp/doc/src/snmpa_mib_data.xml +++ b/lib/snmp/doc/src/snmpa_mib_data.xml @@ -180,7 +180,7 @@ Find the mib-entry corresponding to the Oid State = term() - Reply = {variable, ME} | {table_column, ME, TEOid} | {subagent, SAPid, SAOid} | [false, Reason} + Reply = {variable, ME} | {table_column, ME, TEOid} | {subagent, SAPid, SAOid} | {false, Reason} Oid = TEOid = SAOid = oid() SAPid = pid() ME = me() @@ -193,8 +193,46 @@ and if it is a table, Oid must be
....

+ + + + + + + next(State, Oid, MibView) -> Reply + Finds the lexicographically next oid + + State = term() + Reply = false | endOfTable | {subagent, SAPid, SAOid} | {variable, ME, VarOid} | {table, TableOid, TableRestOid, ME} + Oid = SAOid = VarOid = TableOid = TableRestOid = oid() + SAPid = pid() + ME = me() + + +

Finds the lexicographically next oid.

+ +
+ + + register_subagent(State, Oid, Pid) -> Reply + Register the subagent + + State = NewState = term() + Reply = {ok, NewState} | {error, Reason} + Oid = oid() + Pid = pid() + Reason = term() + + +

Register the the subagent, process, + handling part of the mib-tree.

+ + +
+
+ diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index 599cba4b5b..790b056065 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -53,7 +53,7 @@ {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()} -callback register_subagent(State :: term(), Oid :: oid(), Pid :: pid()) -> - {error, Reason :: term()} | NewState :: term(). + {ok, NewState :: term()} | {error, Reason :: term()}. -callback unregister_subagent(State :: term(), Pid :: pid() | Oid :: oid()) -> diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl index 043dbaef6c..561e97deee 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl @@ -376,7 +376,7 @@ register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) -> NewRootTree -> SAs = [{Pid, Oid} | MibData#mib_data.subagents], T2 = T#tree{root = NewRootTree}, - MibData#mib_data{tree = T2, subagents = SAs} + {ok, MibData#mib_data{tree = T2, subagents = SAs}} end. -- cgit v1.2.3 From 0a1028e1b2ee703417f1f45ba79cedce05be34c6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 16 May 2013 19:13:56 +0200 Subject: [snmp/agent] Added some description and the behaviour attribute --- lib/snmp/src/agent/snmpa_mib_data_ttln.erl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_data_ttln.erl b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl index e20884c0a3..37dace5194 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_ttln.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% 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 @@ -16,9 +16,13 @@ %% %% %CopyrightEnd% %% --module(snmpa_mib_data). + +-module(snmpa_mib_data_ttln). %%%----------------------------------------------------------------- +%%% +%%% 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. @@ -34,16 +38,16 @@ -include("snmp_types.hrl"). -include("snmp_debug.hrl"). --define(VMODULE,"MDATA"). +-define(VMODULE,"MDATA_TTLN"). -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}). +-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}). %%%----------------------------------------------------------------- -- cgit v1.2.3 From 1a29f0c68813c91385441a45b0cf690cf5d2c7ce Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 16 May 2013 19:17:39 +0200 Subject: [snmp/agent] Fixed snmpa_mib_data behaviour and related Finalized snmpa_mib_data behaviour. Updated mib-server and tttn module accordingly. Also assigned proper version, updated app and appup files. --- lib/snmp/src/agent/modules.mk | 2 +- lib/snmp/src/agent/snmpa_mib.erl | 103 +++++++++++++++-------------- lib/snmp/src/agent/snmpa_mib_data.erl | 59 +++++++++++++++-- lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 72 +++++++++++--------- lib/snmp/src/app/snmp.app.src | 1 + lib/snmp/src/app/snmp.appup.src | 12 ++-- lib/snmp/vsn.mk | 2 +- 7 files changed, 154 insertions(+), 97 deletions(-) diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index 23f53a772c..e03f60120a 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -28,6 +28,7 @@ BEHAVIOUR_MODULES = \ snmpa_notification_filter \ snmpa_set_mechanism +# snmpa_mib_data_ttln MODULES = \ $(BEHAVIOUR_MODULES) \ snmpa \ @@ -44,7 +45,6 @@ MODULES = \ snmpa_local_db \ snmpa_mib \ snmpa_mib_data_tttn \ - snmpa_mib_data_ttln \ snmpa_mib_lib \ snmpa_misc_sup \ snmpa_mpd \ diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl index a5e4a4cb73..1bd7398a69 100644 --- a/lib/snmp/src/agent/snmpa_mib.erl +++ b/lib/snmp/src/agent/snmpa_mib.erl @@ -81,7 +81,7 @@ -record(state, {data, meo, teo, backup, cache, cache_tmr, cache_autogc, cache_gclimit, cache_age, - mib_data_mod}). + data_mod}). @@ -225,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) -> @@ -290,15 +290,15 @@ do_init(Prio, Mibs, Opts) -> MeOverride = get_me_override(Opts), TeOverride = get_te_override(Opts), MibStorage = get_mib_storage(Opts), - MibDataMod = get_mib_data_mod(Otps), - Data = snmpa_mib_data:new(MibStorage), + MibDataMod = get_data_mod(Opts), + Data = MibDataMod:new(MibStorage), ?vtrace("init -> mib data created",[]), 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, @@ -308,7 +308,7 @@ do_init(Prio, Mibs, Opts) -> cache_autogc = CacheAutoGC, cache_gclimit = CacheGcLimit, cache_age = CacheAge, - mib_data_mib = MibDataMib}}; + data_mod = MibDataMod}}; {'aborted at', Mib, _NewData, Reason} -> ?vinfo("failed loading mib ~p: ~p",[Mib,Reason]), {error, {Mib, Reason}} @@ -341,12 +341,12 @@ mib_operation(Mod, Operation, Mib, Data0, MeOverride, TeOverride, Force) when is_list(Mib) -> ?vtrace("mib operation on mib ~p", [Mib]), case apply(Mod, Operation, [Data0, Mib, MeOverride, TeOverride]) of - {error, 'already loaded'} when (Operation =:= load_mib) andalso + {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} -> @@ -401,7 +401,7 @@ handle_call({update_cache_opts, Key, Value}, _From, State) -> {reply, Result, NewState}; handle_call({lookup, Oid}, _From, - #state{data = Data, cache = Cache, mib_data_mod = Mod} = State) -> + #state{data = Data, cache = Cache, data_mod = Mod} = State) -> ?vlog("lookup ~p", [Oid]), Key = {lookup, Oid}, {Reply, NewState} = @@ -421,14 +421,14 @@ handle_call({lookup, Oid}, _From, {reply, Reply, NewState}; handle_call({which_mib, Oid}, _From, - #state{data = Data, mib_data_mod = Mod} = State) -> + #state{data = Data, data_mod = Mod} = State) -> ?vlog("which_mib ~p",[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, mib_data_mod = Mod} = State) -> + #state{data = Data, cache = Cache, data_mod = Mod} = State) -> ?vlog("next ~p [~p]", [Oid, MibView]), Key = {next, Oid, MibView}, {Reply, NewState} = @@ -452,18 +452,18 @@ handle_call({load_mibs, Mibs}, _From, teo = TeOverride, meo = MeOverride, cache = Cache, - mib_data_mod = Mod} = State) -> + data_mod = Mod} = State) -> ?vlog("load mibs ~p",[Mibs]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), - {NData,Reply} = + {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, Mod:sync(NData), {reply, Reply, State#state{data = NData, cache = NewCache}}; @@ -473,38 +473,39 @@ handle_call({unload_mibs, Mibs}, _From, teo = TeOverride, meo = MeOverride, cache = Cache, - mib_data_mod = Mod} = State) -> + data_mod = Mod} = State) -> ?vlog("unload mibs ~p",[Mibs]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), %% Unload mib(s) - {NData,Reply} = + {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, 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 = Mod:which_mibs(Data), {reply, Reply, State}; handle_call({whereis_mib, Mib}, _From, #state{data = Data, - mib_data_mod = Mod} = State) -> + data_mod = Mod} = State) -> ?vlog("whereis mib: ~p",[Mib]), Reply = Mod:whereis_mib(Data, Mib), {reply, Reply, State}; handle_call({register_subagent, Oid, Pid}, _From, - #state{data = Data, cache = Cache, - mib_data_mod = Mod} = State) -> + #state{data = Data, + cache = Cache, + data_mod = Mod} = State) -> ?vlog("register subagent ~p, ~p",[Oid,Pid]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), @@ -512,29 +513,31 @@ handle_call({register_subagent, Oid, Pid}, _From, {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, - mib_data_mod = Mod} = State) -> + #state{data = Data, + cache = Cache, + data_mod = Mod} = State) -> ?vlog("unregister subagent ~p",[OidOrPid]), %% Invalidate cache NewCache = maybe_invalidate_cache(Cache), 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, - mib_data_mod = Mod} = State) -> +handle_call(info, _From, #state{data = Data, + cache = Cache, + data_mod = Mod} = State) -> ?vlog("info",[]), Reply = case (catch Mod:info(Data)) of @@ -546,8 +549,8 @@ handle_call(info, _From, #state{data = Data, cache = Cache, {reply, Reply, State}; handle_call({info, Type}, _From, - #state{data = Data, - mib_data_mod = Mod} = State) -> + #state{data = Data, + data_mod = Mod} = State) -> ?vlog("info ~p",[Type]), Reply = case (catch Mod:info(Data, Type)) of @@ -558,13 +561,8 @@ handle_call({info, Type}, _From, end, {reply, Reply, State}; -handle_call(dump, _From, #state{mib_data_mod = Mod} = _State) -> - ?vlog("dump",[]), - Reply = Mod:dump(State#state.data), - {reply, Reply, State}; - handle_call({dump, File}, _From, - #state{data = Data, mib_data_mod = Mod} = State) -> + #state{data = Data, data_mod = Mod} = State) -> ?vlog("dump on ~s",[File]), Reply = Mod:dump(Data, File), {reply, Reply, State}; @@ -573,8 +571,9 @@ handle_call({dump, File}, _From, %% 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, mib_data_mod = Mod} = State) -> + #state{backup = undefined, + data = Data, + data_mod = Mod} = State) -> ?vlog("backup to ~s", [BackupDir]), Pid = self(), V = get(verbosity), @@ -655,7 +654,7 @@ handle_info(Info, State) -> warning_msg("received unknown info: ~n~p", [Info]), {noreply, State}. -terminate(_Reason, #state{data = Data, mib_data_mod = Mod}) -> +terminate(_Reason, #state{data = Data, data_mod = Mod}) -> catch Mod:close(Data), ok. @@ -673,6 +672,11 @@ terminate(_Reason, #state{data = Data, mib_data_mod = Mod}) -> %% 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) -> @@ -681,12 +685,8 @@ terminate(_Reason, #state{data = Data, mib_data_mod = Mod}) -> %% S2 = #state{data = Data, meo = MEO, teo = TEO, backup = B, cache = Cache}, %% {ok, S2}; -code_change({down, _Vsn}, #state{mib_data_mod = Mod} = State, _Extra) -> - Data = Mod:code_change(down, Data0), - {ok, State#state{data = Data}}; - -code_change(_Vsn, #state{mib_data_mod = Mod} = State, _Extra) -> - Data = Mod:code_change(up, Data0), +code_change(Vsn, #state{data = Data0, data_mod = Mod} = State, Extra) -> + Data = Mod:code_change(up, Vsn, Extra, Data0), {ok, State#state{data = Data}}. @@ -706,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 790b056065..30078db3cf 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -18,11 +18,54 @@ %% -module(snmpa_mib_data). +-include_lib("snmp/include/snmp_types.hrl"). + %%%----------------------------------------------------------------- %%% This is the behaviour for the MIB server backend internal %%% data storage. %%%----------------------------------------------------------------- +%% These types should really be defined elsewhere... +-export_type([ + oid/0, + + mib_storage/0, + mib_storage_dir/0, + mib_storage_action/0, + + mib_view/0, + mib_view_elem/0, + mib_view_mask/0, + mib_view_inclusion/0 + ]). + +-type oid() :: [non_neg_integer()]. + +-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()}. + +-type mib_storage_dir() :: default | string(). +-type mib_storage_action() :: clear | keep. + +-type mib_view() :: [mib_view_elem()]. +-type mib_view_elem() :: {SubTree :: 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 + +-type me() :: #me{}. + +-type filename() :: file:filename(). + -callback new(MibStorage :: mib_storage()) -> State :: term(). @@ -33,12 +76,12 @@ -callback load_mib(State :: term(), FileName :: string(), MeOverride :: boolean(), TeOverride :: boolean()) -> - {ok, NewState :: term()} | {error, already_loaded | Reason :: term()}. + {ok, NewState :: term()} | {error, Reason :: already_loaded | term()}. -callback unload_mib(State :: term(), FileName :: string(), MeOverride :: boolean(), TeOverride :: boolean()) -> - {ok, NewState :: term()} | {error, not_loaded | Reason :: term()}. + {ok, NewState :: term()} | {error, Reason :: not_loaded | term()}. -callback lookup(State :: term(), Oid :: oid()) -> {false, Reason :: term()} | @@ -50,14 +93,14 @@ endOfView | false | {subagent, SubAgentPid :: pid(), SAOid :: oid()} | {variable, MibEntry :: me(), VarOid :: oid()} | - {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()} + {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()}. -callback register_subagent(State :: term(), Oid :: oid(), Pid :: pid()) -> {ok, NewState :: term()} | {error, Reason :: term()}. -callback unregister_subagent(State :: term(), - Pid :: pid() | Oid :: oid()) -> - {ok, NewState :: term()} | % When second arg wa a pid() + PidOrOid :: pid() | oid()) -> + {ok, NewState :: term()} | % When second arg was a pid() {ok, NewState :: term(), Pid :: pid()} | % When second arg was a oid() {error, Reason :: term()}. @@ -74,12 +117,14 @@ {ok, Filename :: string()} | {error, Reason :: term()}. -callback info(State :: term()) -> list(). --callback info(State :: term(), Item :: atom()) -> term(). -callback backup(State :: term(), BackupDir :: string()) -> ok | {error, Reason :: term()}. --callback code_change(Direction :: up | down, State :: term()) -> +-callback code_change(Direction :: up | down, + Vsn :: term(), + Extra :: term(), + State :: term()) -> NewState :: term(). diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl index 561e97deee..c24cff43e5 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.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 @@ -19,6 +19,9 @@ -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. @@ -34,16 +37,16 @@ -include("snmp_types.hrl"). -include("snmp_debug.hrl"). --define(VMODULE,"MDATA"). +-define(VMODULE,"MDATA_TTTN"). -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}). +-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}). %%%----------------------------------------------------------------- @@ -69,6 +72,7 @@ %% 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 @@ -80,16 +84,21 @@ %% API --export([new/0, new/1, sync/1, close/1, - load_mib/4, unload_mib/4, which_mibs/1, whereis_mib/2, +-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, - 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]). + code_change/4]). %%----------------------------------------------------------------- @@ -123,8 +132,6 @@ %% 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) -> @@ -408,10 +415,12 @@ whereis_mib(#mib_data{mib_db = Db}, Name) -> %% Purpose: Deletes SA with Pid from all subtrees it handles. %% Returns: NewMibData. %%---------------------------------------------------------------------- -unregister_subagent(MibData, Pid) when is_pid(Pid) -> +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 -> MibData; + 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), @@ -424,7 +433,7 @@ unregister_subagent(MibData, Pid) when is_pid(Pid) -> %% 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 + 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), @@ -434,7 +443,7 @@ unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) -> MibData#mib_data{tree = T2, subagents = SAs}, Pid}; _ -> - {error, {'invalid oid', Oid}} + {error, {invalid_oid, Oid}} end. %%---------------------------------------------------------------------- @@ -479,6 +488,7 @@ old_format(LoadedMibs) -> %%---------------------------------------------------------------------- %% A total dump for debugging. %%---------------------------------------------------------------------- + dump(#mib_data{mib_db = MibDb, node_db = NodeDb, tree = Tree}, io) -> @@ -1346,14 +1356,16 @@ 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)",[]), +code_change(down, _Vsn, _Extra, State) -> + ?d("code_change(down) -> entry when" + "~n Vsn: ~p" + "~n Extra: ~p", [_Vsn, _Extra]), State; -code_change(_Vsn, State) -> +code_change(up, _Vsn, _Extra, State) -> + ?d("code_change(up) -> entry when" + "~n Vsn: ~p" + "~n Extra: ~p", [_Vsn, _Extra]), State. + diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index b11c1ef934..92a06b0c9f 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -52,6 +52,7 @@ 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/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 4c5f14da90..7ffa4a725d 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,19 +22,15 @@ %% ----- U p g r a d e ------------------------------------------------------- [ - {"4.23", - [ - ] - } + {"4.23.1", [{restart_application, snmp}]}, + {"4.23", [{restart_application, snmp}]} ], %% ------D o w n g r a d e --------------------------------------------------- [ - {"4.23", - [ - ] - } + {"4.23.1", [{restart_application, snmp}]}, + {"4.23", [{restart_application, snmp}]} ] }. diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index fb7aa52402..0e48e7ea56 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.23.1 +SNMP_VSN = 4.24 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" -- cgit v1.2.3 From aa4416d43316b1ac294b07ed4b5299adf7d7c6ad Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 16 May 2013 19:20:46 +0200 Subject: [snmp] Added doc/index.html file to git ignore --- lib/snmp/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/snmp/.gitignore b/lib/snmp/.gitignore index b82d23e7bd..b1afc83df4 100644 --- a/lib/snmp/.gitignore +++ b/lib/snmp/.gitignore @@ -2,3 +2,6 @@ *.BKP +doc/index.html + + -- cgit v1.2.3 From 2d56595da89d9225d150912228cb158e80584f8e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 16 May 2013 19:43:59 +0200 Subject: [snmp/agent] Updated documentation and release notes --- lib/snmp/doc/src/notes.xml | 69 +++++++++++ lib/snmp/doc/src/ref_man.xml | 1 + lib/snmp/doc/src/snmp_app.xml | 27 ++++- lib/snmp/doc/src/snmp_config.xml | 31 +++-- lib/snmp/doc/src/snmpa_mib_data.xml | 232 +++++++++++++++++++++++++++++------- 5 files changed, 305 insertions(+), 55 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index b47d4237f5..2dfe347e42 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -33,6 +33,75 @@ +
+ SNMP Development Toolkit 4.24 +

Version 4.24 supports code replacement in runtime from/to + version 4.23.1 and 4.23.

+ +
+ Improvements and new features + + + + +

[agent] Introduced a documented behaviour for the mib-server + mib-data backend. + At present only the default module (snmpa_mib_data_tttn) is + provided.

+

A config option for the (agent) + mib-servers + mib-data backend module has been added to the agent config options, + data_module.

+

Own Id: OTP-11101

+
+
+ +
+ +
+ Fixed Bugs and Malfunctions + + + + +

[compiler] Now handles MIBs importing the pesudotype BITS.

+

Own Id: OTP-10799

+
+ + +

[compiler] The MIB compiler could not handle a table index + that was defined later in the MIB.

+

Own Id: OTP-10808

+
+ +
+ +
+ +
+ Incompatibilities +

-

+ + +
+ +
+ +
SNMP Development Toolkit 4.23.1

Version 4.23.1 supports code replacement in runtime from/to diff --git a/lib/snmp/doc/src/ref_man.xml b/lib/snmp/doc/src/ref_man.xml index 92e8927f6d..2c12ac665a 100644 --- a/lib/snmp/doc/src/ref_man.xml +++ b/lib/snmp/doc/src/ref_man.xml @@ -44,6 +44,7 @@ + diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index 62dfa515d1..6ce5285a68 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -4,7 +4,7 @@

- 19972012 + 19972013 Ericsson AB. All Rights Reserved. @@ -340,7 +340,7 @@ ]]> -

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()}

+

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {data_module, mib_data_module()}

Defines options specific for the SNMP agent mib server.

For defaults see the options in mib_server_opt().

@@ -365,7 +365,28 @@

Default is false.

- + + + ]]> + +

Defines the backend mib_data storage module of the + SNMP agent mib-server as defined by the + snmpa_mib_data + behaviour.

+

At present only the default module is provided with the agent, + snmpa_mib_data_tttn.

+ +

Default module is snmpa_mib_data_tttn.

+
+ + ]]>

Shall the agent utilize the mib server lookup cache or not.

diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index 42831ecb34..4e2e932b46 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -337,7 +337,7 @@ ]]> -

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {mob_data_mod, mib_data_mod()}

+

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {data_module, mib_data_module()}

Defines options specific for the SNMP agent mib server.

For defaults see the options in mib_server_opt().

@@ -362,28 +362,35 @@

Default is false.

- - ]]> - -

Shall the agent utilize the mib server lookup cache or not.

-

Default is true (in which case the mibs_cache_opts() - default values apply).

-
- - - ]]> + + + ]]>

Defines the backend mib_data storage module of the SNMP agent mib-server as defined by the snmpa_mib_data behaviour.

+

At present only the default module is provided with the agent, + snmpa_mib_data_tttn.

+

Default module is snmpa_mib_data_tttn.

+ + ]]> + +

Shall the agent utilize the mib server lookup cache or not.

+

Default is true (in which case the mibs_cache_opts() + default values apply).

+
+ ]]> diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml index eeeb124d9d..8e79bb1673 100644 --- a/lib/snmp/doc/src/snmpa_mib_data.xml +++ b/lib/snmp/doc/src/snmpa_mib_data.xml @@ -23,10 +23,7 @@ snmpa_mib_data - - - snmpa_mib_data.xml @@ -41,76 +38,89 @@ must export the following functions:

-

new/1

+ new/1
-

close/1

+ close/1
-

sync/1

+ sync/1
-

load_mib/4

+ load_mib/4
-

unload_mib/4

+ unload_mib/4
-

lookup/2

+ lookup/2
-

next/3

+ next/3
-

register_subagent/3

+ register_subagent/3
-

unregister_subagent/2

+ unregister_subagent/2
-

which_mib/2

+ which_mib/2
-

which_mibs/1

+ which_mibs/1
-

whereis_mib/2

+ whereis_mib/2
-

backup/2

+ dump/2
-

dump/2

+ info/1
-

info/1,2

+ backup/2
-

code_change/2

+ code_change/4
-

The semantics of them and their exact signatures are explained - below.

- +

The semantics of them and their exact signatures are + explained below.

+ +

Note that some of the data need to be "passed on" to the + symbolic-store for storage, see the default mib-server data + module, snmpa_mib_data_tttn for details.

+ + +
+ CALLBACK FUNCTIONS +

The following functions must be exported from a + mib-server data callback module:

+ + +
+ - new(Storage) -> State - Create new (mib-storage) instance + Module:new(Storage) -> State + Create new (mib-server) data instance Storage = mib_storage() State = term() -

Create a new mib-storage instance.

+

Create a new mib-server data instance.

- close(State) -> void() + Module:close(State) -> void() Close the mib-storage State = term() @@ -123,13 +133,13 @@ - sync(State) -> void() + Module:sync(State) -> void() Synchronize to disc State = term() -

Synchronize (write to disc, if possible) the data to disc. +

Synchronize (write to disc, if possible) the mib-server data. This depends on the mib_storage option, and will only have an effect if the mib-storage option has an actual disc component (such as dets, or ets with a file).

@@ -139,7 +149,7 @@
- load_mib(State, Filename, MeOverride, TeOverride) -> {ok, NewState} | {error, Reason} + Module:load_mib(State, Filename, MeOverride, TeOverride) -> {ok, NewState} | {error, Reason} Load a mib into the mib-server State = NewState = term() @@ -160,7 +170,7 @@ - unload_mib(State, Filename) -> {ok, NewState} | {error, Reason} + Module:unload_mib(State, Filename) -> {ok, NewState} | {error, Reason} Unload mib from the mib-server State = NewState = term() @@ -176,7 +186,7 @@ - lookup(State, Oid) -> Reply + Module:lookup(State, Oid) -> Reply Find the mib-entry corresponding to the Oid State = term() @@ -187,19 +197,18 @@ Reason = term() -

Find the mib-entry corresponding to the Oid. - If it is a variable, the Oid must be - .0 - and if it is a table, Oid must be -

....

- +

Find 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>.

- next(State, Oid, MibView) -> Reply + Module:next(State, Oid, MibView) -> Reply Finds the lexicographically next oid State = term() @@ -216,7 +225,7 @@ - register_subagent(State, Oid, Pid) -> Reply + Module:register_subagent(State, Oid, Pid) -> Reply Register the subagent State = NewState = term() @@ -226,13 +235,156 @@ Reason = term() -

Register the the subagent, process, +

Register the subagent, process, handling part of the mib-tree.

+ + Module:unregister_subagent(State, PidOrOid) -> Reply + Unregister the subagent + + State = NewState = term() + Reply = {ok, NewState} | {ok, NewState, Pid} | {error, Reason} + PidOrOid = pid() | oid() + Pid = pid() + Reason = term() + + +

Unregister the subagent, handling part of the mib-tree, + as specified by the oid() or pid() + (PidOrOid).

+

When unregister the subagent using an oid(), the pid() + of the process handling the sub-tree is also returned.

+ + +
+
+ + + Module:dump(State, Destination) -> Reply + Unregister the subagent + + State = term() + Reply = ok | {error, Reason} + Destination = io | filename() + Pid = pid() + Reason = term() + + +

Dump the mib-server data to stdio (Destination = io) or + the specified file.

+ + +
+
+ + + Module:which_mib(State, Oid) -> Reply + Retrieve the mib file for an oid() + + State = term() + Reply = {ok, MibFile} | {error, Reason} + Oid = oid() + MibFile = string() + Reason = term() + + +

Retrieve the mib-file to which an given oid() belongs.

+ + +
+
+ + + Module:which_mibs(State) -> Reply + Retrieve all loaded mib files + + State = term() + Reply = [{MibName, Filename}] + MibName = atom() + Filename = string() + + +

Retrieve all loaded mib-files.

+ + +
+
+ + + Module:whereis_mib(State, MibName) -> Reply + Retrieve the mib file for the mib + + State = term() + MibName = atom() + Reply = {ok, Filename} | {error, Reason} + Filename = string() + Reason = term() + + +

Retrieve the mib file for the mib.

+ + +
+
+ + + Module:info(State) -> Reply + Retrieve misc info for the mib data + + State = term() + Reply = {ok, Filename} | {error, Reason} + Filename = string() + Reason = term() + + +

Retrieve misc info for the mib data.

+

This is a utility function used to inspect, for instance, + memory usage, in a simple way.

+ + +
+
+ + + Module:backup(State, BackupDir) -> Reply + Perform a backup of the mib-server data + + State = term() + Reply = ok | {error, Reason} + BackupDir = string() + Reason = term() + + +

Perform a backup of the mib-server data.

+

Note that its implementation dependant (and also + dependent on mib-storage is used) if a backup is possible.

+ + +
+
+ + + Module:code_change(Destination, Vsn, Extra, State) -> NewState + Perform a code-change + + Destination = up | down + Vsn = term() + Extra = term() + State = NewState = term() + + +

Perform a code-change (upgrade or downgrade).

+

See + gen_server + for more info regarding the Vsn and Extra arguments.

+ +
+
+ -- cgit v1.2.3 From 27bfa34b48f365f22f0eb01f821f458b9c00a533 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 16 May 2013 19:44:54 +0200 Subject: [snmp/agent] Fixed some test cases --- lib/snmp/test/snmp_agent_test.erl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 09e1eb25a9..a8e921d67d 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -284,8 +284,10 @@ init_per_group(mib_storage_varm_mnesia = GroupName, Config) -> init_varm_mib_storage_mnesia(snmp_test_lib:init_group_top_dir(GroupName, Config)); init_per_group(mib_storage_varm_dets = GroupName, Config) -> - init_varm_mib_storage_dets(snmp_test_lib:init_group_top_dir(GroupName, - Config)); + ?DBG("init_per_group(mib_storage_varm_dets) -> entry with" + "~n Config: ~p", [Config]), + init_varm_mib_storage_dets( + snmp_test_lib:init_group_top_dir(GroupName, Config)); init_per_group(mib_storage_size_check_mnesia = GroupName, Config) -> init_size_check_msm(snmp_test_lib:init_group_top_dir(GroupName, Config)); init_per_group(mib_storage_size_check_dets = GroupName, Config) -> @@ -353,9 +355,6 @@ init_per_testcase(Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" "~n Config: ~p", [Config]), - p("Agent Info: " - "~n ~p", [snmpa:info()]), - init_per_testcase1(Case, Config). init_per_testcase1(otp8395 = Case, Config) when is_list(Config) -> @@ -400,9 +399,6 @@ end_per_testcase(Case, Config) when is_list(Config) -> ?DBG("end_per_testcase -> entry with" "~n Config: ~p", [Config]), - p("Agent Info: " - "~n ~p", [snmpa:info()]), - display_log(Config), end_per_testcase1(Case, Config). @@ -632,18 +628,20 @@ init_mib_storage_ets(Config) when is_list(Config) -> init_ms(Config, [MibStorage]). init_mib_storage_dets(Config) when is_list(Config) -> - ?LOG("init_mib_storage_ets -> entry", []), + ?LOG("init_mib_storage_dets -> entry", []), ?line AgentDbDir = ?GCONF(agent_db_dir, Config), MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, init_ms(Config, [MibStorage]). init_mib_storage_mnesia(Config) when is_list(Config) -> - ?LOG("init_mib_storage_ets -> entry", []), + ?LOG("init_mib_storage_mnesia -> entry", []), MibStorage = {snmp_mib_storage, {mnesia,[]}}, init_ms(Config, [MibStorage]). init_ms(Config, Opts) when is_list(Config) -> - ?LOG("init_mib_storage_ets -> entry", []), + ?LOG("init_ms -> entry with" + "~n Config: ~p" + "~n Opts: ~p", [Config, Opts]), ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), ?line AgentConfDir = ?GCONF(agent_conf_dir, Config), -- cgit v1.2.3 From 68ce9b1e6be904c092ed558627b36526b2fa1a18 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 17 May 2013 12:57:30 +0200 Subject: [snmp] Added rules to git-ignore to not see patch apply result files --- lib/snmp/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/snmp/.gitignore b/lib/snmp/.gitignore index b1afc83df4..650c1d6865 100644 --- a/lib/snmp/.gitignore +++ b/lib/snmp/.gitignore @@ -1,6 +1,8 @@ # Match at any level. *.BKP +*.orig +*.rej doc/index.html -- cgit v1.2.3 From 6667232f2530684222b3a729cd69da84aa6a7647 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 17 May 2013 13:02:08 +0200 Subject: [snmp/agent] Add comment to TTLN file instead of deleting it --- lib/snmp/src/agent/snmpa_mib_data_ttln.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/snmp/src/agent/snmpa_mib_data_ttln.erl b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl index 37dace5194..d367e8f13f 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_ttln.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_ttln.erl @@ -19,6 +19,13 @@ -module(snmpa_mib_data_ttln). +%%%----------------------------------------------------------------- +%%% +%%% THIS FILE IS JUST A PLACE HOLDER - IGNORE +%%% +%%%----------------------------------------------------------------- + + %%%----------------------------------------------------------------- %%% %%% TTLN - TupleTreeListNodes @@ -34,7 +41,9 @@ %%% %%% 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"). -- cgit v1.2.3 From 2d71b25ef2f0edf15012a5f81210ce7f9fe507e8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 17 May 2013 19:35:57 +0200 Subject: [snmp/agent] Improved mib-server data module overview text --- lib/snmp/doc/src/snmpa_mib_data.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml index 8e79bb1673..4c971ec5d9 100644 --- a/lib/snmp/doc/src/snmpa_mib_data.xml +++ b/lib/snmp/doc/src/snmpa_mib_data.xml @@ -90,8 +90,9 @@

The semantics of them and their exact signatures are explained below.

-

Note that some of the data need to be "passed on" to the - symbolic-store for storage, see the default mib-server data +

Note that the data extracted from the imported (loaded) + mibs are stored partly by the mib-server and partly by the + symbolic-store server. See the default mib-server data module, snmpa_mib_data_tttn for details.

-- cgit v1.2.3 From aea7190a8eec464aecd8dbcbf0e7ebcf23583f5b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 11:44:16 +0200 Subject: [snmp/agent] Fixed basic type issues Defines some basic snmp types in the main snmp api module. Also define some basic snmp agent types in the main snmp agent api module. --- lib/snmp/src/agent/snmpa.erl | 16 +++++++++++++++- lib/snmp/src/agent/snmpa_mib_data.erl | 32 ++++++++++++++------------------ lib/snmp/src/app/snmp.erl | 18 +++++++++++++++++- 3 files changed, 46 insertions(+), 20 deletions(-) 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,12 +111,26 @@ -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 diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index 30078db3cf..ffacdd5b3a 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -27,8 +27,6 @@ %% These types should really be defined elsewhere... -export_type([ - oid/0, - mib_storage/0, mib_storage_dir/0, mib_storage_action/0, @@ -39,8 +37,6 @@ mib_view_inclusion/0 ]). --type oid() :: [non_neg_integer()]. - -type mib_storage() :: ets | {ets, Dir :: mib_storage_dir()} | {ets, Dir :: mib_storage_dir(), Action :: mib_storage_action()} | @@ -56,14 +52,12 @@ -type mib_storage_action() :: clear | keep. -type mib_view() :: [mib_view_elem()]. --type mib_view_elem() :: {SubTree :: oid(), +-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 --type me() :: #me{}. - -type filename() :: file:filename(). @@ -83,23 +77,25 @@ TeOverride :: boolean()) -> {ok, NewState :: term()} | {error, Reason :: not_loaded | term()}. --callback lookup(State :: term(), Oid :: oid()) -> +-callback lookup(State :: term(), Oid :: snmp:oid()) -> {false, Reason :: term()} | - {variable, MibEntry :: me()} | - {table_column, MibEntry :: me(), TableEntryOid :: oid()} | - {subagent, SubAgentPid :: pid(), SAOid :: oid()}. + {variable, MibEntry :: snmpa:me()} | + {table_column, MibEntry :: snmpa:me(), TableEntryOid :: snmp:oid()} | + {subagent, SubAgentPid :: pid(), SAOid :: snmp:oid()}. --callback next(State :: term(), Oid :: oid(), MibView :: mib_view()) -> +-callback next(State :: term(), Oid :: snmp:oid(), MibView :: mib_view()) -> endOfView | false | - {subagent, SubAgentPid :: pid(), SAOid :: oid()} | - {variable, MibEntry :: me(), VarOid :: oid()} | - {table, TableOid :: oid(), TableRestOid :: oid(), MibEntry :: me()}. + {subagent, SubAgentPid :: pid(), SAOid :: snmp:oid()} | + {variable, MibEntry :: snmpa:me(), VarOid :: snmp:oid()} | + {table, TableOid :: snmp:oid(), TableRestOid :: snmp:oid(), MibEntry :: snmpa:me()}. --callback register_subagent(State :: term(), Oid :: oid(), Pid :: pid()) -> +-callback register_subagent(State :: term(), + Oid :: snmp:oid(), + Pid :: pid()) -> {ok, NewState :: term()} | {error, Reason :: term()}. -callback unregister_subagent(State :: term(), - PidOrOid :: pid() | oid()) -> + 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()}. @@ -107,7 +103,7 @@ -callback dump(State :: term(), Destination :: io | filename()) -> ok | {error, Reason :: term()}. --callback which_mib(State :: term(), Oid :: oid()) -> +-callback which_mib(State :: term(), Oid :: snmp:oid()) -> {ok, Mib :: string()} | {error, Reason :: term()}. -callback which_mibs(State :: term()) -> diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl index cd3e3a0055..1bb562654a 100644 --- a/lib/snmp/src/app/snmp.erl +++ b/lib/snmp/src/app/snmp.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 @@ -90,6 +90,13 @@ ]). +-export_type([ + oid/0, + + void/0 + ]). + + %% This is for XREF -deprecated([{c, 1, eventually}, {c, 2, eventually}, @@ -143,6 +150,15 @@ -define(APPLICATION, snmp). + +%%----------------------------------------------------------------- +%% Types +%%----------------------------------------------------------------- + +-type oid() :: [non_neg_integer()]. +-type void() :: term(). + + %%----------------------------------------------------------------- %% Application %%----------------------------------------------------------------- -- cgit v1.2.3 From 4c91ed638f30c4ef0bd97d5aa7252020814bc7d4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 11:46:28 +0200 Subject: [snmp/agent] Fixed copyright end date --- lib/snmp/src/app/snmp.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index 92a06b0c9f..dc5acbd4a0 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -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 -- cgit v1.2.3 From 3cac8c978bf944e532c91866a219fa4f86310f4a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 11:47:32 +0200 Subject: [snmp/agent] Make sure main api module is compiled first The main snmp agent api module contains some basic type defs and therefor it must be compiled first. --- lib/snmp/src/agent/modules.mk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index e03f60120a..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 @@ -28,10 +28,12 @@ BEHAVIOUR_MODULES = \ 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 \ -- cgit v1.2.3 From 9387edd3b1b642170d7788256c5ada6748e35f27 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 11:50:01 +0200 Subject: [snmp/agent] Add (make) deepend for the new mib-server data module Add (make) depend rule for the new mib-server data module, snmpa_mib_datas_tttn. Also corrected the depend rule for the mib-server data module behaviour module (snmpa_mib_data). --- lib/snmp/src/agent/depend.mk | 4 ++++ 1 file changed, 4 insertions(+) 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 -- cgit v1.2.3 From 3ffa5b5c87518b6579f48b935cb67c7eb22b10b4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 11:52:14 +0200 Subject: [snmp/agent] Some mib-server options rewording --- lib/snmp/doc/src/snmp_app.xml | 32 ++++++++++++++++---------------- lib/snmp/doc/src/snmp_config.xml | 34 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index 6ce5285a68..c7a74f0ca6 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -340,7 +340,7 @@ ]]> -

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {data_module, mib_data_module()}

+

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {data_module, mib_server_data_module()}

Defines options specific for the SNMP agent mib server.

For defaults see the options in mib_server_opt().

@@ -367,12 +367,12 @@ - ]]> + ]]> -

Defines the backend mib_data storage module of the - SNMP agent mib-server as defined by the +

Defines the backend data module of the SNMP agent mib-server as + defined by the snmpa_mib_data behaviour.

At present only the default module is provided with the agent, @@ -406,30 +406,30 @@ ]]>

Defines if the mib server shall perform cache gc automatically or - leave it to the user (see - gc_mibs_cache/0,1,2,3).

+ leave it to the user (see + gc_mibs_cache/0,1,2,3).

Default is true.

0 ]]> -

Defines how old the entries in the cache will be allowed before - they are GC'ed (assuming GC is performed). Each entry in the - cache is "touched" whenever it is accessed.

-

The age is defined in milliseconds.

-

Default is 10 timutes.

+

Defines how old the entries in the cache will be allowed + to become before they are GC'ed (assuming GC is performed). + Each entry in the cache is "touched" whenever it is accessed.

+

The age is defined in milliseconds.

+

Default is 10 timutes.

0 | infinity ]]>

When performing a GC, this is the max number of cache entries - that will be deleted from the cache.

+ that will be deleted from the cache.

The reason for having this limit is that if the cache is - large, the GC can potentially take a long time, during which - the agent is locked.

-

Default is 100.

+ large, the GC can potentially take a long time, during which + the agent is locked.

+

Default is 100.

diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index 4e2e932b46..28bfcbb3de 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -337,7 +337,7 @@ ]]> -

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {data_module, mib_data_module()}

+

mib_server_opt() = {mibentry_override, mibentry_override()} | {trapentry_override, trapentry_override()} | {verbosity, verbosity()} | {cache, mibs_cache()} | {data_module, mib_server_data_module()}

Defines options specific for the SNMP agent mib server.

For defaults see the options in mib_server_opt().

@@ -364,12 +364,12 @@ - ]]> + ]]> -

Defines the backend mib_data storage module of the - SNMP agent mib-server as defined by the +

Defines the backend data module of the SNMP agent mib-server as + defined by the snmpa_mib_data behaviour.

At present only the default module is provided with the agent, @@ -403,18 +403,18 @@ ]]>

Defines if the mib server shall perform cache gc automatically or - leave it to the user (see - gc_mibs_cache/0,1,2,3).

-

Default is true.

+ leave it to the user (see + gc_mibs_cache/0,1,2,3).

+

Default is true.

0 ]]> -

Defines how old the entries in the cache will be allowed before - they are GC'ed (assuming GC is performed). Each entry in the - cache is "touched" whenever it is accessed.

-

The age is defined in milliseconds.

+

Defines how old the entries in the cache will be allowed + to become before they are GC'ed (assuming GC is performed). + Each entry in the cache is "touched" whenever it is accessed.

+

The age is defined in milliseconds.

Default is 10 timutes.

@@ -422,11 +422,11 @@ 0 | infinity ]]>

When performing a GC, this is the max number of cache entries - that will be deleted from the cache.

-

The reason for having this limit is that if the cache is - large, the GC can potentially take a long time, during which - the agent is locked.

-

Default is 100.

+ that will be deleted from the cache.

+

The reason for having this limit is that if the cache is + large, the GC can potentially take a long time, during which + the agent is locked.

+

Default is 100.

-- cgit v1.2.3 From 31b06641f692641f82c489069584220609e1e840 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 11:17:44 +0200 Subject: [snmp/agent] backup --- lib/snmp/doc/src/snmp_app.xml | 48 ++-- lib/snmp/src/agent/depend.mk | 12 + lib/snmp/src/agent/modules.mk | 4 + lib/snmp/src/agent/snmpa_mib_storage.erl | 181 ++++++++++++++ lib/snmp/src/agent/snmpa_mib_storage_dets.erl | 298 ++++++++++++++++++++++++ lib/snmp/src/agent/snmpa_mib_storage_ets.erl | 298 ++++++++++++++++++++++++ lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl | 265 +++++++++++++++++++++ lib/snmp/src/agent/snmpa_supervisor.erl | 103 ++++++-- 8 files changed, 1172 insertions(+), 37 deletions(-) create mode 100644 lib/snmp/src/agent/snmpa_mib_storage.erl create mode 100644 lib/snmp/src/agent/snmpa_mib_storage_dets.erl create mode 100644 lib/snmp/src/agent/snmpa_mib_storage_ets.erl create mode 100644 lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index c7a74f0ca6..9cdbcc91c5 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -312,29 +312,31 @@ - ]]> - -

Specifies how info retrieved from the mibs will be stored.

-

If mib_storage is {ets, Dir}, the table will also be - stored on file. If Dir is default, then db_dir - will be used.

-

If mib_storage is dets or if Dir is - default, then db_dir will be used for Dir.

-

If mib_storage is mnesia then erlang:nodes() - will be used for Nodes.

-

Default is ets.

-

Dir = default | string(). Dir is the directory where the - files will be stored. If default, then db_dir will be - used.

-

Nodes = visible | connected | [node()]. - Nodes = visible is translated to - erlang:nodes(visible). - Nodes = connected is translated to - erlang:nodes(connected). - If Nodes = [] then the own node is assumed.

-

Action = clear | keep. Default is keep. - Action is used to specify what shall be done if the - mnesia/dets table already exist.

+ + +

mib_storage_opt() = {module, mib_storage_module()} | {options, list()}

+

This option specifies how basic mib data is stored. + This option is used by two parts of the snmp agent: + The mib-server and the symbolic-store.

+

Default is [{module, snmpa_mib_storage_ets}].

+
+ + + ]]> + +

Defines the callback mib data storage module of the + SNMP agent mib-server as defined by the + snmpa_mib_data + behaviour.

+

At present only the default module is provided with the agent, + snmpa_mib_data_tttn.

+ +

Default module is snmpa_mib_data_tttn.

diff --git a/lib/snmp/src/agent/depend.mk b/lib/snmp/src/agent/depend.mk index 096182a626..283de54078 100644 --- a/lib/snmp/src/agent/depend.mk +++ b/lib/snmp/src/agent/depend.mk @@ -88,6 +88,18 @@ $(EBIN)/snmpa_local_db.$(EMULATOR): \ ../../include/snmp_types.hrl \ ../../include/STANDARD-MIB.hrl +$(EBIN)/snmpa_mib_storage.$(EMULATOR): \ + snmpa_mib_storage.erl + +$(EBIN)/snmpa_mib_storage_ets.$(EMULATOR): \ + snmpa_mib_storage_ets.erl + +$(EBIN)/snmpa_mib_storage_dets.$(EMULATOR): \ + snmpa_mib_storage_dets.erl + +$(EBIN)/snmpa_mib_storage_mnesia.$(EMULATOR): \ + snmpa_mib_storage_mnesia.erl + $(EBIN)/snmpa_mib.$(EMULATOR): \ snmpa_mib.erl \ ../misc/snmp_debug.hrl \ diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index da9f6d6b4c..e8d1593a79 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -21,6 +21,7 @@ BEHAVIOUR_MODULES = \ snmpa_authentication_service \ snmpa_discovery_handler \ snmpa_error_report \ + snmpa_mib_storage \ snmpa_mib_data \ snmpa_network_interface \ snmpa_network_interface_filter \ @@ -45,6 +46,9 @@ MODULES = \ snmpa_error_logger \ snmpa_general_db \ snmpa_local_db \ + snmpa_mib_storage_ets \ + snmpa_mib_storage_dets \ + snmpa_mib_storage_mnesia \ snmpa_mib \ snmpa_mib_data_tttn \ snmpa_mib_lib \ diff --git a/lib/snmp/src/agent/snmpa_mib_storage.erl b/lib/snmp/src/agent/snmpa_mib_storage.erl new file mode 100644 index 0000000000..bbb9516ecb --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_storage.erl @@ -0,0 +1,181 @@ +%% +%% %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_storage). + +-export_type([ + mib_storage_fields/0, + mib_storage_table_type/0, + mib_storage_table_id/0, + + void/0 + ]). + + +%%% ---------------------------------------------------------------- +%%% This behaviour module defines the API for the mib-storage. +%%% This is how the agent stores its internal mib-data +%%% (symbolic-store and mib-server). +%%%----------------------------------------------------------------- + +-type mib_storage_fields() :: [atom()]. +-type mib_storage_table_type() :: set | bag. +-type mib_storage_table_id() :: term(). +-type void() :: term(). + + +%% --------------------------------------------------------------- +%% open +%% +%% Open or create a mib-storage table. +%% If any extra info needs to be communicated to the implementor +%% (of the behaviour), this is done using the *Options* argument. +%% --------------------------------------------------------------- + +%% Options is callback module dependant + +-callback open(Name :: atom(), + RecName :: atom(), + Fields :: mib_storage_fields(), + Type :: mib_storage_table_type(), + Options :: list()) -> + {ok, TabId :: mib_storage_table_id()} | {error, Reason :: term()}. + + +%% --------------------------------------------------------------- +%% close +%% +%% Close the mib-storage table. What this does is up to the +%% implementor (when using mnesia it may be a no-op but for ets +%% it may actually delete the table). +%% --------------------------------------------------------------- + +-callback close(TabId :: mib_storage_table_id()) -> + term(). + + +%% --------------------------------------------------------------- +%% read/2 +%% +%% Retrieve a record from the mib-storage table. +%% --------------------------------------------------------------- + +-callback read(TabId :: mib_storage_table_id(), + Key :: term()) -> + false | {value, Record :: tuple()}. + + +%% --------------------------------------------------------------- +%% write/2 +%% +%% Write a record to the mib-storage table. +%% --------------------------------------------------------------- + +-callback write(TabId :: mib_storage_table_id(), + Record :: tuple()) -> + ok | {error, Reason :: term()}. + + +%% --------------------------------------------------------------- +%% delete/1 +%% +%% Delete the mib-storage table. +%% --------------------------------------------------------------- + +-callback delete(TabId :: mib_storage_table_id()) -> + void(). + + +%% --------------------------------------------------------------- +%% delete/2 +%% +%% Delete a record from the mib-storage table. +%% --------------------------------------------------------------- + +-callback delete(TabId :: mib_storage_table_id(), + Key :: term()) -> + ok | {error, Reason :: term()}. + + +%% --------------------------------------------------------------- +%% match_object +%% +%% Search the mib-storage table for records which matches +%% the pattern. +%% --------------------------------------------------------------- + +-callback match_object(TabId :: mib_storage_table_id(), + Pattern :: ets:match_pattern()) -> + {ok, Recs :: [tuple()]} | {error, Reason :: term()}. + + +%% --------------------------------------------------------------- +%% match_delete +%% +%% Search the mib-storage table for records which matches the +%% pattern and deletes them from the database and return the +%5 deleted records. +%% --------------------------------------------------------------- + +-callback match_delete(TabId :: mib_storage_table_id(), + Pattern :: ets:match_pattern()) -> + {ok, Recs :: [tuple()]} | {error, Reason :: term()}. + + +%% --------------------------------------------------------------- +%% tab2list +%% +%% Return all records in the table in the form of a list. +%% --------------------------------------------------------------- + +-callback tab2list(TabId :: mib_storage_table_id()) -> + [tuple()]. + + +%% --------------------------------------------------------------- +%% info +%% +%% Retrieve implementation dependent mib-storage table +%% information. +%% --------------------------------------------------------------- + +-callback info(TabId :: mib_storage_table_id()) -> + {ok, Info :: term()} | {error, Reason :: term()}. + + +%% --------------------------------------------------------------- +%% sync +%% +%% Dump mib-storage table to disc (if it has a disk component). +%% --------------------------------------------------------------- + +-callback sync(TabId :: mib_storage_table_id()) -> + ok. + + +%% --------------------------------------------------------------- +%% backup +%% +%% Make a backup copy of the mib-storage table. +%% --------------------------------------------------------------- + +-callback backup(TabId :: mib_storage_table_id(), + Dir :: file:filename()) -> + ok | {error, Reason :: term()}. + diff --git a/lib/snmp/src/agent/snmpa_mib_storage_dets.erl b/lib/snmp/src/agent/snmpa_mib_storage_dets.erl new file mode 100644 index 0000000000..6062f4327e --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_storage_dets.erl @@ -0,0 +1,298 @@ +%% +%% %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_storage_dets). + +-behaviour(snmpa_mib_storage). + + +%%%----------------------------------------------------------------- +%%% This module implements the snmpa_mib_storage behaviour. +%%% It uses dets for storage. +%%%----------------------------------------------------------------- + +-export([ + open/5, + close/1, + read/2, + write/2, + delete/1, + delete/2, + sync/1, + backup/2, + match_object/2, + match_delete/2, + tab2list/1, + info/1 + ]). + + +-define(VMODULE, "MS-DETS"). +-include("snmp_verbosity.hrl"). + +-record(tab, {id}). + + +%% --------------------------------------------------------------- +%% open +%% +%% Open or create a mib-storage (dets) table. +%% +%% Opts - A list of implementation dependent options +%% dets_open_options() = [dets_open_option()] +%% dets_open_option() = {dir, filename()} | +%% {action, keep | clear} | +%% {auto_save, default | pos_integer()} | +%% {repair, force | boolean()} +%% +%% --------------------------------------------------------------- + +open(Name, _RecName, _Fields, Type, Opts) -> + Dir = snmp_misc:get_option(dir, Opts), + Action = snmp_misc:get_option(action, Opts, keep), + AutoSave = snmp_misc:get_option(auto_save, Opts, default), + Repair = snmp_misc:get_option(repair, Opts, false), + File = dets_filename(Name, Dir), + OpenOpts = [{file, File}, + {type, Type}, + {keypos, 2}, + {repair, Repair}] ++ + case AutoSave of + default -> + []; + _ -> + [{auto_save, AutoSave}] + end, + case dets:open_file(Name, OpenOpts) of + {ok, ID} when (Action =:= keep) -> + {ok, #tab{id = ID}}; + {ok, ID} when (Action =:= clear) -> + dets:match_delete(ID, '_'), + {ok, #tab{id = ID}}; + {error, Reason} -> + {error, {dets_open, Reason}} + end. + +dets_filename(Name, Dir) -> + Dir1 = dets_filename1(Dir), + Dir2 = string:strip(Dir1, right, $/), + io_lib:format("~s/~p.dat", [Dir2, Name]). + +dets_filename1([]) -> "."; +dets_filename1(Dir) -> Dir. + + +%% --------------------------------------------------------------- +%% close +%% +%% Close the table. +%% --------------------------------------------------------------- + +close(#tab{id = ID}) -> + ?vtrace("close database ~p", [ID]), + dets:close(ID). + + +%% --------------------------------------------------------------- +%% read +%% +%% Retrieve a record from the database table. +%% --------------------------------------------------------------- + +read(#tab{id = ID}, Key) -> + ?vtrace("read from table ~p: ~p", [ID, Key]), + case dets:lookup(ID, Key) of + [Rec|_] -> {value, Rec}; + _ -> false + end. + + +%% --------------------------------------------------------------- +%% write +%% +%% Write a record to the database table. +%% --------------------------------------------------------------- + +write(#tab{id = ID}, Rec) -> + ?vtrace("write to table ~p", [ID]), + dets:insert(ID, Rec). + + +%% --------------------------------------------------------------- +%% delete +%% +%% Delete the database table. +%% --------------------------------------------------------------- + +delete(#tab{id = ID}) -> + ?vtrace("delete database ~p", [ID]), + File = dets:info(ID, filename), + case dets:close(ID) of + ok -> + file:delete(File); + Error -> + Error + end. + + +%% --------------------------------------------------------------- +%% delete +%% +%% Delete a record from the database table. +%% --------------------------------------------------------------- + +delete(#tab{id = ID}, Key) -> + ?vtrace("delete from table ~p: ~p", [ID, Key]), + dets:delete(ID, Key). + + +%% --------------------------------------------------------------- +%% match_object +%% +%% Search the database table for records witch matches the pattern. +%% --------------------------------------------------------------- + +match_object(#tab{id = ID}, Pattern) -> + ?vtrace("match_object in ~p of ~p", [ID, Pattern]), + dets:match_object(ID, Pattern). + + +%% --------------------------------------------------------------- +%% match_delete +%% +%% Search the database table for records witch matches the +%% pattern and deletes them from the database table. +%% --------------------------------------------------------------- + +match_delete(#tab{id = ID}, Pattern) -> + ?vtrace("match_delete in ~p with pattern ~p", [ID, Pattern]), + Recs = dets:match_object(ID, Pattern), + dets:match_delete(ID, Pattern), + Recs. + + +%% --------------------------------------------------------------- +%% tab2list +%% +%% Return all records in the table in the form of a list. +%% --------------------------------------------------------------- + +tab2list(#tab{id = ID} = Tab) -> + ?vtrace("tab2list -> list of ~p", [ID]), + match_object(Tab, '_'). + + +%% --------------------------------------------------------------- +%% info +%% +%% Retrieve implementation dependent mib-storage table +%% information. +%% --------------------------------------------------------------- + +info(#tab{id = ID}) -> + ?vtrace("info -> info of ~p", [ID]), + dets:info(ID). + + +%% --------------------------------------------------------------- +%% sync +%% +%% Dump mib-storage table to disc (if it has a disk component) +%% --------------------------------------------------------------- + +sync(#tab{id = ID}) -> + ?vtrace("sync -> sync ~p", [ID]), + dets:sync(ID). + + +%% --------------------------------------------------------------- +%% backup +%% +%% Make a backup copy of the mib-storage table. +%% --------------------------------------------------------------- + +backup(#tab{id = ID}, BackupDir) -> + ?vtrace("backup -> backup of ~p to ~p", [ID, BackupDir]), + case dets:info(ID, filename) of + undefined -> + {error, no_file}; + Filename -> + case filename:dirname(Filename) of + BackupDir -> + {error, db_dir}; + _ -> + Type = dets:info(ID, type), + KP = dets:info(ID, keypos), + dets_backup(ID, + filename:basename(Filename), + BackupDir, Type, KP) + end + end. + + +dets_backup(ID, Filename, BackupDir, Type, KP) -> + ?vtrace("dets_backup -> entry with" + "~n ID: ~p" + "~n Filename: ~p" + "~n BackupDir: ~p" + "~n Type: ~p" + "~n KP: ~p", [ID, Filename, BackupDir, Type, KP]), + BackupFile = filename:join(BackupDir, Filename), + ?vtrace("dets_backup -> " + "~n BackupFile: ~p", [BackupFile]), + Backup = list_to_atom(atom_to_list(ID) ++ "_backup"), + Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}], + case dets:open_file(Backup, Opts) of + {ok, B} -> + ?vtrace("dets_backup -> create fun", []), + F = fun(Arg) -> + dets_backup(Arg, start, ID, B) + end, + dets:safe_fixtable(ID, true), + Res = dets:init_table(Backup, F, [{format, bchunk}]), + dets:safe_fixtable(ID, false), + ?vtrace("dets_backup -> Res: ~p", [Res]), + Res; + Error -> + ?vinfo("dets_backup -> open_file failed: " + "~n ~p", [Error]), + Error + end. + +dets_backup(close, _Cont, _ID, B) -> + dets:close(B), + ok; +dets_backup(read, Cont1, ID, B) -> + case dets:bchunk(ID, Cont1) of + {Cont2, Data} -> + F = fun(Arg) -> + dets_backup(Arg, Cont2, ID, B) + end, + {Data, F}; + '$end_of_table' -> + dets:close(B), + end_of_input; + Error -> + Error + end. + + +%%---------------------------------------------------------------------- + +%% user_err(F, A) -> +%% snmpa_error:user_err(F, A). diff --git a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl new file mode 100644 index 0000000000..fcf5e12043 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl @@ -0,0 +1,298 @@ +%% +%% %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_storage_ets). + +-behaviour(snmpa_mib_storage). + +%%%----------------------------------------------------------------- +%%% This module implements the snmpa_mib_storage behaviour. +%%% It uses ets for storage. +%%%----------------------------------------------------------------- + +-export([ + open/5, + close/1, + read/2, + write/2, + delete/1, + delete/2, + sync/1, + backup/2, + match_object/2, + match_delete/2, + tab2list/1, + info/1 + ]). + + +-define(VMODULE,"MS-ETS"). +-include("snmp_verbosity.hrl"). + +-record(tab, {id, file, checksum = false}). + + +%% --------------------------------------------------------------- +%% open +%% +%% Open or create an ets table. +%% Possibly also read data from a (specified) file (mirror) and +%% populate the table from that (the dir option). +%% +%% Opts - A list of implementation dependent options +%% ets_open_options() = [ets_open_option()] +%% ets_open_option() = {dir, filename()} | +%% {action, keep | clear} | +%% {checksum, boolean()} +%% +%% The RecName and Fields arguments are not used in this +%% implementation. +%% +%% --------------------------------------------------------------- + +%% This function creates the ets table +open(Name, _RecName, _Fields, Type, Opts) -> + ?vtrace("open table ~p", [Name]), + case lists:keysearch(dir, 1, Opts) of + {value, {dir, Dir}} -> + Action = snmp_misc:get_option(action, Opts, keep), + Checksum = snmp_misc:get_option(checksum, Opts, false), + ?vtrace("open ~p database ~p", [Type, Name]), + File = filename:join(Dir, atom_to_list(Name) ++ ".db"), + case file:read_file_info(File) of + {ok, _} -> + case ets:file2tab(File, [{verify, Checksum}]) of + {ok, ID} -> + {ok, #tab{id = ID, + file = File, + checksum = Checksum}}; + {error, Reason} when (Action =:= keep) -> + {error, {file2tab, Reason}}; + {error, Reason} -> + user_err("Warning: could not read file - " + "create new (empty): " + "~n File: ~p" + "~n Reason: ~p", [File, Reason]), + ID = ets:new(Name, [Type, protected, {keypos, 2}]), + write_ets_file(ID, File, Checksum), + {ok, #tab{id = ID, + file = File, + checksum = Checksum}} + end; + {error, Reason} when (Action =:= keep) -> + {error, {read_file_info, Reason}}; + {error, Reason} -> + user_err("Warning: could not read file info - " + "create new: " + "~n File: ~p" + "~n Reason: ~p", [File, Reason]), + ID = ets:new(Name, [Type, protected, {keypos, 2}]), + write_ets_file(ID, File, Checksum), + {ok, #tab{id = ID, + file = File, + checksum = Checksum}} + end; + false -> + ID = ets:new(Name, [Type, protected, {keypos, 2}]), + {ok, #tab{id = ID}} + end. + + +%% --------------------------------------------------------------- +%% close +%% +%% Close the mib-storage table. +%% We will delete the table and if there is a file component, +%% will also be written to file. +%% --------------------------------------------------------------- +close(#tab{id = ID, file = undefined}) -> + ?vtrace("close (delete) table ~p", [ID]), + ets:delete(ID); +close(#tab{id = ID, file = File, checksum = Checksum}) -> + ?vtrace("close (delete) table ~p", [ID]), + write_ets_file(ID, File, Checksum), + ets:delete(ID). + + +%% --------------------------------------------------------------- +%% read +%% +%% Retrieve a record from the mib-storage table. +%% --------------------------------------------------------------- + +read(#tab{id = ID}, Key) -> + ?vtrace("read from table ~p: ~p", [ID, Key]), + case ets:lookup(ID, Key) of + [Rec|_] -> {value, Rec}; + _ -> false + end. + + +%% --------------------------------------------------------------- +%% write +%% +%% Write a record to the mib-storage table. +%% --------------------------------------------------------------- + +write(#tab{id = ID}, Rec) -> + ?vtrace("write to table ~p", [ID]), + ets:insert(ID, Rec). + + +%% --------------------------------------------------------------- +%% delete +%% +%% Delete the mib-storage table. +%% --------------------------------------------------------------- +delete(#tab{id = ID, file = undefined}) -> + ?vtrace("delete table ~p", [ID]), + ets:delete(ID); +delete(#tab{id = ID, file = File}) -> + ?vtrace("delete table ~p", [ID]), + file:delete(File), + ets:delete(ID). + + +%% --------------------------------------------------------------- +%% delete +%% +%% Delete a record from the mib-storage table. +%% --------------------------------------------------------------- +delete(#tab{id = ID}, Key) -> + ?vtrace("delete from table ~p: ~p", [ID, Key]), + ets:delete(ID, Key). + + +%% --------------------------------------------------------------- +%% match_object +%% +%% Search the mib-storage table for records witch matches +%% the pattern. +%% --------------------------------------------------------------- + +match_object(#tab{id = ID}, Pattern) -> + ?vtrace("match_object in ~p of ~p", [ID, Pattern]), + ets:match_object(ID, Pattern). + + +%% --------------------------------------------------------------- +%% match_delete +%% +%% Search the mib-storage table for records witch matches +%% the pattern and deletes them from the table. +%% --------------------------------------------------------------- + +match_delete(#tab{id = ID}, Pattern) -> + ?vtrace("match_delete in ~p with pattern ~p", [ID, Pattern]), + Recs = ets:match_object(ID, Pattern), + ets:match_delete(ID, Pattern), + Recs. + + +%% --------------------------------------------------------------- +%% tab2list +%% +%% Return all records in the mib-storage table in the form +%% of a list. +%% --------------------------------------------------------------- + +tab2list(#tab{id = ID}) -> + ?vtrace("tab2list -> list of ~p", [ID]), + ets:tab2list(ID). + + + +%% --------------------------------------------------------------- +%% info +%% +%% Retrieve implementation dependent mib-storage table +%% information. +%% --------------------------------------------------------------- +info(#tab{id = ID}) -> + ?vtrace("info on ~p", [ID]), + case ets:info(ID) of + undefined -> + []; + L -> + L + end. + + +%% --------------------------------------------------------------- +%% sync +%% +%% Dump mib-storage table to disc (if there is a file compionent) +%% --------------------------------------------------------------- + +sync(#tab{file = undefined}) -> + ok; +sync(#tab{id = ID, file = File, checksum = Checksum}) -> + ?vtrace("sync ~p", [ID]), + write_ets_file(ID, File, Checksum). + + +%% --------------------------------------------------------------- +%% backup +%% +%% Make a backup copy of the mib-storage table. Only valid id +%% there is a file component. +%% --------------------------------------------------------------- + +backup(#tab{file = undefined}, _BackupDir) -> + ok; +backup(#tab{id = ID, file = File, checksum = Checksum}, BackupDir) -> + ?vtrace("backup ~p to ~p", [ID, BackupDir]), + Filename = filename:basename(File), + case filename:join(BackupDir, Filename) of + File -> + %% Oups: backup-dir and db-dir the same + {error, db_dir}; + BackupFile -> + write_ets_file(ID, BackupFile, Checksum) + end. + + +%%---------------------------------------------------------------------- + +write_ets_file(ID, File, Checksum) when (Checksum =:= true) -> + do_write_ets_file(ID, File, [{extended_info, [md5sum]}]); +write_ets_file(ID, File, Checksum) when (Checksum =:= false) -> + do_write_ets_file(ID, File, []). + +do_write_ets_file(ID, File, Options) -> + TmpFile = File ++ ".tmp", + case ets:tab2file(ID, TmpFile, Options) of + ok -> + case file:rename(TmpFile, File) of + ok -> + ok; + Else -> + user_err("Warning: could not move file ~p" + " (~p)", [File, Else]) + end; + {error, Reason} -> + user_err("Warning: could not save file ~p (~p)", + [File, Reason]) + end. + + +%%---------------------------------------------------------------------- + +user_err(F, A) -> + snmpa_error:user_err(F, A). diff --git a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl new file mode 100644 index 0000000000..aee2192b24 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl @@ -0,0 +1,265 @@ +%% +%% %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_storage_mnesia). + + +-behaviour(snmpa_mib_storage). + +%%%----------------------------------------------------------------- +%%% This module implements the snmpa_mib_storage behaviour. +%%% It uses mnesia for storage. +%%%----------------------------------------------------------------- + +-export([ + open/5, + close/1, + read/2, + write/2, + delete/1, + delete/2, + sync/1, + backup/2, + match_object/2, + match_delete/2, + tab2list/1, + info/1 + ]). + + +-define(VMODULE,"MS-MNESIA"). +-include("snmp_verbosity.hrl"). + +-record(tab, {id}). + + +%% --------------------------------------------------------------- +%% open +%% +%% Open or create a mnesia table. +%% +%% Opts - A list of implementation dependent options +%% mnesia_open_options() = [mnesia_open_option()] +%% mnesia_open_option() = {action, keep | clear} | +%% {nodes, [node()]} +%% +%% --------------------------------------------------------------- + +open(Name, RecName, Fields, Type, Opts) -> + ?vtrace("open ~p table ~p for record ~p", + [Type, Name, RecName]), + Action = snmp_misc:get_option(action, Opts, keep), + Nodes = snmp_misc:get_option(nodes, Opts, [node()]), + case table_exists(Name) of + true when (Action =:= keep) -> + {ok, #tab{id = Name}}; + true when (Action =:= clear) -> + F = fun() -> mnesia:clear_table(Name) end, + case mnesia:transaction(F) of + {aborted, Reason} -> + {error, {clear, Reason}}; + {atomic, _} -> + {ok, #tab{id = Name}} + end; + false -> + Args = [{record_name, RecName}, + {attributes, Fields}, + {type, Type}, + {disc_copies, Nodes}], + case mnesia:create_table(Name, Args) of + {atomic, ok} -> + {ok, #tab{id = Name}}; + {aborted, Reason} -> + {error, {create, Reason}} + end + end. + +table_exists(Name) -> + case (catch mnesia:table_info(Name, type)) of + {'EXIT', _Reason} -> + false; + _ -> + true + end. + + +%% --------------------------------------------------------------- +%% close +%% +%% Close the mib-storage table. +%% This does nothing in the mnesia case. +%% --------------------------------------------------------------- + +close(_) -> + ?vtrace("close mib-storage - ignore",[]), + ok. + + +%% --------------------------------------------------------------- +%% read +%% +%% Retrieve a record from the mib-storage table. +%% --------------------------------------------------------------- + +read(#tab{id = ID}, Key) -> + ?vtrace("read (dirty) from database ~p: ~p", [ID, Key]), + case (catch mnesia:dirty_read(ID, Key)) of + [Rec|_] -> {value,Rec}; + _ -> false + end. + + +%% --------------------------------------------------------------- +%% write +%% +%% Write a record to the mib-storage table. +%% --------------------------------------------------------------- + +write(#tab{id = ID}, Rec) -> + ?vtrace("write to database ~p", [ID]), + F = fun() -> mnesia:write(ID, Rec, write) end, + case mnesia:transaction(F) of + {aborted, _Reason} = ABORTED -> + {error, ABORTED}; + {atomic,_} -> + ok + end. + + +%% --------------------------------------------------------------- +%% delete +%% +%% Delete the mib-storage table. +%% --------------------------------------------------------------- + +delete(#tab{id = ID}) -> + ?vtrace("delete database: ~p", [ID]), + mnesia:delete_table(ID). + + +%% --------------------------------------------------------------- +%% delete +%% +%% Delete a record from the mib-storage table. +%% --------------------------------------------------------------- + +delete(#tab{id = ID}, Key) -> + ?vtrace("delete from database ~p: ~p", [ID, Key]), + F = fun() -> mnesia:delete(ID, Key, write) end, + case mnesia:transaction(F) of + {aborted, _Reason} = ABORTED -> + {error, ABORTED}; + {atomic, _} -> + ok + end. + + +%% --------------------------------------------------------------- +%% match_object +%% +%% Search the mib-storage table for records witch matches +%% the pattern. +%% --------------------------------------------------------------- + +match_object(#tab{id = ID}, Pattern) -> + ?vtrace("match_object in ~p of ~p", [ID, Pattern]), + F = fun() -> mnesia:match_object(ID, Pattern, read) end, + case mnesia:transaction(F) of + {aborted, _Reason} = ABORTED -> + {error, ABORTED}; + {atomic, Rs} -> + Rs + end. + + +%% --------------------------------------------------------------- +%% match_delete +%% +%% Search the mib-storage table for records witch matches +%% the pattern and deletes them from the table. +%% --------------------------------------------------------------- + +match_delete(#tab{id = ID}, Pattern) -> + ?vtrace("match_delete in ~p with pattern ~p", [ID, Pattern]), + F = fun() -> + Recs = mnesia:match_object(ID, Pattern, read), + lists:foreach(fun(Rec) -> + mnesia:delete_object(ID, Rec, write) + end, Recs), + Recs + end, + case mnesia:transaction(F) of + {aborted, _Reason} = ABORTED -> + {error, ABORTED}; + {atomic, Rs} -> + Rs + end. + + +%% --------------------------------------------------------------- +%% tab2list +%% +%% Return all records in the mib-storage table in the form of +%% a list. +%% --------------------------------------------------------------- + +tab2list(#tab{id = ID} = Tab) -> + ?vtrace("tab2list -> list of ~p", [ID]), + match_object(Tab, mnesia:table_info(ID, wild_pattern)). + + +%% --------------------------------------------------------------- +%% info +%% +%% Retrieve implementation dependent mib-storage table +%% information. +%% --------------------------------------------------------------- + +info(#tab{id = ID}) -> + case (catch mnesia:table_info(ID, all)) of + Info when is_list(Info) -> + Info; + {'EXIT', {aborted, Reason}} -> + {error, Reason} + end. + + +%% --------------------------------------------------------------- +%% sync +%% +%% Ignore +%% --------------------------------------------------------------- + +sync(_) -> + ok. + + +%% --------------------------------------------------------------- +%% backup +%% +%% Ignore. Mnesia handles its own backups. +%% --------------------------------------------------------------- + +backup(_, _) -> + ok. + + +%%---------------------------------------------------------------------- + +%% user_err(F, A) -> +%% snmpa_error:user_err(F, A). diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl index 886fd074bc..1c8db7ff07 100644 --- a/lib/snmp/src/agent/snmpa_supervisor.erl +++ b/lib/snmp/src/agent/snmpa_supervisor.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 @@ -224,25 +224,100 @@ init([AgentType, Opts]) -> ets:insert(snmp_agent_table, {error_report_mod, ErrorReportMod}), %% -- mib storage -- + %% MibStorage has only one mandatory part: module + %% Everything else is module dependent and therefor + %% put in a special option: options MibStorage = - case get_opt(mib_storage, Opts, ets) of + case get_opt(mib_storage, Opts, [{module, snmpa_mib_storage_ets}]) of + + %% --- ETS wrappers --- + + ets -> + [{module, snmpa_mib_storage_ets}]; + {ets, default} -> + [{module, snmpa_mib_storage_ets}, + {options, [{dir, filename:join([DbDir])}, + {action, keep}]}]; + {ets, Dir} when is_list(Dir) -> + [{module, snmpa_mib_storage_ets}, + {options, [{dir, filename:join([Dir])}, + {action, keep}]}]; + {ets, default, Action} when ((Action =:= keep) orelse + (Action =:= clear)) -> + [{module, snmpa_mib_storage_ets}, + {options, [{dir, filename:join([DbDir])}, + {action, Action}]}]; + {ets, Dir, Action} when is_list(Dir) andalso + ((Action =:= keep) orelse + (Action =:= clear)) -> + [{module, snmpa_mib_storage_ets}, + {options, [{dir, filename:join([Dir])}, + {action, Action}]}]; + + %% --- DETS wrappers --- + dets -> - {dets, DbDir}; + [{module, snmpa_mib_storage_dets}, + {options, [{dir, filename:join([DbDir])}, + {action, keep}]}]; {dets, default} -> - {dets, DbDir}; - {dets, default, Act} -> - {dets, DbDir, Act}; - {ets, default} -> - {ets, DbDir}; + [{module, snmpa_mib_storage_dets}, + {options, [{dir, filename:join([DbDir])}, + {action, keep}]}]; + {dets, default, Action} when ((Action =:= keep) orelse + (Action =:= clear)) -> + [{module, snmpa_mib_storage_dets}, + {options, [{dir, filename:join([DbDir])}, + {action, Action}]}]; + {dets, Dir, Action} when is_list(Dir) andalso + ((Action =:= keep) orelse + (Action =:= clear)) -> + [{module, snmpa_mib_storage_dets}, + {options, [{dir, filename:join([Dir])}, + {action, Action}]}]; + + %% --- Mnesia wrappers --- + mnesia -> - {mnesia, erlang:nodes()}; - {mnesia, visible} -> - {mnesia, erlang:nodes(visible)}; - {mnesia, connected} -> - {mnesia, erlang:nodes(connected)}; - Other -> + [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, erlang:nodes()}, + {action, keep}]}]; + {mnesia, Nodes0} -> + Nodes = + if + Nodes0 =:= visible -> + erlang:nodes(visible); + Nodes0 =:= connected -> + erlang:nodes(connected); + Nodes0 =:= [] -> + [node()]; + true -> + Nodes0 + end, + [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, Nodes}, + {action, keep}]}]; + {mnesia, Nodes0, Action} when ((Action =:= keep) orelse + (Action =:= clear)) -> + Nodes = + if + Nodes0 =:= visible -> + erlang:nodes(visible); + Nodes0 =:= connected -> + erlang:nodes(connected); + Nodes0 =:= [] -> + [node()]; + true -> + Nodes0 + end, + [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, Nodes}, + {action, Action}]}]; + + Other when is_list(Other) -> Other end, + ?vdebug("[agent table] store mib storage: ~w",[MibStorage]), ets:insert(snmp_agent_table, {mib_storage, MibStorage}), -- cgit v1.2.3 From 4e7bd62bf3b38b40eee02766b0ad68fdf75fa1c9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 21 May 2013 15:00:42 +0200 Subject: [snmp/agent] Add mib-storage behaviour ref-man documentation --- lib/snmp/doc/src/files.mk | 1 + lib/snmp/doc/src/notes.xml | 13 +++++ lib/snmp/doc/src/ref_man.xml | 1 + lib/snmp/doc/src/snmp_app.xml | 94 +++++++++++++++++++++++++++++------ lib/snmp/doc/src/snmp_config.xml | 98 +++++++++++++++++++++++++++++++++++++ lib/snmp/doc/src/snmpa_mib_data.xml | 2 +- 6 files changed, 193 insertions(+), 16 deletions(-) diff --git a/lib/snmp/doc/src/files.mk b/lib/snmp/doc/src/files.mk index e215143b03..494c550fff 100644 --- a/lib/snmp/doc/src/files.mk +++ b/lib/snmp/doc/src/files.mk @@ -42,6 +42,7 @@ XML_AGENT_REF3_FILES = \ snmpa_error_logger.xml \ snmpa_local_db.xml \ snmpa_mib_data.xml \ + snmpa_mib_storage.xml \ snmpa_mpd.xml \ snmpa_network_interface.xml \ snmpa_network_interface_filter.xml \ diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 2dfe347e42..b6c7c22fb0 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -56,6 +56,19 @@ data_module.

Own Id: OTP-11101

+ + +

[agent] Introduced a documented behaviour for the + mib storage. + At present there are three simple modules + (snmpa_mib_storage_ets, snmpa_mib_storage_dets and + snmpa_mib_storage_mnesia) implementíng this behaviour, + provided with the app.

+

A config option for the (agent) + mib storage + has been added to the agent config options.

+

Own Id: OTP-11107

+
diff --git a/lib/snmp/doc/src/ref_man.xml b/lib/snmp/doc/src/ref_man.xml index 2c12ac665a..628b30b11a 100644 --- a/lib/snmp/doc/src/ref_man.xml +++ b/lib/snmp/doc/src/ref_man.xml @@ -45,6 +45,7 @@ + diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index 9cdbcc91c5..c62c8f1541 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -311,8 +311,8 @@

Default is [].

- - + + ]]>

mib_storage_opt() = {module, mib_storage_module()} | {options, list()}

This option specifies how basic mib data is stored. @@ -322,21 +322,85 @@ - ]]> + -

Defines the callback mib data storage module of the - SNMP agent mib-server as defined by the - snmpa_mib_data +

Defines the mib storage module of the SNMP agent as defined by the + snmpa_mib_storage behaviour.

-

At present only the default module is provided with the agent, - snmpa_mib_data_tttn.

- -

Default module is snmpa_mib_data_tttn.

+

Several entities (mib-server via the its data module and + the symbolic-store) of the snmp agent uses this for storage + of miscelaneous mib data.

+

There are several implementations provided with the agent: + snmpa_mib_storage_ets, snmpa_mib_storage_dets and + snmpa_mib_storage_mnesia.

+

Default module is snmpa_mib_storage_ets.

+
+ + + ]]> + +

This is implementattion depended. That is, it depends on the + module. For each module a specific set of options are valid:

+ + +

snmpa_mib_storage_ets: {dir, filename()} | {action, keep | clear}, {checksum, boolean()}

+ + +

dir - If present, points to a directory where a file + to which all data in the ets table is "synced".

+

Also, when a table is opened this file is read, + if it exists.

+

By default, this will not be used.

+
+ +

action - Specifies the behaviour when a non-empty + file is found: Keep its content or clear it out.

+

Default is keep.

+
+ +

checksum - Defines if the file is checksummed + or not.

+

Default is false.

+
+
+
+ +

snmpa_mib_storage_dets: {dir, filename()} | {action, keep | clear}, {auto_save, default | pos_integer()} | {repair, force | boolean()}

+ + +

dir - This mandatory option points to a + directory where to place the file of a dets table.

+
+ +

action - Specifies the behaviour when a non-empty + file is found: Keep its content or clear it out.

+

Default is keep.

+
+ +

auto_save - Defines the dets auto-save frequency.

+

Default is default.

+
+ +

repair - Defines the dets repair behaviour.

+

Default is false.

+
+
+
+ +

snmpa_mib_storage_mnesia: {action, keep | clear}, {nodes, [node()]}

+ + +

action - Specifies the behaviour when a non-empty, + already existing, table: Keep its content or clear it out.

+

Default is keep.

+
+ +

nodes - Defines where to open the table.

+

Default is the result of the call: erlang:nodes().

+
+
+
+
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index 28bfcbb3de..a88111085f 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -308,6 +308,103 @@

Default is [].

+ + ]]> + +

mib_storage_opt() = {module, mib_storage_module()} | {options, list()}

+

This option specifies how basic mib data is stored. + This option is used by two parts of the snmp agent: + The mib-server and the symbolic-store.

+

Default is [{module, snmpa_mib_storage_ets}].

+
+ + + + +

Defines the mib storage module of the SNMP agent as defined by the + snmpa_mib_storage + behaviour.

+

Several entities (mib-server via the its data module and + the symbolic-store) of the snmp agent uses this for storage + of miscelaneous mib data.

+

There are several implementations provided with the agent: + snmpa_mib_storage_ets, snmpa_mib_storage_dets and + snmpa_mib_storage_mnesia.

+

Default module is snmpa_mib_storage_ets.

+
+ + + ]]> + +

This is implementattion depended. That is, it depends on the + module. For each module a specific set of options are valid:

+ + +

snmpa_mib_storage_ets: {dir, filename()} | {action, keep | clear}, {checksum, boolean()}

+ + +

dir - If present, points to a directory where a file + to which all data in the ets table is "synced".

+

Also, when a table is opened this file is read, + if it exists.

+

By default, this will not be used.

+
+ +

action - Specifies the behaviour when a non-empty + file is found: Keep its content or clear it out.

+

Default is keep.

+
+ +

checksum - Defines if the file is checksummed + or not.

+

Default is false.

+
+
+
+ +

snmpa_mib_storage_dets: {dir, filename()} | {action, keep | clear}, {auto_save, default | pos_integer()} | {repair, force | boolean()}

+ + +

dir - This mandatory option points to a + directory where to place the file of a dets table.

+
+ +

action - Specifies the behaviour when a non-empty + file is found: Keep its content or clear it out.

+

Default is keep.

+
+ +

auto_save - Defines the dets auto-save frequency.

+

Default is default.

+
+ +

repair - Defines the dets repair behaviour.

+

Default is false.

+
+
+
+ +

snmpa_mib_storage_mnesia: {action, keep | clear}, {nodes, [node()]}

+ + +

action - Specifies the behaviour when a non-empty, + already existing, table: Keep its content or clear it out.

+

Default is keep.

+
+ +

nodes - Defines where to open the table.

+

Default is the result of the call: erlang:nodes().

+
+
+
+
+
+ + ]]> diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml index 4c971ec5d9..ff07a03b98 100644 --- a/lib/snmp/doc/src/snmpa_mib_data.xml +++ b/lib/snmp/doc/src/snmpa_mib_data.xml @@ -122,7 +122,7 @@ Module:close(State) -> void() - Close the mib-storage + Close the mib-server data instance State = term() -- cgit v1.2.3 From ed33fe0a82077f3c9120cc8729d1c869a74c5bd8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 13:52:48 +0200 Subject: [snmp/agent] Removed module from app-file and make file(s) The module snmpa_general_db is no longer used (replaced by the behaviour snmpa_mib_storage and the modules implementing this behaviour). --- lib/snmp/src/agent/depend.mk | 4 ---- lib/snmp/src/agent/modules.mk | 1 - lib/snmp/src/app/snmp.app.src | 5 ++++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/snmp/src/agent/depend.mk b/lib/snmp/src/agent/depend.mk index 283de54078..4b12b66e3b 100644 --- a/lib/snmp/src/agent/depend.mk +++ b/lib/snmp/src/agent/depend.mk @@ -77,10 +77,6 @@ $(EBIN)/snmpa_error_logger.$(EMULATOR): \ snmpa_error_report.erl \ snmpa_error_logger.erl -$(EBIN)/snmpa_general_db.$(EMULATOR): \ - snmpa_general_db.erl \ - ../misc/snmp_verbosity.hrl - $(EBIN)/snmpa_local_db.$(EMULATOR): \ snmpa_local_db.erl \ ../misc/snmp_debug.hrl \ diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index e8d1593a79..ea43904f4a 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -44,7 +44,6 @@ MODULES = \ snmpa_error \ snmpa_error_io \ snmpa_error_logger \ - snmpa_general_db \ snmpa_local_db \ snmpa_mib_storage_ets \ snmpa_mib_storage_dets \ diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index dc5acbd4a0..904d17954b 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -48,12 +48,15 @@ snmpa_error_io, snmpa_error_logger, snmpa_error_report, - snmpa_general_db, snmpa_local_db, snmpa_mib, snmpa_mib_data, snmpa_mib_data_tttn, snmpa_mib_lib, + snmpa_mib_storage, + snmpa_mib_storage_ets, + snmpa_mib_storage_dets, + snmpa_mib_storage_mnesia, snmpa_misc_sup, snmpa_mpd, snmpa_net_if, -- cgit v1.2.3 From b90e858649c07e6e744a2adee95f6f2d22cb63f9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 13:55:36 +0200 Subject: [snmp/agent] Deprecate *old* info conversion function --- lib/snmp/src/agent/snmpa.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index 785276c73d..a22f1b2eb4 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -115,6 +115,7 @@ me/0 ]). +-deprecated([{old_info_format, 1}]). -include("snmpa_atl.hrl"). -- cgit v1.2.3 From ba93ba22b3f7092a1a4ea3c10bae1e591162ba21 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 14:00:16 +0200 Subject: [snmp/agent] Add default value for mib_storage for sub-agent When starting a sub-agent we previously did not provide a value for mib_storage, which was alright because ets was assumed as a default in every place where it was used. Now we expect the value to be defined and therefor we must explicitly add the default value for sub-agents when staring them. --- lib/snmp/src/agent/snmpa_agent_sup.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_agent_sup.erl b/lib/snmp/src/agent/snmpa_agent_sup.erl index 9b8c4d12a6..2805e2dc0d 100644 --- a/lib/snmp/src/agent/snmpa_agent_sup.erl +++ b/lib/snmp/src/agent/snmpa_agent_sup.erl @@ -29,10 +29,12 @@ -export([init/1]). -define(SERVER, ?MODULE). +%% Always use plain ets for sub-agents -ifdef(snmp_debug). --define(DEFAULT_OPTS, [{verbosity, trace}]). +-define(DEFAULT_SA_OPTS, [{mib_storage, [{module, snmpa_mib_storage_ets}]}, + {verbosity, trace}]). -else. --define(DEFAULT_OPTS, []). +-define(DEFAULT_SA_OPTS, [{mib_storage, [{module, snmpa_mib_storage_ets}]}]). -endif. @@ -63,8 +65,8 @@ start_subagent(ParentAgent, Subtree, Mibs) -> Ref = make_ref(), ?d("start_subagent -> Ref: ~p", [Ref]), Options = [{priority, Prio}, - {mibs, Mibs}, - {misc_sup, snmpa_misc_sup} | ?DEFAULT_OPTS], + {mibs, Mibs}, + {misc_sup, snmpa_misc_sup} | ?DEFAULT_SA_OPTS], Agent = {{sub_agent, Max}, {snmpa_agent, start_link, [Prio, ParentAgent, Ref, Options]}, -- cgit v1.2.3 From fabfc0acd2a95178b88ce2d72714c6f8ada36558 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 14:06:37 +0200 Subject: [snmp/agent] Mib server assumes no default value for mib_storage --- lib/snmp/src/agent/snmpa_mib.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl index 1bd7398a69..031309b990 100644 --- a/lib/snmp/src/agent/snmpa_mib.erl +++ b/lib/snmp/src/agent/snmpa_mib.erl @@ -257,7 +257,7 @@ init([Prio, Mibs, Opts]) -> do_init(Prio, Mibs, Opts) -> process_flag(priority, Prio), process_flag(trap_exit, true), - put(sname,ms), + put(sname, ms), put(verbosity, ?vvalidate(get_verbosity(Opts))), ?vlog("starting",[]), @@ -291,8 +291,12 @@ do_init(Prio, Mibs, Opts) -> TeOverride = get_te_override(Opts), MibStorage = get_mib_storage(Opts), MibDataMod = get_data_mod(Opts), + ?vtrace("init -> try create mib data with" + "~n MeOverride: ~p" + "~n TeOverride: ~p" + "~n MibStorage: ~p", [MeOverride, TeOverride, MibStorage]), Data = MibDataMod:new(MibStorage), - ?vtrace("init -> mib data created",[]), + ?vdebug("init -> mib data created", []), case (catch mib_operations(MibDataMod, load_mib, Mibs, Data, MeOverride, TeOverride, true)) of @@ -704,7 +708,7 @@ get_te_override(Options) -> get_opt(trapentry_override, Options, false). get_mib_storage(Options) -> - get_opt(mib_storage, Options, ets). + get_opt(mib_storage, Options). get_data_mod(Options) -> get_opt(data_module, Options, snmpa_mib_data_tttn). @@ -894,6 +898,9 @@ timestamp() -> %% ---------------------------------------------------------------- +get_opt(Key, Options) -> + snmp_misc:get_option(Key, Options). + get_opt(Key, Options, Default) -> snmp_misc:get_option(Key, Options, Default). -- cgit v1.2.3 From cac3dbfed3b3f703a012f52cd7093392a70a53cc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 14:07:30 +0200 Subject: [snmp/agent] Make use of new mib_storage The new mib-storage is now used by both the mib-server and the symbolic-store. --- lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 293 +++++++++++++++++----------- lib/snmp/src/agent/snmpa_symbolic_store.erl | 283 +++++++++++++++------------ 2 files changed, 337 insertions(+), 239 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl index c24cff43e5..f0bd5dabfd 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl @@ -18,6 +18,7 @@ %% -module(snmpa_mib_data_tttn). + %%%----------------------------------------------------------------- %%% %%% TTTN - TupleTreeTupleNodes @@ -33,7 +34,9 @@ %%% %%% 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"). @@ -73,11 +76,15 @@ %% 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_data, + { + module, % Mib storage module + 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}). @@ -121,7 +128,7 @@ %% This record is what is stored in the database. The 'tree' part %% is described above... --record(tree,{generation = ?DUMMY_TREE_GENERATION, root = ?DEFAULT_TREE}). +-record(tree, {generation = ?DUMMY_TREE_GENERATION, root = ?DEFAULT_TREE}). %%%====================================================================== @@ -134,32 +141,54 @@ %%----------------------------------------------------------------- %% Where -> A list of nodes where the tables will be created -new(Storage) -> +new(MibStorage) -> + Mod = snmp_misc:get_option(module, MibStorage), + Opts = snmp_misc:get_option(options, MibStorage, []), + %% 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), + MibDb = + case Mod:open(?MIB_DATA, mib_info, record_info(fields, mib_info), + set, Opts) of + {ok, T1} -> + T1; + {error, Reason1} -> + throw({error, {open, mib_data, Reason1}}) + end, + ?vtrace("open (mib) node database",[]), - NodeDb = snmpa_general_db:open(Storage, ?MIB_NODE, - node_info, - record_info(fields,node_info), set), + NodeDb = + case Mod:open(?MIB_NODE, node_info, record_info(fields, node_info), + set, Opts) of + {ok, T2} -> + T2; + {error, Reason2} -> + throw({error, {open, mib_node, Reason2}}) + end, + ?vtrace("open (mib) tree database",[]), - TreeDb = snmpa_general_db:open(Storage, ?MIB_TREE, - tree, - record_info(fields,tree), set), + TreeDb = + case Mod:open(?MIB_TREE, tree, record_info(fields, tree), + set, Opts) of + {ok, T3} -> + T3; + {error, Reason3} -> + throw({error, {open, mib_tree, Reason3}}) + end, + Tree = - case snmpa_general_db:read(TreeDb, ?DUMMY_TREE_GENERATION) of + case Mod:read(TreeDb, ?DUMMY_TREE_GENERATION) of false -> T = #tree{}, - snmpa_general_db:write(TreeDb, T), + Mod:write(TreeDb, T), T; {value, T} -> T end, - install_mibs(MibDb, NodeDb), - #mib_data{mib_db = MibDb, + install_mibs(Mod, MibDb, NodeDb), + #mib_data{module = Mod, + mib_db = MibDb, node_db = NodeDb, tree_db = TreeDb, tree = Tree}. @@ -168,7 +197,7 @@ new(Storage) -> %%---------------------------------------------------------------------- %% Returns: new mib data | {error, Reason} %%---------------------------------------------------------------------- -load_mib(MibData,FileName,MeOverride,TeOverride) +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", @@ -180,13 +209,14 @@ 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, + #mib_data{module = Mod, + mib_db = MibDb, node_db = NodeDb, %% tree_db = TreeDb, tree = Tree} = MibData, - verify_not_loaded(MibDb, MibName), + verify_not_loaded(Mod, MibDb, MibName), ?vtrace("do_load_mib -> already loaded mibs:" - "~n ~p",[loaded(MibDb)]), + "~n ~p", [loaded(Mod, MibDb)]), Mib = do_read_mib(ActualFileName), ?vtrace("do_load_mib -> read mib ~s",[Mib#mib.name]), NonInternalMes = @@ -208,12 +238,12 @@ do_load_mib(MibData, ActualFileName, MibName, MeOverride, TeOverride) -> 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, + install_mes(Mod, NodeDb, MibName, NonInternalMes), + install_mib(Mod, + 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 @@ -221,10 +251,10 @@ do_load_mib(MibData, ActualFileName, MibName, MeOverride, TeOverride) -> end. -verify_not_loaded(Db, Name) -> - case snmpa_general_db:read(Db, Name) of +verify_not_loaded(Mod, Tab, Name) -> + case Mod:read(Tab, Name) of {value, #mib_info{name = Name}} -> - throw({error, 'already loaded'}); + throw({error, already_loaded}); false -> ok end. @@ -233,26 +263,28 @@ do_read_mib(ActualFileName) -> case snmp_misc:read_mib(ActualFileName) of {error, Reason} -> ?vlog("Failed reading mib file ~p with reason: ~p", - [ActualFileName,Reason]), + [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, +sync(#mib_data{module = Mod, + 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, + Mod:sync(M), + Mod:sync(N), + Mod:write(T, Tree), + Mod:sync(T); +sync(#mib_data{module = Mod, + mib_db = M, node_db = N, tree_db = T, tree = Tree, subagents = SAs}) -> - snmpa_general_db:sync(M), - snmpa_general_db:sync(N), + Mod:sync(M), + Mod:sync(N), %% Ouch. Since the subagent info is dynamic we do not %% want to store the tree containing subagent info. So, we @@ -260,8 +292,8 @@ sync(#mib_data{mib_db = M, case delete_subagents(Tree, SAs) of {ok, TreeWithoutSAs} -> - snmpa_general_db:write(T, TreeWithoutSAs), - snmpa_general_db:sync(T); + Mod:write(T, TreeWithoutSAs), + Mod:sync(T); Error -> Error end. @@ -350,30 +382,34 @@ unload_mib(MibData, FileName, _, _) when is_list(FileName) -> do_unload_mib(MibData, MibName) -> ?vtrace("do_unload_mib -> entry with" "~n MibName: ~p", [MibName]), - #mib_data{mib_db = MibDb, + #mib_data{module = Mod, + mib_db = MibDb, node_db = NodeDb, %% tree_db = TreeDb, tree = Tree} = MibData, - #mib_info{symbolic = Symbolic} = verify_loaded(MibDb, MibName), + #mib_info{symbolic = Symbolic} = verify_loaded(Mod, MibDb, MibName), NewRoot = delete_mib_from_tree(MibName, Tree#tree.root), - MEs = uninstall_mes(NodeDb, MibName), - uninstall_mib(MibDb, Symbolic, MibName, MEs), + MEs = uninstall_mes(Mod, NodeDb, MibName), + uninstall_mib(Mod, 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 +verify_loaded(Mod, Tab, Name) -> + case Mod:read(Tab, Name) of {value, MibInfo} -> MibInfo; false -> - throw({error, 'not loaded'}) + 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), +close(#mib_data{module = Mod, + mib_db = MibDb, + node_db = NodeDb, + tree_db = TreeDb}) -> + Mod:close(MibDb), + Mod:close(NodeDb), + Mod:close(TreeDb), ok. register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) -> @@ -392,8 +428,8 @@ register_subagent(#mib_data{tree = T} = MibData, Oid, Pid) -> %% Returns: [{Name, File}] %%---------------------------------------------------------------------- -which_mibs(#mib_data{mib_db = Db}) -> - Mibs = snmpa_general_db:tab2list(Db), +which_mibs(#mib_data{module = Mod, mib_db = Db}) -> + Mibs = Mod:tab2list(Db), [{Name, File} || #mib_info{name = Name, file_name = File} <- Mibs]. @@ -402,8 +438,8 @@ which_mibs(#mib_data{mib_db = Db}) -> %% Returns: [{Name, File}] %%---------------------------------------------------------------------- -whereis_mib(#mib_data{mib_db = Db}, Name) -> - case snmpa_general_db:read(Db, Name) of +whereis_mib(#mib_data{module = Mod, mib_db = Db}, Name) -> + case Mod:read(Db, Name) of {value, #mib_info{file_name = File}} -> {ok, File}; false -> @@ -451,32 +487,39 @@ unregister_subagent(#mib_data{tree = T} = MibData, Oid) when is_list(Oid) -> %%---------------------------------------------------------------------- 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)), + #mib_data{module = Mod, + mib_db = MibDb, + node_db = NodeDb, + tree_db = TreeDb, + tree = Tree, + subagents = SAs} = MibData, + LoadedMibs = old_format(Mod: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), + {memory, ProcSize} = erlang:process_info(self(), memory), + MibDbSize = Mod:info(MibDb, memory), + NodeDbSize = Mod:info(NodeDb, memory), + TreeDbSize = Mod: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), +info(#mib_data{module = Mod, mib_db = MibDb}, loaded_mibs) -> + Mibs = Mod: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}, +info(#mib_data{module = Mod, + 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}]; + MibDbSize = Mod:info(MibDb, memory), + NodeDbSize = Mod:info(NodeDb, memory), + TreeDbSize = Mod:info(TreeDb, memory), + [{mib, MibDbSize}, {node, NodeDbSize}, {tree, TreeDbSize}]; info(#mib_data{subagents = SAs}, subagents) -> SAs. @@ -489,17 +532,19 @@ old_format(LoadedMibs) -> %% A total dump for debugging. %%---------------------------------------------------------------------- -dump(#mib_data{mib_db = MibDb, +dump(#mib_data{module = Mod, + mib_db = MibDb, node_db = NodeDb, tree = Tree}, io) -> (catch io:format("MIB-tables:~n~p~n~n", - [snmpa_general_db:tab2list(MibDb)])), + [Mod:tab2list(MibDb)])), (catch io:format("MIB-entries:~n~p~n~n", - [snmpa_general_db:tab2list(NodeDb)])), + [Mod:tab2list(NodeDb)])), (catch io:format("Tree:~n~p~n", [Tree])), % good luck reading it! ok; -dump(#mib_data{mib_db = MibDb, +dump(#mib_data{module = Mod, + mib_db = MibDb, node_db = NodeDb, tree = Tree}, File) -> case file:open(File, [write]) of @@ -507,9 +552,9 @@ dump(#mib_data{mib_db = MibDb, 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)])), + [Mod:tab2list(MibDb)])), (catch io:format(Fd, "MIB-entries:~n~p~n~n", - [snmpa_general_db:tab2list(NodeDb)])), + [Mod:tab2list(NodeDb)])), io:format(Fd,"Tree:~n~p~n", [Tree]), % good luck reading it! file:close(Fd), ok; @@ -520,10 +565,13 @@ dump(#mib_data{mib_db = MibDb, 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), +backup(#mib_data{module = Mod, + mib_db = M, + node_db = N, + tree_db = T}, BackupDir) -> + MRes = Mod:backup(M, BackupDir), + NRes = Mod:backup(N, BackupDir), + TRes = Mod:backup(T, BackupDir), handle_backup_res([{mib_db, MRes}, {node_db, NRes}, {tree_db, TRes}]). handle_backup_res(Res) -> @@ -632,9 +680,9 @@ 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, + #mib_data{module = Mod, node_db = Db} = D, Oid = lists:reverse(RevOid), - case snmpa_general_db:read(Db, Oid) of + case Mod: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}; @@ -659,13 +707,13 @@ find_node(D, {node, {table_column, _}}, RestOfOid, [ColInt | RevOid]) -> "~n RestOfOid: ~p" "~n ColInt: ~p" "~n RevOid: ~p",[RestOfOid, ColInt, RevOid]), - #mib_data{node_db = Db} = D, + #mib_data{module = Mod, node_db = Db} = D, Oid = lists:reverse([ColInt | RevOid]), - case snmpa_general_db:read(Db, Oid) of + case Mod:read(Db, Oid) of {value, #node_info{me = ME}} -> {ME, lists:reverse(RevOid)}; false -> - X = snmpa_general_db:read(Db, lists:reverse([ColInt | RevOid])), + X = Mod:read(Db, lists:reverse([ColInt | RevOid])), ?vinfo("find_node -> could not find table_column ME with" "~n RevOid: ~p" "~n trying [~p|~p]" @@ -676,10 +724,9 @@ find_node(D, {node, {table_column, _}}, RestOfOid, [ColInt | RevOid]) -> 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, + #mib_data{module = Mod, 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 + case Mod:read(Db, Oid) of {value, #node_info{me = ME, mib_name = Mib}} -> {variable, ME, Mib}; false -> @@ -761,8 +808,8 @@ next_node(D, {tree, Tree, {table_entry, _MibName}}, ?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 + #mib_data{module = Mod, node_db = Db} = D, + case Mod:read(Db, OidSoFar) of false -> ?vinfo("next_node -> could not find table_entry with" "~n OidSoFar: ~p", [OidSoFar]), @@ -834,8 +881,8 @@ next_node(D, {node, {variable, _MibName}}, [], 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 + #mib_data{module = Mod, node_db = Db} = D, + case Mod:read(Db, lists:reverse(RevOidSoFar)) of false -> ?vinfo("next_node -> could not find variable with" "~n RevOidSoFar: ~p", [RevOidSoFar]), @@ -851,6 +898,7 @@ 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. @@ -882,8 +930,8 @@ find_next(D, {tree, _Tree, {table_entry, _MibName}}, _Index, true -> false; _ -> - #mib_data{node_db = Db} = D, - case snmpa_general_db:read(Db, OidSoFar) of + #mib_data{module = Mod, node_db = Db} = D, + case Mod:read(Db, OidSoFar) of false -> ?vinfo("find_next -> could not find table_entry ME with" "~n OidSoFar: ~p", [OidSoFar]), @@ -896,8 +944,8 @@ 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 + #mib_data{module = Mod, node_db = Db} = D, + case Mod:read(Db, lists:reverse(RevOidSoFar)) of false -> ?vinfo("find_next -> could not find variable with" "~n RevOidSoFar: ~p", [RevOidSoFar]), @@ -1213,9 +1261,11 @@ build_tree_for_subagent([Index]) -> 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}; @@ -1223,6 +1273,7 @@ delete_subagent({tree, Tree, Info}, [Index | TI]) -> {tree, setelement(Index+1, Tree, delete_subagent(element(Index+1, Tree), TI)), Info}. + %%%====================================================================== %%% 7. Misc functions %%%====================================================================== @@ -1232,35 +1283,35 @@ delete_subagent({tree, Tree, Info}, [Index | TI]) -> %% Basically calls the instrumentation functions for all non-internal %% mib-entries %%---------------------------------------------------------------------- -install_mibs(MibDb, NodeDb) -> - MibNames = loaded(MibDb), +install_mibs(Mod, MibDb, NodeDb) -> + MibNames = loaded(Mod, MibDb), ?vtrace("install_mibs -> found following mibs in database: ~n" "~p", [MibNames]), - install_mibs2(NodeDb, MibNames). + install_mibs2(Mod, NodeDb, MibNames). -install_mibs2(_, []) -> +install_mibs2(_, _, []) -> ok; -install_mibs2(NodeDb, [MibName|MibNames]) -> +install_mibs2(Mod, NodeDb, [MibName|MibNames]) -> Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'}, - Nodes = snmpa_general_db:match_object(NodeDb, Pattern), + Nodes = Mod:match_object(NodeDb, Pattern), MEs = [ME || #node_info{me = ME} <- Nodes], ?vtrace("install_mibs2 -> installing ~p MEs for mib ~p", - [length(MEs),MibName]), + [length(MEs), MibName]), NewF = fun(ME) -> call_instrumentation(ME, new) end, lists:foreach(NewF, MEs), - install_mibs2(NodeDb, MibNames). + install_mibs2(Mod, NodeDb, MibNames). %%---------------------------------------------------------------------- %% Does all side effect stuff during load_mib. %%---------------------------------------------------------------------- -install_mib(Db, Symbolic, Mib, MibName, FileName, NonInternalMes) -> +install_mib(Mod, 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), + Mod:write(Db, Rec), install_mib2(Symbolic, MibName, Mib), NewF = fun(ME) -> call_instrumentation(ME, new) end, lists:foreach(NewF, NonInternalMes). @@ -1282,23 +1333,31 @@ install_mib2(true, MibName, Mib) -> install_mib2(_, _, _) -> ok. -install_mes(_Db, _MibName, []) -> +install_mes(Mod, Db, MibName, MEs) -> + Write = fun(#me{oid = Oid} = ME) -> + Node = #node_info{oid = Oid, + mib_name = MibName, + me = ME}, + Mod:write(Db, Node) + end, + install_mes(Write, MEs). + +install_mes(_Write, []) -> 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). +install_mes(Write, [ME|MEs]) -> + Write(ME), + install_mes(Write, MEs). %%---------------------------------------------------------------------- %% Does all side effect stuff during unload_mib. %%---------------------------------------------------------------------- -uninstall_mib(Db, Symbolic, MibName, MEs) -> +uninstall_mib(Mod, 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), + Res = Mod:delete(Db, MibName), ?vtrace("uninstall_mib -> (mib) db delete result: ~p", [Res]), uninstall_mib2(Symbolic, MibName), DelF = fun(ME) -> call_instrumentation(ME, delete) end, @@ -1313,16 +1372,16 @@ uninstall_mib2(true, MibName) -> uninstall_mib2(_, _) -> ok. -uninstall_mes(Db, MibName) -> +uninstall_mes(Mod, Db, MibName) -> Pattern = #node_info{oid = '_', mib_name = MibName, me = '_'}, - snmpa_general_db:match_delete(Db, Pattern). + Mod: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)]. +loaded(Mod, Db) -> + [N || #mib_info{name = N} <- Mod:tab2list(Db)]. %%---------------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_symbolic_store.erl b/lib/snmp/src/agent/snmpa_symbolic_store.erl index 6c58ffde41..00178f4bcd 100644 --- a/lib/snmp/src/agent/snmpa_symbolic_store.erl +++ b/lib/snmp/src/agent/snmpa_symbolic_store.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 @@ -78,7 +78,7 @@ gen_server:start_link({local, ?SERVER}, ?MODULE, [Prio, Opts], [])). -endif. --record(state, {db, backup}). +-record(state, {module, db, backup}). -record(symbol, {key, mib_name, info}). @@ -112,6 +112,9 @@ backup(BackupDir) -> get_db() -> call(get_db). +which_module() -> + call(which_module). + %%---------------------------------------------------------------------- %% Returns: {value, Oid} | false @@ -202,49 +205,63 @@ verbosity(Verbosity) -> %%---------------------------------------------------------------------- %% DB access (read) functions: Returns: {value, Oid} | false %%---------------------------------------------------------------------- + aliasname_to_oid(Db, Aliasname) -> - case snmpa_general_db:read(Db, {alias, Aliasname}) of + Mod = which_module(), + aliasname_to_oid(Mod, Db, Aliasname). + +aliasname_to_oid(Mod, Db, Aliasname) -> + case Mod:read(Db, {alias, Aliasname}) of {value,#symbol{info = {Oid, _Enums}}} -> {value, Oid}; false -> false end. -oid_to_aliasname(Db,Oid) -> - case snmpa_general_db:read(Db, {oid, Oid}) of +oid_to_aliasname(Db, Oid) -> + Mod = which_module(), + oid_to_aliasname(Mod, Db, Oid). + +oid_to_aliasname(Mod, Db, Oid) -> + case Mod:read(Db, {oid, Oid}) of {value,#symbol{info = Aliasname}} -> {value, Aliasname}; _ -> false end. -which_notifications(Db) -> +which_notifications(Mod, Db) -> Pattern = #symbol{key = {trap, '_'}, _ = '_'}, - Symbols = snmpa_general_db:match_object(Db, Pattern), + Symbols = Mod:match_object(Db, Pattern), [{Name, Mib, Rec} || #symbol{key = {trap, Name}, mib_name = Mib, info = Rec} <- Symbols]. -which_aliasnames(Db) -> +which_aliasnames(Mod, Db) -> Pattern = #symbol{key = {alias, '_'}, _ = '_'}, - Symbols = snmpa_general_db:match_object(Db, Pattern), + Symbols = Mod:match_object(Db, Pattern), [Alias || #symbol{key = {alias, Alias}} <- Symbols]. -which_tables(Db) -> +which_tables(Mod, Db) -> Pattern = #symbol{key = {table_info, '_'}, _ = '_'}, - Symbols = snmpa_general_db:match_object(Db, Pattern), + Symbols = Mod:match_object(Db, Pattern), [Name || #symbol{key = {table_info, Name}} <- Symbols]. -which_variables(Db) -> +which_variables(Mod, Db) -> Pattern = #symbol{key = {variable_info, '_'}, _ = '_'}, - Symbols = snmpa_general_db:match_object(Db, Pattern), + Symbols = Mod:match_object(Db, Pattern), [Name || #symbol{key = {variable_info, Name}} <- Symbols]. -int_to_enum(Db,TypeOrObjName,Int) -> - case snmpa_general_db:read(Db, {alias, TypeOrObjName}) of + +int_to_enum(Db, TypeOrObjName, Int) -> + Mod = which_module(), + int_to_enum(Mod, Db, TypeOrObjName, Int). + +int_to_enum(Mod, Db, TypeOrObjName, Int) -> + case Mod:read(Db, {alias, TypeOrObjName}) of {value,#symbol{info = {_Oid, Enums}}} -> case lists:keysearch(Int, 2, Enums) of {value, {Enum, _Int}} -> {value, Enum}; false -> false end; false -> % Not an Aliasname -> - case snmpa_general_db:read(Db, {type, TypeOrObjName}) of + case Mod:read(Db, {type, TypeOrObjName}) of {value,#symbol{info = Enums}} -> case lists:keysearch(Int, 2, Enums) of {value, {Enum, _Int}} -> {value, Enum}; @@ -256,14 +273,18 @@ int_to_enum(Db,TypeOrObjName,Int) -> end. enum_to_int(Db, TypeOrObjName, Enum) -> - case snmpa_general_db:read(Db, {alias, TypeOrObjName}) of + Mod = which_module(), + enum_to_int(Mod, Db, TypeOrObjName, Enum). + +enum_to_int(Mod, Db, TypeOrObjName, Enum) -> + case Mod:read(Db, {alias, TypeOrObjName}) of {value,#symbol{info = {_Oid, Enums}}} -> case lists:keysearch(Enum, 1, Enums) of {value, {_Enum, Int}} -> {value, Int}; false -> false end; false -> % Not an Aliasname - case snmpa_general_db:read(Db, {type, TypeOrObjName}) of + case Mod:read(Db, {type, TypeOrObjName}) of {value,#symbol{info = Enums}} -> case lists:keysearch(Enum, 1, Enums) of {value, {_Enum, Int}} -> {value, Int}; @@ -278,8 +299,9 @@ enum_to_int(Db, TypeOrObjName, Enum) -> %%---------------------------------------------------------------------- %% DB access (read) functions: Returns: false|{value, Info} %%---------------------------------------------------------------------- -table_info(Db,TableName) -> - case snmpa_general_db:read(Db, {table_info, TableName}) of + +table_info(Mod, Db, TableName) -> + case Mod:read(Db, {table_info, TableName}) of {value,#symbol{info = Info}} -> {value, Info}; false -> false end. @@ -288,8 +310,8 @@ table_info(Db,TableName) -> %%---------------------------------------------------------------------- %% DB access (read) functions: Returns: false|{value, Info} %%---------------------------------------------------------------------- -variable_info(Db,VariableName) -> - case snmpa_general_db:read(Db, {variable_info, VariableName}) of +variable_info(Mod, Db, VariableName) -> + case Mod:read(Db, {variable_info, VariableName}) of {value,#symbol{info = Info}} -> {value, Info}; false -> false end. @@ -299,7 +321,7 @@ variable_info(Db,VariableName) -> %% Implementation %%---------------------------------------------------------------------- -init([Prio,Opts]) -> +init([Prio, Opts]) -> ?d("init -> entry with" "~n Prio: ~p" "~n Opts: ~p", [Prio,Opts]), @@ -317,102 +339,125 @@ do_init(Prio, Opts) -> put(sname,ss), put(verbosity,get_verbosity(Opts)), ?vlog("starting",[]), - Storage = get_mib_storage(Opts), + MibStorage = get_mib_storage(Opts), + Mod = snmp_misc:get_option(module, MibStorage), + MsOpts = snmp_misc:get_option(options, MibStorage, []), + %% type = bag solves the problem with import and multiple %% object/type definitions. - Db = snmpa_general_db:open(Storage, snmpa_symbolic_store, - symbol, record_info(fields,symbol), bag), - S = #state{db = Db}, - ?vdebug("started",[]), - {ok, S}. + case Mod:open(?MODULE, symbol, record_info(fields, symbol), bag, MsOpts) of + {ok, Db} -> + S = #state{module = Mod, db = Db}, + ?vdebug("started",[]), + {ok, S}; + {error, _} = ERROR -> + ERROR + end. handle_call(get_db, _From, #state{db = DB} = S) -> ?vlog("get db",[]), {reply, DB, S}; -handle_call({table_info, TableName}, _From, #state{db = DB} = S) -> +handle_call(which_module, _From, #state{module = Mod} = S) -> + ?vlog("which module",[]), + {reply, Mod, S}; + +handle_call({table_info, TableName}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("table info: ~p",[TableName]), - Res = table_info(DB, TableName), + Res = table_info(Mod, DB, TableName), ?vdebug("table info result: ~p",[Res]), {reply, Res, S}; -handle_call({variable_info, VariableName}, _From, #state{db = DB} = S) -> +handle_call({variable_info, VariableName}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("variable info: ~p",[VariableName]), - Res = variable_info(DB, VariableName), + Res = variable_info(Mod, DB, VariableName), ?vdebug("variable info result: ~p",[Res]), {reply, Res, S}; -handle_call({aliasname_to_oid, Aliasname}, _From, #state{db = DB} = S) -> +handle_call({aliasname_to_oid, Aliasname}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("aliasname to oid: ~p",[Aliasname]), - Res = aliasname_to_oid(DB,Aliasname), + Res = aliasname_to_oid(Mod, DB, Aliasname), ?vdebug("aliasname to oid result: ~p",[Res]), {reply, Res, S}; -handle_call({oid_to_aliasname, Oid}, _From, #state{db = DB} = S) -> +handle_call({oid_to_aliasname, Oid}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("oid to aliasname: ~p",[Oid]), - Res = oid_to_aliasname(DB, Oid), + Res = oid_to_aliasname(Mod, DB, Oid), ?vdebug("oid to aliasname result: ~p",[Res]), {reply, Res, S}; -handle_call(which_aliasnames, _From, #state{db = DB} = S) -> +handle_call(which_aliasnames, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("which aliasnames",[]), - Res = which_aliasnames(DB), + Res = which_aliasnames(Mod, DB), ?vdebug("which aliasnames: ~p",[Res]), {reply, Res, S}; -handle_call(which_tables, _From, #state{db = DB} = S) -> +handle_call(which_tables, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("which tables",[]), - Res = which_tables(DB), + Res = which_tables(Mod, DB), ?vdebug("which tables: ~p",[Res]), {reply, Res, S}; -handle_call(which_variables, _From, #state{db = DB} = S) -> +handle_call(which_variables, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("which variables",[]), - Res = which_variables(DB), + Res = which_variables(Mod, DB), ?vdebug("which variables: ~p",[Res]), {reply, Res, S}; -handle_call({enum_to_int, TypeOrObjName, Enum}, _From, #state{db = DB} = S) -> +handle_call({enum_to_int, TypeOrObjName, Enum}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("enum to int: ~p, ~p",[TypeOrObjName,Enum]), - Res = enum_to_int(DB, TypeOrObjName, Enum), + Res = enum_to_int(Mod, DB, TypeOrObjName, Enum), ?vdebug("enum to int result: ~p",[Res]), {reply, Res, S}; -handle_call({int_to_enum, TypeOrObjName, Int}, _From, #state{db = DB} = S) -> +handle_call({int_to_enum, TypeOrObjName, Int}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("int to enum: ~p, ~p",[TypeOrObjName,Int]), - Res = int_to_enum(DB, TypeOrObjName, Int), + Res = int_to_enum(Mod, DB, TypeOrObjName, Int), ?vdebug("int to enum result: ~p",[Res]), {reply, Res, S}; -handle_call({set_notification, MibName, Trap}, _From, #state{db = DB} = S) -> +handle_call({set_notification, MibName, Trap}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("set notification:" "~n ~p~n ~p", [MibName,Trap]), - set_notif(DB, MibName, Trap), + set_notif(Mod, DB, MibName, Trap), {reply, true, S}; -handle_call({delete_notifications, MibName}, _From, #state{db = DB} = S) -> +handle_call({delete_notifications, MibName}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("delete notification: ~p",[MibName]), - delete_notif(DB, MibName), + delete_notif(Mod, DB, MibName), {reply, true, S}; -handle_call(which_notifications, _From, #state{db = DB} = S) -> +handle_call(which_notifications, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("which notifications", []), - Reply = which_notifications(DB), + Reply = which_notifications(Mod, DB), {reply, Reply, S}; -handle_call({get_notification, Key}, _From, #state{db = DB} = S) -> +handle_call({get_notification, Key}, _From, + #state{module = Mod, db = DB} = S) -> ?vlog("get notification: ~p",[Key]), - Res = get_notif(DB, Key), + Res = get_notif(Mod, DB, Key), ?vdebug("get notification result: ~p",[Res]), {reply, Res, S}; -handle_call(info, _From, #state{db = DB} = S) -> +handle_call(info, _From, #state{module = Mod, db = DB} = S) -> ?vlog("info",[]), - Info = get_info(DB), + Info = get_info(Mod, DB), {reply, Info, S}; -handle_call({backup, BackupDir}, From, #state{db = DB} = S) -> +handle_call({backup, BackupDir}, From, #state{module = Mod, db = DB} = S) -> ?vlog("info to ~p",[BackupDir]), Pid = self(), V = get(verbosity), @@ -424,7 +469,7 @@ handle_call({backup, BackupDir}, From, #state{db = DB} = S) -> put(sname, albs), put(verbosity, V), Dir = filename:join([BackupDir]), - Reply = snmpa_general_db:backup(DB, Dir), + Reply = Mod:backup(DB, Dir), Pid ! {backup_done, Reply}, unlink(Pid) end), @@ -446,7 +491,7 @@ handle_call(Req, _From, S) -> {reply, Reply, S}. -handle_cast({add_types, MibName, Types}, #state{db = DB} = S) -> +handle_cast({add_types, MibName, Types}, #state{module = Mod, db = DB} = S) -> ?vlog("add types for ~p:",[MibName]), F = fun(#asn1_type{assocList = Alist, aliasname = Name}) -> case snmp_misc:assq(enums, Alist) of @@ -455,20 +500,21 @@ handle_cast({add_types, MibName, Types}, #state{db = DB} = S) -> Rec = #symbol{key = {type, Name}, mib_name = MibName, info = Es}, - snmpa_general_db:write(DB, Rec); + Mod:write(DB, Rec); false -> done end end, lists:foreach(F, Types), {noreply, S}; -handle_cast({delete_types, MibName}, #state{db = DB} = S) -> +handle_cast({delete_types, MibName}, #state{module = Mod, db = DB} = S) -> ?vlog("delete types: ~p",[MibName]), Pattern = #symbol{key = {type, '_'}, mib_name = MibName, info = '_'}, - snmpa_general_db:match_delete(DB, Pattern), + Mod:match_delete(DB, Pattern), {noreply, S}; -handle_cast({add_aliasnames, MibName, MEs}, #state{db = DB} = S) -> +handle_cast({add_aliasnames, MibName, MEs}, + #state{module = Mod, db = DB} = S) -> ?vlog("add aliasnames for ~p:",[MibName]), F = fun(#me{aliasname = AN, oid = Oid, asn1_type = AT}) -> Enums = @@ -480,20 +526,21 @@ handle_cast({add_aliasnames, MibName, MEs}, #state{db = DB} = S) -> end; _ -> [] end, - write_alias(AN, DB, Enums, MibName, Oid) + write_alias(Mod, AN, DB, Enums, MibName, Oid) end, lists:foreach(F, MEs), {noreply, S}; -handle_cast({delete_aliasname, MibName}, #state{db = DB} = S) -> +handle_cast({delete_aliasname, MibName}, #state{module = Mod, db = DB} = S) -> ?vlog("delete aliasname: ~p",[MibName]), Pattern1 = #symbol{key = {alias, '_'}, mib_name = MibName, info = '_'}, - snmpa_general_db:match_delete(DB, Pattern1), + Mod:match_delete(DB, Pattern1), Pattern2 = #symbol{key = {oid, '_'}, mib_name = MibName, info = '_'}, - snmpa_general_db:match_delete(DB, Pattern2), + Mod:match_delete(DB, Pattern2), {noreply, S}; -handle_cast({add_table_infos, MibName, TableInfos}, #state{db = DB} = S) -> +handle_cast({add_table_infos, MibName, TableInfos}, + #state{module = Mod, db = DB} = S) -> ?vlog("add table infos for ~p:",[MibName]), F = fun({Name, TableInfo}) -> ?vlog("add table info~n ~p -> ~p", @@ -501,19 +548,20 @@ handle_cast({add_table_infos, MibName, TableInfos}, #state{db = DB} = S) -> Rec = #symbol{key = {table_info, Name}, mib_name = MibName, info = TableInfo}, - snmpa_general_db:write(DB, Rec) + Mod:write(DB, Rec) end, lists:foreach(F, TableInfos), {noreply, S}; -handle_cast({delete_table_infos, MibName}, #state{db = DB} = S) -> +handle_cast({delete_table_infos, MibName}, + #state{module = Mod, db = DB} = S) -> ?vlog("delete table infos: ~p",[MibName]), Pattern = #symbol{key = {table_info, '_'}, mib_name = MibName, info = '_'}, - snmpa_general_db:match_delete(DB, Pattern), + Mod:match_delete(DB, Pattern), {noreply, S}; handle_cast({add_variable_infos, MibName, VariableInfos}, - #state{db = DB} = S) -> + #state{module = Mod, db = DB} = S) -> ?vlog("add variable infos for ~p:",[MibName]), F = fun({Name, VariableInfo}) -> ?vlog("add variable info~n ~p -> ~p", @@ -521,17 +569,18 @@ handle_cast({add_variable_infos, MibName, VariableInfos}, Rec = #symbol{key = {variable_info, Name}, mib_name = MibName, info = VariableInfo}, - snmpa_general_db:write(DB, Rec) + Mod:write(DB, Rec) end, lists:foreach(F, VariableInfos), {noreply, S}; -handle_cast({delete_variable_infos, MibName}, #state{db = DB} = S) -> +handle_cast({delete_variable_infos, MibName}, + #state{module = Mod, db = DB} = S) -> ?vlog("delete variable infos: ~p",[MibName]), Pattern = #symbol{key = {variable_info,'_'}, mib_name = MibName, info = '_'}, - snmpa_general_db:match_delete(DB, Pattern), + Mod:match_delete(DB, Pattern), {noreply, S}; handle_cast({verbosity,Verbosity}, State) -> @@ -565,9 +614,9 @@ handle_info(Info, S) -> {noreply, S}. -terminate(Reason, S) -> - ?vlog("terminate: ~p",[Reason]), - snmpa_general_db:close(S#state.db). +terminate(Reason, #state{module = Mod, db = DB}) -> + ?vlog("terminate: ~p", [Reason]), + Mod:close(DB). %%---------------------------------------------------------- @@ -575,18 +624,18 @@ terminate(Reason, S) -> %%---------------------------------------------------------- % downgrade -code_change({down, _Vsn}, #state{db = DB, backup = B}, downgrade_to_pre_4_7) -> - ?d("code_change(down) -> entry", []), - stop_backup_server(B), - S = {state, DB}, - {ok, S}; - -% upgrade -code_change(_Vsn, S, upgrade_from_pre_4_7) -> - ?d("code_change(up) -> entry", []), - {state, DB} = S, - S1 = #state{db = DB}, - {ok, S1}; +%% code_change({down, _Vsn}, #state{db = DB, backup = B}, downgrade_to_pre_4_7) -> +%% ?d("code_change(down) -> entry", []), +%% stop_backup_server(B), +%% S = {state, DB}, +%% {ok, S}; + +%% % upgrade +%% code_change(_Vsn, S, upgrade_from_pre_4_7) -> +%% ?d("code_change(up) -> entry", []), +%% {state, DB} = S, +%% S1 = #state{db = DB}, +%% {ok, S1}; code_change(_Vsn, S, _Extra) -> ?d("code_change -> entry [do nothing]", []), @@ -609,13 +658,13 @@ stop_backup_server({Pid, _}) when is_pid(Pid) -> %%----------------------------------------------------------------- %% Returns: {value, Value} | undefined %%----------------------------------------------------------------- -get_notif(Db, Key) -> - case snmpa_general_db:read(Db, {trap, Key}) of +get_notif(Mod, Db, Key) -> + case Mod:read(Db, {trap, Key}) of {value,#symbol{info = Value}} -> {value, Value}; false -> undefined end. -set_notif(Db, MibName, Trap) when is_record(Trap, trap) -> +set_notif(Mod, Db, MibName, Trap) when is_record(Trap, trap) -> #trap{trapname = Name} = Trap, Rec = #symbol{key = {trap, Name}, mib_name = MibName, info = Trap}, %% convert old v1 trap to oid @@ -625,40 +674,41 @@ set_notif(Db, MibName, Trap) when is_record(Trap, trap) -> Oid0 -> Oid0 ++ [0, Trap#trap.specificcode] end, - write_alias(Name, Db, MibName, Oid), - snmpa_general_db:write(Db, Rec); -set_notif(Db, MibName, Trap) -> + write_alias(Mod, Name, Db, MibName, Oid), + Mod:write(Db, Rec); +set_notif(Mod, Db, MibName, Trap) -> #notification{trapname = Name, oid = Oid} = Trap, Rec = #symbol{key = {trap, Name}, mib_name = MibName, info = Trap}, - write_alias(Name, Db, MibName, Oid), - snmpa_general_db:write(Db, Rec). + write_alias(Mod, Name, Db, MibName, Oid), + Mod:write(Db, Rec). -delete_notif(Db, MibName) -> +delete_notif(Mod, Db, MibName) -> Pattern = #symbol{key = {trap, '_'}, mib_name = MibName, info = '_'}, - snmpa_general_db:match_delete(Db, Pattern). + Mod:match_delete(Db, Pattern). -write_alias(AN, DB, MibName, Oid) -> - write_alias(AN, DB, [], MibName, Oid). +write_alias(Mod, AN, DB, MibName, Oid) -> + write_alias(Mod, AN, DB, [], MibName, Oid). -write_alias(AN, DB, Enums, MibName, Oid) -> +write_alias(Mod, AN, DB, Enums, MibName, Oid) -> ?vlog("add alias~n ~p -> {~p,~p}",[AN, Oid, Enums]), Rec1 = #symbol{key = {alias, AN}, mib_name = MibName, info = {Oid,Enums}}, - snmpa_general_db:write(DB, Rec1), + Mod:write(DB, Rec1), ?vlog("add oid~n ~p -> ~p",[Oid, AN]), Rec2 = #symbol{key = {oid, Oid}, mib_name = MibName, info = AN}, - snmpa_general_db:write(DB, Rec2). + Mod:write(DB, Rec2). + %% ------------------------------------- -get_info(DB) -> +get_info(Mod, DB) -> ProcSize = proc_mem(self()), - DbSz = tab_size(DB), - [{process_memory, ProcSize}, {db_memory, DbSz}]. + DbMemory = Mod:info(DB, memory), + [{process_memory, ProcSize}, {db_memory, DbMemory}]. proc_mem(P) when is_pid(P) -> case (catch erlang:process_info(P, memory)) of @@ -667,26 +717,15 @@ proc_mem(P) when is_pid(P) -> _ -> undefined end. -%% proc_mem(_) -> -%% undefined. - -tab_size(DB) -> - case (catch snmpa_general_db:info(DB, memory)) of - Sz when is_integer(Sz) -> - Sz; - _ -> - undefined - end. - %% ------------------------------------- get_verbosity(L) -> - snmp_misc:get_option(verbosity,L,?default_verbosity). + snmp_misc:get_option(verbosity, L, ?default_verbosity). get_mib_storage(L) -> - snmp_misc:get_option(mib_storage,L,ets). + snmp_misc:get_option(mib_storage, L). %% ------------------------------------- -- cgit v1.2.3 From 58309771ca695ed74dc92c72cca471b93eda8282 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 14:11:35 +0200 Subject: [snmp/agent] Add info/2 and some record checks Add a new function/2 to behaviour. Also changed returnj type for info/1. Also make sure even ets and dets implementation(s) check that the correct type is written. --- lib/snmp/src/agent/snmpa_mib_storage.erl | 16 +++++++------- lib/snmp/src/agent/snmpa_mib_storage_dets.erl | 27 +++++++++++++++++------- lib/snmp/src/agent/snmpa_mib_storage_ets.erl | 28 ++++++++++++++++++------- lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl | 10 ++++++--- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_storage.erl b/lib/snmp/src/agent/snmpa_mib_storage.erl index bbb9516ecb..5c3f76d89b 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage.erl @@ -22,9 +22,7 @@ -export_type([ mib_storage_fields/0, mib_storage_table_type/0, - mib_storage_table_id/0, - - void/0 + mib_storage_table_id/0 ]). @@ -37,7 +35,6 @@ -type mib_storage_fields() :: [atom()]. -type mib_storage_table_type() :: set | bag. -type mib_storage_table_id() :: term(). --type void() :: term(). %% --------------------------------------------------------------- @@ -99,7 +96,7 @@ %% --------------------------------------------------------------- -callback delete(TabId :: mib_storage_table_id()) -> - void(). + snmp:void(). %% --------------------------------------------------------------- @@ -149,14 +146,17 @@ %% --------------------------------------------------------------- -%% info +%% info/1,2 %% %% Retrieve implementation dependent mib-storage table %% information. %% --------------------------------------------------------------- -callback info(TabId :: mib_storage_table_id()) -> - {ok, Info :: term()} | {error, Reason :: term()}. + Info :: term(). + +-callback info(TabId :: mib_storage_table_id(), Item :: atom()) -> + Info :: term(). %% --------------------------------------------------------------- @@ -166,7 +166,7 @@ %% --------------------------------------------------------------- -callback sync(TabId :: mib_storage_table_id()) -> - ok. + snmp:void(). %% --------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_mib_storage_dets.erl b/lib/snmp/src/agent/snmpa_mib_storage_dets.erl index 6062f4327e..e84e18e7ea 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_dets.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_dets.erl @@ -33,19 +33,19 @@ write/2, delete/1, delete/2, - sync/1, - backup/2, match_object/2, match_delete/2, tab2list/1, - info/1 + info/1, info/2, + sync/1, + backup/2 ]). -define(VMODULE, "MS-DETS"). -include("snmp_verbosity.hrl"). --record(tab, {id}). +-record(tab, {id, rec_name}). %% --------------------------------------------------------------- @@ -62,7 +62,7 @@ %% %% --------------------------------------------------------------- -open(Name, _RecName, _Fields, Type, Opts) -> +open(Name, RecName, _Fields, Type, Opts) -> Dir = snmp_misc:get_option(dir, Opts), Action = snmp_misc:get_option(action, Opts, keep), AutoSave = snmp_misc:get_option(auto_save, Opts, default), @@ -80,10 +80,10 @@ open(Name, _RecName, _Fields, Type, Opts) -> end, case dets:open_file(Name, OpenOpts) of {ok, ID} when (Action =:= keep) -> - {ok, #tab{id = ID}}; + {ok, #tab{id = ID, rec_name = RecName}}; {ok, ID} when (Action =:= clear) -> dets:match_delete(ID, '_'), - {ok, #tab{id = ID}}; + {ok, #tab{id = ID, rec_name = RecName}}; {error, Reason} -> {error, {dets_open, Reason}} end. @@ -128,7 +128,8 @@ read(#tab{id = ID}, Key) -> %% Write a record to the database table. %% --------------------------------------------------------------- -write(#tab{id = ID}, Rec) -> +write(#tab{id = ID, rec_name = RecName}, Rec) + when (is_tuple(Rec) andalso (element(1, Rec) =:= RecName)) -> ?vtrace("write to table ~p", [ID]), dets:insert(ID, Rec). @@ -209,6 +210,16 @@ info(#tab{id = ID}) -> dets:info(ID). +info(TabId, all = _Item) -> + info(TabId); +info(#tab{id = ID}, memory = _Item) -> + ?vtrace("info on ~p (~w)", [ID, _Item]), + dets:info(ID, file_size); +info(#tab{id = ID}, Item) -> + ?vtrace("info on ~p (~w)", [ID, Item]), + dets:info(ID, Item). + + %% --------------------------------------------------------------- %% sync %% diff --git a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl index fcf5e12043..4afdc76ee5 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl @@ -33,19 +33,19 @@ write/2, delete/1, delete/2, - sync/1, - backup/2, match_object/2, match_delete/2, tab2list/1, - info/1 + info/1, info/2, + sync/1, + backup/2 ]). -define(VMODULE,"MS-ETS"). -include("snmp_verbosity.hrl"). --record(tab, {id, file, checksum = false}). +-record(tab, {id, rec_name, file, checksum = false}). %% --------------------------------------------------------------- @@ -67,7 +67,7 @@ %% --------------------------------------------------------------- %% This function creates the ets table -open(Name, _RecName, _Fields, Type, Opts) -> +open(Name, RecName, _Fields, Type, Opts) -> ?vtrace("open table ~p", [Name]), case lists:keysearch(dir, 1, Opts) of {value, {dir, Dir}} -> @@ -80,6 +80,7 @@ open(Name, _RecName, _Fields, Type, Opts) -> case ets:file2tab(File, [{verify, Checksum}]) of {ok, ID} -> {ok, #tab{id = ID, + rec_name = RecName, file = File, checksum = Checksum}}; {error, Reason} when (Action =:= keep) -> @@ -92,6 +93,7 @@ open(Name, _RecName, _Fields, Type, Opts) -> ID = ets:new(Name, [Type, protected, {keypos, 2}]), write_ets_file(ID, File, Checksum), {ok, #tab{id = ID, + rec_name = RecName, file = File, checksum = Checksum}} end; @@ -105,12 +107,13 @@ open(Name, _RecName, _Fields, Type, Opts) -> ID = ets:new(Name, [Type, protected, {keypos, 2}]), write_ets_file(ID, File, Checksum), {ok, #tab{id = ID, + rec_name = RecName, file = File, checksum = Checksum}} end; false -> ID = ets:new(Name, [Type, protected, {keypos, 2}]), - {ok, #tab{id = ID}} + {ok, #tab{id = ID, rec_name = RecName}} end. @@ -150,7 +153,9 @@ read(#tab{id = ID}, Key) -> %% Write a record to the mib-storage table. %% --------------------------------------------------------------- -write(#tab{id = ID}, Rec) -> +%% This is a very crude guard test is used instead of: is_record(Rec, RecName) +write(#tab{id = ID, rec_name = RecName}, Rec) + when (is_tuple(Rec) andalso (element(1, Rec) =:= RecName)) -> ?vtrace("write to table ~p", [ID]), ets:insert(ID, Rec). @@ -219,7 +224,7 @@ tab2list(#tab{id = ID}) -> %% --------------------------------------------------------------- -%% info +%% info/1,2 %% %% Retrieve implementation dependent mib-storage table %% information. @@ -234,6 +239,13 @@ info(#tab{id = ID}) -> end. +info(TabId, all = _Item) -> + info(TabId); +info(#tab{id = ID}, Item) -> + ?vtrace("info on ~p", [ID]), + ets:info(ID, Item). + + %% --------------------------------------------------------------- %% sync %% diff --git a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl index aee2192b24..a1f2be9af3 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl @@ -33,12 +33,12 @@ write/2, delete/1, delete/2, - sync/1, - backup/2, match_object/2, match_delete/2, tab2list/1, - info/1 + info/1, info/2, + sync/1, + backup/2 ]). @@ -239,6 +239,10 @@ info(#tab{id = ID}) -> end. +info(#tab{id = ID}, Item) -> + mnesia:table_info(ID, Item). + + %% --------------------------------------------------------------- %% sync %% -- cgit v1.2.3 From bed8ac264d7d8fec56ceaa31a76404a5609dbadf Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 14:15:17 +0200 Subject: [snmp/agent] No default value for mib_storage and some cleanup --- lib/snmp/src/agent/snmpa_agent.erl | 185 +++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 91 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index 57846db13b..c267ce5a70 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -159,12 +159,15 @@ %% it is sent to the worker, and the worker is marked as busy. %% If a request is received when the worker is busy, a new temporary %% worker is spawned. +%% %% Code change %% =========== %% Note that the worker(s) execute the same module as the master %% agent. For code change we have two options - ignore the workers, %% or send them a code change message. +%% %%----------------------------------------------------------------- + -record(state, {type, parent, worker, @@ -219,22 +222,19 @@ %%----------------------------------------------------------------- start_link(Prio, Parent, Ref, Options) -> ?d("start_link -> entry with" - "~n Prio: ~p" - "~n Parent: ~p" - "~n Ref: ~p" - "~n Options: ~p", [Prio, Parent, Ref, Options]), - %% gen_server:start_link(?MODULE, [Prio, Parent, Ref, Options], []). + "~n Prio: ~p" + "~n Parent: ~p" + "~n Ref: ~p" + "~n Options: ~p", [Prio, Parent, Ref, Options]), ?GS_START_LINK3(Prio, Parent, Ref, Options). start_link(Prio, Name, Parent, Ref, Options) -> ?d("start_link -> entry with" - "~n Prio: ~p" - "~n Name: ~p" - "~n Parent: ~p" - "~n Ref: ~p" - "~n Options: ~p", [Prio, Name, Parent, Ref, Options]), -% gen_server:start_link({local, Name}, ?MODULE, -% [Prio, Parent, Ref, Options], []). + "~n Prio: ~p" + "~n Name: ~p" + "~n Parent: ~p" + "~n Ref: ~p" + "~n Options: ~p", [Prio, Name, Parent, Ref, Options]), ?GS_START_LINK4(Prio, Name, Parent, Ref, Options). stop(Agent) -> call(Agent, stop). @@ -335,10 +335,10 @@ increment_counter(Counter, Initial, Max) -> init([Prio, Parent, Ref, Options]) -> ?d("init -> entry with" - "~n Prio: ~p" - "~n Parent: ~p" - "~n Ref: ~p" - "~n Options: ~p", [Prio, Parent, Ref, Options]), + "~n Prio: ~p" + "~n Parent: ~p" + "~n Ref: ~p" + "~n Options: ~p", [Prio, Parent, Ref, Options]), case (catch do_init(Prio, Parent, Ref, Options)) of {ok, State} -> ?vdebug("started",[]), @@ -1457,80 +1457,80 @@ handle_mibs_cache_request(MibServer, Req) -> %% Downgrade %% -code_change({down, _Vsn}, S1, downgrade_to_pre_4_17_3) -> - #state{type = Type, - parent = Parent, - worker = Worker, - worker_state = WorkerState, - set_worker = SetWorker, - multi_threaded = MT, - ref = Ref, - vsns = Vsns, - nfilters = NF, - note_store = NoteStore, - mib_server = MS, - net_if = NetIf, - net_if_mod = NetIfMod, - backup = Backup, - disco = Disco, - mibs_cache_request = MCR} = S1, - S2 = {state, - type = Type, - parent = Parent, - worker = Worker, - worker_state = WorkerState, - set_worker = SetWorker, - multi_threaded = MT, - ref = Ref, - vsns = Vsns, - nfilters = NF, - note_store = NoteStore, - mib_server = MS, - net_if = NetIf, - net_if_mod = NetIfMod, - backup = Backup, - disco = Disco, - mibs_cache_request = MCR}, - {ok, S2}; - -%% Upgrade -%% -code_change(_Vsn, S1, upgrade_from_pre_4_17_3) -> - {state, - type = Type, - parent = Parent, - worker = Worker, - worker_state = WorkerState, - set_worker = SetWorker, - multi_threaded = MT, - ref = Ref, - vsns = Vsns, - nfilters = NF, - note_store = NoteStore, - mib_server = MS, - net_if = NetIf, - net_if_mod = NetIfMod, - backup = Backup, - disco = Disco, - mibs_cache_request = MCR} = S1, - S2 = #state{type = Type, - parent = Parent, - worker = Worker, - worker_state = WorkerState, - set_worker = SetWorker, - multi_threaded = MT, - ref = Ref, - vsns = Vsns, - nfilters = NF, - note_store = NoteStore, - mib_server = MS, - net_if = NetIf, - net_if_mod = NetIfMod, - backup = Backup, - disco = Disco, - mibs_cache_request = MCR, - gb_max_vbs = ?DEFAULT_GB_MAX_VBS}, - {ok, S2}; +%% code_change({down, _Vsn}, S1, downgrade_to_pre_4_17_3) -> +%% #state{type = Type, +%% parent = Parent, +%% worker = Worker, +%% worker_state = WorkerState, +%% set_worker = SetWorker, +%% multi_threaded = MT, +%% ref = Ref, +%% vsns = Vsns, +%% nfilters = NF, +%% note_store = NoteStore, +%% mib_server = MS, +%% net_if = NetIf, +%% net_if_mod = NetIfMod, +%% backup = Backup, +%% disco = Disco, +%% mibs_cache_request = MCR} = S1, +%% S2 = {state, +%% type = Type, +%% parent = Parent, +%% worker = Worker, +%% worker_state = WorkerState, +%% set_worker = SetWorker, +%% multi_threaded = MT, +%% ref = Ref, +%% vsns = Vsns, +%% nfilters = NF, +%% note_store = NoteStore, +%% mib_server = MS, +%% net_if = NetIf, +%% net_if_mod = NetIfMod, +%% backup = Backup, +%% disco = Disco, +%% mibs_cache_request = MCR}, +%% {ok, S2}; + +%% %% Upgrade +%% %% +%% code_change(_Vsn, S1, upgrade_from_pre_4_17_3) -> +%% {state, +%% type = Type, +%% parent = Parent, +%% worker = Worker, +%% worker_state = WorkerState, +%% set_worker = SetWorker, +%% multi_threaded = MT, +%% ref = Ref, +%% vsns = Vsns, +%% nfilters = NF, +%% note_store = NoteStore, +%% mib_server = MS, +%% net_if = NetIf, +%% net_if_mod = NetIfMod, +%% backup = Backup, +%% disco = Disco, +%% mibs_cache_request = MCR} = S1, +%% S2 = #state{type = Type, +%% parent = Parent, +%% worker = Worker, +%% worker_state = WorkerState, +%% set_worker = SetWorker, +%% multi_threaded = MT, +%% ref = Ref, +%% vsns = Vsns, +%% nfilters = NF, +%% note_store = NoteStore, +%% mib_server = MS, +%% net_if = NetIf, +%% net_if_mod = NetIfMod, +%% backup = Backup, +%% disco = Disco, +%% mibs_cache_request = MCR, +%% gb_max_vbs = ?DEFAULT_GB_MAX_VBS}, +%% {ok, S2}; code_change(_Vsn, S, _Extra) -> {ok, S}. @@ -4411,7 +4411,7 @@ get_mibs(Opts) -> get_option(mibs, Opts, []). get_mib_storage(Opts) -> - get_option(mib_storage, Opts, ets). + get_option(mib_storage, Opts). get_set_mechanism(Opts) -> get_option(set_mechanism, Opts, snmpa_set). @@ -4450,6 +4450,9 @@ net_if_verbosity(_Pid,_Verbosity) -> ok. +get_option(Key, Opts) -> + snmp_misc:get_option(Key, Opts). + get_option(Key, Opts, Default) -> snmp_misc:get_option(Key, Opts, Default). -- cgit v1.2.3 From ba6cdcc9a0a477938c99d92a7526b61ec2c70400 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 22 May 2013 14:16:10 +0200 Subject: [snmp/agent] Cosmetic --- lib/snmp/src/agent/snmpa_supervisor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl index 1c8db7ff07..aebcdbaa84 100644 --- a/lib/snmp/src/agent/snmpa_supervisor.erl +++ b/lib/snmp/src/agent/snmpa_supervisor.erl @@ -318,7 +318,7 @@ init([AgentType, Opts]) -> Other end, - ?vdebug("[agent table] store mib storage: ~w",[MibStorage]), + ?vdebug("[agent table] store mib storage: ~w", [MibStorage]), ets:insert(snmp_agent_table, {mib_storage, MibStorage}), %% -- Agent mib storage -- @@ -463,7 +463,7 @@ init([AgentType, Opts]) -> AgentSpec = worker_spec(snmpa_agent, - [Prio,snmp_master_agent,none,Ref,AgentOpts], + [Prio, snmp_master_agent, none, Ref, AgentOpts], Restart, 15000), AgentSupSpec = sup_spec(snmpa_agent_sup, [AgentSpec], -- cgit v1.2.3 From dccaf2ca6b85ceaf05ea43262e18af9f63a76b02 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 11:16:04 +0200 Subject: [snmp/agent] Add more verbosity printouts to the mnesia mib-storage module --- lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl index a1f2be9af3..dca44d3c33 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl @@ -64,11 +64,13 @@ open(Name, RecName, Fields, Type, Opts) -> ?vtrace("open ~p table ~p for record ~p", [Type, Name, RecName]), Action = snmp_misc:get_option(action, Opts, keep), - Nodes = snmp_misc:get_option(nodes, Opts, [node()]), + Nodes = snmp_misc:get_option(nodes, Opts, erlang:nodes()), case table_exists(Name) of true when (Action =:= keep) -> + ?vtrace("open table ~p - exist (keep)", [Name]), {ok, #tab{id = Name}}; true when (Action =:= clear) -> + ?vtrace("open table ~p - exist (clear)", [Name]), F = fun() -> mnesia:clear_table(Name) end, case mnesia:transaction(F) of {aborted, Reason} -> @@ -77,14 +79,18 @@ open(Name, RecName, Fields, Type, Opts) -> {ok, #tab{id = Name}} end; false -> + ?vtrace("open table ~p - does not exist", [Name]), Args = [{record_name, RecName}, {attributes, Fields}, {type, Type}, {disc_copies, Nodes}], case mnesia:create_table(Name, Args) of {atomic, ok} -> + ?vtrace("open table ~p - ok", [Name]), {ok, #tab{id = Name}}; {aborted, Reason} -> + ?vinfo("open table ~p - aborted" + "~n Reason: ~p", [Name, Reason]), {error, {create, Reason}} end end. -- cgit v1.2.3 From 6703fc56a382e62530ba23cb5452da6f4cccbe91 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 11:17:21 +0200 Subject: [snmp/agent] Updated the config tool with new mib-storage format --- lib/snmp/src/app/snmp_app.erl | 14 +++++++------- lib/snmp/src/misc/snmp_config.erl | 22 +++++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/snmp/src/app/snmp_app.erl b/lib/snmp/src/app/snmp_app.erl index deb42cc373..0cfbb22a5a 100644 --- a/lib/snmp/src/app/snmp_app.erl +++ b/lib/snmp/src/app/snmp_app.erl @@ -62,17 +62,17 @@ entities([], []) -> ?d("entities -> converted config: ~n~p", [Conf]), [{agent, Conf}] end; -entities([], E) -> +entities([], Acc) -> ?d("entities -> done", []), - lists:reverse(E); -entities([ET|ETs], E) -> + lists:reverse(Acc); +entities([Ent|Ents], Acc) -> ?d("entities -> entry with" - "~n ET: ~p", [ET]), - case application:get_env(snmp, ET) of + "~n Ent: ~p", [Ent]), + case application:get_env(snmp, Ent) of {ok, Conf} -> - entities(ETs, [{ET, Conf}|E]); + entities(Ents, [{Ent, Conf}|Acc]); _ -> - entities(ETs, E) + entities(Ents, Acc) end. start_entities(_Type, []) -> diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 0bed097b62..22fe25941c 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -238,7 +238,7 @@ config_agent_sys() -> MibStorage = case MibStorageType of ets -> - ets; + [{module, snmpa_mib_storage_ets}]; dets -> DetsDir = ask("6b. Mib storage directory (absolute path)?", DbDir, fun verify_dir/1), @@ -248,13 +248,14 @@ config_agent_sys() -> "default", fun verify_mib_storage_action/1), case DetsAction of default -> - {dets, DetsDir}; + [{module, snmpa_mib_storage_dets}, + {options, [{dir, DetsDir}]}]; _ -> - {dets, DetsDir, DetsAction} + [{module, snmpa_mib_storage_dets}, + {options, [{dir, DetsDir}, + {action, DetsAction}]}] end; mnesia -> -% Nodes = ask("Mib storage nodes?", "none", -% fun verify_mib_storage_nodes/1), Nodes = [], MnesiaAction = ask("6b. Mib storage [mnesia] database start " "action " @@ -262,11 +263,18 @@ config_agent_sys() -> "default", fun verify_mib_storage_action/1), case MnesiaAction of default -> - {mnesia, Nodes}; + [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, Nodes}]}]; _ -> - {mnesia, Nodes, MnesiaAction} + [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, Nodes}, + {action, MnesiaAction}]}] end end, + + %% Here we should ask about mib-server data module, + %% but as we only have one at the moment... + TargetCacheVerb = ask("7. Target cache verbosity " "(silence/info/log/debug/trace)?", "silence", fun verify_verbosity/1), -- cgit v1.2.3 From 69981c172621ed6f2e9fa7495b583af42f64c027 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 11:18:54 +0200 Subject: [snmp/agent] Update agent test (sub-) suite The agent test (sub-) suite updated according to the new mib-storage format. Also changed config of started agent to the "new" format. --- lib/snmp/test/snmp_agent_test.erl | 207 ++++++++++++++++++++++------------ lib/snmp/test/snmp_agent_test_lib.erl | 102 ++++++++--------- 2 files changed, 185 insertions(+), 124 deletions(-) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index a8e921d67d..5216b82cf8 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -456,14 +456,14 @@ end_per_testcase2(_Case, Config) -> cases() -> [ - {group, misc}, - {group, test_v1}, - {group, test_v2}, - {group, test_v1_v2}, - {group, test_v3}, - {group, test_multi_threaded}, - {group, mib_storage}, - {group, tickets1} + {group, misc}, + {group, test_v1}, + {group, test_v2}, + {group, test_v1_v2}, + {group, test_v3}, + {group, test_multi_threaded}, + {group, mib_storage}, + {group, tickets1} ]. @@ -549,7 +549,7 @@ delete_tables() -> mnesia:delete_table(kompissTable2), mnesia:delete_table(snmp_variables). -%% Creation is done in runtime! +%% Tables are created in runtime! delete_mib_storage_mnesia_tables() -> mnesia:delete_table(snmpa_mib_data), mnesia:delete_table(snmpa_mib_tree), @@ -572,40 +572,77 @@ delete_mib_storage_mnesia_tables() -> %% versions as well, _N. %%----------------------------------------------------------------- - - - - - - - - mib_storage_ets_cases() -> -[mse_simple, mse_v1_processing, mse_big, mse_big2, - mse_loop_mib, mse_api, mse_sa_register, mse_v1_trap, - mse_sa_error, mse_next_across_sa, mse_undo, - mse_standard_mib, mse_community_mib, mse_framework_mib, - mse_target_mib, mse_notification_mib, - mse_view_based_acm_mib, mse_sparse_table, mse_me_of, - mse_mib_of]. + [ + mse_simple, + mse_v1_processing, + mse_big, + mse_big2, + mse_loop_mib, + mse_api, + mse_sa_register, + mse_v1_trap, + mse_sa_error, + mse_next_across_sa, + mse_undo, + mse_standard_mib, + mse_community_mib, + mse_framework_mib, + mse_target_mib, + mse_notification_mib, + mse_view_based_acm_mib, + mse_sparse_table, + mse_me_of, + mse_mib_of + ]. mib_storage_dets_cases() -> -[msd_simple, msd_v1_processing, msd_big, msd_big2, - msd_loop_mib, msd_api, msd_sa_register, msd_v1_trap, - msd_sa_error, msd_next_across_sa, msd_undo, - msd_standard_mib, msd_community_mib, msd_framework_mib, - msd_target_mib, msd_notification_mib, - msd_view_based_acm_mib, msd_sparse_table, msd_me_of, - msd_mib_of]. + [ + msd_simple, + msd_v1_processing, + msd_big, + msd_big2, + msd_loop_mib, + msd_api, + msd_sa_register, + msd_v1_trap, + msd_sa_error, + msd_next_across_sa, + msd_undo, + msd_standard_mib, + msd_community_mib, + msd_framework_mib, + msd_target_mib, + msd_notification_mib, + msd_view_based_acm_mib, + msd_sparse_table, + msd_me_of, + msd_mib_of + ]. mib_storage_mnesia_cases() -> -[msm_simple, msm_v1_processing, msm_big, msm_big2, - msm_loop_mib, msm_api, msm_sa_register, msm_v1_trap, - msm_sa_error, msm_next_across_sa, msm_undo, - msm_standard_mib, msm_community_mib, msm_framework_mib, - msm_target_mib, msm_notification_mib, - msm_view_based_acm_mib, msm_sparse_table, msm_me_of, - msm_mib_of]. + [ + msm_simple, + msm_v1_processing, + msm_big, + msm_big2, + msm_loop_mib, + msm_api, + msm_sa_register, + msm_v1_trap, + msm_sa_error, + msm_next_across_sa, + msm_undo, + msm_standard_mib, + msm_community_mib, + msm_framework_mib, + msm_target_mib, + msm_notification_mib, + msm_view_based_acm_mib, + msm_sparse_table, + msm_me_of, + msm_mib_of + ]. mse_size_check_cases() -> [mse_size_check]. @@ -624,18 +661,21 @@ varm_mib_storage_mnesia_cases() -> init_mib_storage_ets(Config) when is_list(Config) -> ?LOG("init_mib_storage_ets -> entry", []), - MibStorage = {snmp_mib_storage,ets}, + MibStorage = {mib_storage, [{module, snmpa_mib_storage_ets}]}, init_ms(Config, [MibStorage]). init_mib_storage_dets(Config) when is_list(Config) -> ?LOG("init_mib_storage_dets -> entry", []), ?line AgentDbDir = ?GCONF(agent_db_dir, Config), - MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, + MibStorage = {mib_storage, [{module, snmpa_mib_storage_dets}, + {options, [{dir, AgentDbDir}]}]}, init_ms(Config, [MibStorage]). init_mib_storage_mnesia(Config) when is_list(Config) -> ?LOG("init_mib_storage_mnesia -> entry", []), - MibStorage = {snmp_mib_storage, {mnesia,[]}}, + ?line AgentNode = ?GCONF(snmp_master, Config), + MibStorage = {mib_storage, [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, [AgentNode]}]}]}, init_ms(Config, [MibStorage]). init_ms(Config, Opts) when is_list(Config) -> @@ -649,23 +689,26 @@ init_ms(Config, Opts) when is_list(Config) -> ?line Ip = ?GCONF(ip, Config), ?line config([v1], MgrDir, AgentConfDir, tuple_to_list(Ip), tuple_to_list(Ip)), - MasterAgentVerbosity = {snmp_master_agent_verbosity, trace}, - MibsVerbosity = {snmp_mibserver_verbosity, trace}, - SymStoreVerbosity = {snmp_symbolic_store_verbosity, trace}, + MasterAgentVerbosity = {agent_verbosity, trace}, + MibsVerbosity = {mib_server, [{verbosity, trace}]}, + SymStoreVerbosity = {symbolic_store, [{verbosity, trace}]}, Opts1 = [MasterAgentVerbosity, MibsVerbosity, SymStoreVerbosity | Opts], [{vsn, v1} | start_v1_agent(Config, Opts1)]. init_size_check_mse(Config) when is_list(Config) -> - MibStorage = {snmp_mib_storage, ets}, + MibStorage = {mib_storage, [{module, snmpa_mib_storage_ets}]}, init_size_check_ms(Config, [MibStorage]). init_size_check_msd(Config) when is_list(Config) -> AgentDbDir = ?GCONF(agent_db_dir, Config), - MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, + MibStorage = {mib_storage, [{module, snmpa_mib_storage_dets}, + {options, [{dir, AgentDbDir}]}]}, init_size_check_ms(Config, [MibStorage]). init_size_check_msm(Config) when is_list(Config) -> - MibStorage = {snmp_mib_storage, {mnesia,[]}}, + ?line AgentNode = ?GCONF(snmp_master, Config), + MibStorage = {mib_storage, [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, [AgentNode]}]}]}, init_size_check_ms(Config, [MibStorage]). init_size_check_ms(Config, Opts) when is_list(Config) -> @@ -700,12 +743,16 @@ init_varm_mib_storage_dets(Config) when is_list(Config) -> ?line Ip = ?GCONF(ip, Config), ?line config([v1], MgrDir, AgentConfDir, tuple_to_list(Ip), tuple_to_list(Ip)), - MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, - MasterAgentVerbosity = {snmp_master_agent_verbosity, trace}, - MibsVerbosity = {snmp_mibserver_verbosity, trace}, - SymStoreVerbosity = {snmp_symbolic_store_verbosity, trace}, - Opts = [MibStorage,MasterAgentVerbosity,MibsVerbosity,SymStoreVerbosity], - [{vsn, v1}, {agent_opts,Opts} | Config]. + MibStorage = {mib_storage, [{module, snmpa_mib_storage_dets}, + {options, [{dir, AgentDbDir}]}]}, + MasterAgentVerbosity = {agent_verbosity, trace}, + MibsVerbosity = {mib_server, [{verbosity, trace}]}, + SymStoreVerbosity = {symbolic_store, [{verbosity, trace}]}, + Opts = [MibStorage, + MasterAgentVerbosity, + MibsVerbosity, + SymStoreVerbosity], + [{vsn, v1}, {agent_opts, Opts} | Config]. init_varm_mib_storage_mnesia(Config) when is_list(Config) -> ?LOG("init_varm_mib_storage_mnesia -> entry", []), @@ -716,12 +763,17 @@ init_varm_mib_storage_mnesia(Config) when is_list(Config) -> ?line Ip = ?GCONF(ip, Config), ?line config([v1], MgrDir, AgentConfDir, tuple_to_list(Ip), tuple_to_list(Ip)), - MibStorage = {snmp_mib_storage,{mnesia,[]}}, - MasterAgentVerbosity = {snmp_master_agent_verbosity, trace}, - MibsVerbosity = {snmp_mibserver_verbosity, trace}, - SymStoreVerbosity = {snmp_symbolic_store_verbosity, trace}, - Opts = [MibStorage,MasterAgentVerbosity,MibsVerbosity,SymStoreVerbosity], - [{vsn, v1}, {agent_opts,Opts} | Config]. + ?line AgentNode = ?GCONF(snmp_master, Config), + MibStorage = {mib_storage, [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, [AgentNode]}]}]}, + MasterAgentVerbosity = {agent_verbosity, trace}, + MibsVerbosity = {mib_server, [{verbosity, trace}]}, + SymStoreVerbosity = {symbolic_store, [{verbosity, trace}]}, + Opts = [MibStorage, + MasterAgentVerbosity, + MibsVerbosity, + SymStoreVerbosity], + [{vsn, v1}, {agent_opts, Opts} | Config]. finish_mib_storage_ets(Config) when is_list(Config) -> ?LOG("finish_mib_storage_ets -> entry", []), @@ -1117,7 +1169,10 @@ finish_misc(Config) -> finish_v1(Config). misc_cases() -> -[app_info, info_test]. + [ + app_info, + info_test + ]. app_info(suite) -> []; app_info(Config) when is_list(Config) -> @@ -1816,23 +1871,38 @@ mnesia_2(X) -> ?P(mnesia_2), mnesia(X). mnesia_3(X) -> ?P(mnesia_3), mnesia(X). - mul_cases() -> -[mul_get, mul_get_err, mul_next, mul_next_err, - mul_set_err]. - + [ + mul_get, + mul_get_err, + mul_next, + mul_next_err, + mul_set_err + ]. + multiple_reqs_3(_X) -> {req, [], {conf, init_mul, mul_cases_3(), finish_mul}}. mul_cases_2() -> -[mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2, - mul_set_err_2]. - + [ + mul_get_2, + mul_get_err_2, + mul_next_2, + mul_next_err_2, + mul_set_err_2 + ]. + mul_cases_3() -> - [mul_get_3, mul_get_err_3, mul_next_3, mul_next_err_3, mul_set_err_3]. + [ + mul_get_3, + mul_get_err_3, + mul_next_3, + mul_next_err_3, + mul_set_err_3 + ]. init_mul(Config) when is_list(Config) -> @@ -2055,7 +2125,6 @@ v3_trap(Config) when is_list(Config) -> v3_inform(_X) -> - %% v2_inform(X). {req, [], {conf, init_v3_inform, [v3_inform_i], finish_v3_inform}}. init_v2_inform(Config) when is_list(Config) -> diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 757aebfa9b..5261a9c40e 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -28,7 +28,7 @@ start_mt_agent/1, start_mt_agent/2, stop_agent/1, - start_sup/0, stop_sup/2, + %% start_sup/0, stop_sup/2, start_subagent/3, stop_subagent/1, start_sub_sup/1, start_sub_sup/2, @@ -418,10 +418,10 @@ start_bilingual_agent(Config, Opts) start_agent(Config, [v1,v2], Opts). start_mt_agent(Config) when is_list(Config) -> - start_agent(Config, [v2], [{snmp_multi_threaded, true}]). + start_agent(Config, [v2], [{multi_threaded, true}]). start_mt_agent(Config, Opts) when is_list(Config) andalso is_list(Opts) -> - start_agent(Config, [v2], [{snmp_multi_threaded, true}|Opts]). + start_agent(Config, [v2], [{multi_threaded, true}|Opts]). start_agent(Config, Vsns) -> start_agent(Config, Vsns, []). @@ -437,65 +437,53 @@ start_agent(Config, Vsns, Opts) -> ?line AgentDbDir = ?config(agent_db_dir, Config), ?line SaNode = ?config(snmp_sa, Config), - app_env_init(vsn_init(Vsns) ++ - [{audit_trail_log, read_write_log}, - {audit_trail_log_dir, AgentLogDir}, - {audit_trail_log_size, {10240, 10}}, - {force_config_reload, false}, - {snmp_agent_type, master}, - {snmp_config_dir, AgentConfDir}, - {snmp_db_dir, AgentDbDir}, - {snmp_local_db_auto_repair, true}, - {snmp_local_db_verbosity, log}, - {snmp_master_agent_verbosity, trace}, - {snmp_supervisor_verbosity, trace}, - {snmp_mibserver_verbosity, log}, - {snmp_symbolic_store_verbosity, log}, - {snmp_note_store_verbosity, log}, - {snmp_net_if_verbosity, trace}], - Opts), - + Env = app_agent_env_init( + [{versions, Vsns}, + {agent_type, master}, + {agent_verbosity, trace}, + {audit_trail_log, [{type, read_write}, + {dir, AgentLogDir}, + {size, {10240, 10}}]}, + {config, [{dir, AgentConfDir}, + {force_load, false}, + {verbosity, trace}]}, + {db_dir, AgentDbDir}, + {local_db, [{repair, true}, + {verbosity, log}]}, + {mib_server, [{verbosity, log}]}, + {symbolic_store, [{verbosity, log}]}, + {note_store, [{verbosity, log}]}, + {net_if, [{verbosity, trace}]}], + Opts), + process_flag(trap_exit,true), {ok, AppSup} = snmp_app_sup:start_link(), unlink(AppSup), - ?DBG("start_agent -> snmp app supervisor: ~p",[AppSup]), + ?DBG("start_agent -> snmp app supervisor: ~p", [AppSup]), - ?DBG("start_agent -> start master agent (old style)",[]), - ?line Sup = start_sup(), + ?DBG("start_agent -> start master agent",[]), + ?line Sup = start_sup(Env), - ?DBG("start_agent -> unlink from supervisor",[]), + ?DBG("start_agent -> unlink from supervisor", []), ?line unlink(Sup), ?line SaDir = ?config(sa_dir, Config), - ?DBG("start_agent -> (rpc) start sub on ~p",[SaNode]), + ?DBG("start_agent -> (rpc) start sub on ~p", [SaNode]), ?line {ok, Sub} = start_sub_sup(SaNode, SaDir), ?DBG("start_agent -> done",[]), ?line [{snmp_sup, {Sup, self()}}, {snmp_sub, Sub} | Config]. -vsn_init(Vsn) -> - vsn_init([v1,v2,v3], Vsn, []). - -vsn_init([], _Vsn, Acc) -> - Acc; -vsn_init([V|Vsns], Vsn, Acc) -> - case lists:member(V, Vsn) of - true -> - vsn_init(Vsns, Vsn, [{V, true}|Acc]); - false -> - vsn_init(Vsns, Vsn, [{V, false}|Acc]) - end. - -app_env_init(Env0, Opts) -> - ?DBG("app_env_init -> unload snmp",[]), +app_agent_env_init(Env0, Opts) -> + ?DBG("app_agent_env_init -> unload snmp",[]), ?line application:unload(snmp), - ?DBG("app_env_init -> load snmp",[]), + ?DBG("app_agent_env_init -> load snmp",[]), ?line application:load(snmp), - ?DBG("app_env_init -> initiate (snmp) application env",[]), + ?DBG("app_agent_env_init -> initiate (snmp agent) application env",[]), F1 = fun({Key, Val} = New, Acc0) -> - ?DBG("app_env_init -> " - "updating setting ~p to ~p", [Key, Val]), + ?DBG("app_agent_env_init -> " + "updating setting ~p to ~p", [Key, Val]), case lists:keyreplace(Key, 1, Acc0, New) of Acc0 -> [New|Acc0]; @@ -504,12 +492,16 @@ app_env_init(Env0, Opts) -> end end, Env = lists:foldr(F1, Env0, Opts), - ?DBG("app_env_init -> Env: ~p",[Env]), - F2 = fun({Key,Val}) -> - ?DBG("app_env_init -> setting ~p to ~p",[Key, Val]), - application_controller:set_env(snmp, Key, Val) - end, - lists:foreach(F2, Env). + ?DBG("app_agent_env_init -> Env: ~p", [Env]), + %% We put it into the app environment just as + %% a precaution, since when starting normally, + %% there is where the environment is taken from. + app_set_agent_env(Env), + Env. + +app_set_agent_env(Value) -> + application_controller:set_env(snmp, agent, Value). + stop_agent(Config) when is_list(Config) -> @@ -544,8 +536,8 @@ stop_agent(Config) when is_list(Config) -> lists:keydelete(snmp_sub, 1, C1). -start_sup() -> - case (catch snmpa_app:start(normal)) of +start_sup(Env) -> + case (catch snmp_app_sup:start_agent(normal, Env)) of {ok, S} -> ?DBG("start_agent -> started, Sup: ~p",[S]), S; @@ -553,7 +545,7 @@ start_sup() -> Else -> ?DBG("start_agent -> unknown result: ~n~p",[Else]), %% Get info about the apps we depend on - ?FAIL({start_failed,Else, ?IS_MNESIA_RUNNING()}) + ?FAIL({start_failed, Else, ?IS_MNESIA_RUNNING()}) end. stop_sup(Pid, _) when (node(Pid) =:= node()) -> @@ -594,7 +586,7 @@ start_sub_sup(Node, Dir) -> start_sub_sup(Dir) -> ?DBG("start_sub -> entry",[]), - Opts = [{db_dir, Dir}, + Opts = [{db_dir, Dir}, {supervisor, [{verbosity, trace}]}], {ok, P} = snmpa_supervisor:start_sub_sup(Opts), unlink(P), -- cgit v1.2.3 From 39540e7c380f6cb218a2ec6324f6296fa8327dba Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 12:35:38 +0200 Subject: [snmp/agent] Updated open options for the mnesia mib-storage module Updated the snmpa_mib_storage_mnesia module to handle alias atoms for the nodes option. Also, (git) added mib-storage behaviour ref-man. --- lib/snmp/doc/src/snmp_app.xml | 29 ++- lib/snmp/doc/src/snmp_config.xml | 29 ++- lib/snmp/doc/src/snmpa_mib_storage.xml | 292 ++++++++++++++++++++++++ lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl | 31 ++- 4 files changed, 373 insertions(+), 8 deletions(-) create mode 100644 lib/snmp/doc/src/snmpa_mib_storage.xml diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index c62c8f1541..f5a6de1099 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -329,7 +329,7 @@ behaviour.

Several entities (mib-server via the its data module and the symbolic-store) of the snmp agent uses this for storage - of miscelaneous mib data.

+ of miscelaneous mib related data retrieved while loading a mib.

There are several implementations provided with the agent: snmpa_mib_storage_ets, snmpa_mib_storage_dets and snmpa_mib_storage_mnesia.

@@ -340,7 +340,8 @@ ]]>

This is implementattion depended. That is, it depends on the - module. For each module a specific set of options are valid:

+ module. For each module a specific set of options are valid. + For the module provided with the app, these options are supported:

snmpa_mib_storage_ets: {dir, filename()} | {action, keep | clear}, {checksum, boolean()}

@@ -395,7 +396,29 @@

Default is keep.

-

nodes - Defines where to open the table.

+

nodes - A list of node names (or an atom + describing a list of nodes) defining where to open the table. + Its up to the user to ensure that mnesia is actually running + on the specified nodes.

+

The following distinct values are recognised:

+ + +

[] - Translated into a list of the own node: [node()]

+
+ +

all - erlang:nodes()

+
+ +

visible - erlang:nodes(visible)

+
+ +

connected - erlang:nodes(connected)

+
+ +

db_nodes - mnesia:system_info(db_nodes)

+
+
+

Default is the result of the call: erlang:nodes().

diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index a88111085f..f1acebf2f7 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -326,7 +326,7 @@ behaviour.

Several entities (mib-server via the its data module and the symbolic-store) of the snmp agent uses this for storage - of miscelaneous mib data.

+ of miscelaneous mib related data dataretrieved while loading a mib.

There are several implementations provided with the agent: snmpa_mib_storage_ets, snmpa_mib_storage_dets and snmpa_mib_storage_mnesia.

@@ -337,7 +337,8 @@ ]]>

This is implementattion depended. That is, it depends on the - module. For each module a specific set of options are valid:

+ module. For each module a specific set of options are valid. + For the module provided with the app, these options are supported:

snmpa_mib_storage_ets: {dir, filename()} | {action, keep | clear}, {checksum, boolean()}

@@ -392,7 +393,29 @@

Default is keep.

-

nodes - Defines where to open the table.

+

nodes - A list of node names (or an atom + describing a list of nodes) defining where to open the table. + Its up to the user to ensure that mnesia is actually running + on the specified nodes.

+

The following distinct values are recognised:

+ + +

[] - Translated into a list of the own node: [node()]

+
+ +

all - erlang:nodes()

+
+ +

visible - erlang:nodes(visible)

+
+ +

connected - erlang:nodes(connected)

+
+ +

db_nodes - mnesia:system_info(db_nodes)

+
+
+

Default is the result of the call: erlang:nodes().

diff --git a/lib/snmp/doc/src/snmpa_mib_storage.xml b/lib/snmp/doc/src/snmpa_mib_storage.xml new file mode 100644 index 0000000000..a857ce79e8 --- /dev/null +++ b/lib/snmp/doc/src/snmpa_mib_storage.xml @@ -0,0 +1,292 @@ + + + + +
+ + 20132013 + Ericsson AB. 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. + + + + snmpa_mib_storage + + + + + snmpa_mib_storage.xml +
+ + snmpa_mib_storage + + Behaviour module for the SNMP agent mib storage. + + +

This module defines the behaviour of the SNMP agent mib storage.

+

The mib storage is used by the agent to store internal mib- + related information. The mib storage module is used by several entities, + not just the mib-server.

+ +

A snmpa_mib_storage compliant module + must export the following functions:

+ + + open/5 + + + close/1 + + + read/2 + + + write/2 + + + delete/1 + + + delete/2 + + + match_object/2 + + + match_delete/2 + + + tab2list/1 + + + info/1 + + + sync/1 + + + backup/2 + + + +

The semantics of them and their exact signatures are + explained below.

+ +
+ +
+ CALLBACK FUNCTIONS +

The following functions must be exported from a + mib-server data callback module:

+ + +
+ + + + Module:open(Name, RecordName, Fields, Type, Options) -> {ok, TabId} | {error, Reason} + Create new (mib-server) data instance + + Name = atom() + RecordName = atom() + Fields = [atom()] + Type = set | bag() + Options = list() + TabId = term() + Reason = term() + + +

Create or open a mib storage table.

+

Note that the RecordName and Fields arguments + my not be used in all implementations (they are actually only + needed for mnesia-based implementations).

+ +

Note also that the Options argument comes from + the options config option of the mib-storage config option, + and is passed on as is.

+ + +
+
+ + + Module:close(TabId) -> void() + Close the mib-storage table + + State = term() + + +

Close the mib-storage table.

+ + +
+
+ + + Module:read(TabId, Key) -> false | {value, Record} + Read a record from the mib-storage table + + TabId = term() + Key = term() + Record = tuple() + + +

Read a record from the mib-storage table.

+ + +
+
+ + + Module:write(TabId, Record) -> ok | {error, Reason} + Write a record to the mib-storage table + + TabId = term() + Record = tuple() + Reason = term() + + +

Write a record to the mib-storage table.

+ + +
+
+ + + Module:delete(TabId) -> void() + Delete an entire mib-storage table + + TabId = term() + + +

Delete an entire mib-storage table.

+ + +
+
+ + + Module:delete(TabId, Key) -> ok | {error, Reason} + Delete a record from the mib-storage table + + TabId = term() + Key = term() + Reason = term() + + +

Delete a record from the mib-storage table.

+ + +
+
+ + + Module:match_object(TabId, Pattern) -> {ok, Recs} | {error, Reason} + Search the mib-storage table for record matching pattern + + TabId = term() + Pattern = match_pattern() + Recs = [tuple()] + Reason = term() + + +

Search the mib-storage table for record that match the + specified pattern.

+ + +
+
+ + + Module:match_delete(TabId, Pattern) -> {ok, Recs} | {error, Reason} + Delete records in the mib-storage table matching pattern + + TabId = term() + Pattern = match_pattern() + Recs = [tuple()] + Reason = term() + + +

Search the mib-storage table for record that match the + specified pattern and then delete them. The records deleted are + also returned.

+ + +
+
+ + + Module:tab2list(TabId) -> Recs + Return all records of the mib-storage table + + TabId = term() + Recs = [tuple()] + + +

Return all records in the mib-storage table in the form + of a list.

+ + +
+
+ + + Module:info(TabId) -> {ok, Info} | {error, Reason} + Returns information about the mib-storage table. + + TabId = term() + Info = term() + Reason = term() + + +

Retrieve implementation dependent mib-storage table + information.

+ + +
+
+ + + Module:sync(TabId) -> void() + Synchronize mib-storage table + + TabId = term() + + +

Synchronize the mib-storage table.

+

What this means, if anything, is implementation dependent.

+ + +
+
+ + + Module:backup(TabId, BackupDir) -> ok | {error, Reason} + Perform a backup of the mib-storage table + + TabId = term() + BackupDir = string() + Reason = term() + + +

Perform a backup of the mib-storage table.

+

What this means, if anything, is implementation dependent.

+ +
+
+ +
+ +
+ diff --git a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl index dca44d3c33..192b5aa26e 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_mnesia.erl @@ -63,8 +63,8 @@ open(Name, RecName, Fields, Type, Opts) -> ?vtrace("open ~p table ~p for record ~p", [Type, Name, RecName]), - Action = snmp_misc:get_option(action, Opts, keep), - Nodes = snmp_misc:get_option(nodes, Opts, erlang:nodes()), + Action = get_action(Opts), + Nodes = get_nodes(Opts), case table_exists(Name) of true when (Action =:= keep) -> ?vtrace("open table ~p - exist (keep)", [Name]), @@ -271,5 +271,32 @@ backup(_, _) -> %%---------------------------------------------------------------------- +get_action(Opts) -> + snmp_misc:get_option(action, Opts, keep). + +get_nodes(Opts) -> + case snmp_misc:get_option(nodes, Opts, erlang:nodes()) of + [] -> + [node()]; + Nodes when is_list(Nodes) -> + Nodes; + all -> + erlang:nodes(); + visible -> + erlang:nodes(visible); + connected -> + erlang:nodes(connected); + db_nodes -> + try mnesia:system_info(db_nodes) of + DbNodes when is_list(DbNodes) -> + DbNodes; + _ -> + erlang:nodes() + catch + _:_ -> + erlang:nodes() + end + end. + %% user_err(F, A) -> %% snmpa_error:user_err(F, A). -- cgit v1.2.3 From 38472ca2c30906d55dc0bee8f9fc2ed97b8576f3 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 15:45:57 +0200 Subject: [snmp/agent] Merging test case specific and common config env There is a set of default agent config environment values used when starting an agent. For specififc group of test cases, other values is used. These two groups of environment is now merged in a more controlled way. Also test case grouping make use of more test cases functions (in order to make the groups() function more readable). --- lib/snmp/test/snmp_agent_test.erl | 255 ++++++++++++++++++---------------- lib/snmp/test/snmp_agent_test_lib.erl | 194 +++++++++++++++++++++++--- 2 files changed, 314 insertions(+), 135 deletions(-) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 5216b82cf8..01f0e58bc2 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -94,19 +94,8 @@ all() -> groups() -> [ - {all_tcs, [], cases()}, - {mib_storage, [], - [ - {group, mib_storage_ets}, - {group, mib_storage_dets}, - {group, mib_storage_mnesia}, - {group, mib_storage_size_check_ets}, - {group, mib_storage_size_check_dets}, - {group, mib_storage_size_check_mnesia}, - {group, mib_storage_varm_dets}, - {group, mib_storage_varm_mnesia} - ] - }, + {all_tcs, [], cases()}, + {mib_storage, [], mib_storage_cases()}, {mib_storage_ets, [], mib_storage_ets_cases()}, {mib_storage_dets, [], mib_storage_dets_cases()}, {mib_storage_mnesia, [], mib_storage_mnesia_cases()}, @@ -123,109 +112,18 @@ groups() -> {test_multi_threaded, [], mt_cases()}, {multiple_reqs, [], mul_cases()}, {multiple_reqs_2, [], mul_cases_2()}, - {v2_inform, [], - [ - v2_inform_i - ] - }, - {v3_security, [], - [ - v3_crypto_basic, - v3_md5_auth, - v3_sha_auth, - v3_des_priv - ] - }, - {standard_mibs, [], - [ - snmp_standard_mib, - snmp_community_mib, - snmp_framework_mib, - snmp_target_mib, - snmp_notification_mib, - snmp_view_based_acm_mib - ] - }, - {standard_mibs_2, [], - [ - snmpv2_mib_2, - snmp_community_mib_2, - snmp_framework_mib_2, - snmp_target_mib_2, - snmp_notification_mib_2, - snmp_view_based_acm_mib_2 - ] - }, - {standard_mibs_3, [], - [ - snmpv2_mib_3, - snmp_framework_mib_3, - snmp_mpd_mib_3, - snmp_target_mib_3, - snmp_notification_mib_3, - snmp_view_based_acm_mib_3, - snmp_user_based_sm_mib_3 - ] - }, - {reported_bugs, [], - [ - otp_1128, - otp_1129, - otp_1131, - otp_1162, - otp_1222, - otp_1298, - otp_1331, - otp_1338, - otp_1342, - otp_2776, - otp_2979, - otp_3187, - otp_3725 - ] - }, - {reported_bugs_2, [], - [ - otp_1128_2, - otp_1129_2, - otp_1131_2, - otp_1162_2, - otp_1222_2, - otp_1298_2, - otp_1331_2, - otp_1338_2, - otp_1342_2, - otp_2776_2, - otp_2979_2, - otp_3187_2 - ] - }, - {reported_bugs_3, [], - [ - otp_1128_3, - otp_1129_3, - otp_1131_3, - otp_1162_3, - otp_1222_3, - otp_1298_3, - otp_1331_3, - otp_1338_3, - otp_1342_3, - otp_2776_3, - otp_2979_3, - otp_3187_3, - otp_3542 - ] - }, - {tickets1, [], - [ - {group, otp_4394}, - {group, otp_7157} - ] - }, - {tickets2, [], [otp8395, otp9884]}, - {otp_4394, [], [otp_4394_test]}, - {otp_7157, [], [otp_7157_test]} + {v2_inform, [], v2_inform_cases()}, + {v3_security, [], v3_security_cases()}, + {standard_mibs, [], standard_mibs_cases()}, + {standard_mibs_2, [], standard_mibs2_cases()}, + {standard_mibs_3, [], standard_mibs3_cases()}, + {reported_bugs, [], reported_bugs_cases()}, + {reported_bugs_2, [], reported_bugs2_cases()}, + {reported_bugs_3, [], reported_bugs3_cases()}, + {tickets1, [], tickets1_cases()}, + {tickets2, [], tickets2_cases()}, + {otp_4394, [], [otp_4394_test]}, + {otp_7157, [], [otp_7157_test]} ]. @@ -572,6 +470,18 @@ delete_mib_storage_mnesia_tables() -> %% versions as well, _N. %%----------------------------------------------------------------- +mib_storage_cases() -> + [ + {group, mib_storage_ets}, + {group, mib_storage_dets}, + {group, mib_storage_mnesia}, + {group, mib_storage_size_check_ets}, + {group, mib_storage_size_check_dets}, + {group, mib_storage_size_check_mnesia}, + {group, mib_storage_varm_dets}, + {group, mib_storage_varm_mnesia} + ]. + mib_storage_ets_cases() -> [ mse_simple, @@ -2144,6 +2054,10 @@ finish_v3_inform(X) -> finish_v2_inform(X). +v2_inform_cases() -> + [ + v2_inform_i + ]. v2_inform_i(suite) -> []; v2_inform_i(Config) when is_list(Config) -> @@ -2382,6 +2296,15 @@ v3_processing(Config) when is_list(Config) -> %% report, which makes it in sync. The notification-generating %% application times out, and send again. This time it'll work. +v3_security_cases() -> + [ + v3_crypto_basic, + v3_md5_auth, + v3_sha_auth, + v3_des_priv + ]. + + v3_crypto_basic(suite) -> []; v3_crypto_basic(_Config) -> ?P(v3_crypto_basic), @@ -4262,7 +4185,16 @@ bad_return() -> %%% already tested by the normal tests. %%%----------------------------------------------------------------- - +standard_mibs_cases() -> + [ + snmp_standard_mib, + snmp_community_mib, + snmp_framework_mib, + snmp_target_mib, + snmp_notification_mib, + snmp_view_based_acm_mib + ]. + %%----------------------------------------------------------------- %% For this test, the agent is configured for v1. @@ -4346,6 +4278,18 @@ std_mib_write() -> std_mib_asn_err() -> snmp_test_mgr:send_bytes([48,99,67,12,0,0,0,0,0,0,5]). + +standard_mibs2_cases() -> + [ + snmpv2_mib_2, + snmp_community_mib_2, + snmp_framework_mib_2, + snmp_target_mib_2, + snmp_notification_mib_2, + snmp_view_based_acm_mib_2 + ]. + + %%----------------------------------------------------------------- %% For this test, the agent is configured for v2 and v3. %% o Test the counters and control objects in SNMPv2-MIB @@ -4394,6 +4338,19 @@ snmpv2_mib_2(Config) when is_list(Config) -> ?LOG("snmpv2_mib_2 -> done",[]). + +standard_mibs3_cases() -> + [ + snmpv2_mib_3, + snmp_framework_mib_3, + snmp_mpd_mib_3, + snmp_target_mib_3, + snmp_notification_mib_3, + snmp_view_based_acm_mib_3, + snmp_user_based_sm_mib_3 + ]. + + %% Req. SNMPv2-MIB snmpv2_mib_3(suite) -> []; snmpv2_mib_3(Config) when is_list(Config) -> @@ -5306,7 +5263,57 @@ loop_it_2(Oid, N) -> %%% Testing of reported bugs and other tickets. %%%----------------------------------------------------------------- +reported_bugs_cases() -> + [ + otp_1128, + otp_1129, + otp_1131, + otp_1162, + otp_1222, + otp_1298, + otp_1331, + otp_1338, + otp_1342, + otp_2776, + otp_2979, + otp_3187, + otp_3725 + ]. + +reported_bugs2_cases() -> + [ + otp_1128_2, + otp_1129_2, + otp_1131_2, + otp_1162_2, + otp_1222_2, + otp_1298_2, + otp_1331_2, + otp_1338_2, + otp_1342_2, + otp_2776_2, + otp_2979_2, + otp_3187_2 + ]. +reported_bugs3_cases() -> + [ + otp_1128_3, + otp_1129_3, + otp_1131_3, + otp_1162_3, + otp_1222_3, + otp_1298_3, + otp_1331_3, + otp_1338_3, + otp_1342_3, + otp_2776_3, + otp_2979_3, + otp_3187_3, + otp_3542 + ]. + + %%----------------------------------------------------------------- %% Ticket: OTP-1128 %% Slogan: Bug in handling of createAndWait set-requests. @@ -5783,7 +5790,12 @@ otp_3725_test(MaNode) -> %% Slogan: Target mib tag list check invalid %%----------------------------------------------------------------- - +tickets1_cases() -> + [ + {group, otp_4394}, + {group, otp_7157} + ]. + init_otp_4394(Config) when is_list(Config) -> ?DBG("init_otp_4394 -> entry with" @@ -5945,6 +5957,13 @@ otp_7157_test1(MA) -> %% These cases are started in the new way %%----------------------------------------------------------------- +tickets2_cases() -> + [ + otp8395, + otp9884 + ]. + + otp8395({init, Config}) when is_list(Config) -> ?DBG("otp8395(init) -> entry with" "~n Config: ~p", [Config]), diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 5261a9c40e..2c24dd3712 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -441,13 +441,13 @@ start_agent(Config, Vsns, Opts) -> [{versions, Vsns}, {agent_type, master}, {agent_verbosity, trace}, + {db_dir, AgentDbDir}, {audit_trail_log, [{type, read_write}, {dir, AgentLogDir}, {size, {10240, 10}}]}, {config, [{dir, AgentConfDir}, {force_load, false}, {verbosity, trace}]}, - {db_dir, AgentDbDir}, {local_db, [{repair, true}, {verbosity, log}]}, {mib_server, [{verbosity, log}]}, @@ -478,30 +478,190 @@ start_agent(Config, Vsns, Opts) -> app_agent_env_init(Env0, Opts) -> ?DBG("app_agent_env_init -> unload snmp",[]), ?line application:unload(snmp), + ?DBG("app_agent_env_init -> load snmp",[]), ?line application:load(snmp), - ?DBG("app_agent_env_init -> initiate (snmp agent) application env",[]), - F1 = fun({Key, Val} = New, Acc0) -> - ?DBG("app_agent_env_init -> " - "updating setting ~p to ~p", [Key, Val]), - case lists:keyreplace(Key, 1, Acc0, New) of - Acc0 -> - [New|Acc0]; - Acc -> - Acc - end - end, - Env = lists:foldr(F1, Env0, Opts), - ?DBG("app_agent_env_init -> Env: ~p", [Env]), + + ?DBG("app_agent_env_init -> " + "merge or maybe replace (snmp agent) app env",[]), + Env = add_or_maybe_merge_agent_env(Opts, Env0), + ?DBG("app_agent_env_init -> merged env: " + "~n ~p", [Env]), + %% We put it into the app environment just as %% a precaution, since when starting normally, - %% there is where the environment is taken from. - app_set_agent_env(Env), + %% this is where the environment is extracted from. + app_agent_set_env(Env), Env. -app_set_agent_env(Value) -> +app_agent_set_env(Value) -> application_controller:set_env(snmp, agent, Value). +add_or_maybe_merge_agent_env([], Env) -> + ?DBG("merging agent env -> merged", []), + lists:keysort(1, Env); +add_or_maybe_merge_agent_env([{Key, Value1}|Opts], Env) -> + ?DBG("merging agent env -> add, replace or merge ~p", [Key]), + case lists:keysearch(Key, 1, Env) of + {value, {Key, Value1}} -> + %% Identical, move on + ?DBG("merging agent env -> " + "no need to merge ~p - identical - keep: " + "~n ~p", [Key, Value1]), + add_or_maybe_merge_agent_env(Opts, Env); + {value, {Key, Value2}} -> + %% Another value, merge or replace + NewValue = merge_or_replace_agent_env(Key, Value1, Value2), + Env2 = lists:keyreplace(Key, 1, Env, {Key, NewValue}), + add_or_maybe_merge_agent_env(Opts, Env2); + false -> + ?DBG("merging agent env -> no old ~p to merge with - add: " + "~n ~p", [Key, Value1]), + add_or_maybe_merge_agent_env(Opts, [{Key, Value1}|Env]) + end. + +merge_or_replace_agent_env(versions, NewVersions, _OldVersions) -> + ?DBG("merging agent env -> versions replaced: ~p -> ~p", + [NewVersions, _OldVersions]), + NewVersions; +merge_or_replace_agent_env(agent_type, NewType, _OldType) -> + ?DBG("merging agent env -> agent type replaced: ~p -> ~p", + [NewType, _OldType]), + NewType; +merge_or_replace_agent_env(agent_verbosity, NewVerbosity, _OldVerbosity) -> + ?DBG("merging agent env -> agent verbosity replaced: ~p -> ~p", + [NewVerbosity, _OldVerbosity]), + NewVerbosity; +merge_or_replace_agent_env(db_dir, NewDbDir, _OldDbDir) -> + ?DBG("merging agent env -> db-dir replaced: ~p -> ~p", + [NewDbDir, _OldDbDir]), + NewDbDir; +merge_or_replace_agent_env(audit_trail_log, NewATL, OldATL) -> + merge_or_replace_agent_env_atl(NewATL, OldATL); +merge_or_replace_agent_env(config, NewConfig, OldConfig) -> + merge_or_replace_agent_env_config(NewConfig, OldConfig); +merge_or_replace_agent_env(local_db, NewLdb, OldLdb) -> + merge_or_replace_agent_env_ldb(NewLdb, OldLdb); +merge_or_replace_agent_env(mib_storage, NewMst, OldMst) -> + merge_or_replace_agent_env_mib_storage(NewMst, OldMst); +merge_or_replace_agent_env(mib_server, NewMibs, OldMibs) -> + merge_or_replace_agent_env_mib_server(NewMibs, OldMibs); +merge_or_replace_agent_env(symbolic_store, NewSymStore, OldSymStore) -> + merge_or_replace_agent_env_symbolic_store(NewSymStore, OldSymStore); +merge_or_replace_agent_env(note_store, NewNoteStore, OldNoteStore) -> + merge_or_replace_agent_env_note_store(NewNoteStore, OldNoteStore); +merge_or_replace_agent_env(net_if, NewNetIf, OldNetIf) -> + merge_or_replace_agent_env_net_if(NewNetIf, OldNetIf); +merge_or_replace_agent_env(Key, NewValue, OldValue) -> + ?FAIL({not_implemented_merge_or_replace, + Key, NewValue, OldValue}). + +merge_or_replace_agent_env_atl(New, Old) -> + ATL = merge_agent_options(New, Old), + ?DBG("merging agent env -> audit-trail-log merged: " + "~n ~p | ~p -> ~p", [New, Old, ATL]), + ATL. + +merge_or_replace_agent_env_config(New, Old) -> + Config = merge_agent_options(New, Old), + case lists:keymember(dir, 1, Config) of + true -> + ?DBG("merging agent env -> config merged: " + "~n ~p | ~p -> ~p", [New, Old, Config]), + Config; + false -> + ?FAIL({missing_mandatory_option, {config, dir}}) + end. + +merge_or_replace_agent_env_ldb(New, Old) -> + LDB = merge_agent_options(New, Old), + ?DBG("merging agent env -> local-db merged: " + "~n ~p | ~p -> ~p", [New, Old, LDB]), + LDB. + +merge_or_replace_agent_env_mib_storage(NewMibStorage, OldMibStorage) -> + %% Shall we merge or replace? + %% module is mandatory. We will only merge if NewModule is + %% equal to OldModule. + NewModule = + case lists:keysearch(module, 1, NewMibStorage) of + {value, {module, M}} -> + M; + false -> + ?FAIL({missing_mandatory_option, {mib_storage, module}}) + end, + case lists:keysearch(module, 1, OldMibStorage) of + {value, {module, NewModule}} -> + %% Same module => merge + %% Non-ex new options => remove + %% Ex new options and non-ex old options => replace + %% Otherwise merge + case lists:keysearch(options, 1, NewMibStorage) of + false -> + ?DBG("merging agent env -> " + "no mib-storage ~p merge needed - " + "no new options (= remove old options)", [NewModule]), + NewMibStorage; + {value, {options, NewOptions}} -> + case lists:keysearch(options, 1, OldMibStorage) of + false -> + ?DBG("merging agent env -> " + "no mib-storage ~p merge needed - " + "no old options", [NewModule]), + NewMibStorage; + {value, {options, OldOptions}} -> + MergedOptions = + merge_agent_options(NewOptions, OldOptions), + ?DBG("merging agent env -> mib-storage ~p merged: " + "~n Options: ~p | ~p -> ~p", + [NewModule, + NewOptions, OldOptions, MergedOptions]), + [{module, NewModule}, + {options, MergedOptions}] + end + end; + _ -> + %% Diff module => replace + ?DBG("merging agent env -> " + "no mib-storage ~p merge needed - " + "new module", [NewModule]), + NewMibStorage + end. + +merge_or_replace_agent_env_mib_server(New, Old) -> + MibServer = merge_agent_options(New, Old), + ?DBG("merging agent env -> mib-server merged: " + "~n ~p | ~p -> ~p", [New, Old, MibServer]), + MibServer. + +merge_or_replace_agent_env_symbolic_store(New, Old) -> + SymbolicStore = merge_agent_options(New, Old), + ?DBG("merging agent env -> symbolic-store merged: " + "~n ~p | ~p -> ~p", [New, Old, SymbolicStore]), + SymbolicStore. + +merge_or_replace_agent_env_note_store(New, Old) -> + NoteStore = merge_agent_options(New, Old), + ?DBG("merging agent env -> note-store merged: " + "~n ~p | ~p -> ~p", [New, Old, NoteStore]), + NoteStore. + +merge_or_replace_agent_env_net_if(New, Old) -> + NetIf = merge_agent_options(New, Old), + ?DBG("merging agent env -> net-if merged: " + "~n ~p | ~p -> ~p", [New, Old, NetIf]), + NetIf. + +merge_agent_options([], Options) -> + lists:keysort(1, Options); +merge_agent_options([{Key, _Value} = Opt|Opts], Options) -> + case lists:keysearch(Key, 1, Options) of + {value, _} -> + NewOptions = lists:keyreplace(Key, 1, Options, Opt), + merge_agent_options(Opts, NewOptions); + false -> + merge_agent_options(Opts, [Opt|Options]) + end. stop_agent(Config) when is_list(Config) -> -- cgit v1.2.3 From 2fa265b38aeac7c4f29a878af6a7d3c49fe50931 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 17:03:17 +0200 Subject: [snmp/agent] Handle mib-storage ets module non-ex file Make sure snmpa_mib_storage_ets can handle a non-ex file whe n openning a table (it should simply create it). --- lib/snmp/src/agent/snmpa_mib_storage_ets.erl | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl index 4afdc76ee5..852df06fa0 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl @@ -73,19 +73,29 @@ open(Name, RecName, _Fields, Type, Opts) -> {value, {dir, Dir}} -> Action = snmp_misc:get_option(action, Opts, keep), Checksum = snmp_misc:get_option(checksum, Opts, false), - ?vtrace("open ~p database ~p", [Type, Name]), + ?vtrace("open ~p database ~p - check if file exist", [Type, Name]), File = filename:join(Dir, atom_to_list(Name) ++ ".db"), case file:read_file_info(File) of {ok, _} -> + ?vdebug("open ~p database ~p - file exist - try reading", + [Type, Name]), case ets:file2tab(File, [{verify, Checksum}]) of {ok, ID} -> + ?vtrace("open ~p database ~p - " + "data read from file", [Type, Name]), {ok, #tab{id = ID, rec_name = RecName, file = File, checksum = Checksum}}; {error, Reason} when (Action =:= keep) -> + ?vinfo("open ~p database ~p - " + "failed reading from file (keep)", + [Type, Name, Reason]), {error, {file2tab, Reason}}; {error, Reason} -> + ?vlog("open ~p database ~p - " + "failed reading from file (clear)", + [Type, Name, Reason]), user_err("Warning: could not read file - " "create new (empty): " "~n File: ~p" @@ -97,9 +107,26 @@ open(Name, RecName, _Fields, Type, Opts) -> file = File, checksum = Checksum}} end; + {error, enoent} -> + %% No such file - create it + ?vdebug("open ~p database ~p - " + "file does *not* exist - create", + [Type, Name]), + ID = ets:new(Name, [Type, protected, {keypos, 2}]), + write_ets_file(ID, File, Checksum), + {ok, #tab{id = ID, + rec_name = RecName, + file = File, + checksum = Checksum}}; {error, Reason} when (Action =:= keep) -> + ?vinfo("open ~p database ~p - " + "failed reading file info (keep)", + [Type, Name, Reason]), {error, {read_file_info, Reason}}; {error, Reason} -> + ?vlog("open ~p database ~p - " + "failed reading file info (clear)", + [Type, Name, Reason]), user_err("Warning: could not read file info - " "create new: " "~n File: ~p" @@ -112,6 +139,7 @@ open(Name, RecName, _Fields, Type, Opts) -> checksum = Checksum}} end; false -> + ?vdebug("open ~p database ~p - ok", [Type, Name]), ID = ets:new(Name, [Type, protected, {keypos, 2}]), {ok, #tab{id = ID, rec_name = RecName}} end. -- cgit v1.2.3 From 246afa772b2fed132ee236fe335045164a8de56f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 23 May 2013 17:04:39 +0200 Subject: [snmp/agent] Make the mib-server test (sub-) suite use new mib-storage --- lib/snmp/test/snmp_agent_mibs_test.erl | 137 ++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 35 deletions(-) diff --git a/lib/snmp/test/snmp_agent_mibs_test.erl b/lib/snmp/test/snmp_agent_mibs_test.erl index 3e48130fac..4569be0b5a 100644 --- a/lib/snmp/test/snmp_agent_mibs_test.erl +++ b/lib/snmp/test/snmp_agent_mibs_test.erl @@ -19,13 +19,16 @@ %% %%---------------------------------------------------------------------- -%% Purpose: +%% Purpose: Test suite of the agent mib-server. %%---------------------------------------------------------------------- + -module(snmp_agent_mibs_test). + %%---------------------------------------------------------------------- %% Include files %%---------------------------------------------------------------------- + -include_lib("test_server/include/test_server.hrl"). -include("snmp_test_lib.hrl"). -include_lib("snmp/include/snmp_types.hrl"). @@ -39,13 +42,23 @@ %% External exports %%---------------------------------------------------------------------- -export([ - all/0,groups/0,init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2, - init_per_suite/1, end_per_suite/1, + all/0, + groups/0, + + init_per_suite/1, + end_per_suite/1, + + init_per_group/2, + end_per_group/2, + + init_per_testcase/2, + end_per_testcase/2, start_and_stop/1, - size_check_ets/1, + size_check_ets1/1, + size_check_ets2/1, + size_check_ets3/1, size_check_dets/1, size_check_mnesia/1, load_unload/1, @@ -55,6 +68,7 @@ ]). + %%---------------------------------------------------------------------- %% Internal exports %%---------------------------------------------------------------------- @@ -71,6 +85,16 @@ %% External functions %%====================================================================== +init_per_testcase(size_check_ets2, Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + EtsDir = join(Dir, "ets_dir2/"), + ?line ok = file:make_dir(EtsDir), + [{ets_dir, EtsDir}|Config]; +init_per_testcase(size_check_ets3, Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + EtsDir = join(Dir, "ets_dir3/"), + ?line ok = file:make_dir(EtsDir), + [{ets_dir, EtsDir}|Config]; init_per_testcase(size_check_dets, Config) when is_list(Config) -> Dir = ?config(priv_dir, Config), DetsDir = join(Dir, "dets_dir/"), @@ -98,6 +122,13 @@ init_per_testcase(cache_test, Config) when is_list(Config) -> init_per_testcase(_Case, Config) when is_list(Config) -> Config. +end_per_testcase(EtsCase, Config) + when (is_list(Config) andalso + ((EtsCase =:= size_check_ets2) orelse + (EtsCase =:= size_check_ets3))) -> + Dir = ?config(ets_dir, Config), + ?line ok = ?DEL_DIR(Dir), + lists:keydelete(ets_dir, 1, Config); end_per_testcase(size_check_dets, Config) when is_list(Config) -> Dir = ?config(dets_dir, Config), ?line ok = ?DEL_DIR(Dir), @@ -124,18 +155,31 @@ cases(). groups() -> [{size_check, [], - [size_check_ets, size_check_dets, size_check_mnesia]}]. + [ + size_check_ets1, % Plain ets + size_check_ets2, % ets with a file + size_check_ets3, % ets with a checksummed file + size_check_dets, % Plain dets + size_check_mnesia % Plain mnesia + ] + }]. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. cases() -> -[start_and_stop, load_unload, {group, size_check}, - me_lookup, which_mib, cache_test]. + [ + start_and_stop, + load_unload, + {group, size_check}, + me_lookup, + which_mib, + cache_test + ]. init_per_suite(Config) when is_list(Config) -> %% Data dir points wrong @@ -175,7 +219,6 @@ load_unload(suite) -> []; load_unload(Config) when is_list(Config) -> Prio = normal, Verbosity = log, - %% MibStorage = ets, MibDir = ?config(snmp_data_dir, Config), ?DBG("load_unload -> start symbolic store", []), @@ -221,21 +264,42 @@ load_unload(Config) when is_list(Config) -> %% --------------------------------------------------------------------- -size_check_ets(suite) -> +size_check_ets1(suite) -> + []; +size_check_ets1(Config) when is_list(Config) -> + MibStorage = [{module, snmpa_mib_storage_ets}], + do_size_check([{mib_storage, MibStorage}|Config]). + +size_check_ets2(suite) -> + []; +size_check_ets2(Config) when is_list(Config) -> + Dir = ?config(ets_dir, Config), + MibStorage = [{module, snmpa_mib_storage_ets}, + {options, [{dir, Dir}]}], + do_size_check([{mib_storage, MibStorage}|Config]). + +size_check_ets3(suite) -> []; -size_check_ets(Config) when is_list(Config) -> - do_size_check([{mib_storage, ets}|Config]). +size_check_ets3(Config) when is_list(Config) -> + Dir = ?config(ets_dir, Config), + MibStorage = [{module, snmpa_mib_storage_ets}, + {options, [{dir, Dir}, {checksum, true}]}], + do_size_check([{mib_storage, MibStorage}|Config]). size_check_dets(suite) -> []; size_check_dets(Config) when is_list(Config) -> Dir = ?config(dets_dir, Config), - do_size_check([{mib_storage, {dets, Dir}}|Config]). + MibStorage = [{module, snmpa_mib_storage_dets}, + {options, [{dir, Dir}]}], + do_size_check([{mib_storage, MibStorage}|Config]). size_check_mnesia(suite) -> []; size_check_mnesia(Config) when is_list(Config) -> - do_size_check([{mib_storage, {mnesia, [node()]}}|Config]). + MibStorage = [{module, snmpa_mib_storage_mnesia}, + {options, [{nodes, [node()]}]}], + do_size_check([{mib_storage, MibStorage}|Config]). do_size_check(Config) -> ?DBG("do_size_check -> start", []), @@ -294,7 +358,6 @@ me_lookup(suite) -> []; me_lookup(Config) when is_list(Config) -> Prio = normal, Verbosity = trace, - %% MibStorage = ets, MibDir = ?config(snmp_data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", Mibs = ["Test2", "TestTrap", "TestTrapv2"], @@ -348,7 +411,6 @@ which_mib(suite) -> []; which_mib(Config) when is_list(Config) -> Prio = normal, Verbosity = trace, - %% MibStorage = ets, MibDir = ?config(snmp_data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", Mibs = ["Test2", "TestTrap", "TestTrapv2"], @@ -406,28 +468,28 @@ cache_test(Config) when is_list(Config) -> ?DBG("cache_test -> start", []), Prio = normal, Verbosity = trace, - MibStorage = ets, + MibStorage = [{module, snmpa_mib_storage_ets}], MibDir = ?config(snmp_data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", - Mibs = ["Test2", "TestTrap", "TestTrapv2"], - StdMibs = ["OTP-SNMPEA-MIB", - "SNMP-COMMUNITY-MIB", - "SNMP-FRAMEWORK-MIB", - "SNMP-MPD-MIB", - "SNMP-NOTIFICATION-MIB", - "SNMP-TARGET-MIB", - %% "SNMP-USER-BASED-SM-MIB", - "SNMP-VIEW-BASED-ACM-MIB", - "SNMPv2-MIB", - "SNMPv2-TC", - "SNMPv2-TM"], + Mibs = ["Test2", "TestTrap", "TestTrapv2"], + StdMibs = ["OTP-SNMPEA-MIB", + "SNMP-COMMUNITY-MIB", + "SNMP-FRAMEWORK-MIB", + "SNMP-MPD-MIB", + "SNMP-NOTIFICATION-MIB", + "SNMP-TARGET-MIB", + %% "SNMP-USER-BASED-SM-MIB", + "SNMP-VIEW-BASED-ACM-MIB", + "SNMPv2-MIB", + "SNMPv2-TC", + "SNMPv2-TM"], ?DBG("cache_test -> start symbolic store", []), ?line sym_start(Prio, MibStorage, Verbosity), ?DBG("cache_test -> start mib server", []), - GcLimit = 2, - Age = timer:seconds(10), + GcLimit = 2, + Age = timer:seconds(10), CacheOpts = [{autogc, false}, {age, Age}, {gclimit, GcLimit}], ?line MibsPid = mibs_start(Prio, MibStorage, [], Verbosity, CacheOpts), @@ -537,7 +599,7 @@ mnesia_stop() -> %% - Symbolic Store mini interface sym_start(Prio, Verbosity) -> - sym_start(Prio, ets, Verbosity). + sym_start(Prio, mib_storage(), Verbosity). sym_start(Prio, MibStorage, Verbosity) -> Opts = [{mib_storage, MibStorage}, {verbosity,Verbosity}], @@ -554,7 +616,7 @@ sym_info() -> %% -- MIB server mini interface mibs_start(Prio, Verbosity) when is_atom(Prio) andalso is_atom(Verbosity) -> - mibs_start(Prio, ets, [], Verbosity). + mibs_start(Prio, mib_storage(), [], Verbosity). mibs_start(Prio, MibStorage, Verbosity) when is_atom(Prio) andalso is_atom(Verbosity) -> @@ -671,6 +733,11 @@ which_mib(M1, M2) -> {error, {invalid_mib, M1, M2}}. +%% Default mib-storage +mib_storage() -> + [{module, snmpa_mib_storage_ets}]. + + %% -- display_memory_usage(MibsPid) -> -- cgit v1.2.3 From 49d1609d1ccdd967f20140a2285f118df887f3e9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 24 May 2013 15:40:46 +0200 Subject: [snmp/agent] Improved mib-storage ets module error handling Also fixed some of the debug printouts. --- lib/snmp/src/agent/snmpa_mib_storage_ets.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl index 852df06fa0..04faf46864 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl @@ -89,13 +89,14 @@ open(Name, RecName, _Fields, Type, Opts) -> checksum = Checksum}}; {error, Reason} when (Action =:= keep) -> ?vinfo("open ~p database ~p - " - "failed reading from file (keep)", + "failed reading from file (keep): " + "~n ~p", [Type, Name, Reason]), {error, {file2tab, Reason}}; {error, Reason} -> ?vlog("open ~p database ~p - " - "failed reading from file (clear)", - [Type, Name, Reason]), + "failed reading from file (clear): " + "~n ~p", [Type, Name, Reason]), user_err("Warning: could not read file - " "create new (empty): " "~n File: ~p" @@ -120,15 +121,17 @@ open(Name, RecName, _Fields, Type, Opts) -> checksum = Checksum}}; {error, Reason} when (Action =:= keep) -> ?vinfo("open ~p database ~p - " - "failed reading file info (keep)", + "failed reading file info (keep): " + "~n ~p", [Type, Name, Reason]), {error, {read_file_info, Reason}}; {error, Reason} -> ?vlog("open ~p database ~p - " - "failed reading file info (clear)", + "failed reading file info (clear): " + "~n ~p", [Type, Name, Reason]), user_err("Warning: could not read file info - " - "create new: " + "create new file: " "~n File: ~p" "~n Reason: ~p", [File, Reason]), ID = ets:new(Name, [Type, protected, {keypos, 2}]), -- cgit v1.2.3 From fd747e9088186657a29fa252cae06a7d7c08761a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 24 May 2013 15:41:43 +0200 Subject: [snmp/agent] Some restructuring and some new test cases of mib-server suite --- lib/snmp/test/snmp_agent_mibs_test.erl | 186 ++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 71 deletions(-) diff --git a/lib/snmp/test/snmp_agent_mibs_test.erl b/lib/snmp/test/snmp_agent_mibs_test.erl index 4569be0b5a..248fe7d83e 100644 --- a/lib/snmp/test/snmp_agent_mibs_test.erl +++ b/lib/snmp/test/snmp_agent_mibs_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-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 @@ -20,6 +20,7 @@ %% %%---------------------------------------------------------------------- %% Purpose: Test suite of the agent mib-server. +%% Some of these tests should really be in a mib-storage suite. %%---------------------------------------------------------------------- -module(snmp_agent_mibs_test). @@ -58,7 +59,9 @@ size_check_ets1/1, size_check_ets2/1, + size_check_ets2_bad_file1/1, size_check_ets3/1, + size_check_ets3_bad_file1/1, size_check_dets/1, size_check_mnesia/1, load_unload/1, @@ -85,28 +88,51 @@ %% External functions %%====================================================================== -init_per_testcase(size_check_ets2, Config) when is_list(Config) -> - Dir = ?config(priv_dir, Config), - EtsDir = join(Dir, "ets_dir2/"), - ?line ok = file:make_dir(EtsDir), - [{ets_dir, EtsDir}|Config]; -init_per_testcase(size_check_ets3, Config) when is_list(Config) -> - Dir = ?config(priv_dir, Config), - EtsDir = join(Dir, "ets_dir3/"), - ?line ok = file:make_dir(EtsDir), - [{ets_dir, EtsDir}|Config]; -init_per_testcase(size_check_dets, Config) when is_list(Config) -> - Dir = ?config(priv_dir, Config), - DetsDir = join(Dir, "dets_dir/"), - ?line ok = file:make_dir(DetsDir), - [{dets_dir, DetsDir}|Config]; -init_per_testcase(size_check_mnesia, Config) when is_list(Config) -> - Dir = ?config(priv_dir, Config), - MnesiaDir = join(Dir, "mnesia_dir/"), - ?line ok = file:make_dir(MnesiaDir), - mnesia_start([{dir, MnesiaDir}]), - [{mnesia_dir, MnesiaDir}|Config]; -init_per_testcase(cache_test, Config) when is_list(Config) -> +init_per_suite(Config0) when is_list(Config0) -> + + ?DBG("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0), + + ?DBG("init_per_suite -> done when" + "~n Config1: ~p", [Config1]), + + Config1. + +end_per_suite(Config) when is_list(Config) -> + + ?DBG("end_per_suite -> entry with" + "~n Config: ~p", [Config]), + + Config. + + +init_per_testcase(Case, Config0) when is_list(Config0) -> + Config1 = snmp_test_lib:fix_data_dir(Config0), + CaseTopDir = snmp_test_lib:init_testcase_top_dir(Case, Config1), + DbDir = join(CaseTopDir, "db_dir/"), + ?line ok = file:make_dir(DbDir), + init_per_testcase2(Case, [{db_dir, DbDir}, + {case_top_dir, CaseTopDir} | Config1]). + +init_per_testcase2(size_check_ets2_bad_file1, Config) when is_list(Config) -> + DbDir = ?config(db_dir, Config), + %% Create a ad file + ok = file:write_file(join(DbDir, "snmpa_symbolic_store.db"), + "calvin and hoppes play chess"), + Config; +init_per_testcase2(size_check_ets3_bad_file1, Config) when is_list(Config) -> + DbDir = ?config(db_dir, Config), + %% Create a ad file + ok = file:write_file(join(DbDir, "snmpa_symbolic_store.db"), + "calvin and hoppes play chess"), + Config; +init_per_testcase2(size_check_mnesia, Config) when is_list(Config) -> + DbDir = ?config(db_dir, Config), + mnesia_start([{dir, DbDir}]), + Config; +init_per_testcase2(cache_test, Config) when is_list(Config) -> Min = timer:minutes(5), Timeout = case lists:keysearch(tc_timeout, 1, Config) of @@ -119,25 +145,26 @@ init_per_testcase(cache_test, Config) when is_list(Config) -> end, Dog = test_server:timetrap(Timeout), [{watchdog, Dog} | Config]; -init_per_testcase(_Case, Config) when is_list(Config) -> +init_per_testcase2(_Case, Config) when is_list(Config) -> Config. -end_per_testcase(EtsCase, Config) - when (is_list(Config) andalso - ((EtsCase =:= size_check_ets2) orelse - (EtsCase =:= size_check_ets3))) -> - Dir = ?config(ets_dir, Config), - ?line ok = ?DEL_DIR(Dir), - lists:keydelete(ets_dir, 1, Config); -end_per_testcase(size_check_dets, Config) when is_list(Config) -> - Dir = ?config(dets_dir, Config), - ?line ok = ?DEL_DIR(Dir), - lists:keydelete(dets_dir, 1, Config); +%% end_per_testcase(EtsCase, Config) +%% when (is_list(Config) andalso +%% ((EtsCase =:= size_check_ets2) orelse +%% (EtsCase =:= size_check_ets3))) -> +%% Dir = ?config(ets_dir, Config), +%% ?line ok = ?DEL_DIR(Dir), +%% lists:keydelete(ets_dir, 1, Config); +%% end_per_testcase(size_check_dets, Config) when is_list(Config) -> +%% Dir = ?config(dets_dir, Config), +%% ?line ok = ?DEL_DIR(Dir), +%% lists:keydelete(dets_dir, 1, Config); end_per_testcase(size_check_mnesia, Config) when is_list(Config) -> mnesia_stop(), - Dir = ?config(mnesia_dir, Config), - ?line ok = ?DEL_DIR(Dir), - lists:keydelete(mnesia_dir, 1, Config); + %% Dir = ?config(db_dir, Config), + %% ?line ok = ?DEL_DIR(Dir), + %% lists:keydelete(mnesia_dir, 1, Config); + Config; end_per_testcase(cache_test, Config) when is_list(Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), @@ -151,23 +178,28 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %%====================================================================== all() -> -cases(). + cases(). groups() -> [{size_check, [], [ - size_check_ets1, % Plain ets - size_check_ets2, % ets with a file - size_check_ets3, % ets with a checksummed file - size_check_dets, % Plain dets - size_check_mnesia % Plain mnesia + size_check_ets1, % Plain ets + size_check_ets2, % ets with a file + size_check_ets2_bad_file1, % ets with a bad file + size_check_ets3, % ets with a checksummed file + size_check_ets3_bad_file1, % ets with bad file (checksummed) + size_check_dets, % Plain dets + size_check_mnesia % Plain mnesia ] }]. -init_per_group(_GroupName, Config) -> - Config. + +init_per_group(GroupName, Config) -> + snmp_test_lib:init_group_top_dir(GroupName, Config). end_per_group(_GroupName, Config) -> + %% Do we really need to do this? + %% lists:keydelete(snmp_group_top_dir, 1, Config). Config. @@ -181,17 +213,6 @@ cases() -> cache_test ]. -init_per_suite(Config) when is_list(Config) -> - %% Data dir points wrong - DataDir0 = ?config(data_dir, Config), - DataDir1 = filename:split(filename:absname(DataDir0)), - [_|DataDir2] = lists:reverse(DataDir1), - DataDir = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]), - [{snmp_data_dir, DataDir ++ "/"}|Config]. - -end_per_suite(Config) when is_list(Config) -> - lists:keydelete(snmp_data_dir, 1, Config). - %%====================================================================== %% Test functions @@ -219,7 +240,7 @@ load_unload(suite) -> []; load_unload(Config) when is_list(Config) -> Prio = normal, Verbosity = log, - MibDir = ?config(snmp_data_dir, Config), + MibDir = ?config(data_dir, Config), ?DBG("load_unload -> start symbolic store", []), ?line sym_start(Prio, Verbosity), @@ -273,42 +294,65 @@ size_check_ets1(Config) when is_list(Config) -> size_check_ets2(suite) -> []; size_check_ets2(Config) when is_list(Config) -> - Dir = ?config(ets_dir, Config), - MibStorage = [{module, snmpa_mib_storage_ets}, + Dir = ?config(db_dir, Config), + MibStorage = [{module, snmpa_mib_storage_ets}, {options, [{dir, Dir}]}], do_size_check([{mib_storage, MibStorage}|Config]). +size_check_ets2_bad_file1(suite) -> + []; +size_check_ets2_bad_file1(Config) when is_list(Config) -> + Dir = ?config(db_dir, Config), + %% Ensure that the bad file does not cause any problems (action = clear) + MibStorage = [{module, snmpa_mib_storage_ets}, + {options, [{dir, Dir}, + {action, clear}]}], + do_size_check([{mib_storage, MibStorage}|Config]). + size_check_ets3(suite) -> []; size_check_ets3(Config) when is_list(Config) -> - Dir = ?config(ets_dir, Config), - MibStorage = [{module, snmpa_mib_storage_ets}, - {options, [{dir, Dir}, {checksum, true}]}], + Dir = ?config(db_dir, Config), + MibStorage = [{module, snmpa_mib_storage_ets}, + {options, [{dir, Dir}, + {checksum, true}]}], + do_size_check([{mib_storage, MibStorage}|Config]). + +size_check_ets3_bad_file1(suite) -> + []; +size_check_ets3_bad_file1(Config) when is_list(Config) -> + Dir = ?config(db_dir, Config), + %% Ensure that the bad file does not cause any problems (action = clear) + MibStorage = [{module, snmpa_mib_storage_ets}, + {options, [{dir, Dir}, + {action, clear}, + {checksum, true}]}], do_size_check([{mib_storage, MibStorage}|Config]). size_check_dets(suite) -> []; size_check_dets(Config) when is_list(Config) -> - Dir = ?config(dets_dir, Config), - MibStorage = [{module, snmpa_mib_storage_dets}, + Dir = ?config(db_dir, Config), + MibStorage = [{module, snmpa_mib_storage_dets}, {options, [{dir, Dir}]}], do_size_check([{mib_storage, MibStorage}|Config]). size_check_mnesia(suite) -> []; size_check_mnesia(Config) when is_list(Config) -> - MibStorage = [{module, snmpa_mib_storage_mnesia}, + MibStorage = [{module, snmpa_mib_storage_mnesia}, {options, [{nodes, [node()]}]}], do_size_check([{mib_storage, MibStorage}|Config]). do_size_check(Config) -> - ?DBG("do_size_check -> start", []), + ?DBG("do_size_check -> start with" + "~n Config: ~p", [Config]), Prio = normal, Verbosity = trace, MibStorage = ?config(mib_storage, Config), ?DBG("do_size_check -> MibStorage: ~p", [MibStorage]), - MibDir = ?config(snmp_data_dir, Config), + MibDir = ?config(data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", ?DBG("do_size_check -> start symbolic store", []), @@ -358,7 +402,7 @@ me_lookup(suite) -> []; me_lookup(Config) when is_list(Config) -> Prio = normal, Verbosity = trace, - MibDir = ?config(snmp_data_dir, Config), + MibDir = ?config(data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", Mibs = ["Test2", "TestTrap", "TestTrapv2"], StdMibs = ["OTP-SNMPEA-MIB", @@ -411,7 +455,7 @@ which_mib(suite) -> []; which_mib(Config) when is_list(Config) -> Prio = normal, Verbosity = trace, - MibDir = ?config(snmp_data_dir, Config), + MibDir = ?config(data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", Mibs = ["Test2", "TestTrap", "TestTrapv2"], StdMibs = ["OTP-SNMPEA-MIB", @@ -469,7 +513,7 @@ cache_test(Config) when is_list(Config) -> Prio = normal, Verbosity = trace, MibStorage = [{module, snmpa_mib_storage_ets}], - MibDir = ?config(snmp_data_dir, Config), + MibDir = ?config(data_dir, Config), StdMibDir = filename:join(code:priv_dir(snmp), "mibs") ++ "/", Mibs = ["Test2", "TestTrap", "TestTrapv2"], StdMibs = ["OTP-SNMPEA-MIB", -- cgit v1.2.3 From 0b2501e1a98c7cfe87813b542d8ac7a876c85072 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 12:31:05 +0200 Subject: [snmp/agent] Updated deprecated attribute --- lib/snmp/src/agent/snmpa.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index a22f1b2eb4..0ce1f611b2 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -115,7 +115,7 @@ me/0 ]). --deprecated([{old_info_format, 1}]). +-deprecated([{old_info_format, 1, next_major_release}]). -include("snmpa_atl.hrl"). -- cgit v1.2.3 From 8f541e4959d4f58480beac053cc76613cd208837 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 12:31:52 +0200 Subject: [stdlib] Updated for snmp deprecations --- lib/stdlib/src/otp_internal.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index a4f4035c79..28a611afb0 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -73,10 +73,12 @@ obsolete_1(snmp, N, A) -> false -> no; true -> - {deprecated,"Deprecated; use snmpa:"++atom_to_list(N)++"/"++ + {deprecated, "Deprecated (will be removed in R17B); use snmpa:"++atom_to_list(N)++"/"++ integer_to_list(A)++" instead"} end; +obsolete_1(snmpa, old_info_format, 1) -> + {deprecated, "Deprecated; (will be removed in R17B); use \"new\" format instead"}; obsolete_1(snmpm, agent_info, 3) -> {removed, {snmpm, agent_info, 2}, "R16B"}; obsolete_1(snmpm, update_agent_info, 5) -> -- cgit v1.2.3 From 6aca7052709ea0f88503b08685c1699845a6de89 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 16:56:44 +0200 Subject: [snmp] Use of new crypto interface correctd in manager Added a common utility function (in the snmp_misc module) for testing for crypto support (sed both by the manager and agent code). OTP-11009 --- lib/snmp/src/agent/snmp_user_based_sm_mib.erl | 14 +++------ lib/snmp/src/manager/snmpm_config.erl | 41 +++++++++++++-------------- lib/snmp/src/misc/snmp_misc.erl | 17 ++++++++++- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl index e675cf1b83..223d3f7218 100644 --- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl +++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl @@ -1219,16 +1219,10 @@ split(N, [H | T], FirstRev) when N > 0 -> split(N-1, T, [H | FirstRev]). -is_crypto_supported(Algo) -> - %% The 'catch' handles the case when 'crypto' is - %% not present in the system (or not started). - Supported = crypto:supports(), - Hashs = proplists:get_value(hashs, Supported), - Ciphers = proplists:get_value(ciphers, Supported), - case catch lists:member(Algo, Hashs ++ Ciphers) of - true -> true; - _ -> false - end. +-compile({inline, [{is_crypto_supported,1}]}). +is_crypto_supported(Func) -> + snmp_misc:is_crypto_supported(Func). + inconsistentValue(V) -> throw({inconsistentValue, V}). inconsistentName(N) -> throw({inconsistentName, N}). diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl index 5bbf9e5542..9d687f1d31 100644 --- a/lib/snmp/src/manager/snmpm_config.erl +++ b/lib/snmp/src/manager/snmpm_config.erl @@ -2028,7 +2028,7 @@ verify_usm_user_auth(usmNoAuthProtocol, AuthKey) -> end; verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) when is_list(AuthKey) andalso (length(AuthKey) =:= 16) -> - case is_crypto_supported(md5_mac_96) of + case is_crypto_supported(md5) of true -> case snmp_conf:all_integer(AuthKey) of true -> @@ -2037,7 +2037,7 @@ verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) error({invalid_auth_key, usmHMACMD5AuthProtocol}) end; false -> - error({unsupported_crypto, md5_mac_96}) + error({unsupported_crypto, md5}) end; verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) when is_list(AuthKey) -> Len = length(AuthKey), @@ -2046,7 +2046,7 @@ verify_usm_user_auth(usmHMACMD5AuthProtocol, _AuthKey) -> error({invalid_auth_key, usmHMACMD5AuthProtocol}); verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) when is_list(AuthKey) andalso (length(AuthKey) =:= 20) -> - case is_crypto_supported(sha_mac_96) of + case is_crypto_supported(sha) of true -> case snmp_conf:all_integer(AuthKey) of true -> @@ -2055,7 +2055,7 @@ verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) error({invalid_auth_key, usmHMACSHAAuthProtocol}) end; false -> - error({unsupported_crypto, sha_mac_96}) + error({unsupported_crypto, sha}) end; verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) when is_list(AuthKey) -> Len = length(AuthKey), @@ -2074,7 +2074,7 @@ verify_usm_user_priv(usmNoPrivProtocol, PrivKey) -> end; verify_usm_user_priv(usmDESPrivProtocol, PrivKey) when (length(PrivKey) =:= 16) -> - case is_crypto_supported(des_cbc_decrypt) of + case is_crypto_supported(des_cbc) of true -> case snmp_conf:all_integer(PrivKey) of true -> @@ -2083,7 +2083,7 @@ verify_usm_user_priv(usmDESPrivProtocol, PrivKey) error({invalid_priv_key, usmDESPrivProtocol}) end; false -> - error({unsupported_crypto, des_cbc_decrypt}) + error({unsupported_crypto, des_cbc}) end; verify_usm_user_priv(usmDESPrivProtocol, PrivKey) when is_list(PrivKey) -> Len = length(PrivKey), @@ -2092,7 +2092,7 @@ verify_usm_user_priv(usmDESPrivProtocol, _PrivKey) -> error({invalid_priv_key, usmDESPrivProtocol}); verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) when (length(PrivKey) =:= 16) -> - case is_crypto_supported(aes_cfb_128_decrypt) of + case is_crypto_supported(aes_cfb128) of true -> case snmp_conf:all_integer(PrivKey) of true -> @@ -2101,7 +2101,7 @@ verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) error({invalid_priv_key, usmAesCfb128Protocol}) end; false -> - error({unsupported_crypto, aes_cfb_128_decrypt}) + error({unsupported_crypto, aes_cfb128}) end; verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) when is_list(PrivKey) -> Len = length(PrivKey), @@ -2111,13 +2111,10 @@ verify_usm_user_priv(usmAesCfb128Protocol, _PrivKey) -> verify_usm_user_priv(PrivP, _PrivKey) -> error({invalid_priv_protocol, PrivP}). + +-compile({inline, [{is_crypto_supported,1}]}). is_crypto_supported(Func) -> - %% The 'catch' handles the case when 'crypto' is - %% not present in the system (or not started). - case (catch lists:member(Func, crypto:info())) of - true -> true; - _ -> false - end. + snmp_misc:is_crypto_supported(Func). read_manager_config_file(Dir) -> @@ -2879,11 +2876,11 @@ do_update_usm_user_info(Key, #usm_user{auth = usmHMACMD5AuthProtocol} = User, auth_key, Val) when length(Val) =:= 16 -> - case is_crypto_supported(md5_mac_96) of + case is_crypto_supported(md5) of true -> do_update_usm_user_info(Key, User#usm_user{auth_key = Val}); false -> - {error, {unsupported_crypto, md5_mac_96}} + {error, {unsupported_crypto, md5}} end; do_update_usm_user_info(_Key, #usm_user{auth = usmHMACMD5AuthProtocol}, @@ -2898,11 +2895,11 @@ do_update_usm_user_info(Key, #usm_user{auth = usmHMACSHAAuthProtocol} = User, auth_key, Val) when length(Val) =:= 20 -> - case is_crypto_supported(sha_mac_96) of + case is_crypto_supported(sha) of true -> do_update_usm_user_info(Key, User#usm_user{auth_key = Val}); false -> - {error, {unsupported_crypto, sha_mac_96}} + {error, {unsupported_crypto, sha}} end; do_update_usm_user_info(_Key, #usm_user{auth = usmHMACSHAAuthProtocol}, @@ -2933,21 +2930,21 @@ do_update_usm_user_info(Key, #usm_user{priv = usmDESPrivProtocol} = User, priv_key, Val) when length(Val) =:= 16 -> - case is_crypto_supported(des_cbc_decrypt) of + case is_crypto_supported(des_cbc) of true -> do_update_usm_user_info(Key, User#usm_user{priv_key = Val}); false -> - {error, {unsupported_crypto, des_cbc_decrypt}} + {error, {unsupported_crypto, des_cbc}} end; do_update_usm_user_info(Key, #usm_user{priv = usmAesCfb128Protocoll} = User, priv_key, Val) when length(Val) =:= 16 -> - case is_crypto_supported(aes_cfb_128_decrypt) of + case is_crypto_supported(aes_cfb128) of true -> do_update_usm_user_info(Key, User#usm_user{priv_key = Val}); false -> - {error, {unsupported_crypto, aes_cfb_128_decrypt}} + {error, {unsupported_crypto, aes_cfb128}} end; do_update_usm_user_info(_Key, #usm_user{auth = usmHMACSHAAuthProtocol}, diff --git a/lib/snmp/src/misc/snmp_misc.erl b/lib/snmp/src/misc/snmp_misc.erl index a061dcd97c..293b22991b 100644 --- a/lib/snmp/src/misc/snmp_misc.erl +++ b/lib/snmp/src/misc/snmp_misc.erl @@ -43,6 +43,7 @@ ip/1, ip/2, is_auth/1, is_BitString/1, + is_crypto_supported/1, is_oid/1, is_priv/1, is_reportable/1, @@ -117,13 +118,27 @@ now(sec) -> (element(3,Now) div 1000000). +is_crypto_supported(Alg) -> + %% The 'try catch' handles the case when 'crypto' is + %% not present in the system (or not started). + try + begin + Supported = crypto:supports(), + Hashs = proplists:get_value(hashs, Supported), + Ciphers = proplists:get_value(ciphers, Supported), + lists:member(Alg, Hashs ++ Ciphers) + end + catch + _:_ -> + false + end. + is_string([]) -> true; is_string([Tkn | Str]) when is_integer(Tkn) andalso (Tkn >= 0) andalso (Tkn =< 255) -> is_string(Str); is_string(_) -> false. - is_oid([E1, E2| Rest]) when (length(Rest) =< 126) andalso (E1 *40 + E2 =< 255) -> is_oid2(Rest); -- cgit v1.2.3 From f64191ff6e73662981581797933db81e7dff4b2a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 17:00:11 +0200 Subject: [snmp] Some cosmetic cleanup (tmp verbosity printouts commented) OTP-11009 --- lib/snmp/src/misc/snmp_usm.erl | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/snmp/src/misc/snmp_usm.erl b/lib/snmp/src/misc/snmp_usm.erl index 53c291ca0e..67e3476816 100644 --- a/lib/snmp/src/misc/snmp_usm.erl +++ b/lib/snmp/src/misc/snmp_usm.erl @@ -142,32 +142,33 @@ auth_out(?usmHMACSHAAuthProtocol, AuthKey, Message, UsmSecParams) -> sha_auth_out(AuthKey, Message, UsmSecParams). md5_auth_out(AuthKey, Message, UsmSecParams) -> + %% ?vtrace("md5_auth_out -> entry with" + %% "~n AuthKey: ~w" + %% "~n Message: ~w" + %% "~n UsmSecParams: ~w", [AuthKey, Message, UsmSecParams]), %% 6.3.1.1 Message2 = set_msg_auth_params(Message, UsmSecParams, ?twelwe_zeros), - Packet = snmp_pdus:enc_message_only(Message2), + Packet = snmp_pdus:enc_message_only(Message2), %% 6.3.1.2-4 is done by the crypto function %% 6.3.1.4 MAC = binary_to_list(crypto:hmac(md5, AuthKey, Packet, 12)), - ?vtrace("md5_auth_out -> entry with" - "~n Packet: ~w" - "~n AuthKey: ~w" - "~n MAC: ~w" - , [Packet, AuthKey, MAC]), + %% ?vtrace("md5_auth_out -> crypto (md5) encoded" + %% "~n MAC: ~w", [MAC]), %% 6.3.1.5 set_msg_auth_params(Message, UsmSecParams, MAC). md5_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) == 12 -> + %% ?vtrace("md5_auth_in -> entry with" + %% "~n AuthKey: ~w" + %% "~n AuthParams: ~w" + %% "~n Packet: ~w", [AuthKey, AuthParams, Packet]), %% 6.3.2.3 Packet2 = patch_packet(binary_to_list(Packet)), %% 6.3.2.5 MAC = binary_to_list(crypto:hmac(md5, AuthKey, Packet2, 12)), %% 6.3.2.6 - ?vtrace("md5_auth_in -> entry with" - "~n Packet2: ~w" - "~n AuthKey: ~w" - "~n AuthParams: ~w" - "~n MAC: ~w" - , [Packet2, AuthKey, AuthParams, MAC]), + %% ?vtrace("md5_auth_in -> crypto (md5) encoded" + %% "~n MAC: ~w", [MAC]), MAC == AuthParams; md5_auth_in(_AuthKey, _AuthParams, _Packet) -> %% 6.3.2.1 -- cgit v1.2.3 From c64d201c559ca912e4730b18daf8603c2fcd2e21 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 17:01:11 +0200 Subject: [snmp] Updated (all) test code to support new crypto interface Test suite utility function for verifying crypto support updated to use new crypto interface. OTP-11009 --- lib/snmp/test/snmp_test_lib.erl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl index f0abae73e8..505332b586 100644 --- a/lib/snmp/test/snmp_test_lib.erl +++ b/lib/snmp/test/snmp_test_lib.erl @@ -435,7 +435,7 @@ crypto_start() -> end. crypto_support() -> - crypto_support([md5_mac_96, sha_mac_96], []). + crypto_support([md5, sha], []). crypto_support([], []) -> yes; @@ -450,12 +450,7 @@ crypto_support([Func|Funcs], Acc) -> end. is_crypto_supported(Func) -> - %% The 'catch' handles the case when 'crypto' is - %% not present in the system (or not started). - case (catch lists:member(Func, crypto:info())) of - true -> true; - _ -> false - end. + snmp_misc:is_crypto_supported(Func). %% ---------------------------------------------------------------- -- cgit v1.2.3 From a9244ce9be2d73397ed71fb9d7d5986c5f70e202 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 17:04:31 +0200 Subject: [snmp] Updated version and release notes OTP-11009 --- lib/snmp/doc/src/notes.xml | 67 ++++++++++++++++++++++++++++++++++++++++++++++ lib/snmp/vsn.mk | 2 +- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 5222922848..6d617e8bba 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -33,6 +33,73 @@ +
+ SNMP Development Toolkit 4.24 +

Version 4.24 supports code replacement in runtime from/to + version 4.23.1 and 4.23.

+ +
+ Improvements and new features +

-

+ + + +
+ +
+ Fixed Bugs and Malfunctions + + + + +

[agent,manager] Updated to support the new crypto interface.

+

Own Id: OTP-11009

+
+ +
+ +
+ +
+ Incompatibilities +

-

+ + +
+ +
+ +
SNMP Development Toolkit 4.23.1

Version 4.23.1 supports code replacement in runtime from/to diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index fb7aa52402..0e48e7ea56 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.23.1 +SNMP_VSN = 4.24 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 0275959ca4db104e1749efe7beedfdadf204dfc4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 28 May 2013 17:51:43 +0200 Subject: [snmp/agent] Corrected the mib_storage type --- lib/snmp/src/agent/snmpa_mib_data.erl | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index ffacdd5b3a..368c714134 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -28,28 +28,23 @@ %% These types should really be defined elsewhere... -export_type([ mib_storage/0, - mib_storage_dir/0, - mib_storage_action/0, - + mib_storage_opt/0, + mib_storage_module/0, + mib_storage_options/0, mib_view/0, mib_view_elem/0, mib_view_mask/0, mib_view_inclusion/0 ]). --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()}. - --type mib_storage_dir() :: default | string(). --type mib_storage_action() :: clear | keep. +-type mib_storage() :: [mib_storage_opt()]. +-type mib_storage_opt() :: {module, mib_storage_module()} | + {options, mib_storage_options()}. + +%% Module implementing the snmpa_mib_storage behaviour +-type mib_storage_module() :: atom(). +%% Options specific to the above module +-type mib_storage_options() :: list(). -type mib_view() :: [mib_view_elem()]. -type mib_view_elem() :: {SubTree :: snmp:oid(), -- cgit v1.2.3 From a49f52da6e07f438686b66e71db8a4865f31af6c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 29 May 2013 11:44:05 +0200 Subject: [snmp/agent] Moved agent config type definition to main agent interface module --- lib/snmp/src/agent/modules.mk | 3 ++- lib/snmp/src/agent/snmpa.erl | 18 +++++++++++++++++- lib/snmp/src/agent/snmpa_mib_data.erl | 15 +-------------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index ea43904f4a..34765475b9 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -30,7 +30,8 @@ BEHAVIOUR_MODULES = \ 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... +# and therefor must be compiled before the modules that use them, including +# the behaviour modules... # snmpa_mib_data_ttln MODULES = \ snmpa \ diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index 0ce1f611b2..14b93439df 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -112,7 +112,13 @@ -export([print_mib_info/0, print_mib_tables/0, print_mib_variables/0]). -export_type([ - me/0 + me/0, + + %% Agent config types + mib_storage/0, + mib_storage_opt/0, + mib_storage_module/0, + mib_storage_options/0 ]). -deprecated([{old_info_format, 1, next_major_release}]). @@ -131,6 +137,16 @@ -type me() :: #me{}. +%% Agent config types +-type mib_storage() :: [mib_storage_opt()]. +-type mib_storage_opt() :: {module, mib_storage_module()} | + {options, mib_storage_options()}. + +%% Module implementing the snmpa_mib_storage behaviour +-type mib_storage_module() :: atom(). +%% Options specific to the above module +-type mib_storage_options() :: list(). + %%----------------------------------------------------------------- %% This utility function is used to convert an old SNMP application diff --git a/lib/snmp/src/agent/snmpa_mib_data.erl b/lib/snmp/src/agent/snmpa_mib_data.erl index 368c714134..4d8a12b6c6 100644 --- a/lib/snmp/src/agent/snmpa_mib_data.erl +++ b/lib/snmp/src/agent/snmpa_mib_data.erl @@ -27,25 +27,12 @@ %% These types should really be defined elsewhere... -export_type([ - mib_storage/0, - mib_storage_opt/0, - mib_storage_module/0, - mib_storage_options/0, mib_view/0, mib_view_elem/0, mib_view_mask/0, mib_view_inclusion/0 ]). --type mib_storage() :: [mib_storage_opt()]. --type mib_storage_opt() :: {module, mib_storage_module()} | - {options, mib_storage_options()}. - -%% Module implementing the snmpa_mib_storage behaviour --type mib_storage_module() :: atom(). -%% Options specific to the above module --type mib_storage_options() :: list(). - -type mib_view() :: [mib_view_elem()]. -type mib_view_elem() :: {SubTree :: snmp:oid(), Mask :: [non_neg_integer()], @@ -56,7 +43,7 @@ -type filename() :: file:filename(). --callback new(MibStorage :: mib_storage()) -> State :: term(). +-callback new(MibStorage :: snmpa:mib_storage()) -> State :: term(). -callback close(State :: term()) -> ok. -- cgit v1.2.3 From dc22c5fea0753ba7e066d26a9f3a4078af6cb2c5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 29 May 2013 14:13:46 +0200 Subject: [snmp/agent] Mib data tttn comments --- lib/snmp/src/agent/snmpa_mib_data_tttn.erl | 75 ++++++++++++++++++------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl index f0bd5dabfd..90ddf4869f 100644 --- a/lib/snmp/src/agent/snmpa_mib_data_tttn.erl +++ b/lib/snmp/src/agent/snmpa_mib_data_tttn.erl @@ -16,6 +16,7 @@ %% %% %CopyrightEnd% %% + -module(snmpa_mib_data_tttn). @@ -23,25 +24,28 @@ %%% %%% 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. +%%% This module implements the MIB (internal) data structures. +%%% The TTTN MIB Data structure consists of three items: +%%% +%%% 1) A mib-storage (as specified when new is called) for +%%% the data associated with each variable, table, +%%% table-entry and table-column in the MIB. +%%% 2) A tree contains information of the Oids in the MIB. +%%% 3) 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"). +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/src/misc/snmp_debug.hrl"). -define(VMODULE,"MDATA_TTTN"). --include("snmp_verbosity.hrl"). +-include_lib("snmp/src/misc/snmp_verbosity.hrl"). -behaviour(snmpa_mib_data). @@ -64,25 +68,31 @@ %%% 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, { - module, % Mib storage module - mib_db, % table of #mib_info - node_db, % table of #node_info - tree_db, % table of #tree - tree, % The actual tree + %% Mib storage module + module :: snmpa:mib_storage_module(), + + %% A database of loaded mibs + %% #mib_info{} + mib_db, + + %% A database with information about each node in the tree + %% #node_info{} + node_db, + + %% A database containing _one_ record with the tree + %% without the subagent(s). + %% (the reason for this is part to get replication + %% and part out of convenience) + %% #tree{} + tree_db, + + %% The root node (same as the tree part of the tree_db + %% but with the subagents added). + tree, + + %% A list of {SAPid, Oid} subagents = [] }). @@ -90,7 +100,7 @@ -record(node_info, {oid, mib_name, me}). -%% API +%% (behaviour) API -export([new/1, close/1, sync/1, @@ -136,7 +146,7 @@ %%%====================================================================== %%----------------------------------------------------------------- -%% Func: new/0, new/1 +%% Func: new/1 %% Returns: A representation of mib data. %%----------------------------------------------------------------- @@ -147,7 +157,7 @@ new(MibStorage) -> %% 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",[]), + ?vdebug("open (mib) database",[]), MibDb = case Mod:open(?MIB_DATA, mib_info, record_info(fields, mib_info), set, Opts) of @@ -157,7 +167,7 @@ new(MibStorage) -> throw({error, {open, mib_data, Reason1}}) end, - ?vtrace("open (mib) node database",[]), + ?vdebug("open (mib) node database",[]), NodeDb = case Mod:open(?MIB_NODE, node_info, record_info(fields, node_info), set, Opts) of @@ -167,7 +177,7 @@ new(MibStorage) -> throw({error, {open, mib_node, Reason2}}) end, - ?vtrace("open (mib) tree database",[]), + ?vdebug("open (mib) tree database",[]), TreeDb = case Mod:open(?MIB_TREE, tree, record_info(fields, tree), set, Opts) of @@ -177,6 +187,7 @@ new(MibStorage) -> throw({error, {open, mib_tree, Reason3}}) end, + ?vdebug("write the default (mib) tree",[]), Tree = case Mod:read(TreeDb, ?DUMMY_TREE_GENERATION) of false -> @@ -186,7 +197,9 @@ new(MibStorage) -> {value, T} -> T end, + ?vdebug("install (existing) mibs",[]), install_mibs(Mod, MibDb, NodeDb), + ?vdebug("done",[]), #mib_data{module = Mod, mib_db = MibDb, node_db = NodeDb, -- cgit v1.2.3 From 315838bc7cb7d9da936c10a3b6fc32c9ea92bca0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 30 May 2013 16:11:51 +0200 Subject: [snmp/agent] Changed the test suite expect function There was an id as the first agument to each expect call. Ufortunatly, this id was often 1 and since the same test function(s) was called in many test cases, there was many "Expect 1", and therefor no way of knowing which expect actually was performed. The expect functions has been changed to instead take module and line number. --- lib/snmp/test/snmp_agent_test.erl | 1095 +++++++++++++++++---------------- lib/snmp/test/snmp_agent_test_lib.erl | 41 +- 2 files changed, 572 insertions(+), 564 deletions(-) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 01f0e58bc2..a35077091e 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -86,6 +86,26 @@ end). +-define(expect1(What), + snmp_agent_test_lib:expect(?MODULE, ?LINE, + What)). +-define(expect2(What, ExpVBs), + snmp_agent_test_lib:expect(?MODULE, ?LINE, + What, ExpVBs)). +-define(expect3(Err, Idx, ExpVBs), + snmp_agent_test_lib:expect(?MODULE, ?LINE, + Err, Idx, ExpVBs)). +-define(expect4(Err, Idx, ExpVBs, To), + snmp_agent_test_lib:expect(?MODULE, ?LINE, + Err, Idx, ExpVBs, To)). +-define(expect5(Type, Ent, Gen, Spec, ExpVBs), + snmp_agent_test_lib:expect(?MODULE, ?LINE, + Type, Ent, Gen, Spec, ExpVBs)). +-define(expect6(Type, Ent, Gen, Spec, ExpVBs, To), + snmp_agent_test_lib:expect(?MODULE, ?LINE, + Type, Ent, Gen, Spec, ExpVBs)). + + all() -> %% Reqs = [mnesia, distribution, {local_slave_nodes, 2}, {time, 360}], Conf1 = [{group, all_tcs}], @@ -2443,9 +2463,9 @@ v3_des_priv(Config) when is_list(Config) -> v3_sync(Funcs) -> ?DBG("v3_sync -> entry with Funcs: ~p",[Funcs]), g([[sysDescr, 0]]), - expect(432, report, [{?usmStatsNotInTimeWindows_instance, any}]), + ?expect2(report, [{?usmStatsNotInTimeWindows_instance, any}]), g([[sysDescr, 0]]), - expect(433, [{[sysDescr,0], any}]), + ?expect1([{[sysDescr,0], any}]), lists:foreach(fun({Func, Args}) -> apply(?MODULE, Func, Args) end, Funcs). v3_inform_sync(MA) -> @@ -2456,9 +2476,9 @@ v3_inform_sync(MA) -> ?DBG("v3_sync -> wait some time: ",[]), ?SLEEP(20000), % more than 1500*10 in target_addr.conf ?DBG("v3_sync -> await response",[]), - ?line expect(1, {inform, true}, - [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]). + ?line ?expect2({inform, true}, + [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]). v2_caps(suite) -> []; @@ -2474,11 +2494,11 @@ v2_caps_3(X) -> ?P(v2_caps_3), v2_caps(X). v2_caps_i(Node) -> ?line Idx = rpc:call(Node, snmp, add_agent_caps, [[1,2,3,4,5], "test cap"]), g([[sysORID, Idx], [sysORDescr, Idx]]), - ?line expect(1, [{[sysORID, Idx], [1,2,3,4,5]}, - {[sysORDescr, Idx], "test cap"}]), + ?line ?expect1([{[sysORID, Idx], [1,2,3,4,5]}, + {[sysORDescr, Idx], "test cap"}]), ?line rpc:call(Node, snmp, del_agent_caps, [Idx]), g([[sysORID, Idx]]), - ?line expect(2, [{[sysORID, Idx], noSuchInstance}]). + ?line ?expect1([{[sysORID, Idx], noSuchInstance}]). %% Req. Test2 @@ -2494,86 +2514,86 @@ v1_proc() -> v1_get_p() -> %% 4.1.2:1 g([[test2]]), - ?line expect(10, noSuchName, 1, [{[test2], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[test2], 'NULL'}]), g([[tDescr]]), - ?line expect(11, noSuchName, 1, [{[tDescr], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[tDescr], 'NULL'}]), g([[tDescr2,0]]), - ?line expect(12, noSuchName, 1, [{[tDescr2,0], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[tDescr2,0], 'NULL'}]), g([[tDescr3,0]]), - ?line expect(131, noSuchName, 1, [{[tDescr3,0], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[tDescr3,0], 'NULL'}]), g([[tDescr4,0]]), - ?line expect(132, noSuchName, 1, [{[tDescr4,0], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[tDescr4,0], 'NULL'}]), g([[sysDescr, 0], [tDescr,0]]), % Outside mibview - ?line expect(14, noSuchName, 2, [{[sysDescr, 0], 'NULL'}, - {[tDescr,0], 'NULL'}]), + ?line ?expect3(noSuchName, 2, [{[sysDescr, 0], 'NULL'}, + {[tDescr,0], 'NULL'}]), g([[sysDescr,3]]), - ?line expect(15, noSuchName, 1, [{[sysDescr, 3], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[sysDescr, 3], 'NULL'}]), %% 4.1.2:2 g([[tTable]]), - ?line expect(20, noSuchName, 1, [{[tTable], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[tTable], 'NULL'}]), g([[tEntry]]), - ?line expect(21, noSuchName, 1, [{[tEntry], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[tEntry], 'NULL'}]), %% 4.1.2:3 g([[tTooBig, 0]]), - ?line expect(30, tooBig, 0, [{[tTooBig, 0], 'NULL'}]), + ?line ?expect3(tooBig, 0, [{[tTooBig, 0], 'NULL'}]), %% 4.1.2:4 g([[tGenErr1, 0]]), - ?line expect(40, genErr, 1, [{[tGenErr1, 0], 'NULL'}]), + ?line ?expect3(genErr, 1, [{[tGenErr1, 0], 'NULL'}]), g([[tGenErr2, 0]]), - ?line expect(41, genErr, 1, [{[tGenErr2, 0], 'NULL'}]), + ?line ?expect3(genErr, 1, [{[tGenErr2, 0], 'NULL'}]), g([[sysDescr, 0], [tGenErr3, 0]]), - ?line expect(42, genErr, 2, [{[sysDescr, 0], 'NULL'}, - {[tGenErr3, 0], 'NULL'}]). + ?line ?expect3(genErr, 2, [{[sysDescr, 0], 'NULL'}, + {[tGenErr3, 0], 'NULL'}]). v1_get_next_p() -> %% 4.1.3:1 gn([[1,3,7,1]]), - ?line expect(10, noSuchName, 1, [{[1,3,7,1], 'NULL'}]), + ?line ?expect3(noSuchName, 1, [{[1,3,7,1], 'NULL'}]), gn([[tDescr2]]), - ?line expect(11, tooBig, 0, any), + ?line ?expect3(tooBig, 0, any), %% 4.1.3:2 gn([[tTooBig]]), io:format("We currently don't handle tooBig correct!!!\n"), -% ?line expect(20, tooBig, 0, [{[tTooBig], 'NULL'}]), - ?line expect(20, tooBig, 0, any), +% ?line ?expect3(tooBig, 0, [{[tTooBig], 'NULL'}]), + ?line ?expect3(tooBig, 0, any), %% 4.1.3:3 gn([[tGenErr1]]), % ?line expect(40, genErr, 1, [{[tGenErr1], 'NULL'}]), - ?line expect(40, genErr, 1, any), + ?line ?expect3(genErr, 1, any), gn([[tGenErr2]]), -% ?line expect(41, genErr, 1, [{[tGenErr2], 'NULL'}]), - ?line expect(41, genErr, 1, any), +% ?line ?expect3(genErr, 1, [{[tGenErr2], 'NULL'}]), + ?line ?expect3(genErr, 1, any), gn([[sysDescr], [tGenErr3]]), -% ?line expect(42, genErr, 2, [{[sysDescr], 'NULL'}, +% ?line ?expect3(genErr, 2, [{[sysDescr], 'NULL'}, % {[tGenErr3], 'NULL'}]). - ?line expect(42, genErr, 2, any). + ?line ?expect3(genErr, 2, any). v1_set_p() -> %% 4.1.5:1 s([{[1,3,7,0], i, 4}]), - ?line expect(10, noSuchName, 1, [{[1,3,7,0], 4}]), + ?line ?expect3(noSuchName, 1, [{[1,3,7,0], 4}]), s([{[tDescr,0], s, "outside mibview"}]), - ?line expect(11, noSuchName, 1, [{[tDescr,0], "outside mibview"}]), + ?line ?expect3(noSuchName, 1, [{[tDescr,0], "outside mibview"}]), s([{[tDescr3,0], s, "read-only"}]), - ?line expect(12, noSuchName, 1, [{[tDescr3,0], "read-only"}]), + ?line ?expect3(noSuchName, 1, [{[tDescr3,0], "read-only"}]), s([{[tDescr3], s, "noSuchObject"}]), - ?line expect(13, noSuchName, 1, [{[tDescr3], "noSuchObject"}]), + ?line ?expect3(noSuchName, 1, [{[tDescr3], "noSuchObject"}]), s([{[tDescr3,1], s, "noSuchInstance"}]), - ?line expect(14, noSuchName, 1, [{[tDescr3,1], "noSuchInstance"}]), + ?line ?expect3(noSuchName, 1, [{[tDescr3,1], "noSuchInstance"}]), s([{[tDescr2,0], s, "inconsistentName"}]), - ?line expect(15, noSuchName, 1, [{[tDescr2,0], "inconsistentName"}]), + ?line ?expect3(noSuchName, 1, [{[tDescr2,0], "inconsistentName"}]), %% 4.1.5:2 s([{[tDescr2, 0], i, 4}]), - ?line expect(20, badValue, 1, [{[tDescr2, 0], 4}]), + ?line ?expect3(badValue, 1, [{[tDescr2, 0], 4}]), s([{[tDescr2, 0], s, "badValue"}]), - ?line expect(21, badValue, 1, [{[tDescr2, 0], "badValue"}]), + ?line ?expect3(badValue, 1, [{[tDescr2, 0], "badValue"}]), %% 4.1.5:3 %% The standard is quite incorrect here. The resp pdu was too big. In @@ -2583,14 +2603,14 @@ v1_set_p() -> %% of the std-like original value. s([{[tTooBig, 0], s, ?tooBigStr}]), %% according to std: -% ?line expect(30, tooBig, 0, [{[tTooBig, 0], ?tooBigStr}]), - ?line expect(30, tooBig, 0, [{[tTooBig, 0], 'NULL'}]), +% ?line ?expect3(tooBig, 0, [{[tTooBig, 0], ?tooBigStr}]), + ?line ?expect3(tooBig, 0, [{[tTooBig, 0], 'NULL'}]), %% 4.1.5:4 s([{[tDescr2, 0], s, "is_set_ok_fail"}]), - ?line expect(40, genErr, 1, [{[tDescr2, 0], "is_set_ok_fail"}]), + ?line ?expect3(genErr, 1, [{[tDescr2, 0], "is_set_ok_fail"}]), s([{[tDescr2, 0], s, "commit_fail"}]), - ?line expect(41, genErr, 1, [{[tDescr2, 0], "commit_fail"}]). + ?line ?expect3(genErr, 1, [{[tDescr2, 0], "commit_fail"}]). %% Req. Test2 v2_proc() -> @@ -2606,183 +2626,183 @@ v2_get_p() -> %% 4.2.1:2 ?DBG("v2_get_p -> entry",[]), g([[test2]]), - ?line expect(10, [{[test2], noSuchObject}]), + ?line ?expect1([{[test2], noSuchObject}]), g([[tDescr]]), - ?line expect(11, [{[tDescr], noSuchObject}]), + ?line ?expect1([{[tDescr], noSuchObject}]), g([[tDescr4,0]]), - ?line expect(12, [{[tDescr4,0], noSuchObject}]), + ?line ?expect1([{[tDescr4,0], noSuchObject}]), g([[sysDescr, 0], [tDescr,0]]), % Outside mibview - ?line expect(13, [{[sysDescr,0], "Erlang SNMP agent"}, - {[tDescr,0], noSuchObject}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {[tDescr,0], noSuchObject}]), g([[tTable]]), - ?line expect(14, [{[tTable], noSuchObject}]), + ?line ?expect1([{[tTable], noSuchObject}]), g([[tEntry]]), - ?line expect(15, [{[tEntry], noSuchObject}]), + ?line ?expect1([{[tEntry], noSuchObject}]), %% 4.2.1:3 g([[tDescr2,0]]), %% instrum ret noSuchName!!! - ?line expect(20, [{[tDescr2,0], noSuchInstance}]), + ?line ?expect1([{[tDescr2,0], noSuchInstance}]), g([[tDescr3,0]]), - ?line expect(21, [{[tDescr3,0], noSuchInstance}]), + ?line ?expect1([{[tDescr3,0], noSuchInstance}]), g([[sysDescr,3]]), - ?line expect(22, [{[sysDescr, 3], noSuchInstance}]), + ?line ?expect1([{[sysDescr, 3], noSuchInstance}]), g([[tIndex,1]]), - ?line expect(23, [{[tIndex, 1], noSuchInstance}]), + ?line ?expect1([{[tIndex, 1], noSuchInstance}]), %% 4.2.1 - any other error: genErr g([[tGenErr1, 0]]), - ?line expect(30, genErr, 1, [{[tGenErr1, 0], 'NULL'}]), + ?line ?expect3(genErr, 1, [{[tGenErr1, 0], 'NULL'}]), g([[tGenErr2, 0]]), - ?line expect(31, genErr, 1, [{[tGenErr2, 0], 'NULL'}]), + ?line ?expect3(genErr, 1, [{[tGenErr2, 0], 'NULL'}]), g([[sysDescr, 0], [tGenErr3, 0]]), - ?line expect(32, genErr, 2, [{[sysDescr, 0], 'NULL'}, - {[tGenErr3, 0], 'NULL'}]), + ?line ?expect3(genErr, 2, [{[sysDescr, 0], 'NULL'}, + {[tGenErr3, 0], 'NULL'}]), %% 4.2.1 - tooBig g([[tTooBig, 0]]), - ?line expect(40, tooBig, 0, []). + ?line ?expect3(tooBig, 0, []). v2_get_next_p() -> %% 4.2.2:2 ?DBG("v2_get_next_p -> entry",[]), gn([[1,3,7,1]]), - ?line expect(10, [{[1,3,7,1], endOfMibView}]), + ?line ?expect1([{[1,3,7,1], endOfMibView}]), gn([[sysDescr], [1,3,7,1]]), - ?line expect(11, [{[sysDescr, 0], "Erlang SNMP agent"}, - {[1,3,7,1], endOfMibView}]), + ?line ?expect1([{[sysDescr, 0], "Erlang SNMP agent"}, + {[1,3,7,1], endOfMibView}]), gn([[tCnt2, 1]]), - ?line expect(12, [{[tCnt2,2], 100}]), + ?line ?expect1([{[tCnt2,2], 100}]), gn([[tCnt2, 2]]), - ?line expect(12, [{[tCnt2,2], endOfMibView}]), + ?line ?expect1([{[tCnt2,2], endOfMibView}]), %% 4.2.2 - any other error: genErr gn([[tGenErr1]]), - ?line expect(20, genErr, 1, [{[tGenErr1], 'NULL'}]), + ?line ?expect3(genErr, 1, [{[tGenErr1], 'NULL'}]), gn([[tGenErr2]]), - ?line expect(21, genErr, 1, [{[tGenErr2], 'NULL'}]), + ?line ?expect3(genErr, 1, [{[tGenErr2], 'NULL'}]), gn([[sysDescr], [tGenErr3]]), - ?line expect(22, genErr, 2, [{[sysDescr], 'NULL'}, - {[tGenErr3], 'NULL'}]), + ?line ?expect3(genErr, 2, [{[sysDescr], 'NULL'}, + {[tGenErr3], 'NULL'}]), %% 4.2.2 - tooBig gn([[tTooBig]]), - ?line expect(20, tooBig, 0, []). + ?line ?expect3(tooBig, 0, []). v2_get_bulk_p() -> %% 4.2.3 ?DBG("v2_get_bulk_p -> entry",[]), gb(1, 1, []), - ?line expect(10, []), + ?line ?expect1([]), gb(-1, 1, []), - ?line expect(11, []), + ?line ?expect1([]), gb(-1, -1, []), - ?line expect(12, []), + ?line ?expect1([]), gb(-1, -1, []), - ?line expect(13, []), + ?line ?expect1([]), gb(2, 0, [[sysDescr], [1,3,7,1]]), - ?line expect(14, [{[sysDescr, 0], "Erlang SNMP agent"}, - {[1,3,7,1], endOfMibView}]), + ?line ?expect1([{[sysDescr, 0], "Erlang SNMP agent"}, + {[1,3,7,1], endOfMibView}]), gb(1, 2, [[sysDescr], [1,3,7,1]]), - ?line expect(15, [{[sysDescr, 0], "Erlang SNMP agent"}, - {[1,3,7,1], endOfMibView}]), + ?line ?expect1([{[sysDescr, 0], "Erlang SNMP agent"}, + {[1,3,7,1], endOfMibView}]), gb(0, 2, [[sysDescr], [1,3,7,1]]), - ?line expect(16, [{[sysDescr, 0], "Erlang SNMP agent"}, - {[1,3,7,1], endOfMibView}, - {[sysObjectID, 0], [1,2,3]}, - {[1,3,7,1], endOfMibView}]), + ?line ?expect1([{[sysDescr, 0], "Erlang SNMP agent"}, + {[1,3,7,1], endOfMibView}, + {[sysObjectID, 0], [1,2,3]}, + {[1,3,7,1], endOfMibView}]), gb(2, 2, [[sysDescr], [1,3,7,1], [sysDescr], [1,3,7,1]]), - ?line expect(17, [{[sysDescr, 0], "Erlang SNMP agent"}, - {[1,3,7,1], endOfMibView}, - {[sysDescr, 0], "Erlang SNMP agent"}, - {[1,3,7,1], endOfMibView}, - {[sysObjectID, 0], [1,2,3]}, - {[1,3,7,1], endOfMibView}]), + ?line ?expect1([{[sysDescr, 0], "Erlang SNMP agent"}, + {[1,3,7,1], endOfMibView}, + {[sysDescr, 0], "Erlang SNMP agent"}, + {[1,3,7,1], endOfMibView}, + {[sysObjectID, 0], [1,2,3]}, + {[1,3,7,1], endOfMibView}]), gb(1, 2, [[sysDescr], [sysDescr], [tTooBig]]), - ?line expect(18, [{[sysDescr, 0], "Erlang SNMP agent"}, - {[sysDescr, 0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr, 0], "Erlang SNMP agent"}, + {[sysDescr, 0], "Erlang SNMP agent"}]), gb(1,12, [[tDescr2], [sysDescr]]), % next one after tDescr2 is tTooBig. - ?line expect(19, []), + ?line ?expect1([]), gb(2,2, [[sysDescr], [sysObjectID], [tGenErr1], [sysDescr]]), - ?line expect(20, genErr, 3, [{[sysDescr], 'NULL'}, - {[sysObjectID], 'NULL'}, - {[tGenErr1], 'NULL'}, - {[sysDescr], 'NULL'}]), + ?line ?expect3(genErr, 3, [{[sysDescr], 'NULL'}, + {[sysObjectID], 'NULL'}, + {[tGenErr1], 'NULL'}, + {[sysDescr], 'NULL'}]), gb(0, 2, [[tCnt2, 1]]), - ?line expect(21, [{[tCnt2,2], 100}, - {[tCnt2,2], endOfMibView}]). + ?line ?expect1([{[tCnt2,2], 100}, + {[tCnt2,2], endOfMibView}]). v2_set_p() -> %% 4.2.5:1 ?DBG("v2_set_p -> entry",[]), s([{[1,3,7,0], i, 4}]), - ?line expect(10, noAccess, 1, [{[1,3,7,0], 4}]), + ?line ?expect3(noAccess, 1, [{[1,3,7,0], 4}]), s([{[tDescr,0], s, "outside mibview"}]), - ?line expect(11, noAccess, 1, [{[tDescr,0], "outside mibview"}]), + ?line ?expect3(noAccess, 1, [{[tDescr,0], "outside mibview"}]), %% 4.2.5:2 s([{[1,3,6,1,0], s, "noSuchObject"}]), - ?line expect(20, notWritable, 1, [{[1,3,6,1,0], "noSuchObject"}]), + ?line ?expect3(notWritable, 1, [{[1,3,6,1,0], "noSuchObject"}]), %% 4.2.5:3 s([{[tDescr2, 0], i, 4}]), - ?line expect(30, wrongType, 1, [{[tDescr2, 0], 4}]), + ?line ?expect3(wrongType, 1, [{[tDescr2, 0], 4}]), s([{[tDescr2, 0], s, "badValue"}]), - ?line expect(31, badValue, 1, [{[tDescr2, 0], "badValue"}]), + ?line ?expect3(badValue, 1, [{[tDescr2, 0], "badValue"}]), %% 4.2.5:4 s([{[tStr, 0], s, ""}]), - ?line expect(40, wrongLength, 1, [{[tStr, 0], ""}]), + ?line ?expect3(wrongLength, 1, [{[tStr, 0], ""}]), s([{[tStr, 0], s, "12345"}]), - ?line expect(40, wrongLength, 1, [{[tStr, 0], "12345"}]), + ?line ?expect3(wrongLength, 1, [{[tStr, 0], "12345"}]), %% 4.2.5:5 - N/A %% 4.2.5:6 s([{[tInt1, 0], i, 0}]), - ?line expect(60, wrongValue, 1, [{[tInt1, 0], 0}]), + ?line ?expect3(wrongValue, 1, [{[tInt1, 0], 0}]), s([{[tInt1, 0], i, 5}]), - ?line expect(61, wrongValue, 1, [{[tInt1, 0], 5}]), + ?line ?expect3(wrongValue, 1, [{[tInt1, 0], 5}]), s([{[tInt2, 0], i, 0}]), - ?line expect(62, wrongValue, 1, [{[tInt2, 0], 0}]), + ?line ?expect3(wrongValue, 1, [{[tInt2, 0], 0}]), s([{[tInt2, 0], i, 5}]), - ?line expect(63, wrongValue, 1, [{[tInt2, 0], 5}]), + ?line ?expect3(wrongValue, 1, [{[tInt2, 0], 5}]), s([{[tInt3, 0], i, 5}]), - ?line expect(64, wrongValue, 1, [{[tInt3, 0], 5}]), + ?line ?expect3(wrongValue, 1, [{[tInt3, 0], 5}]), %% 4.2.5:7 s([{[tDescrX, 1, 1], s, "noCreation"}]), - ?line expect(70, noCreation, 1, [{[tDescrX, 1, 1], "noCreation"}]), + ?line ?expect3(noCreation, 1, [{[tDescrX, 1, 1], "noCreation"}]), %% 4.2.5:8 s([{[tDescrX, 1, 2], s, "inconsistentName"}]), - ?line expect(80, inconsistentName, 1, - [{[tDescrX, 1, 2], "inconsistentName"}]), + ?line ?expect3(inconsistentName, 1, + [{[tDescrX, 1, 2], "inconsistentName"}]), %% 4.2.5:9 s([{[tCnt, 1, 2], i, 5}]), - ?line expect(90, notWritable, 1, [{[tCnt, 1, 2], 5}]), + ?line ?expect3(notWritable, 1, [{[tCnt, 1, 2], 5}]), s([{[tDescr3,0], s, "read-only"}]), - ?line expect(90, notWritable, 1, [{[tDescr3,0], "read-only"}]), + ?line ?expect3(notWritable, 1, [{[tDescr3,0], "read-only"}]), %% 4.2.5:10 s([{[tDescr2,0], s, "inconsistentValue"}]), - ?line expect(100, inconsistentValue, 1, - [{[tDescr2,0], "inconsistentValue"}]), + ?line ?expect3(inconsistentValue, 1, + [{[tDescr2,0], "inconsistentValue"}]), %% 4.2.5:11 s([{[tDescr2,0], s, "resourceUnavailable"}]), - ?line expect(110, resourceUnavailable, 1, - [{[tDescr2,0],"resourceUnavailable"}]), + ?line ?expect3(resourceUnavailable, 1, + [{[tDescr2,0],"resourceUnavailable"}]), %% 4.2.5:12 s([{[tDescr2, 0], s, "is_set_ok_fail"}]), - ?line expect(120, genErr, 1, [{[tDescr2, 0], "is_set_ok_fail"}]). + ?line ?expect3(genErr, 1, [{[tDescr2, 0], "is_set_ok_fail"}]). %% commitFailed and undoFailed is tested by the 'undo' case. @@ -2797,101 +2817,101 @@ table_test() -> Key1c4 = [intCommunityAccess,get(mip),is("public")], EndKey = [intCommunityEntry,[9],get(mip),is("public")], gn([[intCommunityEntry]]), - ?line expect(7, [{Key1c3, 2}]), + ?line ?expect1([{Key1c3, 2}]), gn([[intCommunityTable]]), - ?line expect(71, [{Key1c3, 2}]), + ?line ?expect1([{Key1c3, 2}]), gn([[community]]), - ?line expect(72, [{Key1c3, 2}]), + ?line ?expect1([{Key1c3, 2}]), gn([[otpSnmpeaMIB]]), - ?line expect(73, [{Key1c3, 2}]), + ?line ?expect1([{Key1c3, 2}]), gn([[ericsson]]), - ?line expect(74, [{Key1c3, 2}]), + ?line ?expect1([{Key1c3, 2}]), gn([Key1c3]), - ?line expect(8, [{Key2c3, 2}]), + ?line ?expect1([{Key2c3, 2}]), gn([Key2c3]), - ?line expect(9, [{Key1c4, 2}]), + ?line ?expect1([{Key1c4, 2}]), gn([EndKey]), AgentIp = [intAgentIpAddress,0], - ?line expect(10, [{AgentIp, any}]), + ?line ?expect1([{AgentIp, any}]), g([Key1c3]), - ?line expect(11, [{Key1c3, 2}]), + ?line ?expect1([{Key1c3, 2}]), g([EndKey]), - ?line ?v1_2(expect(12, noSuchName, 1, any), - expect(12, [{EndKey, noSuchObject}])), + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{EndKey, noSuchObject}])), io:format("Testing row creation/deletion on communityTable...~n"), NewKeyc3 = [intCommunityViewIndex,get(mip),is("test")], NewKeyc4 = [intCommunityAccess,get(mip),is("test")], NewKeyc5 = [intCommunityStatus,get(mip),is("test")], s([{NewKeyc5, ?createAndGo}]), - ?line expect(14, ?v1_2(badValue, inconsistentValue), 1,any), + ?line ?expect3(?v1_2(badValue, inconsistentValue), 1, any), s([{NewKeyc5, ?createAndGo}, {NewKeyc3, 2}, {NewKeyc4, 2}]), - ?line expect(15, [{NewKeyc5, ?createAndGo},{NewKeyc3, 2}, {NewKeyc4, 2}]), + ?line ?expect1([{NewKeyc5, ?createAndGo},{NewKeyc3, 2}, {NewKeyc4, 2}]), g([NewKeyc4]), - ?line expect(16, [{NewKeyc4, 2}]), + ?line ?expect1([{NewKeyc4, 2}]), s([{NewKeyc5, ?destroy}]), - ?line expect(17, [{NewKeyc5, ?destroy}]), + ?line ?expect1([{NewKeyc5, ?destroy}]), s([{NewKeyc4, 2}]), - ?line expect(18, ?v1_2(noSuchName, inconsistentName), 1,[{NewKeyc4, 2}]), + ?line ?expect3(?v1_2(noSuchName, inconsistentName), 1, [{NewKeyc4, 2}]), s([{NewKeyc5, ?createAndWait}]), - ?line expect(19, [{NewKeyc5, ?createAndWait}]), + ?line ?expect1([{NewKeyc5, ?createAndWait}]), g([NewKeyc5]), - ?line expect(20, [{NewKeyc5, ?notReady}]), + ?line ?expect1([{NewKeyc5, ?notReady}]), s([{NewKeyc4, 2}]), - ?line expect(21, [{NewKeyc4, 2}]), + ?line ?expect1([{NewKeyc4, 2}]), g([NewKeyc5]), - ?line expect(22, [{NewKeyc5, ?notReady}]), + ?line ?expect1([{NewKeyc5, ?notReady}]), s([{NewKeyc3, 2}]), - ?line expect(23, [{NewKeyc3, 2}]), + ?line ?expect1([{NewKeyc3, 2}]), g([NewKeyc5]), - ?line expect(24, [{NewKeyc5, ?notInService}]), + ?line ?expect1([{NewKeyc5, ?notInService}]), s([{NewKeyc5, ?active}]), - ?line expect(25, [{NewKeyc5, ?active}]), + ?line ?expect1([{NewKeyc5, ?active}]), s([{NewKeyc5, ?destroy}]), - ?line expect(26, [{NewKeyc5, ?destroy}]), + ?line ?expect1([{NewKeyc5, ?destroy}]), s([{NewKeyc3, 3}]), - ?line expect(27, ?v1_2(noSuchName, inconsistentName), 1,[{NewKeyc3, 3}]), + ?line ?expect3(?v1_2(noSuchName, inconsistentName), 1, [{NewKeyc3, 3}]), otp_1128(). %% Req. system group simple_standard_test() -> ?DBG("simple_standard_test -> entry",[]), gn([[1,1]]), - ?line expect(1, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[1,3]]), - ?line expect(11, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[1,3,6]]), - ?line expect(12, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[1,3,6,1]]), - ?line expect(13, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[1,3,6,1,2]]), - ?line expect(14, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[1,3,6,1,2,1]]), - ?line expect(15, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[1,3,6,1,2,1,1]]), - ?line expect(16, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), gn([[sysDescr]]), - ?line expect(17, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), g([[sysDescr,0]]), - ?line expect(2, [{[sysDescr,0], "Erlang SNMP agent"}]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}]), g([[sysDescr]]), - ?line ?v1_2(expect(3, noSuchName, 1, any), - expect(3, [{[sysDescr], noSuchObject}])), + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{[sysDescr], noSuchObject}])), g([[1,6,7,0]]), - ?line ?v1_2(expect(41, noSuchName, 1, any), - expect(3, [{[1,6,7,0], noSuchObject}])), + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{[1,6,7,0], noSuchObject}])), gn([[1,13]]), - ?line ?v1_2(expect(4, noSuchName,1, any), - expect(4, [{[1,13], endOfMibView}])), + ?line ?v1_2(?expect3(noSuchName,1, any), + ?expect1([{[1,13], endOfMibView}])), s([{[sysLocation, 0], "new_value"}]), - ?line expect(5, [{[sysLocation, 0], "new_value"}]), + ?line ?expect1([{[sysLocation, 0], "new_value"}]), g([[sysLocation, 0]]), - ?line expect(6, [{[sysLocation, 0], "new_value"}]), + ?line ?expect1([{[sysLocation, 0], "new_value"}]), io:format("Testing noSuchName and badValue...~n"), s([{[sysServices,0], 3}]), - ?line expect(61, ?v1_2(noSuchName, notWritable), 1, any), + ?line ?expect3(?v1_2(noSuchName, notWritable), 1, any), s([{[sysLocation, 0], i, 3}]), - ?line expect(62, ?v1_2(badValue, wrongType), 1, any), + ?line ?expect3(?v1_2(badValue, wrongType), 1, any), ?DBG("simple_standard_test -> done",[]), ok. @@ -2908,7 +2928,7 @@ db_notify_client(Config) when is_list(Config) -> snmpa_local_db:verbosity(trace), Self = self(), ?DBG("db_notify_client -> register self (~p) notify client", [Self]), - snmpa_local_db:register_notify_client(self(),?MODULE), + snmpa_local_db:register_notify_client(Self, ?MODULE), %% This call (to the manager) will issue to set operations, so %% we expect to receive to notify(insert) calls. @@ -2933,7 +2953,7 @@ db_notify_client(Config) when is_list(Config) -> end, ?DBG("db_notify_client -> unregister self (~p) notify client", [Self]), - snmpa_local_db:unregister_notify_client(self()), + snmpa_local_db:unregister_notify_client(Self), ?DBG("db_notify_client -> minimize verbosity", []), snmpa_local_db:verbosity(silence), @@ -2945,11 +2965,11 @@ db_notify_client(Config) when is_list(Config) -> db_notify_client_test() -> ?DBG("set first new sysLocation",[]), s([{[sysLocation, 0], "new_value"}]), - ?line expect(5, [{[sysLocation, 0], "new_value"}]), + ?line ?expect1([{[sysLocation, 0], "new_value"}]), ?DBG("set second new sysLocation",[]), s([{[sysLocation, 0], "new_value"}]), - ?line expect(5, [{[sysLocation, 0], "new_value"}]). + ?line ?expect1([{[sysLocation, 0], "new_value"}]). notify(Pid, What) -> ?DBG("notify(~p,~p) -> called",[Pid,What]), @@ -2966,24 +2986,23 @@ big_test() -> ?DBG("big_test -> testing simple next/get/set @ subagent...",[]), gn([[klas1]]), - ?line expect(1, [{[fname,0], ""}]), + ?line ?expect1([{[fname,0], ""}]), g([[fname,0]]), - ?line expect(2, [{[fname,0], ""}]), + ?line ?expect1([{[fname,0], ""}]), s([{[fname,0], s, "test set"}]), - ?line expect(3, [{[fname,0], "test set"}]), + ?line ?expect1([{[fname,0], "test set"}]), g([[fname,0]]), - ?line expect(4, [{[fname,0], "test set"}]), + ?line ?expect1([{[fname,0], "test set"}]), ?DBG("big_test -> " "testing next from last instance in master to subagent...",[]), gn([[?v1_2(sysServices, sysORLastChange),0]]), - ?line expect(5, [{[fname,0], "test set"}]), - gn([[1,1], - [?v1_2(sysServices, sysORLastChange),0]]), - ?line expect(51, [{[sysDescr,0], "Erlang SNMP agent"}, - {[fname,0], "test set"}]), + ?line ?expect1([{[fname,0], "test set"}]), + gn([[1,1], [?v1_2(sysServices, sysORLastChange),0]]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {[fname,0], "test set"}]), s([{[fname,0], s, ""}]), - ?line expect(52, [{[fname,0], ""}]), + ?line ?expect1([{[fname,0], ""}]), table_test(), @@ -2991,14 +3010,14 @@ big_test() -> _FTab = [friendsEntry], s([{[friendsEntry, [2, 3]], s, "kompis3"}, {[friendsEntry, [3, 3]], i, ?createAndGo}]), - ?line expect(6, [{[friendsEntry, [2, 3]], "kompis3"}, - {[friendsEntry, [3, 3]], ?createAndGo}]), + ?line ?expect1([{[friendsEntry, [2, 3]], "kompis3"}, + {[friendsEntry, [3, 3]], ?createAndGo}]), g([[friendsEntry, [2, 3]], [friendsEntry, [3, 3]]]), - ?line expect(7, [{[friendsEntry, [2, 3]], "kompis3"}, - {[friendsEntry, [3, 3]], ?active}]), + ?line ?expect1([{[friendsEntry, [2, 3]], "kompis3"}, + {[friendsEntry, [3, 3]], ?active}]), s([{[friendsEntry, [3, 3]], i, ?destroy}]), - ?line expect(8, [{[friendsEntry, [3, 3]], ?destroy}]), + ?line ?expect1([{[friendsEntry, [3, 3]], ?destroy}]), otp_1131(), @@ -3006,28 +3025,28 @@ big_test() -> []), s([{[kompissEntry, [1, 3]], s, "kompis3"}, {[kompissEntry, [2, 3]], i, ?createAndGo}]), - ?line expect(9, [{[kompissEntry, [1, 3]], "kompis3"}, - {[kompissEntry, [2, 3]], ?createAndGo}]), + ?line ?expect1([{[kompissEntry, [1, 3]], "kompis3"}, + {[kompissEntry, [2, 3]], ?createAndGo}]), g([[kompissEntry, [1, 3]], [kompissEntry, [2, 3]]]), - ?line expect(10, [{[kompissEntry, [1, 3]], "kompis3"}, - {[kompissEntry, [2, 3]], ?active}]), + ?line ?expect1([{[kompissEntry, [1, 3]], "kompis3"}, + {[kompissEntry, [2, 3]], ?active}]), gn([[kompissEntry, [1]], [kompissEntry, [2]]]), - ?line expect(11, [{[kompissEntry, [1, 3]], "kompis3"}, - {[kompissEntry, [2, 3]], ?active}]), + ?line ?expect1([{[kompissEntry, [1, 3]], "kompis3"}, + {[kompissEntry, [2, 3]], ?active}]), s([{[kompissEntry, [1, 2]], s, "kompis3"}, {[kompissEntry, [2, 2]], i, ?createAndGo}]), - ?line expect(12, [{[kompissEntry, [1, 2]], "kompis3"}, - {[kompissEntry, [2, 2]], ?createAndGo}]), + ?line ?expect1([{[kompissEntry, [1, 2]], "kompis3"}, + {[kompissEntry, [2, 2]], ?createAndGo}]), gn([[kompissEntry, [1, 1]], [kompissEntry, [2, 1]]]), - ?line expect(13, [{[kompissEntry, [1, 2]], "kompis3"}, - {[kompissEntry, [2, 2]], ?active}]), + ?line ?expect1([{[kompissEntry, [1, 2]], "kompis3"}, + {[kompissEntry, [2, 2]], ?active}]), s([{[kompissEntry, [2, 3]], i, ?destroy}]), - ?line expect(14, [{[kompissEntry, [2, 3]], ?destroy}]), + ?line ?expect1([{[kompissEntry, [2, 3]], ?destroy}]), s([{[kompissEntry, [2, 2]], i, ?destroy}]), - ?line expect(15, [{[kompissEntry, [2, 2]], ?destroy}]), + ?line ?expect1([{[kompissEntry, [2, 2]], ?destroy}]), ?DBG("big_test -> done",[]), ok. @@ -3038,23 +3057,22 @@ big_test_2() -> ?P1("Testing simple next/get/set @ subagent (2)..."), gn([[klas2]]), - ?line expect(1, [{[fname2,0], ""}]), + ?line ?expect1([{[fname2,0], ""}]), g([[fname2,0]]), - ?line expect(2, [{[fname2,0], ""}]), + ?line ?expect1([{[fname2,0], ""}]), s([{[fname2,0], s, "test set"}]), - ?line expect(3, [{[fname2,0], "test set"}]), + ?line ?expect1([{[fname2,0], "test set"}]), g([[fname2,0]]), - ?line expect(4, [{[fname2,0], "test set"}]), + ?line ?expect1([{[fname2,0], "test set"}]), otp_1298(), ?P1("Testing next from last object in master to subagent (2)..."), gn([[?v1_2(sysServices, sysORLastChange),0]]), - ?line expect(5, [{[fname2,0], "test set"}]), - gn([[1,1], - [?v1_2(sysServices, sysORLastChange),0]]), - ?line expect(51, [{[sysDescr,0], "Erlang SNMP agent"}, - {[fname2,0], "test set"}]), + ?line ?expect1([{[fname2,0], "test set"}]), + gn([[1,1], [?v1_2(sysServices, sysORLastChange),0]]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {[fname2,0], "test set"}]), table_test(), @@ -3062,40 +3080,40 @@ big_test_2() -> _FTab = [friendsEntry2], s([{[friendsEntry2, [2, 3]], s, "kompis3"}, {[friendsEntry2, [3, 3]], i, ?createAndGo}]), - ?line expect(6, [{[friendsEntry2, [2, 3]], "kompis3"}, - {[friendsEntry2, [3, 3]], ?createAndGo}]), + ?line ?expect1([{[friendsEntry2, [2, 3]], "kompis3"}, + {[friendsEntry2, [3, 3]], ?createAndGo}]), g([[friendsEntry2, [2, 3]], [friendsEntry2, [3, 3]]]), - ?line expect(7, [{[friendsEntry2, [2, 3]], "kompis3"}, - {[friendsEntry2, [3, 3]], ?active}]), + ?line ?expect1([{[friendsEntry2, [2, 3]], "kompis3"}, + {[friendsEntry2, [3, 3]], ?active}]), s([{[friendsEntry2, [3, 3]], i, ?destroy}]), - ?line expect(8, [{[friendsEntry2, [3, 3]], ?destroy}]), + ?line ?expect1([{[friendsEntry2, [3, 3]], ?destroy}]), ?P1("Adding two rows in subagent table with special INDEX (2)"), s([{[kompissEntry2, [1, 3]], s, "kompis3"}, {[kompissEntry2, [2, 3]], i, ?createAndGo}]), - ?line expect(9, [{[kompissEntry2, [1, 3]], "kompis3"}, - {[kompissEntry2, [2, 3]], ?createAndGo}]), + ?line ?expect1([{[kompissEntry2, [1, 3]], "kompis3"}, + {[kompissEntry2, [2, 3]], ?createAndGo}]), g([[kompissEntry2, [1, 3]], [kompissEntry2, [2, 3]]]), - ?line expect(10, [{[kompissEntry2, [1, 3]], "kompis3"}, - {[kompissEntry2, [2, 3]], ?active}]), + ?line ?expect1([{[kompissEntry2, [1, 3]], "kompis3"}, + {[kompissEntry2, [2, 3]], ?active}]), gn([[kompissEntry2, [1]], [kompissEntry2, [2]]]), - ?line expect(11, [{[kompissEntry2, [1, 3]], "kompis3"}, - {[kompissEntry2, [2, 3]], ?active}]), + ?line ?expect1([{[kompissEntry2, [1, 3]], "kompis3"}, + {[kompissEntry2, [2, 3]], ?active}]), s([{[kompissEntry2, [1, 2]], s, "kompis3"}, {[kompissEntry2, [2, 2]], i, ?createAndGo}]), - ?line expect(12, [{[kompissEntry2, [1, 2]], "kompis3"}, - {[kompissEntry2, [2, 2]], ?createAndGo}]), + ?line ?expect1([{[kompissEntry2, [1, 2]], "kompis3"}, + {[kompissEntry2, [2, 2]], ?createAndGo}]), gn([[kompissEntry2, [1, 1]], [kompissEntry2, [2, 1]]]), - ?line expect(13, [{[kompissEntry2, [1, 2]], "kompis3"}, - {[kompissEntry2, [2, 2]], ?active}]), + ?line ?expect1([{[kompissEntry2, [1, 2]], "kompis3"}, + {[kompissEntry2, [2, 2]], ?active}]), s([{[kompissEntry2, [2, 3]], i, ?destroy}]), - ?line expect(14, [{[kompissEntry2, [2, 3]], ?destroy}]), + ?line ?expect1([{[kompissEntry2, [2, 3]], ?destroy}]), s([{[kompissEntry2, [2, 2]], i, ?destroy}]), - ?line expect(15, [{[kompissEntry2, [2, 2]], ?destroy}]), + ?line ?expect1([{[kompissEntry2, [2, 2]], ?destroy}]), ok. %% Req. Test1 @@ -3104,26 +3122,26 @@ multi_threaded_test() -> g([[multiStr,0]]), Pid = get_multi_pid(), g([[sysUpTime,0]]), - ?line expect(1, [{[sysUpTime,0], any}]), + ?line ?expect1([{[sysUpTime,0], any}]), s([{[sysLocation, 0], s, "pelle"}]), - ?line expect(2, [{[sysLocation, 0], "pelle"}]), + ?line ?expect1([{[sysLocation, 0], "pelle"}]), Pid ! continue, - ?line expect(3, [{[multiStr,0], "ok"}]), + ?line ?expect1([{[multiStr,0], "ok"}]), s([{[multiStr, 0], s, "block"}]), Pid2 = get_multi_pid(), g([[sysUpTime,0]]), - ?line expect(4, [{[sysUpTime,0], any}]), + ?line ?expect1([{[sysUpTime,0], any}]), g([[multiStr,0]]), Pid3 = get_multi_pid(), g([[sysUpTime,0]]), - ?line expect(5, [{[sysUpTime,0], any}]), + ?line ?expect1([{[sysUpTime,0], any}]), s([{[sysLocation, 0], s, "kalle"}]), Pid3 ! continue, - ?line expect(6, [{[multiStr,0], "ok"}]), + ?line ?expect1([{[multiStr,0], "ok"}]), Pid2 ! continue, - ?line expect(7, [{[multiStr,0], "block"}]), - ?line expect(8, [{[sysLocation,0], "kalle"}]). + ?line ?expect1([{[multiStr,0], "block"}]), + ?line ?expect1([{[sysLocation,0], "kalle"}]). %% Req. Test1, TestTrapv2 mt_trap_test(MA) -> @@ -3131,9 +3149,8 @@ mt_trap_test(MA) -> ?DBG("mt_trap_test(01) -> issue testTrapv22 (standard trap)", []), snmpa:send_trap(MA, testTrapv22, "standard trap"), ?DBG("mt_trap_test(02) -> await v2trap", []), - ?line expect(mt_trap_test_1, v2trap, - [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]), ?DBG("mt_trap_test(03) -> issue mtTrap (standard trap)", []), snmpa:send_trap(MA, mtTrap, "standard trap"), @@ -3142,21 +3159,21 @@ mt_trap_test(MA) -> g([[sysUpTime,0]]), ?DBG("mt_trap_test(06) -> await sysUpTime", []), - ?line expect(mt_trap_test_2, [{[sysUpTime,0], any}]), + ?line ?expect1([{[sysUpTime,0], any}]), ?DBG("mt_trap_test(07) -> issue testTrapv22 (standard trap)", []), snmpa:send_trap(MA, testTrapv22, "standard trap"), ?DBG("mt_trap_test(08) -> await v2trap", []), - ?line expect(mt_trap_test_3, v2trap, - [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]), + ?line ?expect2(v2trap, + [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]), ?DBG("mt_trap_test(09) -> send continue to multi-pid", []), Pid ! continue, ?DBG("mt_trap_test(10) -> await v2trap", []), - ?line expect(mt_trap_test_4, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?testTrap ++ [2]}, - {[multiStr,0], "ok"}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?testTrap ++ [2]}, + {[multiStr,0], "ok"}]), ?DBG("mt_trap_test(11) -> done", []), ok. @@ -3177,26 +3194,26 @@ types_v2_test() -> ?P1("Testing v2 types..."), s([{[bits1,0], 2#10}]), - ?line expect(1, [{[bits1,0], ?str(2#10)}]), + ?line ?expect1([{[bits1,0], ?str(2#10)}]), g([[bits1,0]]), - ?line expect(2, [{[bits1,0], ?str(2#101)}]), + ?line ?expect1([{[bits1,0], ?str(2#101)}]), s([{[bits2,0], 2#11000000110}]), - ?line expect(3, [{[bits2,0], ?str(2#11000000110)}]), + ?line ?expect1([{[bits2,0], ?str(2#11000000110)}]), g([[bits2,0]]), - ?line expect(4, [{[bits2,0], ?str(2#11000000110)}]), + ?line ?expect1([{[bits2,0], ?str(2#11000000110)}]), g([[bits3,0]]), - ?line expect(50, genErr, 1, any), + ?line ?expect3(genErr, 1, any), g([[bits4,0]]), - ?line expect(51, genErr, 1, any), + ?line ?expect3(genErr, 1, any), s([{[bits1,0], s, [2#10]}]), - ?line expect(6, ?v1_2(badValue, wrongValue), 1, any), + ?line ?expect3(?v1_2(badValue, wrongValue), 1, any), s([{[bits2,0], 2#11001001101010011}]), - ?line expect(7, ?v1_2(badValue, wrongValue), 1, any). + ?line ?expect3(?v1_2(badValue, wrongValue), 1, any). %% Req. Test1 @@ -3204,64 +3221,63 @@ implied_test(MA) -> ?LOG("implied_test -> start",[]), ?P1("Testing IMPLIED..."), - snmpa:verbosity(MA,trace), - snmpa:verbosity(MA,trace), + snmpa:verbosity(MA, trace), %% Create two rows, check that they are get-nexted in correct order. Idx1 = "apa", Idx2 = "qq", ?DBG("implied_test -> (send) create row 1 '~s' in table 1",[Idx1]), s([{[testStatus, Idx1], i, ?createAndGo}, {[testDescr, Idx1],s,"row 1"}]), - ?line expect(1, [{[testStatus, Idx1], ?createAndGo}, - {[testDescr, Idx1], "row 1"}]), + ?line ?expect1([{[testStatus, Idx1], ?createAndGo}, + {[testDescr, Idx1], "row 1"}]), ?DBG("implied_test -> (send) create row 2 '~s' in table 1",[Idx2]), s([{[testStatus, Idx2], i, ?createAndGo}, {[testDescr, Idx2],s,"row 2"}]), - ?line expect(2, [{[testStatus, Idx2], ?createAndGo}, - {[testDescr, Idx2], "row 2"}]), + ?line ?expect1([{[testStatus, Idx2], ?createAndGo}, + {[testDescr, Idx2], "row 2"}]), ?DBG("implied_test -> get-next(testDescr)",[]), gn([[testDescr]]), - ?line expect(3, [{[testDescr,Idx1], "row 1"}]), + ?line ?expect1([{[testDescr,Idx1], "row 1"}]), ?DBG("implied_test -> get-next(testDescr) of row 1",[]), gn([[testDescr,Idx1]]), - ?line expect(4, [{[testDescr,Idx2], "row 2"}]), + ?line ?expect1([{[testDescr,Idx2], "row 2"}]), % Delete the rows ?DBG("implied_test -> (send) delete row 1 '~s' from table 1",[Idx1]), s([{[testStatus, Idx1], i, ?destroy}]), - ?line expect(5, [{[testStatus, Idx1], ?destroy}]), + ?line ?expect1([{[testStatus, Idx1], ?destroy}]), ?DBG("implied_test -> (send) delete row 2 '~s' from table 1",[Idx2]), s([{[testStatus, Idx2], i, ?destroy}]), - ?line expect(6, [{[testStatus, Idx2], ?destroy}]), + ?line ?expect1([{[testStatus, Idx2], ?destroy}]), %% Try the same in other table Idx3 = [1, "apa"], Idx4 = [1, "qq"], ?DBG("implied_test -> (send) create row 1 '~s' in table 2",[Idx3]), s([{[testStatus2, Idx3], i, ?createAndGo}, {[testDescr2,Idx3],s,"row 1"}]), - ?line expect(1, [{[testStatus2, Idx3], ?createAndGo}, - {[testDescr2, Idx3], "row 1"}]), + ?line ?expect1([{[testStatus2, Idx3], ?createAndGo}, + {[testDescr2, Idx3], "row 1"}]), ?DBG("implied_test -> (send) create row 2 '~s' in table 2",[Idx4]), s([{[testStatus2, Idx4], i, ?createAndGo}, {[testDescr2,Idx4],s,"row 2"}]), - ?line expect(2, [{[testStatus2, Idx4], ?createAndGo}, - {[testDescr2, Idx4], "row 2"}]), + ?line ?expect1([{[testStatus2, Idx4], ?createAndGo}, + {[testDescr2, Idx4], "row 2"}]), ?DBG("implied_test -> get-next(testDescr2)",[]), gn([[testDescr2]]), - ?line expect(3, [{[testDescr2,Idx3], "row 1"}]), + ?line ?expect1([{[testDescr2,Idx3], "row 1"}]), ?DBG("implied_test -> get-next(testDescr2) of row 1",[]), gn([[testDescr2,Idx3]]), - ?line expect(4, [{[testDescr2,Idx4], "row 2"}]), + ?line ?expect1([{[testDescr2,Idx4], "row 2"}]), % Delete the rows ?DBG("implied_test -> (send) delete row 1 '~s' from table 2",[Idx3]), s([{[testStatus2, Idx3], i, ?destroy}]), - ?line expect(5, [{[testStatus2, Idx3], ?destroy}]), + ?line ?expect1([{[testStatus2, Idx3], ?destroy}]), ?DBG("implied_test -> (send) delete row 2 '~s' from table 2",[Idx4]), s([{[testStatus2, Idx4], i, ?destroy}]), - ?line expect(6, [{[testStatus2, Idx4], ?destroy}]), + ?line ?expect1([{[testStatus2, Idx4], ?destroy}]), - snmpa:verbosity(MA,log), + snmpa:verbosity(MA, log), - ?LOG("implied_test -> done",[]). + ?LOG("implied_test -> done", []). @@ -3274,25 +3290,25 @@ sparse_table_test() -> Idx2 = 2, s([{[sparseStatus, Idx1], i, ?createAndGo}, {[sparseDescr, Idx1], s, "row 1"}]), - ?line expect(1, [{[sparseStatus, Idx1], ?createAndGo}, - {[sparseDescr, Idx1], "row 1"}]), + ?line ?expect1([{[sparseStatus, Idx1], ?createAndGo}, + {[sparseDescr, Idx1], "row 1"}]), s([{[sparseStatus, Idx2], i, ?createAndGo}, {[sparseDescr, Idx2], s, "row 2"}]), - ?line expect(2, [{[sparseStatus, Idx2], ?createAndGo}, - {[sparseDescr, Idx2], "row 2"}]), + ?line ?expect1([{[sparseStatus, Idx2], ?createAndGo}, + {[sparseDescr, Idx2], "row 2"}]), ?v1_2(gn([[sparseIndex], [sparseDescr,Idx1], [sparseDescr,Idx2], [sparseStatus,Idx1], [sparseStatus,Idx2]]), gb(0,5,[[sparseIndex]])), - ?line expect(3, [{[sparseDescr,Idx1], "row 1"}, - {[sparseDescr,Idx2], "row 2"}, - {[sparseStatus,Idx1], ?active}, - {[sparseStatus,Idx2], ?active}, - {[sparseStr,0], "slut"}]), - % Delete the rows + ?line ?expect1([{[sparseDescr,Idx1], "row 1"}, + {[sparseDescr,Idx2], "row 2"}, + {[sparseStatus,Idx1], ?active}, + {[sparseStatus,Idx2], ?active}, + {[sparseStr,0], "slut"}]), + %% Delete the rows s([{[sparseStatus, Idx1], i, ?destroy}]), - ?line expect(4, [{[sparseStatus, Idx1], ?destroy}]), + ?line ?expect1([{[sparseStatus, Idx1], ?destroy}]), s([{[sparseStatus, Idx2], i, ?destroy}]), - ?line expect(5, [{[sparseStatus, Idx2], ?destroy}]). + ?line ?expect1([{[sparseStatus, Idx2], ?destroy}]). %% Req. Test1 @@ -3306,13 +3322,13 @@ cnt_64_test(MA) -> ?DBG("get cnt64",[]), g([[cnt64,0]]), ?DBG("await response",[]), - ?line ?v1_2(expect(1, noSuchName, 1, any), - expect(1, [{[cnt64,0],18446744073709551615}])), + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{[cnt64,0],18446744073709551615}])), ?DBG("get-next cnt64",[]), gn([[cnt64]]), ?DBG("await response",[]), - ?line ?v1_2(expect(2, [{[cnt64Str,0], "after cnt64"}]), - expect(2, [{[cnt64,0],18446744073709551615}])), + ?line ?v1_2(?expect1([{[cnt64Str,0], "after cnt64"}]), + ?expect1([{[cnt64,0],18446744073709551615}])), ?DBG("send cntTrap",[]), snmpa:send_trap(MA,cntTrap,"standard trap",[ {sysContact, "pelle"}, @@ -3320,13 +3336,13 @@ cnt_64_test(MA) -> {sysLocation, "here"} ]), ?DBG("await response",[]), - ?line ?v1_2(expect(3, trap, [test], 6, 1, [{[sysContact,0], "pelle"}, - {[sysLocation,0], "here"}]), - expect(3, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?testTrap ++ [1]}, - {[sysContact,0], "pelle"}, - {[cnt64,0], 10}, - {[sysLocation,0], "here"}])), + ?line ?v1_2(?expect5(trap, [test], 6, 1, [{[sysContact,0], "pelle"}, + {[sysLocation,0], "here"}]), + ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?testTrap ++ [1]}, + {[sysContact,0], "pelle"}, + {[cnt64,0], 10}, + {[sysLocation,0], "here"}])), %% Create two rows, check that they are get-nexted in correct order. Idx1 = 1, @@ -3334,27 +3350,27 @@ cnt_64_test(MA) -> ?DBG("create row (cntStatus): ~p",[Idx1]), s([{[cntStatus, Idx1], i, ?createAndGo}]), ?DBG("await response",[]), - ?line expect(1, [{[cntStatus, Idx1], ?createAndGo}]), + ?line ?expect1([{[cntStatus, Idx1], ?createAndGo}]), ?DBG("create row (cntStatus): ~p",[Idx2]), s([{[cntStatus, Idx2], i, ?createAndGo}]), ?DBG("await response",[]), - ?line expect(2, [{[cntStatus, Idx2], ?createAndGo}]), + ?line ?expect1([{[cntStatus, Idx2], ?createAndGo}]), ?DBG("get-next (cntIndex)",[]), gn([[cntIndex]]), ?DBG("await response",[]), - ?line ?v1_2(expect(3, [{[cntStatus,Idx1], ?active}]), - expect(3, [{[cntCnt,Idx1], 0}])), + ?line ?v1_2(?expect1([{[cntStatus,Idx1], ?active}]), + ?expect1([{[cntCnt,Idx1], 0}])), % Delete the rows ?DBG("delete row (cntStatus): ~p",[Idx1]), s([{[cntStatus, Idx1], i, ?destroy}]), ?DBG("await response",[]), - ?line expect(4, [{[cntStatus, Idx1], ?destroy}]), + ?line ?expect1([{[cntStatus, Idx1], ?destroy}]), ?DBG("delete row (cntStatus): ~p",[Idx2]), s([{[cntStatus, Idx2], i, ?destroy}]), ?DBG("await response",[]), - ?line expect(5, [{[cntStatus, Idx2], ?destroy}]), - catch snmpa:verbosity(MA,log), + ?line ?expect1([{[cntStatus, Idx2], ?destroy}]), + catch snmpa:verbosity(MA, log), ?DBG("done",[]), ok. @@ -3362,7 +3378,7 @@ cnt_64_test(MA) -> opaque_test() -> ?P1("Testing Opaque datatype..."), g([[opaqueObj,0]]), - ?line expect(1, [{[opaqueObj,0], "opaque-data"}]). + ?line ?expect1([{[opaqueObj,0], "opaque-data"}]). %% Req. OLD-SNMPEA-MIB api_test(MaNode) -> @@ -3403,70 +3419,69 @@ api_test(MaNode) -> %% Req. Klas3 api_test2() -> g([[fname3,0]]), - ?line expect(1, [{[fname3,0], "ok"}]), + ?line ?expect1([{[fname3,0], "ok"}]), g([[fname4,0]]), - ?line expect(2, [{[fname4,0], 1}]). + ?line ?expect1([{[fname4,0], 1}]). api_test3() -> g([[fname3,0]]), - ?line expect(1, [{[fname3,0], "ok"}]). + ?line ?expect1([{[fname3,0], "ok"}]). unreg_test() -> gn([[?v1_2(sysServices, sysORLastChange),0]]), - ?line expect(1, [{[snmpInPkts, 0], any}]). + ?line ?expect1([{[snmpInPkts, 0], any}]). load_test() -> gn([[?v1_2(sysServices, sysORLastChange),0]]), - ?line expect(1, [{[fname,0], ""}]). + ?line ?expect1([{[fname,0], ""}]). %% Req. Klas1 load_test_sa() -> gn([[?v1_2(sysServices,sysORLastChange), 0]]), - ?line expect(1, [{[fname,0], any}]). + ?line ?expect1([{[fname,0], any}]). %% Req. system group, Klas1, OLD-SNMPEA-MIB do_mul_get() -> Key1c3 = [intCommunityEntry,[3],get(mip),is("public")], Key1c4 = [intCommunityEntry,[4],get(mip),is("public")], s([{[fname,0], s, "test set"}]), - ?line expect(3, [{[fname,0], "test set"}]), - g([[sysDescr,0], Key1c4, [fname,0],Key1c3, - [sysName,0]]), - ?line expect(1, [{[sysDescr,0], "Erlang SNMP agent"}, - {Key1c4, 2}, - {[fname,0], "test set"}, - {Key1c3, 2}, - {[sysName,0], "test"}]), + ?line ?expect1([{[fname,0], "test set"}]), + g([[sysDescr,0], Key1c4, [fname,0],Key1c3,[sysName,0]]), + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {Key1c4, 2}, + {[fname,0], "test set"}, + {Key1c3, 2}, + {[sysName,0], "test"}]), g([[1,3,7,1], Key1c4, [sysDescr,0], [1,3,7,2], Key1c3, [sysDescr,0]]), - ?line ?v1_2(expect(2, noSuchName, [1,4], any), - expect(2, [{[1,3,7,1], noSuchObject}, - {Key1c4, 2}, - {[sysDescr,0], "Erlang SNMP agent"}, - {[1,3,7,2], noSuchObject}, - {Key1c3, 2}, - {[sysDescr,0], "Erlang SNMP agent"}])). + ?line ?v1_2(?expect3(noSuchName, [1,4], any), + ?expect1([{[1,3,7,1], noSuchObject}, + {Key1c4, 2}, + {[sysDescr,0], "Erlang SNMP agent"}, + {[1,3,7,2], noSuchObject}, + {Key1c3, 2}, + {[sysDescr,0], "Erlang SNMP agent"}])). %% Req. v1, system group, Klas1, OLD-SNMPEA-MIB, *ej* Klas3. do_mul_get_err() -> Key1c3 = [intCommunityEntry,[3],get(mip),is("public")], Key1c4 = [intCommunityEntry,[4],get(mip),is("public")], s([{[fname,0], s, "test set"}]), - ?line expect(3, [{[fname,0], "test set"}]), + ?line ?expect1([{[fname,0], "test set"}]), g([[sysDescr,0],Key1c4,[fname,0], Key1c3, [sysName,2]]), - ?line ?v1_2(expect(1, noSuchName, 5, any), - expect(1, [{[sysDescr,0], "Erlang SNMP agent"}, - {Key1c4, 2}, - {[fname,0], "test set"}, - {Key1c3, 2}, - {[sysName,2], noSuchInstance}])), + ?line ?v1_2(?expect3(noSuchName, 5, any), + ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {Key1c4, 2}, + {[fname,0], "test set"}, + {Key1c3, 2}, + {[sysName,2], noSuchInstance}])), g([[sysDescr,0],Key1c4,[fname3,0], Key1c3, [sysName,1]]), - ?line ?v1_2(expect(1, noSuchName, [3,5], any), - expect(1, [{[sysDescr,0], "Erlang SNMP agent"}, - {Key1c4, 2}, - {[fname3,0], noSuchObject}, - {Key1c3, 2}, - {[sysName,1], noSuchInstance}])). + ?line ?v1_2(?expect3(noSuchName, [3,5], any), + ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {Key1c4, 2}, + {[fname3,0], noSuchObject}, + {Key1c3, 2}, + {[sysName,1], noSuchInstance}])). %% Req. system group, Klas1, OLD-SNMPEA-MIB @@ -3476,11 +3491,11 @@ do_mul_next() -> Key1c3 = [intCommunityEntry,[3],get(mip),is("public")], Key1c4 = [intCommunityEntry,[4],get(mip),is("public")], s([{[fname,0], s, "test set"}]), - ?line expect(3, [{[fname,0], "test set"}]), + ?line ?expect1([{[fname,0], "test set"}]), gn([[sysDescr], Key1c4s, [fname],Key1c3s,[sysName]]), - ?line expect(1, [{[sysDescr,0], "Erlang SNMP agent"}, - {Key1c4, 2}, {[fname,0], "test set"}, - {Key1c3, 2}, {[sysName,0], "test"}]). + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {Key1c4, 2}, {[fname,0], "test set"}, + {Key1c3, 2}, {[sysName,0], "test"}]). %% Req. system group, Klas1, OLD-SNMPEA-MIB do_mul_next_err() -> @@ -3489,17 +3504,17 @@ do_mul_next_err() -> Key1c3 = [intCommunityEntry,[3],get(mip),is("public")], Key1c4 = [intCommunityEntry,[4],get(mip),is("public")], s([{[fname,0], s, "test set"}]), - ?line expect(3, [{[fname,0], "test set"}]), + ?line ?expect1([{[fname,0], "test set"}]), gn([[sysDescr], Key1c4s, [1,3,6,999], [fname],[1,3,90], Key1c3s,[sysName]]), - ?line ?v1_2(expect(1, noSuchName, [3,5], any), - expect(1, [{[sysDescr,0], "Erlang SNMP agent"}, - {Key1c4, 2}, - {[1,3,6,999], endOfMibView}, - {[fname,0], "test set"}, - {[1,3,90], endOfMibView}, - {Key1c3, 2}, - {[sysName,0], "test"}])). - + ?line ?v1_2(?expect3(noSuchName, [3,5], any), + ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {Key1c4, 2}, + {[1,3,6,999], endOfMibView}, + {[fname,0], "test set"}, + {[1,3,90], endOfMibView}, + {Key1c3, 2}, + {[sysName,0], "test"}])). + %% Req. system group, Klas1, OLD-SNMPEA-MIB do_mul_set() -> @@ -3513,24 +3528,24 @@ do_mul_set() -> {NewKeyc5, ?createAndGo}, {NewKeyc4, 2}, {[friendsEntry, [3, 3]], ?createAndGo}]), - ?line expect(1, [{[friendsEntry, [2, 3]], "kompis3"}, - {NewKeyc3, 2}, - {[sysLocation,0], "new_value"}, - {NewKeyc5, ?createAndGo}, - {NewKeyc4, 2}, - {[friendsEntry, [3, 3]], ?createAndGo}]), + ?line ?expect1([{[friendsEntry, [2, 3]], "kompis3"}, + {NewKeyc3, 2}, + {[sysLocation,0], "new_value"}, + {NewKeyc5, ?createAndGo}, + {NewKeyc4, 2}, + {[friendsEntry, [3, 3]], ?createAndGo}]), g([[friendsEntry, [2, 3]], - [sysLocation,0], - [friendsEntry, [3, 3]]]), - ?line expect(2, [{[friendsEntry, [2, 3]], "kompis3"}, - {[sysLocation,0], "new_value"}, - {[friendsEntry, [3, 3]], ?active}]), + [sysLocation,0], + [friendsEntry, [3, 3]]]), + ?line ?expect1([{[friendsEntry, [2, 3]], "kompis3"}, + {[sysLocation,0], "new_value"}, + {[friendsEntry, [3, 3]], ?active}]), g([NewKeyc4]), - ?line expect(3, [{NewKeyc4, 2}]), + ?line ?expect1([{NewKeyc4, 2}]), s([{[friendsEntry, [3, 3]], ?destroy}, {NewKeyc5, ?destroy}]), - ?line expect(4, [{[friendsEntry, [3, 3]], ?destroy}, - {NewKeyc5, ?destroy}]). + ?line ?expect1([{[friendsEntry, [3, 3]], ?destroy}, + {NewKeyc5, ?destroy}]). %% Req. system group, Klas1, OLD-SNMPEA-MIB do_mul_set_err() -> @@ -3544,53 +3559,48 @@ do_mul_set_err() -> {NewKeyc5, ?createAndGo}, {NewKeyc4, 2}, {[friendsEntry, [3, 3]], ?createAndGo}]), - ?line expect(1, ?v1_2(noSuchName, notWritable), 3, any), + ?line ?expect3(?v1_2(noSuchName, notWritable), 3, any), g([[friendsEntry, [2, 3]]]), - ?line ?v1_2(expect(2, noSuchName, 1, any), - expect(2, [{[friendsEntry, [2,3]], noSuchInstance}])), + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{[friendsEntry, [2,3]], noSuchInstance}])), g([NewKeyc4]), - ?line ?v1_2(expect(3, noSuchName, 1, any), - expect(3, [{NewKeyc4, noSuchInstance}])). + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{NewKeyc4, noSuchInstance}])). %% Req. SA-MIB sa_mib() -> g([[sa, [2,0]]]), - ?line expect(sa_mib_1, [{[sa, [2,0]], 3}]), + ?line ?expect1([{[sa, [2,0]], 3}]), s([{[sa, [1,0]], s, "sa_test"}]), - ?line expect(sa_mib_2, [{[sa, [1,0]], "sa_test"}]), + ?line ?expect1([{[sa, [1,0]], "sa_test"}]), ok. ma_trap1(MA) -> ok = snmpa:send_trap(MA, testTrap2, "standard trap"), - ?line expect(ma_trap1_1, - trap, [system], 6, 1, [{[system, [4,0]], - "{mbj,eklas}@erlang.ericsson.se"}]), + ?line ?expect5(trap, [system], 6, 1, [{[system, [4,0]], + "{mbj,eklas}@erlang.ericsson.se"}]), ok = snmpa:send_trap(MA, testTrap1, "standard trap"), - ?line expect(ma_trap1_2, - trap, [1,2,3] , 1, 0, [{[system, [4,0]], - "{mbj,eklas}@erlang.ericsson.se"}]), + ?line ?expect5(trap, [1,2,3] , 1, 0, [{[system, [4,0]], + "{mbj,eklas}@erlang.ericsson.se"}]), ok. ma_trap2(MA) -> snmpa:send_trap(MA,testTrap2,"standard trap",[{sysContact,"pelle"}]), - ?line expect(ma_trap2_3, - trap, [system], 6, 1, [{[system, [4,0]], "pelle"}]), + ?line ?expect5(trap, [system], 6, 1, [{[system, [4,0]], "pelle"}]), ok. ma_v2_2_v1_trap(MA) -> snmpa:send_trap(MA,testTrapv22,"standard trap",[{sysContact,"pelle"}]), - ?line expect(ma_v2_2_v1_trap_3, - trap, [system], 6, 1, [{[system, [4,0]], "pelle"}]), + ?line ?expect5(trap, [system], 6, 1, [{[system, [4,0]], "pelle"}]), ok. ma_v2_2_v1_trap2(MA) -> snmpa:send_trap(MA,linkUp,"standard trap",[{ifIndex, [1], 1}, {ifAdminStatus, [1], 1}, {ifOperStatus, [1], 2}]), - ?line expect(ma_v2_2_v1_trap2_3, - trap, [1,2,3], 3, 0, [{[ifIndex, 1], 1}, - {[ifAdminStatus, 1], 1}, - {[ifOperStatus, 1], 2}]), + ?line ?expect5(trap, [1,2,3], 3, 0, [{[ifIndex, 1], 1}, + {[ifAdminStatus, 1], 1}, + {[ifOperStatus, 1], 2}]), ok. sa_trap1(SA) -> @@ -3608,47 +3618,44 @@ sa_trap1(SA) -> %% io:format("sa_trap1 -> SA trap send: " %% "~n TSRes: ~p" %% "~n", [TSRes]), - ?line expect(sa_trap1_4, - trap, [ericsson], 6, 1, [{[system, [4,0]], - "{mbj,eklas}@erlang.ericsson.se"}, - {[sa, [1,0]], "sa_test"}]), + ?line ?expect5(trap, [ericsson], 6, 1, [{[system, [4,0]], + "{mbj,eklas}@erlang.ericsson.se"}, + {[sa, [1,0]], "sa_test"}]), snmpa:verbosity(SA, {subagents, silence}), ok. sa_trap2(SA) -> snmpa:send_trap(SA, saTrap, "standard trap",[{sysContact,"pelle"}]), - ?line expect(sa_trap2_5, - trap, [ericsson], 6, 1, [{[system, [4,0]], "pelle"}, - {[sa, [1,0]], "sa_test"}]), + ?line ?expect5(trap, [ericsson], 6, 1, [{[system, [4,0]], "pelle"}, + {[sa, [1,0]], "sa_test"}]), ok. sa_trap3(SA) -> snmpa:send_trap(SA, saTrap2, "standard trap", [{intViewSubtree, [4], [1,2,3,4]}]), - ?line expect(sa_trap3_6, - trap, [ericsson], 6, 2, [{[system, [4,0]], - "{mbj,eklas}@erlang.ericsson.se"}, - {[sa, [1,0]], "sa_test"}, - {[intViewSubtree,4],[1,2,3,4]}]), + ?line ?expect5(trap, [ericsson], 6, 2, [{[system, [4,0]], + "{mbj,eklas}@erlang.ericsson.se"}, + {[sa, [1,0]], "sa_test"}, + {[intViewSubtree,4],[1,2,3,4]}]), ok. ma_v2_trap1(MA) -> ?DBG("ma_v2_traps -> entry with MA = ~p => " "send standard trap: testTrapv22",[MA]), snmpa:send_trap(MA, testTrapv22, "standard trap"), - ?line expect(1, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]), ?DBG("ma_v2_traps -> send standard trap: testTrapv21",[]), snmpa:send_trap(MA, testTrapv21, "standard trap"), - ?line expect(2, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?snmp ++ [1]}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?snmp ++ [1]}]), ok. ma_v2_trap2(MA) -> snmpa:send_trap(MA,testTrapv22,"standard trap",[{sysContact,"pelle"}]), - ?line expect(3, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}, - {[system, [4,0]], "pelle"}]). + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}, + {[system, [4,0]], "pelle"}]). %% Note: This test case takes a while... actually a couple of minutes. ma_v2_inform1(MA) -> @@ -3657,11 +3664,10 @@ ma_v2_inform1(MA) -> "~n send notification: testTrapv22", [MA]), CmdExpectInform = - fun(No, Response) -> - expect(No, - {inform, Response}, - [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]) + fun(_No, Response) -> + ?expect2({inform, Response}, + [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]) end, CmdExp = @@ -3807,11 +3813,10 @@ ma_v2_inform2(MA) -> "~n send notification: testTrapv22", [MA]), CmdExpectInform = - fun(No, Response) -> - expect(No, - {inform, Response}, - [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]) + fun(_No, Response) -> + ?expect2({inform, Response}, + [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]) end, CmdExp = @@ -3899,17 +3904,17 @@ ma_v2_inform3(MA) -> CmdExpectInform = fun(No, Response) -> - expect(No, - {inform, Response}, - [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}]) + ?DBG("CmdExpectInform -> ~p: ~n~p", [No, Response]), + ?expect2({inform, Response}, + [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}]) end, CmdExp = fun(ok) -> ok; ({ok, Val}) -> - ?DBG("ma_v2_inform3 -> [cmd2] Val: ~p", [Val]), + ?DBG("CmdExp -> Val: ~p", [Val]), ok; ({error, Id, Extra}) -> {error, {unexpected, Id, Extra}}; @@ -3973,10 +3978,10 @@ ma_v2_inform3(MA) -> Commands = [ - {15, "Send notification [tag31]", Cmd15}, - {16, "Expect notification message [tag31]", Cmd16}, - {17, "Expect targets message [tag31]", Cmd17}, - {18, "Expect notification (no) response message [tag31]", Cmd18} + {15, "Send notification [" ++ atom_to_list(Tag15) ++ "]", Cmd15}, + {16, "Expect notification message [" ++ atom_to_list(Tag15) ++ "]", Cmd16}, + {17, "Expect targets message [" ++ atom_to_list(Tag15) ++ "]", Cmd17}, + {18, "Expect notification (no) response message [" ++ atom_to_list(Tag15) ++ "]", Cmd18} ], command_handler(Commands). @@ -4035,40 +4040,40 @@ command_handler([{No, Desc, Cmd}|Rest]) -> ma_v1_2_v2_trap(MA) -> snmpa:send_trap(MA,linkDown,"standard trap",[{ifIndex, [1], 1}]), - ?line expect(2, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?snmpTraps ++ [3]}, - {[ifIndex, 1], 1}, - {[snmpTrapEnterprise, 0], [1,2,3]}]). + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?snmpTraps ++ [3]}, + {[ifIndex, 1], 1}, + {[snmpTrapEnterprise, 0], [1,2,3]}]). ma_v1_2_v2_trap2(MA) -> snmpa:send_trap(MA,testTrap2,"standard trap",[{sysContact,"pelle"}]), - ?line expect(3, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?system ++ [0,1]}, - {[system, [4,0]], "pelle"}, - {[snmpTrapEnterprise, 0], ?system}]). + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?system ++ [0,1]}, + {[system, [4,0]], "pelle"}, + {[snmpTrapEnterprise, 0], ?system}]). sa_v1_2_v2_trap1(SA) -> snmpa:verbosity(SA, {subagents, trace}), snmpa:send_trap(SA, saTrap, "standard trap"), - ?line expect(trap1_4, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?ericsson ++ [0, 1]}, - {[system, [4,0]], - "{mbj,eklas}@erlang.ericsson.se"}, - {[sa, [1,0]], "sa_test"}, - {[snmpTrapEnterprise, 0], ?ericsson}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?ericsson ++ [0, 1]}, + {[system, [4,0]], + "{mbj,eklas}@erlang.ericsson.se"}, + {[sa, [1,0]], "sa_test"}, + {[snmpTrapEnterprise, 0], ?ericsson}]), snmpa:verbosity(SA, {subagents, silence}), ok. sa_v1_2_v2_trap2(SA) -> snmpa:verbosity(SA, {subagents, trace}), snmpa:send_trap(SA, saTrap, "standard trap",[{sysContact,"pelle"}]), - ?line expect(trap2_4, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?ericsson ++ [0, 1]}, - {[system, [4,0]], "pelle"}, - {[sa, [1,0]], "sa_test"}, - {[snmpTrapEnterprise, 0], ?ericsson}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?ericsson ++ [0, 1]}, + {[system, [4,0]], "pelle"}, + {[sa, [1,0]], "sa_test"}, + {[snmpTrapEnterprise, 0], ?ericsson}]), snmpa:verbosity(SA, {subagents, silence}), ok. @@ -4077,13 +4082,13 @@ sa_v1_2_v2_trap3(SA) -> snmpa:verbosity(SA, {subagents, trace}), snmpa:send_trap(SA, saTrap2, "standard trap", [{intViewSubtree, [4], [1,2,3,4]}]), - ?line expect(trap3_4, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], ?ericsson ++ [0, 2]}, - {[system, [4,0]], - "{mbj,eklas}@erlang.ericsson.se"}, - {[sa, [1,0]], "sa_test"}, - {[intViewSubtree,4],[1,2,3,4]}, - {[snmpTrapEnterprise, 0], ?ericsson}]), + ?line ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], ?ericsson ++ [0, 2]}, + {[system, [4,0]], + "{mbj,eklas}@erlang.ericsson.se"}, + {[sa, [1,0]], "sa_test"}, + {[intViewSubtree,4],[1,2,3,4]}, + {[snmpTrapEnterprise, 0], ?ericsson}]), snmpa:verbosity(SA, {subagents, silence}), ok. @@ -4097,15 +4102,15 @@ sa_errs_bad_value() -> {[sa, [2,0]], 5}, % badValue (i is_set_ok) {NewKeyc5, ?createAndGo}, {NewKeyc4, 2}]), - ?line expect(1, badValue, 2, any), + ?line ?expect3(badValue, 2, any), s([{NewKeyc3, 2}, {[sa, [2,0]], 6}, % wrongValue (i is_set_ok) {NewKeyc5, ?createAndGo}, {NewKeyc4, 2}]), - ?line expect(1, ?v1_2(badValue, wrongValue), 2, any), + ?line ?expect3(?v1_2(badValue, wrongValue), 2, any), g([NewKeyc4]), - ?line ?v1_2(expect(2, noSuchName, 1, any), - expect(2, [{NewKeyc4, noSuchInstance}])). + ?line ?v1_2(?expect3(noSuchName, 1, any), + ?expect1([{NewKeyc4, noSuchInstance}])). %% Req. SA-MIB, OLD-SNMPEA-MIB sa_errs_gen_err() -> @@ -4114,23 +4119,23 @@ sa_errs_gen_err() -> NewKeyc5 = [intCommunityEntry,[5],get(mip),is("test")], s([{NewKeyc3, 2},{NewKeyc4, 2}, {NewKeyc5, ?createAndGo}, {[sa, [3,0]], 5}]), - ?line expect(1, genErr, 4, any), + ?line ?expect3(genErr, 4, any), % The row might have been added; we don't know. % (as a matter of fact we do - it is added, because the agent % first sets its own vars, and then th SAs. Lets destroy it. s([{NewKeyc5, ?destroy}]), - ?line expect(2, [{NewKeyc5, ?destroy}]). + ?line ?expect1([{NewKeyc5, ?destroy}]). %% Req. SA-MIB, OLD-SNMPEA-MIB sa_too_big() -> g([[sa, [4,0]]]), - ?line expect(1, tooBig). + ?line ?expect1(tooBig). %% Req. Klas1, system group, snmp group (v1/v2) next_across_sa() -> gn([[sysDescr],[klas1,5]]), - ?line expect(1, [{[sysDescr,0], "Erlang SNMP agent"}, - {[snmpInPkts, 0], any}]). + ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, + {[snmpInPkts, 0], any}]). %% snmp_test_mgr:s([{[fStatus3, 1], 4}, {[fname3,0], "ok"}]). -> noError %% snmp_test_mgr:s([{[fStatus3, 1], 4}, {[fname3,0], "hoj"}]). -> {badValue, 2} @@ -4141,40 +4146,40 @@ next_across_sa() -> %% Req. Klas3, Klas4 undo_test() -> s([{[fStatus3, 1], 4}, {[fname3,0], "ok"}]), - ?line expect(1, [{[fStatus3, 1], 4}, {[fname3,0], "ok"}]), + ?line ?expect1([{[fStatus3, 1], 4}, {[fname3,0], "ok"}]), s([{[fStatus3, 1], 4}, {[fname3,0], "hoj"}]), - ?line expect(2, ?v1_2(badValue, inconsistentValue), 2, any), + ?line ?expect3(?v1_2(badValue, inconsistentValue), 2, any), s([{[fStatus3, 3], 4}, {[fname3,0], "hoj"}]), - ?line expect(3, ?v1_2(genErr, undoFailed), 1, any), + ?line ?expect3(?v1_2(genErr, undoFailed), 1, any), s([{[fStatus3, 4], 4}, {[fname3,0], "ok"}]), - ?line expect(4, ?v1_2(genErr, commitFailed), 1, any), + ?line ?expect3(?v1_2(genErr, commitFailed), 1, any), % unfortunatly we don't know if we'll get undoFailed or commitFailed. % it depends on which order the agent traverses the varbind list. % s([{[fStatus3, 4], 4}, {[fname3,0], "ufail"}]), % ?line expect(5, ?v1_2(genErr, undoFailed), 1, any), s([{[fStatus3, 1], 4}, {[fname3,0], "xfail"}]), - ?line expect(6, genErr, 2, any). + ?line ?expect3(genErr, 2, any). %% Req. Klas3, Klas4 bad_return() -> g([[fStatus4,4], [fName4,4]]), - ?line expect(4, genErr, 2, any), + ?line ?expect3(genErr, 2, any), g([[fStatus4,5], [fName4,5]]), - ?line expect(5, genErr, 1, any), + ?line ?expect3(genErr, 1, any), g([[fStatus4,6], [fName4,6]]), - ?line expect(6, genErr, 2, any), + ?line ?expect3(genErr, 2, any), gn([[fStatus4,7], [fName4,7]]), - ?line expect(7, genErr, 2, any), + ?line ?expect3(genErr, 2, any), gn([[fStatus4,8], [fName4,8]]), - ?line expect(8, genErr, 1, any), + ?line ?expect3(genErr, 1, any), gn([[fStatus4,9], [fName4,9]]), - ?line expect(9, genErr, 2, any). + ?line ?expect3(genErr, 2, any). %%%----------------------------------------------------------------- @@ -4246,27 +4251,27 @@ std_mib_init() -> %% disable authentication failure traps. (otherwise w'd get many of %% them - this is also a test to see that it works). s([{[snmpEnableAuthenTraps,0], 2}]), - ?line expect(std_mib_init_1, [{[snmpEnableAuthenTraps, 0], 2}]). + ?line ?expect1([{[snmpEnableAuthenTraps, 0], 2}]). %% Req. SNMP-STANDARD-MIB | SNMPv2-MIB std_mib_finish() -> %% enable again s([{[snmpEnableAuthenTraps,0], 1}]), - ?line expect(std_mib_finish_1, [{[snmpEnableAuthenTraps, 0], 1}]). + ?line ?expect1([{[snmpEnableAuthenTraps, 0], 1}]). %% Req. SNMP-STANDARD-MIB standard_mib_test_finish() -> %% force a authenticationFailure (should result in a trap) std_mib_write(), %% check that we got a trap - ?line expect(standard_mib_test_finish_2, trap, [1,2,3], 4, 0, []). + ?line ?expect5(trap, [1,2,3], 4, 0, []). %% Req. SNMP-STANDARD-MIB | SNMPv2-MIB std_mib_read() -> ?DBG("std_mib_read -> entry", []), g([[sysUpTime,0]]), % try a bad ; msg dropped, no reply ?DBG("std_mib_read -> await timeout (i.e. no reply)", []), - ?line expect(std_mib_read_1, timeout). % make sure we don't get a trap! + ?line ?expect1(timeout). % make sure we don't get a trap! %% Req. SNMP-STANDARD-MIB | SNMPv2-MIB @@ -4382,13 +4387,13 @@ snmpv2_mib_test_finish() -> %% check that we got a trap ?DBG("ma_v2_inform -> await trap",[]), - ?line expect(2, v2trap, [{[sysUpTime,0], any}, - {[snmpTrapOID,0], ?authenticationFailure}]), + ?line ?expect2(v2trap, [{[sysUpTime,0], any}, + {[snmpTrapOID,0], ?authenticationFailure}]), %% and the the inform ?DBG("ma_v2_inform -> await inform",[]), - ?line expect(2, {inform,true}, [{[sysUpTime,0], any}, - {[snmpTrapOID,0],?authenticationFailure}]). + ?line ?expect2({inform,true}, [{[sysUpTime,0], any}, + {[snmpTrapOID,0],?authenticationFailure}]). %% Req. SNMP-STANDARD-MIB | SNMPv2-MIB std_mib_a() -> @@ -4426,12 +4431,12 @@ std_mib_c({InBadCommunityNames, InBadCommunityUses, InASNErrs}) -> snmpv2_mib_a() -> ?line [SetSerial] = get_req(2, [[snmpSetSerialNo,0]]), s([{[snmpSetSerialNo,0], SetSerial}, {[sysLocation, 0], "val2"}]), - ?line expect(snmpv2_mib_a_3, [{[snmpSetSerialNo,0], SetSerial}, - {[sysLocation, 0], "val2"}]), + ?line ?expect1([{[snmpSetSerialNo,0], SetSerial}, + {[sysLocation, 0], "val2"}]), s([{[sysLocation, 0], "val3"}, {[snmpSetSerialNo,0], SetSerial}]), - ?line expect(snmpv2_mib_a_4, inconsistentValue, 2, - [{[sysLocation, 0], "val3"}, - {[snmpSetSerialNo,0], SetSerial}]), + ?line ?expect3(inconsistentValue, 2, + [{[sysLocation, 0], "val3"}, + {[snmpSetSerialNo,0], SetSerial}]), ?line ["val2"] = get_req(5, [[sysLocation,0]]). @@ -4562,7 +4567,7 @@ snmp_mpd_mib_a() -> -define(snmpUnknownPDUHandlers_instance, [1,3,6,1,6,3,11,2,1,3,0]). snmp_mpd_mib_b() -> g([[sysUpTime,0]]), - ?line expect(1, report, [{?snmpUnknownPDUHandlers_instance, any}]). + ?line ?expect2(report, [{?snmpUnknownPDUHandlers_instance, any}]). snmp_mpd_mib_c(UnknownPDUHs) -> @@ -4752,50 +4757,51 @@ snmp_view_based_acm_mib() -> do_set(Row) -> s(Row), - expect(do_set_1, Row). + ?expect1(Row). add_row(RowStatus) -> s([{RowStatus, ?createAndGo}]), - expect(add_row_1, [{RowStatus, ?createAndGo}]). + ?expect1([{RowStatus, ?createAndGo}]). del_row(RowStatus) -> s([{RowStatus, ?destroy}]), - expect(del_row_1, [{RowStatus, ?destroy}]). + ?expect1([{RowStatus, ?destroy}]). use_no_rights() -> g([[xDescr,0]]), - ?v1_2_3(expect(use_no_rights_11, noSuchName, 1, any), - expect(use_no_rights_12, [{[xDescr,0], noSuchObject}]), - expect(use_no_rights_13, authorizationError, 1, any)), + ?v1_2_3(?expect3(noSuchName, 1, any), + ?expect1([{[xDescr,0], noSuchObject}]), + ?expect3(authorizationError, 1, any)), g([[xDescr2,0]]), - ?v1_2_3(expect(use_no_rights_21, noSuchName, 1, any), - expect(use_no_rights_22, [{[xDescr2,0], noSuchObject}]), - expect(use_no_rights_23, authorizationError, 1, any)), + ?v1_2_3(?expect3(noSuchName, 1, any), + ?expect1([{[xDescr2,0], noSuchObject}]), + ?expect3(authorizationError, 1, any)), gn([[xDescr]]), - ?v1_2_3(expect(use_no_rights_31, noSuchName, 1, any), - expect(use_no_rights_32, [{[xDescr], endOfMibView}]), - expect(use_no_rights_33, authorizationError, 1, any)), + ?v1_2_3(?expect3(noSuchName, 1, any), + ?expect1([{[xDescr], endOfMibView}]), + ?expect3(authorizationError, 1, any)), s([{[xDescr,0], "tryit"}]), - ?v1_2_3(expect(use_no_rights_41, noSuchName, 1, any), - expect(use_no_rights_42, noAccess, 1, any), - expect(use_no_rights_43, authorizationError, 1, any)). + ?v1_2_3(?expect3(noSuchName, 1, any), + ?expect3(noAccess, 1, any), + ?expect3(authorizationError, 1, any)). use_rights() -> g([[xDescr,0]]), - expect(use_rights_1, [{[xDescr,0], any}]), + ?expect1([{[xDescr,0], any}]), g([[xDescr2,0]]), - expect(use_rights_2, [{[xDescr2,0], any}]), + ?expect1([{[xDescr2,0], any}]), s([{[xDescr,0], "tryit"}]), - expect(use_rights_3, noError, 0, any), + ?expect3(noError, 0, any), g([[xDescr,0]]), - expect(use_rights_4, [{[xDescr,0], "tryit"}]). + ?expect1([{[xDescr,0], "tryit"}]). mk_ln(X) -> [length(X) | X]. + %%----------------------------------------------------------------- %% o add/delete users and try them %% o test all secLevels @@ -4895,7 +4901,7 @@ usm_add_user1() -> Vbs1 = [{[usmUserCloneFrom, NewRowIndex], RowPointer}, {[usmUserStatus, NewRowIndex], ?createAndGo}], ?line s(Vbs1), - ?line expect(1, Vbs1), + ?line ?expect1(Vbs1), ok. usm_use_user() -> @@ -4914,7 +4920,7 @@ usm_key_change1(ShaKey, DesKey) -> Vbs1 = [{[usmUserAuthKeyChange, NewRowIndex], ShaKeyChange}, {[usmUserPrivKeyChange, NewRowIndex], DesKeyChange}], s(Vbs1), - ?line expect(1, Vbs1). + ?line ?expect1(Vbs1). %% Change own private keys usm_key_change2(OldShaKey, OldDesKey, ShaKey, DesKey) -> @@ -4928,7 +4934,7 @@ usm_key_change2(OldShaKey, OldDesKey, ShaKey, DesKey) -> Vbs1 = [{[usmUserOwnAuthKeyChange, NewRowIndex], ShaKeyChange}, {[usmUserOwnPrivKeyChange, NewRowIndex], DesKeyChange}], s(Vbs1), - ?line expect(1, Vbs1). + ?line ?expect1(Vbs1). %% Change other's public keys usm_key_change3(OldShaKey, OldDesKey, ShaKey, DesKey) -> @@ -4941,16 +4947,16 @@ usm_key_change3(OldShaKey, OldDesKey, ShaKey, DesKey) -> DesKey), Vbs1 = [{[usmUserOwnAuthKeyChange, NewRowIndex], ShaKeyChange}], s(Vbs1), - ?line expect(1, noAccess, 1, any), + ?line ?expect3(noAccess, 1, any), Vbs2 = [{[usmUserOwnPrivKeyChange, NewRowIndex], DesKeyChange}], s(Vbs2), - ?line expect(2, noAccess, 1, any), + ?line ?expect3(noAccess, 1, any), Vbs3 = [{[usmUserAuthKeyChange, NewRowIndex], ShaKeyChange}, {[usmUserPrivKeyChange, NewRowIndex], DesKeyChange}], s(Vbs3), - ?line expect(1, Vbs3). + ?line ?expect1(Vbs3). usm_read() -> NewRowIndex = [11,"agentEngine", 7, "newUser"], @@ -4960,13 +4966,12 @@ usm_read() -> [usmUserOwnAuthKeyChange, NewRowIndex], [usmUserPrivKeyChange, NewRowIndex], [usmUserOwnPrivKeyChange, NewRowIndex]]), - ?line expect(1, - [{[usmUserSecurityName, NewRowIndex], "newUser"}, - {[usmUserCloneFrom, NewRowIndex], [0,0]}, - {[usmUserAuthKeyChange, NewRowIndex], ""}, - {[usmUserOwnAuthKeyChange, NewRowIndex], ""}, - {[usmUserPrivKeyChange, NewRowIndex], ""}, - {[usmUserOwnPrivKeyChange, NewRowIndex], ""}]), + ?line ?expect1([{[usmUserSecurityName, NewRowIndex], "newUser"}, + {[usmUserCloneFrom, NewRowIndex], [0,0]}, + {[usmUserAuthKeyChange, NewRowIndex], ""}, + {[usmUserOwnAuthKeyChange, NewRowIndex], ""}, + {[usmUserPrivKeyChange, NewRowIndex], ""}, + {[usmUserOwnPrivKeyChange, NewRowIndex], ""}]), ok. @@ -4975,7 +4980,7 @@ usm_del_user() -> NewRowIndex = [11,"agentEngine", 7, "newUser"], Vbs1 = [{[usmUserStatus, NewRowIndex], ?destroy}], ?line s(Vbs1), - ?line expect(1, Vbs1), + ?line ?expect1(Vbs1), ok. -define(usmUserCloneFrom, [1,3,6,1,6,3,15,1,2,2,1,4]). @@ -4996,32 +5001,31 @@ usm_bad() -> Vbs1 = [{[usmUserCloneFrom, NewRowIndex], RowPointer1}, {[usmUserStatus, NewRowIndex], ?createAndGo}], ?line s(Vbs1), - ?line expect(1, inconsistentName, 1, any), + ?line ?expect3(inconsistentName, 1, any), RowPointer2 = ?usmUserCloneFrom ++ [11|"agentEngine"] ++ [7|"privDES"], Vbs2 = [{[usmUserCloneFrom, NewRowIndex], RowPointer2}, {[usmUserStatus, NewRowIndex], ?createAndGo}], ?line s(Vbs2), - ?line expect(2, wrongValue, 1, any), + ?line ?expect3(wrongValue, 1, any), RowPointer3 = ?usmUserSecurityName ++ [11|"agentEngine"] ++ [7|"privDES"], Vbs3 = [{[usmUserCloneFrom, NewRowIndex], RowPointer3}, {[usmUserStatus, NewRowIndex], ?createAndGo}], ?line s(Vbs3), - ?line expect(3, Vbs3), + ?line ?expect1(Vbs3), ?line s([{[usmUserAuthProtocol, NewRowIndex], ?usmNoAuthProtocol}]), - ?line expect(4, inconsistentValue, 1, any), + ?line ?expect3(inconsistentValue, 1, any), ?line s([{[usmUserAuthProtocol, NewRowIndex], ?usmHMACMD5AuthProtocol}]), - ?line expect(5, inconsistentValue, 1, any), + ?line ?expect3(inconsistentValue, 1, any), ?line s([{[usmUserAuthProtocol, NewRowIndex], ?usmDESPrivProtocol}]), - ?line expect(6, wrongValue, 1, any), + ?line ?expect3(wrongValue, 1, any), ?line s([{[usmUserPrivProtocol, NewRowIndex], ?usmHMACSHAAuthProtocol}]), - ?line expect(7, wrongValue, 1, any), + ?line ?expect3(wrongValue, 1, any), Vbs4 = [{[usmUserStatus, NewRowIndex], ?destroy}], ?line s(Vbs4), - ?line expect(1, Vbs4), - + ?line ?expect1(Vbs4), ok. @@ -5340,15 +5344,15 @@ otp_1128() -> NewKeyc5 = [intCommunityStatus,get(mip),is("test")], s([{NewKeyc5, ?createAndWait}, {NewKeyc4, 2}]), - ?line expect(28, [{NewKeyc5, ?createAndWait}, {NewKeyc4, 2}]), + ?line ?expect1([{NewKeyc5, ?createAndWait}, {NewKeyc4, 2}]), g([NewKeyc5]), - ?line expect(29, [{NewKeyc5, ?notReady}]), + ?line ?expect1([{NewKeyc5, ?notReady}]), s([{NewKeyc5, ?active}, {NewKeyc3, 2}]), - ?line expect(30, [{NewKeyc5, ?active}, {NewKeyc3, 2}]), + ?line ?expect1([{NewKeyc5, ?active}, {NewKeyc3, 2}]), g([NewKeyc5]), - ?line expect(31, [{NewKeyc5, ?active}]), + ?line ?expect1([{NewKeyc5, ?active}]), s([{NewKeyc5, ?destroy}]), - ?line expect(32, [{NewKeyc5, ?destroy}]). + ?line ?expect1([{NewKeyc5, ?destroy}]). %%----------------------------------------------------------------- %% Ticket: OTP-1129, OTP-1169 @@ -5371,6 +5375,7 @@ otp_1129_i(MaNode) -> false = rpc:call(MaNode, snmp, int_to_enum, [iso, 1]), false = rpc:call(MaNode, snmp, int_to_enum, [isox, 1]). + %%----------------------------------------------------------------- %% Ticket: OTP-1131 %% Slogan: Agent crashes / erlang node halts if RowIndex in a @@ -5430,7 +5435,7 @@ otp_1131() -> io:format("Testing bug reported in ticket OTP-1131...~n"), s([{[friendsEntry, [2, 3, 1]], s, "kompis3"}, {[friendsEntry, [3, 3, 1]], i, ?createAndGo}]), - ?line expect(1, ?v1_2(noSuchName, noCreation), 2, any). + ?line ?expect3(?v1_2(noSuchName, noCreation), 2, any). %%----------------------------------------------------------------- @@ -5451,7 +5456,7 @@ otp_1162_3(X) -> ?P(otp_1162_3), otp_1162(X). otp_1162() -> s([{[sa, [2,0]], 6}]), % wrongValue (i is_set_ok) - ?line expect(1, ?v1_2(badValue, wrongValue), 1, any). + ?line ?expect3(?v1_2(badValue, wrongValue), 1, any). %%----------------------------------------------------------------- @@ -5475,9 +5480,9 @@ otp_1222_3(X) -> ?P(otp_1222_3), otp_1222(X). otp_1222() -> io:format("Testing bug reported in ticket OTP-1222...~n"), s([{[fStatus4,1], 4}, {[fName4,1], 1}]), - ?line expect(1, genErr, 0, any), + ?line ?expect3(genErr, 0, any), s([{[fStatus4,2], 4}, {[fName4,2], 1}]), - ?line expect(2, genErr, 0, any). + ?line ?expect3(genErr, 0, any). %%----------------------------------------------------------------- %% Ticket: OTP-1298 @@ -5499,7 +5504,7 @@ otp_1298_3(X) -> ?P(otp_1298_3), otp_1298(X). otp_1298() -> io:format("Testing bug reported in ticket OTP-1298...~n"), s([{[fint,0], -1}]), - ?line expect(1298, [{[fint,0], -1}]). + ?line ?expect1([{[fint,0], -1}]). %%----------------------------------------------------------------- @@ -5522,7 +5527,7 @@ otp_1331_3(X) -> ?P(otp_1331_3), otp_1331(X). otp_1331() -> NewKeyc5 = [intCommunityStatus,[127,32,0,0],is("test")], s([{NewKeyc5, ?destroy}]), - ?line expect(1, [{NewKeyc5, ?destroy}]). + ?line ?expect1([{NewKeyc5, ?destroy}]). %%----------------------------------------------------------------- @@ -5544,9 +5549,9 @@ otp_1338_3(X) -> ?P(otp_1338_3), otp_1338(X). otp_1338() -> s([{[kStatus2, 7], i, ?createAndGo}]), - ?line expect(1, [{[kStatus2, 7], ?createAndGo}]), + ?line ?expect1([{[kStatus2, 7], ?createAndGo}]), g([[kName2, 7]]), - ?line expect(2, [{[kName2, 7], "JJJ"}]). + ?line ?expect1([{[kName2, 7], "JJJ"}]). %%----------------------------------------------------------------- %% Ticket: OTP-1342 @@ -5569,7 +5574,7 @@ otp_1342() -> s([{[fIndex5, 1], i, 1}, {[fName5, 1], i, 3}, {[fStatus5, 1], i, ?createAndGo}]), - ?line expect(1, ?v1_2(noSuchName, noCreation), 3, any). + ?line ?expect3(?v1_2(noSuchName, noCreation), 3, any). %%----------------------------------------------------------------- @@ -5681,8 +5686,8 @@ otp_2979_3(X) -> ?P(otp_2979_3), otp_2979(X). otp_2979() -> gn([[sparseDescr], [sparseStatus]]), - ?line expect(1, [{[sparseStr,0], "slut"}, - {[sparseStr,0], "slut"}]). + ?line ?expect1([{[sparseStr,0], "slut"}, + {[sparseStr,0], "slut"}]). %%----------------------------------------------------------------- %% Ticket: OTP-3187 @@ -5941,11 +5946,11 @@ otp_7157_test1(MA) -> ?DBG("await response",[]), %% We don't really care about the values, just the vb order. - ?line ok = expect(1, v2trap, [{[sysUpTime, 0], any}, - {[snmpTrapOID, 0], any}, - {[sysContact, 0], any}, - {[cnt64, 0], any}, - {[sysLocation, 0], any}]), + ?line ok = ?expect2(v2trap, [{[sysUpTime, 0], any}, + {[snmpTrapOID, 0], any}, + {[sysContact, 0], any}, + {[cnt64, 0], any}, + {[sysLocation, 0], any}]), ?DBG("done", []), ok. @@ -6591,10 +6596,10 @@ gn(OidsOrN) -> snmp_test_mgr:gn(OidsOrN). gb(NR, MR, Oids) -> snmp_test_mgr:gb(NR, MR, Oids). s(VAV) -> snmp_test_mgr:s(VAV). -expect(A, B) -> snmp_agent_test_lib:expect(A, B). -expect(A, B, C) -> snmp_agent_test_lib:expect(A, B, C). -expect(A, B, C, D) -> snmp_agent_test_lib:expect(A, B, C, D). -expect(A, B, C, D, E, F) -> snmp_agent_test_lib:expect(A, B, C, D, E, F). +%% expect(A, B) -> snmp_agent_test_lib:expect(A, B). +%% expect(A, B, C) -> snmp_agent_test_lib:expect(A, B, C). +%% expect(A, B, C, D) -> snmp_agent_test_lib:expect(A, B, C, D). +%% expect(A, B, C, D, E, F) -> snmp_agent_test_lib:expect(A, B, C, D, E, F). get_req(Id, Vars) -> snmp_agent_test_lib:get_req(Id, Vars). diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 2c24dd3712..11c05fc1db 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -58,7 +58,7 @@ init_all/1, finish_all/1, init_case/1, try_test/2, try_test/3, try_test/4, - expect/2, expect/3, expect/4, expect/6, + expect/3, expect/4, expect/5, expect/7, regs/0, rpc/3 @@ -842,31 +842,33 @@ agent_info(Sup) -> %% --- +%% The first two arguments are simple to be able to find where in the +%% (test) code this call is made. -expect(Id, A) -> - Fun = fun() -> do_expect(A) end, - expect2(Id, Fun). +expect(Mod, Line, What) -> + Fun = fun() -> do_expect(What) end, + expect2(Mod, Line, Fun). -expect(Id, A, B) -> - Fun = fun() -> do_expect(A, B) end, - expect2(Id, Fun). +expect(Mod, Line, What, ExpVBs) -> + Fun = fun() -> do_expect(What, ExpVBs) end, + expect2(Mod, Line, Fun). -expect(Id, A, B, C) -> - Fun = fun() -> do_expect(A, B, C) end, - expect2(Id, Fun). +expect(Mod, Line, Error, Index, ExpVBS) -> + Fun = fun() -> do_expect(Error, Index, ExpVBS) end, + expect2(Mod, Line, Fun). -expect(Id, A, B, C, D, E) -> - Fun = fun() -> do_expect(A, B, C, D, E) end, - expect2(Id, Fun). +expect(Mod, Line, Type, Enterp, Generic, Specific, ExpVBs) -> + Fun = fun() -> do_expect(Type, Enterp, Generic, Specific, ExpVBs) end, + expect2(Mod, Line, Fun). -expect2(Id, F) -> - io:format("EXPECT for ~w~n", [Id]), +expect2(Mod, Line, F) -> + io:format("EXPECT for ~w:~w~n", [Mod, Line]), case F() of {error, Reason} -> - io:format("EXPECT failed for ~w: ~n~p~n", [Id, Reason]), - throw({error, {expect, Id, Reason}}); + io:format("EXPECT failed at ~w:~w => ~n~p~n", [Mod, Line, Reason]), + throw({error, {expect, Mod, Line, Reason}}); Else -> - io:format("EXPECT result for ~w: ~n~p~n", [Id, Else]), + io:format("EXPECT result for ~w:~w => ~n~p~n", [Mod, Line, Else]), Else end. @@ -918,7 +920,8 @@ do_expect({timeout, To}) -> end; do_expect({Err, To}) - when is_atom(Err) andalso (is_integer(To) orelse (To =:= infinity)) -> + when (is_atom(Err) andalso + ((is_integer(To) andalso To > 0) orelse (To =:= infinity))) -> io:format("EXPECT error ~w within ~w~n", [Err, To]), do_expect({{error, Err}, To}); -- cgit v1.2.3 From 13a16c97e02daf8cf266aa96addaee251e3eda0c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 30 May 2013 16:27:48 +0200 Subject: [snmp/agent] Fixed expect macro --- lib/snmp/test/snmp_agent_test.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index a35077091e..d298f9ac75 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -103,7 +103,7 @@ Type, Ent, Gen, Spec, ExpVBs)). -define(expect6(Type, Ent, Gen, Spec, ExpVBs, To), snmp_agent_test_lib:expect(?MODULE, ?LINE, - Type, Ent, Gen, Spec, ExpVBs)). + Type, Ent, Gen, Spec, ExpVBs, To)). all() -> -- cgit v1.2.3 From 988846bb26e652ad2272fac24a679259059bcf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 13:15:43 +0100 Subject: asn1ct_name: Remove unused functions asn1ct_name:delete/1 is used from asn1ct_gen_ber_bin_v2:add_removed_bytes/1, but that function is not used. --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 14 ---------- lib/asn1/src/asn1ct_name.erl | 51 ---------------------------------- 2 files changed, 65 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index de0adef2b2..7de80f216f 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -26,7 +26,6 @@ -export([pgen/4]). -export([decode_class/1, decode_type/1]). --export([add_removed_bytes/0]). -export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]). -export([gen_encode_prim/4]). -export([gen_dec_prim/7]). @@ -1615,19 +1614,6 @@ decode_type('BMPString') -> 30; decode_type('CHOICE') -> 'CHOICE'; % choice gets the tag from the actual alternative decode_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}). -add_removed_bytes() -> - asn1ct_name:delete(rb), - add_removed_bytes(asn1ct_name:all(rb)). - -add_removed_bytes([H,T1|T]) -> - emit({{var,H},"+"}), - add_removed_bytes([T1|T]); -add_removed_bytes([H|T]) -> - emit({{var,H}}), - add_removed_bytes(T); -add_removed_bytes([]) -> - true. - mkfuncname(WhatKind,DecOrEnc) -> case WhatKind of #'Externaltypereference'{module=Mod,type=EType} -> diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index 3ab6f7b0ed..24b28ade7e 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -22,11 +22,8 @@ %%-compile(export_all). -export([start/0, stop/0, - push/1, - pop/1, curr/1, clear/0, - delete/1, active/1, prev/1, next/1, @@ -56,15 +53,6 @@ name_server_loop({Ref, Parent} = Monitor,Vars) -> {From,{current,Variable}} -> From ! {?MODULE,get_curr(Vars,Variable)}, name_server_loop(Monitor,Vars); - {From,{pop,Variable}} -> - From ! {?MODULE,done}, - name_server_loop(Monitor,pop_var(Vars,Variable)); - {From,{push,Variable}} -> - From ! {?MODULE,done}, - name_server_loop(Monitor,push_var(Vars,Variable)); - {From,{delete,Variable}} -> - From ! {?MODULE,done}, - name_server_loop(Monitor,delete_var(Vars,Variable)); {From,{new,Variable}} -> From ! {?MODULE,done}, name_server_loop(Monitor,new_var(Vars,Variable)); @@ -94,12 +82,9 @@ req(Req) -> exit(name_server_timeout) end. -pop(V) -> req({pop,V}). -push(V) -> req({push,V}). clear() -> stop(), start(). curr(V) -> req({current,V}). new(V) -> req({new,V}). -delete(V) -> req({delete,V}). prev(V) -> case req({prev,V}) of none -> @@ -146,26 +131,6 @@ get_digs([H|T]) -> [] end. -push_var(Vars,Variable) -> - case lists:keysearch(Variable,1,Vars) of - false -> - [{Variable,[0]}|Vars]; - {value,{Variable,[Digit|Drest]}} -> - NewVars = lists:keydelete(Variable,1,Vars), - [{Variable,[Digit,Digit|Drest]}|NewVars] - end. - -pop_var(Vars,Variable) -> - case lists:keysearch(Variable,1,Vars) of - false -> - ok; - {value,{Variable,[_Dig]}} -> - lists:keydelete(Variable,1,Vars); - {value,{Variable,[_Dig|Digits]}} -> - NewVars = lists:keydelete(Variable,1,Vars), - [{Variable,Digits}|NewVars] - end. - get_curr([],Variable) -> Variable; get_curr([{Variable,[0|_Drest]}|_Tail],Variable) -> @@ -185,22 +150,6 @@ new_var(Vars,Variable) -> [{Variable,[Digit+1|Drest]}|NewVars] end. -delete_var(Vars,Variable) -> - case lists:keysearch(Variable,1,Vars) of - false -> - Vars; - {value,{Variable,[N]}} when N =< 1 -> - lists:keydelete(Variable,1,Vars); - {value,{Variable,[Digit|Drest]}} -> - case Digit of - 0 -> - Vars; - _ -> - NewVars = lists:keydelete(Variable,1,Vars), - [{Variable,[Digit-1|Drest]}|NewVars] - end - end. - get_prev(Vars,Variable) -> case lists:keysearch(Variable,1,Vars) of false -> -- cgit v1.2.3 From eba8580fe10c483cab3a2dc4c603ac27b3ffb4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 14:17:57 +0100 Subject: asn1ct_name: Simplify the data structures of the name server process Since we no longer need to support push/1 and pop/1, we can simplify the data structures used for keeping track of the variables. Each entry in the list can be simply {Var,integer()} instead of {Var,[integer()]}. While at it, eliminate calls to the obsolete lists:keysearch/3 function, don't use integer_to_list/0 within a call to lists:concat/1, and the catch all clause in get_prev/2 and get_next/2. --- lib/asn1/src/asn1ct_name.erl | 55 +++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index 24b28ade7e..617bea759a 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -93,11 +93,7 @@ prev(V) -> end. next(V) -> - case req({next,V}) of - none -> - exit('cant get next of none'); - Rep -> Rep - end. + req({next,V}). all(V) -> Curr = curr(V), @@ -131,45 +127,36 @@ get_digs([H|T]) -> [] end. -get_curr([],Variable) -> +get_curr([], Variable) -> Variable; -get_curr([{Variable,[0|_Drest]}|_Tail],Variable) -> - Variable; -get_curr([{Variable,[Digit|_Drest]}|_Tail],Variable) -> - list_to_atom(lists:concat([Variable,integer_to_list(Digit)])); - -get_curr([_|Tail],Variable) -> - get_curr(Tail,Variable). +get_curr([{Variable,Digit}|_Tail], Variable) -> + list_to_atom(lists:concat([Variable,Digit])); +get_curr([_|Tail], Variable) -> + get_curr(Tail, Variable). -new_var(Vars,Variable) -> - case lists:keysearch(Variable,1,Vars) of +new_var(Vars, Variable) -> + case lists:keyfind(Variable, 1, Vars) of false -> - [{Variable,[1]}|Vars]; - {value,{Variable,[Digit|Drest]}} -> - NewVars = lists:keydelete(Variable,1,Vars), - [{Variable,[Digit+1|Drest]}|NewVars] + [{Variable,1}|Vars]; + {Variable,Digit} -> + NewVars = lists:keydelete(Variable, 1, Vars), + [{Variable,Digit+1}|NewVars] end. -get_prev(Vars,Variable) -> - case lists:keysearch(Variable,1,Vars) of +get_prev(Vars, Variable) -> + case lists:keyfind(Variable, 1, Vars) of false -> none; - {value,{Variable,[Digit|_]}} when Digit =< 1 -> + {Variable,Digit} when Digit =< 1 -> Variable; - {value,{Variable,[Digit|_]}} when Digit > 1 -> - list_to_atom(lists:concat([Variable, - integer_to_list(Digit-1)])); - _ -> - none + {Variable,Digit} when Digit > 1 -> + list_to_atom(lists:concat([Variable,Digit-1])) end. -get_next(Vars,Variable) -> - case lists:keysearch(Variable,1,Vars) of +get_next(Vars, Variable) -> + case lists:keyfind(Variable, 1, Vars) of false -> list_to_atom(lists:concat([Variable,"1"])); - {value,{Variable,[Digit|_]}} when Digit >= 0 -> - list_to_atom(lists:concat([Variable, - integer_to_list(Digit+1)])); - _ -> - none + {Variable,Digit} when Digit >= 0 -> + list_to_atom(lists:concat([Variable,Digit+1])) end. -- cgit v1.2.3 From 8e740bf11519cc7281d52be1325b1ac119daa926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 13:21:01 +0100 Subject: asn1ct_name: Use a monitor instead of an arbitrary timeout --- lib/asn1/src/asn1ct_name.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index 617bea759a..66d2bb9540 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -75,11 +75,14 @@ active(V) -> end. req(Req) -> - get(?MODULE) ! {self(), Req}, + Pid = get(?MODULE), + Ref = monitor(process, Pid), + Pid ! {self(), Req}, receive - {?MODULE, Reply} -> Reply - after 5000 -> - exit(name_server_timeout) + {?MODULE, Reply} -> + Reply; + {'DOWN', Ref, process, Pid, Reason} -> + error({name_server_died,Reason}) end. clear() -> stop(), start(). -- cgit v1.2.3 From de80abaac0af1ee62b69a405a5a36ecd601fab7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 13:24:08 +0100 Subject: asn1ct_name: Make new/1 asynchronous Since new/1 does not return any useful value and cannot fail, there is no reason to wait for a response from the name server process. --- lib/asn1/src/asn1ct_name.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index 66d2bb9540..9711ea6d31 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -53,8 +53,7 @@ name_server_loop({Ref, Parent} = Monitor,Vars) -> {From,{current,Variable}} -> From ! {?MODULE,get_curr(Vars,Variable)}, name_server_loop(Monitor,Vars); - {From,{new,Variable}} -> - From ! {?MODULE,done}, + {_From,{new,Variable}} -> name_server_loop(Monitor,new_var(Vars,Variable)); {From,{prev,Variable}} -> From ! {?MODULE,get_prev(Vars,Variable)}, @@ -85,9 +84,14 @@ req(Req) -> error({name_server_died,Reason}) end. +cast(Req) -> + get(?MODULE) ! {self(), Req}, + ok. + clear() -> stop(), start(). curr(V) -> req({current,V}). -new(V) -> req({new,V}). +new(V) -> cast({new,V}). + prev(V) -> case req({prev,V}) of none -> -- cgit v1.2.3 From c4565ebd3af11ea8a7a010470b01317b5a4fc230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 13:29:53 +0100 Subject: asnct1_name: Optimize clean/1 clean/1 is used quite lot; while not a bottleneck it does seem unnecessary to stop and start the name server just to clear the variables. Since the change to clean/1 makes stop/1 unused, we can remove it. --- lib/asn1/src/asn1ct_name.erl | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index 9711ea6d31..4674048a13 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -21,7 +21,6 @@ %%-compile(export_all). -export([start/0, - stop/0, curr/1, clear/0, active/1, @@ -43,13 +42,11 @@ start() -> already_started end. -stop() -> - req(stop), - erase(?MODULE). - name_server_loop({Ref, Parent} = Monitor,Vars) -> %% io:format("name -- ~w~n",[Vars]), receive + {_From,clear} -> + name_server_loop(Monitor, []); {From,{current,Variable}} -> From ! {?MODULE,get_curr(Vars,Variable)}, name_server_loop(Monitor,Vars); @@ -61,10 +58,8 @@ name_server_loop({Ref, Parent} = Monitor,Vars) -> {From,{next,Variable}} -> From ! {?MODULE,get_next(Vars,Variable)}, name_server_loop(Monitor,Vars); - {'DOWN', Ref, process, Parent, Reason} -> - exit(Reason); - {From,stop} -> - From ! {?MODULE,stopped} + {'DOWN', Ref, process, Parent, Reason} -> + exit(Reason) end. active(V) -> @@ -88,7 +83,7 @@ cast(Req) -> get(?MODULE) ! {self(), Req}, ok. -clear() -> stop(), start(). +clear() -> cast(clear). curr(V) -> req({current,V}). new(V) -> cast({new,V}). -- cgit v1.2.3 From a0340855b3d92869ade4c48886685ff75fe17c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 14:01:02 +0100 Subject: asn1ct_name: Remove broken active/1 active/1 always returned 'true' (curr(Var) can never return 'nil', except if Var =:= 'nil'). Therefore, we can eliminate its use and remove it. --- lib/asn1/src/asn1ct_gen_per.erl | 7 +------ lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 7 +------ lib/asn1/src/asn1ct_name.erl | 7 ------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index fac233532b..2fc970cad9 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -115,12 +115,7 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> gen_encode_prim(Erules,D,DoTag) -> - Value = case asn1ct_name:active(val) of - true -> - asn1ct_gen:mk_var(asn1ct_name:curr(val)); - false -> - "Val" - end, + Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), gen_encode_prim(Erules,D,DoTag,Value). gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 81d8cdcae6..83f8feaf94 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -105,12 +105,7 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> gen_encode_prim(Erules,D,DoTag) -> - Value = case asn1ct_name:active(val) of - true -> - asn1ct_gen:mk_var(asn1ct_name:curr(val)); - false -> - "Val" - end, + Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), gen_encode_prim(Erules,D,DoTag,Value). diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index 4674048a13..c9709d352f 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -23,7 +23,6 @@ -export([start/0, curr/1, clear/0, - active/1, prev/1, next/1, all/1, @@ -62,12 +61,6 @@ name_server_loop({Ref, Parent} = Monitor,Vars) -> exit(Reason) end. -active(V) -> - case curr(V) of - nil -> false; - _ -> true - end. - req(Req) -> Pid = get(?MODULE), Ref = monitor(process, Pid), -- cgit v1.2.3 From aa81984614f5b53d3d9a94f06334cf94e1fc43ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 13:55:29 +0100 Subject: Don't call asn1ct_name:clear/0 directly after asn1ct_name:start/0 Ensure that asn1ct_name:start/0 will call asn1ct_name:clear/0 if the name server process is already running so that we can be sure that the variables are cleared. --- lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 3 --- lib/asn1/src/asn1ct_constructed_per.erl | 2 -- lib/asn1/src/asn1ct_name.erl | 3 ++- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 341a04761b..95086da7b1 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -183,7 +183,6 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> gen_decode_sequence(Erules,Typename,D) when is_record(D,type) -> asn1ct_name:start(), - asn1ct_name:clear(), asn1ct_name:new(tag), #'SEQUENCE'{tablecinf=TableConsInfo,components=CList0} = D#type.def, @@ -349,7 +348,6 @@ gen_encode_set(Erules,Typename,D) when is_record(D,type) -> gen_decode_set(Erules,Typename,D) when is_record(D,type) -> asn1ct_name:start(), - asn1ct_name:clear(), %% asn1ct_name:new(term), asn1ct_name:new(tag), #'SET'{tablecinf=TableConsInfo,components=TCompList0} = D#type.def, @@ -504,7 +502,6 @@ gen_encode_sof(Erules,Typename,_InnerTypename,D) when is_record(D,type) -> gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) -> asn1ct_name:start(), - asn1ct_name:clear(), {SeqOrSetOf, _TypeTag, Cont} = case D#type.def of {'SET OF',_Cont} -> {'SET OF','SET',_Cont}; diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index aa5ee18c80..06b315a86f 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -227,7 +227,6 @@ gen_decode_constructed(Erule, Typename, #type{}=D) -> Imm0 = gen_dec_constructed_imm(Erule, Typename, #type{}=D), Imm = opt_imm(Imm0), asn1ct_name:start(), - asn1ct_name:clear(), emit_gen_dec_imm(Imm), emit([".",nl,nl]). @@ -489,7 +488,6 @@ gen_encode_choice(Erule,Typename,D) when is_record(D,type) -> gen_decode_choice(Erules,Typename,D) when is_record(D,type) -> asn1ct_name:start(), - asn1ct_name:clear(), asn1ct_name:new(bytes), {'CHOICE',CompList} = D#type.def, Ext = extensible_enc(CompList), diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl index c9709d352f..c0c2ed302c 100644 --- a/lib/asn1/src/asn1ct_name.erl +++ b/lib/asn1/src/asn1ct_name.erl @@ -38,7 +38,8 @@ start() -> end)), ok; _Pid -> - already_started + %% Already started. Clear the variables. + clear() end. name_server_loop({Ref, Parent} = Monitor,Vars) -> -- cgit v1.2.3 From f78b1e7c4d5f8bd03b4a5194bedf7b57ebe0f72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 19 Feb 2013 10:38:04 +0100 Subject: asn1_constructed_per: Refactor gen_enc_choice2() gen_enc_choice2() duplicates code merely to avoid outputting a ";" separator after the last item. --- lib/asn1/src/asn1ct_constructed_per.erl | 58 ++++++++++----------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 06b315a86f..8707cadac7 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1603,16 +1603,14 @@ get_name_list([], Acc) -> gen_enc_choice2(Erule,TopType, {L1,L2}, Ext) -> - gen_enc_choice2(Erule,TopType, L1 ++ L2, 0, Ext); -gen_enc_choice2(Erule,TopType, {L1,L2,L3}, Ext) -> - gen_enc_choice2(Erule,TopType, L1 ++ L3 ++ L2, 0, Ext); + gen_enc_choice2(Erule, TopType, L1 ++ L2, 0, [], Ext); +gen_enc_choice2(Erule, TopType, {L1,L2,L3}, Ext) -> + gen_enc_choice2(Erule, TopType, L1 ++ L3 ++ L2, 0, [], Ext); gen_enc_choice2(Erule,TopType, L, Ext) -> - gen_enc_choice2(Erule,TopType, L, 0, Ext). + gen_enc_choice2(Erule,TopType, L, 0, [], Ext). -gen_enc_choice2(Erule,TopType,[H1,H2|T], Pos, Ext) -when is_record(H1,'ComponentType'), is_record(H2,'ComponentType') -> - Cname = H1#'ComponentType'.name, - Type = H1#'ComponentType'.typespec, +gen_enc_choice2(Erule, TopType, [H|T], Pos, Sep0, Ext) -> + #'ComponentType'{name=Cname,typespec=Type} = H, EncObj = case asn1ct_gen:get_constraint(Type#type.constraint, componentrelation) of @@ -1620,44 +1618,22 @@ when is_record(H1,'ComponentType'), is_record(H2,'ComponentType') -> case Type#type.tablecinf of [{objfun,_}|_] -> {"got objfun through args","ObjFun"}; - _ ->false - end; - _ -> {no_attr,"ObjFun"} - end, - emit({{asis,Cname}," ->",nl}), - DoExt = case Ext of - {ext,ExtPos,_} when (Pos + 1) < ExtPos -> noext; - _ -> Ext - end, - gen_enc_line(Erule,TopType,Cname,Type,"element(2,Val)", - Pos+1,EncObj,DoExt), - emit({";",nl}), - gen_enc_choice2(Erule,TopType,[H2|T], Pos+1, Ext); -gen_enc_choice2(Erule,TopType,[H1|T], Pos, Ext) - when is_record(H1,'ComponentType') -> - Cname = H1#'ComponentType'.name, - Type = H1#'ComponentType'.typespec, - EncObj = - case asn1ct_gen:get_constraint(Type#type.constraint, - componentrelation) of - no -> - case Type#type.tablecinf of - [{objfun,_}|_] -> - {"got objfun through args","ObjFun"}; - _ ->false + _ -> + false end; - _ -> {no_attr,"ObjFun"} + _ -> + {no_attr,"ObjFun"} end, - emit({{asis,H1#'ComponentType'.name}," ->",nl}), + emit([Sep0,{asis,Cname}," ->",nl]), DoExt = case Ext of - {ext,ExtPos,_} when (Pos + 1) < ExtPos -> noext; + {ext,ExtPos,_} when Pos + 1 < ExtPos -> noext; _ -> Ext end, - gen_enc_line(Erule,TopType,Cname,Type,"element(2,Val)", - Pos+1,EncObj,DoExt), - gen_enc_choice2(Erule,TopType,T, Pos+1, Ext); -gen_enc_choice2(_Erule,_,[], _, _) -> - true. + gen_enc_line(Erule, TopType, Cname, Type, "element(2, Val)", + Pos+1, EncObj, DoExt), + Sep = [";",nl], + gen_enc_choice2(Erule, TopType, T, Pos+1, Sep, Ext); +gen_enc_choice2(_, _, [], _, _, _) -> ok. gen_dec_choice(Erule,TopType,CompList,{ext,Pos,NumExt}) -> emit(["{Ext,",{curr,bytes},"} = ", -- cgit v1.2.3 From c42ce67e3fa3b2a43964761dc7d1d3177e259f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 19 Feb 2013 09:31:49 +0100 Subject: asn1ct_constructed_per: Refactor gen_dec_choice2() gen_dec_choice2() is unnecessarily complicated. Code is duplicated merely to avoid outputting a ";" separator after the last item, and there is code to skip extensionmarks that are never actually present. While at it, eliminate the the wrap_gen_dec_line() function which is not needed. It puts a {component_type,C} key in the process dictionary, but that is actually never used. Besides that, it only shuffles the argument. --- lib/asn1/src/asn1ct_constructed_per.erl | 53 ++++++++------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 8707cadac7..9808d12811 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1322,8 +1322,8 @@ gen_dec_component_no_val({ext,_,_},mandatory) -> emit({"asn1_NOVALUE"}). -gen_dec_line(Erule, TopType, Comp, Pos, DecInfObj, Ext) -> - Imm0 = gen_dec_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext), +gen_dec_line(Erule, TopType, Comp, Pos, Ext) -> + Imm0 = gen_dec_line_imm(Erule, TopType, Comp, Pos, false, Ext), Init = {ignore,fun(_) -> {[],[]} end}, Imm = [{group,[Init|Imm0]}], emit_gen_dec_imm(Imm). @@ -1675,42 +1675,23 @@ gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) -> gen_dec_choice2(Erule,TopType,L,Ext) -> - gen_dec_choice2(Erule,TopType,L,0,Ext). + gen_dec_choice2(Erule, TopType, L, 0, [], Ext). -gen_dec_choice2(Erule,TopType,[H1,H2|T],Pos,Ext) -when is_record(H1,'ComponentType'), is_record(H2,'ComponentType') -> - Cname = H1#'ComponentType'.name, - Type = H1#'ComponentType'.typespec, +gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Ext) -> + #'ComponentType'{name=Cname,typespec=Type} = H0, + H = H0#'ComponentType'{prop=mandatory}, case Type#type.def of #'ObjectClassFieldType'{type={typefield,_}} -> - emit({Pos," -> ",nl}), - wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext), - emit({";",nl}); + emit([Sep0,Pos," ->",nl]), + gen_dec_line(Erule, TopType, H, Pos+1, Ext); _ -> - emit({Pos," -> {",{asis,Cname},",",nl}), - wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext), - emit({"};",nl}) - end, - gen_dec_choice2(Erule,TopType,[H2|T],Pos+1,Ext); -gen_dec_choice2(Erule,TopType,[H1,_H2|T],Pos,Ext) when is_record(H1,'ComponentType') -> - gen_dec_choice2(Erule,TopType,[H1|T],Pos,Ext); % skip extensionmark -gen_dec_choice2(Erule,TopType,[H1|T],Pos,Ext) when is_record(H1,'ComponentType') -> - Cname = H1#'ComponentType'.name, - Type = H1#'ComponentType'.typespec, - case Type#type.def of - #'ObjectClassFieldType'{type={typefield,_}} -> - emit({Pos," -> ",nl}), - wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext); - _ -> - emit({Pos," -> {",{asis,Cname},",",nl}), - wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext), + emit([Sep0,Pos," -> {",{asis,Cname},",",nl]), + gen_dec_line(Erule, TopType, H, Pos+1, Ext), emit("}") end, - gen_dec_choice2(Erule,TopType,[T],Pos+1); -gen_dec_choice2(Erule,TopType,[_|T],Pos,Ext) -> - gen_dec_choice2(Erule,TopType,T,Pos,Ext);% skip extensionmark -gen_dec_choice2(_,_,[],Pos,_) -> - Pos. + Sep = [";",nl], + gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Ext); +gen_dec_choice2(_, _, [], _, _, _) -> ok. indent(N) -> lists:duplicate(N,32). % 32 = space @@ -1790,14 +1771,6 @@ wrap_extensionAdditionGroups([H|T],ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupN wrap_extensionAdditionGroups([],_,Acc,_,_) -> lists:reverse(Acc). - -wrap_gen_dec_line(Erule,C,TopType,_Cname,_Type,Pos,DIO,Ext) -> - put(component_type,{true,C}), - gen_dec_line(Erule, TopType, C#'ComponentType'{prop=mandatory}, - Pos, DIO, Ext), - erase(component_type). - - value_match(Index,Value) when is_atom(Value) -> value_match(Index,atom_to_list(Value)); value_match([],Value) -> -- cgit v1.2.3 From 23a99bfc9cde454b91e75f9cb1801666581b7051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Feb 2013 11:11:41 +0100 Subject: Refactor decoding of items in SEQUENCEs and CHOICEs We will need more explicit control of decoding of open types for CHOICEs, so refactor the gen_dec_line() and gen_dec_line_imm() to break out the decoding of the open types. Note that gen_dec_line_special() will not generate correct code if Ext =/= noext; thus, we can eliminate the Ext parameter. --- lib/asn1/src/asn1ct_constructed_per.erl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 9808d12811..7fc904ec16 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1254,7 +1254,7 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, St end end, - Lines = gen_dec_line_imm(Erule, TopType, Comp, Tpos, DecInfObj, Ext), + Lines = gen_dec_seq_line_imm(Erule, TopType, Comp, Tpos, DecInfObj, Ext), Postamble = case {Ext,Prop} of {noext,mandatory} -> @@ -1322,13 +1322,18 @@ gen_dec_component_no_val({ext,_,_},mandatory) -> emit({"asn1_NOVALUE"}). -gen_dec_line(Erule, TopType, Comp, Pos, Ext) -> - Imm0 = gen_dec_line_imm(Erule, TopType, Comp, Pos, false, Ext), +gen_dec_choice_line(Erule, TopType, Comp, Pos, Ext) -> + Pre = gen_dec_line_open_type(Erule, Ext, Pos), + Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre), Init = {ignore,fun(_) -> {[],[]} end}, Imm = [{group,[Init|Imm0]}], emit_gen_dec_imm(Imm). -gen_dec_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext) -> +gen_dec_seq_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext) -> + Pre = gen_dec_line_open_type(Erule, Ext, Pos), + gen_dec_line_imm(Erule, TopType, Comp, DecInfObj, Pre). + +gen_dec_line_imm(Erule, TopType, Comp, DecInfObj, Pre) -> #'ComponentType'{name=Cname,typespec=Type} = Comp, Atype = case Type of @@ -1337,9 +1342,7 @@ gen_dec_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext) -> _ -> asn1ct_gen:get_inner(Type#type.def) end, - - Pre = gen_dec_line_open_type(Erule, Ext, Pos), - Decode = gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj, Ext), + Decode = gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj), Post = fun({SaveBytes,Finish}) -> {AccTerm,AccBytes} = Finish(), @@ -1383,7 +1386,7 @@ gen_dec_line_open_type(_, _, _) -> end}. gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, - DecInfObj, Ext) -> + DecInfObj) -> #'ComponentType'{name=Cname,typespec=Type,prop=Prop} = Comp, fun({_BytesVar,PrevSt}) -> case DecInfObj of @@ -1415,7 +1418,7 @@ gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, %% objfun though arguments on function %% invocation. if - Ext == noext andalso Prop == mandatory -> + Prop =:= mandatory -> ok; true -> asn1ct_name:new(tmpterm), @@ -1429,7 +1432,7 @@ gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, asn1ct_imm:dec_code_gen(Imm, BytesVar), emit([com,nl]), if - Ext == noext andalso Prop == mandatory -> + Prop =:= mandatory -> emit([{curr,term}," =",nl," "]); true -> emit([" {"]) @@ -1446,7 +1449,7 @@ gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, emit([indent(6),{curr,tmpterm},nl]), emit([indent(2),"end"]), if - Ext == noext andalso Prop == mandatory -> + Prop =:= mandatory -> ok; true -> emit([",",nl,{curr,tmpbytes},"}"]) @@ -1466,7 +1469,7 @@ gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, end end; gen_dec_line_special(Erule, {objectfield,PrimFieldName1,PFNList}, _TopType, - Comp, _DecInfObj, _Ext) -> + Comp, _DecInfObj) -> fun({_BytesVar,PrevSt}) -> Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), @@ -1478,7 +1481,7 @@ gen_dec_line_special(Erule, {objectfield,PrimFieldName1,PFNList}, _TopType, Prop}], {SaveBytes,PrevSt} end; -gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj, _Ext) -> +gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj) -> case gen_dec_line_other(Erule, Atype, TopType, Comp) of Fun when is_function(Fun, 1) -> fun({BytesVar,PrevSt}) -> @@ -1683,10 +1686,10 @@ gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Ext) -> case Type#type.def of #'ObjectClassFieldType'{type={typefield,_}} -> emit([Sep0,Pos," ->",nl]), - gen_dec_line(Erule, TopType, H, Pos+1, Ext); + gen_dec_choice_line(Erule, TopType, H, Pos+1, Ext); _ -> emit([Sep0,Pos," -> {",{asis,Cname},",",nl]), - gen_dec_line(Erule, TopType, H, Pos+1, Ext), + gen_dec_choice_line(Erule, TopType, H, Pos+1, Ext), emit("}") end, Sep = [";",nl], -- cgit v1.2.3 From f4c7a59d96b110f79cc0fcc16286ece770a166a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 19 Feb 2013 15:00:23 +0100 Subject: asn1_constructed_per: Optimize decoding of CHOICE Optimize the decoding of CHOICE. Most important is to inline decoding of the extension bit (if present) and decoding of the choice index to give the BEAM compiler more opportunities for optimization. We will also change the structure of the generated code. The current code uses a flattened case for both the root and extension alternatives: case Choice + NumRootChoices * Ext of %% Root alternatives. 0 - ...; : LastRootAlternative -> ...; %% Extension alternatives. LastRootAlternative+1 -> ...; : %% Unknown extension. _ -> ...; end We will instead generate nested cases: case Ext of 0 -> case Choice of %% Root alternatives. 0 - ...; : LastRootAlternative -> ... end; 1 -> %% Extension alternatives. case Choice of 0 -> ...; : LastExtensionAlternative -> ...; %% Unknown extension. _ -> ...; end end Nested cases should be slightly faster. For decoding of the extensions, it also makes it possible to hoist the decoding of the open type up from each case to before the case switching on the extension index, thus reducing the size of the generated code. We will also do another change to the structure. Currently, the big flat clase is wrapped in code that repackages the return values: {Alt,{Value,RemainingEncodedData}} = case Choice + NumRootChoices * Ext of : end, {{Value,Alt},RemainingEncodedData}. We still need to do the repackaging, but we can push it down to the case arm for decoding each alternative. In many cases, that will give the BEAM compiler the opportunity to avoid building the temporary tuples. --- lib/asn1/src/asn1ct_constructed_per.erl | 160 +++++++++++++++++------- lib/asn1/src/asn1ct_imm.erl | 1 + lib/asn1/src/asn1rtt_per.erl | 100 +-------------- lib/asn1/src/asn1rtt_uper.erl | 46 +------ lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 | 6 + lib/asn1/test/testChoExtension.erl | 5 + 6 files changed, 130 insertions(+), 188 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 7fc904ec16..c3067e53be 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1322,8 +1322,7 @@ gen_dec_component_no_val({ext,_,_},mandatory) -> emit({"asn1_NOVALUE"}). -gen_dec_choice_line(Erule, TopType, Comp, Pos, Ext) -> - Pre = gen_dec_line_open_type(Erule, Ext, Pos), +gen_dec_choice_line(Erule, TopType, Comp, Pre) -> Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre), Init = {ignore,fun(_) -> {[],[]} end}, Imm = [{group,[Init|Imm0]}], @@ -1638,62 +1637,135 @@ gen_enc_choice2(Erule, TopType, [H|T], Pos, Sep0, Ext) -> gen_enc_choice2(Erule, TopType, T, Pos+1, Sep, Ext); gen_enc_choice2(_, _, [], _, _, _) -> ok. -gen_dec_choice(Erule,TopType,CompList,{ext,Pos,NumExt}) -> - emit(["{Ext,",{curr,bytes},"} = ", - {call,Erule,getbit,["Bytes"]},com,nl]), +%% Generate the code for CHOICE. If the CHOICE is extensible, +%% the structure of the generated code is as follows: +%% +%% case Bytes of +%% <<0:1,Bytes1/bitstring>> -> +%% Choice = +%% case Choice of +%% 0 -> ; +%% : +%% LastRootChoice -> +%% end; +%% <<1:1,Bytes1/bitstring>> -> +%% Choice = +%% TmpVal = +%% case Choice of +%% 0 -> ; +%% : +%% LastExtension -> ; +%% _ -> +%% end +%% end +%% +%% The return value from the generated function always looks like: +%% {{ChoiceTag,Value},RemainingBuffer} +%% where ChoiceTag will be 'asn1_ExtAlt' for an unknown extension. +%% +%% If the CHOICE is not extensible, the top-level case is omitted +%% and only the code in the first case arm is generated. + +gen_dec_choice(Erule, TopType, CompList, {ext,_,_}=Ext) -> + {RootList,ExtList} = split_complist(CompList), + emit(["case Bytes of",nl]), + case RootList of + [] -> + ok; + [_|_] -> + emit(["<<0:1,Bytes1/bitstring>> ->",nl]), + asn1ct_name:new(bytes), + gen_dec_choice1(Erule, TopType, RootList, noext), + emit([";",nl,nl]) + end, + emit(["<<1:1,Bytes1/bitstring>> ->",nl]), + asn1ct_name:clear(), + asn1ct_name:new(bytes), asn1ct_name:new(bytes), - gen_dec_choice1(Erule,TopType,CompList,{ext,Pos,NumExt}); -gen_dec_choice(Erule,TopType,CompList,noext) -> - gen_dec_choice1(Erule,TopType,CompList,noext). - -gen_dec_choice1(Erule,TopType,CompList,noext) -> - emit(["{Choice,",{curr,bytes}, - "} = ",{call,Erule,getchoice, - [{prev,bytes},length(CompList),"0"]},com,nl, - "{Cname,{Val,NewBytes}} = case Choice of",nl]), - gen_dec_choice2(Erule,TopType,CompList,noext), - emit({nl,"end,",nl}), - emit({nl,"{{Cname,Val},NewBytes}"}); -gen_dec_choice1(Erule,TopType,{RootList,ExtList},Ext) -> - NewList = RootList ++ ExtList, - gen_dec_choice1(Erule,TopType, NewList, Ext); -gen_dec_choice1(Erule,TopType,{RootList,ExtList,RootList2},Ext) -> - NewList = RootList ++ RootList2 ++ ExtList, - gen_dec_choice1(Erule,TopType, NewList, Ext); -gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) -> - emit(["{Choice,",{curr,bytes},"} = ", - {call,Erule,getchoice, - [{prev,bytes},length(CompList)-ExtNum,"Ext"]},com,nl]), - emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}), - gen_dec_choice2(Erule,TopType,CompList,{ext,ExtPos,ExtNum}), + gen_dec_choice1(Erule, TopType, ExtList, Ext), + emit([nl,"end"]); +gen_dec_choice(Erule, TopType, CompList, noext) -> + gen_dec_choice1(Erule, TopType, CompList, noext). + +split_complist({Root1,Ext,Root2}) -> + {Root1++Root2,Ext}; +split_complist({_,_}=CompList) -> + CompList. + +gen_dec_choice1(Erule, TopType, CompList, noext=Ext) -> + emit_getchoice(Erule, CompList, Ext), + emit(["case Choice of",nl]), + Pre = {safe,fun(St) -> + {asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + fun() -> St end} + end}, + gen_dec_choice2(Erule, TopType, CompList, Pre), + emit([nl,"end"]); +gen_dec_choice1(Erule, TopType, CompList, {ext,_,_}=Ext) -> + emit_getchoice(Erule, CompList, Ext), Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + emit(["begin",nl]), BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), - emit([";",nl, - "_ ->",nl]), - {TmpTerm,TmpBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar), - emit([com,nl, - "{asn1_ExtAlt,{",TmpTerm,com,TmpBuf,"}}",nl, - "end,",nl,nl, - "{{Cname,Val},NewBytes}"]). - + {Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar), + emit([nl, + "end,",nl, + "case Choice of",nl]), + Pre = {safe,fun(St) -> + emit(["{TmpVal,_} = "]), + {Dst, + fun() -> + emit([",",nl, + "{TmpVal,",DstBuf,"}"]), + St + end} + end}, + gen_dec_choice2(Erule, TopType, CompList, Pre), + case CompList of + [] -> ok; + [_|_] -> emit([";",nl]) + end, + emit(["_ ->",nl, + "{{asn1_ExtAlt,",Dst,"},",DstBuf,"}",nl, + "end"]). + +emit_getchoice(Erule, CompList, Ext) -> + Al = is_aligned(Erule), + Imm = case {Ext,CompList} of + {noext,[_]} -> + {value,0}; + {noext,_} -> + asn1ct_imm:per_dec_constrained(0, length(CompList)-1, Al); + {{ext,_,_},_} -> + asn1ct_imm:per_dec_normally_small_number(Al) + end, + emit(["{Choice,",{curr,bytes},"} = ",nl]), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:prev(bytes)), + asn1ct_imm:dec_code_gen(Imm, BytesVar), + emit([com,nl]). gen_dec_choice2(Erule,TopType,L,Ext) -> gen_dec_choice2(Erule, TopType, L, 0, [], Ext). -gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Ext) -> +gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Pre) -> #'ComponentType'{name=Cname,typespec=Type} = H0, H = H0#'ComponentType'{prop=mandatory}, + emit([Sep0,Pos," ->",nl]), case Type#type.def of #'ObjectClassFieldType'{type={typefield,_}} -> - emit([Sep0,Pos," ->",nl]), - gen_dec_choice_line(Erule, TopType, H, Pos+1, Ext); + emit("{Cname,{Val,NewBytes}} = begin\n"), + gen_dec_choice_line(Erule, TopType, H, Pre), + emit([nl, + "end,",nl, + "{{Cname,Val},NewBytes}"]); _ -> - emit([Sep0,Pos," -> {",{asis,Cname},",",nl]), - gen_dec_choice_line(Erule, TopType, H, Pos+1, Ext), - emit("}") + emit("{Val,NewBytes} = begin\n"), + gen_dec_choice_line(Erule, TopType, H, Pre), + emit([nl, + "end,",nl, + "{{",{asis,Cname},",Val},NewBytes}"]) end, Sep = [";",nl], - gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Ext); + gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Pre); gen_dec_choice2(_, _, [], _, _, _) -> ok. indent(N) -> diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 4b2c3b1b65..2c5620d5c8 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -25,6 +25,7 @@ per_dec_length/3,per_dec_named_integer/3, per_dec_octet_string/2,per_dec_open_type/1,per_dec_real/1, per_dec_restricted_string/1]). +-export([per_dec_constrained/3,per_dec_normally_small_number/1]). -export([optimize_alignment/1,optimize_alignment/2, dec_slim_cg/2,dec_code_gen/2]). -export([effective_constraint/2]). diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index aa6cf4da0a..1023be8ce1 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -19,7 +19,7 @@ -module(asn1rtt_per). -export([setext/1, fixextensions/2, - skipextensions/3, getbit/1, getchoice/3, + skipextensions/3, set_choice/3,encode_integer/2, encode_small_number/1, encode_constrained_number/2, @@ -88,23 +88,6 @@ skipextensions(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) - Bytes0 end. - -getchoice(Bytes, 1, 0) -> % only 1 alternative is not encoded - {0,Bytes}; -getchoice(Bytes, _, 1) -> - decode_small_number(Bytes); -getchoice(Bytes, NumChoices, 0) -> - decode_constrained_number(Bytes, {0,NumChoices-1}). - - -getbit(Buffer) -> - <> = Buffer, - {B,Rest}. - -getbits(Buffer, Num) when is_bitstring(Buffer) -> - <> = Buffer, - {Bs,Rest}. - align(Bin) when is_binary(Bin) -> Bin; align(BitStr) when is_bitstring(BitStr) -> @@ -112,28 +95,6 @@ align(BitStr) when is_bitstring(BitStr) -> <<_:AlignBits,Rest/binary>> = BitStr, Rest. - -%% First align buffer, then pick the first Num octets. -%% Returns octets as an integer with bit significance as in buffer. -getoctets(Buffer, Num) when is_binary(Buffer) -> - <> = Buffer, - {Val,RestBin}; -getoctets(Buffer, Num) when is_bitstring(Buffer) -> - AlignBits = bit_size(Buffer) rem 8, - <<_:AlignBits,Val:Num/integer-unit:8,RestBin/binary>> = Buffer, - {Val,RestBin}. - - -%% First align buffer, then pick the first Num octets. -%% Returns octets as a binary -getoctets_as_bin(Bin,Num) when is_binary(Bin) -> - <> = Bin, - {Octets,RestBin}; -getoctets_as_bin(Bin,Num) when is_bitstring(Bin) -> - AlignBits = bit_size(Bin) rem 8, - <<_:AlignBits,Val:Num/binary,RestBin/binary>> = Bin, - {Val,RestBin}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% set_choice(Alt,Choices,Altnum) -> ListofBitSettings %% Alt = atom() @@ -238,15 +199,6 @@ encode_small_number(Val) when Val < 64 -> encode_small_number(Val) -> [1|encode_semi_constrained_number(0, Val)]. -decode_small_number(Bytes) -> - {Bit,Bytes2} = getbit(Bytes), - case Bit of - 0 -> - getbits(Bytes2, 6); - 1 -> - decode_semi_constrained_number(Bytes2) - end. - %% X.691:10.7 Encoding of a semi-constrained whole number encode_semi_constrained_number(Lb, Val) -> Val2 = Val - Lb, @@ -261,10 +213,6 @@ encode_semi_constrained_number(Lb, Val) -> [encode_length(Len),21,<>|Oct] end. -decode_semi_constrained_number(Bytes) -> - {Len,Bytes2} = decode_length(Bytes), - getoctets(Bytes2, Len). - encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) -> Val2 = Val-Lb, [10,N,Val2]; @@ -333,47 +281,6 @@ encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> encode_constrained_number({_,_},Val) -> exit({error,{asn1,{illegal_value,Val}}}). -decode_constrained_number(Buffer,VR={Lb,Ub}) -> - Range = Ub - Lb + 1, - decode_constrained_number(Buffer,VR,Range). - -decode_constrained_number(Buffer,{Lb,_Ub},Range) -> - % Val2 = Val - Lb, - {Val,Remain} = - if - Range == 1 -> - {0,Buffer}; - Range == 2 -> - getbits(Buffer,1); - Range =< 4 -> - getbits(Buffer,2); - Range =< 8 -> - getbits(Buffer,3); - Range =< 16 -> - getbits(Buffer,4); - Range =< 32 -> - getbits(Buffer,5); - Range =< 64 -> - getbits(Buffer,6); - Range =< 128 -> - getbits(Buffer,7); - Range =< 255 -> - getbits(Buffer,8); - Range =< 256 -> - getoctets(Buffer,1); - Range =< 65536 -> - getoctets(Buffer,2); - Range =< (1 bsl (255*8)) -> - OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), - RangeOctLen = length(OList), - {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), - {Octs, RestBytes} = getoctets_as_bin(Bytes, Len), - {binary:decode_unsigned(Octs), RestBytes}; - true -> - exit({not_supported,{integer_range,Range}}) - end, - {Val+Lb,Remain}. - %% For some reason the minimum bits needed in the length field in %% the encoding of constrained whole numbers must always be at least 2? minimum_bits(N) when N < 4 -> 2; @@ -476,11 +383,6 @@ decode_length(Buffer) -> % un-constrained exit({error,{asn1,{decode_length,{nyi,above_16k}}}}) end. -decode_length(Buffer, {Lb,Ub}) when Ub =< 65535, Lb >= 0 -> % constrained - decode_constrained_number(Buffer, {Lb,Ub}); -decode_length(Buffer, {Lb,_Ub}) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 - decode_length(Buffer). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% bitstring NamedBitList %% Val can be of: diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index 8efe9a7b0f..e55535860f 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -21,7 +21,7 @@ -export([setext/1, fixoptionals/3, fixextensions/2, - skipextensions/3, getbit/1, getchoice/3 ]). + skipextensions/3]). -export([set_choice/3, encode_integer/2, encode_integer/3]). -export([encode_small_number/1, encode_constrained_number/2, encode_boolean/1, @@ -123,29 +123,6 @@ skipextensions(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) - end. -getchoice(Bytes,1,0) -> % only 1 alternative is not encoded - {0,Bytes}; -getchoice(Bytes,_,1) -> - decode_small_number(Bytes); -getchoice(Bytes,NumChoices,0) -> - decode_constrained_number(Bytes,{0,NumChoices-1}). - - -getbit(Buffer) -> - <> = Buffer, - {B,Rest}. - -getbits(Buffer, Num) when is_bitstring(Buffer) -> - <> = Buffer, - {Bs,Rest}. - - -%% Pick the first Num octets. -%% Returns octets as an integer with bit significance as in buffer. -getoctets(Buffer, Num) when is_bitstring(Buffer) -> - <> = Buffer, - {Val,RestBitStr}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% set_choice(Alt,Choices,Altnum) -> ListofBitSettings %% Alt = atom() @@ -265,15 +242,6 @@ encode_small_number(Val) when Val < 64 -> encode_small_number(Val) -> [<<1:1>>|encode_semi_constrained_number(0, Val)]. -decode_small_number(Bytes) -> - {Bit,Bytes2} = getbit(Bytes), - case Bit of - 0 -> - getbits(Bytes2,6); - 1 -> - decode_semi_constrained_number(Bytes2) - end. - %% X.691:10.7 Encoding of a semi-constrained whole number encode_semi_constrained_number(Lb, Val) -> %% encoding in minimum number of octets preceeded by a length @@ -289,11 +257,6 @@ encode_semi_constrained_number(Lb, Val) -> [encode_length(Size),Bin] end. -decode_semi_constrained_number(Bytes) -> - {Len,Bytes2} = decode_length(Bytes), - {V,Bytes3} = getoctets(Bytes2,Len), - {V,Bytes3}. - encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> Range = Ub - Lb + 1, Val2 = Val - Lb, @@ -302,13 +265,6 @@ encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> encode_constrained_number(Range,Val) -> exit({error,{asn1,{integer_range,Range,value,Val}}}). - -decode_constrained_number(Buffer, {Lb,Ub}) -> - Range = Ub - Lb + 1, - NumBits = num_bits(Range), - {Val,Remain} = getbits(Buffer,NumBits), - {Val+Lb,Remain}. - %% X.691:10.8 Encoding of an unconstrained whole number encode_unconstrained_number(Val) when Val >= 0 -> diff --git a/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 b/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 index 18473bae30..f6fe18be10 100644 --- a/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 @@ -41,4 +41,10 @@ ChoExt4 ::= CHOICE str OCTET STRING } +ChoEmptyRoot ::= CHOICE { + ..., + bool BOOLEAN, + int INTEGER (0..7) +} + END diff --git a/lib/asn1/test/testChoExtension.erl b/lib/asn1/test/testChoExtension.erl index 067d4d2bf7..5c67ff62ce 100644 --- a/lib/asn1/test/testChoExtension.erl +++ b/lib/asn1/test/testChoExtension.erl @@ -42,6 +42,11 @@ extension(_Rules) -> roundtrip('ChoExt3', {int,33}), roundtrip('ChoExt4', {str,"abc"}), + roundtrip('ChoEmptyRoot', {bool,false}), + roundtrip('ChoEmptyRoot', {bool,true}), + roundtrip('ChoEmptyRoot', {int,0}), + roundtrip('ChoEmptyRoot', {int,7}), + ok. -- cgit v1.2.3 From 002d26b4660cf14509ea6848b9260014ae5960c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 22 Feb 2013 11:49:06 +0100 Subject: asn1ct_constructed_per: Break out part of gen_dec_comp_call() Improve readability and maintainability. --- lib/asn1/src/asn1ct_constructed_per.erl | 109 +++++++++++++++----------------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index c3067e53be..bcc5aee8bb 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1233,68 +1233,63 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, end end end, - - OptOrDef = - case {Ext,Prop} of - {noext,mandatory} -> - ignore; - {noext,_} -> %% OPTIONAL or DEFAULT - OptPos = get_optionality_pos(TextPos, OptTable), - Element = io_lib:format("Opt band (1 bsl ~w)", - [NumberOfOptionals - OptPos]), - fun(St) -> - emit(["case ",Element," of",nl]), - emit([" _Opt",TextPos," when _Opt",TextPos," > 0 ->"]), - St - end; - {{ext,_,_},_} -> %Extension - fun(St) -> - emit(["case Extensions of",nl, - " <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]), - St - end - end, + {Pre,Post} = comp_call_pre_post(Ext, Prop, Pos, Type, TextPos, + OptTable, NumberOfOptionals, Ext), Lines = gen_dec_seq_line_imm(Erule, TopType, Comp, Tpos, DecInfObj, Ext), - Postamble = - case {Ext,Prop} of - {noext,mandatory} -> - ignore; - {noext,_} -> - fun(St) -> - emit([";",nl,"0 ->"]), - emit(["{"]), - gen_dec_component_no_val(Ext,Prop), - emit({",",{curr,bytes},"}",nl}), - emit([nl,"end"]), - St - end; - _ -> - fun(St) -> - emit([";",nl,"_ ->",nl]), - emit(["{"]), - case Type of - #type{def=#'SEQUENCE'{ - extaddgroup=Number2, - components=ExtGroupCompList2}} - when is_integer(Number2)-> - emit({"{extAddGroup,"}), - gen_dec_extaddGroup_no_val(Ext,ExtGroupCompList2), - emit({"}"}); - _ -> - gen_dec_component_no_val(Ext, Prop) - end, - emit({",",{curr,bytes},"}",nl}), - emit([nl,"end"]), - St - end - end, AdvBuffer = {ignore,fun(St) -> asn1ct_name:new(bytes), St end}, - [{group,[{safe,Comment},{safe,Preamble}, - OptOrDef|Lines]++ - [Postamble,{safe,AdvBuffer}]}]. + [{group,[{safe,Comment},{safe,Preamble}] ++ Pre ++ + Lines ++ Post ++ [{safe,AdvBuffer}]}]. + +comp_call_pre_post(noext, mandatory, _, _, _, _, _, _) -> + {[],[]}; +comp_call_pre_post(noext, Prop, _, _, TextPos, OptTable, NumOptionals, Ext) -> + %% OPTIONAL or DEFAULT + OptPos = get_optionality_pos(TextPos, OptTable), + Element = io_lib:format("Opt band (1 bsl ~w)", + [NumOptionals - OptPos]), + {[fun(St) -> + emit(["case ",Element," of",nl, + " _Opt",TextPos," when _Opt",TextPos," > 0 ->"]), + St + end], + [fun(St) -> + emit([";",nl, + "0 ->",nl, + "{"]), + gen_dec_component_no_val(Ext, Prop), + emit([",",{curr,bytes},"}",nl, + "end"]), + St + end]}; +comp_call_pre_post({ext,_,_}, Prop, Pos, Type, _, _, _, Ext) -> + %% Extension + {[fun(St) -> + emit(["case Extensions of",nl, + " <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]), + St + end], + [fun(St) -> + emit([";",nl, + "_ ->",nl, + "{"]), + case Type of + #type{def=#'SEQUENCE'{ + extaddgroup=Number2, + components=ExtGroupCompList2}} + when is_integer(Number2)-> + emit("{extAddGroup,"), + gen_dec_extaddGroup_no_val(Ext, ExtGroupCompList2), + emit("}"); + _ -> + gen_dec_component_no_val(Ext, Prop) + end, + emit([",",{curr,bytes},"}",nl, + "end"]), + St + end]}. is_mandatory_predef_tab_c(noext, mandatory, {"got objfun through args","ObjFun"}) -> -- cgit v1.2.3 From bf3cce888ad50e19e260a7aab76a58d49f4fdf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 22 Feb 2013 15:57:04 +0100 Subject: asn1ct_constructed_per: Optimize decoding of OPTIONAL --- lib/asn1/src/asn1ct_constructed_per.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index bcc5aee8bb..02947ed6ea 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1248,11 +1248,15 @@ comp_call_pre_post(noext, mandatory, _, _, _, _, _, _) -> comp_call_pre_post(noext, Prop, _, _, TextPos, OptTable, NumOptionals, Ext) -> %% OPTIONAL or DEFAULT OptPos = get_optionality_pos(TextPos, OptTable), - Element = io_lib:format("Opt band (1 bsl ~w)", - [NumOptionals - OptPos]), + Element = case NumOptionals - OptPos of + 0 -> + "Opt band 1"; + Shift -> + lists:concat(["(Opt bsr ",Shift,") band 1"]) + end, {[fun(St) -> emit(["case ",Element," of",nl, - " _Opt",TextPos," when _Opt",TextPos," > 0 ->"]), + "1 ->",nl]), St end], [fun(St) -> -- cgit v1.2.3 From 8f9bee52101deefef7d8027961fa7739fa0fbe76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 22 Feb 2013 17:22:10 +0100 Subject: Remove unnecessary code duplication in gen_objset_{dec,enc}() --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 76 +++++++---------------------- lib/asn1/src/asn1ct_gen_per.erl | 88 ++++++++-------------------------- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 45 +++++------------ 3 files changed, 48 insertions(+), 161 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 7de80f216f..70a5451237 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -1107,36 +1107,13 @@ gen_objset_enc(_,_,{unique,undefined},_,_,_,_,_) -> %% There is no unique field in the class of this object set %% don't bother about the constraint []; -gen_objset_enc(Erules,ObjSName,UniqueName, - [{ObjName,Val,Fields},T|Rest],ClName,ClFields, +gen_objset_enc(Erules, ObjSetName, UniqueName, + [{ObjName,Val,Fields}|T], ClName, ClFields, NthObj,Acc)-> - emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val}, - ") ->",nl}), + emit(["'getenc_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val}, + ") ->",nl]), CurrMod = get(currmod), {InternalFunc,NewNthObj}= - case ObjName of - {no_mod,no_name} -> - gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj); - {CurrMod,Name} -> - emit({" fun 'enc_",Name,"'/3"}), - {[],NthObj}; - {ModuleName,Name} -> - emit_ext_fun(enc,ModuleName,Name), -% emit([" {'",ModuleName,"', 'enc_",Name,"'}"]), - {[],NthObj}; - _ -> - emit({" fun 'enc_",ObjName,"'/3"}), - {[],NthObj} - end, - emit({";",nl}), - gen_objset_enc(Erules,ObjSName,UniqueName,[T|Rest],ClName,ClFields, - NewNthObj,InternalFunc ++ Acc); -gen_objset_enc(_,ObjSetName,UniqueName, - [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) -> - emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl}), - CurrMod = get(currmod), - {InternalFunc,_} = case ObjName of {no_mod,no_name} -> gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj); @@ -1145,16 +1122,14 @@ gen_objset_enc(_,ObjSetName,UniqueName, {[],NthObj}; {ModuleName,Name} -> emit_ext_fun(enc,ModuleName,Name), -% emit([" {'",ModuleName,"', 'enc_",Name,"'}"]), {[],NthObj}; _ -> emit({" fun 'enc_",ObjName,"'/3"}), {[],NthObj} end, - emit([";",nl]), - emit_default_getenc(ObjSetName,UniqueName), - emit({".",nl,nl}), - InternalFunc ++ Acc; + emit({";",nl}), + gen_objset_enc(Erules, ObjSetName, UniqueName, T, ClName, ClFields, + NewNthObj, InternalFunc ++ Acc); %% See X.681 Annex E for the following case gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj,Acc) -> @@ -1166,7 +1141,9 @@ gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, emit({indent(6),"{Val,Len}",nl}), emit({indent(3),"end.",nl,nl}), Acc; -gen_objset_enc(_,_,_,[],_,_,_,Acc) -> +gen_objset_enc(_, ObjSetName, UniqueName, [], _, _, _, Acc) -> + emit_default_getenc(ObjSetName, UniqueName), + emit({".",nl,nl}), Acc. emit_ext_fun(EncDec,ModuleName,Name) -> @@ -1344,8 +1321,8 @@ gen_objset_dec(_,_,{unique,undefined},_,_,_,_) -> %% There is no unique field in the class of this object set %% don't bother about the constraint ok; -gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest], - ClName,ClFields,NthObj)-> +gen_objset_dec(Erules, ObjSName, UniqueName, [{ObjName,Val,Fields}|T], + ClName, ClFields, NthObj)-> emit(["'getdec_",ObjSName,"'(",{asis,UniqueName},",", {asis,Val},") ->",nl]), CurrMod = get(currmod), @@ -1358,35 +1335,14 @@ gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest], NthObj; {ModuleName,Name} -> emit_ext_fun(dec,ModuleName,Name), -% emit([" {'",ModuleName,"', 'dec_",Name,"'}"]), NthObj; _ -> emit([" fun 'dec_",ObjName,"'/3"]), NthObj end, emit([";",nl]), - gen_objset_dec(Erules,ObjSName,UniqueName,[T|Rest],ClName, - ClFields,NewNthObj); -gen_objset_dec(_,ObjSetName,UniqueName,[{ObjName,Val,Fields}], - _ClName,ClFields,NthObj) -> - emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl]), - CurrMod = get(currmod), - case ObjName of - {no_mod,no_name} -> - gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj); - {CurrMod,Name} -> - emit([" fun 'dec_",Name,"'/3"]); - {ModuleName,Name} -> - emit_ext_fun(dec,ModuleName,Name); -% emit([" {'",ModuleName,"', 'dec_",Name,"'}"]); - _ -> - emit([" fun 'dec_",ObjName,"'/3"]) - end, - emit([";",nl]), - emit_default_getdec(ObjSetName,UniqueName), - emit([".",nl,nl]), - ok; + gen_objset_dec(Erules, ObjSName, UniqueName, T, ClName, + ClFields, NewNthObj); gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj) -> emit(["'getdec_",ObjSetName,"'(_, _) ->",nl]), @@ -1400,7 +1356,9 @@ gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, indent(4),"end",nl]), emit([indent(2),"end.",nl,nl]), ok; -gen_objset_dec(_,_,_,[],_,_,_) -> +gen_objset_dec(_, ObjSetName, UniqueName, [], _, _, _) -> + emit_default_getdec(ObjSetName, UniqueName), + emit([".",nl,nl]), ok. emit_default_getdec(ObjSetName,UniqueName) -> diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 2fc970cad9..9c82337135 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -660,67 +660,40 @@ gen_objset_enc(_,_,{unique,undefined},_,_,_,_,_) -> %% There is no unique field in the class of this object set %% don't bother about the constraint []; -gen_objset_enc(Erule,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest], - ClName,ClFields,NthObj,Acc)-> - emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val}, - ") ->",nl}), +gen_objset_enc(Erule, ObjSetName, UniqueName, [{ObjName,Val,Fields}|T], + ClName, ClFields, NthObj, Acc)-> + emit(["'getenc_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val}, + ") ->",nl]), CurrMod = get(currmod), {InternalFunc,NewNthObj}= case ObjName of {no_mod,no_name} -> - gen_inlined_enc_funs(Erule,Fields,ClFields,ObjSName,NthObj); + gen_inlined_enc_funs(Erule, Fields, ClFields, + ObjSetName, NthObj); {CurrMod,Name} -> emit({" fun 'enc_",Name,"'/3"}), {[],0}; {ModName,Name} -> emit_ext_encfun(ModName,Name), -% emit([" {'",ModName,"', 'enc_",Name,"'}"]), {[],0}; _Other -> emit({" fun 'enc_",ObjName,"'/3"}), {[],0} end, emit({";",nl}), - gen_objset_enc(Erule,ObjSName,UniqueName,[T|Rest],ClName,ClFields, - NewNthObj,InternalFunc ++ Acc); -gen_objset_enc(Erule,ObjSetName,UniqueName, - [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) -> - - emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl}), - CurrMod = get(currmod), - {InternalFunc,_}= - case ObjName of - {no_mod,no_name} -> - gen_inlined_enc_funs(Erule,Fields,ClFields,ObjSetName,NthObj); - {CurrMod,Name} -> - emit({" fun 'enc_",Name,"'/3"}), - {[],NthObj}; - {ModName,Name} -> - emit_ext_encfun(ModName,Name), -% emit([" {'",ModName,"', 'enc_",Name,"'}"]), - {[],NthObj}; - _Other -> - emit({" fun 'enc_",ObjName,"'/3"}), - {[],NthObj} - end, - emit([";",nl]), - emit_default_getenc(ObjSetName,UniqueName), - emit({".",nl,nl}), - InternalFunc++Acc; + gen_objset_enc(Erule, ObjSetName, UniqueName, T, ClName, ClFields, + NewNthObj, InternalFunc ++ Acc); gen_objset_enc(Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj,Acc) -> emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}), emit({indent(3),"fun(_, Val, _) ->",nl}), - case Erule of - uper -> - emit([indent(6),"Val",nl]); - _ -> - emit([indent(6),"[{octets,Val}]",nl]) - end, - emit({indent(3),"end.",nl,nl}), + uper = Erule, + emit([indent(6),"Val",nl, + indent(3),"end.",nl,nl]), Acc; -gen_objset_enc(_,_,_,[],_,_,_,Acc) -> +gen_objset_enc(_, ObjSetName, UniqueName, [], _, _, _, Acc) -> + emit_default_getenc(ObjSetName, UniqueName), + emit([".",nl,nl]), Acc. emit_ext_encfun(ModuleName,Name) -> @@ -863,9 +836,8 @@ gen_objset_dec(_,{unique,undefined},_,_,_,_) -> %% There is no unique field in the class of this object set %% don't bother about the constraint ok; -gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName, - ClFields,NthObj)-> - +gen_objset_dec(ObjSName, UniqueName, [{ObjName,Val,Fields}|T], ClName, + ClFields, NthObj)-> emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val}, ") ->",nl}), CurrMod = get(currmod), @@ -878,35 +850,13 @@ gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName, NthObj; {ModName,Name} -> emit_ext_decfun(ModName,Name), -% emit([" {'",ModName,"', 'dec_",Name,"'}"]), NthObj; _Other -> emit({" fun 'dec_",ObjName,"'/4"}), NthObj end, emit({";",nl}), - gen_objset_dec(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NewNthObj); -gen_objset_dec(ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName, - ClFields,NthObj) -> - - emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val}, - ") ->",nl}), - CurrMod=get(currmod), - case ObjName of - {no_mod,no_name} -> - gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj); - {CurrMod,Name} -> - emit([" fun 'dec_",Name,"'/4"]); - {ModName,Name} -> - emit_ext_decfun(ModName,Name); -% emit([" {'",ModName,"', 'dec_",Name,"'}"]); - _Other -> - emit({" fun 'dec_",ObjName,"'/4"}) - end, - emit([";",nl]), - emit_default_getdec(ObjSetName,UniqueName), - emit({".",nl,nl}), - ok; + gen_objset_dec(ObjSName, UniqueName, T, ClName, ClFields, NewNthObj); gen_objset_dec(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields, _NthObj) -> emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}), @@ -914,7 +864,9 @@ gen_objset_dec(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields, emit({indent(6),"{Bytes,Attr1}",nl}), emit({indent(3),"end.",nl,nl}), ok; -gen_objset_dec(_,_,[],_,_,_) -> +gen_objset_dec(ObjSetName, UniqueName, [], _, _, _) -> + emit_default_getdec(ObjSetName, UniqueName), + emit([".",nl,nl]), ok. emit_ext_decfun(ModuleName,Name) -> diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 83f8feaf94..423d10ef14 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -928,54 +928,29 @@ gen_objset_enc(_Erule,_,{unique,undefined},_,_,_,_,_) -> %% There is no unique field in the class of this object set %% don't bother about the constraint []; -gen_objset_enc(Erule,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest], - ClName,ClFields,NthObj,Acc)-> - emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl}), +gen_objset_enc(Erule, ObjSetName, UniqueName, [{ObjName,Val,Fields}|T], + ClName, ClFields, NthObj, Acc)-> + emit(["'getenc_",ObjSetName,"'(",{asis,UniqueName},",", + {asis,Val},") ->",nl]), CurrMod = get(currmod), {InternalFunc,NewNthObj}= case ObjName of {no_mod,no_name} -> - gen_inlined_enc_funs(Erule,Fields,ClFields,ObjSName,NthObj); + gen_inlined_enc_funs(Erule, Fields, ClFields, + ObjSetName, NthObj); {CurrMod,Name} -> emit({" fun 'enc_",Name,"'/3"}), {[],NthObj}; {ModName,Name} -> emit_ext_encfun(ModName,Name), -% emit([" {'",ModName,"', 'enc_",Name,"'}"]), {[],NthObj}; _ -> emit({" fun 'enc_",ObjName,"'/3"}), {[],NthObj} end, emit({";",nl}), - gen_objset_enc(Erule,ObjSName,UniqueName,[T|Rest],ClName,ClFields, - NewNthObj,InternalFunc++Acc); -gen_objset_enc(Erule,ObjSetName,UniqueName, - [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) -> - - emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl}), - CurrMod = get(currmod), - {InternalFunc,_}= - case ObjName of - {no_mod,no_name} -> - gen_inlined_enc_funs(Erule,Fields,ClFields,ObjSetName,NthObj); - {CurrMod,Name} -> - emit({" fun 'enc_",Name,"'/3"}), - {[],NthObj}; - {ModName,Name} -> - emit_ext_encfun(ModName,Name), -% emit([" {'",ModName,"', 'enc_",Name,"'}"]), - {[],NthObj}; - _ -> - emit({" fun 'enc_",ObjName,"'/3"}), - {[],NthObj} - end, - emit([";",nl]), - emit_default_getenc(ObjSetName,UniqueName), - emit({".",nl,nl}), - InternalFunc++Acc; + gen_objset_enc(Erule, ObjSetName, UniqueName, T, ClName, ClFields, + NewNthObj, InternalFunc++Acc); gen_objset_enc(_Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj,Acc) -> emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}), @@ -993,7 +968,9 @@ gen_objset_enc(_Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, emit({indent(6),"end",nl}), emit({indent(3),"end.",nl,nl}), Acc; -gen_objset_enc(_Erule,_,_,[],_,_,_,Acc) -> +gen_objset_enc(_Erule, ObjSetName, UniqueName, [], _, _, _, Acc) -> + emit_default_getenc(ObjSetName, UniqueName), + emit([".",nl,nl]), Acc. emit_ext_encfun(ModuleName,Name) -> -- cgit v1.2.3 From 78bf2e6ab930212e49a291b9991bd3bfc886bf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Feb 2013 08:39:48 +0100 Subject: testInfObj: Introduce roundtrip/3 to simplify the test suite --- lib/asn1/test/testInfObj.erl | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index 03e70c730a..6318bf44b5 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -22,8 +22,6 @@ -export([main/1]). --include_lib("test_server/include/test_server.hrl"). - -record('InitiatingMessage',{procedureCode,criticality,value}). -record('InitiatingMessage2',{procedureCode,criticality,value}). -record('Iu-ReleaseCommand',{first,second}). @@ -34,22 +32,11 @@ main(_Erule) -> value=#'Iu-ReleaseCommand'{ first=13, second=true}}, - ?line {ok,Bytes1} = - asn1_wrapper:encode('RANAPextract1','InitiatingMessage',Val1), - - ?line {ok,{'InitiatingMessage',1,ignore,{'Iu-ReleaseCommand',13,true}}}= - asn1_wrapper:decode('RANAPextract1','InitiatingMessage',Bytes1), - - ?line {ok,Bytes2} = - asn1_wrapper:encode('InfObj','InitiatingMessage',Val1), - - ?line {ok,Val1} = - asn1_wrapper:decode('InfObj','InitiatingMessage',Bytes2), + roundtrip('RANAPextract1', 'InitiatingMessage', Val1), + roundtrip('InfObj', 'InitiatingMessage', Val1), Val2 = Val1#'InitiatingMessage'{procedureCode=2}, - - ?line {error,_R1} = - asn1_wrapper:encode('InfObj','InitiatingMessage',Val2), + {error,_R1} = 'InfObj':encode('InitiatingMessage', Val2), %% Test case for OTP-4275 @@ -59,10 +46,9 @@ main(_Erule) -> first=13, second=true}}, - ?line {ok,Bytes3} = - asn1_wrapper:encode('RANAPextract1','InitiatingMessage2',Val3), + roundtrip('RANAPextract1', 'InitiatingMessage2', Val3). - - ?line {ok,{'InitiatingMessage2',3,reject,{'Iu-ReleaseCommand',13,true}}}= - asn1_wrapper:decode('RANAPextract1','InitiatingMessage2',Bytes3). - +roundtrip(M, T, V) -> + {ok,Enc} = M:encode(T, V), + {ok,V} = M:decode(T, Enc), + ok. -- cgit v1.2.3 From 1aad483e439410aeee17c966991754f65f6852a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Feb 2013 09:24:41 +0100 Subject: UPER: Fix bug in encoding/decoding of default types The wrong backend was used. --- lib/asn1/src/asn1ct_gen_per.erl | 4 +-- lib/asn1/test/asn1_SUITE_data/InfObj.asn | 55 +++++++++++++++++++++++--------- lib/asn1/test/testInfObj.erl | 7 +++- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 9c82337135..7d6ceae8d6 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -454,7 +454,7 @@ gen_encode_default_call(ClassName,FieldName,Type) -> [#typedef{name=[FieldName,ClassName], typespec=Type}]; {primitive,bif} -> - gen_encode_prim(per,Type,"false","Val"), + gen_encode_prim(uper, Type, "false", "Val"), []; #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'enc_",Etype,"'(Val)",nl]), @@ -590,7 +590,7 @@ gen_decode_default_call(ClassName,FieldName,Bytes,Type) -> [#typedef{name=[FieldName,ClassName], typespec=Type}]; {primitive,bif} -> - gen_dec_prim(per,Type,Bytes), + gen_dec_prim(uper, Type, Bytes), []; #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]), diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj.asn b/lib/asn1/test/asn1_SUITE_data/InfObj.asn index 0a437e12df..abd49e64f3 100644 --- a/lib/asn1/test/asn1_SUITE_data/InfObj.asn +++ b/lib/asn1/test/asn1_SUITE_data/InfObj.asn @@ -39,21 +39,6 @@ RANAP-PDU ::= CHOICE { CLASS2 ::= RANAP-ELEMENTARY-PROCEDURE -MY-CLASS ::= CLASS { - &integerValue INTEGER UNIQUE, - &booleanValue BOOLEAN, - &stringValue PrintableString - } - -myobject MY-CLASS ::= { - &integerValue 12, - &booleanValue TRUE, - &stringValue "hejsan" - } -MyObjectSet MY-CLASS ::= { - myobject - } - InitiatingMessage ::= SEQUENCE { procedureCode RANAP-ELEMENTARY-PROCEDURE.&procedureCode ({RANAP-ELEMENTARY-PROCEDURES}), criticality RANAP-ELEMENTARY-PROCEDURE.&criticality ({RANAP-ELEMENTARY-PROCEDURES}{@procedureCode}), @@ -148,6 +133,46 @@ id-Iu-Release3 INTEGER ::= 3 id-Iu-Release4 INTEGER ::= 4 id-Iu-Release5 INTEGER ::= 5 +-- +-- MY-CLASS +-- + +Seq ::= SEQUENCE { + int INTEGER, + str OCTET STRING +} + +MY-CLASS ::= CLASS { + &Count DEFAULT INTEGER, + &integerValue INTEGER UNIQUE, + &booleanValue BOOLEAN, + &stringValue PrintableString +} + +myobject MY-CLASS ::= { + &integerValue 12, + &booleanValue TRUE, + &stringValue "hejsan" +} + +myotherobject MY-CLASS ::= { + &Count Seq, + &integerValue 42, + &booleanValue FALSE, + &stringValue "hoppsan" +} + +MyObjectSet MY-CLASS ::= { + myobject | myotherobject +} + +MyPdu ::= SEQUENCE { + count MY-CLASS.&Count ({MyObjectSet}{@int}), + int MY-CLASS.&integerValue ({MyObjectSet}), + bool MY-CLASS.&booleanValue ({MyObjectSet}{@int}), + str MY-CLASS.&stringValue ({MyObjectSet}{@int}) +} + END diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index 6318bf44b5..97e6a9aaa9 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -46,7 +46,12 @@ main(_Erule) -> first=13, second=true}}, - roundtrip('RANAPextract1', 'InitiatingMessage2', Val3). + roundtrip('RANAPextract1', 'InitiatingMessage2', Val3), + + roundtrip('InfObj', 'MyPdu', {'MyPdu',42,12,false,"string"}), + roundtrip('InfObj', 'MyPdu', {'MyPdu',{'Seq',1023,"hello"}, + 42,true,"longer string"}). + roundtrip(M, T, V) -> {ok,Enc} = M:encode(T, V), -- cgit v1.2.3 From d43c046f00f46413e747e0b5b95a488897e597ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Feb 2013 09:29:00 +0100 Subject: Remove unused function pgen/4 in all backends --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 13 ------------- lib/asn1/src/asn1ct_gen_per.erl | 13 +------------ lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 13 +------------ 3 files changed, 2 insertions(+), 37 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 70a5451237..d5e8b71f01 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -24,7 +24,6 @@ -include("asn1_records.hrl"). --export([pgen/4]). -export([decode_class/1, decode_type/1]). -export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]). -export([gen_encode_prim/4]). @@ -58,18 +57,6 @@ -define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed -define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed -%% pgen(Erules, Module, TypeOrVal) -%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module -%% .hrl file is only generated if necessary -%% Erules = per | ber -%% Module = atom() -%% TypeOrVal = {TypeList,ValueList,PTypeList} -%% TypeList = ValueList = [atom()] - -pgen(OutFile,Erules,Module,TypeOrVal) -> - asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,[],true). - - %%=============================================================================== %%=============================================================================== %%=============================================================================== diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 7d6ceae8d6..5503ddc528 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -26,7 +26,7 @@ %-compile(export_all). -export([gen_dec_imm/2]). --export([pgen/4,gen_dec_prim/3,gen_encode_prim/4]). +-export([gen_dec_prim/3,gen_encode_prim/4]). -export([gen_obj_code/3,gen_objectset_code/2]). -export([gen_decode/2, gen_decode/3]). -export([gen_encode/2, gen_encode/3]). @@ -37,17 +37,6 @@ -import(asn1ct_gen, [emit/1,demit/1]). -import(asn1ct_func, [call/3]). -%% pgen(Erules, Module, TypeOrVal) -%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module -%% .hrl file is only generated if necessary -%% Erules = per | ber -%% Module = atom() -%% TypeOrVal = {TypeList,ValueList} -%% TypeList = ValueList = [atom()] - -pgen(OutFile,Erules,Module,TypeOrVal) -> - asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,[],true). - %% Generate ENCODING ****************************** %%****************************************x diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 423d10ef14..d7c2a983bd 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -25,7 +25,7 @@ -include("asn1_records.hrl"). %-compile(export_all). --export([pgen/4,gen_dec_prim/3,gen_encode_prim/4]). +-export([gen_dec_prim/3,gen_encode_prim/4]). -export([gen_obj_code/3,gen_objectset_code/2]). -export([gen_decode/2, gen_decode/3]). -export([gen_encode/2, gen_encode/3]). @@ -36,17 +36,6 @@ get_class_fields/1,get_object_field/2]). -import(asn1ct_func, [call/3]). -%% pgen(Erules, Module, TypeOrVal) -%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module -%% .hrl file is only generated if necessary -%% Erules = per | ber -%% Module = atom() -%% TypeOrVal = {TypeList,ValueList} -%% TypeList = ValueList = [atom()] - -pgen(OutFile,Erules,Module,TypeOrVal) -> - asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,[],true). - %% Generate ENCODING ****************************** %%****************************************x -- cgit v1.2.3 From 82e02fa4ba16b0abd087437829e12049576f2a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Feb 2013 11:47:32 +0100 Subject: Eliminate duplicated code in gen_inlined_{dec,enc}_funs() --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 209 ++++++++++----------------------- lib/asn1/src/asn1ct_gen_per.erl | 110 ++++++----------- 2 files changed, 96 insertions(+), 223 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index d5e8b71f01..19f387bf2c 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -1144,78 +1144,34 @@ emit_default_getenc(ObjSetName,UniqueName) -> %% gen_inlined_enc_funs for each object iterates over all fields of a %% class, and for each typefield it checks if the object has that %% field and emits the proper code. -gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest], - ObjSetName,NthObj) -> - CurrMod = get(currmod), - InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]), - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl, - indent(6),"case Type of",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit([indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl, - indent(6),"case Type of",nl]), - emit([indent(9),{asis,Name}," ->",nl]), - if - M == CurrMod -> - emit([indent(12),"'enc_",T,"'(Val)"]); - true -> - #typedef{typespec=Type} = asn1_db:dbget(M,T), - OTag = Type#type.tag, -%% Tag = [encode_tag_val((decode_class(X#tag.class) bsl 10) + -%% X#tag.number) || -%% X <- OTag], - Tag = [encode_tag_val(decode_class(X#tag.class), - X#tag.form,X#tag.number) || - X <- OTag], - emit([indent(12),"'",M,"':'enc_",T,"'(Val, ",{asis,Tag},")"]) - end, - gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,[]); - false -> - %% This field was not present in the object thus there - %% were no type in the table and we therefore generate - %% code that returns the input for application treatment. - emit([indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl, - indent(6),"case Type of",nl, - indent(9),{asis,Name}," ->",nl, - indent(12),"Len = case Val of",nl, - indent(15),"B when is_binary(B) -> size(B);",nl, - indent(15),"_ -> length(Val)",nl, - indent(12),"end,",nl, - indent(12),"{Val,Len}"]), - gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,[]) - end; +gen_inlined_enc_funs(Fields, [{typefield,_,_}|_]=T, ObjSetName, NthObj) -> + emit([indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl, + indent(6),"case Type of",nl]), + gen_inlined_enc_funs1(Fields, T, ObjSetName, [], NthObj, []); gen_inlined_enc_funs(Fields,[_|Rest],ObjSetName,NthObj) -> gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj); gen_inlined_enc_funs(_,[],_,NthObj) -> {[],NthObj}. -gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName, - NthObj,Acc) -> +gen_inlined_enc_funs1(Fields, [{typefield,Name,_}|Rest], ObjSetName, + Sep0, NthObj, Acc0) -> + emit(Sep0), + Sep = [";",nl], CurrMod = get(currmod), InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]), - {Acc2,NAdd}= - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({";",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - {Ret++Acc,N}; - {value,{_,Type}} when is_record(Type,typedef) -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - {Ret++Acc,N}; - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), + {Acc,NAdd} = + case lists:keyfind(Name,1,Fields) of + {_,#type{}=Type} -> + {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName), + {Ret++Acc0,N}; + {_,#typedef{}=Type} -> + emit([indent(9),{asis,Name}," ->",nl]), + {Ret,N} = emit_inner_of_fun(Type, InternalDefFunName), + {Ret++Acc0,N}; + {_,#'Externaltypereference'{module=M,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl]), if - M == CurrMod -> + M =:= CurrMod -> emit([indent(12),"'enc_",T,"'(Val)"]); true -> #typedef{typespec=Type} = asn1_db:dbget(M,T), @@ -1223,27 +1179,30 @@ gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName, Tag = [encode_tag_val(decode_class(X#tag.class), X#tag.form,X#tag.number) || X <- OTag], - emit([indent(12),"'",M,"':'enc_",T,"'(Val, ",{asis,Tag},")"]) + emit([indent(12),"'",M,"':'enc_",T,"'(Val, ", + {asis,Tag},")"]) end, - {Acc,0}; + {Acc0,0}; false -> %% This field was not present in the object thus there %% were no type in the table and we therefore generate %% code that returns the input for application %% treatment. - emit([";",nl,indent(9),{asis,Name}," ->",nl]), - emit([indent(12),"Len = case Val of",nl, - indent(15),"Bin when is_binary(Bin) -> byte_size(Bin);",nl, - indent(15),"_ -> length(Val)",nl,indent(12),"end,",nl, + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"Len = case Val of",nl, + indent(15),"Bin when is_binary(Bin) -> " + "byte_size(Bin);",nl, + indent(15),"_ -> length(Val)",nl, + indent(12),"end,",nl, indent(12),"{Val,Len}"]), - {Acc,0} + {Acc0,0} end, - gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2); -gen_inlined_enc_funs1(Fields,[_|Rest],ObjSetName,NthObj,Acc)-> - gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc); -gen_inlined_enc_funs1(_,[],_,NthObj,Acc) -> - emit({nl,indent(6),"end",nl}), - emit({indent(3),"end"}), + gen_inlined_enc_funs1(Fields, Rest, ObjSetName, Sep, NthObj+NAdd, Acc); +gen_inlined_enc_funs1(Fields,[_|Rest], ObjSetName, Sep, NthObj, Acc)-> + gen_inlined_enc_funs1(Fields, Rest, ObjSetName, Sep, NthObj, Acc); +gen_inlined_enc_funs1(_, [], _, _, NthObj, Acc) -> + emit([nl,indent(6),"end",nl, + indent(3),"end"]), {Acc,NthObj}. emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type}, @@ -1352,79 +1311,32 @@ emit_default_getdec(ObjSetName,UniqueName) -> emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]), emit([indent(2), "fun(C,V,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]). -gen_inlined_dec_funs(Fields,[{typefield,Name,Prop}|Rest], - ObjSetName,NthObj) -> - DecProp = case Prop of - 'OPTIONAL' -> opt_or_default; - {'DEFAULT',_} -> opt_or_default; - _ -> mandatory - end, - CurrMod = get(currmod), - InternalDefFunName = [NthObj,Name,ObjSetName], - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->", - nl,indent(6),"case Type of",nl]), - N=emit_inner_of_decfun(Type,DecProp,InternalDefFunName), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); - {value,{_,Type}} when is_record(Type,typedef) -> - emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->", - nl,indent(6),"case Type of",nl]), - emit([indent(9),{asis,Name}," ->",nl]), - N=emit_inner_of_decfun(Type,DecProp,InternalDefFunName), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->", - nl,indent(6),"case Type of",nl]), - emit([indent(9),{asis,Name}," ->",nl]), - if - M == CurrMod -> - emit([indent(12),"'dec_",T,"'(Bytes)"]); - true -> - #typedef{typespec=Type} = asn1_db:dbget(M,T), - OTag = Type#type.tag, - Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || - X <- OTag], - emit([indent(12),"'",M,"':'dec_",T,"'(Bytes, ",{asis,Tag},")"]) - end, - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); - false -> - emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->", - nl,indent(6),"case Type of",nl, - indent(9),{asis,Name}," ->",nl, - indent(12),"Len = case Bytes of",nl, - indent(15),"B when is_binary(B) -> byte_size(B);",nl, - indent(15),"_ -> length(Bytes)",nl, - indent(12),"end,",nl, - indent(12),"{Bytes,[],Len}"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj) - end; -gen_inlined_dec_funs(Fields,[_H|Rest],ObjSetName,NthObj) -> - gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj); -gen_inlined_dec_funs(_,[],_,NthObj) -> - NthObj. +gen_inlined_dec_funs(Fields, ClFields, ObjSetName, NthObj) -> + emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->",nl, + indent(6),"case Type of",nl]), + gen_inlined_dec_funs1(Fields, ClFields, ObjSetName, "", NthObj). -gen_inlined_dec_funs1(Fields,[{typefield,Name,Prop}|Rest], - ObjSetName,NthObj) -> +gen_inlined_dec_funs1(Fields, [{typefield,Name,Prop}|Rest], + ObjSetName, Sep0, NthObj) -> + emit(Sep0), + Sep = [";",nl], DecProp = case Prop of 'OPTIONAL' -> opt_or_default; {'DEFAULT',_} -> opt_or_default; _ -> mandatory end, - CurrMod = get(currmod), InternalDefFunName = [NthObj,Name,ObjSetName], - N= - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit([";",nl]), + N = case lists:keyfind(Name, 1, Fields) of + {_,#type{}=Type} -> emit_inner_of_decfun(Type,DecProp,InternalDefFunName); - {value,{_,Type}} when is_record(Type,typedef) -> - emit([";",nl,indent(9),{asis,Name}," ->",nl]), + {_,#typedef{}=Type} -> + emit([indent(9),{asis,Name}," ->",nl]), emit_inner_of_decfun(Type,DecProp,InternalDefFunName); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit([";",nl,indent(9),{asis,Name}," ->",nl]), + {_,#'Externaltypereference'{module=M,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl]), + CurrMod = get(currmod), if - M == CurrMod -> + M =:= CurrMod -> emit([indent(12),"'dec_",T,"'(Bytes)"]); true -> #typedef{typespec=Type} = asn1_db:dbget(M,T), @@ -1435,21 +1347,20 @@ gen_inlined_dec_funs1(Fields,[{typefield,Name,Prop}|Rest], end, 0; false -> - emit([";",nl, - indent(9),{asis,Name}," ->",nl, + emit([indent(9),{asis,Name}," ->",nl, indent(12),"Len = case Bytes of",nl, - indent(15),"B when is_binary(B) -> size(B);",nl, + indent(15),"B when is_binary(B) -> byte_size(B);",nl, indent(15),"_ -> length(Bytes)",nl, indent(12),"end,",nl, indent(12),"{Bytes,[],Len}"]), 0 end, - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); -gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)-> - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); -gen_inlined_dec_funs1(_,[],_,NthObj) -> - emit([nl,indent(6),"end",nl]), - emit([indent(3),"end"]), + gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); +gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj)-> + gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj); +gen_inlined_dec_funs1(_, [], _, _, NthObj) -> + emit([nl,indent(6),"end",nl, + indent(3),"end"]), NthObj. emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},Prop, diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 5503ddc528..003ddfdb4e 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -697,89 +697,51 @@ emit_default_getenc(ObjSetName,UniqueName) -> %% gen_inlined_enc_funs for each object iterates over all fields of a %% class, and for each typefield it checks if the object has that %% field and emits the proper code. -gen_inlined_enc_funs(Erule,Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) -> - CurrMod = get(currmod), - InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]), - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj+N,Ret); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj+N,Ret); - {value,{_,#'Externaltypereference'{module=CurrMod,type=T}}} -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'enc_",T,"'(Val)"]), -% {Ret,N} = emit_inner_of_fun(TDef,InternalDefFunName), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'",M,"'",":'enc_",T,"'(Val)"]), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]); - false when Erule =:= uper -> - emit([indent(3),"fun(Type,Val,_) ->",nl, - indent(6),"case Type of",nl, - indent(9),{asis,Name}," -> Val",nl]), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]); - false -> - emit([indent(3),"fun(Type,Val,_) ->",nl, - indent(6),"case Type of",nl, - indent(9),{asis,Name}," -> [{octets,Val}]",nl]), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]) - end; +gen_inlined_enc_funs(Erule, Fields, [{typefield,_,_}|_]=T, + ObjSetName, NthObj) -> + emit([indent(3),"fun(Type, Val, _) ->",nl, + indent(6),"case Type of",nl]), + gen_inlined_enc_funs1(Erule, Fields, T, ObjSetName, [], NthObj, []); gen_inlined_enc_funs(Erule,Fields,[_H|Rest],ObjSetName,NthObj) -> gen_inlined_enc_funs(Erule,Fields,Rest,ObjSetName,NthObj); gen_inlined_enc_funs(_,_,[],_,NthObj) -> {[],NthObj}. -gen_inlined_enc_funs1(Erule,Fields,[{typefield,Name,_}|Rest],ObjSetName, - NthObj,Acc) -> +gen_inlined_enc_funs1(Erule, Fields, [{typefield,Name,_}|Rest], ObjSetName, + Sep0, NthObj, Acc0) -> + emit(Sep0), + Sep = [";",nl], CurrentMod = get(currmod), InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]), - {Acc2,NAdd}= - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({";",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - {Ret++Acc,N}; - {value,{_,Type}} when is_record(Type,typedef) -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName), - {Ret++Acc,N}; - {value,{_,#'Externaltypereference'{module=CurrentMod,type=T}}} -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'enc_",T,"'(Val)"]), - {Acc,0}; - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'",M,"'",":'enc_",T,"'(Val)"]), - {Acc,0}; + {Acc,NAdd} = + case lists:keyfind(Name, 1, Fields) of + {_,#type{}=Type} -> + {Ret,N} = emit_inner_of_fun(Type, InternalDefFunName), + {Ret++Acc0,N}; + {_,#typedef{}=Type} -> + emit([indent(9),{asis,Name}," ->",nl]), + {Ret,N} = emit_inner_of_fun(Type, InternalDefFunName), + {Ret++Acc0,N}; + {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"'enc_",T,"'(Val)"]), + {Acc0,0}; + {_,#'Externaltypereference'{module=M,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"'",M,"'",":'enc_",T,"'(Val)"]), + {Acc0,0}; false when Erule =:= uper -> - emit([";",nl, - indent(9),{asis,Name}," -> ",nl, - "Val",nl]), - {Acc,0}; - false -> - emit([";",nl, - indent(9),{asis,Name}," -> ",nl, - "[{octets,Val}]",nl]), - {Acc,0} + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"Val",nl]), + {Acc0,0} end, - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj+NAdd,Acc2); -gen_inlined_enc_funs1(Erule,Fields,[_H|Rest],ObjSetName,NthObj,Acc)-> - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,Acc); -gen_inlined_enc_funs1(_,_,[],_,NthObj,Acc) -> - emit({nl,indent(6),"end",nl}), - emit({indent(3),"end"}), + gen_inlined_enc_funs1(Erule, Fields, Rest, ObjSetName, Sep, + NthObj+NAdd, Acc); +gen_inlined_enc_funs1(Erule, Fields, [_|T], ObjSetName, Sep, NthObj, Acc)-> + gen_inlined_enc_funs1(Erule, Fields, T, ObjSetName, Sep, NthObj, Acc); +gen_inlined_enc_funs1(_, _, [], _, _, NthObj, Acc) -> + emit([nl,indent(6),"end",nl, + indent(3),"end"]), {Acc,NthObj}. emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type}, -- cgit v1.2.3 From 509ccd85ef448f5843716d4b51a8b07f875cdfda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Feb 2013 09:55:59 +0100 Subject: PER/UPER: Share all code except encoding of primitives The only code that is really different between the PER and UPER backends is encoding of primitive types. --- lib/asn1/src/asn1ct_gen.erl | 2 +- lib/asn1/src/asn1ct_gen_per.erl | 191 ++++--- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 878 +------------------------------ lib/asn1/test/asn1_SUITE_data/InfObj.asn | 9 +- lib/asn1/test/testInfObj.erl | 3 +- 5 files changed, 128 insertions(+), 955 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 76c4182160..c598dafc47 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -1902,7 +1902,7 @@ index2suffix(N) -> ct_gen_module(ber) -> asn1ct_gen_ber_bin_v2; ct_gen_module(per) -> - asn1ct_gen_per_rt2ct; + asn1ct_gen_per; ct_gen_module(uper) -> asn1ct_gen_per. diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 003ddfdb4e..f9681491df 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -30,8 +30,6 @@ -export([gen_obj_code/3,gen_objectset_code/2]). -export([gen_decode/2, gen_decode/3]). -export([gen_encode/2, gen_encode/3]). --export([is_already_generated/2,more_genfields/1,get_class_fields/1, - get_object_field/2]). -export([extaddgroup2sequence/1]). -import(asn1ct_gen, [emit/1,demit/1]). @@ -107,6 +105,19 @@ gen_encode_prim(Erules,D,DoTag) -> Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), gen_encode_prim(Erules,D,DoTag,Value). +gen_encode_prim(Erules, #type{def={'ENUMERATED',{N1,N2}}}, _, Value) -> + NewList = [{0,X} || {X,_} <- N1] ++ ['EXT_MARK'] ++ + [{1,X} || {X,_} <- N2], + NewC = {0,length(N1)-1}, + emit(["case ",Value," of",nl]), + emit_enc_enumerated_cases(Erules, NewC, NewList, 0); +gen_encode_prim(Erules, #type{def={'ENUMERATED',NNL}}, _, Value) -> + NewList = [X || {X,_} <- NNL], + NewC = {0,length(NewList)-1}, + emit(["case ",Value," of",nl]), + emit_enc_enumerated_cases(Erules, NewC, NewList, 0); +gen_encode_prim(per=Erules, D, DoTag, Value) -> + asn1ct_gen_per_rt2ct:gen_encode_prim(Erules, D, DoTag, Value); gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> Constraint = D#type.constraint, asn1ct_name:new(enumval), @@ -119,18 +130,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> Args = [{asis,asn1ct_imm:effective_constraint(integer,Constraint)}, Value,{asis,NamedNumberList}], call(Erules, encode_integer, Args); - {'ENUMERATED',{Nlist1,Nlist2}} -> - NewList = [{0,X} || {X,_} <- Nlist1] ++ ['EXT_MARK'] ++ - [{1,X} || {X,_} <- Nlist2], - NewC = {0,length(Nlist1)-1}, - emit(["case ",Value," of",nl]), - emit_enc_enumerated_cases(Erules, NewC, NewList, 0); - {'ENUMERATED',NamedNumberList} -> - NewList = [X || {X,_} <- NamedNumberList], - NewC = {0,length(NewList)-1}, - emit(["case ",Value," of",nl]), - emit_enc_enumerated_cases(Erules, NewC, NewList, 0); - 'REAL' -> emit_enc_real(Erules, Value); @@ -285,8 +284,8 @@ gen_obj_code(Erules,_Module,Obj) when is_record(Obj,typedef) -> gen_encode_constr_type(Erules,EncConstructed), emit(nl), DecConstructed = - gen_decode_objectfields(ClassName,get_class_fields(Class), - ObjName,Fields,[]), + gen_decode_objectfields(Erules, ClassName, get_class_fields(Class), + ObjName, Fields, []), emit(nl), gen_decode_constr_type(Erules,DecConstructed), emit(nl); @@ -294,8 +293,9 @@ gen_obj_code(_,_,Obj) when is_record(Obj,pobjectdef) -> ok. -gen_encode_objectfields(Erule,ClassName,[{typefield,Name,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> +gen_encode_objectfields(Erule, ClassName, + [{typefield,Name,OptOrMand}|Rest], + ObjName, ObjectFields, ConstrAcc) -> EmitFuncClause = fun(V) -> emit(["'enc_",ObjName,"'(",{asis,Name}, @@ -313,18 +313,24 @@ gen_encode_objectfields(Erule,ClassName,[{typefield,Name,OptOrMand}|Rest], case Erule of uper -> emit(" Val"); - _ -> - emit(" [{octets,Val}]") + per -> + emit([" if",nl, + " is_list(Val) ->",nl, + " NewVal = list_to_binary(Val),",nl, + " [20,byte_size(NewVal),NewVal];",nl, + " is_binary(Val) ->",nl, + " [20,byte_size(Val),Val]",nl, + " end"]) end, []; {false,{'DEFAULT',DefaultType}} -> EmitFuncClause("Val"), - gen_encode_default_call(ClassName,Name,DefaultType); + gen_encode_default_call(Erule, ClassName, Name, DefaultType); {{Name,TypeSpec},_} -> %% A specified field owerwrites any 'DEFAULT' or %% 'OPTIONAL' field in the class EmitFuncClause("Val"), - gen_encode_field_call(ObjName,Name,TypeSpec) + gen_encode_field_call(Erule, ObjName, Name, TypeSpec) end, case more_genfields(Rest) of true -> @@ -400,7 +406,7 @@ gen_encode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) -> gen_encode_constr_type(_,[]) -> ok. -gen_encode_field_call(_ObjName,_FieldName, +gen_encode_field_call(_Erules, _ObjName, _FieldName, #'Externaltypereference'{module=M,type=T}) -> CurrentMod = get(currmod), if @@ -411,12 +417,11 @@ gen_encode_field_call(_ObjName,_FieldName, emit({" '",M,"':'enc_",T,"'(Val)"}), [] end; -gen_encode_field_call(ObjName,FieldName,Type) -> +gen_encode_field_call(Erules, ObjName, FieldName, Type) -> Def = Type#typedef.typespec, case Type#typedef.name of {primitive,bif} -> - gen_encode_prim(uper,Def,"false", - "Val"), + gen_encode_prim(Erules, Def, "false", "Val"), []; {constructed,bif} -> emit({" 'enc_",ObjName,'_',FieldName, @@ -432,7 +437,7 @@ gen_encode_field_call(ObjName,FieldName,Type) -> [] end. -gen_encode_default_call(ClassName,FieldName,Type) -> +gen_encode_default_call(Erules, ClassName, FieldName, Type) -> CurrentMod = get(currmod), InnerType = asn1ct_gen:get_inner(Type#type.def), case asn1ct_gen:type(InnerType) of @@ -443,7 +448,7 @@ gen_encode_default_call(ClassName,FieldName,Type) -> [#typedef{name=[FieldName,ClassName], typespec=Type}]; {primitive,bif} -> - gen_encode_prim(uper, Type, "false", "Val"), + gen_encode_prim(Erules, Type, "false", "Val"), []; #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'enc_",Etype,"'(Val)",nl]), @@ -454,8 +459,9 @@ gen_encode_default_call(ClassName,FieldName,Type) -> end. -gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> +gen_decode_objectfields(Erules, ClassName, + [{typefield,Name,OptOrMand}|Rest], + ObjName, ObjectFields, ConstrAcc) -> EmitFuncClause = fun(Bytes) -> emit(["'dec_",ObjName,"'(",{asis,Name},",",Bytes, @@ -472,12 +478,13 @@ gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest], []; {false,{'DEFAULT',DefaultType}} -> EmitFuncClause("Bytes"), - gen_decode_default_call(ClassName,Name,"Bytes",DefaultType); + gen_decode_default_call(Erules, ClassName, Name, "Bytes", + DefaultType); {{Name,TypeSpec},_} -> %% A specified field owerwrites any 'DEFAULT' or %% 'OPTIONAL' field in the class EmitFuncClause("Bytes"), - gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec) + gen_decode_field_call(Erules, ObjName, Name, "Bytes", TypeSpec) end, case more_genfields(Rest) of true -> @@ -485,9 +492,11 @@ gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest], false -> emit([".",nl]) end, - gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc); -gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> + gen_decode_objectfields(Erules, ClassName, Rest, ObjName, + ObjectFields, MaybeConstr++ConstrAcc); +gen_decode_objectfields(Erules, ClassName, + [{objectfield,Name,_,_,OptOrMand}|Rest], + ObjName, ObjectFields, ConstrAcc) -> CurrentMod = get(currmod), EmitFuncClause = fun(Attrs) -> @@ -530,15 +539,16 @@ gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest], false -> emit([".",nl]) end, - gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc); -gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) -> - gen_decode_objectfields(CN,Cs,O,OF,CAcc); -gen_decode_objectfields(_,[],_,_,CAcc) -> + gen_decode_objectfields(Erules, ClassName, Rest, ObjName, + ObjectFields, ConstrAcc); +gen_decode_objectfields(Erules, CN, [_C|Cs], O, OF, CAcc) -> + gen_decode_objectfields(Erules, CN, Cs, O, OF, CAcc); +gen_decode_objectfields(_, _, [], _, _, CAcc) -> CAcc. -gen_decode_field_call(_ObjName,_FieldName,Bytes, +gen_decode_field_call(_Erules, _ObjName, _FieldName, Bytes, #'Externaltypereference'{module=M,type=T}) -> CurrentMod = get(currmod), if @@ -549,11 +559,11 @@ gen_decode_field_call(_ObjName,_FieldName,Bytes, emit([" '",M,"':'dec_",T,"'(",Bytes,", telltype)"]), [] end; -gen_decode_field_call(ObjName,FieldName,Bytes,Type) -> +gen_decode_field_call(Erules, ObjName, FieldName, Bytes, Type) -> Def = Type#typedef.typespec, case Type#typedef.name of {primitive,bif} -> - gen_dec_prim(uper, Def, Bytes), + gen_dec_prim(Erules, Def, Bytes), []; {constructed,bif} -> emit({" 'dec_",ObjName,'_',FieldName, @@ -569,7 +579,7 @@ gen_decode_field_call(ObjName,FieldName,Bytes,Type) -> [] end. -gen_decode_default_call(ClassName,FieldName,Bytes,Type) -> +gen_decode_default_call(Erules, ClassName, FieldName, Bytes, Type) -> CurrentMod = get(currmod), InnerType = asn1ct_gen:get_inner(Type#type.def), case asn1ct_gen:type(InnerType) of @@ -579,7 +589,7 @@ gen_decode_default_call(ClassName,FieldName,Bytes,Type) -> [#typedef{name=[FieldName,ClassName], typespec=Type}]; {primitive,bif} -> - gen_dec_prim(uper, Type, Bytes), + gen_dec_prim(Erules, Type, Bytes), []; #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]), @@ -641,7 +651,7 @@ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)-> ClassFields = (ClassDef#classdef.typespec)#objectclass.fields, InternalFuncs= gen_objset_enc(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassFields,1,[]), - gen_objset_dec(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1), + gen_objset_dec(Erules, ObjSetName,UniqueFName,Set,ClassName,ClassFields,1), gen_internal_funcs(Erules,InternalFuncs). %% gen_objset_enc iterates over the objects of the object set @@ -672,14 +682,30 @@ gen_objset_enc(Erule, ObjSetName, UniqueName, [{ObjName,Val,Fields}|T], emit({";",nl}), gen_objset_enc(Erule, ObjSetName, UniqueName, T, ClName, ClFields, NewNthObj, InternalFunc ++ Acc); -gen_objset_enc(Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, - _ClFields,_NthObj,Acc) -> +gen_objset_enc(uper, ObjSetName, _UniqueName, ['EXTENSIONMARK'], + _ClName, _ClFields, _NthObj, Acc) -> emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}), emit({indent(3),"fun(_, Val, _) ->",nl}), - uper = Erule, emit([indent(6),"Val",nl, indent(3),"end.",nl,nl]), Acc; +gen_objset_enc(per, ObjSetName, _UniqueName, ['EXTENSIONMARK'], + _ClName, _ClFields, _NthObj, Acc) -> + emit(["'getenc_",ObjSetName,"'(_, _) ->",nl, + indent(3),"fun(_, Val, _) ->",nl, + indent(6),"BinVal = if",nl, + indent(9),"is_list(Val) -> list_to_binary(Val);",nl, + indent(9),"true -> Val",nl, + indent(6),"end,",nl, + indent(6),"Size = byte_size(BinVal),",nl, + indent(6),"if",nl, + indent(9),"Size < 256 ->",nl, + indent(12),"[20,Size,BinVal];",nl, + indent(9),"true ->",nl, + indent(12),"[21,<>,Val]",nl, + indent(6),"end",nl, + indent(3),"end.",nl,nl]), + Acc; gen_objset_enc(_, ObjSetName, UniqueName, [], _, _, _, Acc) -> emit_default_getenc(ObjSetName, UniqueName), emit([".",nl,nl]), @@ -716,11 +742,11 @@ gen_inlined_enc_funs1(Erule, Fields, [{typefield,Name,_}|Rest], ObjSetName, {Acc,NAdd} = case lists:keyfind(Name, 1, Fields) of {_,#type{}=Type} -> - {Ret,N} = emit_inner_of_fun(Type, InternalDefFunName), + {Ret,N} = emit_inner_of_fun(Erule, Type, InternalDefFunName), {Ret++Acc0,N}; {_,#typedef{}=Type} -> emit([indent(9),{asis,Name}," ->",nl]), - {Ret,N} = emit_inner_of_fun(Type, InternalDefFunName), + {Ret,N} = emit_inner_of_fun(Erule, Type, InternalDefFunName), {Ret++Acc0,N}; {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> emit([indent(9),{asis,Name}," ->",nl, @@ -733,6 +759,17 @@ gen_inlined_enc_funs1(Erule, Fields, [{typefield,Name,_}|Rest], ObjSetName, false when Erule =:= uper -> emit([indent(9),{asis,Name}," ->",nl, indent(12),"Val",nl]), + {Acc0,0}; + false when Erule =:= per -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"Size = case Val of",nl, + indent(15),"B when is_binary(B) -> size(B);",nl, + indent(15),"_ -> length(Val)",nl, + indent(12),"end,",nl, + indent(12),"if",nl, + indent(15),"Size < 256 -> [20,Size,Val];",nl, + indent(15),"true -> [21,<>,Val]",nl, + indent(12),"end"]), {Acc0,0} end, gen_inlined_enc_funs1(Erule, Fields, Rest, ObjSetName, Sep, @@ -744,12 +781,12 @@ gen_inlined_enc_funs1(_, _, [], _, _, NthObj, Acc) -> indent(3),"end"]), {Acc,NthObj}. -emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type}, +emit_inner_of_fun(Erule, #typedef{name={ExtMod,Name},typespec=Type}=TDef, InternalDefFunName) -> case {ExtMod,Name} of {primitive,bif} -> emit(indent(12)), - gen_encode_prim(uper,Type,dotag,"Val"), + gen_encode_prim(Erule, Type, dotag, "Val"), {[],0}; {constructed,bif} -> emit([indent(12),"'enc_", @@ -759,15 +796,15 @@ emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type}, emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}), {[],0} end; -emit_inner_of_fun(#typedef{name=Name},_) -> +emit_inner_of_fun(_Erule, #typedef{name=Name}, _) -> emit({indent(12),"'enc_",Name,"'(Val)"}), {[],0}; -emit_inner_of_fun(Type,_) when is_record(Type,type) -> +emit_inner_of_fun(Erule, #type{}=Type, _) -> CurrMod = get(currmod), case Type#type.def of Def when is_atom(Def) -> emit({indent(9),Def," ->",nl,indent(12)}), - gen_encode_prim(erules,Type,dotag,"Val"); + gen_encode_prim(Erule, Type, dotag, "Val"); TRef when is_record(TRef,typereference) -> T = TRef#typereference.val, emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"}); @@ -783,11 +820,11 @@ indent(N) -> lists:duplicate(N,32). % 32 = space -gen_objset_dec(_,{unique,undefined},_,_,_,_) -> +gen_objset_dec(_, _, {unique,undefined}, _, _, _, _) -> %% There is no unique field in the class of this object set %% don't bother about the constraint ok; -gen_objset_dec(ObjSName, UniqueName, [{ObjName,Val,Fields}|T], ClName, +gen_objset_dec(Erule, ObjSName, UniqueName, [{ObjName,Val,Fields}|T], ClName, ClFields, NthObj)-> emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val}, ") ->",nl}), @@ -795,7 +832,8 @@ gen_objset_dec(ObjSName, UniqueName, [{ObjName,Val,Fields}|T], ClName, NewNthObj= case ObjName of {no_mod,no_name} -> - gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj); + gen_inlined_dec_funs(Erule, Fields, ClFields, + ObjSName, NthObj); {CurrMod,Name} -> emit([" fun 'dec_",Name,"'/4"]), NthObj; @@ -807,15 +845,15 @@ gen_objset_dec(ObjSName, UniqueName, [{ObjName,Val,Fields}|T], ClName, NthObj end, emit({";",nl}), - gen_objset_dec(ObjSName, UniqueName, T, ClName, ClFields, NewNthObj); -gen_objset_dec(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields, - _NthObj) -> + gen_objset_dec(Erule, ObjSName, UniqueName, T, ClName, ClFields, NewNthObj); +gen_objset_dec(_Erule, ObjSetName, _UniqueName, ['EXTENSIONMARK'], + _ClName, _ClFields, _NthObj) -> emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}), emit({indent(3),"fun(Attr1, Bytes, _,_) ->",nl}), emit({indent(6),"{Bytes,Attr1}",nl}), emit({indent(3),"end.",nl,nl}), ok; -gen_objset_dec(ObjSetName, UniqueName, [], _, _, _) -> +gen_objset_dec(_Erule, ObjSetName, UniqueName, [], _, _, _) -> emit_default_getdec(ObjSetName, UniqueName), emit([".",nl,nl]), ok. @@ -829,15 +867,16 @@ emit_default_getdec(ObjSetName,UniqueName) -> emit([indent(2), "fun(C,V,_,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]). -gen_inlined_dec_funs(Fields, List, ObjSetName, NthObj0) -> +gen_inlined_dec_funs(Erule, Fields, List, ObjSetName, NthObj0) -> emit([indent(3),"fun(Type, Val, _, _) ->",nl, indent(6),"case Type of",nl]), - NthObj = gen_inlined_dec_funs1(Fields, List, ObjSetName, "", NthObj0), + NthObj = gen_inlined_dec_funs1(Erule, Fields, List, + ObjSetName, "", NthObj0), emit([nl,indent(6),"end",nl, indent(3),"end"]), NthObj. -gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest], +gen_inlined_dec_funs1(Erule, Fields, [{typefield,Name,_}|Rest], ObjSetName, Sep0, NthObj) -> CurrentMod = get(currmod), InternalDefFunName = [NthObj,Name,ObjSetName], @@ -845,10 +884,10 @@ gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest], Sep = [";",nl], N = case lists:keyfind(Name, 1, Fields) of {_,#type{}=Type} -> - emit_inner_of_decfun(Type, InternalDefFunName); + emit_inner_of_decfun(Erule, Type, InternalDefFunName); {_,#typedef{}=Type} -> emit([indent(9),{asis,Name}," ->",nl]), - emit_inner_of_decfun(Type, InternalDefFunName); + emit_inner_of_decfun(Erule, Type, InternalDefFunName); {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> emit([indent(9),{asis,Name}," ->",nl, indent(12),"'dec_",T,"'(Val,telltype)"]), @@ -861,17 +900,17 @@ gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest], emit([indent(9),{asis,Name}," -> {Val,Type}"]), 0 end, - gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); -gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj) -> - gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj); -gen_inlined_dec_funs1(_, [], _, _, NthObj) -> NthObj. + gen_inlined_dec_funs1(Erule, Fields, Rest, ObjSetName, Sep, NthObj+N); +gen_inlined_dec_funs1(Erule, Fields, [_|Rest], ObjSetName, Sep, NthObj) -> + gen_inlined_dec_funs1(Erule, Fields, Rest, ObjSetName, Sep, NthObj); +gen_inlined_dec_funs1(_, _, [], _, _, NthObj) -> NthObj. -emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, +emit_inner_of_decfun(Erule, #typedef{name={ExtName,Name},typespec=Type}, InternalDefFunName) -> case {ExtName,Name} of {primitive,bif} -> emit(indent(12)), - gen_dec_prim(uper, Type, "Val"), + gen_dec_prim(Erule, Type, "Val"), 0; {constructed,bif} -> emit({indent(12),"'dec_", @@ -881,15 +920,15 @@ emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, emit({indent(12),"'",ExtName,"':'dec_",Name,"'(Val, telltype)"}), 0 end; -emit_inner_of_decfun(#typedef{name=Name},_) -> +emit_inner_of_decfun(_Erule, #typedef{name=Name}, _) -> emit({indent(12),"'dec_",Name,"'(Val, telltype)"}), 0; -emit_inner_of_decfun(Type,_) when is_record(Type,type) -> +emit_inner_of_decfun(Erule, #type{}=Type, _) -> CurrMod = get(currmod), case Type#type.def of Def when is_atom(Def) -> emit({indent(9),Def," ->",nl,indent(12)}), - gen_dec_prim(uper, Type, "Val"); + gen_dec_prim(Erule, Type, "Val"); TRef when is_record(TRef,typereference) -> T = TRef#typereference.val, emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"}); diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index d7c2a983bd..b0b0b37d47 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -19,88 +19,15 @@ %% -module(asn1ct_gen_per_rt2ct). -%% Generate erlang module which handles (PER) encode and decode for -%% all types in an ASN.1 module +%% Handle encoding of primitives for aligned PER. -include("asn1_records.hrl"). -%-compile(export_all). --export([gen_dec_prim/3,gen_encode_prim/4]). --export([gen_obj_code/3,gen_objectset_code/2]). --export([gen_decode/2, gen_decode/3]). --export([gen_encode/2, gen_encode/3]). --export([extaddgroup2sequence/1]). +-export([gen_encode_prim/4]). -import(asn1ct_gen, [emit/1,demit/1]). --import(asn1ct_gen_per, [is_already_generated/2,more_genfields/1, - get_class_fields/1,get_object_field/2]). -import(asn1ct_func, [call/3]). - -%% Generate ENCODING ****************************** -%%****************************************x - - -gen_encode(Erules,Type) when is_record(Type,typedef) -> - gen_encode_user(Erules,Type). - -gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) -> - NewTypename = [Cname|Typename], - gen_encode(Erules,NewTypename,Type); - -gen_encode(Erules,Typename,Type) when is_record(Type,type) -> - InnerType = asn1ct_gen:get_inner(Type#type.def), - ObjFun = - case lists:keysearch(objfun,1,Type#type.tablecinf) of - {value,{_,_Name}} -> - ", ObjFun"; - false -> - "" - end, - case asn1ct_gen:type(InnerType) of - {constructed,bif} -> - emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun, - ") ->",nl}), - asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type); - _ -> - true - end. - - -gen_encode_user(Erules,D) when is_record(D,typedef) -> - CurrMod = get(currmod), - Typename = [D#typedef.name], - Def = D#typedef.typespec, - InnerType = asn1ct_gen:get_inner(Def#type.def), - emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}), - case asn1ct_gen:type(InnerType) of - {primitive,bif} -> - gen_encode_prim(Erules,Def,"false"), - emit({".",nl}); - 'ASN1_OPEN_TYPE' -> - gen_encode_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"false"), - emit({".",nl}); - {constructed,bif} -> - asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D); - #'Externaltypereference'{module=CurrMod,type=Etype} -> - emit({"'enc_",Etype,"'(Val).",nl,nl}); - #'Externaltypereference'{module=Emod,type=Etype} -> - emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl}); - #typereference{val=Ename} -> - emit({"'enc_",Ename,"'(Val).",nl,nl}); - {notype,_} -> - emit({"'enc_",InnerType,"'(Val).",nl,nl}) - end. - - -gen_encode_prim(Erules,D,DoTag) -> - Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), - gen_encode_prim(Erules,D,DoTag,Value). - - - - - gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> Constraint = D#type.constraint, case D#type.def of @@ -115,8 +42,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> emit([" %%INTEGER with effective constraint: ", {asis,EffectiveConstr},nl]), emit_enc_integer_NNL(Erules,EffectiveConstr,Value,NamedNumberList); - {'ENUMERATED',_} -> - asn1ct_gen_per:gen_encode_prim(Erules, D, DoTag, Value); 'REAL' -> emit_enc_real(Erules, Value); @@ -547,802 +472,3 @@ no_bits(N) when N=<32 -> 5; no_bits(N) when N=<64 -> 6; no_bits(N) when N=<128 -> 7; no_bits(N) when N=<255 -> 8. - -%% Object code generating for encoding and decoding -%% ------------------------------------------------ - -gen_obj_code(Erules,_Module,Obj) when is_record(Obj,typedef) -> - ObjName = Obj#typedef.name, - Def = Obj#typedef.typespec, - #'Externaltypereference'{module=Mod,type=ClassName} = - Def#'Object'.classname, - Class = asn1_db:dbget(Mod,ClassName), - {object,_,Fields} = Def#'Object'.def, - emit({nl,nl,nl,"%%================================"}), - emit({nl,"%% ",ObjName}), - emit({nl,"%%================================",nl}), - EncConstructed = - gen_encode_objectfields(Erules,ClassName,get_class_fields(Class), - ObjName,Fields,[]), - emit(nl), - gen_encode_constr_type(Erules,EncConstructed), - emit(nl), - DecConstructed = - gen_decode_objectfields(ClassName,get_class_fields(Class), - ObjName,Fields,[]), - emit(nl), - gen_decode_constr_type(Erules,DecConstructed), - emit(nl); -gen_obj_code(_Erules,_Module,Obj) when is_record(Obj,pobjectdef) -> - ok. - -gen_encode_objectfields(Erules,ClassName,[{typefield,Name,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> - EmitFuncClause = - fun(V) -> - emit(["'enc_",ObjName,"'(",{asis,Name}, - ",",V,",_RestPrimFieldName) ->",nl]) - end, - - MaybeConstr = - case {get_object_field(Name,ObjectFields),OptOrMand} of - {false,'MANDATORY'} -> %% this case is illegal - exit({error,{asn1,{"missing mandatory field in object", - ObjName}}}); - {false,'OPTIONAL'} -> - EmitFuncClause("Val"), - emit([" if",nl, - " is_list(Val) ->",nl, - " NewVal = list_to_binary(Val),",nl, - " [20,byte_size(NewVal),NewVal];",nl, - " is_binary(Val) ->",nl, - " [20,byte_size(Val),Val]",nl, - " end"]), - []; - {false,{'DEFAULT',DefaultType}} -> - EmitFuncClause("Val"), - gen_encode_default_call(Erules,ClassName,Name,DefaultType); - {{Name,TypeSpec},_} -> - %% A specified field owerwrites any 'DEFAULT' or - %% 'OPTIONAL' field in the class - EmitFuncClause("Val"), - gen_encode_field_call(Erules,ObjName,Name,TypeSpec) - end, - case more_genfields(Rest) of - true -> - emit([";",nl]); - false -> - emit([".",nl]) - end, - gen_encode_objectfields(Erules,ClassName,Rest,ObjName,ObjectFields, - MaybeConstr++ConstrAcc); -gen_encode_objectfields(Erules,ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> - CurrentMod = get(currmod), - EmitFuncClause = - fun(Attrs) -> - emit(["'enc_",ObjName,"'(",{asis,Name}, - ",",Attrs,") ->",nl]) - end, -% emit(["'enc_",ObjName,"'(",{asis,Name}, -% ", Val,[H|T]) ->",nl]), - case {get_object_field(Name,ObjectFields),OptOrMand} of - {false,'MANDATORY'} -> - exit({error,{asn1,{"missing mandatory field in object", - ObjName}}}); - {false,'OPTIONAL'} -> - EmitFuncClause("_,_"), - emit([" exit({error,{'use of missing field in object', ",{asis,Name}, - "}})"]); - {false,{'DEFAULT',_DefaultObject}} -> - exit({error,{asn1,{"not implemented yet",Name}}}); - {{Name,#'Externalvaluereference'{module=CurrentMod, - value=TypeName}},_} -> - EmitFuncClause(" Val, [H|T]"), - emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"}); - {{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} -> - EmitFuncClause(" Val, [H|T]"), - emit({indent(3),"'",M,"':'enc_",TypeName,"'(H, Val, T)"}); - {{Name,TypeSpec},_} -> - EmitFuncClause("Val,[H|T]"), - case TypeSpec#typedef.name of - {ExtMod,TypeName} -> - emit({indent(3),"'",ExtMod,"':'enc_",TypeName, - "'(H, Val, T)"}); - TypeName -> - emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"}) - end - end, - case more_genfields(Rest) of - true -> - emit([";",nl]); - false -> - emit([".",nl]) - end, - gen_encode_objectfields(Erules,ClassName,Rest,ObjName,ObjectFields,ConstrAcc); -gen_encode_objectfields(Erules,ClassName,[_C|Cs],O,OF,Acc) -> - gen_encode_objectfields(Erules,ClassName,Cs,O,OF,Acc); -gen_encode_objectfields(_Erules,_,[],_,_,Acc) -> - Acc. - - - -gen_encode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) -> - case is_already_generated(enc,TypeDef#typedef.name) of - true -> ok; - _ -> - Name = lists:concat(["enc_",TypeDef#typedef.name]), - emit({Name,"(Val) ->",nl}), - Def = TypeDef#typedef.typespec, - InnerType = asn1ct_gen:get_inner(Def#type.def), - asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def), - gen_encode_constr_type(Erules,Rest) - end; -gen_encode_constr_type(_,[]) -> - ok. - -gen_encode_field_call(_Erule,_ObjName,_FieldName, - #'Externaltypereference'{module=M,type=T}) -> - CurrentMod = get(currmod), - if - M == CurrentMod -> - emit({" 'enc_",T,"'(Val)"}), - []; - true -> - emit({" '",M,"':'enc_",T,"'(Val)"}), - [] - end; -gen_encode_field_call(Erule,ObjName,FieldName,Type) -> - Def = Type#typedef.typespec, - case Type#typedef.name of - {primitive,bif} -> - gen_encode_prim(Erule,Def,"false", - "Val"), - []; - {constructed,bif} -> - emit({" 'enc_",ObjName,'_',FieldName, - "'(Val)"}), - [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}]; - {ExtMod,TypeName} -> - emit({" '",ExtMod,"':'enc_",TypeName, - "'(Val)"}), - []; - TypeName -> - emit({" 'enc_",TypeName,"'(Val)"}), - [] - end. - -gen_encode_default_call(Erules,ClassName,FieldName,Type) -> - CurrentMod = get(currmod), - InnerType = asn1ct_gen:get_inner(Type#type.def), - case asn1ct_gen:type(InnerType) of - {constructed,bif} -> -%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type); - emit([" 'enc_",ClassName,'_',FieldName,"'(Val)"]), - [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])), - typespec=Type}]; - {primitive,bif} -> - gen_encode_prim(Erules,Type,"false","Val"), - []; - #'Externaltypereference'{module=CurrentMod,type=Etype} -> - emit([" 'enc_",Etype,"'(Val)",nl]), - []; - #'Externaltypereference'{module=Emod,type=Etype} -> - emit([" '",Emod,"':'enc_",Etype,"'(Val)",nl]), - [] - end. - - - -gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> - EmitFuncClause = - fun(Bytes) -> - emit(["'dec_",ObjName,"'(",{asis,Name},",",Bytes, - ",_,_RestPrimFieldName) ->",nl]) - end, -% emit(["'dec_",ObjName,"'(",{asis,Name}, -% ", Bytes, _, RestPrimFieldName) ->",nl]), - MaybeConstr= - case {get_object_field(Name,ObjectFields),OptOrMand} of - {false,'MANDATORY'} -> %% this case is illegal - exit({error,{asn1,{"missing mandatory field in object", - ObjName}}}); - {false,'OPTIONAL'} -> - EmitFuncClause("Bytes"), - emit([" {Bytes,[]}"]), - []; - {false,{'DEFAULT',DefaultType}} -> - EmitFuncClause("Bytes"), - gen_decode_default_call(ClassName,Name,"Bytes",DefaultType); - {{Name,TypeSpec},_} -> - %% A specified field owerwrites any 'DEFAULT' or - %% 'OPTIONAL' field in the class - EmitFuncClause("Bytes"), - gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec) - end, - case more_genfields(Rest) of - true -> - emit([";",nl]); - false -> - emit([".",nl]) - end, - gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc); -gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest], - ObjName,ObjectFields,ConstrAcc) -> - CurrentMod = get(currmod), - EmitFuncClause = - fun(Attrs) -> - emit(["'dec_",ObjName,"'(",{asis,Name}, - ",",Attrs,") ->",nl]) - end, -% emit(["'dec_",ObjName,"'(",{asis,Name}, -% ", Bytes,_,[H|T]) ->",nl]), - case {get_object_field(Name,ObjectFields),OptOrMand} of - {false,'MANDATORY'} -> - exit({error,{asn1,{"missing mandatory field in object", - ObjName}}}); - {false,'OPTIONAL'} -> - EmitFuncClause("_,_,_"), - emit([" exit({error,{'illegal use of missing field in object', ",{asis,Name}, - "}})"]); - {false,{'DEFAULT',_DefaultObject}} -> - exit({error,{asn1,{"not implemented yet",Name}}}); - {{Name,#'Externalvaluereference'{module=CurrentMod, - value=TypeName}},_} -> - EmitFuncClause("Bytes,_,[H|T]"), - emit({indent(3),"'dec_",TypeName,"'(H, Bytes, telltype, T)"}); - {{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} -> - EmitFuncClause("Bytes,_,[H|T]"), - emit({indent(3),"'",M,"':'dec_",TypeName, - "'(H, Bytes, telltype, T)"}); - {{Name,TypeSpec},_} -> - EmitFuncClause("Bytes,_,[H|T]"), - case TypeSpec#typedef.name of - {ExtMod,TypeName} -> - emit({indent(3),"'",ExtMod,"':'dec_",TypeName, - "'(H, Bytes, telltype, T)"}); - TypeName -> - emit({indent(3),"'dec_",TypeName,"'(H, Bytes, telltype, T)"}) - end - end, - case more_genfields(Rest) of - true -> - emit([";",nl]); - false -> - emit([".",nl]) - end, - gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc); -gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) -> - gen_decode_objectfields(CN,Cs,O,OF,CAcc); -gen_decode_objectfields(_,[],_,_,CAcc) -> - CAcc. - - -gen_decode_field_call(_ObjName,_FieldName,Bytes, - #'Externaltypereference'{module=M,type=T}) -> - CurrentMod = get(currmod), - if - M == CurrentMod -> - emit([" 'dec_",T,"'(",Bytes,", telltype)"]), - []; - true -> - emit([" '",M,"':'dec_",T,"'(",Bytes,", telltype)"]), - [] - end; -gen_decode_field_call(ObjName,FieldName,Bytes,Type) -> - Def = Type#typedef.typespec, - case Type#typedef.name of - {primitive,bif} -> - gen_dec_prim(per,Def,Bytes), - []; - {constructed,bif} -> - emit({" 'dec_",ObjName,'_',FieldName, - "'(",Bytes,",telltype)"}), - [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}]; - {ExtMod,TypeName} -> - emit({" '",ExtMod,"':'dec_",TypeName, - "'(",Bytes,", telltype)"}), - []; - TypeName -> - emit({" 'dec_",TypeName,"'(",Bytes,", telltype)"}), - [] - end. - -gen_decode_default_call(ClassName,FieldName,Bytes,Type) -> - CurrentMod = get(currmod), - InnerType = asn1ct_gen:get_inner(Type#type.def), - case asn1ct_gen:type(InnerType) of - {constructed,bif} -> - emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,", telltype)"]), - [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])), - typespec=Type}]; - {primitive,bif} -> - gen_dec_prim(per,Type,Bytes), - []; - #'Externaltypereference'{module=CurrentMod,type=Etype} -> - emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]), - []; - #'Externaltypereference'{module=Emod,type=Etype} -> - emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", telltype)",nl]), - [] - end. - -%%%%%%%%%%%%%%% - - -gen_decode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) -> - case is_already_generated(dec,TypeDef#typedef.name) of - true -> ok; - _ -> - gen_decode(Erules,TypeDef) - end, - gen_decode_constr_type(Erules,Rest); -gen_decode_constr_type(_,[]) -> - ok. - -%% Object Set code generating for encoding and decoding -%% ---------------------------------------------------- -gen_objectset_code(Erules,ObjSet) -> - ObjSetName = ObjSet#typedef.name, - Def = ObjSet#typedef.typespec, -%% {ClassName,ClassDef} = Def#'ObjectSet'.class, - #'Externaltypereference'{module=ClassModule, - type=ClassName} = Def#'ObjectSet'.class, - ClassDef = asn1_db:dbget(ClassModule,ClassName), - UniqueFName = Def#'ObjectSet'.uniquefname, - Set = Def#'ObjectSet'.set, - emit({nl,nl,nl,"%%================================"}), - emit({nl,"%% ",ObjSetName}), - emit({nl,"%%================================",nl}), - case ClassName of - {_Module,ExtClassName} -> - gen_objset_code(Erules,ObjSetName,UniqueFName,Set, - ExtClassName,ClassDef); - _ -> - gen_objset_code(Erules,ObjSetName,UniqueFName,Set, - ClassName,ClassDef) - end, - emit(nl). - -gen_objset_code(Erule,ObjSetName,UniqueFName,Set,ClassName,ClassDef)-> - ClassFields = (ClassDef#classdef.typespec)#objectclass.fields, - InternalFuncs= - gen_objset_enc(Erule,ObjSetName,UniqueFName,Set,ClassName, - ClassFields,1,[]), - gen_objset_dec(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1), - gen_internal_funcs(Erule,InternalFuncs). - -gen_objset_enc(_Erule,_,{unique,undefined},_,_,_,_,_) -> - %% There is no unique field in the class of this object set - %% don't bother about the constraint - []; -gen_objset_enc(Erule, ObjSetName, UniqueName, [{ObjName,Val,Fields}|T], - ClName, ClFields, NthObj, Acc)-> - emit(["'getenc_",ObjSetName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl]), - CurrMod = get(currmod), - {InternalFunc,NewNthObj}= - case ObjName of - {no_mod,no_name} -> - gen_inlined_enc_funs(Erule, Fields, ClFields, - ObjSetName, NthObj); - {CurrMod,Name} -> - emit({" fun 'enc_",Name,"'/3"}), - {[],NthObj}; - {ModName,Name} -> - emit_ext_encfun(ModName,Name), - {[],NthObj}; - _ -> - emit({" fun 'enc_",ObjName,"'/3"}), - {[],NthObj} - end, - emit({";",nl}), - gen_objset_enc(Erule, ObjSetName, UniqueName, T, ClName, ClFields, - NewNthObj, InternalFunc++Acc); -gen_objset_enc(_Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, - _ClFields,_NthObj,Acc) -> - emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}), - emit({indent(3),"fun(_, Val, _) ->",nl}), - emit({indent(6),"BinVal = if",nl}), - emit({indent(9),"is_list(Val) -> list_to_binary(Val);",nl}), - emit({indent(9),"true -> Val",nl}), - emit({indent(6),"end,",nl}), - emit({indent(6),"Size = byte_size(BinVal),",nl}), - emit({indent(6),"if",nl}), - emit({indent(9),"Size < 256 ->",nl}), - emit({indent(12),"[20,Size,BinVal];",nl}), - emit({indent(9),"true ->",nl}), - emit({indent(12),"[21,<>,Val]",nl}), - emit({indent(6),"end",nl}), - emit({indent(3),"end.",nl,nl}), - Acc; -gen_objset_enc(_Erule, ObjSetName, UniqueName, [], _, _, _, Acc) -> - emit_default_getenc(ObjSetName, UniqueName), - emit([".",nl,nl]), - Acc. - -emit_ext_encfun(ModuleName,Name) -> - emit([indent(4),"fun(T,V,O) -> '",ModuleName,"':'enc_", - Name,"'(T,V,O) end"]). - -emit_default_getenc(ObjSetName,UniqueName) -> - emit(["'getenc_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]), - emit([indent(4),"fun(C,V,_) -> exit({'Type not compatible with table constraint',{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]). - - -%% gen_inlined_enc_funs for each object iterates over all fields of a -%% class, and for each typefield it checks if the object has that -%% field and emits the proper code. -gen_inlined_enc_funs(Erule,Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) -> - CurrMod = get(currmod), - InternalDefFunName=asn1ct_gen:list2name([NthObj,Name,ObjSetName]), - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - {Ret,N}=emit_inner_of_fun(Erule,Type,InternalDefFunName), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj+N,Ret); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - {Ret,N}=emit_inner_of_fun(Erule,Type,InternalDefFunName), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj+N,Ret); - {value,{_,#'Externaltypereference'{module=CurrMod,type=T}}} -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'enc_",T,"'(Val)"]), -% {Ret,N} = emit_inner_of_fun(Erule,TDef,InternalDefFunName), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'",M,"'",":'enc_",T,"'(Val)"]), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]); - false -> - emit([indent(3),"fun(Type, Val, _) ->",nl, - indent(6),"case Type of",nl, - indent(9),{asis,Name}," ->",nl, - indent(12),"Size = case Val of",nl, - indent(15),"B when is_binary(B) -> size(B);",nl, - indent(15),"_ -> length(Val)",nl, - indent(12),"end,",nl, - indent(12),"if",nl, - indent(15),"Size < 256 -> [20,Size,Val];",nl, - indent(15),"true -> [21,<>,Val]",nl, - indent(12),"end"]), - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]) - end; -gen_inlined_enc_funs(Erule,Fields,[_|Rest],ObjSetName,NthObj) -> - gen_inlined_enc_funs(Erule,Fields,Rest,ObjSetName,NthObj); -gen_inlined_enc_funs(_Erule,_,[],_,NthObj) -> - {[],NthObj}. - -gen_inlined_enc_funs1(Erule,Fields,[{typefield,Name,_}|Rest],ObjSetName, - NthObj,Acc) -> - CurrentMod = get(currmod), - InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]), - {Acc2,NAdd}= - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({";",nl}), - {Ret,N}=emit_inner_of_fun(Erule,Type,InternalDefFunName), - {Ret++Acc,N}; - {value,{_,Type}} when is_record(Type,typedef) -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - {Ret,N}=emit_inner_of_fun(Erule,Type,InternalDefFunName), - {Ret++Acc,N}; - {value,{_,#'Externaltypereference'{module=CurrentMod,type=T}}} -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'enc_",T,"'(Val)"]), - {Acc,0}; - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'",M,"'",":'enc_",T,"'(Val)"]), - {Acc,0}; - false -> - emit([";",nl, - indent(9),{asis,Name}," ->",nl, - indent(12),"Size = case Val of",nl, - indent(15),"B when is_binary(B) -> size(B);",nl, - indent(15),"_ -> length(Val)",nl, - indent(12),"end,",nl, - indent(12),"if",nl, - indent(15),"Size < 256 -> [20,Size,Val];",nl, - indent(15),"true -> [21,<>,Val]",nl, - indent(12),"end"]), - {Acc,0} - end, - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj+NAdd,Acc2); -gen_inlined_enc_funs1(Erule,Fields,[_|Rest],ObjSetName,NthObj,Acc)-> - gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,Acc); -gen_inlined_enc_funs1(_Erule,_,[],_,NthObj,Acc) -> - emit({nl,indent(6),"end",nl}), - emit({indent(3),"end"}), - {Acc,NthObj}. - -emit_inner_of_fun(Erule,TDef=#typedef{name={ExtMod,Name},typespec=Type}, - InternalDefFunName) -> - case {ExtMod,Name} of - {primitive,bif} -> - emit(indent(12)), - gen_encode_prim(Erule,Type,dotag,"Val"), - {[],0}; - {constructed,bif} -> - emit([indent(12),"'enc_", - InternalDefFunName,"'(Val)"]), - {[TDef#typedef{name=InternalDefFunName}],1}; - _ -> - emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}), - {[],0} - end; -emit_inner_of_fun(_Erule,#typedef{name=Name},_) -> - emit({indent(12),"'enc_",Name,"'(Val)"}), - {[],0}; -emit_inner_of_fun(Erule,Type,_) when is_record(Type,type) -> - CurrMod = get(currmod), - case Type#type.def of - Def when is_atom(Def) -> - emit({indent(9),Def," ->",nl,indent(12)}), - gen_encode_prim(Erule,Type,dotag,"Val"); - TRef when is_record(TRef,typereference) -> - T = TRef#typereference.val, - emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"}); - #'Externaltypereference'{module=CurrMod,type=T} -> - emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"}); - #'Externaltypereference'{module=ExtMod,type=T} -> - emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_", - T,"'(Val)"}) - end, - {[],0}. - -indent(N) -> - lists:duplicate(N,32). % 32 = space - - -gen_objset_dec(_,{unique,undefined},_,_,_,_) -> - %% There is no unique field in the class of this object set - %% don't bother about the constraint - ok; -gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName, - ClFields,NthObj)-> - - emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl}), - CurrMod = get(currmod), - NewNthObj= - case ObjName of - {no_mod,no_name} -> - gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj); - {CurrMod,Name} -> - emit([" fun 'dec_",Name,"'/4"]), - NthObj; - {ModName,Name} -> - emit_ext_decfun(ModName,Name), -% emit([" {'",ModName,"', 'dec_",Name,"'}"]), - NthObj; - _ -> - emit({" fun 'dec_",ObjName,"'/4"}), - NthObj - end, - emit({";",nl}), - gen_objset_dec(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NewNthObj); -gen_objset_dec(ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName, - ClFields,NthObj) -> - - emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",", - {asis,Val},") ->",nl}), - CurrMod=get(currmod), - case ObjName of - {no_mod,no_name} -> - gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj); - {CurrMod,Name} -> - emit([" fun 'dec_",Name,"'/4"]); - {ModName,Name} -> - emit_ext_decfun(ModName,Name); -% emit([" {'",ModName,"', 'dec_",Name,"'}"]); - _ -> - emit({" fun 'dec_",ObjName,"'/4"}) - end, - emit([";",nl]), - emit_default_getdec(ObjSetName,UniqueName), - emit({".",nl,nl}), - ok; -gen_objset_dec(ObjSetName,_,['EXTENSIONMARK'],_ClName,_ClFields, - _NthObj) -> - emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}), - emit({indent(3),"fun(Attr1, Bytes, _, _) ->",nl}), - emit({indent(6),"{Bytes,Attr1}",nl}), - emit({indent(3),"end.",nl,nl}), - ok; -gen_objset_dec(_,_,[],_,_,_) -> - ok. - -emit_ext_decfun(ModuleName,Name) -> - emit([indent(3),"fun(T,V,O1,O2) -> '",ModuleName,"':'dec_", - Name,"'(T,V,O1,O2) end"]). - -emit_default_getdec(ObjSetName,UniqueName) -> - emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]), - emit([indent(2), "fun(C,V,_,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]). - - -gen_inlined_dec_funs(Fields, List, ObjSetName, NthObj0) -> - emit([indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl]), - NthObj = gen_inlined_dec_funs1(Fields, List, ObjSetName, "", NthObj0), - emit([nl,indent(6),"end",nl, - indent(3),"end"]), - NthObj. - -gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest], - ObjSetName, Sep0, NthObj) -> - CurrentMod = get(currmod), - InternalDefFunName = [NthObj,Name,ObjSetName], - emit(Sep0), - Sep = [";",nl], - N = case lists:keyfind(Name, 1, Fields) of - {_,#type{}=Type} -> - emit_inner_of_decfun(Type, InternalDefFunName); - {_,#typedef{}=Type} -> - emit([indent(9),{asis,Name}," ->",nl]), - emit_inner_of_decfun(Type, InternalDefFunName); - {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"'dec_",T,"'(Val,telltype)"]), - 0; - {_,#'Externaltypereference'{module=M,type=T}} -> - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]), - 0; - false -> - emit([indent(9),{asis,Name}," -> {Val,Type}"]), - 0 - end, - gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); -gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj) -> - gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj); -gen_inlined_dec_funs1(_, [], _, _, NthObj) -> NthObj. - -emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, - InternalDefFunName) -> - case {ExtName,Name} of - {primitive,bif} -> - emit(indent(12)), - gen_dec_prim(per,Type,"Val"), - 0; - {constructed,bif} -> - emit({indent(12),"'dec_", - asn1ct_gen:list2name(InternalDefFunName),"'(Val)"}), - 1; - _ -> - emit({indent(12),"'",ExtName,"':'dec_",Name, - "'(Val, telltype)"}), - 0 - end; -emit_inner_of_decfun(#typedef{name=Name},_) -> - emit({indent(12),"'dec_",Name,"'(Val, telltype)"}), - 0; -emit_inner_of_decfun(Type,_) when is_record(Type,type) -> - CurrMod = get(currmod), - case Type#type.def of - Def when is_atom(Def) -> - emit({indent(9),Def," ->",nl,indent(12)}), - gen_dec_prim(per, Type, "Val"); - TRef when is_record(TRef,typereference) -> - T = TRef#typereference.val, - emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"}); - #'Externaltypereference'{module=CurrMod,type=T} -> - emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"}); - #'Externaltypereference'{module=ExtMod,type=T} -> - emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_", - T,"'(Val)"}) - end, - 0. - - -gen_internal_funcs(_Erules,[]) -> - ok; -gen_internal_funcs(Erules,[TypeDef|Rest]) -> - gen_encode_user(Erules,TypeDef), - emit([nl,nl,"'dec_",TypeDef#typedef.name,"'(Bytes) ->",nl]), - gen_decode_user(Erules,TypeDef), - gen_internal_funcs(Erules,Rest). - - - -%% DECODING ***************************** -%%*************************************** - - -gen_decode(Erules,Type) when is_record(Type,typedef) -> - D = Type, - emit({nl,nl}), - emit({"'dec_",Type#typedef.name,"'(Bytes,_) ->",nl}), - dbdec(Type#typedef.name), - gen_decode_user(Erules,D). - -gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) -> - NewTname = [Cname|Tname], - gen_decode(Erules,NewTname,Type); - -gen_decode(Erules,Typename,Type) when is_record(Type,type) -> - InnerType = asn1ct_gen:get_inner(Type#type.def), - case asn1ct_gen:type(InnerType) of - {constructed,bif} -> - ObjFun = - case Type#type.tablecinf of - [{objfun,_}|_R] -> - ", ObjFun"; - _ -> - "" - end, - emit({nl,"'dec_",asn1ct_gen:list2name(Typename), - "'(Bytes,_",ObjFun,") ->",nl}), - dbdec(Typename), - asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type); - _ -> - true - end. - -dbdec(Type) when is_list(Type)-> - demit({"io:format(\"decoding: ",asn1ct_gen:list2name(Type),"~w~n\",[Bytes]),",nl}); -dbdec(Type) -> - demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}). - -gen_decode_user(Erules,D) when is_record(D,typedef) -> - CurrMod = get(currmod), - Typename = [D#typedef.name], - Def = D#typedef.typespec, - InnerType = asn1ct_gen:get_inner(Def#type.def), - case asn1ct_gen:type(InnerType) of - {primitive,bif} -> - gen_dec_prim(Erules,Def,"Bytes"), - emit({".",nl,nl}); - 'ASN1_OPEN_TYPE' -> - gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"), - emit({".",nl,nl}); - {constructed,bif} -> - asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D); - #typereference{val=Dname} -> - emit({"'dec_",Dname,"'(Bytes,telltype)"}), - emit({".",nl,nl}); - #'Externaltypereference'{module=CurrMod,type=Etype} -> - emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl}); - #'Externaltypereference'{module=Emod,type=Etype} -> - emit({"'",Emod,"':'dec_",Etype,"'(Bytes,telltype).",nl,nl}); - Other -> - exit({error,{asn1,{unknown,Other}}}) - end. - - - -gen_dec_prim(Erules, Att, BytesVar) -> - asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar). - -%% For PER the ExtensionAdditionGroup notation has significance for the encoding and decoding -%% the components within the ExtensionAdditionGroup is treated in a similar way as if they -%% have been specified within a SEQUENCE, therefore we construct a fake sequence type here -%% so that we can generate code for it -extaddgroup2sequence(ExtList) -> - extaddgroup2sequence(ExtList,0,[]). - -extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],ExtNum,Acc) -> - Number = case Number0 of undefined -> 1; _ -> Number0 end, - {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} = - lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T), - extaddgroup2sequence(T2,ExtNum+1, - [#'ComponentType'{ - name=list_to_atom("ExtAddGroup"++ - integer_to_list(ExtNum+1)), - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=Number, - components=ExtGroupComps}}, - prop='OPTIONAL'}|Acc]); -extaddgroup2sequence([C|T],ExtNum,Acc) -> - extaddgroup2sequence(T,ExtNum,[C|Acc]); -extaddgroup2sequence([],_,Acc) -> - lists:reverse(Acc). diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj.asn b/lib/asn1/test/asn1_SUITE_data/InfObj.asn index abd49e64f3..ff11b36788 100644 --- a/lib/asn1/test/asn1_SUITE_data/InfObj.asn +++ b/lib/asn1/test/asn1_SUITE_data/InfObj.asn @@ -163,7 +163,14 @@ myotherobject MY-CLASS ::= { } MyObjectSet MY-CLASS ::= { - myobject | myotherobject + myobject | myotherobject | + { + -- Each character will be encoded in 3 bits in UPER, 4 bits in PER. + &Count NumericString (FROM("01234567") ^ SIZE(8)), + &integerValue 43, + &booleanValue TRUE, + &stringValue "tjosan" + } } MyPdu ::= SEQUENCE { diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index 97e6a9aaa9..c0f86eab60 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -50,7 +50,8 @@ main(_Erule) -> roundtrip('InfObj', 'MyPdu', {'MyPdu',42,12,false,"string"}), roundtrip('InfObj', 'MyPdu', {'MyPdu',{'Seq',1023,"hello"}, - 42,true,"longer string"}). + 42,true,"longer string"}), + roundtrip('InfObj', 'MyPdu', {'MyPdu',"75712346",43,true,"string"}). roundtrip(M, T, V) -> -- cgit v1.2.3 From ab7f7f53dc75cdf6ff05abd5663b1c2add39963c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Feb 2013 15:19:13 +0100 Subject: Eliminate general use of #typereference{} The record #typereference{} is only used internally within the asn1ct_parser2 module (the parser translates it to an #'Externaltypereference{} record). --- lib/asn1/src/asn1_records.hrl | 1 - lib/asn1/src/asn1ct_check.erl | 27 +++----------------------- lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 2 -- lib/asn1/src/asn1ct_constructed_per.erl | 8 -------- lib/asn1/src/asn1ct_gen.erl | 3 --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 8 -------- lib/asn1/src/asn1ct_gen_per.erl | 11 ----------- lib/asn1/src/asn1ct_parser2.erl | 3 +++ 8 files changed, 6 insertions(+), 57 deletions(-) diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl index 16d14c2e7b..1dfe329f3b 100644 --- a/lib/asn1/src/asn1_records.hrl +++ b/lib/asn1/src/asn1_records.hrl @@ -45,7 +45,6 @@ -record(pobjectdef,{checked=false,pos,name,args,class,def}). -record(pobjectsetdef,{checked=false,pos,name,args,class,def}). --record(typereference,{pos,val}). -record(identifier,{pos,val}). -record(constraint,{c,e}). -record('Constraint',{'SingleValue'=no,'SizeConstraint'=no,'ValueRange'=no,'PermittedAlphabet'=no, diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 0622998445..1c6b0525b8 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -695,8 +695,7 @@ check_class_fields(S,[F|Fields],Acc) -> Type2 = maybe_unchecked_OCFT(S,Type), Cat = case asn1ct_gen:type(asn1ct_gen:get_inner(Type2#type.def)) of - Def when is_record(Def,typereference); - is_record(Def,'Externaltypereference') -> + Def when is_record(Def,'Externaltypereference') -> {_,D} = get_referenced_type(S,Def), D; {undefined,user} -> @@ -1233,8 +1232,7 @@ check_object_list(S,ClassRef,[ObjOrSet|Objs],Acc) -> ObjSet when is_record(ObjSet,type) -> ObjSetDef = case ObjSet#type.def of - Ref when is_record(Ref,typereference); - is_record(Ref,'Externaltypereference') -> + Ref when is_record(Ref,'Externaltypereference') -> {_,D} = get_referenced_type(S,ObjSet#type.def), D; Other -> @@ -3535,10 +3533,6 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))}; %% This is a temporary hack until the full Information Obj Spec %% in X.681 is supported - {{typereference,_,'TYPE-IDENTIFIER'},[{typefieldreference,_,'Type'}]} -> - Ct=maybe_illicit_implicit_tag(open_type,Tag), - TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct}; - {#'Externaltypereference'{type='TYPE-IDENTIFIER'}, [{typefieldreference,_,'Type'}]} -> Ct=maybe_illicit_implicit_tag(open_type,Tag), @@ -4925,19 +4919,7 @@ get_referenced(S,Emod,Ename,Pos) -> end; T when is_record(T,typedef) -> ?dbg("get_referenced T: ~p~n",[T]), - Spec = T#typedef.typespec, %% XXXX Spec may be something else than #type - case Spec of - #type{def=#typereference{}} -> - Tref = Spec#type.def, - Def = #'Externaltypereference'{module=Emod, - type=Tref#typereference.val, - pos=Tref#typereference.pos}, - - - {Emod,T#typedef{typespec=Spec#type{def=Def}}}; - _ -> - {Emod,T} % should add check that T is exported here - end; + {Emod,T}; % should add check that T is exported here V -> ?dbg("get_referenced V: ~p~n",[V]), {Emod,V} @@ -6875,9 +6857,6 @@ get_OCFType(S,Fields,[PrimFieldName|Rest]) -> get_taglist(S,Ext) when is_record(Ext,'Externaltypereference') -> {_,T} = get_referenced_type(S,Ext), get_taglist(S,T#typedef.typespec); -get_taglist(S,Tref) when is_record(Tref,typereference) -> - {_,T} = get_referenced_type(S,Tref), - get_taglist(S,T#typedef.typespec); get_taglist(S,Type) when is_record(Type,type) -> case Type#type.tag of [] -> diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 95086da7b1..6f4fd2520e 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -1455,8 +1455,6 @@ print_attribute_comment(InnerType,Pos,Cname,Prop) -> CommentLine = "%%-------------------------------------------------", emit([nl,CommentLine]), case InnerType of - {typereference,_,Name} -> - emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",Name]); {'Externaltypereference',_,XModule,Name} -> emit([nl,"%% attribute ",Cname,"(",Pos,") External ",XModule,":",Name]); _ -> diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 02947ed6ea..8c181aa2f0 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -675,8 +675,6 @@ gen_decode_sof_components(Erule,Typename,SeqOrSetOf,Cont) -> NewTypename = [Constructed_Suffix|Typename], emit({"'dec_",asn1ct_gen:list2name(NewTypename), "'(Bytes, telltype",ObjFun,"),",nl}); - #typereference{val=Dname} -> - emit({"'dec_",Dname,"'(Bytes,telltype),",nl}); #'Externaltypereference'{module=CurrMod,type=EType} -> emit({"'dec_",EType,"'(Bytes,telltype),",nl}); #'Externaltypereference'{module=EMod,type=EType} -> @@ -1055,8 +1053,6 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> #'Externaltypereference'{module=Mod,type=EType} -> emit({"'",Mod,"':'enc_", EType,"'(",Element,")"}); - #typereference{val=Ename} -> - emit({"'enc_",Ename,"'(",Element,")"}); {notype,_} -> emit({"'enc_",Atype,"'(",Element,")"}); {primitive,bif} -> @@ -1541,10 +1537,6 @@ gen_dec_line_other(Erule, Atype, TopType, Comp) -> _ -> asn1ct_gen_per:gen_dec_imm(Erule, Type) end; - #typereference{val=Dname} -> - fun(BytesVar) -> - emit({"'dec_",Dname,"'(",BytesVar,",telltype)"}) - end; {notype,_} -> fun(BytesVar) -> emit({"'dec_",Atype,"'(",BytesVar,",telltype)"}) diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index c598dafc47..956497a3d5 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -1671,7 +1671,6 @@ unify_if_string(PrimType) -> get_inner(A) when is_atom(A) -> A; get_inner(Ext) when is_record(Ext,'Externaltypereference') -> Ext; -get_inner(Tref) when is_record(Tref,typereference) -> Tref; get_inner({fixedtypevaluefield,_,Type}) -> if is_record(Type,type) -> @@ -1704,8 +1703,6 @@ get_inner(T) when is_tuple(T) -> type(X) when is_record(X,'Externaltypereference') -> X; -type(X) when is_record(X,typereference) -> - X; type('ASN1_OPEN_TYPE') -> 'ASN1_OPEN_TYPE'; type({fixedtypevaluefield,_Name,Type}) when is_record(Type,type) -> diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 19f387bf2c..3a98cbb452 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -141,8 +141,6 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> {primitive,bif} -> gen_encode_prim(ber,Type,"TagIn","Val"), emit([".",nl]); - #typereference{val=Ename} -> - emit([" 'enc_",Ename,"'(Val, TagIn).",nl]); #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'enc_",Etype,"'(Val, TagIn).",nl]); #'Externaltypereference'{module=Emod,type=Etype} -> @@ -1241,10 +1239,6 @@ emit_inner_of_fun(Type,_) when is_record(Type,type) -> X#tag.form,X#tag.number)||X <- OTag], emit([indent(9),Def," ->",nl,indent(12)]), gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val"); - TRef when is_record(TRef,typereference) -> - T = TRef#typereference.val, - emit([indent(9),T," ->",nl,indent(12),"'enc_",T, - "'(Val)"]); #'Externaltypereference'{module=CurrMod,type=T} -> emit([indent(9),T," ->",nl,indent(12),"'enc_",T, "'(Val)"]); @@ -1481,8 +1475,6 @@ mkfuncname(WhatKind,DecOrEnc) -> % io:format("CurrMod: ~p, Mod: ~p~n",[CurrMod,Mod]), lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"]) end; - #'typereference'{val=EType} -> - lists:concat(["'",DecOrEnc,"_",EType,"'"]); 'ASN1_OPEN_TYPE' -> lists:concat(["'",DecOrEnc,"_",WhatKind,"'"]) diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index f9681491df..2ffbe87ab2 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -94,8 +94,6 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> emit({"'enc_",Etype,"'(Val).",nl,nl}); #'Externaltypereference'{module=Emod,type=Etype} -> emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl}); - #typereference{val=Ename} -> - emit({"'enc_",Ename,"'(Val).",nl,nl}); {notype,_} -> emit({"'enc_",InnerType,"'(Val).",nl,nl}) end. @@ -805,9 +803,6 @@ emit_inner_of_fun(Erule, #type{}=Type, _) -> Def when is_atom(Def) -> emit({indent(9),Def," ->",nl,indent(12)}), gen_encode_prim(Erule, Type, dotag, "Val"); - TRef when is_record(TRef,typereference) -> - T = TRef#typereference.val, - emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"}); #'Externaltypereference'{module=CurrMod,type=T} -> emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"}); #'Externaltypereference'{module=ExtMod,type=T} -> @@ -929,9 +924,6 @@ emit_inner_of_decfun(Erule, #type{}=Type, _) -> Def when is_atom(Def) -> emit({indent(9),Def," ->",nl,indent(12)}), gen_dec_prim(Erule, Type, "Val"); - TRef when is_record(TRef,typereference) -> - T = TRef#typereference.val, - emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"}); #'Externaltypereference'{module=CurrMod,type=T} -> emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"}); #'Externaltypereference'{module=ExtMod,type=T} -> @@ -1004,9 +996,6 @@ gen_decode_user(Erules,D) when is_record(D,typedef) -> emit({".",nl,nl}); {constructed,bif} -> asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D); - #typereference{val=Dname} -> - emit({"'dec_",Dname,"'(Bytes,telltype)"}), - emit({".",nl,nl}); #'Externaltypereference'{module=CurrMod,type=Etype} -> emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl}); #'Externaltypereference'{module=Emod,type=Etype} -> diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index 9e1fcce2b1..f136f90131 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -23,6 +23,9 @@ -export([parse/1]). -include("asn1_records.hrl"). +%% Only used internally within this module. +-record(typereference, {pos,val}). + %% parse all types in module parse(Tokens) -> case catch parse_ModuleDefinition(Tokens) of -- cgit v1.2.3 From 7a9d412b23f3e4f413b7408a66fb4b076d0efefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Feb 2013 07:55:31 +0100 Subject: Use the record definition for #Externaltypereference --- lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 6f4fd2520e..bc8189c2c8 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -1455,7 +1455,7 @@ print_attribute_comment(InnerType,Pos,Cname,Prop) -> CommentLine = "%%-------------------------------------------------", emit([nl,CommentLine]), case InnerType of - {'Externaltypereference',_,XModule,Name} -> + #'Externaltypereference'{module=XModule,type=Name} -> emit([nl,"%% attribute ",Cname,"(",Pos,") External ",XModule,":",Name]); _ -> emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",InnerType]) -- cgit v1.2.3 From f84849cb7d2579d4f0d07595d2a15b49c1178868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Feb 2013 08:01:33 +0100 Subject: ascn1ct_check: Remove effect-less code --- lib/asn1/src/asn1ct_check.erl | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 1c6b0525b8..a40117dda1 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -875,14 +875,6 @@ check_object(S, ?dbg("check_object set: ~p~n",[ObjSet#'ObjectSet'.set]), {_,ClassDef} = get_referenced_type(S,ClassRef), NewClassRef = check_externaltypereference(S,ClassRef), - %% XXXXXXXXXX - case ObjSet of - #'ObjectSet'{set={'Externaltypereference',undefined,'MSAccessProtocol', - 'AllOperations'}} -> - ok; - _ -> - ok - end, {UniqueFieldName,UniqueInfo} = case (catch get_unique_fieldname(S,ClassDef)) of {error,'__undefined_',_} -> @@ -3997,26 +3989,10 @@ categorize(_S,type,Def) when is_record(Def,type) -> categorize(_,_,Def) -> [Def]. categorize(S,object_set,Def,ClassRef) -> - %% XXXXXXXXXX - case Def of - {'Externaltypereference',undefined,'MSAccessProtocol','AllOperations'} -> - ok; - _ -> - ok - end, NewObjSetSpec = check_object(S,Def,#'ObjectSet'{class = ClassRef, set = parse_objectset(Def)}), Name = new_reference_name("object_set_argument"), - %% XXXXXXXXXX - case Name of - internal_object_set_argument_78 -> - ok; - internal_object_set_argument_77 -> - ok; - _ -> - ok - end, [save_object_set_instance(S,Name,NewObjSetSpec)]; categorize(_S,object,Def,_ClassRef) -> %% should be handled -- cgit v1.2.3 From 9972afbb35b4f4d40aefce37f5220a26c91e19ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Feb 2013 08:44:25 +0100 Subject: Eliminate use of #constraint{} outside of the parser The record #constraint{} is almost unused outside of the parser except for two places in asn1ct_check. The only correct usage of the record is in instance_of_constraints/2. Eliminate that usage by updating the parser to pass that constraint in the same way as all other constraints. In check_integer_range/2, the record is used incorrectly. A constraint for an integer will never be a list of #constraint{} records. Therefore, the list comprehension will always produce an empty list, and check_constr/2 will not actually check anything (which is kind of lucky, since the 'ValueRange' range constraint is incorrectly written - the lower and upper bounds should be in a tuple). For now, we will not attempt to actually start validating integer ranges. Firstly (obviously) we will need to be sure that we correctly handles all forms of constraints, and secondly we will need to consider whether we need to produce a warning rather than an error for compatibility reasons. --- lib/asn1/src/asn1_records.hrl | 1 - lib/asn1/src/asn1ct_check.erl | 18 +++--------------- lib/asn1/src/asn1ct_parser2.erl | 4 +++- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl index 1dfe329f3b..396ba0fcfa 100644 --- a/lib/asn1/src/asn1_records.hrl +++ b/lib/asn1/src/asn1_records.hrl @@ -46,7 +46,6 @@ -record(pobjectsetdef,{checked=false,pos,name,args,class,def}). -record(identifier,{pos,val}). --record(constraint,{c,e}). -record('Constraint',{'SingleValue'=no,'SizeConstraint'=no,'ValueRange'=no,'PermittedAlphabet'=no, 'ContainedSubtype'=no, 'TypeConstraint'=no,'InnerSubtyping'=no,e=no,'Other'=no}). -record(simpletableattributes,{objectsetname,c_name,c_index,usedclassfield, diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index a40117dda1..60977ba54f 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2348,17 +2348,7 @@ validate_integer_ref(S,Ref,NamedNumberList,Constr) -> -check_integer_range(Int,Constr) when is_list(Constr) -> - NewConstr = [X || #constraint{c=X} <- Constr], - check_constr(Int,NewConstr); - -check_integer_range(_Int,_Constr) -> - %%io:format("~p~n",[Constr]), - ok. - -check_constr(Int,[{'ValueRange',Lb,Ub}|T]) when Int >= Lb, Int =< Ub -> - check_constr(Int,T); -check_constr(_Int,[]) -> +check_integer_range(_Int, Constr) when is_list(Constr) -> ok. validate_bitstring(_S,_Value,_NamedNumberList,_Constr) -> @@ -4018,9 +4008,7 @@ parse_objectset(Set) -> %% check_constraints/2 %% check_constraints(S,C) when is_list(C) -> - check_constraints(S, C, []); -check_constraints(S,C) when is_record(C,constraint) -> - check_constraints(S, C#constraint.c, []). + check_constraints(S, C, []). resolv_tuple_or_list(S,List) when is_list(List) -> lists:map(fun(X)->resolv_value(S,X) end, List); @@ -5350,7 +5338,7 @@ iof_associated_type1(S,C) -> %% the tablecinf value for the second component. instance_of_constraints(_,[]) -> {false,[],[],[]}; -instance_of_constraints(S,#constraint{c={simpletable,Type}}) -> +instance_of_constraints(S, [{simpletable,Type}]) -> #type{def=#'Externaltypereference'{type=Name}} = Type, ModuleName = S#state.mname, ObjectSetRef=#'Externaltypereference'{module=ModuleName, diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index f136f90131..344fdf44dd 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -25,6 +25,7 @@ %% Only used internally within this module. -record(typereference, {pos,val}). +-record(constraint,{c,e}). %% parse all types in module parse(Tokens) -> @@ -461,7 +462,8 @@ parse_BuiltinType([{'INSTANCE',_},{'OF',_}|Rest]) -> {DefinedObjectClass,Rest2} = parse_DefinedObjectClass(Rest), case Rest2 of [{'(',_}|_] -> - {Constraint,Rest3} = parse_Constraint(Rest2), + {Constraint0,Rest3} = parse_Constraint(Rest2), + Constraint = merge_constraints([Constraint0]), {#type{def={'INSTANCE OF',DefinedObjectClass,Constraint}},Rest3}; _ -> {#type{def={'INSTANCE OF',DefinedObjectClass,[]}},Rest2} -- cgit v1.2.3 From 08f656658b460eb2a1acf7a57ef0f5a9a3f4bc68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Feb 2013 13:29:14 +0100 Subject: Fix handling of open interval constraints with pre-defined integers The compiler would crash when given code such as the following: Type ::= INTEGER (lower<.. resolv_value1(S,VDef) end end; -resolv_value1(S,{gt,V}) -> - case V of +resolv_value1(S, {gt,V}) -> + case resolv_value1(S, V) of Int when is_integer(Int) -> - V + 1; - #valuedef{value=Int} -> - 1 + resolv_value(S,Int); + Int + 1; Other -> - throw({error,{asn1,{undefined_type_or_value,Other}}}) + throw({error,{asn1,{not_integer_value,Other}}}) end; -resolv_value1(S,{lt,V}) -> - case V of +resolv_value1(S, {lt,V}) -> + case resolv_value1(S, V) of Int when is_integer(Int) -> - V - 1; - #valuedef{value=Int} -> - resolv_value(S,Int) - 1; + Int - 1; Other -> - throw({error,{asn1,{undefined_type_or_value,Other}}}) + throw({error,{asn1,{not_integer_value,Other}}}) end; resolv_value1(S,{'ValueFromObject',{object,Object},[{valuefieldreference, FieldName}]}) -> diff --git a/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 index 6a97c1b38e..ad69553813 100644 --- a/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 @@ -8,6 +8,9 @@ BEGIN SingleValueX5 ::= INTEGER ((42) INTERSECTION (MIN..MAX)) SingleValueX6 ::= INTEGER ((42) INTERSECTION (40..49)) SingleValueX7 ::= INTEGER (42..42) + SingleValueX8 ::= INTEGER (integer42) + SingleValueX9 ::= INTEGER (integer42..integer42) + SingleValueX10 ::= INTEGER ((integer42) INTERSECTION (40..49)) UnconstrainedX0 ::= INTEGER UnconstrainedX1 ::= INTEGER (MIN..MAX) @@ -24,6 +27,7 @@ BEGIN RangeX04 ::= INTEGER (5|6|7|8|9|10) RangeX05 ::= INTEGER (10|9|8|7|6|5) RangeX06 ::= INTEGER (5|6|7..10) + RangeX07 ::= INTEGER (integer4<.. Date: Thu, 28 Feb 2013 08:41:22 +0100 Subject: testConstraints: Add more tests of SIZE constraints --- lib/asn1/test/testConstraints.erl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index c8d9008641..ef6c1b3486 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -128,7 +128,20 @@ int_constraints(Rules) -> %%========================================================== roundtrip('T', "IA"), - roundtrip('T2', "IA"). + roundtrip('T2', "IA"), + + %%========================================================== + %% More SIZE Constraints + %%========================================================== + + roundtrip('FixedSize', "0123456789"), + roundtrip('FixedSize2', "0123456789"), + roundtrip('FixedSize2', "0123456789abcdefghij"), + + [roundtrip('VariableSize', lists:seq($A, $A+L-1)) || + L <- lists:seq(1, 10)], + + ok. refed_NNL_name(_Erule) -> ?line {ok,_} = asn1_wrapper:encode('Constraints','AnotherThing',fred), -- cgit v1.2.3 From 649114be27c1893bb1624ec02e2948ae4a7332c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 28 Feb 2013 08:52:02 +0100 Subject: PER: Eliminate useless argument for encode_octet_string() For what seems to be historical reasons, asn1_rtt_per:encode_octet_string/3 has an ExtensionMarker argument that is no longer used. The extension mark (if any) is included in the constraints argument. --- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 2 +- lib/asn1/src/asn1rtt_per.erl | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index b0b0b37d47..4eddba3150 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -317,7 +317,7 @@ emit_enc_octet_string(Erules, Constraint, Value) -> " end"]); C -> call(Erules, encode_octet_string, - [{asis,C},false,Value]) + [{asis,C},Value]) end. emit_enc_integer_case(Value) -> diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index 1023be8ce1..b30a7aebd2 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -36,7 +36,7 @@ encode_VideotexString/2, encode_ObjectDescriptor/2, encode_UTF8String/1, - encode_octet_string/3, + encode_octet_string/2, encode_known_multiplier_string/4, octets_to_complete/2]). @@ -660,12 +660,10 @@ make_and_set_list([], _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% X.691:16 -%% encode_octet_string(Constraint,ExtensionMarker,Val) +%% encode_octet_string(Constraint, Val) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -encode_octet_string(_C, true, _Val) -> - exit({error,{asn1,{'not_supported',extensionmarker}}}); -encode_octet_string({_,_}=SZ, false, Val) -> +encode_octet_string({_,_}=SZ, Val) -> Len = length(Val), try [encode_length(SZ, Len),2|octets_to_complete(Len, Val)] @@ -673,7 +671,7 @@ encode_octet_string({_,_}=SZ, false, Val) -> exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; -encode_octet_string(SZ, false, Val) when is_list(SZ) -> +encode_octet_string(SZ, Val) when is_list(SZ) -> Len = length(Val), try [encode_length({hd(SZ),lists:max(SZ)},Len),2| @@ -682,9 +680,9 @@ encode_octet_string(SZ, false, Val) when is_list(SZ) -> exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; -encode_octet_string(Sv, false, Val) when is_integer(Sv) -> +encode_octet_string(Sv, Val) when is_integer(Sv) -> encode_fragmented_octet_string(Val); -encode_octet_string(no, false, Val) -> +encode_octet_string(no, Val) -> Len = length(Val), try [encode_length(Len),2|octets_to_complete(Len, Val)] @@ -692,7 +690,7 @@ encode_octet_string(no, false, Val) -> exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; -encode_octet_string(C, _, _) -> +encode_octet_string(C, _) -> exit({error,{not_implemented,C}}). encode_fragmented_octet_string(Val) -> -- cgit v1.2.3 From 902b51b8b43fe66fd4488c7fa10c05c3b9da59b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 28 Feb 2013 11:42:47 +0100 Subject: PER/UPER: Correct encoding of a length outside the root range Consider a type with a size constraint with an extension marker such as: S ::= OCTET STRING (SIZE (0..10, ...)) For a length outside the root range (e.g. 42), the PER/UPER encoder will encode the length field in the same way as it would the type INTEGER (0..MAX) (i.e., as semi-constrained whole number), while the decoder would decode the length in the same way as length field without any constraint. Clearly, either the encoder or the decoder is wrong. But which one? Dubuisson's [1] book (page 442) says that the length should be encoded as a semi-constrained whole number if the length is outside the root range. The X.691 standard document [2] also says (e.g. in 15.11) that length fields should be a semi-constrained number, but gives a reference to section gives a reference to section 10.9, "General rules for encoding a length determinant", and not to to 10.7, "Encoding of a semi-constrained whole number". Reading the standard that way should imply that a length outside the root range should be encoded in the same way as an unconstrained length, and that the decoder does the right thing. Further support for that interpretation: - Larmouth's book [3], page 303. - The ASN.1 playground. [4] References: [1] http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF [2] http://www.itu.int/ITU-T/studygroups/com17/languages/X.691-0207.pdf [3] http://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf [4] http://asn1-playground.oss.com --- lib/asn1/src/asn1rtt_per.erl | 4 ++-- lib/asn1/src/asn1rtt_uper.erl | 4 ++-- lib/asn1/test/testConstraints.erl | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index b30a7aebd2..b8971b7c33 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -359,8 +359,8 @@ encode_length({{Lb,Ub}=Vr,Ext}, Len) when Ub =< 65535 ,Lb >= 0,Len= %% constrained extensible [0|encode_constrained_number(Vr,Len)]; -encode_length({{Lb,_},Ext},Len) when is_list(Ext) -> - [1|encode_semi_constrained_number(Lb, Len)]; +encode_length({{_,_},Ext},Len) when is_list(Ext) -> + [1|encode_length(Len)]; encode_length(SingleValue, _Len) when is_integer(SingleValue) -> []. diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index e55535860f..3f362d227e 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -358,8 +358,8 @@ encode_length({{Lb,Ub}=Vr,Ext},Len) when Ub =< 65535, Lb >= 0, Len =< Ub, is_list(Ext) -> %% constrained extensible [<<0:1>>,encode_constrained_number(Vr,Len)]; -encode_length({{Lb,_Ub},Ext}, Len) when is_list(Ext) -> - [<<1:1>>,encode_semi_constrained_number(Lb, Len)]; +encode_length({{_Lb,_Ub},Ext}, Len) when is_list(Ext) -> + [<<1:1>>,encode_length(Len)]; encode_length(SingleValue, _Len) when is_integer(SingleValue) -> []. diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index ef6c1b3486..d6db1c2b91 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -128,7 +128,11 @@ int_constraints(Rules) -> %%========================================================== roundtrip('T', "IA"), + roundtrip('T', "IAB"), + roundtrip('T', "IABC"), roundtrip('T2', "IA"), + roundtrip('T2', "IAB"), + roundtrip('T2', "IABC"), %%========================================================== %% More SIZE Constraints -- cgit v1.2.3 From 95af544936f9b6d7b8d03f3f49effaf5c314513d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 28 Feb 2013 15:09:06 +0100 Subject: Correct encoding (decoding) of lengths less than the root range Given the type: S ::= IA5String (SIZE (5, ...)) attempting to encode (to PER/UPER) a string shorter than 5 characters would fail. Similarly, attempting to decode such string in the BER format would fail. In the case of BER, we can do no range checks if the size constraint is extensible. --- lib/asn1/src/asn1rtt_ber.erl | 7 +------ lib/asn1/src/asn1rtt_per.erl | 2 +- lib/asn1/src/asn1rtt_uper.erl | 2 +- lib/asn1/test/asn1_SUITE_data/Constraints.py | 1 + lib/asn1/test/testConstraints.erl | 17 +++++++++++++++++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 5fbf116747..34f246fb22 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -1270,12 +1270,7 @@ check_restricted_string(Val, StrLen, Range) -> case Range of {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint Val; - {{Lb,_Ub},[]} when StrLen >= Lb -> - Val; - {{Lb,_Ub},_Ext=[Min|_]} when StrLen >= Lb; StrLen >= Min -> - Val; - {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1; - StrLen =< Ub2, StrLen >= Lb2 -> + {_,Ext} when is_list(Ext) -> Val; StrLen -> % fixed length constraint Val; diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index b8971b7c33..d5952755f8 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -356,7 +356,7 @@ encode_length({Lb,Ub}=Vr, Len) when Ub =< 65535 ,Lb >= 0 -> % constrained encode_length({Lb,_Ub}, Len) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 encode_length(Len); encode_length({{Lb,Ub}=Vr,Ext}, Len) - when Ub =< 65535 ,Lb >= 0,Len= + when Ub =< 65535, Lb =< Len, Len =< Ub, is_list(Ext) -> %% constrained extensible [0|encode_constrained_number(Vr,Len)]; encode_length({{_,_},Ext},Len) when is_list(Ext) -> diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index 3f362d227e..0a543ea6d7 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -355,7 +355,7 @@ encode_length({Lb,Ub}=Vr, Len) when Ub =< 65535, Lb >= 0 -> % constrained encode_length({Lb,_Ub}, Len) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 encode_length(Len); encode_length({{Lb,Ub}=Vr,Ext},Len) - when Ub =< 65535, Lb >= 0, Len =< Ub, is_list(Ext) -> + when Ub =< 65535, Lb =< Len, Len =< Ub, is_list(Ext) -> %% constrained extensible [<<0:1>>,encode_constrained_number(Vr,Len)]; encode_length({{_Lb,_Ub},Ext}, Len) when is_list(Ext) -> diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py index 87243121f7..a069364084 100644 --- a/lib/asn1/test/asn1_SUITE_data/Constraints.py +++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py @@ -86,5 +86,6 @@ Document ::= OCTET STRING (ENCODED BY pdf) pdf OBJECT IDENTIFIER ::= {1,2,3,4,5} +ShorterExt ::= IA5String (SIZE (5, ...)) END diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index d6db1c2b91..d245f6d1d5 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -145,8 +145,16 @@ int_constraints(Rules) -> [roundtrip('VariableSize', lists:seq($A, $A+L-1)) || L <- lists:seq(1, 10)], + roundtrip_enc('ShorterExt', "a", shorter_ext(Rules, "a")), + roundtrip('ShorterExt', "abcde"), + roundtrip('ShorterExt', "abcdef"), + ok. +shorter_ext(per, "a") -> <<16#80,16#01,16#61>>; +shorter_ext(uper, "a") -> <<16#80,16#E1>>; +shorter_ext(ber, _) -> none. + refed_NNL_name(_Erule) -> ?line {ok,_} = asn1_wrapper:encode('Constraints','AnotherThing',fred), ?line {error,_Reason} = @@ -160,6 +168,15 @@ roundtrip(Module, Type, Value) -> {ok,Value} = Module:decode(Type, Encoded), ok. +roundtrip_enc(Type, Value, Enc) -> + Module = 'Constraints', + {ok,Encoded} = Module:encode(Type, Value), + {ok,Value} = Module:decode(Type, Encoded), + case Enc of + none -> ok; + Encoded -> ok + end. + range_error(ber, Type, Value) -> %% BER: Values outside the effective range should be rejected %% on decode. -- cgit v1.2.3 From c11b5a2ebb602c76d0957b67a0ca7741c45fea85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 2 Mar 2013 12:59:36 +0100 Subject: PER: Fix aligments bugs for short strings The encoder wrongly assumed that a known multiplier string (such as IA5String) encoded as exactly 16 bits did not need to be aligned to an octet boundary. X.691 (07/2002) 27.5.7 says that it does. Since an OCTET STRING encoded to 16 bits (two octets) should not be aligned to an octet boundary, that means that asnct_imm:dec_string() needs an additional parameter to determine whether a string of a given length needs to be aligned. Furthermore, there is another subtle rule difference: An OCTET STRING which does not have fixed length is always aligned (in PER), but a known multiplier string is aligned if its upper bound is greater than or equal to 16. In encoding, make sure that short known multiplier strings and OCTET STRINGs with extensible sizes are not aligned when they are below the appropriate limit. --- lib/asn1/src/asn1ct_imm.erl | 43 +++++++++------ lib/asn1/src/asn1rtt_per.erl | 31 +++++++++-- lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 | 72 ++++++++++++++++++++++++++ lib/asn1/test/testPrimStrings.erl | 33 ++++++++++-- 4 files changed, 156 insertions(+), 23 deletions(-) diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 2c5620d5c8..e0e4a5a451 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -117,15 +117,25 @@ per_dec_named_integer(Constraint, NamedList0, Aligned) -> per_dec_k_m_string(StringType, Constraint, Aligned) -> SzConstr = get_constraint(Constraint, 'SizeConstraint'), N = string_num_bits(StringType, Constraint, Aligned), - Imm = dec_string(SzConstr, N, Aligned), + %% X.691 (07/2002) 27.5.7 says if the upper bound times the number + %% of bits is greater than or equal to 16, then the bit field should + %% be aligned. + Imm = dec_string(SzConstr, N, Aligned, fun(_, Ub) -> Ub >= 16 end), Chars = char_tab(Constraint, StringType, N), convert_string(N, Chars, Imm). per_dec_octet_string(Constraint, Aligned) -> - dec_string(Constraint, 8, Aligned). + dec_string(Constraint, 8, Aligned, + %% Aligned unless the size is fixed and =< 16. + fun(Sv, Sv) -> Sv > 16; + (_, _) -> true + end). per_dec_raw_bitstring(Constraint, Aligned) -> - dec_string(Constraint, 1, Aligned). + dec_string(Constraint, 1, Aligned, + fun(Sv, Sv) -> Sv > 16; + (_, _) -> true + end). per_dec_open_type(Aligned) -> {get_bits,decode_unconstrained_length(true, Aligned), @@ -149,21 +159,22 @@ per_dec_restricted_string(Aligned) -> %%% Local functions. %%% -dec_string(Sv, U, _Aligned) when is_integer(Sv), U*Sv =< 16 -> - {get_bits,Sv,[U,binary]}; -dec_string(Sv, U, Aligned) when is_integer(Sv), Sv < 16#10000 -> +dec_string(Sv, U, Aligned0, AF) when is_integer(Sv), Sv < 16#10000 -> + Bits = U*Sv, + Aligned = Aligned0 andalso AF(Bits, Bits), {get_bits,Sv,[U,binary,{align,Aligned}]}; -dec_string([_|_]=C, U, Aligned) when is_list(C) -> - dec_string({hd(C),lists:max(C)}, U, Aligned); -dec_string({Sv,Sv}, U, Aligned) -> - dec_string(Sv, U, Aligned); -dec_string({{_,_}=C,_}, U, Aligned) -> - bit_case(dec_string(C, U, Aligned), - dec_string(no, U, Aligned)); -dec_string({Lb,Ub}, U, Aligned) when Ub < 16#10000 -> - Len = per_dec_constrained(Lb, Ub, Aligned), +dec_string([_|_]=C, U, Aligned, AF) when is_list(C) -> + dec_string({hd(C),lists:max(C)}, U, Aligned, AF); +dec_string({Sv,Sv}, U, Aligned, AF) -> + dec_string(Sv, U, Aligned, AF); +dec_string({{_,_}=C,_}, U, Aligned, AF) -> + bit_case(dec_string(C, U, Aligned, AF), + dec_string(no, U, Aligned, AF)); +dec_string({Lb,Ub}, U, Aligned0, AF) when Ub < 16#10000 -> + Len = per_dec_constrained(Lb, Ub, Aligned0), + Aligned = Aligned0 andalso AF(Lb*U, Ub*U), {get_bits,Len,[U,binary,{align,Aligned}]}; -dec_string(_, U, Aligned) -> +dec_string(_, U, Aligned, _AF) -> Al = [{align,Aligned}], DecRest = fun(V, Buf) -> asn1ct_func:call(per_common, diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index d5952755f8..8d2025d603 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -663,6 +663,19 @@ make_and_set_list([], _) -> %% encode_octet_string(Constraint, Val) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +encode_octet_string({{Sv,Sv},Ext}=SZ, Val) when is_list(Ext), Sv =< 2 -> + Len = length(Val), + try + case encode_length(SZ, Len) of + [0|_]=EncLen -> + [EncLen,45,Sv*8,Sv,Val]; + [_|_]=EncLen -> + [EncLen|octets_to_complete(Len, Val)] + end + catch + exit:{error,{asn1,{encode_length,_}}} -> + encode_fragmented_octet_string(Val) + end; encode_octet_string({_,_}=SZ, Val) -> Len = length(Val), try @@ -725,12 +738,24 @@ encode_restricted_string(Val) when is_list(Val)-> encode_known_multiplier_string(SizeC, NumBits, CharOutTab, Val) -> Result = chars_encode2(Val, NumBits, CharOutTab), case SizeC of - Ub when is_integer(Ub), Ub*NumBits =< 16 -> + Ub when is_integer(Ub), Ub*NumBits < 16 -> Result; Ub when is_integer(Ub), Ub =<65535 -> % fixed length [2,Result]; - {Ub,Lb} -> - [encode_length({Ub,Lb},length(Val)),2,Result]; + {{_,Ub},Ext}=SZ when is_list(Ext) -> + Len = length(Val), + case encode_length(SZ, Len) of + [0|_]=EncLen when Ub*NumBits < 16 -> + [EncLen,45,Len*NumBits,Len,Val]; + [_|_]=EncLen -> + [EncLen,2|Result] + end; + {_,Ub}=Range -> + [encode_length(Range, length(Val))| + if + Ub*NumBits < 16 -> Result; + true -> [2|Result] + end]; no -> [encode_length(length(Val)),2,Result] end. diff --git a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 index cfaf4cf034..99fe38c07c 100644 --- a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 @@ -72,6 +72,32 @@ BS1024 ::= BIT STRING (SIZE (1024)) i INTEGER (0..1024) } + OsFixedStringsExt ::= SEQUENCE { + b1 BOOLEAN, -- Unalign + s0 OCTET STRING (SIZE (0, ...)), + s1 OCTET STRING (SIZE (1, ...)), + s2 OCTET STRING (SIZE (2, ...)), + s3 OCTET STRING (SIZE (3, ...)), + b2 BOOLEAN, -- Unalign + s255 OCTET STRING (SIZE (255, ...)), + s256 OCTET STRING (SIZE (256, ...)), + s257 OCTET STRING (SIZE (257, ...)), + i INTEGER (0..1024) + } + + OsVarStringsExt ::= SEQUENCE { + b1 BOOLEAN, -- Unalign + s0 OCTET STRING (SIZE (0, ...)), + s1 OCTET STRING (SIZE (0..1, ...)), + s2 OCTET STRING (SIZE (1..2, ...)), + s3 OCTET STRING (SIZE (2..3, ...)), + b2 BOOLEAN, -- Unalign + s255 OCTET STRING (SIZE (254..255, ...)), + s256 OCTET STRING (SIZE (255..256, ...)), + s257 OCTET STRING (SIZE (256..257, ...)), + i INTEGER (0..1024) + } + OsAlignment ::= SEQUENCE { b1 BOOLEAN, s1 Os, @@ -82,6 +108,52 @@ BS1024 ::= BIT STRING (SIZE (1024)) i INTEGER (0..63) } + IA5FixedStrings ::= SEQUENCE { + b1 BOOLEAN, -- Unalign + s0 IA5String (SIZE (0)), + s1 IA5String (SIZE (1)), + s2 IA5String (SIZE (2)), + s3 IA5String (SIZE (3)), + b2 BOOLEAN, -- Unalign + s4 IA5String (SIZE (4)), + b3 BOOLEAN, -- Unalign + s255 IA5String (SIZE (255)), + s256 IA5String (SIZE (256)), + s257 IA5String (SIZE (257)), + i INTEGER (0..1024) + } + + IA5FixedStringsExt ::= SEQUENCE { + b1 BOOLEAN, -- Unalign + s0 IA5String (SIZE (0, ...)), + s1 IA5String (SIZE (1, ...)), + s2 IA5String (SIZE (2, ...)), + s3 IA5String (SIZE (3, ...)), + b2 BOOLEAN, -- Unalign + s4 IA5String (SIZE (4, ...)), + b3 BOOLEAN, -- Unalign + s255 IA5String (SIZE (255, ...)), + s256 IA5String (SIZE (256, ...)), + s257 IA5String (SIZE (257, ...)), + i INTEGER (0..1024) + } + + IA5VarStringsExt ::= SEQUENCE { + b1 BOOLEAN, -- Unalign + s0 IA5String (SIZE (0, ...)), + s1 IA5String (SIZE (0..1, ...)), + s2 IA5String (SIZE (1..2, ...)), + s3 IA5String (SIZE (2..3, ...)), + b2 BOOLEAN, -- Unalign + s4 IA5String (SIZE (3..4, ...)), + b3 BOOLEAN, -- Unalign + s255 IA5String (SIZE (254..255, ...)), + s256 IA5String (SIZE (255..256, ...)), + s257 IA5String (SIZE (256..257, ...)), + i INTEGER (0..1024) + } + + Ns ::= NumericString NsCon ::= [70] NumericString NsExpCon ::= [71] EXPLICIT NumericString diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index f8b0c5b05a..66415afe87 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -255,11 +255,16 @@ octet_string(Rules) -> fragmented_octet_string(Rules), S255 = lists:seq(1, 255), - FixedStrings = {'OsFixedStrings',true,"","1","12","345",true, - S255,[$a|S255],[$a,$b|S255],397}, - roundtrip('OsFixedStrings', FixedStrings), + Strings = {type,true,"","1","12","345",true, + S255,[$a|S255],[$a,$b|S255],397}, + p_roundtrip('OsFixedStrings', Strings), + p_roundtrip('OsFixedStringsExt', Strings), + p_roundtrip('OsVarStringsExt', Strings), + ShortenedStrings = shorten_by_two(Strings), + p_roundtrip('OsFixedStringsExt', ShortenedStrings), + p_roundtrip('OsVarStringsExt', ShortenedStrings), ok. - + fragmented_octet_string(Erules) -> K16 = 1 bsl 14, K32 = K16 + K16, @@ -438,6 +443,15 @@ other_strings(_Rules) -> roundtrip('IA5Visible', lists:seq($\s, $~)), + S255 = lists:seq(0, 127) ++ lists:seq(1, 127), + Strings = {type,true,"","1","12","345",true,"6789",true, + S255,[$a|S255],[$a,$b|S255],397}, + p_roundtrip('IA5FixedStrings', Strings), + p_roundtrip('IA5FixedStringsExt', Strings), + p_roundtrip('IA5VarStringsExt', Strings), + ShortenedStrings = shorten_by_two(Strings), + p_roundtrip('IA5VarStringsExt', ShortenedStrings), + ok. @@ -709,6 +723,17 @@ wrapper_utf8_binary_to_list(L) when is_list(L) -> wrapper_utf8_binary_to_list(B) -> asn1rt:utf8_binary_to_list(B). +shorten_by_two(Tuple) -> + L = [case E of + [_,_|T] -> T; + _ -> E + end || E <- tuple_to_list(Tuple)], + list_to_tuple(L). + +p_roundtrip(Type, Value0) -> + Value = setelement(1, Value0, Type), + roundtrip(Type, Value). + roundtrip(Type, Value) -> {ok,Encoded} = 'PrimStrings':encode(Type, Value), {ok,Value} = 'PrimStrings':decode(Type, Encoded), -- cgit v1.2.3 From 53022b787c723a6c4cdf153f5705bde5fb4655ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 2 Mar 2013 12:46:37 +0100 Subject: Normalize SIZE constraints to simplify backends --- lib/asn1/src/asn1ct_check.erl | 37 +++++- lib/asn1/src/asn1ct_constructed_per.erl | 18 +-- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 16 ++- lib/asn1/src/asn1ct_gen_per.erl | 46 ++++---- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 8 +- lib/asn1/src/asn1ct_imm.erl | 53 ++++++--- lib/asn1/src/asn1rtt_ber.erl | 46 +++----- lib/asn1/src/asn1rtt_per.erl | 42 +++---- lib/asn1/src/asn1rtt_uper.erl | 126 ++++++++------------- lib/asn1/test/asn1_SUITE.erl | 1 - .../asn1_SUITE_data/ConstraintEquivalence.asn1 | 12 ++ lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 | 5 + lib/asn1/test/testConstraints.erl | 5 +- lib/asn1/test/testPrimStrings.erl | 4 +- 14 files changed, 214 insertions(+), 205 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 80bd429e2c..4f1b0cac7a 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -4310,7 +4310,42 @@ normalize_cs([{'ValueRange',{Sv,Sv}}|Cs]) -> [{'SingleValue',Sv}|normalize_cs(Cs)]; normalize_cs([{'ValueRange',{'MIN','MAX'}}|Cs]) -> normalize_cs(Cs); -normalize_cs(Other) -> Other. +normalize_cs([{'SizeConstraint',C0}|Cs]) -> + case normalize_size_constraint(C0) of + none -> + normalize_cs(Cs); + C -> + [{'SizeConstraint',C}|normalize_cs(Cs)] + end; +normalize_cs([H|T]) -> + [H|normalize_cs(T)]; +normalize_cs([]) -> []. + +%% Normalize a size constraint to make it non-ambiguous and +%% easy to interpret for the backends. +%% +%% Returns one of the following terms: +%% {LowerBound,UpperBound} +%% {{LowerBound,UpperBound},[]} % Extensible +%% none % Remove size constraint from list +%% +%% where: +%% LowerBound = integer() +%% UpperBound = integer() | 'MAX' + +normalize_size_constraint(Sv) when is_integer(Sv) -> + {Sv,Sv}; +normalize_size_constraint({Root,Ext}) when is_list(Ext) -> + {normalize_size_constraint(Root),[]}; +normalize_size_constraint({{_,_},Ext}) when is_integer(Ext) -> + normalize_size_constraint(Ext); +normalize_size_constraint([H|T]) -> + {H,lists:last(T)}; +normalize_size_constraint({0,'MAX'}) -> + none; +normalize_size_constraint({Lb,Ub}=Range) + when is_integer(Lb), is_integer(Ub) orelse Ub =:= 'MAX' -> + Range. is_range(Prev, [H|T]) when Prev =:= H - 1 -> is_range(H, T); is_range(_, [_|_]) -> false; diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 8c181aa2f0..bcd0a01bbb 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -502,12 +502,8 @@ gen_encode_sof(Erule,Typename,SeqOrSetOf,D) when is_record(D,type) -> asn1ct_name:start(), {_SeqOrSetOf,ComponentType} = D#type.def, emit({"[",nl}), - SizeConstraint = - case asn1ct_gen:get_constraint(D#type.constraint, - 'SizeConstraint') of - no -> undefined; - Range -> Range - end, + SizeConstraint = asn1ct_imm:effective_constraint(bitstring, + D#type.constraint), ObjFun = case D#type.tablecinf of [{objfun,_}|_R] -> @@ -563,7 +559,7 @@ gen_encode_length(per, {Lb,Ub}) when Ub =< 65535, Lb >= 0 -> gen_encode_length(Erules, SizeConstraint) -> emit([nl,indent(3), case SizeConstraint of - undefined -> + no -> {call,Erules,encode_length,["length(Val)"]}; _ -> {call,Erules,encode_length, @@ -574,12 +570,8 @@ gen_encode_length(Erules, SizeConstraint) -> gen_decode_sof(Erules,Typename,SeqOrSetOf,D) when is_record(D,type) -> asn1ct_name:start(), {_SeqOrSetOf,ComponentType} = D#type.def, - SizeConstraint = - case asn1ct_gen:get_constraint(D#type.constraint, - 'SizeConstraint') of - no -> undefined; - Range -> Range - end, + SizeConstraint = asn1ct_imm:effective_constraint(bitstring, + D#type.constraint), ObjFun = case D#type.tablecinf of [{objfun,_}|_R] -> diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 3a98cbb452..59653e383b 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -154,7 +154,7 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> end. gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> - BitStringConstraint = D#type.constraint, + BitStringConstraint = get_size_constraint(D#type.constraint), asn1ct_name:new(enumval), Type = case D#type.def of 'OCTET STRING' -> restricted_string; @@ -459,11 +459,7 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> %% Currently not used for BER replaced with [] as place holder %% Constraint = Att#type.constraint, %% Constraint = [], - Constraint = - case get_constraint(Att#type.constraint,'SizeConstraint') of - no -> []; - Tc -> Tc - end, + Constraint = get_size_constraint(Att#type.constraint), ValueRange = case get_constraint(Att#type.constraint,'ValueRange') of no -> []; @@ -1480,6 +1476,14 @@ mkfuncname(WhatKind,DecOrEnc) -> end. +get_size_constraint(C) -> + case lists:keyfind('SizeConstraint', 1, C) of + false -> []; + {_,{_,[]}} -> []; %Extensible. + {_,{Sv,Sv}} -> Sv; + {_,{_,_}=Tc} -> Tc + end. + get_constraint(C,Key) -> case lists:keysearch(Key,1,C) of false -> diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 2ffbe87ab2..3e35eb9f0e 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -118,6 +118,11 @@ gen_encode_prim(per=Erules, D, DoTag, Value) -> asn1ct_gen_per_rt2ct:gen_encode_prim(Erules, D, DoTag, Value); gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> Constraint = D#type.constraint, + SizeConstr = asn1ct_imm:effective_constraint(bitstring, Constraint), + Pa = case lists:keyfind('PermittedAlphabet', 1, Constraint) of + false -> no; + {_,Pa0} -> Pa0 + end, asn1ct_name:new(enumval), case D#type.def of 'INTEGER' -> @@ -132,7 +137,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> emit_enc_real(Erules, Value); {'BIT STRING',NamedNumberList} -> - SizeConstr = get_constraint(Constraint, 'SizeConstraint'), call(Erules, encode_bit_string, [{asis,SizeConstr},Value, {asis,NamedNumberList}]); @@ -148,7 +152,7 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> 'BOOLEAN' -> call(Erules, encode_boolean, [Value]); 'OCTET STRING' -> - case get_constraint(Constraint, 'SizeConstraint') of + case SizeConstr of 0 -> emit("[]"); no -> @@ -157,30 +161,38 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> call(Erules, encode_octet_string, [{asis,C},Value]) end; 'NumericString' -> - call(Erules, encode_NumericString, [{asis,Constraint},Value]); + call(Erules, encode_NumericString, [{asis,SizeConstr}, + {asis,Pa},Value]); TString when TString == 'TeletexString'; TString == 'T61String' -> call(Erules, encode_TeletexString, [{asis,Constraint},Value]); 'VideotexString' -> call(Erules, encode_VideotexString, [{asis,Constraint},Value]); 'UTCTime' -> - call(Erules, encode_VisibleString, [{asis,Constraint},Value]); + call(Erules, encode_VisibleString, [{asis,SizeConstr}, + {asis,Pa},Value]); 'GeneralizedTime' -> - call(Erules, encode_VisibleString, [{asis,Constraint},Value]); + call(Erules, encode_VisibleString, [{asis,SizeConstr}, + {asis,Pa},Value]); 'GraphicString' -> call(Erules, encode_GraphicString, [{asis,Constraint},Value]); 'VisibleString' -> - call(Erules, encode_VisibleString, [{asis,Constraint},Value]); + call(Erules, encode_VisibleString, [{asis,SizeConstr}, + {asis,Pa},Value]); 'GeneralString' -> call(Erules, encode_GeneralString, [{asis,Constraint},Value]); 'PrintableString' -> - call(Erules, encode_PrintableString, [{asis,Constraint},Value]); + call(Erules, encode_PrintableString, [{asis,SizeConstr}, + {asis,Pa},Value]); 'IA5String' -> - call(Erules, encode_IA5String, [{asis,Constraint},Value]); + call(Erules, encode_IA5String, [{asis,SizeConstr}, + {asis,Pa},Value]); 'BMPString' -> - call(Erules, encode_BMPString, [{asis,Constraint},Value]); + call(Erules, encode_BMPString, [{asis,SizeConstr}, + {asis,Pa},Value]); 'UniversalString' -> - call(Erules, encode_UniversalString, [{asis,Constraint},Value]); + call(Erules, encode_UniversalString, [{asis,SizeConstr}, + {asis,Pa},Value]); 'UTF8String' -> call(Erules, encode_UTF8String, [Value]); 'ANY' -> @@ -252,16 +264,6 @@ enc_ext_and_val(uper, E, F, Args) -> <>. -get_constraint([{Key,V}], Key) -> - V; -get_constraint([], _) -> - no; -get_constraint(C, Key) -> - case lists:keyfind(Key, 1, C) of - false -> no; - {Key,V} -> V - end. - %% Object code generating for encoding and decoding %% ------------------------------------------------ @@ -1016,7 +1018,7 @@ gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) -> gen_dec_imm_1('ANY', _Constraint, Aligned) -> imm_decode_open_type([], Aligned); gen_dec_imm_1({'BIT STRING',NNL}, Constr0, Aligned) -> - Constr = get_constraint(Constr0, 'SizeConstraint'), + Constr = asn1ct_imm:effective_constraint(bitstring, Constr0), Imm = asn1ct_imm:per_dec_raw_bitstring(Constr, Aligned), case NNL of [] -> @@ -1069,7 +1071,7 @@ gen_dec_imm_1('UTCTime', Constraint, Aligned) -> gen_dec_imm_1('GeneralizedTime', Constraint, Aligned) -> gen_dec_k_m_string('VisibleString', Constraint, Aligned); gen_dec_imm_1('OCTET STRING', Constraint, Aligned) -> - SzConstr = get_constraint(Constraint, 'SizeConstraint'), + SzConstr = asn1ct_imm:effective_constraint(bitstring, Constraint), Imm = asn1ct_imm:per_dec_octet_string(SzConstr, Aligned), {convert,binary_to_list,Imm}; gen_dec_imm_1('TeletexString', _Constraint, Aligned) -> diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 4eddba3150..1a16054fa0 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -139,11 +139,7 @@ emit_enc_real(Erules, Real) -> "end"]). emit_enc_known_multiplier_string(StringType,C,Value) -> - SizeC = - case get_constraint(C,'SizeConstraint') of - L when is_list(L) -> {lists:min(L),lists:max(L)}; - L -> L - end, + SizeC = effective_constraint(bitstring, C), PAlphabC = get_constraint(C,'PermittedAlphabet'), case {StringType,PAlphabC} of {'UniversalString',{_,_}} -> @@ -268,7 +264,7 @@ charbits1(NumOfChars) -> %% copied from run time module emit_enc_octet_string(Erules, Constraint, Value) -> - case get_constraint(Constraint,'SizeConstraint') of + case effective_constraint(bitstring, Constraint) of 0 -> emit({" []"}); 1 -> diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index e0e4a5a451..5585cad925 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -95,18 +95,16 @@ per_dec_integer(Constraint0, Aligned) -> per_dec_length(SingleValue, _, _Aligned) when is_integer(SingleValue) -> {value,SingleValue}; -per_dec_length({S,S}, _, _Aligned) when is_integer(S) -> - {value,S}; -per_dec_length({{_,_}=Constr,_}, AllowZero, Aligned) -> +per_dec_length({{Fixed,Fixed},[]}, AllowZero, Aligned) -> + bit_case(per_dec_length(Fixed, AllowZero, Aligned), + per_dec_length(no, AllowZero, Aligned)); +per_dec_length({{_,_}=Constr,[]}, AllowZero, Aligned) -> bit_case(per_dec_length(Constr, AllowZero, Aligned), - per_dec_length(undefined, AllowZero, Aligned)); + per_dec_length(no, AllowZero, Aligned)); per_dec_length({Lb,Ub}, _AllowZero, Aligned) when is_integer(Lb), - is_integer(Lb), - Ub =< 65535 -> + is_integer(Lb) -> per_dec_constrained(Lb, Ub, Aligned); -per_dec_length({_,_}, AllowZero, Aligned) -> - decode_unconstrained_length(AllowZero, Aligned); -per_dec_length(undefined, AllowZero, Aligned) -> +per_dec_length(no, AllowZero, Aligned) -> decode_unconstrained_length(AllowZero, Aligned). per_dec_named_integer(Constraint, NamedList0, Aligned) -> @@ -115,7 +113,7 @@ per_dec_named_integer(Constraint, NamedList0, Aligned) -> {map,Int,NamedList}. per_dec_k_m_string(StringType, Constraint, Aligned) -> - SzConstr = get_constraint(Constraint, 'SizeConstraint'), + SzConstr = effective_constraint(bitstring, Constraint), N = string_num_bits(StringType, Constraint, Aligned), %% X.691 (07/2002) 27.5.7 says if the upper bound times the number %% of bits is greater than or equal to 16, then the bit field should @@ -159,18 +157,17 @@ per_dec_restricted_string(Aligned) -> %%% Local functions. %%% -dec_string(Sv, U, Aligned0, AF) when is_integer(Sv), Sv < 16#10000 -> +dec_string(Sv, U, Aligned0, AF) when is_integer(Sv) -> Bits = U*Sv, Aligned = Aligned0 andalso AF(Bits, Bits), {get_bits,Sv,[U,binary,{align,Aligned}]}; -dec_string([_|_]=C, U, Aligned, AF) when is_list(C) -> - dec_string({hd(C),lists:max(C)}, U, Aligned, AF); -dec_string({Sv,Sv}, U, Aligned, AF) -> - dec_string(Sv, U, Aligned, AF); -dec_string({{_,_}=C,_}, U, Aligned, AF) -> +dec_string({{Sv,Sv},[]}, U, Aligned, AF) -> + bit_case(dec_string(Sv, U, Aligned, AF), + dec_string(no, U, Aligned, AF)); +dec_string({{_,_}=C,[]}, U, Aligned, AF) -> bit_case(dec_string(C, U, Aligned, AF), dec_string(no, U, Aligned, AF)); -dec_string({Lb,Ub}, U, Aligned0, AF) when Ub < 16#10000 -> +dec_string({Lb,Ub}, U, Aligned0, AF) -> Len = per_dec_constrained(Lb, Ub, Aligned0), Aligned = Aligned0 andalso AF(Lb*U, Ub*U), {get_bits,Len,[U,binary,{align,Aligned}]}; @@ -712,7 +709,27 @@ effective_constraint(integer, C) -> VR = effective_constr('ValueRange', VRs), greatest_common_range(SV, VR); effective_constraint(bitstring, C) -> - get_constraint(C, 'SizeConstraint'). + case get_constraint(C, 'SizeConstraint') of + {{Lb,Ub},[]}=Range when is_integer(Lb) -> + if + is_integer(Ub), Ub < 16#10000 -> + Range; + true -> + no + end; + {Lb,Ub}=Range when is_integer(Lb) -> + if + is_integer(Ub), Ub < 16#10000 -> + if + Lb =:= Ub -> Lb; + true -> Range + end; + true -> + no + end; + no -> + no + end. effective_constr(_, []) -> []; effective_constr('SingleValue', List) -> diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 34f246fb22..509c091355 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -838,8 +838,8 @@ int_to_bitlist(Int) when is_integer(Int), Int >= 0 -> %% and BinBits is a binary representing the BIT STRING. %%================================================================= encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList,TagIn)-> - case get_constraint(C,'SizeConstraint') of - no -> + case C of + [] -> remove_unused_then_dotag(TagIn, Unused, BinBits); {_Min,Max} -> BBLen = (byte_size(BinBits)*8)-Unused, @@ -885,8 +885,8 @@ remove_unused_then_dotag(TagIn,Unused,BinBits) -> encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn) -> ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []), Size = - case get_constraint(C,'SizeConstraint') of - no -> + case C of + [] -> lists:max(ToSetPos)+1; {_Min,Max} -> Max; @@ -943,8 +943,8 @@ make_and_set_list(Len, [], XPos) -> %% Encode bit string for lists of ones and zeroes %%================================================================= encode_bit_string_bits(C, BitListVal, _NamedBitList, TagIn) when is_list(BitListVal) -> - case get_constraint(C,'SizeConstraint') of - no -> + case C of + [] -> {Len, Unused, OctetList} = encode_bitstring(BitListVal), %%add unused byte to the Len encode_tags(TagIn, [Unused | OctetList], Len+1); @@ -957,7 +957,7 @@ encode_bit_string_bits(C, BitListVal, _NamedBitList, TagIn) when is_list(BitList Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}} %% constraint with extension mark encode_constr_bit_str_bits(Constr,BitListVal,TagIn); - Size -> + Size when is_integer(Size) -> case length(BitListVal) of BitSize when BitSize == Size -> {Len, Unused, OctetList} = encode_bitstring(BitListVal), @@ -1266,22 +1266,14 @@ decode_restricted_string(Tlv, Range, TagsIn) -> Bin = match_and_collect(Tlv, TagsIn), check_restricted_string(binary_to_list(Bin), byte_size(Bin), Range). -check_restricted_string(Val, StrLen, Range) -> - case Range of - {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint - Val; - {_,Ext} when is_list(Ext) -> - Val; - StrLen -> % fixed length constraint - Val; - {_,_} -> - exit({error,{asn1,{length,Range,Val}}}); - _Len when is_integer(_Len) -> - exit({error,{asn1,{length,Range,Val}}}); - _ -> % some strange constraint that we don't support yet - Val - end. - +check_restricted_string(Val, _Len, []) -> + Val; +check_restricted_string(Val, Len, {Lb,Ub}) when Lb =< Len, Len =< Ub -> + Val; +check_restricted_string(Val, Len, Len) -> + Val; +check_restricted_string(Val, _Len, Range) -> + exit({error,{asn1,{length,Range,Val}}}). %%============================================================================ %% encode Universal string @@ -1530,14 +1522,6 @@ match_and_collect(Tlv, TagsIn) -> Bin end. -get_constraint(C, Key) -> - case lists:keyfind(Key, 1, C) of - false -> - no; - {_,V} -> - V - end. - collect_parts(TlvList) -> collect_parts(TlvList, []). diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index 8d2025d603..74ae11632d 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -347,22 +347,17 @@ encode_length(Len) -> % unconstrained exit({error,{asn1,{encode_length,{nyi,above_16k}}}}) end. -encode_length(undefined, Len) -> % un-constrained - encode_length(Len); -encode_length({0,'MAX'},Len) -> - encode_length(undefined,Len); -encode_length({Lb,Ub}=Vr, Len) when Ub =< 65535 ,Lb >= 0 -> % constrained - encode_constrained_number(Vr,Len); -encode_length({Lb,_Ub}, Len) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 - encode_length(Len); -encode_length({{Lb,Ub}=Vr,Ext}, Len) - when Ub =< 65535, Lb =< Len, Len =< Ub, is_list(Ext) -> - %% constrained extensible - [0|encode_constrained_number(Vr,Len)]; -encode_length({{_,_},Ext},Len) when is_list(Ext) -> - [1|encode_length(Len)]; -encode_length(SingleValue, _Len) when is_integer(SingleValue) -> - []. +encode_length({C,[]}, Len) -> + case C of + {Lb,Ub}=Vr when Lb =< Len, Len =< Ub -> + [0|encode_constrained_number(Vr, Len)]; + _ -> + [1|encode_length(Len)] + end; +encode_length(Len, Len) -> + []; +encode_length(Vr, Len) -> + encode_constrained_number(Vr, Len). %% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension %% additions in a sequence or set @@ -684,15 +679,6 @@ encode_octet_string({_,_}=SZ, Val) -> exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; -encode_octet_string(SZ, Val) when is_list(SZ) -> - Len = length(Val), - try - [encode_length({hd(SZ),lists:max(SZ)},Len),2| - octets_to_complete(Len,Val)] - catch - exit:{error,{asn1,{encode_length,_}}} -> - encode_fragmented_octet_string(Val) - end; encode_octet_string(Sv, Val) when is_integer(Sv) -> encode_fragmented_octet_string(Val); encode_octet_string(no, Val) -> @@ -702,9 +688,7 @@ encode_octet_string(no, Val) -> catch exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) - end; -encode_octet_string(C, _) -> - exit({error,{not_implemented,C}}). + end. encode_fragmented_octet_string(Val) -> Bin = iolist_to_binary(Val), @@ -740,7 +724,7 @@ encode_known_multiplier_string(SizeC, NumBits, CharOutTab, Val) -> case SizeC of Ub when is_integer(Ub), Ub*NumBits < 16 -> Result; - Ub when is_integer(Ub), Ub =<65535 -> % fixed length + Ub when is_integer(Ub) -> [2,Result]; {{_,Ub},Ext}=SZ when is_list(Ext) -> Len = length(Val), diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index 0a543ea6d7..8cf23a9239 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -34,17 +34,17 @@ -export([encode_open_type/1]). - -export([encode_UniversalString/2, - encode_PrintableString/2, + -export([encode_UniversalString/3, + encode_PrintableString/3, encode_GeneralString/2, encode_GraphicString/2, encode_TeletexString/2, encode_VideotexString/2, - encode_VisibleString/2, + encode_VisibleString/3, encode_UTF8String/1, - encode_BMPString/2, - encode_IA5String/2, - encode_NumericString/2, + encode_BMPString/3, + encode_IA5String/3, + encode_NumericString/3, encode_ObjectDescriptor/2 ]). @@ -346,22 +346,18 @@ encode_length(Len) -> % un-constrained error({error,{asn1,{encode_length,{nyi,above_16k}}}}) end. -encode_length(undefined, Len) -> % unconstrained - encode_length(Len); -encode_length({0,'MAX'},Len) -> - encode_length(undefined, Len); -encode_length({Lb,Ub}=Vr, Len) when Ub =< 65535, Lb >= 0 -> % constrained - encode_constrained_number(Vr,Len); -encode_length({Lb,_Ub}, Len) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 - encode_length(Len); -encode_length({{Lb,Ub}=Vr,Ext},Len) - when Ub =< 65535, Lb =< Len, Len =< Ub, is_list(Ext) -> - %% constrained extensible - [<<0:1>>,encode_constrained_number(Vr,Len)]; -encode_length({{_Lb,_Ub},Ext}, Len) when is_list(Ext) -> - [<<1:1>>,encode_length(Len)]; -encode_length(SingleValue, _Len) when is_integer(SingleValue) -> - []. +encode_length({C,[]}, Len) -> + case C of + {Lb,Ub}=Vr when Lb =< Len, Len =< Ub -> + [<<0:1>>|encode_constrained_number(Vr, Len)]; + _ -> + [<<1:1>>|encode_length(Len)] + end; +encode_length(Len, Len) -> + []; +encode_length(Vr, Len) -> + encode_constrained_number(Vr, Len). + %% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension %% additions in a sequence or set @@ -643,10 +639,6 @@ encode_octet_string(Val) -> encode_octet_string(C, Val) -> case C of - 1 -> - list_to_binary(Val); - 2 -> - list_to_binary(Val); {_,_}=VR -> try [encode_length(VR, length(Val)),list_to_binary(Val)] @@ -655,20 +647,7 @@ encode_octet_string(C, Val) -> encode_fragmented_octet_string(Val) end; Sv when is_integer(Sv), Sv =:= length(Val) -> % fixed length - if - Sv =< 65535 -> - list_to_binary(Val); - true -> - encode_fragmented_octet_string(Val) - end; - Sv when is_list(Sv) -> - try - [encode_length({hd(Sv),lists:max(Sv)}, - length(Val)),list_to_binary(Val)] - catch - error:{error,{asn1,{encode_length,_}}} -> - encode_fragmented_octet_string(Val) - end + list_to_binary(Val) end. @@ -698,41 +677,34 @@ efos_1(<>) -> encode_restricted_string(Val) when is_list(Val)-> [encode_length(length(Val)),list_to_binary(Val)]. -encode_known_multiplier_string(StringType, C, Val) -> - Result = chars_encode(C, StringType, Val), - NumBits = get_NumBits(C, StringType), - case get_constraint(C, 'SizeConstraint') of - Ub when is_integer(Ub), Ub*NumBits =< 16 -> - Result; - 0 -> - []; - Ub when is_integer(Ub),Ub =<65535 -> % fixed length +encode_known_multiplier_string(StringType, C, Pa, Val) -> + Result = chars_encode(Pa, StringType, Val), + case C of + Ub when is_integer(Ub) -> Result; - {Ub,Lb} -> - [encode_length({Ub,Lb}, length(Val)),Result]; - Vl when is_list(Vl) -> - [encode_length({lists:min(Vl),lists:max(Vl)}, length(Val)),Result]; + {_,_}=Range -> + [encode_length(Range, length(Val)),Result]; no -> [encode_length(length(Val)),Result] end. -encode_NumericString(C,Val) -> - encode_known_multiplier_string('NumericString',C,Val). +encode_NumericString(C, Pa, Val) -> + encode_known_multiplier_string('NumericString', C, Pa, Val). -encode_PrintableString(C,Val) -> - encode_known_multiplier_string('PrintableString',C,Val). +encode_PrintableString(C, Pa, Val) -> + encode_known_multiplier_string('PrintableString', C, Pa, Val). -encode_VisibleString(C,Val) -> % equivalent with ISO646String - encode_known_multiplier_string('VisibleString',C,Val). +encode_VisibleString(C, Pa, Val) -> % equivalent with ISO646String + encode_known_multiplier_string('VisibleString', C, Pa, Val). -encode_IA5String(C,Val) -> - encode_known_multiplier_string('IA5String',C,Val). +encode_IA5String(C, Pa, Val) -> + encode_known_multiplier_string('IA5String', C, Pa, Val). -encode_BMPString(C,Val) -> - encode_known_multiplier_string('BMPString',C,Val). +encode_BMPString(C, Pa, Val) -> + encode_known_multiplier_string('BMPString', C, Pa, Val). -encode_UniversalString(C,Val) -> - encode_known_multiplier_string('UniversalString',C,Val). +encode_UniversalString(C, Pa, Val) -> + encode_known_multiplier_string('UniversalString', C, Pa, Val). %% end of known-multiplier strings for which PER visible constraints are @@ -761,14 +733,15 @@ encode_VideotexString(_C,Val) -> %% into account. %% This function does only encode the value part and NOT the length -chars_encode(C,StringType,Value) -> - case {StringType,get_constraint(C,'PermittedAlphabet')} of +chars_encode(Pa, StringType, Value) -> + case {StringType,Pa} of {'UniversalString',{_,_Sv}} -> exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}}); {'BMPString',{_,_Sv}} -> exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}}); _ -> - {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)}, + {NumBits,CharOutTab} = {get_NumBits(Pa, StringType), + get_CharOutTab(Pa, StringType)}, chars_encode2(Value,NumBits,CharOutTab) end. @@ -795,8 +768,8 @@ exit_if_false(V,false)-> exit_if_false(_,V) ->V. -get_NumBits(C,StringType) -> - case get_constraint(C,'PermittedAlphabet') of +get_NumBits(Pa, StringType) -> + case Pa of {'SingleValue',Sv} -> charbits(length(Sv)); no -> @@ -816,22 +789,23 @@ get_NumBits(C,StringType) -> end end. -get_CharOutTab(C,StringType) -> - case get_constraint(C,'PermittedAlphabet') of +get_CharOutTab(Pa, StringType) -> + case Pa of {'SingleValue',Sv} -> - get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv); + get_CharTab2(Pa, StringType, hd(Sv), lists:max(Sv), Sv); no -> case StringType of 'IA5String' -> {0,16#7F,notab}; 'VisibleString' -> - get_CharTab2(C,StringType,16#20,16#7F,notab); + get_CharTab2(Pa, StringType, 16#20, 16#7F, notab); 'PrintableString' -> Chars = lists:sort( " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), - get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars); + get_CharTab2(Pa, StringType, hd(Chars), + lists:max(Chars), Chars); 'NumericString' -> - get_CharTab2(C,StringType,16#20,$9," 0123456789"); + get_CharTab2(Pa, StringType, 16#20, $9, " 0123456789"); 'UniversalString' -> {0,16#FFFFFFFF,notab}; 'BMPString' -> diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 8deabece37..0c5ba43a5f 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -655,7 +655,6 @@ constraint_equivalence(Config) -> AbsFile = filename:join(CaseDir, Asn1Spec++".abs"), {ok,Terms} = file:consult(AbsFile), Cs = [begin - 'INTEGER' = element(3, Type), %Assertion. Constraints = element(4, Type), Name1 = atom_to_list(Name0), {Name,_} = lists:splitwith(fun(C) -> C =/= $X end, Name1), diff --git a/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 index ad69553813..8b3d151502 100644 --- a/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 @@ -43,6 +43,18 @@ BEGIN RangeX23 ::= INTEGER ((0..10) INTERSECTION (5..20) ^ (MIN..MAX)) RangeX24 ::= INTEGER ((5|6|7|8|9|10) INTERSECTION (5..20) ^ (MIN..MAX)) + UnconstrainedStringX00 ::= IA5String + UnconstrainedStringX01 ::= IA5String (SIZE (0..MAX)) + + ConstrainedStringX00 ::= IA5String (SIZE (0..5)) + ConstrainedStringX01 ::= IA5String (SIZE (0|1|2|3|4|5)) + + -- Note: None of the back-ends care about the exact values + -- outside of the root range. + ExtConstrainedStringX00 ::= IA5String (SIZE (1..2, ...)) + ExtConstrainedStringX01 ::= IA5String (SIZE (1|2, ..., 3)) + ExtConstrainedStringX02 ::= IA5String (SIZE (1|2, ..., 3|4|5)) + integer4 INTEGER ::= 4 integer11 INTEGER ::= 11 integer42 INTEGER ::= 42 diff --git a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 index 99fe38c07c..64fa5f4cd4 100644 --- a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 @@ -55,10 +55,15 @@ BS1024 ::= BIT STRING (SIZE (1024)) OsExpCon ::= [60] EXPLICIT OCTET STRING OsExpPri ::= [PRIVATE 61] EXPLICIT OCTET STRING OsExpApp ::= [APPLICATION 62] EXPLICIT OCTET STRING + OsFrag ::= OCTET STRING (SIZE (0..100000)) FixedOs65536 ::= OCTET STRING (SIZE (65536)) FixedOs65537 ::= OCTET STRING (SIZE (65537)) + OsFragExt ::= OCTET STRING (SIZE (0..100000, ...)) + FixedOs65536Ext ::= OCTET STRING (SIZE (65536, ...)) + FixedOs65537Ext ::= OCTET STRING (SIZE (65537, ...)) + OsFixedStrings ::= SEQUENCE { b1 BOOLEAN, -- Unalign s0 OCTET STRING (SIZE (0)), diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index d245f6d1d5..fc217e45f7 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -142,6 +142,9 @@ int_constraints(Rules) -> roundtrip('FixedSize2', "0123456789"), roundtrip('FixedSize2', "0123456789abcdefghij"), + range_error(Rules, 'FixedSize', "short"), + range_error(Rules, 'FixedSize2', "short"), + [roundtrip('VariableSize', lists:seq($A, $A+L-1)) || L <- lists:seq(1, 10)], @@ -181,7 +184,7 @@ range_error(ber, Type, Value) -> %% BER: Values outside the effective range should be rejected %% on decode. {ok,Encoded} = 'Constraints':encode(Type, Value), - {error,{asn1,{integer_range,_,_}}} = 'Constraints':decode(Type, Encoded), + {error,{asn1,_}} = 'Constraints':decode(Type, Encoded), ok; range_error(Per, Type, Value) when Per =:= per; Per =:= uper -> %% (U)PER: Values outside the effective range should be rejected diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index 66415afe87..6f5a7f2437 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -276,10 +276,12 @@ fragmented_octet_string(Erules) -> K48-1,K48,K48+1,K48+(1 bsl 7)-1,K48+(1 bsl 7),K48+(1 bsl 7)+1, K64-1,K64,K64+1,K64+(1 bsl 7)-1,K64+(1 bsl 7),K64+(1 bsl 7)+1, K64+K16-1,K64+K16,K64+K16+1], - Types = ['Os','OsFrag'], + Types = ['Os','OsFrag','OsFragExt'], [fragmented_octet_string(Erules, Types, L) || L <- Lens], fragmented_octet_string(Erules, ['FixedOs65536'], 65536), fragmented_octet_string(Erules, ['FixedOs65537'], 65537), + fragmented_octet_string(Erules, ['FixedOs65536Ext'], 65536), + fragmented_octet_string(Erules, ['FixedOs65537Ext'], 65537), %% Make sure that octet alignment works. roundtrip('OsAlignment', -- cgit v1.2.3 From 7767bb01374427a6a09a2655de1a53a5ffe1d673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 4 Mar 2013 19:50:00 +0100 Subject: Get rid of 'ANY' in the backends asn1ct_check has translated all occurrences of 'ANY' to 'ASN1_OPEN_TYPE'. --- lib/asn1/src/asn1ct_gen.erl | 1 - lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 3 --- lib/asn1/src/asn1ct_gen_per.erl | 4 ---- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 2 -- lib/asn1/src/asn1ct_value.erl | 2 -- 5 files changed, 12 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 956497a3d5..249cde96b5 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -1737,7 +1737,6 @@ prim_bif(X) -> 'REAL', 'OBJECT IDENTIFIER', 'RELATIVE-OID', - 'ANY', 'NULL', 'BIT STRING' , 'OCTET STRING' , diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 59653e383b..4073643f9e 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -192,8 +192,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> call(encode_bit_string, [{asis,BitStringConstraint},Value, {asis,NamedNumberList},DoTag]); - 'ANY' -> - call(encode_open_type, [Value,DoTag]); 'NULL' -> call(encode_null, [Value,DoTag]); 'OBJECT IDENTIFIER' -> @@ -475,7 +473,6 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> _ -> "" end, NewTypeName = case Typename of - 'ANY' -> 'ASN1_OPEN_TYPE'; 'OCTET STRING' -> restricted_string; 'NumericString' -> restricted_string; 'TeletexString' -> restricted_string; diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 3e35eb9f0e..37f5b9c960 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -195,8 +195,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> {asis,Pa},Value]); 'UTF8String' -> call(Erules, encode_UTF8String, [Value]); - 'ANY' -> - call(Erules, encode_open_type, [Value]); 'ASN1_OPEN_TYPE' -> NewValue = case Constraint of [#'Externaltypereference'{type=Tname}] -> @@ -1015,8 +1013,6 @@ gen_dec_imm(Erule, #type{def=Name,constraint=C}) -> gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) -> imm_decode_open_type(Constraint, Aligned); -gen_dec_imm_1('ANY', _Constraint, Aligned) -> - imm_decode_open_type([], Aligned); gen_dec_imm_1({'BIT STRING',NNL}, Constr0, Aligned) -> Constr = asn1ct_imm:effective_constraint(bitstring, Constr0), Imm = asn1ct_imm:per_dec_raw_bitstring(Constr, Aligned), diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 1a16054fa0..8e9d90ae60 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -100,8 +100,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> emit_enc_known_multiplier_string('UniversalString',Constraint,Value); 'UTF8String' -> call(Erules, encode_UTF8String, [Value]); - 'ANY' -> - call(Erules, encode_open_type, [Value]); 'ASN1_OPEN_TYPE' -> NewValue = case Constraint of [#'Externaltypereference'{type=Tname}] -> diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index 764555c4d2..ce8be773d8 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -216,8 +216,6 @@ from_type_prim(M, D) -> _ -> [lists:nth(random(length(NN)),NN)] end; - 'ANY' -> - exit({asn1_error,nyi,'ANY'}); 'NULL' -> 'NULL'; 'OBJECT IDENTIFIER' -> -- cgit v1.2.3 From 7c026e9f74754df4138a02cb087f70c0f92d24c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 4 Mar 2013 20:32:52 +0100 Subject: asn1ct_check: Simplify code at the end of check_type/3 The code does record operations one step at the time and checks for conditions that cannot happen. --- lib/asn1/src/asn1ct_check.erl | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 4f1b0cac7a..91e77219c8 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -3567,29 +3567,14 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> Other -> exit({'cant check' ,Other}) end, - Ts2 = case NewDef of - #newt{type=unchanged} -> - Ts#type{def=Def}; - #newt{type=TDef}-> - Ts#type{def=TDef} - end, - NewTag = case NewDef of - #newt{tag=unchanged} -> - Tag; - #newt{tag=TT} -> - TT - end, - T3 = Ts2#type{tag = lists:map(fun(TempTag = #tag{type={default,TTx}}) -> - TempTag#tag{type=TTx}; - (Else) -> Else end, NewTag)}, - T4 = case NewDef of - #newt{constraint=unchanged} -> - T3#type{constraint=Constr}; - #newt{constraint=NewConstr} -> - T3#type{constraint=NewConstr} - end, - T5 = T4#type{inlined=NewDef#newt.inlined}, - T5#type{constraint=check_constraints(S,T5#type.constraint)}; + #newt{type=TDef,tag=NewTags,constraint=NewConstr,inlined=Inlined} = NewDef, + Ts#type{def=TDef, + inlined=Inlined, + constraint=check_constraints(S, NewConstr), + tag=lists:map(fun(#tag{type={default,TTx}}=TempTag) -> + TempTag#tag{type=TTx}; + (Other) -> Other + end, NewTags)}; check_type(_S,Type,Ts) -> exit({error,{asn1,internal_error,Type,Ts}}). -- cgit v1.2.3 From 6ec2f04e936bf8991cd398a8ae5c2f6f325a8122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Feb 2013 08:55:17 +0100 Subject: asn1_test_lib: Give more information when things goes wrong Ensure that compilation errors of Erlang code gets printed. If compilation of an ASN.1 specification goes wrong, print out the filename of the offending specification. It can be seen in the test case log, but because most test cases run in a parallel group, the test case log is not available until all test case in the group have finished. --- lib/asn1/test/asn1_test_lib.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index 1e40fd7b9e..191c321992 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -50,6 +50,7 @@ compile_file(File, Options) -> end catch Class:Reason -> + ct:print("Failed to compile ~s\n", [File]), erlang:error({compile_failed, {File, Options}, {Class, Reason}}) end. @@ -58,7 +59,7 @@ compile_erlang(Mod, Config, Options) -> CaseDir = ?config(case_dir, Config), M = list_to_atom(Mod), {ok, M} = compile:file(filename:join(DataDir, Mod), - [{i, CaseDir}, {outdir, CaseDir}|Options]). + [report,{i,CaseDir},{outdir,CaseDir}|Options]). should_load(File, Options) -> case lists:member(abs, Options) of -- cgit v1.2.3 From 09d41209d62b9c8010827cbbb7b7046460b90090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 5 Mar 2013 06:26:56 +0100 Subject: testExtensionAddtionGroup/1: Clean up and enhance For the example from X.691, make sure to test that our encoded value is the same as given in X.691. Run the test for BER, too. --- lib/asn1/test/asn1_SUITE.erl | 11 ++- .../asn1_SUITE_data/extensionAdditionGroup.erl | 82 ++++++++++++---------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 0c5ba43a5f..242456a4cf 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -1076,17 +1076,14 @@ ticket_6143(Config) -> ok = test_compile_options:ticket_6143(Config). testExtensionAdditionGroup(Config) -> - %% FIXME problems with automatic tags [ber_bin], [ber_bin, optimize] - test(Config, fun testExtensionAdditionGroup/3, [per, uper]). + test(Config, fun testExtensionAdditionGroup/3). testExtensionAdditionGroup(Config, Rule, Opts) -> asn1_test_lib:compile("Extension-Addition-Group", Config, [Rule|Opts]), asn1_test_lib:compile_erlang("extensionAdditionGroup", Config, [debug_info]), - extensionAdditionGroup:run([Rule|Opts]), - extensionAdditionGroup:run2([Rule|Opts]), - extensionAdditionGroup:run3(), - asn1_test_lib:compile("EUTRA-RRC-Definitions", Config, [Rule, {record_name_prefix, "RRC-"}|Opts]), - extensionAdditionGroup:run3([Rule|Opts]). + asn1_test_lib:compile("EUTRA-RRC-Definitions", Config, + [Rule,{record_name_prefix,"RRC-"}|Opts]), + extensionAdditionGroup:run(Rule). % parse_modules() -> % ["ImportsFrom"]. diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl index d46560979d..00e4c707dd 100644 --- a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl +++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl @@ -1,9 +1,3 @@ -%%%------------------------------------------------------------------- -%%% File : extensionAdditionGroup.erl -%%% Author : Kenneth Lundin -%%% Description : -%%% -%%% Created : 18 May 2010 by kenneth %% %% %CopyrightBegin% %% @@ -25,36 +19,38 @@ %%%------------------------------------------------------------------- -module(extensionAdditionGroup). -include("Extension-Addition-Group.hrl"). - +-export([run/1]). -compile(export_all). run(Erule) -> - Val = #'Ax'{a=253, b = true, c= {e,true}, g="123", h = true}, - io:format("~p:~p~n",[Erule,Val]), - {ok,List}= asn1rt:encode('Extension-Addition-Group','Ax',Val), - Enc = iolist_to_binary(List), - io:format("~p~n",[Enc]), - {ok,Val2} = asn1rt:decode('Extension-Addition-Group','Ax',Enc), - io:format("~p~n",[Val2]), - case Val2 of - Val -> ok; - _ -> exit({expected,Val, got, Val2}) - end. + Val = #'Ax'{a=253,b=true,c={e,true},g="123",h=true}, + Enc = hex_to_binary(encoded_ax(Erule)), + roundtrip('Ax', Val, Enc), -run2(Erule) -> - Val = #'Ax3'{a=253, b = true, s = #'Ax3_s'{sa = 11, sb = true, sextaddgroup = 17}}, - io:format("~p:~p~n",[Erule,Val]), - {ok,List}= asn1rt:encode('Extension-Addition-Group','Ax3',Val), - Enc = iolist_to_binary(List), - io:format("~p~n",[Enc]), - {ok,Val2} = asn1rt:decode('Extension-Addition-Group','Ax3',Enc), - io:format("~p~n",[Val2]), - case Val2 of - Val -> ok; - _ -> exit({expected,Val, got, Val2}) - end. + Val2 = #'Ax3'{a=253,b=true,s=#'Ax3_s'{sa=11,sb=true,sextaddgroup=17}}, + roundtrip('Ax3', Val2), + + run3(), + run3(Erule), + + ok. + +%% From X.691 (07/2002) A.4. +encoded_ax(per) -> "9E000180 010291A4"; +encoded_ax(uper) -> "9E000600 040A4690"; +encoded_ax(ber) -> none. +hex_to_binary(none) -> + none; +hex_to_binary(L) -> + << <<(hex_digit_to_binary(D)):4>> || D <- L, D =/= $\s >>. + +hex_digit_to_binary(D) -> + if + $0 =< D, D =< $9 -> D - $0; + $A =< D, D =< $F -> D - ($A-10) + end. run3(Erule) -> Val = {'RRC-DL-DCCH-Message', @@ -141,15 +137,23 @@ run3() -> 'ac-BarringFactor' = p00, 'ac-BarringTime' = s4, 'ac-BarringForSpecialAC' = <<0:5>>}, - roundtrip(SI), - roundtrip(SI#'SystemInformationBlockType2'{ - 'ssac-BarringForMMTEL-Voice-r9'=Barring}), - roundtrip(SI#'SystemInformationBlockType2'{ + T = 'SystemInformationBlockType2', + roundtrip(T, SI), + roundtrip(T, SI#'SystemInformationBlockType2'{ + 'ssac-BarringForMMTEL-Voice-r9'=Barring}), + roundtrip(T, SI#'SystemInformationBlockType2'{ 'ssac-BarringForMMTEL-Video-r9'=Barring}), - roundtrip(SI#'SystemInformationBlockType2'{ - 'ac-BarringForCSFB-r10'=Barring}). + roundtrip(T, SI#'SystemInformationBlockType2'{ + 'ac-BarringForCSFB-r10'=Barring}). -roundtrip(V) -> +roundtrip(T, V) -> + roundtrip(T, V, none). + +roundtrip(T, V, Expected) -> Mod = 'Extension-Addition-Group', - {ok,E} = Mod:encode('SystemInformationBlockType2', V), - {ok,V} = Mod:decode('SystemInformationBlockType2', iolist_to_binary(E)). + {ok,E} = Mod:encode(T, V), + {ok,V} = Mod:decode(T, E), + case Expected of + none -> ok; + E -> ok + end. -- cgit v1.2.3 From 59733fd6f95717cee0e67ccfcaa563ec9c56e5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 5 Mar 2013 06:41:48 +0100 Subject: ber_other/1: Test more specifications with the BER backend --- lib/asn1/test/asn1_SUITE.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 242456a4cf..5f17170b2c 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -1093,11 +1093,8 @@ per_modules() -> ber_modules() -> [X || X <- test_modules(), - X =/= "CommonDataTypes", - X =/= "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath", X =/= "H323-MESSAGES", - X =/= "H235-SECURITY-MESSAGES", - X =/= "MULTIMEDIA-SYSTEM-CONTROL"]. + X =/= "H235-SECURITY-MESSAGES"]. test_modules() -> ["BitStr", -- cgit v1.2.3 From 015f4ede0aa6464c98cbfe00066907d097bc0a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 5 Mar 2013 09:49:54 +0100 Subject: Clean up testPrimStrings Eliminate use of line macros and asn1_wrapper. --- lib/asn1/test/testPrimStrings.erl | 395 +++++++++++++------------------------- 1 file changed, 138 insertions(+), 257 deletions(-) diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index 6f5a7f2437..a347453d20 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -54,7 +54,7 @@ bit_string(Rules) -> bs_roundtrip('Bs1', [1,0,0,0,0,0,0,0,0]), bs_roundtrip('Bs1', [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]), - case asn1_wrapper:erule(Rules) of + case Rules of ber -> bs_decode('Bs1', <<35,8,3,2,0,73,3,2,4,32>>, [0,1,0,0,1,0,0,1,0,0,1,0]), @@ -62,7 +62,7 @@ bit_string(Rules) -> [1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]), bs_decode('Bs1', <<35,128,3,2,0,234,3,3,7,156,0,0,0>>, [1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]); - per -> + _ -> ok end, @@ -71,28 +71,9 @@ bit_string(Rules) -> %% Bs2 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (7)) %%========================================================== - ?line {ok,Bytes21} = asn1_wrapper:encode('PrimStrings','Bs2',[mo,tu,fr]), - ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs2',lists:flatten(Bytes21)), - - ?line {ok,Bytes22} = asn1_wrapper:encode('PrimStrings','Bs2',[0,1,1,0,0,1,0]), - ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs2',lists:flatten(Bytes22)), - ok, -%% skip this because it is wrong -% ?line case asn1_wrapper:erule(Rules) of -% ber -> -% ?line {ok,[mo,tu,fr,su,mo,th]} = -% asn1_wrapper:decode('PrimStrings','Bs2',[35,8,3,2,0,101,3,2,2,200]), - -% ?line {ok,[mo,tu,fr,su,mo,th]} = -% asn1_wrapper:decode('PrimStrings','Bs2',[35,128,3,2,1,100,3,2,2,200,0,0]), -% ok; - -% per -> -% ok -% end, - - - + roundtrip('Bs2', [mo,tu,fr]), + roundtrip('Bs2', [0,1,1,0,0,1,0], [mo,tu,fr]), + %%========================================================== %% Bs3 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..7)) %%========================================================== @@ -114,10 +95,9 @@ bit_string(Rules) -> %%========================================================== bs_roundtrip('BsPri', 45, [1,0,1,1,0,1]), - bs_roundtrip('BsPri', 211, [1,1,0,0,1,0,1,1]), - case asn1_wrapper:erule(Rules) of + case Rules of ber -> bs_decode('BsPri', <<223,61,4,5,75,226,96>>, [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]), @@ -127,7 +107,7 @@ bit_string(Rules) -> [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]), bs_decode('BsPri', <<255,61,128,3,2,0,75,3,3,5,226,96,0,0>>, [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]); - per -> + _ -> ok end, @@ -139,11 +119,11 @@ bit_string(Rules) -> bs_roundtrip('BsExpPri', 45, [1,0,1,1,0,1]), bs_roundtrip('BsExpPri', 211, [1,1,0,0,1,0,1,1]), - case asn1_wrapper:erule(Rules) of + case Rules of ber -> bs_decode('BsExpPri', <<255,61,6,3,4,5,75,226,96>>, [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]); - per -> + _ -> ok end, @@ -151,24 +131,22 @@ bit_string(Rules) -> %% TestS ::= BIT STRING {a(0),b(1)} (SIZE (3..8)), test case for OTP-4353 %%========================================================== - ?line {ok,Bytes53} = asn1_wrapper:encode('PrimStrings','TestS',[a]), - ?line {ok,[a]} = - asn1_wrapper:decode('PrimStrings','TestS',lists:flatten(Bytes53)), + roundtrip('TestS', [a]), %%========================================================== %% PersonalStatus ::= BIT STRING {married(0),employed(1), %% veteran(2), collegeGraduate(3)}, test case for OTP-5710 %%========================================================== - ?line {ok,Bytes54} = asn1_wrapper:encode('BitStr','PersonalStatus',[]), - ?line {ok,[]} = asn1_wrapper:decode('BitStr','PersonalStatus',Bytes54), + {ok,Bytes54} = 'BitStr':encode('PersonalStatus', []), + {ok,[]} = 'BitStr':decode('PersonalStatus', Bytes54), %%========================================================== %% BS5932 ::= BIT STRING (SIZE (5..MAX)) %% test case for OTP-5932 %%========================================================== bs_roundtrip('BSMAX', [1,0,1,0,1]), - case asn1_wrapper:erule(Rules) of + case Rules of ber -> {error,_} = 'PrimStrings':encode('BSMAX', [1,0,1]); _ -> @@ -203,21 +181,18 @@ octet_string(Rules) -> %% Os ::= OCTET STRING %%========================================================== - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,"Jones"} = - asn1_wrapper:decode('PrimStrings','Os',[4,5,16#4A,16#6F,16#6E,16#65,16#73]), - - ?line {ok,"Jones"} = - asn1_wrapper:decode('PrimStrings','Os',[36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73]), - - ?line {ok,"Jones"} = - asn1_wrapper:decode('PrimStrings','Os',[36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0]), - ok; - - per -> - ok - end, + case Rules of + ber -> + {ok,"Jones"} = + 'PrimStrings':decode('Os', <<4,5,16#4A,16#6F,16#6E,16#65,16#73>>), + {ok,"Jones"} = + 'PrimStrings':decode('Os', <<36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73>>), + {ok,"Jones"} = + 'PrimStrings':decode('Os', <<36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0>>), + ok; + _ -> + ok + end, roundtrip('Os', [47,23,99,255,1]), roundtrip('OsCon', [47,23,99,255,1]), @@ -239,18 +214,18 @@ octet_string(Rules) -> roundtrip('OsExpApp', OsR), - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','OsExpApp',[127,62,7,4,5,16#4A,16#6F,16#6E,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','OsExpApp',[127,62,11,36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','OsExpApp',[127,62,13,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','OsExpApp',[127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0]), - ?line {ok,"JonesJones"} = asn1_wrapper:decode('PrimStrings','OsExpApp',[127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0]), - ok; - - per -> - ok - end, + case Rules of + ber -> + {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,7,4,5,16#4A,16#6F,16#6E,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,11,36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,13,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0>>), + {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0>>), + {ok,"JonesJones"} = 'PrimStrings':decode('OsExpApp', <<127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0>>), + ok; + + _-> + ok + end, fragmented_octet_string(Rules), @@ -343,75 +318,58 @@ numeric_string(Rules) -> %%========================================================== roundtrip('Ns', []), + roundtrip('Ns', "01 34"), + case Rules of + ber -> + {ok,"Jones"} = 'PrimStrings':decode('Ns', + <<16#12,5,16#4A,16#6F, + 16#6E,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('Ns', + <<16#32,9,18,3,16#4A,16#6F, + 16#6E,18,2,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('Ns', + <<16#32,128,18,3,16#4A,16#6F, + 16#6E,18,2,16#65,16#73,0,0>>), + ok; + _ -> + ok + end, - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,BytesNs1} = asn1_wrapper:encode('PrimStrings','Ns',[48,49,32,51,52]), - ?line {ok,[48,49,32,51,52]} = asn1_wrapper:decode('PrimStrings','Ns',lists:flatten(BytesNs1)), - - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','Ns',[16#12,5,16#4A,16#6F,16#6E,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','Ns',[16#32,9,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','Ns',[16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0]), - ok; - - per -> - ?line {ok,BytesNs1} = asn1_wrapper:encode('PrimStrings','Ns',[48,49,32,51,52]), - ?line {ok,"01 34"} = asn1_wrapper:decode('PrimStrings','Ns',lists:flatten(BytesNs1)), - ok - end, - - - - %%========================================================== %% NsCon ::= [70] NumericString %%========================================================== roundtrip('NsCon', []), + roundtrip('NsCon', "01 34"), - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,BytesNs11} = asn1_wrapper:encode('PrimStrings','NsCon',[48,49,32,51,52]), - ?line {ok,[48,49,32,51,52]} = asn1_wrapper:decode('PrimStrings','NsCon',lists:flatten(BytesNs11)), - - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','NsCon',[16#9F,16#46,5,16#4A,16#6F,16#6E,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','NsCon',[16#BF,16#46,9,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','NsCon',[16#BF,16#46,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0]), - ok; - - per -> - ?line {ok,BytesNs11} = asn1_wrapper:encode('PrimStrings','NsCon',[48,49,32,51,52]), - ?line {ok,"01 34"} = asn1_wrapper:decode('PrimStrings','NsCon',lists:flatten(BytesNs11)), - ok - end, - + case Rules of + ber -> + {ok,"Jones"} = 'PrimStrings':decode('NsCon', <<16#9F,16#46,5,16#4A,16#6F,16#6E,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('NsCon', <<16#BF,16#46,9,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('NsCon', <<16#BF,16#46,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0>>), + ok; + _ -> + ok + end, - %%========================================================== %% NsExpCon ::= [71] EXPLICIT NumericString %%========================================================== roundtrip('NsExpCon', []), + roundtrip('NsExpCon', "01 34"), - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,BytesNs21} = asn1_wrapper:encode('PrimStrings','NsExpCon',[48,49,32,51,52]), - ?line {ok,[48,49,32,51,52]} = asn1_wrapper:decode('PrimStrings','NsExpCon',lists:flatten(BytesNs21)), - - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','NsExpCon',[16#BF,16#47,16#07,16#12,16#05,16#4A,16#6F,16#6E,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','NsExpCon',[16#BF,16#47,11,16#32,9,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73]), - ?line {ok,"Jones"} = asn1_wrapper:decode('PrimStrings','NsExpCon',[16#BF,16#47,128,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,0,0]), - ?line {ok,"JonesJones"} = asn1_wrapper:decode('PrimStrings','NsExpCon',[16#BF,16#47,26,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0]), - ?line {ok,"JonesJones"} = asn1_wrapper:decode('PrimStrings','NsExpCon',[16#BF,16#47,128,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,0,0]), - ok; - - per -> - ?line {ok,BytesNs21} = asn1_wrapper:encode('PrimStrings','NsExpCon',[48,49,32,51,52]), - ?line {ok,"01 34"} = asn1_wrapper:decode('PrimStrings','NsExpCon',lists:flatten(BytesNs21)), - ok - end, - - ok. + case Rules of + ber -> + {ok,"Jones"} = 'PrimStrings':decode('NsExpCon', <<16#BF,16#47,16#07,16#12,16#05,16#4A,16#6F,16#6E,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('NsExpCon', <<16#BF,16#47,11,16#32,9,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73>>), + {ok,"Jones"} = 'PrimStrings':decode('NsExpCon', <<16#BF,16#47,128,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,0,0>>), + {ok,"JonesJones"} = 'PrimStrings':decode('NsExpCon', <<16#BF,16#47,26,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0>>), + {ok,"JonesJones"} = 'PrimStrings':decode('NsExpCon', <<16#BF,16#47,128,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,16#32,128,18,3,16#4A,16#6F,16#6E,18,2,16#65,16#73,0,0,0,0>>), + ok; + _ -> + ok + end. other_strings(_Rules) -> @@ -507,23 +465,19 @@ universal_string(Rules) -> %%========================================================== roundtrip('Us', [{47,23,99,47},{0,0,55,66}]), - - ?line {ok,Bytes2} = - asn1_wrapper:encode('PrimStrings','Us',[{47,23,99,255},{0,0,0,201}]), - ?line {ok,[{47,23,99,255},201]} = - asn1_wrapper:decode('PrimStrings','Us',lists:flatten(Bytes2)), - + roundtrip('Us', + [{47,23,99,255},{0,0,0,201}], + [{47,23,99,255},201]), roundtrip('Us', "Universal String"), roundtrip('Us', []), roundtrip('Us', [{47,23,99,47}]), - ?line case asn1_wrapper:erule(Rules) of - ber -> - - ?line {ok,[{47,23,99,255},{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','Us',lists:flatten([16#3C,12,28,4,47,23,99,255,28,4,0,0,2,201])), - ?line {ok,[{47,23,99,255},{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','Us',lists:flatten([16#3C,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0])); + case Rules of + ber -> + {ok,[{47,23,99,255},{0,0,2,201}]} = + 'PrimStrings':decode('Us', <<16#3C,12,28,4,47,23,99,255,28,4,0,0,2,201>>), + {ok,[{47,23,99,255},{0,0,2,201}]} = + 'PrimStrings':decode('Us', <<16#3C,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0>>); _ -> ok end, @@ -538,24 +492,21 @@ universal_string(Rules) -> %%========================================================== roundtrip('UsCon', [{47,23,99,255},{0,0,2,201}]), - - ?line {ok,Bytes12} = - asn1_wrapper:encode('PrimStrings','UsCon',[{47,23,99,255},{0,0,0,201}]), - ?line {ok,[{47,23,99,255},201]} = - asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten(Bytes12)), - + roundtrip('UsCon', + [{47,23,99,255},{0,0,0,201}], + [{47,23,99,255},201]), roundtrip('UsCon', "Universal String"), roundtrip('UsCon', []), - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,[{47,23,99,255},{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten([16#BF,16#46,12,28,4,47,23,99,255,28,4,0,0,2,201])), - ?line {ok,[{47,23,99,255},{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten([16#BF,16#46,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0])); - _ -> ok - end, - + case Rules of + ber -> + {ok,[{47,23,99,255},{0,0,2,201}]} = + 'PrimStrings':decode('UsCon', <<16#BF,16#46,12,28,4,47,23,99,255,28,4,0,0,2,201>>), + {ok,[{47,23,99,255},{0,0,2,201}]} = + 'PrimStrings':decode('UsCon', <<16#BF,16#46,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0>>); + _ -> + ok + end, %%========================================================== @@ -563,25 +514,21 @@ universal_string(Rules) -> %%========================================================== roundtrip('UsExpCon', [{47,23,99,255},{0,0,2,201}]), - - ?line {ok,Bytes22} = - asn1_wrapper:encode('PrimStrings','UsExpCon',[{47,23,99,255},{0,0,0,201}]), - ?line {ok,[{47,23,99,255},201]} = - asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten(Bytes22)), - + roundtrip('UsExpCon', + [{47,23,99,255},{0,0,0,201}], + [{47,23,99,255},201]), roundtrip('UsExpCon', "Universal String"), roundtrip('UsExpCon', []), - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,[{47,23,99,255},{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten([16#BF,16#47,14,60,12,28,4,47,23,99,255,28,4,0,0,2,201])), - ?line {ok,[{47,23,99,255},{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten([16#BF,16#47,16,60,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0])); - _ -> ok - end, - - ok. + case Rules of + ber -> + {ok,[{47,23,99,255},{0,0,2,201}]} = + 'PrimStrings':decode('UsExpCon', <<16#BF,16#47,14,60,12,28,4,47,23,99,255,28,4,0,0,2,201>>), + {ok,[{47,23,99,255},{0,0,2,201}]} = + 'PrimStrings':decode('UsExpCon', <<16#BF,16#47,16,60,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0>>); + _ -> + ok + end. bmp_string(_Rules) -> @@ -591,12 +538,9 @@ bmp_string(_Rules) -> %%========================================================== roundtrip('BMP', [{0,0,99,48},{0,0,2,201}]), - - ?line {ok,Bytes2} = - asn1_wrapper:encode('PrimStrings','BMP',[{0,0,0,48},{0,0,2,201}]), - ?line {ok,[48,{0,0,2,201}]} = - asn1_wrapper:decode('PrimStrings','BMP',lists:flatten(Bytes2)), - + roundtrip('BMP', + [{0,0,0,48},{0,0,2,201}], + [48,{0,0,2,201}]), roundtrip('BMP', "BMP String"), roundtrip('BMP', []), @@ -605,9 +549,6 @@ bmp_string(_Rules) -> ok. - - - times(_Rules) -> @@ -636,94 +577,29 @@ utf8_string(_Rules) -> %% UTF ::= UTF8String %%========================================================== - %% test values in all ranges - - ValLbR1 = [16#00], - ValUbR1 = [16#7f], - ValLbR2 = [16#80], - ValUbR2 = [16#7ff], - ValLbR3 = [16#800], - ValUbR3 = [16#ffff], - ValLbR4 = [16#10000], - ValUbR4 = [16#1fffff], - ValLbR5 = [16#200000], - ValUbR5 = [16#3ffffff], - ValLbR6 = [16#4000000], - ValUbR6 = [16#7fffffff], - - ?line {ok,UTF8L1} = asn1rt:utf8_list_to_binary(ValLbR1), - ?line {ok,Bytes1} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L1), - ?line {ok,Bin1} = asn1_wrapper:decode('PrimStrings','UTF',Bytes1), - ?line {ok,ValLbR1} = wrapper_utf8_binary_to_list(Bin1), - - ?line {ok,UTF8L2} = asn1rt:utf8_list_to_binary(ValUbR1), - ?line {ok,Bytes2} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L2), - ?line {ok,Bin2} = asn1_wrapper:decode('PrimStrings','UTF',Bytes2), - ?line {ok,ValUbR1} = wrapper_utf8_binary_to_list(Bin2), - - ?line {ok,UTF8L3} = asn1rt:utf8_list_to_binary(ValLbR2), - ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L3), - ?line {ok,Bin3} = asn1_wrapper:decode('PrimStrings','UTF',Bytes3), - ?line {ok,ValLbR2} = wrapper_utf8_binary_to_list(Bin3), - - ?line {ok,UTF8L4} = asn1rt:utf8_list_to_binary(ValUbR2), - ?line {ok,Bytes4} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L4), - ?line {ok,Bin4} = asn1_wrapper:decode('PrimStrings','UTF',Bytes4), - ?line {ok,ValUbR2} = wrapper_utf8_binary_to_list(Bin4), - - ?line {ok,UTF8L5} = asn1rt:utf8_list_to_binary(ValLbR3), - ?line {ok,Bytes5} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L5), - ?line {ok,Bin5} = asn1_wrapper:decode('PrimStrings','UTF',Bytes5), - ?line {ok,ValLbR3} = wrapper_utf8_binary_to_list(Bin5), - - ?line {ok,UTF8L6} = asn1rt:utf8_list_to_binary(ValUbR3), - ?line {ok,Bytes6} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L6), - ?line {ok,Bin6} = asn1_wrapper:decode('PrimStrings','UTF',Bytes6), - ?line {ok,ValUbR3} = wrapper_utf8_binary_to_list(Bin6), - - ?line {ok,UTF8L7} = asn1rt:utf8_list_to_binary(ValLbR4), - ?line {ok,Bytes7} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L7), - ?line {ok,Bin7} = asn1_wrapper:decode('PrimStrings','UTF',Bytes7), - ?line {ok,ValLbR4} = wrapper_utf8_binary_to_list(Bin7), - - ?line {ok,UTF8L8} = asn1rt:utf8_list_to_binary(ValUbR4), - ?line {ok,Bytes8} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L8), - ?line {ok,Bin8} = asn1_wrapper:decode('PrimStrings','UTF',Bytes8), - ?line {ok,ValUbR4} = wrapper_utf8_binary_to_list(Bin8), - - ?line {ok,UTF8L9} = asn1rt:utf8_list_to_binary(ValLbR5), - ?line {ok,Bytes9} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L9), - ?line {ok,Bin9} = asn1_wrapper:decode('PrimStrings','UTF',Bytes9), - ?line {ok,ValLbR5} = wrapper_utf8_binary_to_list(Bin9), - - ?line {ok,UTF8L10} = asn1rt:utf8_list_to_binary(ValUbR5), - ?line {ok,Bytes10} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L10), - ?line {ok,Bin10} = asn1_wrapper:decode('PrimStrings','UTF',Bytes10), - ?line {ok,ValUbR5} = wrapper_utf8_binary_to_list(Bin10), - - ?line {ok,UTF8L11} = asn1rt:utf8_list_to_binary(ValLbR6), - ?line {ok,Bytes11} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L11), - ?line {ok,Bin11} = asn1_wrapper:decode('PrimStrings','UTF',Bytes11), - ?line {ok,ValLbR6} = wrapper_utf8_binary_to_list(Bin11), - - ?line {ok,UTF8L12} = asn1rt:utf8_list_to_binary(ValUbR6), - ?line {ok,Bytes12} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L12), - ?line {ok,Bin12} = asn1_wrapper:decode('PrimStrings','UTF',Bytes12), - ?line {ok,ValUbR6} = wrapper_utf8_binary_to_list(Bin12), - - LVal = ValLbR1++ValUbR1++ValLbR2++ValUbR2++ValLbR3++ValUbR3++ - ValLbR4++ValUbR4++ValLbR5++ValUbR5++ValLbR6++ValUbR6, - LongVal = LVal++LVal++LVal++LVal++LVal++LVal++LVal++"hello", - - ?line {ok,UTF8L13} = asn1rt:utf8_list_to_binary(LongVal), - ?line {ok,Bytes13} = asn1_wrapper:encode('PrimStrings','UTF',UTF8L13), - ?line {ok,Bin13} = asn1_wrapper:decode('PrimStrings','UTF',Bytes13), - ?line {ok,LongVal} = wrapper_utf8_binary_to_list(Bin13). + AllRanges = [16#00, + 16#7f, + 16#80, + 16#7ff, + 16#800, + 16#ffff, + 16#10000, + 16#1fffff, + 16#200000, + 16#3ffffff, + 16#4000000, + 16#7fffffff], + [begin + {ok,UTF8} = asn1rt:utf8_list_to_binary([Char]), + {ok,[Char]} = asn1rt:utf8_binary_to_list(UTF8), + roundtrip('UTF', UTF8) + end || Char <- AllRanges], + + {ok,UTF8} = asn1rt:utf8_list_to_binary(AllRanges), + {ok,AllRanges} = asn1rt:utf8_binary_to_list(UTF8), + roundtrip('UTF', UTF8), + ok. -wrapper_utf8_binary_to_list(L) when is_list(L) -> - asn1rt:utf8_binary_to_list(list_to_binary(L)); -wrapper_utf8_binary_to_list(B) -> - asn1rt:utf8_binary_to_list(B). shorten_by_two(Tuple) -> L = [case E of @@ -741,6 +617,11 @@ roundtrip(Type, Value) -> {ok,Value} = 'PrimStrings':decode(Type, Encoded), ok. +roundtrip(Type, Value, Expected) -> + {ok,Encoded} = 'PrimStrings':encode(Type, Value), + {ok,Expected} = 'PrimStrings':decode(Type, Encoded), + ok. + bs_roundtrip(Type, Value) -> bs_roundtrip(Type, Value, Value). -- cgit v1.2.3 From f863775f492d31f4f38fc6e661dd6391810cfc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 5 Mar 2013 12:36:09 +0100 Subject: Introduce asn1ct_gen_per:gen_decode_external/3 Hiding the details of decoding an external type will facilitate changing the calling convention in a future commit. --- lib/asn1/src/asn1ct_constructed_per.erl | 18 ++++-------- lib/asn1/src/asn1ct_gen_per.erl | 50 ++++++++++++++------------------- 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index bcd0a01bbb..7c0c3aae9e 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -658,7 +658,6 @@ gen_decode_sof_components(Erule,Typename,SeqOrSetOf,Cont) -> Cont#type.def), Conttype = asn1ct_gen:get_inner(Cont#type.def), Ctgenmod = asn1ct_gen:ct_gen_module(Erule), - CurrMod = get(currmod), case asn1ct_gen:type(Conttype) of {primitive,bif} -> Ctgenmod:gen_dec_prim(Erule,Cont,"Bytes"), @@ -667,10 +666,9 @@ gen_decode_sof_components(Erule,Typename,SeqOrSetOf,Cont) -> NewTypename = [Constructed_Suffix|Typename], emit({"'dec_",asn1ct_gen:list2name(NewTypename), "'(Bytes, telltype",ObjFun,"),",nl}); - #'Externaltypereference'{module=CurrMod,type=EType} -> - emit({"'dec_",EType,"'(Bytes,telltype),",nl}); - #'Externaltypereference'{module=EMod,type=EType} -> - emit({"'",EMod,"':'dec_",EType,"'(Bytes,telltype),",nl}); + #'Externaltypereference'{}=Etype -> + asn1ct_gen_per:gen_dec_external(Etype, "Bytes"), + emit([com,nl]); 'ASN1_OPEN_TYPE' -> Ctgenmod:gen_dec_prim(Erule,#type{def='ASN1_OPEN_TYPE'}, "Bytes"), @@ -1504,16 +1502,10 @@ gen_dec_line_dec_inf(Comp, DecInfObj) -> gen_dec_line_other(Erule, Atype, TopType, Comp) -> #'ComponentType'{name=Cname,typespec=Type} = Comp, - CurrMod = get(currmod), case asn1ct_gen:type(Atype) of - #'Externaltypereference'{module=CurrMod,type=EType} -> - fun(BytesVar) -> - emit({"'dec_",EType,"'(",BytesVar,",telltype)"}) - end; - #'Externaltypereference'{module=Mod,type=EType} -> + #'Externaltypereference'{}=Etype -> fun(BytesVar) -> - emit({"'",Mod,"':'dec_",EType,"'(",BytesVar, - ",telltype)"}) + asn1ct_gen_per:gen_dec_external(Etype, BytesVar) end; {primitive,bif} -> case Atype of diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 37f5b9c960..ec3d7f61ce 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -30,6 +30,7 @@ -export([gen_obj_code/3,gen_objectset_code/2]). -export([gen_decode/2, gen_decode/3]). -export([gen_encode/2, gen_encode/3]). +-export([gen_dec_external/2]). -export([extaddgroup2sequence/1]). -import(asn1ct_gen, [emit/1,demit/1]). @@ -547,16 +548,10 @@ gen_decode_objectfields(_, _, [], _, _, CAcc) -> gen_decode_field_call(_Erules, _ObjName, _FieldName, Bytes, - #'Externaltypereference'{module=M,type=T}) -> - CurrentMod = get(currmod), - if - M == CurrentMod -> - emit([" 'dec_",T,"'(",Bytes,", telltype)"]), - []; - true -> - emit([" '",M,"':'dec_",T,"'(",Bytes,", telltype)"]), - [] - end; + #'Externaltypereference'{}=Etype) -> + emit(" "), + gen_dec_external(Etype, Bytes), + []; gen_decode_field_call(Erules, ObjName, FieldName, Bytes, Type) -> Def = Type#typedef.typespec, case Type#typedef.name of @@ -578,7 +573,6 @@ gen_decode_field_call(Erules, ObjName, FieldName, Bytes, Type) -> end. gen_decode_default_call(Erules, ClassName, FieldName, Bytes, Type) -> - CurrentMod = get(currmod), InnerType = asn1ct_gen:get_inner(Type#type.def), case asn1ct_gen:type(InnerType) of {constructed,bif} -> @@ -589,11 +583,8 @@ gen_decode_default_call(Erules, ClassName, FieldName, Bytes, Type) -> {primitive,bif} -> gen_dec_prim(Erules, Type, Bytes), []; - #'Externaltypereference'{module=CurrentMod,type=Etype} -> - emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]), - []; - #'Externaltypereference'{module=Emod,type=Etype} -> - emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", telltype)",nl]), + #'Externaltypereference'{}=Etype -> + asn1ct_gen_per:gen_dec_external(Etype, Bytes), [] end. @@ -873,7 +864,6 @@ gen_inlined_dec_funs(Erule, Fields, List, ObjSetName, NthObj0) -> gen_inlined_dec_funs1(Erule, Fields, [{typefield,Name,_}|Rest], ObjSetName, Sep0, NthObj) -> - CurrentMod = get(currmod), InternalDefFunName = [NthObj,Name,ObjSetName], emit(Sep0), Sep = [";",nl], @@ -883,13 +873,10 @@ gen_inlined_dec_funs1(Erule, Fields, [{typefield,Name,_}|Rest], {_,#typedef{}=Type} -> emit([indent(9),{asis,Name}," ->",nl]), emit_inner_of_decfun(Erule, Type, InternalDefFunName); - {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"'dec_",T,"'(Val,telltype)"]), - 0; - {_,#'Externaltypereference'{module=M,type=T}} -> + {_,#'Externaltypereference'{}=Etype} -> emit([indent(9),{asis,Name}," ->",nl, - indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]), + indent(12)]), + gen_dec_external(Etype, "Val"), 0; false -> emit([indent(9),{asis,Name}," -> {Val,Type}"]), @@ -946,7 +933,6 @@ gen_internal_funcs(Erules,[TypeDef|Rest]) -> %% DECODING ***************************** %%*************************************** - gen_decode(Erules,Type) when is_record(Type,typedef) -> D = Type, emit({nl,nl}), @@ -983,7 +969,6 @@ dbdec(Type) -> demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}). gen_decode_user(Erules,D) when is_record(D,typedef) -> - CurrMod = get(currmod), Typename = [D#typedef.name], Def = D#typedef.typespec, InnerType = asn1ct_gen:get_inner(Def#type.def), @@ -996,14 +981,21 @@ gen_decode_user(Erules,D) when is_record(D,typedef) -> emit({".",nl,nl}); {constructed,bif} -> asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D); - #'Externaltypereference'{module=CurrMod,type=Etype} -> - emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl}); - #'Externaltypereference'{module=Emod,type=Etype} -> - emit({"'",Emod,"':'dec_",Etype,"'(Bytes,telltype).",nl,nl}); + #'Externaltypereference'{}=Etype -> + gen_dec_external(Etype, "Bytes"), + emit([".",nl,nl]); Other -> exit({error,{asn1,{unknown,Other}}}) end. +gen_dec_external(Ext, BytesVar) -> + CurrMod = get(currmod), + #'Externaltypereference'{module=Mod,type=Type} = Ext, + emit([case CurrMod of + Mod -> []; + _ -> ["'",Mod,"':"] + end,"'dec_",Type,"'(",BytesVar,",telltype)"]). + gen_dec_imm(Erule, #type{def=Name,constraint=C}) -> Aligned = case Erule of uper -> false; -- cgit v1.2.3 From a07a97291fcac1d3132a185d0efd7b4089720d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 6 Mar 2013 07:40:02 +0100 Subject: PER/UPER: Remove support for a list argument for encode_open_type/1 Almost always, encode_open_type/1 is called with the return value from complete/1, which always is a binary. In the rare situation that encode_open_type/1 is called directly with data from the user application, call iolist_to_binary/1 before calling encode_open_type/1. --- lib/asn1/src/asn1ct_gen_per.erl | 4 +++- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 4 +++- lib/asn1/src/asn1rtt_per.erl | 10 +--------- lib/asn1/src/asn1rtt_uper.erl | 4 +--- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index ec3d7f61ce..170a3c7352 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -207,7 +207,9 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> io_lib:format( "complete(enc_~s(~s))", [Tname,Value]); - _ -> Value + _ -> + io_lib:format("iolist_to_binary(~s)", + [Value]) end, call(Erules, encode_open_type, [NewValue]); #'ObjectClassFieldType'{} -> diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 8e9d90ae60..9fc6cea59a 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -111,7 +111,9 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> io_lib:format( "complete(enc_~s(~s))", [Tname,Value]); - _ -> Value + _ -> + io_lib:format("iolist_to_binary(~s)", + [Value]) end, call(Erules, encode_open_type, [NewValue]); #'ObjectClassFieldType'{} -> diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index 74ae11632d..21acb4e1ec 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -146,15 +146,7 @@ set_choice_tag(_Alt,[],_Tag) -> %% | binary %% Contraint = not used in this version %% -encode_open_type(Val) when is_list(Val) -> - Bin = list_to_binary(Val), - case byte_size(Bin) of - Size when Size > 255 -> - [encode_length(Size),21,<>,Bin]; - Size -> - [encode_length(Size),20,Size,Bin] - end; -encode_open_type(Val) when is_binary(Val) -> +encode_open_type(Val) -> case byte_size(Val) of Size when Size > 255 -> [encode_length(Size),21,<>,Val]; % octets implies align diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index 8cf23a9239..320038caa2 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -175,9 +175,7 @@ set_choice_tag(_Alt,[],_Tag) -> %% | binary %% Contraint = not used in this version %% -encode_open_type(Val) when is_list(Val) -> - encode_open_type(list_to_binary(Val)); -encode_open_type(Val) when is_binary(Val) -> +encode_open_type(Val) -> [encode_length(byte_size(Val)),Val]. -- cgit v1.2.3 From ad3cd6debf4173f44a4f72bc866b908739bdfc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 7 Mar 2013 11:43:53 +0100 Subject: asn1_test_lib: Purge old code when loading a new module In the 64-bit run-time, there is about 40Mb in old code after running the asn1 test suite. Since it is very easy to reclaim that memory, let's do it. Also get rid of the unnecessary call to code:soft_purge/1 just before loading the new code. --- lib/asn1/test/asn1_test_lib.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index 191c321992..fc237da868 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -45,8 +45,8 @@ compile_file(File, Options) -> ok; {module, Module} -> code:purge(Module), - true = code:soft_purge(Module), - {module, Module} = code:load_file(Module) + {module, Module} = code:load_file(Module), + code:purge(Module) end catch Class:Reason -> -- cgit v1.2.3 From 9d56389aa8fd0d366df7b0f666369d23cf946639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 7 Mar 2013 11:44:01 +0100 Subject: Generate one call to io:put_chars/2 for each call to asn1ct_gen:emit/1 asn1ct_gen:emit/1 used to make one call to io:put_chars/2 for each part of the term passed emit/1. By collecting all output into one iolist for each call emit/1 the time for running the entire asn1 test suite is reduced from about 460 seconds to 340 seconds on my computer. --- lib/asn1/src/asn1ct_gen.erl | 91 +++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 249cde96b5..a7a43b7426 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -1146,77 +1146,64 @@ demit(Term) -> end. % always generation +emit(Term) -> + io:put_chars(get(gen_file_out), do_emit(Term)). -emit({external,_M,T}) -> - emit(T); +do_emit({external,_M,T}) -> + do_emit(T); -emit({prev,Variable}) when is_atom(Variable) -> - emit({var,asn1ct_name:prev(Variable)}); +do_emit({prev,Variable}) when is_atom(Variable) -> + do_emit({var,asn1ct_name:prev(Variable)}); -emit({next,Variable}) when is_atom(Variable) -> - emit({var,asn1ct_name:next(Variable)}); +do_emit({next,Variable}) when is_atom(Variable) -> + do_emit({var,asn1ct_name:next(Variable)}); -emit({curr,Variable}) when is_atom(Variable) -> - emit({var,asn1ct_name:curr(Variable)}); +do_emit({curr,Variable}) when is_atom(Variable) -> + do_emit({var,asn1ct_name:curr(Variable)}); -emit({var,Variable}) when is_atom(Variable) -> +do_emit({var,Variable}) when is_atom(Variable) -> [Head|V] = atom_to_list(Variable), - emit([Head-32|V]); + [Head-32|V]; -emit({var,Variable}) -> +do_emit({var,Variable}) -> [Head|V] = Variable, - emit([Head-32|V]); + [Head-32|V]; -emit({asis,What}) -> - format(get(gen_file_out),"~w",[What]); +do_emit({asis,What}) -> + io_lib:format("~w", [What]); -emit({call,M,F,A}) -> - asn1ct_func:call(M, F, A); +do_emit({call,M,F,A}) -> + MFA = {M,F,length(A)}, + asn1ct_func:need(MFA), + [atom_to_list(F),"(",call_args(A, "")|")"]; -emit(nl) -> - nl(get(gen_file_out)); +do_emit(nl) -> + "\n"; -emit(com) -> - emit(","); +do_emit(com) -> + ","; -emit(tab) -> - put_chars(get(gen_file_out)," "); +do_emit(tab) -> + " "; -emit(What) when is_integer(What) -> - put_chars(get(gen_file_out),integer_to_list(What)); +do_emit(What) when is_integer(What) -> + integer_to_list(What); -emit(What) when is_list(What), is_integer(hd(What)) -> - put_chars(get(gen_file_out),What); +do_emit(What) when is_list(What), is_integer(hd(What)) -> + What; -emit(What) when is_atom(What) -> - put_chars(get(gen_file_out),atom_to_list(What)); +do_emit(What) when is_atom(What) -> + atom_to_list(What); -emit(What) when is_tuple(What) -> - emit_parts(tuple_to_list(What)); +do_emit(What) when is_tuple(What) -> + [do_emit(E) || E <- tuple_to_list(What)]; -emit(What) when is_list(What) -> - emit_parts(What); +do_emit(What) when is_list(What) -> + [do_emit(E) || E <- What]. -emit(X) -> - exit({'cant emit ',X}). - -emit_parts([]) -> true; -emit_parts([H|T]) -> - emit(H), - emit_parts(T). - -format(undefined,X,Y) -> - io:format(X,Y); -format(X,Y,Z) -> - io:format(X,Y,Z). - -nl(undefined) -> io:nl(); -nl(X) -> io:nl(X). - -put_chars(undefined,X) -> - io:put_chars(X); -put_chars(Y,X) -> - io:put_chars(Y,X). +call_args([A|As], Sep) -> + [Sep,do_emit(A)|call_args(As, ", ")]; +call_args([], _) -> []. fopen(F, ModeList) -> case file:open(F, ModeList) of -- cgit v1.2.3 From 77fde7589ae338efa15fecfb5f75ec9168fa921f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 7 Mar 2013 12:33:26 +0100 Subject: Open the output file in raw mode with delayed write This change brings down the execution time on my computer for the entire asn1 test suite from about 340 seconds to 310 seconds. --- lib/asn1/src/asn1ct_func.erl | 21 ++++++++------------- lib/asn1/src/asn1ct_gen.erl | 10 +++++----- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/asn1/src/asn1ct_func.erl b/lib/asn1/src/asn1ct_func.erl index 2d221ca1b9..262bef6862 100644 --- a/lib/asn1/src/asn1ct_func.erl +++ b/lib/asn1/src/asn1ct_func.erl @@ -37,9 +37,13 @@ need(MFA) -> cast({need,MFA}). generate(Fd) -> - req({generate,Fd}), + Used0 = req(get_used), erase(?MODULE), - ok. + Used = sofs:set(Used0, [mfa]), + Code = sofs:relation(asn1ct_rtt:code(), [{mfa,code}]), + Funcs0 = sofs:image(Code, Used), + Funcs = sofs:to_external(Funcs0), + ok = file:write(Fd, Funcs). req(Req) -> gen_server:call(get(?MODULE), Req, infinity). @@ -64,9 +68,8 @@ handle_cast({need,MFA}, #st{used=Used0}=St) -> {noreply,St} end. -handle_call({generate,Fd}, _From, #st{used=Used}=St) -> - generate(Fd, Used), - {stop,normal,ok,St}. +handle_call(get_used, _From, #st{used=Used}=St) -> + {stop,normal,gb_sets:to_list(Used),St}. terminate(_, _) -> ok. @@ -75,14 +78,6 @@ call_args([A|As], Sep) -> [Sep,A|call_args(As, ", ")]; call_args([], _) -> []. -generate(Fd, Used0) -> - Used1 = gb_sets:to_list(Used0), - Used = sofs:set(Used1, [mfa]), - Code = sofs:relation(asn1ct_rtt:code(), [{mfa,code}]), - Funcs0 = sofs:image(Code, Used), - Funcs = sofs:to_external(Funcs0), - io:put_chars(Fd, Funcs). - pull_in_deps(Ws0, Used0) -> case gb_sets:is_empty(Ws0) of true -> diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index a7a43b7426..570f41f91d 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -75,7 +75,7 @@ pgen_module(OutFile,Erules,Module, HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent), asn1ct_name:start(), ErlFile = lists:concat([OutFile,".erl"]), - Fid = fopen(ErlFile,[write]), + Fid = fopen(ErlFile), put(gen_file_out,Fid), asn1ct_func:start_link(), gen_head(Erules,Module,HrlGenerated), @@ -1131,7 +1131,7 @@ pgen_info() -> open_hrl(OutFile,Module) -> File = lists:concat([OutFile,".hrl"]), - Fid = fopen(File,[write]), + Fid = fopen(File), put(gen_file_out,Fid), gen_hrlhead(Module). @@ -1147,7 +1147,7 @@ demit(Term) -> % always generation emit(Term) -> - io:put_chars(get(gen_file_out), do_emit(Term)). + ok = file:write(get(gen_file_out), do_emit(Term)). do_emit({external,_M,T}) -> do_emit(T); @@ -1205,8 +1205,8 @@ call_args([A|As], Sep) -> [Sep,do_emit(A)|call_args(As, ", ")]; call_args([], _) -> []. -fopen(F, ModeList) -> - case file:open(F, ModeList) of +fopen(F) -> + case file:open(F, [write,raw,delayed_write]) of {ok, Fd} -> Fd; {error, Reason} -> -- cgit v1.2.3 From c2ec5589d5eaf9ef791932a187b4527ab737e3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 8 Mar 2013 15:11:05 +0100 Subject: BER: Optimize handling of constraints for INTEGERs This slight optimization will also eliminate some Dialyzer warnings. --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 73 ++++++++++++++++++---------------- lib/asn1/src/asn1rtt_ber.erl | 31 +++++++-------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 4073643f9e..c57bb6a335 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -458,16 +458,7 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> %% Constraint = Att#type.constraint, %% Constraint = [], Constraint = get_size_constraint(Att#type.constraint), - ValueRange = - case get_constraint(Att#type.constraint,'ValueRange') of - no -> []; - Tv -> Tv - end, - SingleValue = - case get_constraint(Att#type.constraint,'SingleValue') of - no -> []; - Sv -> Sv - end, + IntConstr = int_constr(Att#type.constraint), AsBin = case get(binary_strings) of true -> "_as_bin"; _ -> "" @@ -490,14 +481,27 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> emit(["decode_boolean(",BytesVar,","]), need(decode_boolean, 2); 'INTEGER' -> - emit(["decode_integer(",BytesVar,",", - {asis,int_constr(SingleValue,ValueRange)},","]), - need(decode_integer, 3); + case IntConstr of + [] -> + emit(["decode_integer(",BytesVar,","]), + need(decode_integer, 2); + {_,_} -> + emit(["decode_integer(",BytesVar,",", + {asis,IntConstr},","]), + need(decode_integer, 3) + end; {'INTEGER',NamedNumberList} -> - emit(["decode_integer(",BytesVar,",", - {asis,int_constr(SingleValue,ValueRange)},",", - {asis,NamedNumberList},","]), - need(decode_integer, 4); + case IntConstr of + [] -> + emit(["decode_named_integer(",BytesVar,",", + {asis,NamedNumberList},","]), + need(decode_named_integer, 3); + {_,_} -> + emit(["decode_named_integer(",BytesVar,",", + {asis,IntConstr},",", + {asis,NamedNumberList},","]), + need(decode_named_integer, 4) + end; {'ENUMERATED',NamedNumberList} -> emit(["decode_enumerated(",BytesVar,",", {asis,NamedNumberList},","]), @@ -591,15 +595,23 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> emit([TagStr,")"]) end. - -int_constr([],[]) -> - []; -int_constr([],ValueRange) -> - ValueRange; -int_constr(SingleValue,[]) -> - SingleValue; -int_constr(SV,VR) -> - [SV,VR]. +%% Simplify an integer constraint so that we can efficiently test it. +-spec int_constr(term()) -> [] | {integer(),integer()|'MAX'}. +int_constr(C) -> + case asn1ct_imm:effective_constraint(integer, C) of + [{_,[]}] -> + %% Extension - ignore constraint. + []; + [{'ValueRange',{'MIN',_}}] -> + %% Tricky to implement efficiently - ignore it. + []; + [{'ValueRange',{_,_}=Range}] -> + Range; + [{'SingleValue',Sv}] -> + {Sv,Sv}; + [] -> + [] + end. gen_dec_bit_string(BytesVar, _Constraint, [_|_]=NNL, TagStr) -> call(decode_named_bit_string, @@ -1481,15 +1493,6 @@ get_size_constraint(C) -> {_,{_,_}=Tc} -> Tc end. -get_constraint(C,Key) -> - case lists:keysearch(Key,1,C) of - false -> - no; - {value,{_,V}} -> - V - end. - - get_class_fields(#classdef{typespec=ObjClass}) -> ObjClass#objectclass.fields; get_class_fields(#objectclass{fields=Fields}) -> diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 509c091355..499db701d9 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -27,7 +27,8 @@ skip_ExtensionAdditions/2]). -export([encode_boolean/2,decode_boolean/2, encode_integer/2,encode_integer/3, - decode_integer/3,decode_integer/4, + decode_integer/2,decode_integer/3, + decode_named_integer/3,decode_named_integer/4, encode_enumerated/2,decode_enumerated/3, encode_bit_string/4, decode_named_bit_string/3, @@ -700,11 +701,20 @@ encode_integer_neg(N, Acc) -> %% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes} %%=============================================================================== -decode_integer(Tlv, Range, NamedNumberList, TagIn) -> +decode_named_integer(Tlv, NamedNumberList, TagIn) -> + V = match_tags(Tlv, TagIn), + Int = decode_integer(V), + number2name(Int, NamedNumberList). + +decode_named_integer(Tlv, Range, NamedNumberList, TagIn) -> V = match_tags(Tlv, TagIn), Int = range_check_integer(decode_integer(V), Range), number2name(Int, NamedNumberList). +decode_integer(Tlv, TagIn) -> + V = match_tags(Tlv, TagIn), + decode_integer(V). + decode_integer(Tlv, Range, TagIn) -> V = match_tags(Tlv, TagIn), Int = decode_integer(V), @@ -715,21 +725,10 @@ decode_integer(Bin) -> <> = Bin, Int. +range_check_integer(Int, {Lb,Ub}) when Lb =< Int, Int =< Ub -> + Int; range_check_integer(Int, Range) -> - case Range of - [] -> % No length constraint - Int; - {Lb,Ub} when Int >= Lb, Ub >= Int -> % variable length constraint - Int; - {_,_} -> - exit({error,{asn1,{integer_range,Range,Int}}}); - Int -> % fixed value constraint - Int; - SingleValue when is_integer(SingleValue) -> - exit({error,{asn1,{integer_range,Range,Int}}}); - _ -> % some strange constraint that we don't support yet - Int - end. + exit({error,{asn1,{integer_range,Range,Int}}}). number2name(Int, []) -> Int; -- cgit v1.2.3 From 2138a1eb879f37141378f239d7efa2e324f5e731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 8 Mar 2013 15:25:42 +0100 Subject: BER: Remove special case handling of certain string types ObjectDescriptor, UTCTime, and GeneralizedTime are not special and can be handled in the same way as all other restricted string types. --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 21 ++++--------- lib/asn1/src/asn1rtt_ber.erl | 54 +--------------------------------- 2 files changed, 6 insertions(+), 69 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index c57bb6a335..a31074b4c1 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -168,6 +168,8 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> 'GeneralString' -> restricted_string; 'PrintableString' -> restricted_string; 'IA5String' -> restricted_string; + 'UTCTime' -> restricted_string; + 'GeneralizedTime' -> restricted_string; Other -> Other end, case Type of @@ -204,10 +206,6 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> call(encode_UTF8_string, [Value,DoTag]); 'BMPString' -> call(encode_BMP_string, [Value,DoTag]); - 'UTCTime' -> - call(encode_utc_time, [Value,DoTag]); - 'GeneralizedTime' -> - call(encode_generalized_time, [Value,DoTag]); 'ASN1_OPEN_TYPE' -> call(encode_open_type, [Value,DoTag]); #'ObjectClassFieldType'{} -> @@ -474,6 +472,9 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> 'GeneralString' -> restricted_string; 'PrintableString' -> restricted_string; 'IA5String' -> restricted_string; + 'ObjectDescriptor'-> restricted_string; + 'UTCTime' -> restricted_string; + 'GeneralizedTime' -> restricted_string; _ -> Typename end, case NewTypeName of @@ -519,10 +520,6 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> 'RELATIVE-OID' -> emit(["decode_relative_oid(",BytesVar,","]), need(decode_relative_oid, 2); - 'ObjectDescriptor' -> - emit(["decode_restricted_string(", - BytesVar,",",{asis,Constraint},","]), - need(decode_restricted_string, 3); restricted_string -> emit(["decode_restricted_string",AsBin,"(",BytesVar,","]), case Constraint of @@ -544,14 +541,6 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> emit(["decode_BMP_string",AsBin,"(", BytesVar,",",{asis,Constraint},","]), need(decode_BMP_string, 3); - 'UTCTime' -> - emit(["decode_utc_time",AsBin,"(", - BytesVar,",",{asis,Constraint},","]), - need(decode_utc_time, 3); - 'GeneralizedTime' -> - emit(["decode_generalized_time",AsBin,"(", - BytesVar,",",{asis,Constraint},","]), - need(decode_generalized_time, 3); 'ASN1_OPEN_TYPE' -> emit(["decode_open_type_as_binary(", BytesVar,","]), diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 499db701d9..b5429fe324 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -42,9 +42,7 @@ decode_restricted_string/2,decode_restricted_string/3, encode_universal_string/2,decode_universal_string/3, encode_UTF8_string/2,decode_UTF8_string/2, - encode_BMP_string/2,decode_BMP_string/3, - encode_generalized_time/2,decode_generalized_time/3, - encode_utc_time/2,decode_utc_time/3]). + encode_BMP_string/2,decode_BMP_string/3]). -export([encode_open_type/2,decode_open_type/2, decode_open_type_as_binary/2]). @@ -1376,56 +1374,6 @@ mk_BMP_string([0,B|T], US) -> mk_BMP_string([C,D|T], US) -> mk_BMP_string(T, [{0,0,C,D}|US]). - -%%============================================================================ -%% Generalized time, ITU_T X.680 Chapter 39 -%% -%% encode Generalized time -%%============================================================================ - -encode_generalized_time(OctetList, TagIn) -> - encode_tags(TagIn, OctetList, length(OctetList)). - -%%============================================================================ -%% decode Generalized time -%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes} -%%============================================================================ - -decode_generalized_time(Tlv, _Range, Tags) -> - Val = match_tags(Tlv, Tags), - NewVal = case Val of - [_H|_T]=PartList -> % constructed - collect_parts(PartList); - Bin -> - Bin - end, - binary_to_list(NewVal). - -%%============================================================================ -%% Universal time, ITU_T X.680 Chapter 40 -%% -%% encode UTC time -%%============================================================================ - -encode_utc_time(OctetList, TagIn) -> - encode_tags(TagIn, OctetList, length(OctetList)). - -%%============================================================================ -%% decode UTC time -%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes} -%%============================================================================ - -decode_utc_time(Tlv, _Range, Tags) -> - Val = match_tags(Tlv, Tags), - NewVal = case Val of - [_|_]=PartList -> % constructed - collect_parts(PartList); - Bin -> - Bin - end, - binary_to_list(NewVal). - - %%============================================================================ %% Length handling %% -- cgit v1.2.3 From c7ccb343abe2858ee4296cdd549b369423ac3b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 9 Mar 2013 07:38:21 +0100 Subject: asn1ct, asn1ct_gen: Eliminate unused exports Stop export functions that are not called from outside their module. If the functions are not used at all, remove the functions too. The unused exports were found by running: xref:start(s). xref:add_application(s, code:lib_dir(asn1)). io:format("~p\n", [xref:analyze(s, exports_not_used)]). --- lib/asn1/src/asn1ct.erl | 67 ++++----------------------------------------- lib/asn1/src/asn1ct_gen.erl | 46 +------------------------------ 2 files changed, 7 insertions(+), 106 deletions(-) diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 770b92cbc3..7f9d5d821a 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -25,21 +25,20 @@ %%-compile(export_all). %% Public exports -export([compile/1, compile/2]). --export([start/0, start/1]). -export([encode/2, encode/3, decode/3]). -export([test/1, test/2, test/3, value/2, value/3]). %% Application internal exports -export([compile_asn/3,compile_asn1/3,compile_py/3,compile/3, vsn/0, get_name_of_def/1,get_pos_of_def/1]). --export([read_config_data/1,get_gen_state_field/1,get_gen_state/0, - partial_inc_dec_toptype/1,save_gen_state/1,update_gen_state/2, +-export([read_config_data/1,get_gen_state_field/1, + partial_inc_dec_toptype/1,update_gen_state/2, get_tobe_refed_func/1,reset_gen_state/0,is_function_generated/1, - generated_refed_func/1,next_refed_func/0,pop_namelist/0, - next_namelist_el/0,update_namelist/1,step_in_constructed/0, + generated_refed_func/1,next_refed_func/0, + update_namelist/1,step_in_constructed/0, add_tobe_refed_func/1,add_generated_refed_func/1, - maybe_rename_function/3,latest_sindex/0,current_sindex/0, - set_current_sindex/1,next_sindex/0,maybe_saved_sindex/2, + maybe_rename_function/3,current_sindex/0, + set_current_sindex/1,maybe_saved_sindex/2, parse_and_save/2,verbose/3,warning/3,warning/4,error/3]). -export([get_bit_string_format/0]). @@ -2097,52 +2096,6 @@ update_namelist(Name) -> Other -> Other end. -pop_namelist() -> - DeepTail = %% removes next element in order - fun([[{_,A}]|T],_Fun) when is_atom(A) -> T; - ([{_N,L}|T],_Fun) when is_list(L) -> [L|T]; - ([[]|T],Fun) -> Fun(T,Fun); - ([L1|L2],Fun) when is_list(L1) -> - case lists:flatten(L1) of - [] -> Fun([L2],Fun); - _ -> [Fun(L1,Fun)|L2] - end; - ([_H|T],_Fun) -> T - end, - {Pop,NewNL} = - case get_gen_state_field(namelist) of - [] -> {[],[]}; - L -> - {next_namelist_el(L), - DeepTail(L,DeepTail)} - end, - update_gen_state(namelist,NewNL), - Pop. - -%% next_namelist_el fetches the next type/component name in turn in -%% the namelist, without changing the namelist. -next_namelist_el() -> - case get_gen_state_field(namelist) of - undefined -> undefined; - L when is_list(L) -> next_namelist_el(L) - end. - -next_namelist_el([]) -> - []; -next_namelist_el([L]) when is_list(L) -> - next_namelist_el(L); -next_namelist_el([H|_]) when is_atom(H) -> - H; -next_namelist_el([L|T]) when is_list(L) -> - case next_namelist_el(L) of - [] -> - next_namelist_el([T]); - R -> - R - end; -next_namelist_el([H={_,A}|_]) when is_atom(A) -> - H. - %% removes a bracket from the namelist step_in_constructed() -> case get_gen_state_field(namelist) of @@ -2382,14 +2335,6 @@ maybe_saved_sindex(Name,Pattern) -> end end. -next_sindex() -> - SI = get_gen_state_field(suffix_index), - update_gen_state(suffix_index,SI+1), - SI+1. - -latest_sindex() -> - get_gen_state_field(suffix_index). - current_sindex() -> get_gen_state_field(current_suffix_index). diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 570f41f91d..5cddaba52e 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -21,15 +21,9 @@ -include("asn1_records.hrl"). --export([pgen_exports/3, - pgen_hrl/5, - gen_head/3, - demit/1, +-export([demit/1, emit/1, get_inner/1,type/1,def_to_tag/1,prim_bif/1, - type_from_object/1, - get_typefromobject/1,get_fieldcategory/2, - get_classfieldcategory/2, list2name/1, list2rname/1, constructed_suffix/2, @@ -41,7 +35,6 @@ index2suffix/1, get_record_name_prefix/0]). -export([pgen/5, - pgen_module/6, mk_var/1, un_hyphen_var/1]). -export([gen_encode_constructed/4, @@ -1767,15 +1760,6 @@ def_to_tag(Def) -> %% Information Object Class -type_from_object(X) -> - case (catch lists:last(element(2,X))) of - {'EXIT',_} -> - {notype,X}; - Normal -> - Normal - end. - - get_fieldtype([],_FieldName)-> {no_type,no_name}; get_fieldtype([Field|Rest],FieldName) -> @@ -1791,34 +1775,6 @@ get_fieldtype([Field|Rest],FieldName) -> get_fieldtype(Rest,FieldName) end. -get_fieldcategory([],_FieldName) -> - no_cat; -get_fieldcategory([Field|Rest],FieldName) -> - case element(2,Field) of - FieldName -> - element(1,Field); - _ -> - get_fieldcategory(Rest,FieldName) - end. - -get_typefromobject(Type) when is_record(Type,type) -> - case Type#type.def of - {{objectclass,_,_},TypeFrObj} when is_list(TypeFrObj) -> - {_,FieldName} = lists:last(TypeFrObj), - FieldName; - _ -> - {no_field} - end. - -get_classfieldcategory(Type,FieldName) -> - case (catch Type#type.def) of - {{obejctclass,Fields,_},_} -> - get_fieldcategory(Fields,FieldName); - {'EXIT',_} -> - no_cat; - _ -> - no_cat - end. %% Information Object Class %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From de6152b91af13a07c67280125aebe9dad4dd9f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 11 Mar 2013 15:14:12 +0100 Subject: BER: Don't generate functions that are never used --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 42 +++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index a31074b4c1..9c224d64a3 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -69,8 +69,8 @@ %% encode #{typedef, {pos, name, typespec}} %%=============================================================================== -gen_encode(Erules,Type) when is_record(Type,typedef) -> - gen_encode_user(Erules,Type). +gen_encode(Erules, #typedef{}=D) -> + gen_encode_user(Erules, #typedef{}=D, true). %%=============================================================================== %% encode #{type, {tag, def, constraint}} @@ -120,20 +120,28 @@ gen_encode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) -> NewType = Type#type{tag=[]}, gen_encode(Erules,NewTname,NewType). -gen_encode_user(Erules,D) when is_record(D,typedef) -> +gen_encode_user(Erules, #typedef{}=D, Wrapper) -> Typename = [D#typedef.name], Type = D#typedef.typespec, InnerType = asn1ct_gen:get_inner(Type#type.def), - OTag = Type#type.tag, - Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag], emit([nl,nl,"%%================================"]), emit([nl,"%% ",Typename]), emit([nl,"%%================================",nl]), - emit(["'enc_",asn1ct_gen:list2name(Typename), - "'(Val",") ->",nl]), - emit([" 'enc_",asn1ct_gen:list2name(Typename), - "'(Val, ", {asis,lists:reverse(Tag)},").",nl,nl]), - emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn) ->",nl}), + FuncName = "'enc_" ++ asn1ct_gen:list2name(Typename) ++ "'", + case Wrapper of + true -> + %% This is a top-level type. Generate an 'enc_Type'/1 + %% wrapper. + OTag = Type#type.tag, + Tag0 = [encode_tag_val(decode_class(Class), Form, Number) || + #tag{class=Class,form=Form,number=Number} <- OTag], + Tag = lists:reverse(Tag0), + emit([FuncName,"(Val) ->",nl, + " ",FuncName,"(Val, ",{asis,Tag},").",nl,nl]); + false -> + ok + end, + emit([FuncName,"(Val, TagIn) ->",nl]), CurrentMod = get(currmod), case asn1ct_gen:type(InnerType) of {constructed,bif} -> @@ -735,7 +743,7 @@ gen_encode_objectfields(_,[],_,_,Acc) -> gen_encode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) -> case is_already_generated(enc,TypeDef#typedef.name) of true -> ok; - _ -> gen_encode_user(Erules,TypeDef) + false -> gen_encode_user(Erules, TypeDef, false) end, gen_encode_constr_type(Erules,Rest); gen_encode_constr_type(_,[]) -> @@ -942,7 +950,10 @@ gen_decode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) -> case is_already_generated(dec,TypeDef#typedef.name) of true -> ok; _ -> - gen_decode(Erules,TypeDef) + emit([nl,nl, + "'dec_",TypeDef#typedef.name, + "'(Tlv, TagIn) ->",nl]), + gen_decode_user(Erules, TypeDef) end, gen_decode_constr_type(Erules,Rest); gen_decode_constr_type(_,[]) -> @@ -1404,10 +1415,9 @@ emit_inner_of_decfun(Type,Prop,_) when is_record(Type,type) -> gen_internal_funcs(_,[]) -> ok; gen_internal_funcs(Erules,[TypeDef|Rest]) -> - gen_encode_user(Erules,TypeDef), - emit([nl,nl,"'dec_",TypeDef#typedef.name, -% "'(Tlv, OptOrMand, TagIn) ->",nl]), - "'(Tlv, TagIn) ->",nl]), + gen_encode_user(Erules, TypeDef, false), + emit([nl,nl, + "'dec_",TypeDef#typedef.name,"'(Tlv, TagIn) ->",nl]), gen_decode_user(Erules,TypeDef), gen_internal_funcs(Erules,Rest). -- cgit v1.2.3 From 66cc5110814e74d602f6bda65e8388babc3982a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 11 Mar 2013 15:14:32 +0100 Subject: When testing, don't tolerate warnings from the BEAM compiler --- lib/asn1/test/asn1_test_lib.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index fc237da868..b839dfcf2a 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -39,7 +39,7 @@ compile_all(Files, Config, Options) -> compile_file(File, Options) -> try - ok = asn1ct:compile(File, Options), + ok = asn1ct:compile(File, [warnings_as_errors|Options]), case should_load(File, Options) of false -> ok; -- cgit v1.2.3 From f4e25971233abdc0fe8872cb8d7b5113d4198a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 12 Mar 2013 16:45:16 +0100 Subject: Eliminate the {notype,_} return value from asn1ct_gen:type/1 The last clause in asn1ct_gen:type/1 does a catched call to type2/1. If the type2/1 fails {notype,X} is returned. Since the body of type2/1 essentially is: case lists:member(X, [...]) of true -> {primitive,bif}; false -> case lists:member(X, [...]) of true -> {constructed,bif}; false -> {undefined,user} end end there is no way that type2/1 can fail. Therefore, we can eliminate the catch and put the body of type2/1 into the last clause of type/1. We can also eliminate the code in the callers of type/1 that match {notype,X}. --- lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 6 +----- lib/asn1/src/asn1ct_constructed_per.erl | 8 -------- lib/asn1/src/asn1ct_gen.erl | 9 --------- lib/asn1/src/asn1ct_gen_per.erl | 4 +--- lib/asn1/src/asn1ct_value.erl | 2 -- 5 files changed, 2 insertions(+), 27 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index bc8189c2c8..9a55b139b9 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -999,9 +999,7 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj) {componentrelation,_,_}} -> {_LeadingAttrName,Fun} = EncObj, case RefedFieldName of -%% {notype,T} -> -%% throw({error,{notype,type_from_object,T}}); - {Name,RestFieldNames} when is_atom(Name), Name =/= notype -> + {Name,RestFieldNames} when is_atom(Name) -> case OptOrMand of mandatory -> ok; _ -> @@ -1041,8 +1039,6 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj) end, ?ASN1CT_GEN_BER:gen_encode_prim(ber,EncType,{asis,Tag}, Element); -%% {notype,_} -> -%% emit(["'enc_",InnerType,"'(",Element,", ",{asis,Tag},")"]); 'ASN1_OPEN_TYPE' -> case Type#type.def of #'ObjectClassFieldType'{} -> %Open Type diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 7c0c3aae9e..da8b59ad25 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1012,8 +1012,6 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> case DynamicEnc of {_LeadingAttrName,Fun} -> case (Type#type.def)#'ObjectClassFieldType'.fieldname of - {notype,T} -> - throw({error,{notype,type_from_object,T}}); {Name,RestFieldNames} when is_atom(Name) -> asn1ct_func:need({Erule,complete,1}), asn1ct_func:need({Erule,encode_open_type,1}), @@ -1043,8 +1041,6 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> #'Externaltypereference'{module=Mod,type=EType} -> emit({"'",Mod,"':'enc_", EType,"'(",Element,")"}); - {notype,_} -> - emit({"'enc_",Atype,"'(",Element,")"}); {primitive,bif} -> EncType = case Atype of @@ -1521,10 +1517,6 @@ gen_dec_line_other(Erule, Atype, TopType, Comp) -> _ -> asn1ct_gen_per:gen_dec_imm(Erule, Type) end; - {notype,_} -> - fun(BytesVar) -> - emit({"'dec_",Atype,"'(",BytesVar,",telltype)"}) - end; {constructed,bif} -> NewTypename = [Cname|TopType], case Type#type.tablecinf of diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 5cddaba52e..978ac280dd 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -1690,15 +1690,6 @@ type({fixedtypevaluefield,_Name,Type}) when is_record(Type,type) -> type({typefield,_}) -> 'ASN1_OPEN_TYPE'; type(X) -> - %% io:format("asn1_types:type(~p)~n",[X]), - case catch type2(X) of - {'EXIT',_} -> - {notype,X}; - Normal -> - Normal - end. - -type2(X) -> case prim_bif(X) of true -> {primitive,bif}; diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 170a3c7352..4572d41711 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -94,9 +94,7 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> #'Externaltypereference'{module=CurrMod,type=Etype} -> emit({"'enc_",Etype,"'(Val).",nl,nl}); #'Externaltypereference'{module=Emod,type=Etype} -> - emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl}); - {notype,_} -> - emit({"'enc_",InnerType,"'(Val).",nl,nl}) + emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl}) end. diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index ce8be773d8..ecdfa3f645 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -51,8 +51,6 @@ from_type(M,Typename,Type) when is_record(Type,type) -> from_type(Emod,Etype); {_,user} -> from_type(M,InnerType); - {notype,_} -> - true; {primitive,bif} -> from_type_prim(M, Type); 'ASN1_OPEN_TYPE' -> -- cgit v1.2.3 From 4dcde42e8a257b4992f29dc5f2acfd0d1f625d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Mar 2013 07:36:34 +0100 Subject: Eliminate clauses that can never match Dialyzer issued two new warnings when the 'catch' was removed in the previous commit. --- lib/asn1/src/asn1ct_check.erl | 4 ---- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 21 +++++++-------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 91e77219c8..e1e5140e08 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -1816,10 +1816,6 @@ convert_to_defaultfield(S,ObjFieldName,[OFS|RestOFS],CField)-> T = check_type(S,#typedef{typespec=ObjFieldSetting}, ObjFieldSetting), {#typedef{checked=true,name=Bif,typespec=T},RestSettings}; - _OCFT = #'ObjectClassFieldType'{} -> - T=check_type(S,#typedef{typespec=ObjFieldSetting},ObjFieldSetting), - %%io:format("OCFT=~p~n,T=~p~n",[OCFT,T]), - {#typedef{checked=true,typespec=T},RestSettings}; _ -> %this case should not happen any more {Mod,T} = diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 9c224d64a3..d4fd5247d7 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -1468,20 +1468,13 @@ decode_type('BMPString') -> 30; decode_type('CHOICE') -> 'CHOICE'; % choice gets the tag from the actual alternative decode_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}). -mkfuncname(WhatKind,DecOrEnc) -> - case WhatKind of - #'Externaltypereference'{module=Mod,type=EType} -> - CurrMod = get(currmod), - case CurrMod of - Mod -> - lists:concat(["'",DecOrEnc,"_",EType,"'"]); - _ -> -% io:format("CurrMod: ~p, Mod: ~p~n",[CurrMod,Mod]), - lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"]) - end; - 'ASN1_OPEN_TYPE' -> - lists:concat(["'",DecOrEnc,"_",WhatKind,"'"]) - +mkfuncname(#'Externaltypereference'{module=Mod,type=EType}, DecOrEnc) -> + CurrMod = get(currmod), + case CurrMod of + Mod -> + lists:concat(["'",DecOrEnc,"_",EType,"'"]); + _ -> + lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"]) end. get_size_constraint(C) -> -- cgit v1.2.3 From daa806bb0c40f19dba5382f5773a0064b8e25d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 13 Mar 2013 08:41:31 +0100 Subject: Eliminate handling of #pobjectdef{} from the backends asn1ct_check does not pass #pobjectdef{} records on to the backends (all the original #pobjectdef{} records have been instantiated and changed to #objectdef{} records). --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 4 +--- lib/asn1/src/asn1ct_gen_per.erl | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index d4fd5247d7..52d8210321 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -650,9 +650,7 @@ gen_obj_code(Erules,_Module,Obj) when is_record(Obj,typedef) -> ObjName,Fields,[]), emit(nl), gen_decode_constr_type(Erules,DecConstructed), - emit_tlv_format_function(); -gen_obj_code(_Erules,_Module,Obj) when is_record(Obj,pobjectdef) -> - ok. + emit_tlv_format_function(). gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest], ObjName,ObjectFields,ConstrAcc) -> diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 4572d41711..23a6ce256c 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -287,9 +287,7 @@ gen_obj_code(Erules,_Module,Obj) when is_record(Obj,typedef) -> ObjName, Fields, []), emit(nl), gen_decode_constr_type(Erules,DecConstructed), - emit(nl); -gen_obj_code(_,_,Obj) when is_record(Obj,pobjectdef) -> - ok. + emit(nl). gen_encode_objectfields(Erule, ClassName, -- cgit v1.2.3 From 1cc6c23112042b7d7d301315225b147c2976cfbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 14 Mar 2013 16:24:21 +0100 Subject: asn1_SUITE: Add testS1AP/1 --- lib/asn1/test/asn1_SUITE.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 5f17170b2c..5ec201fd78 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -177,6 +177,7 @@ groups() -> testX420]}, testTcapsystem, testNBAPsystem, + testS1AP, test_compile_options, testDoubleEllipses, test_x691, @@ -1023,6 +1024,16 @@ testNBAPsystem(Config, Rule, Opts) -> testNBAPsystem:compile(Config, [Rule|Opts]), testNBAPsystem:test(Rule, Config). +testS1AP(Config) -> test(Config, fun testS1AP/3). +testS1AP(Config, Rule, Opts) -> + S1AP = ["S1AP-CommonDataTypes", + "S1AP-Constants", + "S1AP-Containers", + "S1AP-IEs", + "S1AP-PDU-Contents", + "S1AP-PDU-Descriptions"], + asn1_test_lib:compile_all(S1AP, Config, [Rule|Opts]). + test_compile_options(Config) -> ok = test_compile_options:wrong_path(Config), ok = test_compile_options:path(Config), -- cgit v1.2.3 From c7bfb442de270d8d1e37cb5e521f678f0176d241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 14 Mar 2013 16:00:26 +0100 Subject: Remove files that are not used by any test case The list of files to remove was obtained by running "ls -lut" after running the test cases and comparing the atime for each file with the start of the test run. --- lib/asn1/test/asn1_SUITE_data/Certificate.asn | 0 .../asn1_SUITE_data/EUTRA-InterNodeDefinitions.asn | 123 ------ .../test/asn1_SUITE_data/EUTRA-UE-Variables.asn | 49 --- lib/asn1/test/asn1_SUITE_data/InfObj2.asn | 156 ------- .../MAP-insertSubscriberData-def.py | 102 ----- lib/asn1/test/asn1_SUITE_data/Mod.set.asn | 5 - lib/asn1/test/asn1_SUITE_data/Mod1.asn | 18 - lib/asn1/test/asn1_SUITE_data/Mod2.asn | 43 -- lib/asn1/test/asn1_SUITE_data/Mod3.asn | 33 -- lib/asn1/test/asn1_SUITE_data/Mod4.asn | 33 -- lib/asn1/test/asn1_SUITE_data/Mod5.asn | 37 -- lib/asn1/test/asn1_SUITE_data/Mvrasn.set.asn | 7 - lib/asn1/test/asn1_SUITE_data/P-Record.asn1db | Bin 3128 -> 0 bytes lib/asn1/test/asn1_SUITE_data/P-Record.erl | 244 ----------- lib/asn1/test/asn1_SUITE_data/P-Record.hrl | 17 - lib/asn1/test/asn1_SUITE_data/PDUs.py | 325 --------------- lib/asn1/test/asn1_SUITE_data/Pattern.asn | 8 - lib/asn1/test/asn1_SUITE_data/ROSE.asn1 | 449 --------------------- lib/asn1/test/asn1_SUITE_data/Tst.py | 153 ------- lib/asn1/test/asn1_SUITE_data/Two.py | 34 -- lib/asn1/test/asn1_SUITE_data/UPERDefault.asn | 18 - lib/asn1/test/asn1_SUITE_data/UndefType.py | 14 - 22 files changed, 1868 deletions(-) delete mode 100644 lib/asn1/test/asn1_SUITE_data/Certificate.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/EUTRA-InterNodeDefinitions.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/EUTRA-UE-Variables.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/InfObj2.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/MAP-insertSubscriberData-def.py delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mod.set.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mod1.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mod2.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mod3.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mod4.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mod5.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/Mvrasn.set.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/P-Record.asn1db delete mode 100644 lib/asn1/test/asn1_SUITE_data/P-Record.erl delete mode 100644 lib/asn1/test/asn1_SUITE_data/P-Record.hrl delete mode 100644 lib/asn1/test/asn1_SUITE_data/PDUs.py delete mode 100644 lib/asn1/test/asn1_SUITE_data/Pattern.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/ROSE.asn1 delete mode 100644 lib/asn1/test/asn1_SUITE_data/Tst.py delete mode 100644 lib/asn1/test/asn1_SUITE_data/Two.py delete mode 100644 lib/asn1/test/asn1_SUITE_data/UPERDefault.asn delete mode 100644 lib/asn1/test/asn1_SUITE_data/UndefType.py diff --git a/lib/asn1/test/asn1_SUITE_data/Certificate.asn b/lib/asn1/test/asn1_SUITE_data/Certificate.asn deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/asn1/test/asn1_SUITE_data/EUTRA-InterNodeDefinitions.asn b/lib/asn1/test/asn1_SUITE_data/EUTRA-InterNodeDefinitions.asn deleted file mode 100644 index 5e6313dc02..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/EUTRA-InterNodeDefinitions.asn +++ /dev/null @@ -1,123 +0,0 @@ --- 3GPP TS 36.331 V8.8.0 (2009-12) --- $Id$ --- -EUTRA-InterNodeDefinitions DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - - -HandoverCommand ::= SEQUENCE { - criticalExtensions CHOICE { - c1 CHOICE{ - handoverCommand-r8 HandoverCommand-r8-IEs, - spare7 NULL, - spare6 NULL, spare5 NULL, spare4 NULL, - spare3 NULL, spare2 NULL, spare1 NULL - }, - criticalExtensionsFuture SEQUENCE {} - } -} - -HandoverCommand-r8-IEs ::= SEQUENCE { - handoverCommandMessage OCTET STRING (CONTAINING DL-DCCH-Message), - nonCriticalExtension SEQUENCE {} OPTIONAL -} - - -HandoverPreparationInformation ::= SEQUENCE { - criticalExtensions CHOICE { - c1 CHOICE{ - handoverPreparationInformation-r8 HandoverPreparationInformation-r8-IEs, - spare7 NULL, - spare6 NULL, spare5 NULL, spare4 NULL, - spare3 NULL, spare2 NULL, spare1 NULL - }, - criticalExtensionsFuture SEQUENCE {} - } -} - -HandoverPreparationInformation-r8-IEs ::= SEQUENCE { - ue-RadioAccessCapabilityInfo UE-CapabilityRAT-ContainerList, - as-Config AS-Config OPTIONAL, -- Cond HO - rrm-Config RRM-Config OPTIONAL, - as-Context AS-Context OPTIONAL, -- Cond HO - nonCriticalExtension SEQUENCE {} OPTIONAL -} - - -UERadioAccessCapabilityInformation ::= SEQUENCE { - criticalExtensions CHOICE { - c1 CHOICE{ - ueRadioAccessCapabilityInformation-r8 - UERadioAccessCapabilityInformation-r8-IEs, - spare7 NULL, - spare6 NULL, spare5 NULL, spare4 NULL, - spare3 NULL, spare2 NULL, spare1 NULL - }, - criticalExtensionsFuture SEQUENCE {} - } -} - -UERadioAccessCapabilityInformation-r8-IEs ::= SEQUENCE { - ue-RadioAccessCapabilityInfo OCTET STRING (CONTAINING UECapabilityInformation), - nonCriticalExtension SEQUENCE {} OPTIONAL -} - - -AS-Config ::= SEQUENCE { - sourceMeasConfig MeasConfig, - sourceRadioResourceConfig RadioResourceConfigDedicated, - sourceSecurityAlgorithmConfig SecurityAlgorithmConfig, - sourceUE-Identity C-RNTI, - sourceMasterInformationBlock MasterInformationBlock, - sourceSystemInformationBlockType1 SystemInformationBlockType1, - sourceSystemInformationBlockType2 SystemInformationBlockType2, - antennaInfoCommon AntennaInfoCommon, - sourceDl-CarrierFreq ARFCN-ValueEUTRA, - ... -} - - -AS-Context ::= SEQUENCE { - reestablishmentInfo ReestablishmentInfo OPTIONAL -- Cond HO -} - - -ReestablishmentInfo ::= SEQUENCE { - sourcePhysCellId PhysCellId, - targetCellShortMAC-I ShortMAC-I, - additionalReestabInfoList AdditionalReestabInfoList OPTIONAL, - ... -} - -AdditionalReestabInfoList ::= SEQUENCE ( SIZE (1..maxReestabInfo) ) OF AdditionalReestabInfo - -AdditionalReestabInfo ::= SEQUENCE{ - cellIdentity CellIdentity, - key-eNodeB-Star Key-eNodeB-Star, - shortMAC-I ShortMAC-I -} - -Key-eNodeB-Star ::= BIT STRING (SIZE (256)) - - -RRM-Config ::= SEQUENCE { - ue-InactiveTime ENUMERATED { - s1, s2, s3, s5, s7, s10, s15, s20, - s25, s30, s40, s50, min1, min1s20c, min1s40, - min2, min2s30, min3, min3s30, min4, min5, min6, - min7, min8, min9, min10, min12, min14, min17, min20, - min24, min28, min33, min38, min44, min50, hr1, - hr1min30, hr2, hr2min30, hr3, hr3min30, hr4, hr5, hr6, - hr8, hr10, hr13, hr16, hr20, day1, day1hr12, day2, - day2hr12, day3, day4, day5, day7, day10, day14, day19, - day24, day30, dayMoreThan30} OPTIONAL, - ... -} - - -maxReestabInfo INTEGER ::= 32 -- Maximum number of KeNB* and shortMAC-I forwarded - -- at handover for re-establishment preparation - - -END diff --git a/lib/asn1/test/asn1_SUITE_data/EUTRA-UE-Variables.asn b/lib/asn1/test/asn1_SUITE_data/EUTRA-UE-Variables.asn deleted file mode 100644 index 414140a6fb..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/EUTRA-UE-Variables.asn +++ /dev/null @@ -1,49 +0,0 @@ --- 3GPP TS 36.331 V8.8.0 (2009-12) --- $Id$ --- -EUTRA-UE-Variables DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - - -VarMeasConfig ::= SEQUENCE { - -- Measurement identities - measIdList MeasIdToAddModList OPTIONAL, - -- Measurement objects - measObjectList MeasObjectToAddModList OPTIONAL, - -- Reporting configurations - reportConfigList ReportConfigToAddModList OPTIONAL, - -- Other parameters - quantityConfig QuantityConfig OPTIONAL, - s-Measure RSRP-Range OPTIONAL, - speedStatePars CHOICE { - release NULL, - setup SEQUENCE { - mobilityStateParameters MobilityStateParameters, - timeToTrigger-SF SpeedStateScaleFactors - } - } OPTIONAL -} - - -VarMeasReportList ::= SEQUENCE (SIZE (1..maxMeasId)) OF VarMeasReport - -VarMeasReport ::= SEQUENCE { - -- List of measurement that have been triggered - measId MeasId, - cellsTriggeredList CellsTriggeredList OPTIONAL, - numberOfReportsSent INTEGER -} - -CellsTriggeredList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF PhysCellId - - -VarShortMAC-Input ::= SEQUENCE { - cellIdentity CellIdentity, - physCellId PhysCellId, - c-RNTI C-RNTI -} - - - -END \ No newline at end of file diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj2.asn b/lib/asn1/test/asn1_SUITE_data/InfObj2.asn deleted file mode 100644 index faba7371a4..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/InfObj2.asn +++ /dev/null @@ -1,156 +0,0 @@ -InfObj2 DEFINITIONS ::= -BEGIN - - -RANAP-ELEMENTARY-PROCEDURE ::= CLASS { - &InitiatingMessage , - &SuccessfulOutcome OPTIONAL, - &Outcome DEFAULT NULL, - &vartypvalue &Outcome, - &FixTypeValSet PrintableString, - &VarTypeValSet &InitiatingMessage, - &infoObject RANAP-ELEMENTARY-PROCEDURE, - &InfObjectSet CLASS2, - &UnsuccessfulOutcome OPTIONAL, - &procedureCode ProcedureCode UNIQUE, - &criticality Criticality DEFAULT ignore -} -WITH SYNTAX { - INITIATING MESSAGE &InitiatingMessage - [SUCCESSFUL OUTCOME &SuccessfulOutcome] - [UNSUCCESSFUL OUTCOME &UnsuccessfulOutcome] - [OUTCOME &Outcome] - PROCEDURE CODE &procedureCode - [CRITICALITY &criticality] -} - -RANAP-PDU ::= CHOICE { - initiatingMessage [0] InitiatingMessage, - substrings [1] SEQUENCE { - type RANAP-ELEMENTARY-PROCEDURE.&procedureCode({RANAP-ELEMENTARY-PROCEDURES}), - strings SEQUENCE OF CHOICE { - initial [0] RANAP-ELEMENTARY-PROCEDURE.&Outcome({ - RANAP-ELEMENTARY-PROCEDURES}{@substrings.type}), - final [1] RANAP-ELEMENTARY-PROCEDURE.&Outcome({RANAP-ELEMENTARY-PROCEDURES}{@substrings.type}) - } - }, --- successfulOutcome SuccessfulOutcome, --- unsuccessfulOutcome UnsuccessfulOutcome, --- outcome Outcome, - ... - } - -CLASS2 ::= RANAP-ELEMENTARY-PROCEDURE - -MY-CLASS ::= CLASS { - &integerValue INTEGER UNIQUE, - &booleanValue BOOLEAN, - &stringValue PrintableString - } - -myobject MY-CLASS ::= { - &integerValue 12, - &booleanValue TRUE, - &stringValue "hejsan" - } -MyObjectSet MY-CLASS ::= { - myobject - } - -InitiatingMessage ::= SEQUENCE { - procedureCode RANAP-ELEMENTARY-PROCEDURE.&procedureCode ({RANAP-ELEMENTARY-PROCEDURES}), - criticality RANAP-ELEMENTARY-PROCEDURE.&criticality ({RANAP-ELEMENTARY-PROCEDURES}{@procedureCode}), - value RANAP-ELEMENTARY-PROCEDURE.&InitiatingMessage ({RANAP-ELEMENTARY-PROCEDURES}{@procedureCode}) - } - -iu-Release RANAP-ELEMENTARY-PROCEDURE ::= { - INITIATING MESSAGE Iu-ReleaseCommand - SUCCESSFUL OUTCOME Iu-ReleaseComplete - PROCEDURE CODE id-Iu-Release1 - CRITICALITY ignore - } - -relocationPreparation RANAP-ELEMENTARY-PROCEDURE ::= { - INITIATING MESSAGE INTEGER --Iu-ReleaseCommand - SUCCESSFUL OUTCOME Iu-ReleaseComplete - PROCEDURE CODE id-Iu-Release2 - CRITICALITY notify - } - -object3 RANAP-ELEMENTARY-PROCEDURE ::= { - &InitiatingMessage Iu-ReleaseCommand, - &SuccessfulOutcome Iu-ReleaseComplete, - &procedureCode id-Iu-Release3, - &criticality reject - } - -object4 RANAP-ELEMENTARY-PROCEDURE ::= { - &InitiatingMessage INTEGER, - &SuccessfulOutcome PrintableString, - &procedureCode id-Iu-Release4, - &criticality reject - } - -object5 RANAP-ELEMENTARY-PROCEDURE ::= { - &InitiatingMessage INTEGER, - &SuccessfulOutcome PrintableString, - &Outcome ProcedureCode, - &vartypvalue 12, - &infoObject object4, - &InfObjectSet MyObjectSet, - &procedureCode id-Iu-Release5, - &criticality reject - } - - -RANAP-ELEMENTARY-PROCEDURES RANAP-ELEMENTARY-PROCEDURE ::= { - iu-Release | - relocationPreparation , - ... - } - -RANAP-ELEMENTARY-PROCEDURES2 RANAP-ELEMENTARY-PROCEDURE ::= { - iu-Release | - relocationPreparation - } - - -OBJECTSET1 RANAP-ELEMENTARY-PROCEDURE ::= { - {INITIATING MESSAGE Iu-ReleaseCommand SUCCESSFUL OUTCOME Iu-ReleaseComplete PROCEDURE CODE id-Iu-Release1 CRITICALITY ignore} | {INITIATING MESSAGE Iu-ReleaseCommand PROCEDURE CODE id-Iu-Release2} - } - -OBJECTSET2 RANAP-ELEMENTARY-PROCEDURE ::= { - iu-Release | - {INITIATING MESSAGE Iu-ReleaseCommand SUCCESSFUL OUTCOME Iu-ReleaseComplete PROCEDURE CODE id-Iu-Release4 CRITICALITY ignore} | - relocationPreparation | - {INITIATING MESSAGE Iu-ReleaseCommand PROCEDURE CODE id-Iu-Release5} , - ... - } - -OBJECTSET3 RANAP-ELEMENTARY-PROCEDURE ::= { - iu-Release, - ... - } - -OBJECTSET4 RANAP-ELEMENTARY-PROCEDURE ::= { - iu-Release - } - -Iu-ReleaseCommand ::= SEQUENCE { - first INTEGER, - second BOOLEAN - } - -Iu-ReleaseComplete ::= INTEGER (1..510) - -ProcedureCode ::= INTEGER (0..255) -Criticality ::= ENUMERATED { reject, ignore, notify } -id-Iu-Release1 INTEGER ::= 1 -id-Iu-Release2 INTEGER ::= 2 -id-Iu-Release3 INTEGER ::= 3 -id-Iu-Release4 INTEGER ::= 4 -id-Iu-Release5 INTEGER ::= 5 - -END - - diff --git a/lib/asn1/test/asn1_SUITE_data/MAP-insertSubscriberData-def.py b/lib/asn1/test/asn1_SUITE_data/MAP-insertSubscriberData-def.py deleted file mode 100644 index 298319b0ed..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/MAP-insertSubscriberData-def.py +++ /dev/null @@ -1,102 +0,0 @@ -MAP-insertSubscriberData-def - { ccitt (0) identified-organization( 4) etsi( 0) mobileDomain(0) - gsm-Network( 1) modules( 3) map-Protocol( 4) version2(2) } -DEFINITIONS ::= - -BEGIN - -EXPORTS -InsertSubsDataArg, InsertSubsDatRes; -IMPORTS -IMSI, ISDN-AddressString, LMSI FROM MAP-commonDataTypes; - -InsertSubsDataArg ::= SEQUENCE{ - imsi [0] IMPLICIT IMSI OPTIONAL, - msisdn [1] IMPLICIT ISDN-AddressString OPTIONAL, - category [2] IMPLICIT OCTET STRING (SIZE(1)) OPTIONAL, - subscriberStatus [3] IMPLICIT SubscriberStatus OPTIONAL, - bearerServiceList [4] IMPLICIT SEQUENCE OF - OCTET STRING(SIZE(1)) OPTIONAL, - teleServiceList [6] IMPLICIT SEQUENCE OF - OCTET STRING(SIZE(1)) OPTIONAL, - provisionedSS [7] IMPLICIT SEQUENCE OF SS-Information OPTIONAL - } - -SS-Information ::= CHOICE{ - forwardingInfo [0] IMPLICIT ForwardingInfo, - callBarringInfoInfo [1] IMPLICIT CallBarringInfoInfo, - ss-Data [3] IMPLICIT SS-Data } - -SS-Data ::= SEQUENCE { - ss-Code OCTET STRING (SIZE(1)), - ss-Status [4] IMPLICIT OCTET STRING (SIZE(1)) - } - - -ForwardingInfo ::= SEQUENCE { - ss-Code OCTET STRING(SIZE(1)) OPTIONAL, - forwardingFeatureList ForwardingFeatureList - } - -CallBarringInfoInfo ::= SEQUENCE { - ss-Code OCTET STRING(SIZE(1)) OPTIONAL, - callBarringFeatureList CallBarringFeatureList} - -CallBarringFeatureList ::= SEQUENCE OF CallBarringFeature - -CallBarringFeature ::= SEQUENCE{ - basicService BasicServiceCode OPTIONAL, - ss-Status [2] IMPLICIT OCTET STRING(SIZE(1)) OPTIONAL - } - -InsertSubsDatRes ::= - SEQUENCE { - teleServiceList [1] IMPLICIT SEQUENCE OF - OCTET STRING (SIZE(1)) OPTIONAL, - bearerServiceList [2] IMPLICIT SEQUENCE OF - OCTET STRING (SIZE(1)) OPTIONAL, - ss-List [3] IMPLICIT SEQUENCE OF - OCTET STRING (SIZE(1)) OPTIONAL, - odb-GeneralData [4] IMPLICIT BIT STRING { - allOG-CallsBarred (0), - internationalOGCallsBarred (1), - internationalOGCallsNotToHPLMN-CountryBarred (2), - premiumRateInformationOGCallsBarred (3), - premiumRateEntertainementOGCallsBarred (4), - ss-AccessBarred (5) } (SIZE(6)) OPTIONAL, - regionalSubscriptionResponse [5] IMPLICIT ENUMERATED{ - msc-AreaRestricted (0), - tooManyZoneCodes (1), - zoneCodeConflict (2), - regionalSubscNotSupported (3) } OPTIONAL - } - - -ForwardingFeatureList ::= SEQUENCE OF ForwardingFeature - -ForwardingFeature ::= SEQUENCE{ - basicService BasicServiceCode OPTIONAL, - ss-Status [4] IMPLICIT OCTET STRING(SIZE(1)) OPTIONAL, - forwardedToNumber [5] ISDN-AddressString OPTIONAL, - forwardingOptions [6] IMPLICIT OCTET STRING(SIZE(1)) OPTIONAL, - noReplyConditionTime [7] IMPLICIT INTEGER(5..30) OPTIONAL - } - - -BasicServiceCode ::= CHOICE { - bearerService [2] IMPLICIT OCTET STRING(SIZE(1)), - teleService [3] IMPLICIT OCTET STRING(SIZE(1)) - } - - -BasicServiceGroupList ::= SEQUENCE OF - BasicServiceCode - - -SubscriberStatus ::= ENUMERATED { - serviceGranted (0), - operatorDeterminedBarring (1) - } - -END -- of MAP-insertSubscriberData-def - diff --git a/lib/asn1/test/asn1_SUITE_data/Mod.set.asn b/lib/asn1/test/asn1_SUITE_data/Mod.set.asn deleted file mode 100644 index 5dcd8706ae..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mod.set.asn +++ /dev/null @@ -1,5 +0,0 @@ -Mod1.asn -Mod2.asn -Mod3.asn -Mod4.asn -Mod5.asn diff --git a/lib/asn1/test/asn1_SUITE_data/Mod1.asn b/lib/asn1/test/asn1_SUITE_data/Mod1.asn deleted file mode 100644 index cb29997985..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mod1.asn +++ /dev/null @@ -1,18 +0,0 @@ -Mod1 DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -IMPORTS - Co,Reg - FROM Mod5 - Name - FROM Mod4; - - -L ::= SEQUENCE { - country Co, - region Reg, - name Name -} - -END diff --git a/lib/asn1/test/asn1_SUITE_data/Mod2.asn b/lib/asn1/test/asn1_SUITE_data/Mod2.asn deleted file mode 100644 index cc22c6f13c..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mod2.asn +++ /dev/null @@ -1,43 +0,0 @@ -Mod2 DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -IMPORTS - Stat,Country - FROM Mod3 - L - FROM Mod1 - Time,LocName,ThingName,Name - FROM Mod4; - -T ::= SEQUENCE { - unit ENUMERATED{celsius,fahrenheit,kelvin}, - degree INTEGER, - location L, - time Time, - statistics Stat -} - -OtherName ::= SEQUENCE { - locationName LocName, - thingName ThingName -} - -FirstName ::= CHOICE { - firstname PrintableString, - nickname PrintableString -} - -FamilyName ::= SEQUENCE{ - prefix ENUMERATED{none,von,af}, - secondname PrintableString -} - -Lang ::= SEQUENCE{ - l PrintableString} - -Inhabitant ::= SEQUENCE { - name Name, - country Country} - -END diff --git a/lib/asn1/test/asn1_SUITE_data/Mod3.asn b/lib/asn1/test/asn1_SUITE_data/Mod3.asn deleted file mode 100644 index 8069bedcf9..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mod3.asn +++ /dev/null @@ -1,33 +0,0 @@ -Mod3 DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -IMPORTS - Name - FROM Mod4 - Lang, Inhabitant,FirstName,FamilyName - FROM Mod2 - TS, RFS, WS, HS - FROM Mod5; - -Stat ::= SEQUENCE { - tempstat TS, - rainfallstat RFS, - windstat WS, - humiditystat HS -} - -Country ::= SEQUENCE{ - name Name, - language Lang -} - -RegionName ::= Name -Inhabitants ::= SEQUENCE OF Inhabitant - -PersonName ::= SEQUENCE { - name1 FirstName, - name2 FamilyName -} - -END diff --git a/lib/asn1/test/asn1_SUITE_data/Mod4.asn b/lib/asn1/test/asn1_SUITE_data/Mod4.asn deleted file mode 100644 index 4a1aaff9dc..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mod4.asn +++ /dev/null @@ -1,33 +0,0 @@ -Mod4 DEFINITIONS AUTOMATIC TAGS ::= - - -BEGIN - -IMPORTS - PersonName - FROM Mod3 - OtherName,FirstName,FamilyName - FROM Mod2; - -Time ::= SEQUENCE { - year OCTET STRING(SIZE(4)), - month OCTET STRING(SIZE(2)), - hour INTEGER, - minute INTEGER -} - -Name ::= CHOICE { - person PersonName, - othername OtherName -} - - - -LocName ::= SEQUENCE { - region ENUMERATED{gotaland,svealand,norrland}, - name PrintableString -} - -ThingName ::= PrintableString - -END diff --git a/lib/asn1/test/asn1_SUITE_data/Mod5.asn b/lib/asn1/test/asn1_SUITE_data/Mod5.asn deleted file mode 100644 index 71b483d0e0..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mod5.asn +++ /dev/null @@ -1,37 +0,0 @@ -Mod5 DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -IMPORTS - Country,RegionName,Inhabitants - FROM Mod3; -TS ::= SEQUENCE { - average INTEGER, - highest INTEGER, - lowest INTEGER -} - -RFS ::= SEQUENCE { - average INTEGER, - highest INTEGER -} - -WS ::= SEQUENCE { - average INTEGER, - highest INTEGER -} - -HS ::= SEQUENCE { - average INTEGER, - highest INTEGER, - lowest INTEGER -} - -Co ::= Country - -Reg ::= SEQUENCE { - name RegionName, - inhabitants Inhabitants -} - -END diff --git a/lib/asn1/test/asn1_SUITE_data/Mvrasn.set.asn b/lib/asn1/test/asn1_SUITE_data/Mvrasn.set.asn deleted file mode 100644 index 8a61da0160..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Mvrasn.set.asn +++ /dev/null @@ -1,7 +0,0 @@ -Mvrasn-11-6.asn -Mvrasn-21-4.asn -Mvrasn-20-6.asn -Mvrasn-19-6.asn -Mvrasn-15-6.asn -Mvrasn-18-6.asn -Mvrasn-14-6.asn diff --git a/lib/asn1/test/asn1_SUITE_data/P-Record.asn1db b/lib/asn1/test/asn1_SUITE_data/P-Record.asn1db deleted file mode 100644 index 13e1162c64..0000000000 Binary files a/lib/asn1/test/asn1_SUITE_data/P-Record.asn1db and /dev/null differ diff --git a/lib/asn1/test/asn1_SUITE_data/P-Record.erl b/lib/asn1/test/asn1_SUITE_data/P-Record.erl deleted file mode 100644 index 9fc6f50d64..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/P-Record.erl +++ /dev/null @@ -1,244 +0,0 @@ -%% Generated by the Erlang ASN.1 BER-compiler version, utilizing bit-syntax:1.3.1.4 -%% Purpose: encoder and decoder to the types in mod P-Record - --module('P-Record'). --include("P-Record.hrl"). --define('RT_PER',asn1rt_per_bin). --export([encoding_rule/0]). --export([ -'enc_PersonnelRecord'/1, -'enc_ChildInformation'/1, -'enc_Name'/1, -'enc_EmployeeNumber'/1, -'enc_Date'/1 -]). - --export([ -'dec_PersonnelRecord'/2, -'dec_ChildInformation'/2, -'dec_Name'/2, -'dec_EmployeeNumber'/2, -'dec_Date'/2 -]). - --export([ -'v'/0 -]). - - - --export([encode/2,decode/2,encode_disp/2,decode_disp/2]). - -encoding_rule() -> - per_bin. - -encode(Type,Data) -> -case catch ?RT_PER:complete(encode_disp(Type,Data)) of - {'EXIT',{error,Reason}} -> - {error,Reason}; - {'EXIT',Reason} -> - {error,{asn1,Reason}}; - {Bytes,Len} -> - {ok,Bytes}; - X -> - {ok,X} -end. - -decode(Type,Data) -> -case catch decode_disp(Type,Data) of - {'EXIT',{error,Reason}} -> - {error,Reason}; - {'EXIT',Reason} -> - {error,{asn1,Reason}}; - {X,_Rest} -> - {ok,X}; - {X,_Rest,_Len} -> - {ok,X} -end. - -encode_disp('PersonnelRecord',Data) -> 'enc_PersonnelRecord'(Data); -encode_disp('ChildInformation',Data) -> 'enc_ChildInformation'(Data); -encode_disp('Name',Data) -> 'enc_Name'(Data); -encode_disp('EmployeeNumber',Data) -> 'enc_EmployeeNumber'(Data); -encode_disp('Date',Data) -> 'enc_Date'(Data); -encode_disp(Type,Data) -> exit({error,{asn1,{undefined_type,Type}}}). - - -decode_disp('PersonnelRecord',Data) -> 'dec_PersonnelRecord'(Data,mandatory); -decode_disp('ChildInformation',Data) -> 'dec_ChildInformation'(Data,mandatory); -decode_disp('Name',Data) -> 'dec_Name'(Data,mandatory); -decode_disp('EmployeeNumber',Data) -> 'dec_EmployeeNumber'(Data,mandatory); -decode_disp('Date',Data) -> 'dec_Date'(Data,mandatory); -decode_disp(Type,Data) -> exit({error,{asn1,{undefined_type,Type}}}). - - - - - -'enc_PersonnelRecord'(Val) -> -{Val1,Opt} = ?RT_PER:fixoptionals([{children,6}],Val), -[ -?RT_PER:setoptionals(Opt), - -%% attribute number 1 with type Externaltypereference6P-RecordName -'enc_Name'(?RT_PER:cindex(2,Val1,name)), - -%% attribute number 2 with type VisibleString -?RT_PER:encode_VisibleString([],?RT_PER:cindex(3,Val1,title)), - -%% attribute number 3 with type INTEGER -?RT_PER:encode_integer([],?RT_PER:cindex(4,Val1,number)), - -%% attribute number 4 with type VisibleString -?RT_PER:encode_VisibleString([],?RT_PER:cindex(5,Val1,dateOfHire)), - -%% attribute number 5 with type Externaltypereference10P-RecordName -'enc_Name'(?RT_PER:cindex(6,Val1,nameOfSpouse)), -case ?RT_PER:cindex(7,Val1,children) of -asn1_DEFAULT -> []; -_ -> - -%% attribute number 6 with type SEQUENCE OF -'enc_PersonnelRecord_children'(?RT_PER:cindex(7,Val1,children)) -end]. - -'enc_PersonnelRecord_children'({'PersonnelRecord_children',Val}) -> -'enc_PersonnelRecord_children'(Val); - -'enc_PersonnelRecord_children'(Val) -> -[ - - ?RT_PER:encode_length(undefined,length(Val)), - 'enc_PersonnelRecord_children_components'(Val, []) -]. -'enc_PersonnelRecord_children_components'([], Acc) -> lists:reverse(Acc); - -'enc_PersonnelRecord_children_components'([H|T], Acc) -> -'enc_PersonnelRecord_children_components'(T, ['enc_ChildInformation'(H) - - | Acc]). - -'dec_PersonnelRecord_children'(Bytes,Telltype) -> - -{Num,Bytes1} = ?RT_PER:decode_length(Bytes,undefined), -'dec_PersonnelRecord_children_components'(Num, Bytes1, Telltype, []). -'dec_PersonnelRecord_children_components'(0, Bytes, Telltype, Acc) -> - {lists:reverse(Acc), Bytes}; -'dec_PersonnelRecord_children_components'(Num, Bytes, Telltype, Acc) -> - {Term,Remain} = 'P-Record':'dec_ChildInformation'(Bytes,Telltype), - 'dec_PersonnelRecord_children_components'(Num-1, Remain, Telltype, [Term|Acc]). - - -'dec_PersonnelRecord'(Bytes,Telltype) -> -{Opt,Bytes1} = ?RT_PER:getoptionals(Bytes,1), -%% attribute number 1 with type Name -{Term1,Bytes2} = 'dec_Name'(Bytes1,telltype), - -%% attribute number 2 with type VisibleString -{Term2,Bytes3} = ?RT_PER:decode_VisibleString(Bytes2,[]), - -%% attribute number 3 with type INTEGER -{Term3,Bytes4} = ?RT_PER:decode_integer(Bytes3,[]), - -%% attribute number 4 with type VisibleString -{Term4,Bytes5} = ?RT_PER:decode_VisibleString(Bytes4,[]), - -%% attribute number 5 with type Name -{Term5,Bytes6} = 'dec_Name'(Bytes5,telltype), - -%% attribute number 6 with type SEQUENCE OF -{Term6,Bytes7} = case element(1,Opt) of -1 ->'dec_PersonnelRecord_children'(Bytes6, Telltype); -0 ->{[],Bytes6} - -end, -{{'PersonnelRecord',Term1,Term2,Term3,Term4,Term5,Term6},Bytes7}. - -'enc_ChildInformation'(Val) -> -{Val1,Opt} = ?RT_PER:fixoptionals([{name,1},{dateOfBirth,2}],Val), -[ -?RT_PER:setoptionals(Opt), -case ?RT_PER:cindex(2,Val1,name) of -asn1_NOVALUE -> []; -_ -> - -%% attribute number 1 with type Externaltypereference15P-RecordName -'enc_Name'(?RT_PER:cindex(2,Val1,name)) -end, -case ?RT_PER:cindex(3,Val1,dateOfBirth) of -asn1_NOVALUE -> []; -_ -> - -%% attribute number 2 with type VisibleString -?RT_PER:encode_VisibleString([],?RT_PER:cindex(3,Val1,dateOfBirth)) -end]. - - -'dec_ChildInformation'(Bytes,Telltype) -> -{Opt,Bytes1} = ?RT_PER:getoptionals(Bytes,2), -%% attribute number 1 with type Name -{Term1,Bytes2} = case element(1,Opt) of -1 ->'dec_Name'(Bytes1,telltype); -0 ->{asn1_NOVALUE,Bytes1} - -end, - -%% attribute number 2 with type VisibleString -{Term2,Bytes3} = case element(2,Opt) of -1 ->?RT_PER:decode_VisibleString(Bytes2,[]); -0 ->{asn1_NOVALUE,Bytes2} - -end, -{{'ChildInformation',Term1,Term2},Bytes3}. - -'enc_Name'(Val) -> -Val1 = ?RT_PER:list_to_record('Name', Val), -[ - -%% attribute number 1 with type VisibleString -?RT_PER:encode_VisibleString([],?RT_PER:cindex(2,Val1,givenName)), - -%% attribute number 2 with type VisibleString -?RT_PER:encode_VisibleString([],?RT_PER:cindex(3,Val1,initial)), - -%% attribute number 3 with type VisibleString -?RT_PER:encode_VisibleString([],?RT_PER:cindex(4,Val1,familyName))]. - - -'dec_Name'(Bytes,Telltype) -> - -%% attribute number 1 with type VisibleString -{Term1,Bytes1} = ?RT_PER:decode_VisibleString(Bytes,[]), - -%% attribute number 2 with type VisibleString -{Term2,Bytes2} = ?RT_PER:decode_VisibleString(Bytes1,[]), - -%% attribute number 3 with type VisibleString -{Term3,Bytes3} = ?RT_PER:decode_VisibleString(Bytes2,[]), -{{'Name',Term1,Term2,Term3},Bytes3}. - - -'enc_EmployeeNumber'({'EmployeeNumber',Val}) -> -'enc_EmployeeNumber'(Val); - -'enc_EmployeeNumber'(Val) -> -?RT_PER:encode_integer([],Val). - - -'dec_EmployeeNumber'(Bytes,Telltype) -> -?RT_PER:decode_integer(Bytes,[]). - - -'enc_Date'({'Date',Val}) -> -'enc_Date'(Val); - -'enc_Date'(Val) -> -?RT_PER:encode_VisibleString([],Val). - - -'dec_Date'(Bytes,Telltype) -> -?RT_PER:decode_VisibleString(Bytes,[]). - -'v'() -> -{'PersonnelRecord',{'Name',{74,111,104,110},[80],[83,109,105,116,104]},[68,105,114,101,99,116,111,114],51,[49,57,55,49,48,57,49,55],{'Name',{77,97,114,121},[84],[83,109,105,116,104]},[{'ChildInformation',{'Name',[82,97,108,112,104],[84],[83,109,105,116,104]},[49,57,53,55,49,49,49,49]},{'ChildInformation',{'Name',[83,117,115,97,110],[66],[74,111,110,101,115]},[49,57,53,57,48,55,49,55]}]}. - diff --git a/lib/asn1/test/asn1_SUITE_data/P-Record.hrl b/lib/asn1/test/asn1_SUITE_data/P-Record.hrl deleted file mode 100644 index 92aa1a44e2..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/P-Record.hrl +++ /dev/null @@ -1,17 +0,0 @@ -%% Generated by the Erlang ASN.1 compiler version:1.3.1.4 -%% Purpose: Erlang record definitions for each named and unnamed -%% SEQUENCE and SET, and macro definitions for each value -%% definition,in module P-Record - - - --record('PersonnelRecord',{ -name, title, number, dateOfHire, nameOfSpouse, children = asn1_DEFAULT}). - --record('ChildInformation',{ -name = asn1_NOVALUE, dateOfBirth = asn1_NOVALUE}). - --record('Name',{ -givenName, initial, familyName}). - --define('v', {'PersonnelRecord',{'Name',{74,111,104,110},[80],[83,109,105,116,104]},[68,105,114,101,99,116,111,114],51,[49,57,55,49,48,57,49,55],{'Name',{77,97,114,121},[84],[83,109,105,116,104]},[{'ChildInformation',{'Name',[82,97,108,112,104],[84],[83,109,105,116,104]},[49,57,53,55,49,49,49,49]},{'ChildInformation',{'Name',[83,117,115,97,110],[66],[74,111,110,101,115]},[49,57,53,57,48,55,49,55]}]}). diff --git a/lib/asn1/test/asn1_SUITE_data/PDUs.py b/lib/asn1/test/asn1_SUITE_data/PDUs.py deleted file mode 100644 index 907348193f..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/PDUs.py +++ /dev/null @@ -1,325 +0,0 @@ -PDUs DEFINITIONS ::= - --- Search for 'org' to find changes for erlang. - --- SnmpMgmtCom and PDUs only for dbg. - - -BEGIN -EXPORTS SnmpPrivMsg, SnmpAuthMsg, SnmpMgmtCom, PDUs; - --- From RFC 1442 - - -- names of objects - - ObjectName ::= - OBJECT IDENTIFIER - - - -- syntax of objects - - ObjectSyntax ::= - CHOICE { - simple - SimpleSyntax, - - -- note that SEQUENCEs for conceptual tables and - -- rows are not mentioned here... - - applicationWide - ApplicationSyntax - } - - - -- built-in ASN.1 types - - SimpleSyntax ::= - CHOICE { - -- INTEGERs with a more restrictive range - -- may also be used - integerValue - INTEGER, - - stringValue - OCTET STRING, - - objectIDValue - OBJECT IDENTIFIER, - - -- only the enumerated form is allowed - bitValue - BIT STRING - } - - - -- indistinguishable from INTEGER, but never needs more than - -- 32Bits for a two's complement representation - Integer32 ::= - [UNIVERSAL 2] - IMPLICIT INTEGER (-2147483648..2147483647) - - - -- applicationWide types - - ApplicationSyntax ::= - CHOICE { - ipAddressValue - IpAddress, - - counterValue - Counter32, - - gaugeValue - Gauge32, - - timeticksValue - TimeTicks, - - arbitraryValue - Opaque, - - nsapAddressValue - NsapAddress, - - bigCounterValue - Counter64, - - unsignedIntegerValue - UInteger32 - } - - -- in networkByte order - -- (this is a tagged type for historical reasons) - IpAddress ::= - [APPLICATION 0] - IMPLICIT OCTET STRING (SIZE (4)) - - - - - -- this wraps - Counter32 ::= - [APPLICATION 1] - IMPLICIT INTEGER (0..4294967295) - - -- this doesn't wrap - Gauge32 ::= - [APPLICATION 2] - IMPLICIT INTEGER (0..4294967295) - - -- hundredths of seconds since an epoch - TimeTicks ::= - [APPLICATION 3] - IMPLICIT INTEGER (0..4294967295) - - -- for backwardCompatibility only - Opaque ::= - [APPLICATION 4] - IMPLICIT OCTET STRING - - -- for OSI NSAP addresses - -- (this is a tagged type for historical reasons) - NsapAddress ::= - [APPLICATION 5] --- org: IMPLICIT OCTET STRING (SIZE (1 | 4..21)) - IMPLICIT OCTET STRING - - -- for counters that wrap in less than one hour with only 32 bits - Counter64 ::= - [APPLICATION 6] - IMPLICIT INTEGER (0..18446744073709551615) - - -- an unsigned 32Bit quantity - UInteger32 ::= - [APPLICATION 7] - IMPLICIT INTEGER (0..4294967295) - - --- From RFC 1445 - - SnmpPrivMsg ::= [1] IMPLICIT SEQUENCE { - privDst - OBJECT IDENTIFIER, - privData - [1] IMPLICIT OCTET STRING - } - - SnmpAuthMsg ::= [1] IMPLICIT SEQUENCE { - authInfo - ANY, -- defined by authentication protocol - authData - SnmpMgmtCom - } - - SnmpMgmtCom ::= [2] IMPLICIT SEQUENCE { - dstParty - OBJECT IDENTIFIER, - srcParty - OBJECT IDENTIFIER, - context - OBJECT IDENTIFIER, - pdu - PDUs - } - - --- From RFC 1448 - - -- org: no tag at all. we need a tag to test 'PDUs'. - PDUs ::= [PRIVATE 1] - -- remove tag when 'PDUs' only is used in another type. - CHOICE { - getRequest - GetRequestPdu, - - getNextRequest - GetNextRequestPdu, - - getBulkRequest - GetBulkRequestPdu, - - response - ResponsePdu, - - setRequest - SetRequestPdu, - - informRequest - InformRequestPdu, - - snmpV2Trap - SNMPv2TrapPdu - } - - -- PDUs - - GetRequestPdu ::= - [0] - IMPLICIT PDU - - GetNextRequestPdu ::= - [1] - IMPLICIT PDU - - ResponsePdu ::= - [2] - IMPLICIT PDU - - SetRequestPdu ::= - [3] - IMPLICIT PDU - - -- [4] is obsolete - - GetBulkRequestPdu ::= - [5] - IMPLICIT BulkPDU - - InformRequestPdu ::= - [6] - IMPLICIT PDU - - SNMPv2TrapPdu ::= - [7] - IMPLICIT PDU - - - maxBindings - INTEGER ::= 2147483647 - - PDU ::= - SEQUENCE { - requestId - Integer32, - - errorStatus -- sometimes ignored - INTEGER { - noError(0), - tooBig(1), - noSuchName(2), -- for proxy compatibility - badValue(3), -- for proxy compatibility - readOnly(4), -- for proxy compatibility - genErr(5), - noAccess(6), - wrongType(7), - wrongLength(8), - wrongEncoding(9), - wrongValue(10), - noCreation(11), - inconsistentValue(12), - resourceUnavailable(13), - commitFailed(14), - undoFailed(15), - authorizationError(16), - notWritable(17), - inconsistentName(18) - }, - - errorIndex -- sometimes ignored - INTEGER (0..maxBindings), - - variableBindings -- values are sometimes ignored - VarBindList - } - - - BulkPDU ::= -- MUST be identical in - SEQUENCE { -- structure to PDU - requestId - Integer32, - - nonRepeaters - INTEGER (0..maxBindings), - - maxRepetitions - INTEGER (0..maxBindings), - - variableBindings -- values are ignored - VarBindList - } - - - VarBind ::= - SEQUENCE { - name - ObjectName, - - data CHOICE { - value - ObjectSyntax, - - unSpecified -- in retrieval requests - NULL, - - -- exceptions in responses - noSuchObject[0] - IMPLICIT NULL, - - noSuchInstance[1] - IMPLICIT NULL, - - endOfMibView[2] - IMPLICIT NULL - } - } - - - -- variableBinding list - - VarBindList ::= - SEQUENCE OF VarBind - --- org: --- VarBindList ::= --- SEQUENCE (SIZE (0..maxBindings)) OF --- VarBind - -END - - - - - - - - diff --git a/lib/asn1/test/asn1_SUITE_data/Pattern.asn b/lib/asn1/test/asn1_SUITE_data/Pattern.asn deleted file mode 100644 index 730b4ba32a..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Pattern.asn +++ /dev/null @@ -1,8 +0,0 @@ -Pattern DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -DateAndTime ::= VisibleString (PATTERN "\d#2/\d#2/\d#4-\d#2:\d#2") --- DD/MM/YYY-HH:MM - -END \ No newline at end of file diff --git a/lib/asn1/test/asn1_SUITE_data/ROSE.asn1 b/lib/asn1/test/asn1_SUITE_data/ROSE.asn1 deleted file mode 100644 index 2fefae3caf..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/ROSE.asn1 +++ /dev/null @@ -1,449 +0,0 @@ -ROSE DEFINITIONS IMPLICIT TAGS ::= - - -BEGIN - -OPERATION ::= CLASS -{ - &ArgumentType OPTIONAL, - &argumentTypeOptional BOOLEAN OPTIONAL, - &returnResult BOOLEAN DEFAULT TRUE, - &ResultType OPTIONAL, - &resultTypeOptional BOOLEAN OPTIONAL, - &Errors ERROR OPTIONAL, - &Linked OPERATION OPTIONAL, - &synchronous BOOLEAN DEFAULT FALSE, - &idempotent BOOLEAN DEFAULT FALSE, - &alwaysReturns BOOLEAN DEFAULT TRUE, - &InvokePriority Priority OPTIONAL, - &ResultPriority Priority OPTIONAL, - &operationCode Code UNIQUE OPTIONAL - } -WITH SYNTAX - { - [ARGUMENT &ArgumentType [OPTIONAL &argumentTypeOptional]] - [RESULT &ResultType [OPTIONAL &resultTypeOptional]] - [RETURN RESULT &returnResult] - [ERRORS &Errors] - [LINKED &Linked] - [SYNCHRONOUS &synchronous] - [IDEMPOTENT &idempotent] - [ALWAYS RESPONDS &alwaysReturns] - [INVOKE PRIORITY &InvokePriority] - [RESULT-PRIORITY &ResultPriority] - [CODE &operationCode] - } - -ERROR ::= CLASS -{ - &ParameterType OPTIONAL, - ¶meterTypeOptional BOOLEAN OPTIONAL, - &ErrorPriority Priority OPTIONAL, - &errorCode Code UNIQUE OPTIONAL - } -WITH SYNTAX -{ - [PARAMETER &ParameterType [OPTIONAL ¶meterTypeOptional]] - [PRIORITY &ErrorPriority] - [CODE &errorCode] - } - -OPERATION-PACKAGE ::= CLASS -{ - &Both OPERATION OPTIONAL, - &Consumer OPERATION OPTIONAL, - &Supplier OPERATION OPTIONAL, - &id OBJECT IDENTIFIER UNIQUE OPTIONAL - } -WITH SYNTAX -{ - [OPERATIONS &Both] - [CONSUMER INVOKES &Supplier] - [SUPPLIER INVOKES &Consumer] - [ID &id] - } - -CONNECTION-PACKAGE ::= CLASS -{ - &bind OPERATION DEFAULT emptyBind, - &unbind OPERATION DEFAULT emptyUnbind, - &responderCanUnbind BOOLEAN DEFAULT FALSE, - &unbindCanFail BOOLEAN DEFAULT FALSE, - &id OBJECT IDENTIFIER UNIQUE OPTIONAL - } -WITH SYNTAX -{ - [BIND &bind] - [UNBIND &unbind] - [RESPONDER UNBIND &responderCanUnbind] - [FAILURE TO UNBIND &unbindCanFail] - [ID &id] - } - -CONTRACT ::= CLASS -{ - &connection CONNECTION-PACKAGE OPTIONAL, - &OperationsOf OPERATION-PACKAGE OPTIONAL, - &InitiatorConsumerOf OPERATION-PACKAGE OPTIONAL, - &InitiatorSupplierOf OPERATION-PACKAGE OPTIONAL, - &id OBJECT IDENTIFIER UNIQUE OPTIONAL - } -WITH SYNTAX -{ - [CONNECTION &connection] - [OPERATIONS OF &OperationsOf] - [INITIATOR CONSUMER OF &InitiatorConsumerOf] - [RESPONDER CONSUMER OF &InitiatorSupplierOf] - [ID &id] -} - -ROS-OBJECT-CLASS ::= CLASS -{ - &Is ROS-OBJECT-CLASS OPTIONAL, - &Initiates CONTRACT OPTIONAL, - &Responds CONTRACT OPTIONAL, - &InitiatesAndResponds CONTRACT OPTIONAL, - &id OBJECT IDENTIFIER UNIQUE - } -WITH SYNTAX -{ - [IS &Is] - [BOTH &InitiatesAndResponds] - [INITIATES &Initiates] - [RESPONDS &Responds] - ID &id - } - -Code ::= CHOICE -{ - local INTEGER, - global OBJECT IDENTIFIER - } - -Priority ::= INTEGER (0..MAX) - -ROS {InvokeId:InvokeIdSet,OPERATION:Invokable,OPERATION:Returnable} ::= CHOICE - { - invoke [1] Invoke {{InvokeIdSet}, {Invokable}}, - returnResult [2] ReturnResult {{Returnable}}, - returnError [3] ReturnError {{Errors{{Returnable}}}}, - reject [4] Reject - } -(CONSTRAINED BY {-- must conform to the above definition --} - ! RejectProblem : general-unrecognizedPDU) - -Invoke {InvokeId:InvokeIdSet, OPERATION:Operations} ::= SEQUENCE -{ - invokeId InvokeId (InvokeIdSet) - (CONSTRAINED BY {-- must be unambiguous --} - ! RejectProblem : invoke-duplicateInvocation), - linkedId CHOICE { - present [0] IMPLICIT present < InvokeId, - absent [1] IMPLICIT NULL - } - (CONSTRAINED BY {-- must identify an outstanding operation --} - ! RejectProblem : invoke-unrecognizedLinkedId) - (CONSTRAINED BY {-- which has one or more linked operations--} - ! RejectProblem : invoke-linkedResponseUnexpected) - OPTIONAL, - opcode OPERATION.&operationCode - ({Operations} - ! RejectProblem : invoke-unrecognizedOperation), - argument OPERATION.&ArgumentType - ({Operations} {@opcode} - ! RejectProblem : invoke-mistypedArgument) - OPTIONAL - } -(CONSTRAINED BY {-- must conform to the above definition --} - ! RejectProblem : general-mistypedPDU) -( -WITH COMPONENTS -{..., - linkedId ABSENT - } -| WITH COMPONENTS -{..., - linkedId PRESENT, - opcode - (CONSTRAINED BY {-- must be in the &Linked field of the associated operation -- - } - ! RejectProblem : invoke-unexpectedLinkedOperation) - } -) - -ReturnResult {OPERATION:Operations}::= SEQUENCE -{ - invokeId InvokeId - (CONSTRAINED BY {-- must be that for an outstanding operation --} - ! RejectProblem:returnResult-unrecognizedInvocation) - (CONSTRAINED BY {-- which returns a result --} - ! RejectProblem:returnResult-resultResponseUnexpected), - result SEQUENCE - { - opcode OPERATION.&operationCode - ({Operations})(CONSTRAINED BY {-- identified by invokeId --} - ! RejectProblem:returnResult-unrecognizedInvocation), - result OPERATION.&ResultType ({Operations} {@.opcode} - ! RejectProblem:returnResult-mistypedResult) - } - OPTIONAL - } -(CONSTRAINED BY {-- must conform to the above definition -- - } -! RejectProblem:general-mistypedPDU) - -ReturnError {ERROR:Errors} ::= SEQUENCE -{ - invokeId InvokeId - (CONSTRAINED BY {-- must be that for an outstanding operation -- - } - ! RejectProblem : returnError-unrecognizedInvocation) - (CONSTRAINED BY {-- which returns an error -- - } - ! RejectProblem : returnError-errorResponseUnexpected), - errcode ERROR.&errorCode - ({Errors} - ! RejectProblem : returnError-unrecognizedError) - (CONSTRAINED BY {-- must be in the &Errors field of the associated operation -- - } - ! RejectProblem : returnError-unexpectedError), - parameter ERROR.&ParameterType - ({Errors}{@errcode} - ! RejectProblem : returnError-mistypedParameter) OPTIONAL - } -(CONSTRAINED BY {-- must conform to the above definition -- - } -! RejectProblem : general-mistypedPDU) - -Reject ::= SEQUENCE -{ - invokeId InvokeId, - problem CHOICE - { - general [0] GeneralProblem, - invoke [1] InvokeProblem, - returnResult [2] ReturnResultProblem, - returnError [3] ReturnErrorProblem - } - } -(CONSTRAINED BY {-- must conform to the above definition -- - } -! RejectProblem : general-mistypedPDU) - -GeneralProblem ::= INTEGER -{ - unrecognizedPDU (0), - mistypedPDU (1), - badlyStructuredPDU (2) - } - -InvokeProblem ::= INTEGER -{ - duplicateInvocation (0), - unrecognizedOperation (1), - mistypedArgument (2), - resourceLimitation (3), - releaseInProgress (4), - unrecognizedLinkedId (5), - linkedResponseUnexpected (6), - unexpectedLinkedOperation (7) - } - -ReturnResultProblem ::= INTEGER -{ - unrecognizedInvocation (0), - resultResponseUnexpected (1), - mistypedResult (2) - } - -ReturnErrorProblem ::= INTEGER -{ - unrecognizedInvocation (0), - errorResponseUnexpected (1), - unrecognizedError (2), - unexpectedError (3), - mistypedParameter (4) - } - -RejectProblem ::= INTEGER -{ - general-unrecognizedPDU (0), - general-mistypedPDU (1), - general-badlyStructuredPDU (2), - invoke-duplicateInvocation (10), - invoke-unrecognizedOperation (11), - invoke-mistypedArgument (12), - invoke-resourceLimitation (13), - invoke-releaseInProgress (14), - invoke-unrecognizedLinkedId (15), - invoke-linkedResponseUnexpected (16), - invoke-unexpectedLinkedOperation (17), - returnResult-unrecognizedInvocation (20), - returnResult-resultResponseUnexpected (21), - returnResult-mistypedResult (22), - returnError-unrecognizedInvocation (30), - returnError-errorResponseUnexpected (31), - returnError-unrecognizedError (32), - returnError-unexpectedError (33), - returnError-mistypedParameter (34) - } - -InvokeId ::= CHOICE -{ - present INTEGER, - absent NULL - } - -noInvokeId InvokeId ::= absent:NULL - -NoInvokeId InvokeId ::= {noInvokeId} - -Errors {OPERATION:Operations} ERROR ::= {Operations.&Errors} - -Bind {OPERATION:operation} ::= CHOICE -{ - bind-invoke [16] OPERATION.&ArgumentType({operation}), - bind-result [17] OPERATION.&ResultType ({operation}), - bind-error [18] OPERATION.&Errors.&ParameterType ({operation}) - } - -Unbind {OPERATION:operation} ::= CHOICE -{ - unbind-invoke [19] OPERATION.&ArgumentType({operation}), - unbind-result [20] OPERATION.&ResultType ({operation}), - unbind-error [21] OPERATION.&Errors.&ParameterType ({operation}) - } - -emptyBind OPERATION ::= {ERRORS {refuse} SYNCHRONOUS TRUE} - -emptyUnbind OPERATION ::= { SYNCHRONOUS TRUE } - -refuse ERROR ::= {CODE local:-1} - -no-op OPERATION ::= - { - IDEMPOTENT TRUE - ALWAYS RESPONDS FALSE - CODE local:-1 - } - -Forward {OPERATION:OperationSet} OPERATION ::= -{ - OperationSet | - OperationSet.&Linked.&Linked | - OperationSet.&Linked.&Linked.&Linked.&Linked - } - -Reverse {OPERATION:OperationSet} OPERATION ::= -{Forward{{OperationSet.&Linked}}} - -ConsumerPerforms {OPERATION-PACKAGE:package} OPERATION ::= -{ - Forward{{package.&Consumer}} | - Forward{{package.&Both}} | - Reverse{{package.&Supplier}} | - Reverse{{package.&Both}} - } - -SupplierPerforms {OPERATION-PACKAGE:package} OPERATION ::= -{ - Forward{{package.&Supplier}} | - Forward{{package.&Both}} | - Reverse{{package.&Consumer}} | - Reverse{{package.&Both}} - } - -AllOperations {OPERATION-PACKAGE:package} OPERATION ::= -{ - ConsumerPerforms {package} | - SupplierPerforms {package} - } - -recode {OPERATION:operation, Code:code} OPERATION ::= -{ - ARGUMENT operation.&ArgumentType - OPTIONAL operation.&argumentTypeOptional - RESULT operation.&ResultType - OPTIONAL operation.&resultTypeOptional - RETURN RESULT operation.&returnResult - ERRORS {operation.&Errors} - LINKED {operation.&Linked} - SYNCHRONOUS operation.&synchronous - ALWAYS RESPONDS operation.&alwaysReturns - INVOKE PRIORITY {operation.&InvokePriority} - RESULT-PRIORITY {operation.&ResultPriority} - CODE code - } - -switch {OPERATION-PACKAGE:package, OBJECT IDENTIFIER:id} OPERATION-PACKAGE ::= -{ - OPERATIONS {package.&Both} - CONSUMER INVOKES {package.&Consumer} - SUPPLIER INVOKES {package.&Supplier} - ID id - } - -combine {OPERATION-PACKAGE:ConsumerConsumes,OPERATION-PACKAGE:ConsumerSupplies, - OPERATION-PACKAGE:base - } OPERATION-PACKAGE ::= -{ - OPERATIONS {ConsumerConsumes.&Both | ConsumerSupplies.&Both} - CONSUMER INVOKES {ConsumerConsumes.&Consumer | ConsumerSupplies.&Supplier} - SUPPLIER INVOKES {ConsumerConsumes.&Supplier | ConsumerSupplies.&Consumer} - ID base.&id - } - -ROS-SingleAS {InvokeId:InvokeIdSet, OPERATION-PACKAGE:package} ::= ROS -{{InvokeIdSet}, {AllOperations{package}}, {AllOperations{package}}} - -ROS-ConsumerAS {InvokeId:InvokeIdSet, OPERATION-PACKAGE:package} ::= ROS -{{InvokeIdSet}, {ConsumerPerforms{package}}, {SupplierPerforms{package}}} - -ROS-SupplierAS {InvokeId:InvokeIdSet, OPERATION-PACKAGE:package} ::= ROS -{{InvokeIdSet}, {SupplierPerforms{package}}, {ConsumerPerforms{package}}} - -probe OPERATION ::= - { - ARGUMENT SEQUENCE - { - invokeId [0] InvokeId - } - RESULT ENUMERATED{running(0), finished(1), unknown(2), ...} - IDEMPOTENT TRUE - CODE local:-2 - } - -acknowledge OPERATION ::= - { - ARGUMENT InvokeId - RESULT ENUMERATED{acknowledged(0), unknown(1), ...} - IDEMPOTENT TRUE - CODE local:-3 - } - -ProbeAndAcknowledge OPERATION ::= {probe | acknowledge} - -cancel OPERATION ::= - { - ARGUMENT InvokeId - ERRORS {cancelFailed} - IDEMPOTENT TRUE - CODE local:-4 - } - -cancelFailed ERROR ::= - { - PARAMETER SET - { - problem [0] CancelProblem, - operation [1] InvokeId - } - CODE local:-2 - } - -CancelProblem ::= ENUMERATED -{unknownOperation(0), tooLate(1), operationNotCancellable(2), ...} - -cancelled ERROR ::= {CODE local:-3} - -END -- end of useful definitions. diff --git a/lib/asn1/test/asn1_SUITE_data/Tst.py b/lib/asn1/test/asn1_SUITE_data/Tst.py deleted file mode 100644 index d80b32dad5..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Tst.py +++ /dev/null @@ -1,153 +0,0 @@ -Tst { 2 6 6 24 7 1 } DEFINITIONS IMPLICIT TAGS ::= - -BEGIN - ---EXPORTS SomeSet , Id0 , Aset,Id1 ,A,B,C, --- Uhh ,Foo ,Cho,Person,Hobbe,Robbe,X,Y; - -IMPORTS Fooo FROM Bobby; - - -Robbe ::= SET { - ttt TT } - -Koo ::= SET { - c CHOICE { - a INTEGER, - b BOOLEAN }, - s SET OF Id0 } - - -Hobbe ::= [APPLICATION 1] SET { - aaa [0] SET OF INTEGER, - bbb [1] UU - } - -UU ::= PP -PP ::= CHOICE { - cc [1] CHOICE { - a [0] INTEGER, - b [1] BOOLEAN, - c [2] BIT STRING }, - ii [0] Id0 - } - - -TT ::= SS -SS ::= SET { - b BOOLEAN DEFAULT TRUE - } - -Aset ::= [PRIVATE 2] SET OF Uhh - - - -SomeSet ::= [PRIVATE 3] IMPLICIT SET { - aaaa [2] SET{ - ggg [0] INTEGER}, - kkkk [1] SET OF Id2, - booby [4] OCTET STRING, - puck [3] INTEGER {red(0),blue(1),yellow(-2)}, - baby [5] IMPLICIT Id1, - bool [6] BOOLEAN } - - -Id0 ::= INTEGER (4 .. 99) - -Id1 ::= Id0 - -Id2 ::= [PRIVATE 4] EXPLICIT Id1 - - -Uhh ::= SET { - a [1] IMPLICIT Id1} - - - -Soon ::= [PRIVATE 5] Moon - -Moon ::= [PRIVATE 6] IMPLICIT Person - - -Person ::= [PRIVATE 7] IMPLICIT SEQUENCE { - szzzs SET OF SET { - aaa [0] INTEGER, - bbb [1] Id0}, - cho Cho, - name OCTET STRING , - location INTEGER, - asss Aset, - oops [2] IMPLICIT SET { - q [0] INTEGER, - p [1] Uhh}, - on INTEGER, - mybits [3] IMPLICIT BIT STRING, - foo Foo, - age INTEGER, - hobbe [5] SEQUENCE { - a [4] CHOICE { - a INTEGER, - b BOOLEAN }, - b [5] Id0}} - - - - - -Foo ::= [PRIVATE 8] IMPLICIT SEQUENCE { - goofy [3] INTEGER OPTIONAL, - somestring [10] IMPLICIT OCTET STRING DEFAULT '77BB'H, - hoohoo [11] IMPLICIT SEQUENCE { - bar [1] Id1 OPTIONAL, - foo INTEGER, - zombie [9] CHOICE { - a [1] IMPLICIT INTEGER, - b [2] IMPLICIT BOOLEAN } - }, - moon [4] IMPLICIT INTEGER } - - - -Cho ::= [PRIVATE 9] EXPLICIT CHOICE { - somestring [2] IMPLICIT OCTET STRING, - goofy [9] INTEGER, - moon [4] IMPLICIT INTEGER } - - -A ::= [APPLICATION 2] SET { - ppp IA5String , - a [0] INTEGER {aaa(6),bbb(77)} DEFAULT 998, - b [1] Id1 OPTIONAL, - c [2] OCTET STRING (SIZE(8)), - dd [3] BIT STRING DEFAULT '11001'B } - -B ::= [APPLICATION 3] SET { - ww [1] SET { - a A OPTIONAL, - goofy [3] INTEGER OPTIONAL, - somestring [10] IMPLICIT OCTET STRING DEFAULT '77BB'H } - } - - -C::= [APPLICATION 4] SEQUENCE OF X - -Y ::= OBJECT IDENTIFIER - -X ::= SET { - a NULL, - b GeneralString, - c UTCTime, - d VideotexString, - g GeneralizedTime, - h GraphicString, - i VisibleString, - j IA5String, - k PrintableString, - l OCTET STRING, - e TeletexString, - m ANY, - n ObjectDescriptor, - o OBJECT IDENTIFIER, - f NumericString } - -END diff --git a/lib/asn1/test/asn1_SUITE_data/Two.py b/lib/asn1/test/asn1_SUITE_data/Two.py deleted file mode 100644 index c8e6f1a55b..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/Two.py +++ /dev/null @@ -1,34 +0,0 @@ -Two { 1 2 3} DEFINITIONS EXPLICIT TAGS ::= - -BEGIN -EXPORTS A, D,Boo,Szz; - - - -D ::= [PRIVATE 1] SEQUENCE { - a INTEGER, - b Boo, - c ANY DEFINED BY a , - d ANY } - - -Boo ::= SEQUENCE OF INTEGER (198..200) - -A ::= [PRIVATE 2] SEQUENCE { - a INTEGER (1..1), - b INTEGER (3..3) } - - -Szz ::= CHOICE { - one INTEGER, - two BOOLEAN } - -C ::= SET { - a [0] INTEGER (0..8), - xx [4] CHOICE { - [7] INTEGER (9..10), - a INTEGER (11 ..13) }, - f Boo, - r [2] INTEGER (20..22)} -END - diff --git a/lib/asn1/test/asn1_SUITE_data/UPERDefault.asn b/lib/asn1/test/asn1_SUITE_data/UPERDefault.asn deleted file mode 100644 index 7b81a0e09f..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/UPERDefault.asn +++ /dev/null @@ -1,18 +0,0 @@ -UPERDefault DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - --- OTP-7681 -Int ::= INTEGER (0..32767) - -Seq ::= SEQUENCE { - a Int, - b INTEGER (-27..27) DEFAULT 0, -- OTP-7678 - c INTEGER OPTIONAL -} - -seq Seq ::= -{a 12, - b 0} - -END \ No newline at end of file diff --git a/lib/asn1/test/asn1_SUITE_data/UndefType.py b/lib/asn1/test/asn1_SUITE_data/UndefType.py deleted file mode 100644 index cdbe083803..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/UndefType.py +++ /dev/null @@ -1,14 +0,0 @@ -Person DEFINITIONS IMPLICIT TAGS ::= -BEGIN -EXPORTS Person; -IMPORTS - ImportedFromUndefined FROM UndefinedModule; - -Feltyp ::= UndefinedType -Feltyp2 ::= ImportedFromUndefined -Person ::= [PRIVATE 19] SEQUENCE { - name Undefined, - location INTEGER {home(0),field(1),roving(2)}, - age ImportedFromUndefined OPTIONAL - } -END -- cgit v1.2.3 From 2bc24f790938c8a67e38eb24ccf14fc8afeebe71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 15 Mar 2013 15:07:07 +0100 Subject: PER/UPER: Eliminate gen_encode_prim_wrapper() and DoTag argument asn1ct_constructed_per:gen_encode_prim_wrapper() no longer serves any useful purpose, as it is easier to call asn1ct_per:gen_encode_prim() directly. Also, the DoTag argument for asn1ct_per:gen_encode_prim() is never actually used, so it can be eliminated at the same time. --- lib/asn1/src/asn1ct_constructed_per.erl | 28 ++++++++++------------------ lib/asn1/src/asn1ct_gen_per.erl | 32 ++++++++++++++++---------------- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 8 ++++---- 3 files changed, 30 insertions(+), 38 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index da8b59ad25..cb5352a4bd 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -619,10 +619,9 @@ gen_encode_sof_components(Erule,Typename,SeqOrSetOf,Cont) -> Conttype = asn1ct_gen:get_inner(Cont#type.def), Currmod = get(currmod), - Ctgenmod = asn1ct_gen:ct_gen_module(Erule), case asn1ct_gen:type(Conttype) of {primitive,bif} -> - gen_encode_prim_wrapper(Ctgenmod,Erule,Cont,false,"H"); + asn1ct_gen_per:gen_encode_prim(Erule, Cont, "H"); {constructed,bif} -> NewTypename = [Constructed_Suffix|Typename], emit({"'enc_",asn1ct_gen:list2name(NewTypename),"'(H", @@ -632,9 +631,9 @@ gen_encode_sof_components(Erule,Typename,SeqOrSetOf,Cont) -> #'Externaltypereference'{module=EMod,type=EType} -> emit({"'",EMod,"':'enc_",EType,"'(H)",nl,nl}); 'ASN1_OPEN_TYPE' -> - gen_encode_prim_wrapper(Ctgenmod,Erule, - #type{def='ASN1_OPEN_TYPE'}, - false,"H"); + asn1ct_gen_per:gen_encode_prim(Erule, + #type{def='ASN1_OPEN_TYPE'}, + "H"); _ -> emit({"'enc_",Conttype,"'(H)",nl,nl}) end, @@ -990,7 +989,6 @@ gen_enc_line(Erule,TopType, Cname, Type, [], Pos,DynamicEnc,Ext) -> Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))), gen_enc_line(Erule,TopType,Cname,Type,Element, Pos,DynamicEnc,Ext); gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> - Ctgenmod = asn1ct_gen:ct_gen_module(Erule), Atype = case Type of #type{def=#'ObjectClassFieldType'{type=InnerType}} -> @@ -1049,17 +1047,16 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> _ -> Type end, - gen_encode_prim_wrapper(Ctgenmod,Erule,EncType, - false,Element); + asn1ct_gen_per:gen_encode_prim(Erule, EncType, Element); 'ASN1_OPEN_TYPE' -> case Type#type.def of #'ObjectClassFieldType'{type=OpenType} -> - gen_encode_prim_wrapper(Ctgenmod,Erule, - #type{def=OpenType}, - false,Element); + asn1ct_gen_per:gen_encode_prim(Erule, + #type{def=OpenType}, + Element); _ -> - gen_encode_prim_wrapper(Ctgenmod,Erule,Type, - false,Element) + asn1ct_gen_per:gen_encode_prim(Erule, Type, + Element) end; {constructed,bif} -> NewTypename = [Cname|TopType], @@ -1738,11 +1735,6 @@ gen_dec_choice2(_, _, [], _, _, _) -> ok. indent(N) -> lists:duplicate(N,32). % 32 = space -gen_encode_prim_wrapper(CtgenMod,Erule,Cont,DoTag,Value) -> -% put(component_type,true), % add more info in component_type - CtgenMod:gen_encode_prim(Erule,Cont,DoTag,Value). -% erase(component_type). - make_elements(I,Val,ExtCnames) -> make_elements(I,Val,ExtCnames,[]). diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 23a6ce256c..12a4cd240c 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -26,7 +26,7 @@ %-compile(export_all). -export([gen_dec_imm/2]). --export([gen_dec_prim/3,gen_encode_prim/4]). +-export([gen_dec_prim/3,gen_encode_prim/3]). -export([gen_obj_code/3,gen_objectset_code/2]). -export([gen_decode/2, gen_decode/3]). -export([gen_encode/2, gen_encode/3]). @@ -84,10 +84,10 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}), case asn1ct_gen:type(InnerType) of {primitive,bif} -> - gen_encode_prim(Erules,Def,"false"), + gen_encode_prim(Erules, Def), emit({".",nl}); 'ASN1_OPEN_TYPE' -> - gen_encode_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"false"), + gen_encode_prim(Erules, Def#type{def='ASN1_OPEN_TYPE'}), emit({".",nl}); {constructed,bif} -> asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D); @@ -98,24 +98,24 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> end. -gen_encode_prim(Erules,D,DoTag) -> +gen_encode_prim(Erules, D) -> Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), - gen_encode_prim(Erules,D,DoTag,Value). + gen_encode_prim(Erules, D, Value). -gen_encode_prim(Erules, #type{def={'ENUMERATED',{N1,N2}}}, _, Value) -> +gen_encode_prim(Erules, #type{def={'ENUMERATED',{N1,N2}}}, Value) -> NewList = [{0,X} || {X,_} <- N1] ++ ['EXT_MARK'] ++ [{1,X} || {X,_} <- N2], NewC = {0,length(N1)-1}, emit(["case ",Value," of",nl]), emit_enc_enumerated_cases(Erules, NewC, NewList, 0); -gen_encode_prim(Erules, #type{def={'ENUMERATED',NNL}}, _, Value) -> +gen_encode_prim(Erules, #type{def={'ENUMERATED',NNL}}, Value) -> NewList = [X || {X,_} <- NNL], NewC = {0,length(NewList)-1}, emit(["case ",Value," of",nl]), emit_enc_enumerated_cases(Erules, NewC, NewList, 0); -gen_encode_prim(per=Erules, D, DoTag, Value) -> - asn1ct_gen_per_rt2ct:gen_encode_prim(Erules, D, DoTag, Value); -gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> +gen_encode_prim(per=Erules, D, Value) -> + asn1ct_gen_per_rt2ct:gen_encode_prim(Erules, D, Value); +gen_encode_prim(Erules, #type{}=D, Value) -> Constraint = D#type.constraint, SizeConstr = asn1ct_imm:effective_constraint(bitstring, Constraint), Pa = case lists:keyfind('PermittedAlphabet', 1, Constraint) of @@ -213,9 +213,9 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> #'ObjectClassFieldType'{} -> case asn1ct_gen:get_inner(D#type.def) of {fixedtypevaluefield,_,InnerType} -> - gen_encode_prim(Erules,InnerType,DoTag,Value); + gen_encode_prim(Erules, InnerType, Value); T -> %% 'ASN1_OPEN_TYPE' - gen_encode_prim(Erules,D#type{def=T},DoTag,Value) + gen_encode_prim(Erules, D#type{def=T}, Value) end; XX -> exit({asn1_error,nyi,XX}) @@ -418,7 +418,7 @@ gen_encode_field_call(Erules, ObjName, FieldName, Type) -> Def = Type#typedef.typespec, case Type#typedef.name of {primitive,bif} -> - gen_encode_prim(Erules, Def, "false", "Val"), + gen_encode_prim(Erules, Def, "Val"), []; {constructed,bif} -> emit({" 'enc_",ObjName,'_',FieldName, @@ -445,7 +445,7 @@ gen_encode_default_call(Erules, ClassName, FieldName, Type) -> [#typedef{name=[FieldName,ClassName], typespec=Type}]; {primitive,bif} -> - gen_encode_prim(Erules, Type, "false", "Val"), + gen_encode_prim(Erules, Type, "Val"), []; #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'enc_",Etype,"'(Val)",nl]), @@ -773,7 +773,7 @@ emit_inner_of_fun(Erule, #typedef{name={ExtMod,Name},typespec=Type}=TDef, case {ExtMod,Name} of {primitive,bif} -> emit(indent(12)), - gen_encode_prim(Erule, Type, dotag, "Val"), + gen_encode_prim(Erule, Type, "Val"), {[],0}; {constructed,bif} -> emit([indent(12),"'enc_", @@ -791,7 +791,7 @@ emit_inner_of_fun(Erule, #type{}=Type, _) -> case Type#type.def of Def when is_atom(Def) -> emit({indent(9),Def," ->",nl,indent(12)}), - gen_encode_prim(Erule, Type, dotag, "Val"); + gen_encode_prim(Erule, Type, "Val"); #'Externaltypereference'{module=CurrMod,type=T} -> emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"}); #'Externaltypereference'{module=ExtMod,type=T} -> diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 9fc6cea59a..c9e5f52e38 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -23,12 +23,12 @@ -include("asn1_records.hrl"). --export([gen_encode_prim/4]). +-export([gen_encode_prim/3]). -import(asn1ct_gen, [emit/1,demit/1]). -import(asn1ct_func, [call/3]). -gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> +gen_encode_prim(Erules, #type{}=D, Value) -> Constraint = D#type.constraint, case D#type.def of 'INTEGER' -> @@ -119,9 +119,9 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> #'ObjectClassFieldType'{} -> case asn1ct_gen:get_inner(D#type.def) of {fixedtypevaluefield,_,InnerType} -> - gen_encode_prim(Erules,InnerType,DoTag,Value); + gen_encode_prim(Erules, InnerType, Value); T -> %% 'ASN1_OPEN_TYPE' - gen_encode_prim(Erules,D#type{def=T},DoTag,Value) + gen_encode_prim(Erules, D#type{def=T}, Value) end; XX -> exit({asn1_error,nyi,XX}) -- cgit v1.2.3 From 6574e0f48d9ea6f18550d9a7a3bf3aec3d1790e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 19 Mar 2013 14:32:04 +0100 Subject: asn1ct_constructed_per: Remove dead code for handling of ENUMERATED An ENUMERATED is always represented as a two-tuple, never as three-tuple. --- lib/asn1/src/asn1ct_constructed_per.erl | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index cb5352a4bd..1edacd266a 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -515,13 +515,7 @@ gen_encode_sof(Erule,Typename,SeqOrSetOf,D) when is_record(D,type) -> emit({indent(3),"'enc_",asn1ct_gen:list2name(Typename), "_components'(Val",ObjFun,", [])"}), emit({nl,"].",nl}), - NewComponentType = - case ComponentType#type.def of - {'ENUMERATED',_,Component}-> - ComponentType#type{def={'ENUMERATED',Component}}; - _ -> ComponentType - end, - gen_encode_sof_components(Erule,Typename,SeqOrSetOf,NewComponentType). + gen_encode_sof_components(Erule, Typename, SeqOrSetOf, ComponentType). %% Logic copied from asn1_per_bin_rt2ct:encode_constrained_number @@ -583,13 +577,7 @@ gen_decode_sof(Erules,Typename,SeqOrSetOf,D) when is_record(D,type) -> emit([",",nl, "'dec_",asn1ct_gen:list2name(Typename), "_components'(",Num,", ",Buf,ObjFun,", []).",nl,nl]), - NewComponentType = - case ComponentType#type.def of - {'ENUMERATED',_,Component}-> - ComponentType#type{def={'ENUMERATED',Component}}; - _ -> ComponentType - end, - gen_decode_sof_components(Erules,Typename,SeqOrSetOf,NewComponentType). + gen_decode_sof_components(Erules, Typename, SeqOrSetOf, ComponentType). is_aligned(per) -> true; is_aligned(uper) -> false. -- cgit v1.2.3 From d6a126da137dcd46f1e238fbd906d6eab4691710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 20 Mar 2013 12:11:08 +0100 Subject: asn1ct: Refactor running of compiler passes As a preparation for future clean up of the error handling, we will need to take control on how how asn1ct runs the different compiler passes. --- lib/asn1/src/asn1ct.erl | 487 ++++++++++++++++++++++++------------------------ 1 file changed, 243 insertions(+), 244 deletions(-) diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 7f9d5d821a..b41af6ab1b 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -81,7 +81,6 @@ %% %% - compile(File) -> compile(File,[]). @@ -95,14 +94,30 @@ compile(File, Options0) when is_list(Options0) -> Error end. +-record(st, + {file=[], + files=[], + inputmodules=[], + code, + opts=[], + outfile, + dbfile, + includes=[], + erule, + error=none, + run + }). + compile_proc(File, Includes, Options) -> + Erule = get_rule(Options), + St = #st{opts=Options,includes=Includes,erule=Erule}, case input_file_type(File, Includes) of {single_file, SuffixedFile} -> %% "e.g. "/tmp/File.asn" - compile1(SuffixedFile, Options); + compile1(SuffixedFile, St); {multiple_files_file, SetBase, FileName} -> case get_file_list(FileName, Includes) of FileList when is_list(FileList) -> - compile_set(SetBase, FileList, Options); + compile_set(SetBase, FileList, St); Err -> Err end; @@ -110,22 +125,205 @@ compile_proc(File, Includes, Options) -> {error, Err} end. -compile1(File,Options) when is_list(Options) -> - verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File],Options), - verbose("Compiler Options: ~p~n",[Options],Options), - Ext = filename:extension(File), - Base = filename:basename(File,Ext), - OutFile = outfile(Base,"",Options), - DbFile = outfile(Base,"asn1db",Options), - Includes = [I || {i,I} <- Options], - EncodingRule = get_rule(Options), - Continue1 = scan(File,Options), - Continue2 = parse(Continue1,File,Options), - Continue3 = check(Continue2,File,OutFile,Includes,EncodingRule, - DbFile,Options,[]), - Continue4 = generate(Continue3,OutFile,EncodingRule,Options), - compile_erl(Continue4, OutFile, Options). +set_passes() -> + [{pass,scan_parse,fun set_scan_parse_pass/1}, + {pass,merge,fun merge_pass/1}|common_passes()]. + +single_passes() -> + [{pass,scan,fun scan_pass/1}, + {pass,parse,fun parse_pass/1}|common_passes()]. + +parse_and_save_passes() -> + [{pass,scan,fun scan_pass/1}, + {pass,parse,fun parse_pass/1}, + {pass,save,fun save_pass/1}]. + +common_passes() -> + [{pass,check,fun check_pass/1}, + {iff,abs,{pass,abs_listing,fun abs_listing/1}}, + {pass,generate,fun generate_pass/1}, + {unless,noobj,{pass,compile,fun compile_pass/1}}]. + +scan_pass(#st{file=File}=St) -> + case asn1ct_tok:file(File) of + {error,Reason} -> + {error,St#st{error=Reason}}; + Tokens when is_list(Tokens) -> + {ok,St#st{code=Tokens}} + end. + +set_scan_parse_pass(#st{files=Files}=St) -> + try + L = set_scan_parse_pass_1(Files, St), + {ok,St#st{code=L}} + catch + throw:Error -> + {error,St#st{error=Error}} + end. + +set_scan_parse_pass_1([F|Fs], St) -> + case asn1ct_tok:file(F) of + {error,Error} -> + throw(Error); + Tokens when is_list(Tokens) -> + case catch asn1ct_parser2:parse(Tokens) of + {ok,M} -> + [M|set_scan_parse_pass_1(Fs, St)]; + {error,ErrorTerm} -> + throw(handle_parse_error(ErrorTerm, St)) + end + end; +set_scan_parse_pass_1([], _) -> []. + +parse_pass(#st{code=Tokens}=St) -> + case catch asn1ct_parser2:parse(Tokens) of + {ok,M} -> + {ok,St#st{code=M}}; + {error,ErrorTerm} -> + {error,St#st{error=handle_parse_error(ErrorTerm, St)}} + end. + +handle_parse_error(ErrorTerm, #st{file=File,opts=Opts}) -> + case ErrorTerm of + {{Line,_Mod,Message},_TokTup} -> + if + is_integer(Line) -> + BaseName = filename:basename(File), + error("syntax error at line ~p in module ~s:~n", + [Line,BaseName], Opts); + true -> + error("syntax error in module ~p:~n", + [File], Opts) + end, + print_error_message(Message), + Message; + {Line,_Mod,[Message,Token]} -> + error("syntax error: ~p ~p at line ~p~n", + [Message,Token,Line], Opts), + {Line,[Message,Token]} + end. + +merge_pass(#st{file=Base,code=Code}=St) -> + M = merge_modules(Code, Base), + {ok,St#st{code=M}}. +check_pass(#st{code=M,file=File,includes=Includes, + erule=Erule,dbfile=DbFile,opts=Opts, + inputmodules=InputModules}=St) -> + start(Includes), + case asn1ct_check:storeindb(#state{erule=Erule}, M) of + ok -> + Module = asn1_db:dbget(M#module.name, 'MODULE'), + State = #state{mname=Module#module.name, + module=Module#module{typeorval=[]}, + erule=Erule, + inputmodules=InputModules, + options=Opts, + sourcedir=filename:dirname(File)}, + case asn1ct_check:check(State, Module#module.typeorval) of + {error,Reason} -> + {error,St#st{error=Reason}}; + {ok,NewTypeOrVal,GenTypeOrVal} -> + NewM = Module#module{typeorval=NewTypeOrVal}, + asn1_db:dbput(NewM#module.name, 'MODULE', NewM), + asn1_db:dbsave(DbFile, M#module.name), + verbose("--~p--~n", [{generated,DbFile}], Opts), + {ok,St#st{code={M,GenTypeOrVal}}} + end; + {error,Reason} -> + {error,St#st{error=Reason}} + end. + +save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) -> + ok = asn1ct_check:storeindb(#state{erule=Erule}, M), + asn1_db:dbsave(DbFile,M#module.name), + {ok,St}. + +abs_listing(#st{code={M,_},outfile=OutFile}) -> + pretty2(M#module.name, OutFile++".abs"), + done. + +generate_pass(#st{code=Code,outfile=OutFile,erule=Erule,opts=Opts}=St0) -> + St = St0#st{code=undefined}, %Reclaim heap space + case generate(Code, OutFile, Erule, Opts) of + {error,Reason} -> + {error,St#st{error=Reason}}; + ok -> + {ok,St} + end. + +compile_pass(#st{outfile=OutFile,opts=Opts0}=St) -> + asn1_db:dbstop(), %Reclaim memory. + asn1ct_table:delete([renamed_defs,original_imports,automatic_tags]), + Opts = remove_asn_flags(Opts0), + case c:c(OutFile, Opts) of + {ok,_Module} -> + {ok,St}; + _ -> + {error,St} + end. + +run_passes(Passes, #st{opts=Opts}=St) -> + Run = case lists:member(time, Opts) of + false -> + fun(_, Pass, S) -> Pass(S) end; + true -> + fun run_tc/3 + end, + run_passes_1(Passes, St#st{run=Run}). + +run_tc(Name, Fun, St) -> + Before0 = statistics(runtime), + Val = (catch Fun(St)), + After0 = statistics(runtime), + {Before_c, _} = Before0, + {After_c, _} = After0, + io:format("~-31s: ~10.2f s\n", + [Name,(After_c-Before_c) / 1000]), + Val. + +run_passes_1([{unless,Opt,Pass}|Passes], #st{opts=Opts}=St) -> + case proplists:get_bool(Opt, Opts) of + false -> + run_passes_1([Pass|Passes], St); + true -> + run_passes_1(Passes, St) + end; +run_passes_1([{iff,Opt,Pass}|Passes], #st{opts=Opts}=St) -> + case proplists:get_bool(Opt, Opts) of + true -> + run_passes_1([Pass|Passes], St); + false -> + run_passes_1(Passes, St) + end; +run_passes_1([{pass,Name,Pass}|Passes], #st{run=Run}=St0) + when is_function(Pass, 1) -> + try Run(Name, Pass, St0) of + {ok,St} -> + run_passes_1(Passes, St); + {error,#st{error=Error}} -> + {error,Error}; + done -> + ok + catch + Class:Error -> + Stk = erlang:get_stacktrace(), + io:format("Internal error: ~p:~p\n~p\n", + [Class,Error,Stk]), + {error,{internal_error,{Class,Error}}} + end; +run_passes_1([], _St) -> + ok. + +compile1(File, #st{opts=Opts}=St0) -> + verbose("Erlang ASN.1 version ~p, compiling ~p~n", [?vsn,File], Opts), + verbose("Compiler Options: ~p~n", [Opts], Opts), + Passes = single_passes(), + Base = filename:rootname(filename:basename(File)), + OutFile = outfile(Base, "", Opts), + DbFile = outfile(Base, "asn1db", Opts), + St1 = St0#st{file=File,outfile=OutFile,dbfile=DbFile}, + run_passes(Passes, St1). %%****************************************************************************%% %% functions dealing with compiling of several input files to one output file %% @@ -133,53 +331,20 @@ compile1(File,Options) when is_list(Options) -> %% compile_set/3 merges and compiles a number of asn1 modules %% specified in a .set.asn file to one .erl file. -compile_set(SetBase,Files,Options) - when is_list(hd(Files)),is_list(Options) -> - %% case when there are several input files in a list - verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files],Options), - verbose("Compiler Options: ~p~n",[Options],Options), - OutFile = outfile(SetBase,"",Options), - DbFile = outfile(SetBase,"asn1db",Options), - Includes = [I || {i,I} <- Options], - EncodingRule = get_rule(Options), - ScanRes = scan_set(Files,Options), - ParseRes = parse_set(ScanRes,Options), - Result = - case [X||X <- ParseRes,element(1,X)==true] of - [] -> %% all were false, time to quit - lists:map(fun(X)->element(2,X) end,ParseRes); - ParseRes -> %% all were true, continue with check - InputModules = - lists:map( - fun(F)-> - E = filename:extension(F), - B = filename:basename(F,E), - if - is_list(B) -> list_to_atom(B); - true -> B - end - end, - Files), - check_set(ParseRes,SetBase,OutFile,Includes, - EncodingRule,DbFile,Options,InputModules); - Other -> - {error,{'unexpected error in scan/parse phase', - lists:map(fun(X)->element(3,X) end,Other)}} - end, - Result. - -check_set(ParseRes,SetBase,OutFile,Includes,EncRule,DbFile, - Options,InputModules) -> - - MergedModule = merge_modules(ParseRes,SetBase), - SetM = MergedModule#module{name=SetBase}, - Continue1 = check({true,SetM},SetBase,OutFile,Includes,EncRule,DbFile, - Options,InputModules), - Continue2 = generate(Continue1,OutFile,EncRule,Options), - - asn1ct_table:delete([renamed_defs, original_imports, automatic_tags]), - - compile_erl(Continue2, OutFile, Options). +compile_set(SetBase, Files, #st{opts=Opts}=St0) -> + verbose("Erlang ASN.1 version ~p compiling ~p ~n", [?vsn,Files], Opts), + verbose("Compiler Options: ~p~n",[Opts], Opts), + OutFile = outfile(SetBase, "", Opts), + DbFile = outfile(SetBase, "asn1db", Opts), + InputModules = [begin + F1 = filename:basename(F0), + F = filename:rootname(F1), + list_to_atom(F) + end || F0 <- Files], + St = St0#st{file=SetBase,files=Files,outfile=OutFile, + dbfile=DbFile,inputmodules=InputModules}, + Passes = set_passes(), + run_passes(Passes, St). %% merge_modules/2 -> returns a module record where the typeorval lists are merged, %% the exports lists are merged, the imports lists are merged when the @@ -187,8 +352,7 @@ check_set(ParseRes,SetBase,OutFile,Includes,EncRule,DbFile, %% field gets the shared value if all modules have same tagging scheme, %% otherwise a tagging_error exception is thrown, %% the extensiondefault ...(not handled yet). -merge_modules(ParseRes,CommonName) -> - ModuleList = lists:map(fun(X)->element(2,X) end,ParseRes), +merge_modules(ModuleList, CommonName) -> NewModuleList = remove_name_collisions(ModuleList), case asn1ct_table:size(renamed_defs) of 0 -> asn1ct_table:delete(renamed_defs); @@ -650,123 +814,9 @@ delete_double_of_symbol1([],Acc) -> Acc. -scan_set(Files,Options) -> - %% The files in Files already have their relative path and extension - lists:map( - fun(F)-> - case scan(F,Options) of - {false,{error,Reason}} -> - throw({error,{'scan error in file:',F,Reason}}); - {TrueOrFalse,Res} -> - {TrueOrFalse,Res,F} - end - end, - Files). - -parse_set(ScanRes,Options) -> - lists:map( - fun({TorF,Toks,F})-> - case parse({TorF,Toks},F,Options) of - {false,{error,Reason}} -> - throw({error,{'parse error in file:',F,Reason}}); - {TrueOrFalse,Res} -> - {TrueOrFalse,Res,F} - end - end, - ScanRes). - - %%*********************************** - -scan(File,Options) -> - case asn1ct_tok:file(File) of - {error,Reason} -> - error("~p~n",[Reason],Options), - {false,{error,Reason}}; - Tokens -> - case lists:member(ss,Options) of - true -> % we terminate after scan - {false,Tokens}; - false -> % continue with next pass - {true,Tokens} - end - end. - - -parse({true,Tokens},File,Options) -> - %Presult = asn1ct_parser2:parse(Tokens), - %%case lists:member(p1,Options) of - %% true -> - %% asn1ct_parser:parse(Tokens); - %% _ -> - %% asn1ct_parser2:parse(Tokens) - %% end, - case catch asn1ct_parser2:parse(Tokens) of - {error,{{Line,_Mod,Message},_TokTup}} -> - if - is_integer(Line) -> - BaseName = filename:basename(File), - error("syntax error at line ~p in module ~s:~n", - [Line,BaseName],Options); - true -> - error("syntax error in module ~p:~n", - [File],Options) - end, - print_error_message(Message), - {false,{error,Message}}; - {error,{Line,_Mod,[Message,Token]}} -> - error("syntax error: ~p ~p at line ~p~n", - [Message,Token,Line],Options), - {false,{error,{Line,[Message,Token]}}}; - {ok,M} -> - case lists:member(sp,Options) of - true -> % terminate after parse - {false,M}; - false -> % continue with next pass - {true,M} - end; - OtherError -> - error("~p~n",[OtherError],Options) - end; -parse({false,Tokens},_,_) -> - {false,Tokens}. - -check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) -> - - start(Includes), - case asn1ct_check:storeindb(#state{erule=EncodingRule},M) of - ok -> - Module = asn1_db:dbget(M#module.name,'MODULE'), - State = #state{mname=Module#module.name, - module=Module#module{typeorval=[]}, - erule=EncodingRule, - inputmodules=InputMods, - options=Options, - sourcedir=filename:dirname(File)}, - Check = asn1ct_check:check(State,Module#module.typeorval), - case {Check,lists:member(abs,Options)} of - {{error,Reason},_} -> - {false,{error,Reason}}; - {{ok,NewTypeOrVal,_},true} -> - NewM = Module#module{typeorval=NewTypeOrVal}, - asn1_db:dbput(NewM#module.name,'MODULE',NewM), - pretty2(M#module.name,lists:concat([OutFile,".abs"])), - {false,ok}; - {{ok,NewTypeOrVal,GenTypeOrVal},_} -> - NewM = Module#module{typeorval=NewTypeOrVal}, - asn1_db:dbput(NewM#module.name,'MODULE',NewM), - asn1_db:dbsave(DbFile,M#module.name), - verbose("--~p--~n",[{generated,DbFile}],Options), - {true,{M,NewM,GenTypeOrVal}} - end; - ErrorList = {error,_} -> - {false,ErrorList} - end; -check({false,M},_,_,_,_,_,_,_) -> - {false,M}. - -generate({true,{M,_Module,GenTOrV}}, OutFile, EncodingRule, Options) -> +generate({M,GenTOrV}, OutFile, EncodingRule, Options) -> debug_on(Options), setup_bit_string_format(Options), put(encoding_options,Options), @@ -795,19 +845,7 @@ generate({true,{M,_Module,GenTOrV}}, OutFile, EncodingRule, Options) -> erase(tlv_format), % used in ber erase(class_default_type),% used in ber asn1ct_table:delete(check_functions), - case Result of - {error,_} -> - {false,Result}; - ok -> - case lists:member(sg,Options) of - true -> % terminate here , with .erl file generated - {false,true}; - false -> - {true,true} - end - end; -generate({false,M},_,_,_) -> - {false,M}. + Result. setup_bit_string_format(Opts) -> Format = case {lists:member(compact_bit_string, Opts), @@ -835,14 +873,13 @@ get_bit_string_format() -> parse_and_save(Module,S) -> Options = S#state.options, SourceDir = S#state.sourcedir, - Includes = [I || {i,I} <-Options], - - case get_input_file(Module,[SourceDir|Includes]) of + Includes = [I || {i,I} <- Options], + case get_input_file(Module, [SourceDir|Includes]) of %% search for asn1 source {file,SuffixedASN1source} -> case dbfile_uptodate(SuffixedASN1source,Options) of false -> - parse_and_save1(S,SuffixedASN1source,Options,Includes); + parse_and_save1(S, SuffixedASN1source, Options); _ -> ok end; Err -> @@ -850,24 +887,14 @@ parse_and_save(Module,S) -> [lists:concat([Module,".asn1db"])],Options), {error,{asn1,input_file_error,Err}} end. -parse_and_save1(S,File,Options,Includes) -> + +parse_and_save1(#state{erule=Erule}, File, Options) -> Ext = filename:extension(File), - Base = filename:basename(File,Ext), - DbFile = outfile(Base,"asn1db",Options), - Continue1 = scan(File,Options), - M = - case parse(Continue1,File,Options) of - {true,Mod} -> Mod; - _ -> -%% io:format("~p~nnow I die!!!!!!!!!!!~n",[File]), - exit({error,{asn1,File,"no such file"}}) - end, -% start(["."|Includes]), - start(Includes), - case asn1ct_check:storeindb(S,M) of - ok -> - asn1_db:dbsave(DbFile,M#module.name) - end. + Base = filename:basename(File, Ext), + DbFile = outfile(Base, "asn1db", Options), + St = #st{file=File,dbfile=DbFile,erule=Erule}, + Passes = parse_and_save_passes(), + run_passes(Passes, St). get_input_file(Module,[]) -> Module; @@ -925,13 +952,6 @@ dbfile_uptodate(File,Options) -> end. -compile_erl({true,_},OutFile,Options) -> - erl_compile(OutFile,Options); -compile_erl({false,true},_,_) -> - ok; -compile_erl({false,Result},_,_) -> - Result. - input_file_type(Name,I) -> case input_file_type(Name) of {error,_} -> input_file_type2(filename:basename(Name),I); @@ -1063,22 +1083,6 @@ translate_options([H|T]) -> [H|translate_options(T)]; translate_options([]) -> []. -erl_compile(OutFile,Options) -> -% io:format("Options:~n~p~n",[Options]), - case lists:member(noobj,Options) of - true -> - ok; - _ -> - ErlOptions = remove_asn_flags(Options), - %% io:format("~n~nc:c(~p,~p)~n~n",[OutFile,ErlOptions]), - case c:c(OutFile,ErlOptions) of - {ok,_Module} -> - ok; - _ -> - {error,'no_compilation'} - end - end. - remove_asn_flags(Options) -> [X || X <- Options, X /= get_rule(Options), @@ -1215,7 +1219,6 @@ make_erl_options(Opts) -> lists:map(fun(Dir) -> {i, Dir} end, Includes)]++Specific. pretty2(Module,AbsFile) -> - start(), {ok,F} = file:open(AbsFile,[write]), M = asn1_db:dbget(Module,'MODULE'), io:format(F,"%%%%%%%%%%%%%%%%%%% ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), @@ -1250,10 +1253,6 @@ pretty2(Module,AbsFile) -> lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,ObjectSets). -start() -> - Includes = ["."], - start(Includes). - start(Includes) when is_list(Includes) -> asn1_db:dbstart(Includes). -- cgit v1.2.3 From ccd626e59d7fa42e4fa23be4264e1fad01a22cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Mar 2013 14:35:26 +0100 Subject: asn1ct_check: Introduce check_fold/3 and clean up checkt/3 Capture the common pattern of checking a list of named ASN.1 items in a check_fold/3 function. Clean up checkt/3 using it, replacing the old-style catch with a try..catch. --- lib/asn1/src/asn1ct_check.erl | 89 +++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index e1e5140e08..d5fa42d377 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -99,7 +99,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> asn1ct_table:new(inlined_objects), - Terror = checkt(S,Types,[]), + Terror = checkt(S, Types), ?dbg("checkt finished with errors:~n~p~n~n",[Terror]), %% get parameterized object sets sent to checkt/3 @@ -338,51 +338,40 @@ chained_import(S,ImpMod,DefMod,Name) -> chained_import(S,OtherMod,DefMod,Name) end end. - -checkt(S,[Name|T],Acc) -> - ?dbg("Checking type ~p~n",[Name]), - Result = - case asn1_db:dbget(S#state.mname,Name) of - undefined -> - error({type,{internal_error,'???'},S}); - Type when is_record(Type,typedef) -> - NewS = S#state{type=Type,tname=Name}, - case catch(check_type(NewS,Type,Type#typedef.typespec)) of - {error,Reason} -> - error({type,Reason,NewS}); - {'EXIT',Reason} -> - error({type,{internal_error,Reason},NewS}); - {asn1_class,_ClassDef} -> - {asn1_class,Name}; - pobjectsetdef -> - {pobjectsetdef,Name}; - pvalueset -> - {pvalueset,Name}; - Ts -> - case Type#typedef.checked of - true -> % already checked and updated - ok; - _ -> - NewTypeDef = Type#typedef{checked=true,typespec = Ts}, - - asn1_db:dbput(NewS#state.mname,Name,NewTypeDef), % update the type - ok - end - end - end, - case Result of - ok -> - checkt(S,T,Acc); - _ -> - checkt(S,T,[Result|Acc]) - end; -checkt(S,[],Acc) -> - case check_contextswitchingtypes(S,[]) of - [] -> - lists:reverse(Acc); - L -> - checkt(S,L,Acc) +checkt(S0, Names) -> + Check = fun do_checkt/3, + + %% NOTE: check_type/3 will store information in the process + %% dictionary if context switching types are encountered; + %% therefore we must force the evaluation order. + Types = check_fold(S0, Names, Check), + CtxtSwitch = check_contextswitchingtypes(S0, []), + check_fold(S0, lists:reverse(CtxtSwitch), Check) ++ Types. + +do_checkt(S, Name, #typedef{typespec=TypeSpec}=Type0) -> + NewS = S#state{type=Type0,tname=Name}, + try check_type(NewS, Type0, TypeSpec) of + #type{}=Ts -> + case Type0#typedef.checked of + true -> %already checked and updated + ok; + _ -> + Type = Type0#typedef{checked=true, + typespec=Ts}, + asn1_db:dbput(NewS#state.mname, + Name, Type), + ok + end + catch + {error,Reason} -> + error({type,Reason,NewS}); + {asn1_class,_ClassDef} -> + {asn1_class,Name}; + pobjectsetdef -> + {pobjectsetdef,Name}; + pvalueset -> + {pvalueset,Name} end. check_contextswitchingtypes(S,Acc) -> @@ -7399,3 +7388,13 @@ insert_once(S,Tab,Key) -> _ -> skipped end. + +check_fold(S, [H|T], Check) -> + Type = asn1_db:dbget(S#state.mname, H), + case Check(S, H, Type) of + ok -> + check_fold(S, T, Check); + Error -> + [Error|check_fold(S, T, Check)] + end; +check_fold(_, [], Check) when is_function(Check, 3) -> []. -- cgit v1.2.3 From 5427156238ae33bfaa1e89a5ae7e44fcdb52b918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Mar 2013 15:23:03 +0100 Subject: asn1ct_check: Clean up checkv/3 --- lib/asn1/src/asn1ct_check.erl | 89 ++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index d5fa42d377..e557b64ca9 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -107,7 +107,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> {PObjSetNames1,Terror2} = filter_errors(IsPObjSet,Terror), - Verror = checkv(S,Values ++ ObjectSets,[]), %value sets may be parsed as object sets + Verror = checkv(S, Values ++ ObjectSets), %value sets may be parsed as object sets ?dbg("checkv finished with errors:~n~p~n~n",[Verror]), %% get information object classes wrongly sent to checkt/3 %% and update Terror2 @@ -391,56 +391,43 @@ check_contextswitchingtypes(S,[{T,TName}|Ts],Acc) -> check_contextswitchingtypes(_,[],Acc) -> Acc. -checkv(S,[Name|T],Acc) -> - ?dbg("Checking valuedef ~p~n",[Name]), - Result = case asn1_db:dbget(S#state.mname,Name) of - undefined -> error({value,{internal_error,'???'},S}); - Value when is_record(Value,valuedef); - is_record(Value,typedef); %Value set may be parsed as object set. - is_record(Value,pvaluedef); - is_record(Value,pvaluesetdef) -> - NewS = S#state{value=Value}, - case catch(check_value(NewS,Value)) of - {error,Reason} -> - error({value,Reason,NewS}); - {'EXIT',Reason} -> - error({value,{internal_error,Reason},NewS}); - {pobjectsetdef} -> - {pobjectsetdef,Name}; - {objectsetdef} -> - {objectsetdef,Name}; - {objectdef} -> - %% this is an object, save as typedef - #valuedef{checked=C,pos=Pos,name=N,type=Type, - value=Def}=Value, - ClassName = Type#type.def, - NewSpec = #'Object'{classname=ClassName, - def=Def}, - NewDef = #typedef{checked=C,pos=Pos,name=N, - typespec=NewSpec}, - asn1_db:dbput(NewS#state.mname,Name,NewDef), - {objectdef,Name}; - {valueset,VSet} -> - Pos = asn1ct:get_pos_of_def(Value), - CheckedVSDef = #typedef{checked=true,pos=Pos, - name=Name,typespec=VSet}, - asn1_db:dbput(NewS#state.mname,Name,CheckedVSDef), - {valueset,Name}; - V -> - %% update the valuedef - asn1_db:dbput(NewS#state.mname,Name,V), - ok - end - end, - case Result of - ok -> - checkv(S,T,Acc); - _ -> - checkv(S,T,[Result|Acc]) - end; -checkv(_S,[],Acc) -> - lists:reverse(Acc). - +checkv(S, Names) -> + check_fold(S, Names, fun do_checkv/3). + +do_checkv(S, Name, Value) + when is_record(Value, valuedef); + is_record(Value, typedef); %Value set may be parsed as object set. + is_record(Value, pvaluedef); + is_record(Value, pvaluesetdef) -> + NewS = S#state{value=Value}, + try check_value(NewS, Value) of + {valueset,VSet} -> + Pos = asn1ct:get_pos_of_def(Value), + CheckedVSDef = #typedef{checked=true,pos=Pos, + name=Name,typespec=VSet}, + asn1_db:dbput(NewS#state.mname, Name, CheckedVSDef), + {valueset,Name}; + V -> + %% update the valuedef + asn1_db:dbput(NewS#state.mname, Name, V), + ok + catch + {error,Reason} -> + error({value,Reason,NewS}); + {pobjectsetdef} -> + {pobjectsetdef,Name}; + {objectsetdef} -> + {objectsetdef,Name}; + {objectdef} -> + %% this is an object, save as typedef + #valuedef{checked=C,pos=Pos,name=N,type=Type, + value=Def} = Value, + ClassName = Type#type.def, + NewSpec = #'Object'{classname=ClassName,def=Def}, + NewDef = #typedef{checked=C,pos=Pos,name=N,typespec=NewSpec}, + asn1_db:dbput(NewS#state.mname, Name, NewDef), + {objectdef,Name} + end. checkp(S,[Name|T],Acc) -> %io:format("check_ptypedef:~p~n",[Name]), -- cgit v1.2.3 From e19f0f1e500748e154358fc01510ac69a30faa2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Mar 2013 15:35:17 +0100 Subject: asn1ct_check: Clean up checkp/3 --- lib/asn1/src/asn1ct_check.erl | 51 ++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index e557b64ca9..e4711ce471 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -92,7 +92,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> save_asn1db_uptodate(S,S#state.erule,S#state.mname), put(top_module,S#state.mname), - _Perror = checkp(S,ParameterizedTypes,[]), % must do this before the templates are used + _ = checkp(S, ParameterizedTypes), %must do this before the templates are used %% table to save instances of parameterized objects,object sets asn1ct_table:new(parameterized_objects), @@ -429,38 +429,25 @@ do_checkv(S, Name, Value) {objectdef,Name} end. -checkp(S,[Name|T],Acc) -> - %io:format("check_ptypedef:~p~n",[Name]), - Result = case asn1_db:dbget(S#state.mname,Name) of - undefined -> - error({type,{internal_error,'???'},S}); - Type when is_record(Type,ptypedef) -> - NewS = S#state{type=Type,tname=Name}, - case catch(check_ptype(NewS,Type,Type#ptypedef.typespec)) of - {error,Reason} -> - error({type,Reason,NewS}); - {'EXIT',Reason} -> - error({type,{internal_error,Reason},NewS}); - {asn1_class,_ClassDef} -> - {asn1_class,Name}; - {asn1_param_class,_} -> ok; - Ts -> - NewType = Type#ptypedef{checked=true,typespec = Ts}, - asn1_db:dbput(NewS#state.mname,Name,NewType), % update the type - ok - end - end, - case Result of - ok -> - checkp(S,T,Acc); - _ -> - checkp(S,T,[Result|Acc]) - end; -checkp(_S,[],Acc) -> - lists:reverse(Acc). - - +%% Check parameterized types. +checkp(S, Names) -> + check_fold(S, Names, fun do_checkp/3). +do_checkp(S0, Name, #ptypedef{typespec=TypeSpec}=Type0) -> + S = S0#state{type=Type0,tname=Name}, + try check_ptype(S, Type0, TypeSpec) of + #type{}=Ts -> + Type = Type0#ptypedef{checked=true,typespec=Ts}, + asn1_db:dbput(S#state.mname, Name, Type), + ok + catch + {error,Reason} -> + error({type,Reason,S}); + {asn1_class,_ClassDef} -> + {asn1_class,Name}; + {asn1_param_class,_} -> + ok + end. checkc(S,[Name|Cs],Acc) -> Result = -- cgit v1.2.3 From acce6d51405293637982bf2d8cb12cfc5218036c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 21 Mar 2013 15:48:42 +0100 Subject: asn1ct_check: Clean up checkc/3 --- lib/asn1/src/asn1ct_check.erl | 63 +++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index e4711ce471..bb0469d7a5 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -116,7 +116,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> NewClasses = Classes++AddClasses, - Cerror = checkc(S,NewClasses,[]), + Cerror = checkc(S, NewClasses), ?dbg("checkc finished with errors:~n~p~n~n",[Cerror]), %% get object sets incorrectly sent to checkv/3 %% and update Verror @@ -449,47 +449,28 @@ do_checkp(S0, Name, #ptypedef{typespec=TypeSpec}=Type0) -> ok end. -checkc(S,[Name|Cs],Acc) -> - Result = - case asn1_db:dbget(S#state.mname,Name) of - undefined -> - error({class,{internal_error,'???'},S}); - Class -> - ClassSpec = if - is_record(Class,classdef) -> -% Class#classdef.typespec; - Class; - is_record(Class,typedef) -> - Class#typedef.typespec - end, - NewS = S#state{type=Class,tname=Name}, - case catch(check_class(NewS,ClassSpec)) of - {error,Reason} -> - error({class,Reason,NewS}); - {'EXIT',Reason} -> - error({class,{internal_error,Reason},NewS}); - C -> - %% update the classdef - NewClass = - if - is_record(Class,classdef) -> - Class#classdef{checked=true,typespec=C}; - is_record(Class,typedef) -> - #classdef{checked=true,name=Name,typespec=C} - end, - asn1_db:dbput(NewS#state.mname,Name,NewClass), - ok - end +%% Check class definitions. +checkc(S, Names) -> + check_fold(S, Names, fun do_checkc/3). + +do_checkc(S0, Name, Class0) -> + {Class1,ClassSpec} = + case Class0 of + #classdef{} -> + {Class0,Class0}; + #typedef{} -> + {#classdef{name=Name},Class0#typedef.typespec} end, - case Result of - ok -> - checkc(S,Cs,Acc); - _ -> - checkc(S,Cs,[Result|Acc]) - end; -checkc(_S,[],Acc) -> -%% include_default_class(S#state.mname), - lists:reverse(Acc). + S = S0#state{type=Class0,tname=Name}, + try check_class(S, ClassSpec) of + C -> + Class = Class1#classdef{checked=true,typespec=C}, + asn1_db:dbput(S#state.mname, Name, Class), + ok + catch + {error,Reason} -> + error({class,Reason,S}) + end. checko(S,[Name|Os],Acc,ExclO,ExclOS) -> ?dbg("Checking object ~p~n",[Name]), -- cgit v1.2.3 From f5e68a7e844f491497d5363ce5bf8b5362ec387b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 22 Mar 2013 07:55:43 +0100 Subject: asn1_db: Remove unused functions Also replace unused code with assertions. While at it, also let reply/2 return 'ok' to silence Dialyzer warnings for unmatched returns. --- lib/asn1/src/asn1_db.erl | 52 ++++++++---------------------------------------- 1 file changed, 8 insertions(+), 44 deletions(-) diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl index 843fc66c9c..7ea29b7021 100644 --- a/lib/asn1/src/asn1_db.erl +++ b/lib/asn1/src/asn1_db.erl @@ -19,31 +19,22 @@ %% -module(asn1_db). --export([dbstart/1,dbnew/1,dbsave/2,dbload/1,dbput/3,dbget/2,dbget_all/1]). --export([dbget_all_mod/1,dbclear/0,dberase_module/1,dbstop/0]). +-export([dbstart/1,dbnew/1,dbsave/2,dbput/3,dbget/2]). +-export([dbstop/0]). -record(state, {parent, monitor, includes, table}). %% Interface dbstart(Includes) -> Parent = self(), - case get(?MODULE) of - undefined -> - put(?MODULE, spawn_link(fun() -> init(Parent, Includes) end)), - true; - _Pid -> - req({new_includes, Includes}) - end. + undefined = get(?MODULE), %Assertion. + put(?MODULE, spawn_link(fun() -> init(Parent, Includes) end)), + ok. dbnew(Module) -> req({new, Module}). dbsave(OutFile, Module) -> req({save, OutFile, Module}). -dbload(Module) -> req({load, Module}). dbput(Module, K, V) -> req({set, Module, K, V}). dbget(Module, K) -> req({get, Module, K}). -dbget_all(K) -> req({get_all, K}). -dbget_all_mod(Mod) -> req({all_mod, Mod}). -dbclear() -> req(clear). -dberase_module({module,M}) -> req({delete_mod, M}). dbstop() -> Resp = req(stop), erase(?MODULE), Resp. %% Internal functions @@ -60,7 +51,8 @@ req(Request) -> end. reply({Ref,From}, Response) -> - From ! {{Ref,?MODULE}, Response}. + From ! {{Ref,?MODULE}, Response}, + ok. init(Parent, Includes) -> MRef = erlang:monitor(process, Parent), @@ -85,44 +77,16 @@ loop(#state{parent = Parent, monitor = MRef, table = Table, _Error -> reply(From, undefined) end, loop(State); - {From, {all_mod, Mod}} -> - [{_, Modtab}] = ets:lookup(Table, Mod), - reply(From, ets:tab2list(Modtab)), - loop(State); - {From, {delete_mod, Mod}} -> - [{_, Modtab}] = ets:lookup(Table, Mod), - ets:delete(Modtab), - ets:delete(Table, Mod), - reply(From, ok), - loop(State); {From, {save, OutFile, Mod}} -> [{_,Mtab}] = ets:lookup(Table, Mod), reply(From, ets:tab2file(Mtab, OutFile)), loop(State); - {From, {load, Mod}} -> - Result = case ets:lookup(Table, Mod) of - [] -> opentab(Table, Mod, Includes); - [{_, Modtab}] -> {ok, Modtab} - end, - reply(From, Result), - loop(State); {From, {new, Mod}} -> - case ets:lookup(Table, Mod) of - [{_, Modtab}] -> ets:delete(Modtab); - _ -> true - end, + [] = ets:lookup(Table, Mod), %Assertion. ModTableId = ets:new(list_to_atom(lists:concat(["asn1_",Mod])), []), ets:insert(Table, {Mod, ModTableId}), reply(From, ok), loop(State); - {From, clear} -> - [ets:delete(Mt) || {_, Mt} <- ets:tab2list(Table)], - ets:delete(Table), - reply(From, cleared), - loop(State#state{table = ets:new(asn1, [set])}); - {From, {new_includes, NewIncludes}} -> - reply(From, true), - loop(State#state{includes = NewIncludes}); {From, stop} -> reply(From, stopped); %% Nothing to store {'DOWN', MRef, process, Parent, Reason} -> -- cgit v1.2.3 From 6f9ebc3237cf62fb550c9417569ca78c79865b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 22 Mar 2013 09:46:23 +0100 Subject: asn1_db: Make dbput/3 and dbsave/2 asynchronous Those functions have no reason to be synchronous since they don't have a useful return value. --- lib/asn1/src/asn1_db.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl index 7ea29b7021..e96ca9ae25 100644 --- a/lib/asn1/src/asn1_db.erl +++ b/lib/asn1/src/asn1_db.erl @@ -32,8 +32,8 @@ dbstart(Includes) -> ok. dbnew(Module) -> req({new, Module}). -dbsave(OutFile, Module) -> req({save, OutFile, Module}). -dbput(Module, K, V) -> req({set, Module, K, V}). +dbsave(OutFile, Module) -> cast({save, OutFile, Module}). +dbput(Module, K, V) -> cast({set, Module, K, V}). dbget(Module, K) -> req({get, Module, K}). dbstop() -> Resp = req(stop), erase(?MODULE), Resp. @@ -50,6 +50,10 @@ req(Request) -> exit({db_error,Info}) end. +cast(Request) -> + get(?MODULE) ! Request, + ok. + reply({Ref,From}, Response) -> From ! {{Ref,?MODULE}, Response}, ok. @@ -62,10 +66,9 @@ init(Parent, Includes) -> loop(#state{parent = Parent, monitor = MRef, table = Table, includes = Includes} = State) -> receive - {From, {set, Mod, K2, V}} -> + {set, Mod, K2, V} -> [{_, Modtab}] = ets:lookup(Table, Mod), ets:insert(Modtab, {K2, V}), - reply(From, ok), loop(State); {From, {get, Mod, K2}} -> Result = case ets:lookup(Table, Mod) of @@ -77,9 +80,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table, _Error -> reply(From, undefined) end, loop(State); - {From, {save, OutFile, Mod}} -> + {save, OutFile, Mod} -> [{_,Mtab}] = ets:lookup(Table, Mod), - reply(From, ets:tab2file(Mtab, OutFile)), + ok = ets:tab2file(Mtab, OutFile), loop(State); {From, {new, Mod}} -> [] = ets:lookup(Table, Mod), %Assertion. -- cgit v1.2.3 From a70395cd59e07a03ad003fa0cf166e1237151cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Mar 2013 11:44:18 +0100 Subject: Introduce a new mechanism for structured error handling --- lib/asn1/src/asn1ct.erl | 21 +++++++++-- lib/asn1/src/asn1ct_check.erl | 82 +++++++++++++++---------------------------- lib/asn1/test/Makefile | 3 +- lib/asn1/test/asn1_SUITE.erl | 6 ++-- lib/asn1/test/error_SUITE.erl | 73 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 59 deletions(-) create mode 100644 lib/asn1/test/error_SUITE.erl diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index b41af6ab1b..15aa4efe7d 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -301,8 +301,10 @@ run_passes_1([{pass,Name,Pass}|Passes], #st{run=Run}=St0) try Run(Name, Pass, St0) of {ok,St} -> run_passes_1(Passes, St); - {error,#st{error=Error}} -> - {error,Error}; + {error,#st{error=Errors}} -> + {Structured,AllErrors} = clean_errors(Errors), + print_structured_errors(Structured), + {error,AllErrors}; done -> ok catch @@ -315,6 +317,21 @@ run_passes_1([{pass,Name,Pass}|Passes], #st{run=Run}=St0) run_passes_1([], _St) -> ok. +clean_errors(Errors) when is_list(Errors) -> + F = fun({structured_error,_,_,_}) -> true; + (_) -> false + end, + {Structured0,AdHoc} = lists:partition(F, Errors), + Structured = lists:sort(Structured0), + {Structured,Structured ++ AdHoc}; +clean_errors(AdHoc) -> {[],AdHoc}. + +print_structured_errors([_|_]=Errors) -> + _ = [io:format("~ts:~w: ~ts\n", [F,L,M:format_error(E)]) || + {structured_error,{F,L},M,E} <- Errors], + ok; +print_structured_errors(_) -> ok. + compile1(File, #st{opts=Opts}=St0) -> verbose("Erlang ASN.1 version ~p, compiling ~p~n", [?vsn,File], Opts), verbose("Compiler Options: ~p~n", [Opts], Opts), diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index bb0469d7a5..3c9e0dd8d2 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -25,7 +25,7 @@ %-compile(export_all). %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). --export([check/2,storeindb/2]). +-export([check/2,storeindb/2,format_error/1]). %-define(debug,1). -include("asn1_records.hrl"). %%% The tag-number for universal types @@ -175,8 +175,9 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> {NewTypes,NewValues,ParameterizedTypes,NewClasses, lists:subtract(NewObjects,ExclO)++InlinedObjects, lists:subtract(NewObjectSets,ExclOS)++ParObjectSetNames}}; - _ ->{error,{asn1,lists:flatten([Terror3,Verror5,Cerror, - Oerror,Exporterror,ImportError])}} + _ -> + {error,lists:flatten([Terror3,Verror5,Cerror, + Oerror,Exporterror,ImportError])} end. context_switch_in_spec() -> @@ -6946,60 +6947,27 @@ storeindb(S,M) when is_record(M,module) -> NewM = M#module{typeorval=findtypes_and_values(TVlist)}, asn1_db:dbnew(NewM#module.name), asn1_db:dbput(NewM#module.name,'MODULE', NewM), - Res = storeindb(NewM#module.name,TVlist,[]), + Res = storeindb(#state{mname=NewM#module.name}, TVlist, []), include_default_class(S,NewM#module.name), include_default_type(NewM#module.name), Res. -storeindb(Module,[H|T],ErrAcc) when is_record(H,typedef) -> - storeindb(Module,H#typedef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,valuedef) -> - storeindb(Module,H#valuedef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,ptypedef) -> - storeindb(Module,H#ptypedef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,classdef) -> - storeindb(Module,H#classdef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,pvaluesetdef) -> - storeindb(Module,H#pvaluesetdef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,pobjectdef) -> - storeindb(Module,H#pobjectdef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,pvaluedef) -> - storeindb(Module,H#pvaluedef.name,H,T,ErrAcc); -storeindb(_,[],[]) -> ok; -storeindb(_,[],ErrAcc) -> - {error,ErrAcc}. - -storeindb(Module,Name,H,T,ErrAcc) -> - case asn1_db:dbget(Module,Name) of +storeindb(#state{mname=Module}=S, [H|T], Errors) -> + Name = asn1ct:get_name_of_def(H), + case asn1_db:dbget(Module, Name) of undefined -> - asn1_db:dbput(Module,Name,H), - storeindb(Module,T,ErrAcc); - _ -> - case H of - _Type when is_record(H,typedef) -> - error({type,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,valuedef) -> - error({value,"already defined", - #state{mname=Module,value=H,vname=Name}}); - _Type when is_record(H,ptypedef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,pobjectdef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,pvaluesetdef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,pvaluedef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,classdef) -> - error({class,"already defined", - #state{mname=Module,value=H,vname=Name}}) - end, - storeindb(Module,T,[H|ErrAcc]) - end. + asn1_db:dbput(Module, Name, H), + storeindb(S, T, Errors); + Prev -> + PrevLine = asn1ct:get_pos_of_def(Prev), + {error,Error} = asn1_error(S, H, {already_defined,Name,PrevLine}), + storeindb(S, T, [Error|Errors]) + end; +storeindb(_, [], []) -> + ok; +storeindb(_, [], [_|_]=Errors) -> + {error,Errors}. + findtypes_and_values(TVList) -> findtypes_and_values(TVList,[],[],[],[],[],[]).%% Types,Values, @@ -7039,7 +7007,15 @@ findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) -> {lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc), lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}. - +asn1_error(#state{mname=Where}, Item, Error) -> + Pos = asn1ct:get_pos_of_def(Item), + {error,{structured_error,{Where,Pos},?MODULE,Error}}. + +format_error({already_defined,Name,PrevLine}) -> + io_lib:format("the name ~p has already been defined at line ~p", + [Name,PrevLine]); +format_error(Other) -> + io_lib:format("~p", [Other]). error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) -> Pos = Ref#'Externaltypereference'.pos, diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile index 6e6ab4639c..15b97df972 100644 --- a/lib/asn1/test/Makefile +++ b/lib/asn1/test/Makefile @@ -114,7 +114,8 @@ MODULES= \ asn1_app_test \ asn1_appup_test \ asn1_wrapper \ - asn1_SUITE + asn1_SUITE \ + error_SUITE SUITE= asn1_SUITE.erl diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 5ec201fd78..2b3ff07980 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -860,7 +860,7 @@ testInvokeMod(Config, Rule, Opts) -> {ok, _Result2} = 'PrimStrings':encode('Bs1', [1, 0, 1, 0]). testExport(Config) -> - {error, {asn1, _Reason}} = + {error, _} = asn1ct:compile(filename:join(?config(data_dir, Config), "IllegalExport"), [{outdir, ?config(case_dir, Config)}]). @@ -906,8 +906,8 @@ testOpenTypeImplicitTag(Config, Rule, Opts) -> duplicate_tags(Config) -> DataDir = ?config(data_dir, Config), CaseDir = ?config(case_dir, Config), - {error, {asn1, [{error, {type, _, _, 'SeqOpt1Imp', - {asn1, {duplicates_of_the_tags, _}}}}]}} = + {error, [{error, {type, _, _, 'SeqOpt1Imp', + {asn1, {duplicates_of_the_tags, _}}}}]} = asn1ct:compile(filename:join(DataDir, "SeqOptional2"), [abs, {outdir, CaseDir}]). diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl new file mode 100644 index 0000000000..4dd4d58aad --- /dev/null +++ b/lib/asn1/test/error_SUITE.erl @@ -0,0 +1,73 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(error_SUITE). +-export([suite/0,all/0,groups/0, + already_defined/1]). + +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group,p}]. + +groups() -> + [{p,parallel(),[already_defined]}]. + +parallel() -> + case erlang:system_info(schedulers) > 1 of + true -> [parallel]; + false -> [] + end. + +already_defined(Config) -> + M = 'Already', + P = {M, + <<"Already DEFINITIONS ::= BEGIN\n" + " I ::= INTEGER\n" + " i I ::= 42\n" + " I ::= OCTET STRING\n" + " I ::= CLASS { &Type }\n" + " MYCLASS ::= CLASS { &Type }\n" + " i MYCLASS ::= { &Type INTEGER }\n" + " o MYCLASS ::= { &Type INTEGER }\n" + " I MYCLASS ::= { o }\n" + " I{T} ::= SEQUENCE OF T\n" + " I{INTEGER:x} INTEGER ::= { 1 | 2 | x }\n" + " i{T} MYCLASS ::= { &Type T }\n" + "END\n">>}, + {error, + [ + {structured_error,{M,4},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,5},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,7},asn1ct_check,{already_defined,'i',3}}, + {structured_error,{M,9},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,10},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,11},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,12},asn1ct_check,{already_defined,'i',3}} + ] + } = run(P, Config), + ok. + +run({Mod,Spec}, Config) -> + Base = atom_to_list(Mod) ++ ".asn1", + File = filename:join(?config(priv_dir, Config), Base), + ok = file:write_file(File, Spec), + asn1ct:compile(File). -- cgit v1.2.3 From 8b68ddddd113ca304690136efb6889fc565aeb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Mar 2013 11:51:59 +0100 Subject: Clean up checking of values for ENUMERATEDs Unify the code for checking an enumeration value named in a DEFAULT and in an ENUMERATED value. There is no need to handle those cases differently. That also will also make sure that the following works: E ::= ENUMERATED { x, ..., y } e E ::= x (Extensible ENUMERATEDs were not handled when defining values.) Always generate an error when an unknown enumeration value is given (used in a DEFAULT, a message would be printed, but the compilation would succeed). Also make sure that we always include the line number for the incorrect enumeration. Write a new test case and remove the extremely rudimentary value_bad_enum_test/1 test case. --- lib/asn1/src/asn1ct.erl | 2 + lib/asn1/src/asn1ct_check.erl | 60 ++++++++----------------- lib/asn1/test/asn1_SUITE.erl | 6 --- lib/asn1/test/asn1_SUITE_data/BadEnumValue1.asn | 8 ---- lib/asn1/test/error_SUITE.erl | 35 ++++++++++++++- 5 files changed, 54 insertions(+), 57 deletions(-) delete mode 100644 lib/asn1/test/asn1_SUITE_data/BadEnumValue1.asn diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 15aa4efe7d..8e71a5697c 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -559,6 +559,8 @@ get_pos_of_def(#pobjectdef{pos=Pos}) -> Pos; get_pos_of_def(#pobjectsetdef{pos=Pos}) -> Pos; +get_pos_of_def(#'Externalvaluereference'{pos=Pos}) -> + Pos; get_pos_of_def(_) -> undefined. diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 3c9e0dd8d2..ff583d2605 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2151,8 +2151,7 @@ check_value(OldS=#state{recordtopname=TopName},V) when is_record(V,valuedef) -> 'REAL' -> ok = validate_real(SVal,Value,Constr), #newv{value=normalize_value(SVal,Vtype,Value,[])}; - {'ENUMERATED',NamedNumberList} -> - ok=validate_enumerated(SVal,Value,NamedNumberList,Constr), + {'ENUMERATED',_} -> #newv{value=normalize_value(SVal,Vtype,Value,[])}; 'BOOLEAN'-> ok=validate_boolean(SVal,Value,Constr), @@ -2511,23 +2510,6 @@ validate_objectdescriptor(_S,_Value,_Constr) -> validate_real(_S,_Value,_Constr) -> ok. -validate_enumerated(S,Id,NamedNumberList,_Constr) when is_atom(Id) -> - case lists:keysearch(Id,1,NamedNumberList) of - {value,_} -> ok; - false -> error({value,"unknown ENUMERATED",S}) - end; -validate_enumerated(S,{identifier,_Pos,Id},NamedNumberList,_Constr) -> - case lists:keysearch(Id,1,NamedNumberList) of - {value,_} -> ok; - false -> error({value,"unknown ENUMERATED",S}) - end; -validate_enumerated(S,#'Externalvaluereference'{value=Id}, - NamedNumberList,_Constr) -> - case lists:keysearch(Id,1,NamedNumberList) of - {value,_} -> ok; - false -> error({value,"unknown ENUMERATED",S}) - end. - validate_boolean(_S,_Value,_Constr) -> ok. @@ -2588,7 +2570,8 @@ normalize_value(_,_,mandatory,_) -> mandatory; normalize_value(_,_,'OPTIONAL',_) -> 'OPTIONAL'; -normalize_value(S,Type,{'DEFAULT',Value},NameList) -> +normalize_value(S0, Type, {'DEFAULT',Value}, NameList) -> + S = S0#state{value=Value}, case catch get_canonic_type(S,Type,NameList) of {'BOOLEAN',CType,_} -> normalize_boolean(S,Value,CType); @@ -2827,29 +2810,20 @@ normalize_objectdescriptor(Value) -> normalize_real(Value) -> Value. -normalize_enumerated(S,#'Externalvaluereference'{value=V},CType) - when is_list(CType) -> - normalize_enumerated2(S,V,CType); -normalize_enumerated(S,Value,CType) when is_atom(Value),is_list(CType) -> - normalize_enumerated2(S,Value,CType); -normalize_enumerated(S,{Name,EnumV},CType) when is_atom(Name) -> - normalize_enumerated(S,EnumV,CType); -normalize_enumerated(S,Value,{CType1,CType2}) when is_list(CType1), is_list(CType2)-> - normalize_enumerated(S,Value,CType1++CType2); -normalize_enumerated(S,V,CType) -> - asn1ct:warning("Enumerated unknown type ~p~n",[CType],S, - "Enumerated unknown type"), - V. -normalize_enumerated2(S,V,Enum) -> - case lists:keysearch(V,1,Enum) of - {value,{Val,_}} -> Val; - _ -> - asn1ct:warning("enumerated value is not correct ~p~n",[V],S, - "enumerated value is not correct"), - V +normalize_enumerated(S, Id, {Base,Ext}) -> + %% Extensible ENUMERATED. + normalize_enumerated(S, Id, Base++Ext); +normalize_enumerated(S, #'Externalvaluereference'{value=Id}, + NamedNumberList) -> + normalize_enumerated(S, Id, NamedNumberList); +normalize_enumerated(S, Id, NamedNumberList) when is_atom(Id) -> + case lists:keymember(Id, 1, NamedNumberList) of + true -> + Id; + false -> + throw(asn1_error(S, S#state.value, {undefined,Id})) end. - normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) -> case catch lists:keysearch(C,#'ComponentType'.name,CType) of {value,#'ComponentType'{typespec=CT,name=Name}} -> @@ -7014,9 +6988,13 @@ asn1_error(#state{mname=Where}, Item, Error) -> format_error({already_defined,Name,PrevLine}) -> io_lib:format("the name ~p has already been defined at line ~p", [Name,PrevLine]); +format_error({undefined,Name}) -> + io_lib:format("'~s' is referenced, but is not defined", [Name]); format_error(Other) -> io_lib:format("~p", [Other]). +error({_,{structured_error,_,_,_}=SE,_}) -> + SE; error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) -> Pos = Ref#'Externaltypereference'.pos, io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]), diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 2b3ff07980..6be493320c 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -139,7 +139,6 @@ groups() -> testSetOfCho, testEnumExt, value_test, - value_bad_enum_test, testSeq2738, % Uses 'Constructed' {group, [], [constructed, @@ -741,11 +740,6 @@ value_test(Config, Rule, Opts) -> {ok, _} = asn1ct:test('ObjIdValues', 'ObjIdType', 'ObjIdValues':'mobileDomainId'()). -value_bad_enum_test(Config) -> - {error, _} = asn1ct:compile(?config(data_dir, Config) ++ - "BadEnumValue1", - [{outdir, ?config(case_dir, Config)}]). - constructed(Config) -> test(Config, fun constructed/3, [ber]). constructed(Config, Rule, Opts) -> diff --git a/lib/asn1/test/asn1_SUITE_data/BadEnumValue1.asn b/lib/asn1/test/asn1_SUITE_data/BadEnumValue1.asn deleted file mode 100644 index dbc224a74b..0000000000 --- a/lib/asn1/test/asn1_SUITE_data/BadEnumValue1.asn +++ /dev/null @@ -1,8 +0,0 @@ -BadEnumValue1 DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -E3 ::= ENUMERATED {monday,thuesday(0)} -enumWrongVal E3 ::= sunday - -END diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl index 4dd4d58aad..a94a6d95a0 100644 --- a/lib/asn1/test/error_SUITE.erl +++ b/lib/asn1/test/error_SUITE.erl @@ -19,7 +19,7 @@ -module(error_SUITE). -export([suite/0,all/0,groups/0, - already_defined/1]). + already_defined/1,enumerated/1]). -include_lib("test_server/include/test_server.hrl"). @@ -29,7 +29,8 @@ all() -> [{group,p}]. groups() -> - [{p,parallel(),[already_defined]}]. + [{p,parallel(),[already_defined, + enumerated]}]. parallel() -> case erlang:system_info(schedulers) > 1 of @@ -66,6 +67,36 @@ already_defined(Config) -> } = run(P, Config), ok. +enumerated(Config) -> + M = 'Enumerated', + P = {M, + <<"Enumerated DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + " Enum ::= ENUMERATED { a, b, c }\n" + " e Enum ::= d\n" + " EnumExt ::= ENUMERATED { x, ..., y }\n" + " ext EnumExt ::= z\n" + " S1 ::= SEQUENCE {\n" + " ge1 Enum DEFAULT a,\n" + " ge2 EnumExt DEFAULT x,\n" + " ge3 EnumExt DEFAULT y,\n" + " e Enum DEFAULT aa\n" + " }\n" + " S2 ::= SEQUENCE {\n" + " e2 EnumExt DEFAULT xyz\n" + " }\n" + "END\n">>}, + {error, + [ + {structured_error,{'Enumerated',3},asn1ct_check,{undefined,d}}, + {structured_error,{'Enumerated',5},asn1ct_check,{undefined,z}}, + {structured_error,{'Enumerated',10},asn1ct_check,{undefined,aa}}, + {structured_error,{'Enumerated',13},asn1ct_check,{undefined,xyz}} + ] + } = run(P, Config), + ok. + + + run({Mod,Spec}, Config) -> Base = atom_to_list(Mod) ++ ".asn1", File = filename:join(?config(priv_dir, Config), Base), -- cgit v1.2.3 From 19b84eb5f8a2b344447cd8204cdd490d0d9123ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 26 Mar 2013 16:37:15 +0100 Subject: Extend tests to cover more code in asn1ct_check --- lib/asn1/test/asn1_SUITE_data/SeqDefault.asn1 | 11 +++++++++++ lib/asn1/test/asn1_SUITE_data/SetDefault.asn1 | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/asn1/test/asn1_SUITE_data/SeqDefault.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqDefault.asn1 index 99e79da972..5c8583884a 100644 --- a/lib/asn1/test/asn1_SUITE_data/SeqDefault.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/SeqDefault.asn1 @@ -74,4 +74,15 @@ SeqIn ::= SEQUENCE intIn INTEGER DEFAULT 12 } +SeqExp ::= SEQUENCE +{ + bool BOOLEAN, + ..., + int INTEGER +} + +SeqDef4 ::= SEQUENCE { + seq SeqExp DEFAULT { bool TRUE, int 42 } +} + END diff --git a/lib/asn1/test/asn1_SUITE_data/SetDefault.asn1 b/lib/asn1/test/asn1_SUITE_data/SetDefault.asn1 index cb9e0ead62..0bbe301ae7 100644 --- a/lib/asn1/test/asn1_SUITE_data/SetDefault.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/SetDefault.asn1 @@ -30,4 +30,15 @@ SetIn ::= SET intIn INTEGER DEFAULT 12 } +SetDef4 ::= SET { + set SetExt DEFAULT { intIn 42, boolIn TRUE } +} + +SetExt ::= SET +{ + boolIn BOOLEAN, + ..., + intIn INTEGER +} + END -- cgit v1.2.3 From f6269228fdac5c11decb9e5daef81b859a942936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 27 Mar 2013 07:32:36 +0100 Subject: asn1ct: Simplify check_value/2 Most types don't have any validation functions that does anything useful, so it is sufficient to call normalize_value/4 for them. --- lib/asn1/src/asn1ct_check.erl | 280 ++++++++++-------------------------------- 1 file changed, 68 insertions(+), 212 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index ff583d2605..b6e2bc6e95 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -73,7 +73,6 @@ end). -record(newt,{type=unchanged,tag=unchanged,constraint=unchanged,inlined=no}). % used in check_type to update type and tag --record(newv,{type=unchanged,value=unchanged}). % used in check_value to update type and value check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> %%Predicates used to filter errors @@ -2071,171 +2070,72 @@ check_value(S,#valuedef{pos=Pos,name=Name,type=Type, NewType = Type#type{constraint=[Constr]}, {valueset, check_type(S,#typedef{pos=Pos,name=Name,typespec=NewType},NewType)}; -check_value(OldS=#state{recordtopname=TopName},V) when is_record(V,valuedef) -> - #valuedef{name=Name,checked=Checked,type=Vtype, - value=Value,module=ModName} = V, - ?dbg("check_value, V: ~p~n",[V]), - case Checked of - true -> +check_value(S, #valuedef{}=V) -> + ?dbg("check_value, V: ~p~n",[V0]), + case V of + #valuedef{checked=true} -> V; - {error,_} -> - V; - false -> - Def = Vtype#type.def, - Constr = Vtype#type.constraint, - S = OldS#state{type=Vtype,tname=Def,value=V,vname=Name}, - SVal = update_state(S,ModName), - NewDef = - case Def of - Ext when is_record(Ext,'Externaltypereference') -> - RecName = Ext#'Externaltypereference'.type, - {RefM,Type} = get_referenced_type(S,Ext), - %% If V isn't a value but an object Type is a #classdef{} - %%NewS = S#state{mname=RefM}, - NewS = update_state(S,RefM), - case Type of - #classdef{} -> - throw({objectdef}); - #typedef{} -> - case is_contextswitchtype(Type) of - true -> - #valuedef{value=CheckedVal}= - check_value(NewS,V#valuedef{type=Type#typedef.typespec}), - #newv{value=CheckedVal}; - _ -> - #valuedef{value=CheckedVal}= - check_value(NewS#state{recordtopname=[RecName|TopName]}, - V#valuedef{type=Type#typedef.typespec}), - #newv{value=CheckedVal} - end; - #type{} -> - %% A parameter that couldn't be categorized. - #valuedef{value=CheckedVal}= - check_value(NewS#state{recordtopname=[RecName|TopName]}, - V#valuedef{type=Type}), - #newv{value=CheckedVal} - end; - 'ANY' -> - case Value of - {opentypefieldvalue,ANYType,ANYValue} -> - CheckedV= - check_value(SVal,#valuedef{name=Name, - type=ANYType, - value=ANYValue, - module=ModName}), - #newv{value=CheckedV#valuedef.value}; - _ -> - throw({error,{asn1,{'cant check value of type',Def}}}) - end; - 'INTEGER' -> - ok=validate_integer(SVal,Value,[],Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - {'INTEGER',NamedNumberList} -> - ok=validate_integer(SVal,Value,NamedNumberList,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - {'BIT STRING',NamedNumberList} -> - ok=validate_bitstring(SVal,Value,NamedNumberList,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'NULL' -> - ok=validate_null(SVal,Value,Constr), - #newv{}; - 'OBJECT IDENTIFIER' -> - {ok,_}=validate_objectidentifier(SVal,Value,Constr), - #newv{value = normalize_value(SVal,Vtype,Value,[])}; - 'RELATIVE-OID' -> - {ok,_}=validate_relative_oid(SVal,Value,Constr), - #newv{value = Value}; - 'ObjectDescriptor' -> - ok=validate_objectdescriptor(SVal,Value,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'REAL' -> - ok = validate_real(SVal,Value,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - {'ENUMERATED',_} -> - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'BOOLEAN'-> - ok=validate_boolean(SVal,Value,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'OCTET STRING' -> - ok=validate_octetstring(SVal,Value,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'NumericString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - TString when TString =:= 'TeletexString'; - TString =:= 'T61String' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'VideotexString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'UTCTime' -> - #newv{value=normalize_value(SVal,Vtype,Value,[])}; -% exit({'cant check value of type' ,Def}); - 'GeneralizedTime' -> - #newv{value=normalize_value(SVal,Vtype,Value,[])}; -% exit({'cant check value of type' ,Def}); - 'GraphicString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'VisibleString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'GeneralString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'PrintableString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'IA5String' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'BMPString' -> - ok=validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'UTF8String' -> - ok = validate_restrictedstring(SVal,Vtype,Value,Constr), - %%io:format("Vtype: ~p~nValue: ~p~n",[Vtype,Value]); - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - 'UniversalString' -> %added 6/12 -00 - ok = validate_restrictedstring(SVal,Value,Def,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,[])}; - Seq when is_record(Seq,'SEQUENCE') -> - {ok,SeqVal} = validate_sequence(SVal,Value, - Seq#'SEQUENCE'.components, - Constr), - #newv{value=normalize_value(SVal,Vtype,SeqVal,TopName)}; - {'SEQUENCE OF',Components} -> - ok=validate_sequenceof(SVal,Value,Components,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,TopName)}; - {'CHOICE',Components} -> - ok=validate_choice(SVal,Value,Components,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,TopName)}; - Set when is_record(Set,'SET') -> - ok=validate_set(SVal,Value,Set#'SET'.components, - Constr), - #newv{value=normalize_value(SVal,Vtype,Value,TopName)}; - {'SET OF',Components} -> - ok=validate_setof(SVal,Value,Components,Constr), - #newv{value=normalize_value(SVal,Vtype,Value,TopName)}; - {'SelectionType',SelName,SelT} -> - CheckedT = check_selectiontype(SVal,SelName,SelT), - NewV = V#valuedef{type=CheckedT}, - SelVDef=check_value(S#state{value=NewV},NewV), - #newv{value=SelVDef#valuedef.value}; - Other -> - exit({'cannot check value of type' ,Other}) - end, - case NewDef#newv.value of - unchanged -> - V#valuedef{checked=true,value=Value}; - ok -> - V#valuedef{checked=true,value=Value}; - {error,Reason} -> - V#valuedef{checked={error,Reason},value=Value}; - _V -> - V#valuedef{checked=true,value=_V} - end + #valuedef{checked=false} -> + check_valuedef(S, V) + end. + +check_valuedef(#state{recordtopname=TopName}=S0, V0) -> + #valuedef{name=Name,type=Vtype,value=Value,module=ModName} = V0, + V = V0#valuedef{checked=true}, + Def = Vtype#type.def, + Constr = Vtype#type.constraint, + S1 = S0#state{type=Vtype,tname=Def,value=V0,vname=Name}, + SVal = update_state(S1, ModName), + case Def of + #'Externaltypereference'{type=RecName}=Ext -> + {RefM,Type} = get_referenced_type(S1, Ext), + %% If V isn't a value but an object Type is a #classdef{} + S2 = update_state(S1, RefM), + case Type of + #classdef{} -> + throw({objectdef}); + #typedef{typespec=TypeSpec} -> + S3 = case is_contextswitchtype(Type) of + true -> + S2; + false -> + S2#state{recordtopname=[RecName|TopName]} + end, + #valuedef{value=CheckedVal} = + check_value(S3, V0#valuedef{type=TypeSpec}), + V#valuedef{value=CheckedVal}; + #type{} -> + %% A parameter that couldn't be categorized. + #valuedef{value=CheckedVal} = + check_value(S2#state{recordtopname=[RecName|TopName]}, + V#valuedef{type=Type}), + V#valuedef{value=CheckedVal} + end; + 'ANY' -> + {opentypefieldvalue,ANYType,ANYValue} = Value, + CheckedV = check_value(SVal,#valuedef{name=Name, + type=ANYType, + value=ANYValue, + module=ModName}), + V#valuedef{value=CheckedV#valuedef.value}; + 'INTEGER' -> + ok = validate_integer(SVal, Value, [], Constr), + V#valuedef{value=normalize_value(SVal, Vtype, Value, [])}; + {'INTEGER',NamedNumberList} -> + ok = validate_integer(SVal, Value, NamedNumberList, Constr), + V#valuedef{value=normalize_value(SVal, Vtype, Value, [])}; + #'SEQUENCE'{components=Components} -> + {ok,SeqVal} = validate_sequence(SVal, Value, + Components, Constr), + V#valuedef{value=normalize_value(SVal, Vtype, + SeqVal, TopName)}; + {'SelectionType',SelName,SelT} -> + CheckedT = check_selectiontype(SVal, SelName, SelT), + NewV = V#valuedef{type=CheckedT}, + SelVDef = check_value(S1#state{value=NewV}, NewV), + V#valuedef{value=SelVDef#valuedef.value}; + _ -> + V#valuedef{value=normalize_value(SVal, Vtype, Value, TopName)} end. is_contextswitchtype(#typedef{name='EXTERNAL'})-> @@ -2291,12 +2191,6 @@ validate_integer_ref(S,Ref,NamedNumberList,Constr) -> check_integer_range(_Int, Constr) when is_list(Constr) -> ok. -validate_bitstring(_S,_Value,_NamedNumberList,_Constr) -> - ok. - -validate_null(_S,'NULL',_Constr) -> - ok. - %%------------ %% This can be removed when the old parser is removed %% The function removes 'space' atoms from the list @@ -2310,9 +2204,6 @@ is_space_list([],Acc) -> is_space_list([H|T],Acc) -> is_space_list(T,[H|Acc]). -validate_objectidentifier(S,ERef,C) -> - validate_objectidentifier(S,o_id,ERef,C). - validate_objectidentifier(S,OID,ERef,C) when is_record(ERef,'Externalvaluereference') -> validate_objectidentifier(S,OID,[ERef],C); @@ -2410,9 +2301,6 @@ validate_oid(_, S, OID, [Atom|Rest],Acc) when is_atom(Atom) -> validate_oid(_, S, OID, V, Acc) -> error({value, {"illegal "++to_string(OID),V,Acc},S}). -validate_relative_oid(S,Value,Constr) -> - validate_objectidentifier(S,rel_oid,Value,Constr). - is_object_id(OID,S,ERef=#'Externaltypereference'{}) -> {_,OI} = get_referenced_type(S,ERef), is_object_id(OID,S,OI#typedef.typespec); @@ -2499,26 +2387,6 @@ valid_objectid(o_id,_I,[1]) -> false; valid_objectid(o_id,_I,[2]) -> true; valid_objectid(_,_,_) -> true. - - - - - -validate_objectdescriptor(_S,_Value,_Constr) -> - ok. - -validate_real(_S,_Value,_Constr) -> - ok. - -validate_boolean(_S,_Value,_Constr) -> - ok. - -validate_octetstring(_S,_Value,_Constr) -> - ok. - -validate_restrictedstring(_S,_Value,_Def,_Constr) -> - ok. - validate_sequence(S=#state{type=Vtype},Value,_Components,_Constr) -> case Vtype of #type{tag=[{tag,'UNIVERSAL',8,'IMPLICIT',32}]} -> @@ -2533,18 +2401,6 @@ validate_sequence(S=#state{type=Vtype},Value,_Components,_Constr) -> {ok,Value} end. -validate_sequenceof(_S,_Value,_Components,_Constr) -> - ok. - -validate_choice(_S,_Value,_Components,_Constr) -> - ok. - -validate_set(_S,_Value,_Components,_Constr) -> - ok. - -validate_setof(_S,_Value,_Components,_Constr) -> - ok. - to_EXTERNAL1990(S,[{identification,{'CHOICE',{syntax,Stx}}}|Rest]) -> to_EXTERNAL1990(S,Rest,[{'direct-reference',Stx}]); to_EXTERNAL1990(S,[{identification,{'CHOICE',{'presentation-context-id',I}}}|Rest]) -> @@ -2796,12 +2652,12 @@ hstring_to_octetlist([H|T],BSL,Acc) when H >= $0; H =< $9 -> hstring_to_octetlist([],_,Acc) -> lists:reverse(Acc). -normalize_objectidentifier(S,Value) -> - {ok,Val}=validate_objectidentifier(S,Value,[]), +normalize_objectidentifier(S, Value) -> + {ok,Val} = validate_objectidentifier(S, o_id, Value, []), Val. normalize_relative_oid(S,Value) -> - {ok,Val} = validate_relative_oid(S,Value,[]), + {ok,Val} = validate_objectidentifier(S, rel_oid, Value, []), Val. normalize_objectdescriptor(Value) -> -- cgit v1.2.3 From 83fe4f33369c6c33f6241d279611dc2b91594298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 8 Apr 2013 10:16:37 +0200 Subject: Normalize data representation for table constraints The name of the referenced object set name in #simpletableattributes{} would when used by INSTANCE OF be an atom, but in all other cases be a {Module,ObjectSetName} tuple. Simplify the code by always using the latter format. --- lib/asn1/src/asn1ct_check.erl | 3 +- lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 56 ++++++-------------------- lib/asn1/src/asn1ct_constructed_per.erl | 41 +++++++------------ 3 files changed, 29 insertions(+), 71 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index b6e2bc6e95..0a95cbd3b5 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -5136,7 +5136,8 @@ instance_of_constraints(S, [{simpletable,Type}]) -> [{innermost, [#'Externalvaluereference'{module=ModuleName, value=type}]}]}], - TableCInf=#simpletableattributes{objectsetname=Name, + Mod = S#state.mname, + TableCInf=#simpletableattributes{objectsetname={Mod,Name}, c_name='type-id', c_index=1, usedclassfield=id, diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 9a55b139b9..b318cd1920 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -114,30 +114,16 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> usedclassfield=UniqueFieldName, uniqueclassfield=UniqueFieldName, valueindex=ValueIndex} -> %% N is index of attribute that determines constraint - OSDef = - case ObjectSetRef of - {Module,OSName} -> - asn1_db:dbget(Module,OSName); - OSName -> - asn1_db:dbget(get(currmod),OSName) - end, -% io:format("currmod: ~p~nOSName: ~p~nAttrN: ~p~nN: ~p~nUniqueFieldName: ~p~n", -% [get(currmod),OSName,AttrN,N,UniqueFieldName]), + {ObjSetMod,ObjSetName} = ObjectSetRef, + OSDef = asn1_db:dbget(ObjSetMod, ObjSetName), case (OSDef#typedef.typespec)#'ObjectSet'.gen of true -> ObjectEncode = asn1ct_gen:un_hyphen_var(lists:concat(['Obj', AttrN])), - {ObjSetMod,ObjSetName} = - case ObjectSetRef of - {M,O} -> - {{asis,M},O}; - _ -> - {"?MODULE",ObjectSetRef} - end, - emit([ObjectEncode," = ",nl]), - emit([" ",ObjSetMod,":'getenc_",ObjSetName,"'(",{asis,UniqueFieldName}, - ", ",nl]), + emit([ObjectEncode," = ",nl, + " ",{asis,ObjSetMod},":'getenc_",ObjSetName, + "'(",{asis,UniqueFieldName},", ",nl]), ValueMatch = value_match(ValueIndex, lists:concat(["Cindex",N])), emit([indent(35),ValueMatch,"),",nl]), @@ -257,15 +243,9 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) -> {[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} -> DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])), ValueMatch = value_match(ValueIndex,Term), - {ObjSetMod,ObjSetName} = - case ObjSetRef of - {M,O} -> - {{asis,M},O}; - _ -> - {"?MODULE",ObjSetRef} - end, + {ObjSetMod,ObjSetName} = ObjSetRef, emit([DecObj," =",nl, - " ",ObjSetMod,":'getdec_",ObjSetName,"'(", + " ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(", {asis,UniqueFName},", ",ValueMatch,"),",nl]), gen_dec_postponed_decs(DecObj,PostponedDecArgs) end, @@ -442,15 +422,9 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) -> {[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} -> DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])), ValueMatch = value_match(ValueIndex,Term), - {ObjSetMod,ObjSetName} = - case ObjSetRef of - {M,O} -> - {{asis,M},O}; - _ -> - {"?MODULE",ObjSetRef} - end, + {ObjSetMod,ObjSetName} = ObjSetRef, emit([DecObj," =",nl, - " ",ObjSetMod,":'getdec_",ObjSetName,"'(", + " ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(", {asis,UniqueFName},", ",ValueMatch,"),",nl]), gen_dec_postponed_decs(DecObj,PostponedDecArgs) end, @@ -1259,15 +1233,9 @@ gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand, {Cname,{_,OSet,UniqueFName,ValIndex}} -> Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)), ValueMatch = value_match(ValIndex,Term), - {ObjSetMod,ObjSetName} = - case OSet of - {M,O} -> - {{asis,M},O}; - _ -> - {"?MODULE",OSet} - end, - emit([",",nl,"ObjFun = ",ObjSetMod,":'getdec_",ObjSetName,"'(", - {asis,UniqueFName},", ",ValueMatch,")"]); + {ObjSetMod,ObjSetName} = OSet, + emit([",",nl,"ObjFun = ",{asis,ObjSetMod},":'getdec_",ObjSetName, + "'(",{asis,UniqueFName},", ",ValueMatch,")"]); _ -> ok end, diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 1edacd266a..9405d052db 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -143,19 +143,15 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) -> uniqueclassfield=UniqueFieldName, valueindex=ValueIndex } -> %% N is index of attribute that determines constraint - {{ObjSetMod,ObjSetName},OSDef} = - case ObjectSet of - {Module,OSName} -> - {{{asis,Module},OSName},asn1_db:dbget(Module,OSName)}; - OSName -> - {{"?MODULE",OSName},asn1_db:dbget(get(currmod),OSName)} - end, - case (OSDef#typedef.typespec)#'ObjectSet'.gen of + {Module,ObjSetName} = ObjectSet, + #typedef{typespec=#'ObjectSet'{gen=Gen}} = + asn1_db:dbget(Module, ObjSetName), + case Gen of true -> ObjectEncode = asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])), - emit([ObjectEncode," = ",nl]), - emit([" ",ObjSetMod,":'getenc_",ObjSetName,"'(", + emit([ObjectEncode," =",nl, + " ",{asis,Module},":'getenc_",ObjSetName,"'(", {asis,UniqueFieldName},", ",nl]), El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))), @@ -176,7 +172,7 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) -> notice_value_match() end, {AttrN,ObjectEncode}; - _ -> + false -> false end; _ -> @@ -364,14 +360,10 @@ gen_dec_constructed_imm_2(Typename, CompList, {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} -> DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])), ValueMatch = value_match(ValueIndex,Term), - {ObjSetMod,ObjSetName} = - case ObjSet of - {M,O} -> {{asis,M},O}; - _ -> {"?MODULE",ObjSet} - end, - emit({DecObj," =",nl," ",ObjSetMod,":'getdec_",ObjSetName,"'(", -% {asis,UniqueFName},", ",Term,"),",nl}), - {asis,UniqueFName},", ",ValueMatch,"),",nl}), + {ObjSetMod,ObjSetName} = ObjSet, + emit([DecObj," =",nl, + " ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(", + {asis,UniqueFName},", ",ValueMatch,"),",nl]), gen_dec_listofopentypes(DecObj,ListOfOpenTypes,false) end, %% we don't return named lists any more Cnames = mkcnamelist(CompList), @@ -1469,14 +1461,11 @@ gen_dec_line_dec_inf(Comp, DecInfObj) -> {Cname,{_,OSet,UniqueFName,ValIndex}} -> Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)), ValueMatch = value_match(ValIndex,Term), - {ObjSetMod,ObjSetName} = - case OSet of - {M,O} -> {{asis,M},O}; - _ -> {"?MODULE",OSet} - end, - emit({",",nl,"ObjFun = ",ObjSetMod, + {ObjSetMod,ObjSetName} = OSet, + emit([",",nl, + "ObjFun = ",{asis,ObjSetMod}, ":'getdec_",ObjSetName,"'(", - {asis,UniqueFName},", ",ValueMatch,")"}); + {asis,UniqueFName},", ",ValueMatch,")"]); _ -> ok end. -- cgit v1.2.3 From 4001ac2a291e26d9fa912dbeefbe92278aceb345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 9 Apr 2013 07:23:40 +0200 Subject: PER: Generate code for deep table constraints at compile-time For the PER backends, generate code for accessing deep table constraints at compile-time in the same way as is done for BER. While at it, remove the complicated indentation code. Also modernize the test suite and add a test for a deeper nested constraint. --- lib/asn1/src/asn1ct_constructed_per.erl | 27 ++------------ lib/asn1/src/asn1ct_gen.erl | 19 +--------- lib/asn1/test/asn1_SUITE_data/TConstr.asn1 | 6 +++ lib/asn1/test/testDeepTConstr.erl | 59 ++++++++++++------------------ 4 files changed, 34 insertions(+), 77 deletions(-) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 9405d052db..dd5737dd8c 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -150,27 +150,12 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) -> true -> ObjectEncode = asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])), + El = make_element(N+1, asn1ct_gen:mk_var(asn1ct_name:curr(val))), + ValueMatch = value_match(ValueIndex, El), emit([ObjectEncode," =",nl, " ",{asis,Module},":'getenc_",ObjSetName,"'(", - {asis,UniqueFieldName},", ",nl]), - El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))), - - Length = fun(X,_LFun) when is_atom(X) -> - length(atom_to_list(X)); - (X,_LFun) when is_list(X) -> - length(X); - ({X1,X2},LFun) -> - LFun(X1,LFun) + LFun(X2,LFun) - end, - Indent = 12 + Length(ObjectSet,Length), - case ValueIndex of - [] -> - emit([indent(Indent),El,"),",nl]); - _ -> - emit([indent(Indent),"value_match(", - {asis,ValueIndex},",",El,")),",nl]), - notice_value_match() - end, + {asis,UniqueFieldName},", ",nl, + " ",ValueMatch,"),",nl]), {AttrN,ObjectEncode}; false -> false @@ -1793,9 +1778,5 @@ value_match1(Value,[],Acc,Depth) -> value_match1(Value,[{VI,_}|VIs],Acc,Depth) -> value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1). -notice_value_match() -> - Module = get(currmod), - put(value_match,{true,Module}). - is_optimized(per) -> true; is_optimized(uper) -> false. diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 978ac280dd..9095e145a3 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -108,8 +108,7 @@ pgen_values(Erules,Module,[H|T]) -> gen_value(Valuedef), pgen_values(Erules,Module,T). -pgen_types(_,_,_,Module,[]) -> - gen_value_match(Module), +pgen_types(_, _, _, _, []) -> true; pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) -> asn1ct_name:clear(), @@ -573,22 +572,6 @@ gen_types(Erules,Tname,Type) when is_record(Type,type) -> asn1ct_name:clear(), Rtmod:gen_decode(Erules,Tname,Type). -gen_value_match(Module) -> - case get(value_match) of - {true,Module} -> - emit(["value_match([{Index,Cname}|Rest],Value) ->",nl, - " Value2 =",nl, - " case element(Index,Value) of",nl, - " {Cname,Val2} -> Val2;",nl, - " X -> X",nl, - " end,",nl, - " value_match(Rest,Value2);",nl, - "value_match([],Value) ->",nl, - " Value.",nl]); - _ -> ok - end, - put(value_match,undefined). - gen_check_defaultval(Erules,Module,[{Name,Type}|Rest]) -> gen_check_func(Name,Type), gen_check_defaultval(Erules,Module,Rest); diff --git a/lib/asn1/test/asn1_SUITE_data/TConstr.asn1 b/lib/asn1/test/asn1_SUITE_data/TConstr.asn1 index 63f5dbde77..e2e0a11dc4 100644 --- a/lib/asn1/test/asn1_SUITE_data/TConstr.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/TConstr.asn1 @@ -51,6 +51,12 @@ Seq2 ::= SEQUENCE { } } +Deeper ::= SEQUENCE { + a SEQUENCE {aa INTEGER, + s SEQUENCE { ab MYCLASS.&id ({ObjectSet}), + ac INTEGER }}, + b SEQUENCE {ba INTEGER, bb MYCLASS.&Type ({ObjectSet}{@a.s.ab})} +} -- following from Peter's definitions diff --git a/lib/asn1/test/testDeepTConstr.erl b/lib/asn1/test/testDeepTConstr.erl index 3df7bcbaa0..e826cafa0c 100644 --- a/lib/asn1/test/testDeepTConstr.erl +++ b/lib/asn1/test/testDeepTConstr.erl @@ -40,53 +40,40 @@ main(_Erule) -> {any,"DK"}, {final,"NO"}]}}, - ?line {ok,Bytes1} = - asn1_wrapper:encode('TConstrChoice','FilterItem',Val1), - - ?line {error,Reason} = asn1_wrapper:decode('TConstrChoice','FilterItem',Bytes1), - + {ok,Bytes1} = 'TConstrChoice':encode('FilterItem', Val1), + {error,Reason} = asn1_wrapper:decode('TConstrChoice','FilterItem',Bytes1), io:format("Reason: ~p~n~n",[Reason]), - - ?line {ok,Bytes2} = - asn1_wrapper:encode('TConstrChoice','FilterItem',Val2), - - ?line {ok,Res} = asn1_wrapper:decode('TConstrChoice','FilterItem',Bytes2), - - + {ok,Bytes2} = 'TConstrChoice':encode('FilterItem', Val2), + {ok,Res} = 'TConstrChoice':decode('FilterItem', Bytes2), %% test of OTP-4248. - ?line {ok,Bytes3} = - asn1_wrapper:encode('TConstrChoice','Seq',{'Seq',3,Bytes2}), - - ?line {ok,{'Seq',3,Bytes4}} = - asn1_wrapper:decode('TConstrChoice','Seq',Bytes3), - - ?line {ok,Res} = asn1_wrapper:decode('TConstrChoice','FilterItem',Bytes4), + {ok,Bytes3} = 'TConstrChoice':encode('Seq', {'Seq',3,Bytes2}), + {ok,{'Seq',3,Bytes4}} = 'TConstrChoice':decode('Seq', Bytes3), + {ok,Res} = 'TConstrChoice':decode('FilterItem', Bytes4), %% test of TConstr - Seq1Val = {'Seq1',{'Seq1_a',12,{2,4}},{'Seq1_b',13,{'Type-object1',14,true}}}, - ?line {ok,Bytes5} = - asn1_wrapper:encode('TConstr','Seq1',Seq1Val), - - ?line {ok,Seq1Val} = - asn1_wrapper:decode('TConstr','Seq1',Bytes5), + Seq1Val = {'Seq1',{'Seq1_a',12,{2,4}}, + {'Seq1_b',13,{'Type-object1',14,true}}}, + roundtrip('TConstr', 'Seq1', Seq1Val), Seq2Val = {'Seq2',123,{'Seq2_content',{2,6,7}, {first,{'Type-object3_first',false,47}}, false}}, - - ?line {ok,Bytes6} = - asn1_wrapper:encode('TConstr','Seq2',Seq2Val), + roundtrip('TConstr', 'Seq2', Seq2Val), - ?line {ok,Seq2Val} = - asn1_wrapper:decode('TConstr','Seq2',Bytes6), + roundtrip('TConstr', 'Info', {'Info',{'Info_xyz',{1,2}},1234}), + + roundtrip('TConstr', 'Deeper', + {'Deeper', + {'Deeper_a',12, + {'Deeper_a_s',{2,4},42}}, + {'Deeper_b',13,{'Type-object1',14,true}}}), + ok. - InfoVal = {'Info',{'Info_xyz',{1,2}},1234}, - - ?line {ok,Bytes7} = - asn1_wrapper:encode('TConstr','Info',InfoVal), - ?line {ok,InfoVal} = - asn1_wrapper:decode('TConstr','Info',Bytes7). +roundtrip(M, T, V) -> + {ok,E} = M:encode(T, V), + {ok,V} = M:decode(T, E), + ok. -- cgit v1.2.3 From e3d18eb865c7bcba21836837b1c4c415e7c857e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 10 Apr 2013 07:53:37 +0200 Subject: BER: Fix handling of a constructed default value for a class --- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 4 ++-- lib/asn1/test/asn1_SUITE_data/InfObj.asn | 24 ++++++++++++++++++++++++ lib/asn1/test/testInfObj.erl | 9 ++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 52d8210321..96100b1b2f 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -799,8 +799,8 @@ gen_encode_default_call(ClassName,FieldName,Type) -> Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag], case asn1ct_gen:type(InnerType) of {constructed,bif} -> -%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type); - emit([" 'enc_",ClassName,'_',FieldName,"'(Bytes)"]), + emit([" 'enc_",ClassName,'_',FieldName,"'", + "(Val, ",{asis,Tag},")"]), [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])), typespec=Type}]; {primitive,bif} -> diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj.asn b/lib/asn1/test/asn1_SUITE_data/InfObj.asn index ff11b36788..61d9d23ea2 100644 --- a/lib/asn1/test/asn1_SUITE_data/InfObj.asn +++ b/lib/asn1/test/asn1_SUITE_data/InfObj.asn @@ -180,6 +180,30 @@ MyPdu ::= SEQUENCE { str MY-CLASS.&stringValue ({MyObjectSet}{@int}) } +-- +-- Class with constructed default +-- + +CONSTRUCTED-DEFAULT ::= CLASS { + &id INTEGER UNIQUE, + &Type DEFAULT SEQUENCE { a INTEGER, b BOOLEAN }, + &ok BOOLEAN DEFAULT TRUE +} + +constructed1 CONSTRUCTED-DEFAULT ::= { &id 1 } +constructed2 CONSTRUCTED-DEFAULT ::= { &id 2, &ok false } + +ConstructedDefaultSet CONSTRUCTED-DEFAULT ::= { + constructed1 | + constructed2 | + { &id 3, &Type BOOLEAN } +} + +ConstructedPdu ::= SEQUENCE { + id CONSTRUCTED-DEFAULT.&id ({ConstructedDefaultSet}), + content CONSTRUCTED-DEFAULT.&Type ({ConstructedDefaultSet}{@id}) +} + END diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index c0f86eab60..d9890e4358 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -51,7 +51,14 @@ main(_Erule) -> roundtrip('InfObj', 'MyPdu', {'MyPdu',42,12,false,"string"}), roundtrip('InfObj', 'MyPdu', {'MyPdu',{'Seq',1023,"hello"}, 42,true,"longer string"}), - roundtrip('InfObj', 'MyPdu', {'MyPdu',"75712346",43,true,"string"}). + roundtrip('InfObj', 'MyPdu', {'MyPdu',"75712346",43,true,"string"}), + + roundtrip('InfObj', 'ConstructedPdu', + {'ConstructedPdu',1,{'CONSTRUCTED-DEFAULT_Type',-2001,true}}), + roundtrip('InfObj', 'ConstructedPdu', + {'ConstructedPdu',2,{'CONSTRUCTED-DEFAULT_Type',999,false}}), + roundtrip('InfObj', 'ConstructedPdu', + {'ConstructedPdu',3,true}). roundtrip(M, T, V) -> -- cgit v1.2.3 From e0a1be73c126998b37ca42bbdc1da3d40bef3723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 16 Apr 2013 08:47:54 +0200 Subject: testPrim: Simplify test cases using a roundtrip function While at it, also test more integer values. --- lib/asn1/test/asn1_SUITE_data/Prim.asn1 | 12 +- lib/asn1/test/testPrim.erl | 569 +++----------------------------- 2 files changed, 61 insertions(+), 520 deletions(-) diff --git a/lib/asn1/test/asn1_SUITE_data/Prim.asn1 b/lib/asn1/test/asn1_SUITE_data/Prim.asn1 index c3d54dbbb3..cc0e61422a 100644 --- a/lib/asn1/test/asn1_SUITE_data/Prim.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/Prim.asn1 @@ -47,5 +47,15 @@ BEGIN b Bool, i4 INTEGER (0..63) } - + + ASeq ::= SEQUENCE { + e254 BOOLEAN, + i254 INTEGER (0..254), + e255 BOOLEAN, + i255 INTEGER (0..255), + e256 BOOLEAN, + i256 INTEGER (0..256), + e BOOLEAN, + magic INTEGER + } END diff --git a/lib/asn1/test/testPrim.erl b/lib/asn1/test/testPrim.erl index 91fb9fffca..990e7adcd9 100644 --- a/lib/asn1/test/testPrim.erl +++ b/lib/asn1/test/testPrim.erl @@ -30,465 +30,57 @@ -include_lib("test_server/include/test_server.hrl"). bool(Rules) -> - - %%========================================================== - %% Bool ::= BOOLEAN - %%========================================================== - - ?line {ok,Bytes1} = asn1_wrapper:encode('Prim','Bool',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','Bool',lists:flatten(Bytes1)), - - ?line {ok,Bytes2} = asn1_wrapper:encode('Prim','Bool',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','Bool',lists:flatten(Bytes2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','Bool',517)), - ok; - per -> - ok - end, - - - - - - %%========================================================== - %% BoolCon ::= [20] BOOLEAN - %%========================================================== - - - ?line {ok,BytesCon1} = asn1_wrapper:encode('Prim','BoolCon',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','BoolCon',lists:flatten(BytesCon1)), - - ?line {ok,BytesCon2} = asn1_wrapper:encode('Prim','BoolCon',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','BoolCon',lists:flatten(BytesCon2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','BoolCon',517)), - ok; - per -> - ok - end, - - - - - - %%========================================================== - %% BoolPri ::= [PRIVATE 21] BOOLEAN - %%========================================================== - - ?line {ok,BytesPri1} = asn1_wrapper:encode('Prim','BoolPri',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','BoolPri',lists:flatten(BytesPri1)), - - ?line {ok,BytesPri2} = asn1_wrapper:encode('Prim','BoolPri',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','BoolPri',lists:flatten(BytesPri2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','BoolPri',517)), - ok; - per -> - ok - end, - - - %%========================================================== - %% BoolApp ::= [APPLICATION 22] BOOLEAN - %%========================================================== - - ?line {ok,BytesApp1} = asn1_wrapper:encode('Prim','BoolApp',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','BoolApp',lists:flatten(BytesApp1)), - - ?line {ok,BytesApp2} = asn1_wrapper:encode('Prim','BoolApp',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','BoolApp',lists:flatten(BytesApp2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','BoolApp',517)), - ok; - per -> - ok - end, - - - %%========================================================== - %% BoolExpCon ::= [30] EXPLICIT BOOLEAN - %%========================================================== - - ?line {ok,BytesExpCon1} = asn1_wrapper:encode('Prim','BoolExpCon',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','BoolExpCon',lists:flatten(BytesExpCon1)), - - ?line {ok,BytesExpCon2} = asn1_wrapper:encode('Prim','BoolExpCon',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','BoolExpCon',lists:flatten(BytesExpCon2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','BoolExpCon',517)), - ok; - per -> - ok - end, - - - - %%========================================================== - %% BoolExpPri ::= [PRIVATE 31] EXPLICIT BOOLEAN - %%========================================================== - - ?line {ok,BytesExpPri1} = asn1_wrapper:encode('Prim','BoolExpPri',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','BoolExpPri',lists:flatten(BytesExpPri1)), - - ?line {ok,BytesExpPri2} = asn1_wrapper:encode('Prim','BoolExpPri',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','BoolExpPri',lists:flatten(BytesExpPri2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','BoolExpPri',517)), - ok; - per -> - ok - end, - - - %%========================================================== - %% BoolExpApp ::= [APPLICATION 32] EXPLICIT BOOLEAN - %%========================================================== - - ?line {ok,BytesExpApp1} = asn1_wrapper:encode('Prim','BoolExpApp',true), - ?line {ok,true} = asn1_wrapper:decode('Prim','BoolExpApp',lists:flatten(BytesExpApp1)), - - ?line {ok,BytesExpApp2} = asn1_wrapper:encode('Prim','BoolExpApp',false), - ?line {ok,false} = asn1_wrapper:decode('Prim','BoolExpApp',lists:flatten(BytesExpApp2)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{encode_boolean,517}}} = - (catch asn1_wrapper:encode('Prim','BoolExpApp',517)), - ok; - per -> - ok - end, - - ok. + Types = ['Bool','BoolCon','BoolPri','BoolApp', + 'BoolExpCon','BoolExpPri','BoolExpApp'], + [roundtrip(T, V) || T <- Types, V <- [true,false]], + case Rules of + ber -> + [begin + {error,{asn1,{encode_boolean,517}}} = + (catch 'Prim':encode(T, 517)) + end || T <- Types], + ok; + _ -> + ok + end. int(Rules) -> - - - %%========================================================== - %% Int ::= INTEGER - %%========================================================== - - %% test of OTP-2666 encoding should use minimum number of octets x.690 8.3.2 - ?line {ok,Bytes0} = asn1_wrapper:encode('Prim','Int',-128), - ?line L0 = lists:flatten(Bytes0), - ?line {ok,-128} = asn1_wrapper:decode('Prim','Int',lists:flatten(L0)), - case asn1_wrapper:erule(Rules) of + %% OTP-2666: encoding should use minimum number of octets; x.690 8.3.2 + Bytes0 = roundtrip('Int', -128), + case Rules of ber -> - ?line [_,1,128] = L0; - per -> ok + <<_,1,128>> = Bytes0; + _ -> + ok end, - ?line {ok,Bytes1} = asn1_wrapper:encode('Prim','Int',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes1)), - - ?line {ok,Bytes2} = asn1_wrapper:encode('Prim','Int',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes2)), - - ?line {ok,Bytes3} = asn1_wrapper:encode('Prim','Int',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes3)), - - ?line {ok,Bytes4} = asn1_wrapper:encode('Prim','Int',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes4)), - - ?line {ok,Bytes5} = asn1_wrapper:encode('Prim','Int',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes5)), - - ?line {ok,Bytes6} = asn1_wrapper:encode('Prim','Int',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes6)), - - ?line {ok,Bytes7} = asn1_wrapper:encode('Prim','Int',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes7)), - - ?line {ok,Bytes8} = asn1_wrapper:encode('Prim','Int',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes8)), - - ?line {ok,Bytes9} = asn1_wrapper:encode('Prim','Int',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes9)), - - ?line {ok,Bytes10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(Bytes10)), - - - - - - %%========================================================== - %% IntCon ::= [40] INTEGER - %%========================================================== - - ?line {ok,BytesCon1} = asn1_wrapper:encode('Prim','IntCon',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon1)), - - ?line {ok,BytesCon2} = asn1_wrapper:encode('Prim','IntCon',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon2)), - - ?line {ok,BytesCon3} = asn1_wrapper:encode('Prim','IntCon',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon3)), - - ?line {ok,BytesCon4} = asn1_wrapper:encode('Prim','IntCon',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon4)), - - ?line {ok,BytesCon5} = asn1_wrapper:encode('Prim','IntCon',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon5)), - - ?line {ok,BytesCon6} = asn1_wrapper:encode('Prim','IntCon',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon6)), - - ?line {ok,BytesCon7} = asn1_wrapper:encode('Prim','IntCon',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon7)), - - ?line {ok,BytesCon8} = asn1_wrapper:encode('Prim','IntCon',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon8)), - - ?line {ok,BytesCon9} = asn1_wrapper:encode('Prim','IntCon',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntCon',lists:flatten(BytesCon9)), - - ?line {ok,BytesCon10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesCon10)), - - - - %%========================================================== - %% IntPri ::= [PRIVATE 41] INTEGER - %%========================================================== - - ?line {ok,BytesPri1} = asn1_wrapper:encode('Prim','IntPri',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri1)), - - ?line {ok,BytesPri2} = asn1_wrapper:encode('Prim','IntPri',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri2)), - - ?line {ok,BytesPri3} = asn1_wrapper:encode('Prim','IntPri',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri3)), - - ?line {ok,BytesPri4} = asn1_wrapper:encode('Prim','IntPri',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri4)), - - ?line {ok,BytesPri5} = asn1_wrapper:encode('Prim','IntPri',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri5)), - - ?line {ok,BytesPri6} = asn1_wrapper:encode('Prim','IntPri',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri6)), - - ?line {ok,BytesPri7} = asn1_wrapper:encode('Prim','IntPri',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri7)), - - ?line {ok,BytesPri8} = asn1_wrapper:encode('Prim','IntPri',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri8)), - - ?line {ok,BytesPri9} = asn1_wrapper:encode('Prim','IntPri',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntPri',lists:flatten(BytesPri9)), - - ?line {ok,BytesPri10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesPri10)), - - - - %%========================================================== - %% IntApp ::= [APPLICATION 42] INTEGER - %%========================================================== - - ?line {ok,BytesApp1} = asn1_wrapper:encode('Prim','IntApp',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp1)), - - ?line {ok,BytesApp2} = asn1_wrapper:encode('Prim','IntApp',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp2)), - - ?line {ok,BytesApp3} = asn1_wrapper:encode('Prim','IntApp',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp3)), - - ?line {ok,BytesApp4} = asn1_wrapper:encode('Prim','IntApp',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp4)), - - ?line {ok,BytesApp5} = asn1_wrapper:encode('Prim','IntApp',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp5)), - - ?line {ok,BytesApp6} = asn1_wrapper:encode('Prim','IntApp',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp6)), - - ?line {ok,BytesApp7} = asn1_wrapper:encode('Prim','IntApp',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp7)), - - ?line {ok,BytesApp8} = asn1_wrapper:encode('Prim','IntApp',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp8)), - - ?line {ok,BytesApp9} = asn1_wrapper:encode('Prim','IntApp',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntApp',lists:flatten(BytesApp9)), - - ?line {ok,BytesApp10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesApp10)), - - - %%========================================================== - %% IntExpCon ::= [50] EXPLICIT INTEGER - %%========================================================== - - ?line {ok,BytesExpCon1} = asn1_wrapper:encode('Prim','IntExpCon',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon1)), - - ?line {ok,BytesExpCon2} = asn1_wrapper:encode('Prim','IntExpCon',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon2)), - - ?line {ok,BytesExpCon3} = asn1_wrapper:encode('Prim','IntExpCon',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon3)), - - ?line {ok,BytesExpCon4} = asn1_wrapper:encode('Prim','IntExpCon',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon4)), - - ?line {ok,BytesExpCon5} = asn1_wrapper:encode('Prim','IntExpCon',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon5)), - - ?line {ok,BytesExpCon6} = asn1_wrapper:encode('Prim','IntExpCon',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon6)), - - ?line {ok,BytesExpCon7} = asn1_wrapper:encode('Prim','IntExpCon',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon7)), - - ?line {ok,BytesExpCon8} = asn1_wrapper:encode('Prim','IntExpCon',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon8)), - - ?line {ok,BytesExpCon9} = asn1_wrapper:encode('Prim','IntExpCon',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntExpCon',lists:flatten(BytesExpCon9)), - - ?line {ok,BytesExpCon10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesExpCon10)), - - %%========================================================== - %% IntExpPri ::= [PRIVATE 51] EXPLICIT INTEGER - %%========================================================== - - ?line {ok,BytesExpPri1} = asn1_wrapper:encode('Prim','IntExpPri',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri1)), - - ?line {ok,BytesExpPri2} = asn1_wrapper:encode('Prim','IntExpPri',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri2)), - - ?line {ok,BytesExpPri3} = asn1_wrapper:encode('Prim','IntExpPri',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri3)), - - ?line {ok,BytesExpPri4} = asn1_wrapper:encode('Prim','IntExpPri',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri4)), - - ?line {ok,BytesExpPri5} = asn1_wrapper:encode('Prim','IntExpPri',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri5)), - - ?line {ok,BytesExpPri6} = asn1_wrapper:encode('Prim','IntExpPri',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri6)), - - ?line {ok,BytesExpPri7} = asn1_wrapper:encode('Prim','IntExpPri',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri7)), - - ?line {ok,BytesExpPri8} = asn1_wrapper:encode('Prim','IntExpPri',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri8)), - - ?line {ok,BytesExpPri9} = asn1_wrapper:encode('Prim','IntExpPri',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntExpPri',lists:flatten(BytesExpPri9)), - - ?line {ok,BytesExpPri10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesExpPri10)), - - %%========================================================== - %% IntExpApp ::= [APPLICATION 52] EXPLICIT INTEGER - %%========================================================== - - ?line {ok,BytesExpApp1} = asn1_wrapper:encode('Prim','IntExpApp',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp1)), - - ?line {ok,BytesExpApp2} = asn1_wrapper:encode('Prim','IntExpApp',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp2)), - - ?line {ok,BytesExpApp3} = asn1_wrapper:encode('Prim','IntExpApp',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp3)), - - ?line {ok,BytesExpApp4} = asn1_wrapper:encode('Prim','IntExpApp',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp4)), - - ?line {ok,BytesExpApp5} = asn1_wrapper:encode('Prim','IntExpApp',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp5)), - - ?line {ok,BytesExpApp6} = asn1_wrapper:encode('Prim','IntExpApp',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp6)), - - ?line {ok,BytesExpApp7} = asn1_wrapper:encode('Prim','IntExpApp',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp7)), - - ?line {ok,BytesExpApp8} = asn1_wrapper:encode('Prim','IntExpApp',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp8)), - - ?line {ok,BytesExpApp9} = asn1_wrapper:encode('Prim','IntExpApp',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntExpApp',lists:flatten(BytesExpApp9)), - - ?line {ok,BytesExpApp10} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesExpApp10)), - + Values = [0,2,3,4,127,128,254,255,256,257,444, + 16383,16384,16385,65534,65535,65536,65537, + 123456789,12345678901234567890, + -1,-2,-3,-4,-100,-127,-255,-256,-257, + -1234567890,-2147483648], + [roundtrip(T, V) || + T <- ['Int','IntCon','IntPri','IntApp', + 'IntExpCon','IntExpPri','IntExpApp'], + V <- [1|Values]], %%========================================================== %% IntEnum ::= INTEGER {first(1),last(31)} %%========================================================== - ?line {ok,BytesEnum1} = asn1_wrapper:encode('Prim','IntEnum',4), - ?line {ok,4} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum1)), - - ?line {ok,BytesEnum2} = asn1_wrapper:encode('Prim','IntEnum',444), - ?line {ok,444} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum2)), - - ?line {ok,BytesEnum3} = asn1_wrapper:encode('Prim','IntEnum',123456789), - ?line {ok,123456789} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum3)), - - ?line {ok,BytesEnum4} = asn1_wrapper:encode('Prim','IntEnum',12345678901234567890), - ?line {ok,12345678901234567890} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum4)), + [roundtrip('IntEnum', V) || V <- Values], - ?line {ok,BytesEnum5} = asn1_wrapper:encode('Prim','IntEnum',-100), - ?line {ok,-100} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum5)), - - ?line {ok,BytesEnum6} = asn1_wrapper:encode('Prim','IntEnum',-255), - ?line {ok,-255} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum6)), - - ?line {ok,BytesEnum7} = asn1_wrapper:encode('Prim','IntEnum',-256), - ?line {ok,-256} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum7)), - - ?line {ok,BytesEnum8} = asn1_wrapper:encode('Prim','IntEnum',-257), - ?line {ok,-257} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum8)), - - ?line {ok,BytesEnum9} = asn1_wrapper:encode('Prim','IntEnum',-1234567890), - ?line {ok,-1234567890} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum9)), - - ?line {ok,BytesEnum910} = asn1_wrapper:encode('Prim','Int',-2147483648), - ?line {ok,-2147483648} = asn1_wrapper:decode('Prim','Int',lists:flatten(BytesEnum910)), - - - ?line {ok,BytesEnum10} = asn1_wrapper:encode('Prim','IntEnum',first), - ?line {ok,first} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum10)), - - ?line {ok,BytesEnum11} = asn1_wrapper:encode('Prim','IntEnum',last), - ?line {ok,last} = asn1_wrapper:decode('Prim','IntEnum',lists:flatten(BytesEnum11)), - + roundtrip('IntEnum', first), + roundtrip('IntEnum', last), + roundtrip('ASeq', {'ASeq',true,254,false,255,true,256,true,68789}), + roundtrip('ASeq', {'ASeq',false,250,true,200,true,199,true,77788}), + roundtrip('ASeq', {'ASeq',true,0,false,0,true,0,true,68789}), ok. - enum(Rules) -> %%========================================================== @@ -496,23 +88,9 @@ enum(Rules) -> %% friday(5),saturday(6),sunday(7)} %%========================================================== - ?line {ok,BytesEnum1} = asn1_wrapper:encode('Prim','Enum',monday), - ?line {ok,monday} = asn1_wrapper:decode('Prim','Enum',lists:flatten(BytesEnum1)), - - ?line {ok,BytesEnum2} = asn1_wrapper:encode('Prim','Enum',thursday), - ?line {ok,thursday} = asn1_wrapper:decode('Prim','Enum',lists:flatten(BytesEnum2)), - - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {error,{asn1,{_,4}}} = - case catch asn1_wrapper:encode('Prim','Enum',4) of Enum -> Enum end, - ok; - per -> - ?line {error,{asn1,{_,4}}} = - case catch asn1_wrapper:encode('Prim','Enum',4) of Enum -> Enum end, - ok - end, + roundtrip('Enum', monday), + roundtrip('Enum', thursday), + {error,{asn1,{_,4}}} = (catch 'Prim':encode('Enum', 4)), case Rules of Per when Per =:= per; Per =:= uper -> @@ -524,88 +102,41 @@ enum(Rules) -> ok. - -obj_id(Rules) -> +obj_id(_) -> %%========================================================== %% ObjId ::= OBJECT IDENTIFIER %%========================================================== - ?line {ok,Bytes1} = asn1_wrapper:encode('Prim','ObjId',{0,22,3}), - ?line {ok,{0,22,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes1)), - - ?line {ok,Bytes2} = asn1_wrapper:encode('Prim','ObjId',{1,39,3}), - ?line {ok,{1,39,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes2)), - - ?line {ok,Bytes3} = asn1_wrapper:encode('Prim','ObjId',{2,100,3}), - ?line {ok,{2,100,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes3)), - - ?line {ok,Bytes4} = asn1_wrapper:encode('Prim','ObjId',{2,16303,3}), - ?line {ok,{2,16303,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes4)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,Bytes5} = asn1_wrapper:encode('Prim','ObjId',{2,16304,3}), - ?line {ok,{2,16304,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes5)), - ok; - per -> - ?line {ok,Bytes5} = asn1_wrapper:encode('Prim','ObjId',{2,16304,3}), - ?line {ok,{2,16304,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes5)), -%% ?line test_server:format("~p~n",[Kurt]), -% ?line {ok,{2,16304,3}} = asn1_wrapper:decode('Prim','ObjId',lists:flatten(Bytes5)), - ok - end, - - - + [roundtrip('ObjId', V) || + V <- [{0,22,3},{1,39,3},{2,100,3},{2,16303,3},{2,16304,3}]], ok. rel_oid(_Rules) -> - %%========================================================== %% RelOid ::= RELATIVE-OID %%========================================================== - ?line {ok,Bytes1} = asn1_wrapper:encode('Prim','RelOid',{0,22,3}), - ?line {ok,{0,22,3}} = asn1_wrapper:decode('Prim','RelOid',lists:flatten(Bytes1)), - - ?line {ok,Bytes2} = asn1_wrapper:encode('Prim','RelOid',{1,39,3}), - ?line {ok,{1,39,3}} = asn1_wrapper:decode('Prim','RelOid',lists:flatten(Bytes2)), - - ?line {ok,Bytes3} = asn1_wrapper:encode('Prim','RelOid',{2,100,3}), - ?line {ok,{2,100,3}} = asn1_wrapper:decode('Prim','RelOid',lists:flatten(Bytes3)), - - ?line {ok,Bytes4} = asn1_wrapper:encode('Prim','RelOid',{2,16303,3}), - ?line {ok,{2,16303,3}} = asn1_wrapper:decode('Prim','RelOid',lists:flatten(Bytes4)), - - ?line {ok,Bytes5} = asn1_wrapper:encode('Prim','RelOid',{2,16304,3}), - ?line {ok,{2,16304,3}} = asn1_wrapper:decode('Prim','RelOid',lists:flatten(Bytes5)), - - ?line {ok,Bytes6} = asn1_wrapper:encode('Prim','RelOid',{8,16304,16#ffff}), - ?line {ok,{8,16304,16#ffff}} = asn1_wrapper:decode('Prim','RelOid',lists:flatten(Bytes6)), - - - + [roundtrip('RelOid', V) || + V <- [{0,22,3},{1,39,3},{2,100,3},{2,16303,3}, + {2,16304,3},{8,16304,16#ffff}]], ok. - - - null(_Rules) -> %%========================================================== %% Null ::= NULL %%========================================================== - ?line {ok,Bytes1} = asn1_wrapper:encode('Prim','Null',monday), - ?line {ok,'NULL'} = asn1_wrapper:decode('Prim','Null',lists:flatten(Bytes1)), - - - -ok. - + {ok,Bytes1} = asn1_wrapper:encode('Prim','Null',monday), + {ok,'NULL'} = asn1_wrapper:decode('Prim','Null',lists:flatten(Bytes1)), + ok. +roundtrip(T, V) -> + {ok,E} = 'Prim':encode(T, V), + {ok,V} = 'Prim':decode(T, E), + E. real(_Rules) -> %%========================================================== -- cgit v1.2.3 From a1495bd1e09dbefa8595e9388140140c143088f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 16 Apr 2013 10:40:32 +0200 Subject: PER/UPER: Fix decoding of semi-constrained INTEGERs A semi-constrained INTEGER with a non-zero lower bound would be incorrectly decoded. This bug was introduced in R16. --- lib/asn1/src/asn1ct_imm.erl | 2 +- lib/asn1/test/asn1_SUITE_data/Constraints.py | 4 ++++ lib/asn1/test/testConstraints.erl | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 5585cad925..c6803a0f96 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -198,7 +198,7 @@ per_dec_enumerated_fix_list([], Tail, _) -> Tail. per_dec_integer_1([{'SingleValue',Value}], _Aligned) -> {value,Value}; per_dec_integer_1([{'ValueRange',{Lb,'MAX'}}], Aligned) when is_integer(Lb) -> - per_dec_unconstrained(Aligned); + per_decode_semi_constrained(Lb, Aligned); per_dec_integer_1([{'ValueRange',{Lb,Ub}}], Aligned) when is_integer(Lb), is_integer(Ub) -> per_dec_constrained(Lb, Ub, Aligned); diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py index a069364084..1d2f4bc44c 100644 --- a/lib/asn1/test/asn1_SUITE_data/Constraints.py +++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py @@ -12,6 +12,10 @@ ContainedSubtype ::= INTEGER (INCLUDES Range10to20) -- Some ranges for additional constrained number testing. LongLong ::= INTEGER (0..18446744073709551615) Range256to65536 ::= INTEGER (256..65536) +SemiConstrained ::= INTEGER (100..MAX) +NegSemiConstrained ::= INTEGER (-128..MAX) + +-- Other constraints FixedSize ::= OCTET STRING (SIZE(10)) FixedSize2 ::= OCTET STRING (SIZE(10|20)) VariableSize ::= OCTET STRING (SIZE(1..10)) diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index fc217e45f7..b90ae8cab2 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -121,6 +121,16 @@ int_constraints(Rules) -> roundtrip('X1', 20), range_error(Rules, 'X1', 21), + %%========================================================== + %% SemiConstrained + %%========================================================== + + roundtrip('SemiConstrained', 100), + roundtrip('SemiConstrained', 397249742397243), + roundtrip('NegSemiConstrained', -128), + roundtrip('NegSemiConstrained', -1), + roundtrip('NegSemiConstrained', 500), + %%========================================================== %% SIZE Constraint (Duboisson p. 268) %% T ::= IA5String (SIZE (1|2, ..., SIZE (1|2|3))) -- cgit v1.2.3 From 4709d67f92bf89c264e38fbf94e3324edd922ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 23 Apr 2013 10:39:50 +0200 Subject: Fix encoding of semi-constrained, extensible INTEGERs Given: Semi ::= INTEGER (Lb..MAX, ...) where Lb is an arbitrary integer, attempting to encode an integer less than Lb would cause the encoder to enter an infinite loop. --- lib/asn1/src/asn1rtt_per.erl | 2 +- lib/asn1/src/asn1rtt_uper.erl | 2 +- lib/asn1/test/asn1_SUITE_data/Constraints.py | 2 ++ lib/asn1/test/testConstraints.erl | 10 ++++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index 21acb4e1ec..9f4b7500d8 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -173,7 +173,7 @@ encode_integer([{'ValueRange',{Lb,Ub}=VR,Range,PreEnc}],Val) when Val >= Lb, Ub >= Val -> %% this case when NamedNumberList encode_constrained_number(VR, Range, PreEnc, Val); -encode_integer([{'ValueRange',{Lb,'MAX'}}], Val) -> +encode_integer([{'ValueRange',{Lb,'MAX'}}], Val) when Lb =< Val -> encode_semi_constrained_number(Lb, Val); encode_integer([{'ValueRange',{'MIN',_}}], Val) -> encode_unconstrained_number(Val); diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index 320038caa2..a5035c6660 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -223,7 +223,7 @@ encode_integer1(C, Val) -> case VR = get_constraint(C, 'ValueRange') of no -> encode_unconstrained_number(Val); - {Lb,'MAX'} -> + {Lb,'MAX'} when Lb =< Val -> encode_semi_constrained_number(Lb, Val); %% positive with range {Lb,Ub} when Val >= Lb, Ub >= Val -> diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py index 1d2f4bc44c..e4bc987e4c 100644 --- a/lib/asn1/test/asn1_SUITE_data/Constraints.py +++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py @@ -14,6 +14,8 @@ LongLong ::= INTEGER (0..18446744073709551615) Range256to65536 ::= INTEGER (256..65536) SemiConstrained ::= INTEGER (100..MAX) NegSemiConstrained ::= INTEGER (-128..MAX) +SemiConstrainedExt ::= INTEGER (42..MAX, ...) +NegSemiConstrainedExt ::= INTEGER (-128..MAX, ...) -- Other constraints FixedSize ::= OCTET STRING (SIZE(10)) diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index b90ae8cab2..e825302629 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -131,6 +131,16 @@ int_constraints(Rules) -> roundtrip('NegSemiConstrained', -1), roundtrip('NegSemiConstrained', 500), + roundtrip('SemiConstrainedExt', -65536), + roundtrip('SemiConstrainedExt', 0), + roundtrip('SemiConstrainedExt', 42), + roundtrip('SemiConstrainedExt', 100), + roundtrip('SemiConstrainedExt', 47777789), + roundtrip('NegSemiConstrainedExt', -1023), + roundtrip('NegSemiConstrainedExt', -128), + roundtrip('NegSemiConstrainedExt', -1), + roundtrip('NegSemiConstrainedExt', 500), + %%========================================================== %% SIZE Constraint (Duboisson p. 268) %% T ::= IA5String (SIZE (1|2, ..., SIZE (1|2|3))) -- cgit v1.2.3 From f15e0270a6f68e93a7e33aa9e8aed2345f74dcd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 7 May 2013 12:53:18 +0200 Subject: asn1ct_gen_per: Remove useless renewal of 'enumval' Since fbcb7fe589edbfe79d10d7fe01be8a9f77926b89, the 'enumval' variable is no longer used. --- lib/asn1/src/asn1ct_gen_per.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 12a4cd240c..d604b9e24c 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -122,7 +122,6 @@ gen_encode_prim(Erules, #type{}=D, Value) -> false -> no; {_,Pa0} -> Pa0 end, - asn1ct_name:new(enumval), case D#type.def of 'INTEGER' -> Args = [{asis,asn1ct_imm:effective_constraint(integer,Constraint)}, -- cgit v1.2.3 From 72fa58fa2d150b0e73252ad3e13853e6b644cb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 20 May 2013 13:20:21 +0200 Subject: asn1ct_check: Don't pass on #'ObjectClassFieldType'{} with fixed type Simplify the backends by letting asn1ct_check replacing a with the actual type. --- lib/asn1/src/asn1ct_check.erl | 48 +++++++++++++++++++++----- lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 14 ++------ lib/asn1/src/asn1ct_constructed_per.erl | 16 ++------- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 34 +++--------------- lib/asn1/src/asn1ct_gen_per.erl | 20 ++--------- lib/asn1/src/asn1ct_gen_per_rt2ct.erl | 11 +----- lib/asn1/test/asn1_SUITE_data/InfObj.asn | 6 ++++ lib/asn1/test/testInfObj.erl | 6 +++- 8 files changed, 62 insertions(+), 93 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 0a95cbd3b5..5dda85c955 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -3322,7 +3322,15 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> _ -> MergedTag end, - TempNewDef#newt{type=NewTypeDef,tag=Ct}; + case TopName of + [] when Type#typedef.name =/= undefined -> + %% This is a top-level type. + #type{def=Simplified} = + simplify_type(#type{def=NewTypeDef}), + TempNewDef#newt{type=Simplified,tag=Ct}; + _ -> + TempNewDef#newt{type=NewTypeDef,tag=Ct} + end; {'TypeFromObject',{object,Object},TypeField} -> CheckedT = get_type_from_object(S,Object,TypeField), @@ -3357,6 +3365,28 @@ get_non_typedef(S, Tref0) -> Type end. + +%% +%% Simplify the backends by getting rid of an #'ObjectClassFieldType'{} +%% with a type known at compile time. +%% + +simplify_comps(Comps) -> + [simplify_comp(Comp) || Comp <- Comps]. + +simplify_comp(#'ComponentType'{typespec=Type0}=C) -> + Type = simplify_type(Type0), + C#'ComponentType'{typespec=Type}; +simplify_comp(Other) -> Other. + +simplify_type(#type{tag=Tag,def=Inner}=T) -> + case Inner of + #'ObjectClassFieldType'{type={fixedtypevaluefield,_,Type}} -> + Type#type{tag=Tag}; + _ -> + T + end. + %% tablecinf_choose. A SEQUENCE or SET may be inserted in another %% SEQUENCE or SET by the COMPONENTS OF directive. If this inserted %% type is a referenced type that already has been checked it already @@ -5118,7 +5148,7 @@ iof_associated_type1(S,C) -> prop=mandatory, tags=[{'CONTEXT',0}]}], #'SEQUENCE'{tablecinf=TableCInf, - components=IOFComponents}. + components=simplify_comps(IOFComponents)}. %% returns the leading attribute, the constraint of the components and @@ -5278,8 +5308,9 @@ check_sequence(S,Type,Comps) -> %% all extensions i.e together with the first Root components NewComps3 = textual_order(CompListWithTblInf), + NewComps4 = simplify_comps(NewComps3), CompListTuple = - complist_as_tuple(is_erule_per(S#state.erule),NewComps3), + complist_as_tuple(is_erule_per(S#state.erule), NewComps4), {CRelInf,CompListTuple}; Dupl -> throw({error,{asn1,{duplicate_components,Dupl}}}) @@ -5391,7 +5422,7 @@ check_unique_sequence_tags1(S,[],Acc) -> check_unique_tags(S,lists:reverse(Acc)). check_sequenceof(S,Type,Component) when is_record(Component,type) -> - check_type(S,Type,Component). + simplify_type(check_type(S, Type, Component)). check_set(S,Type,Components) -> {TableCInf,NewComponents} = check_sequence(S,Type,Components), @@ -5613,7 +5644,7 @@ extension({Root1,ExtList,Root2}) -> X = #'ComponentType'{prop=Y}<-ExtList], Root2}. check_setof(S,Type,Component) when is_record(Component,type) -> - check_type(S,Type,Component). + simplify_type(check_type(S, Type, Component)). check_selectiontype(S,Name,#type{def=Eref}) when is_record(Eref,'Externaltypereference') -> @@ -5677,8 +5708,9 @@ check_choice(S,Type,Components) when is_list(Components) -> ('ExtensionAdditionGroupEnd') -> false; (_) -> true end,NewComps), - check_unique_tags(S,NewComps2), - complist_as_tuple(is_erule_per(S#state.erule),NewComps2); + NewComps3 = simplify_comps(NewComps2), + check_unique_tags(S, NewComps3), + complist_as_tuple(is_erule_per(S#state.erule), NewComps3); Dupl -> throw({error,{asn1,{duplicate_choice_alternatives,Dupl}}}) end; @@ -6056,7 +6088,7 @@ get_simple_table_info1(S,#'ComponentType'{typespec=TS},Cnames,Path) -> simple_table_info(S,#'ObjectClassFieldType'{classname=ClRef, class=ObjectClass, fieldname=FieldName},Path) -> - + ObjectClassFieldName = case FieldName of {LastFieldName,[]} -> LastFieldName; diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index b318cd1920..761faa53c5 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -1004,15 +1004,8 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj) _ -> case WhatKind of {primitive,bif} -> - EncType = - case Type#type.def of - #'ObjectClassFieldType'{type={fixedtypevaluefield,_,Btype}} -> - Btype; - _ -> - Type - end, - ?ASN1CT_GEN_BER:gen_encode_prim(ber,EncType,{asis,Tag}, - Element); + ?ASN1CT_GEN_BER:gen_encode_prim(ber, Type, {asis,Tag}, + Element); 'ASN1_OPEN_TYPE' -> case Type#type.def of #'ObjectClassFieldType'{} -> %Open Type @@ -1249,9 +1242,6 @@ gen_dec_call1({primitive,bif},InnerType,Erules,TopType,Cname,Type,BytesVar, asn1ct:update_gen_state(namelist,Rest), emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',", BytesVar,"}"]); - {_,{fixedtypevaluefield,_,Btype}} -> - ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Btype,BytesVar,Tag,[], - ?PRIMITIVE,OptOrMand); _ -> ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[], ?PRIMITIVE,OptOrMand) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index dd5737dd8c..efb55cf015 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1005,14 +1005,7 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> emit({"'",Mod,"':'enc_", EType,"'(",Element,")"}); {primitive,bif} -> - EncType = - case Atype of - {fixedtypevaluefield,_,Btype} -> - Btype; - _ -> - Type - end, - asn1ct_gen_per:gen_encode_prim(Erule, EncType, Element); + asn1ct_gen_per:gen_encode_prim(Erule, Type, Element); 'ASN1_OPEN_TYPE' -> case Type#type.def of #'ObjectClassFieldType'{type=OpenType} -> @@ -1463,12 +1456,7 @@ gen_dec_line_other(Erule, Atype, TopType, Comp) -> asn1ct_gen_per:gen_dec_external(Etype, BytesVar) end; {primitive,bif} -> - case Atype of - {fixedtypevaluefield,_,Btype} -> - asn1ct_gen_per:gen_dec_imm(Erule, Btype); - _ -> - asn1ct_gen_per:gen_dec_imm(Erule, Type) - end; + asn1ct_gen_per:gen_dec_imm(Erule, Type); 'ASN1_OPEN_TYPE' -> case Type#type.def of #'ObjectClassFieldType'{type=OpenType} -> diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 96100b1b2f..8ab49aec2c 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -161,7 +161,7 @@ gen_encode_user(Erules, #typedef{}=D, Wrapper) -> emit([".",nl]) end. -gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> +gen_encode_prim(_Erules, #type{}=D, DoTag, Value) -> BitStringConstraint = get_size_constraint(D#type.constraint), asn1ct_name:new(enumval), Type = case D#type.def of @@ -215,14 +215,7 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) -> 'BMPString' -> call(encode_BMP_string, [Value,DoTag]); 'ASN1_OPEN_TYPE' -> - call(encode_open_type, [Value,DoTag]); - #'ObjectClassFieldType'{} -> - case asn1ct_gen:get_inner(D#type.def) of - {fixedtypevaluefield,_,InnerType} -> - gen_encode_prim(Erules,InnerType,DoTag,Value); - 'ASN1_OPEN_TYPE' -> - call(encode_open_type, [Value,DoTag]) - end + call(encode_open_type, [Value,DoTag]) end. emit_enc_enumerated_cases({L1,L2}, Tags) -> @@ -458,7 +451,7 @@ gen_decode_user(Erules,D) when is_record(D,typedef) -> end. -gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> +gen_dec_prim(_Erules, Att, BytesVar, DoTag, _TagIn, _Form, _OptOrMand) -> Typename = Att#type.def, %% Currently not used for BER replaced with [] as place holder %% Constraint = Att#type.constraint, @@ -552,20 +545,7 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> 'ASN1_OPEN_TYPE' -> emit(["decode_open_type_as_binary(", BytesVar,","]), - need(decode_open_type_as_binary, 2); - #'ObjectClassFieldType'{} -> - case asn1ct_gen:get_inner(Att#type.def) of - {fixedtypevaluefield,_,InnerType} -> - gen_dec_prim(Erules,InnerType,BytesVar,DoTag,TagIn,Form,OptOrMand); - 'ASN1_OPEN_TYPE' -> - emit(["decode_open_type_as_binary(", - BytesVar,","]), - need(decode_open_type_as_binary, 2); - Other -> - exit({'cannot decode',Other}) - end; - Other -> - exit({'cannot decode',Other}) + need(decode_open_type_as_binary, 2) end, TagStr = case DoTag of @@ -582,12 +562,6 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) -> {call,ber,match_tags,[BytesVar,TagStr]},com,nl, {call,real_common,decode_real,[{curr,tmpbuf}]},nl, "end",nl]); - #'ObjectClassFieldType'{} -> - case asn1ct_gen:get_inner(Att#type.def) of - 'ASN1_OPEN_TYPE' -> - emit([TagStr,")"]); - _ -> ok - end; _ -> emit([TagStr,")"]) end. diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index d604b9e24c..30c9ab9365 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -208,16 +208,7 @@ gen_encode_prim(Erules, #type{}=D, Value) -> io_lib:format("iolist_to_binary(~s)", [Value]) end, - call(Erules, encode_open_type, [NewValue]); - #'ObjectClassFieldType'{} -> - case asn1ct_gen:get_inner(D#type.def) of - {fixedtypevaluefield,_,InnerType} -> - gen_encode_prim(Erules, InnerType, Value); - T -> %% 'ASN1_OPEN_TYPE' - gen_encode_prim(Erules, D#type{def=T}, Value) - end; - XX -> - exit({asn1_error,nyi,XX}) + call(Erules, encode_open_type, [NewValue]) end. emit_enc_real(Erules, Real) -> @@ -1086,14 +1077,7 @@ gen_dec_imm_1('RELATIVE-OID', _Constraint, Aligned) -> gen_dec_imm_1('UTF8String', _Constraint, Aligned) -> asn1ct_imm:per_dec_restricted_string(Aligned); gen_dec_imm_1('REAL', _Constraint, Aligned) -> - asn1ct_imm:per_dec_real(Aligned); -gen_dec_imm_1(#'ObjectClassFieldType'{}=TypeName, _Constraint, Aligned) -> - case asn1ct_gen:get_inner(TypeName) of - {fixedtypevaluefield,_,#type{def=InnerType,constraint=C}} -> - gen_dec_imm_1(InnerType, C, Aligned); - #type{def=T,constraint=C} -> - gen_dec_imm_1(T, C, Aligned) - end. + asn1ct_imm:per_dec_real(Aligned). gen_dec_bit_string(F, Imm) -> D = fun(V, Buf) -> diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index c9e5f52e38..012d54e7a1 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -115,16 +115,7 @@ gen_encode_prim(Erules, #type{}=D, Value) -> io_lib:format("iolist_to_binary(~s)", [Value]) end, - call(Erules, encode_open_type, [NewValue]); - #'ObjectClassFieldType'{} -> - case asn1ct_gen:get_inner(D#type.def) of - {fixedtypevaluefield,_,InnerType} -> - gen_encode_prim(Erules, InnerType, Value); - T -> %% 'ASN1_OPEN_TYPE' - gen_encode_prim(Erules, D#type{def=T}, Value) - end; - XX -> - exit({asn1_error,nyi,XX}) + call(Erules, encode_open_type, [NewValue]) end. emit_enc_real(Erules, Real) -> diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj.asn b/lib/asn1/test/asn1_SUITE_data/InfObj.asn index 61d9d23ea2..53e5043cb7 100644 --- a/lib/asn1/test/asn1_SUITE_data/InfObj.asn +++ b/lib/asn1/test/asn1_SUITE_data/InfObj.asn @@ -180,6 +180,12 @@ MyPdu ::= SEQUENCE { str MY-CLASS.&stringValue ({MyObjectSet}{@int}) } +Seq2 ::= SEQUENCE { + int MY-CLASS.&integerValue ({MyObjectSet}), + seqof SEQUENCE (1..10) OF MY-CLASS.&booleanValue ({MyObjectSet}{@int}), + setof SET (1..10) OF MY-CLASS.&booleanValue ({MyObjectSet}{@int}) +} + -- -- Class with constructed default -- diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index d9890e4358..75f4dae310 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -58,7 +58,11 @@ main(_Erule) -> roundtrip('InfObj', 'ConstructedPdu', {'ConstructedPdu',2,{'CONSTRUCTED-DEFAULT_Type',999,false}}), roundtrip('InfObj', 'ConstructedPdu', - {'ConstructedPdu',3,true}). + {'ConstructedPdu',3,true}), + + roundtrip('InfObj', 'Seq2', + {'Seq2',42,[true,false,false,true], + [false,true,false]}). roundtrip(M, T, V) -> -- cgit v1.2.3 From 7ffa31b173b57ed61a111e2dcc93967695ccf01c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 21 May 2013 09:18:44 +0200 Subject: asn1ct_check: Eliminate useless Per argument from complist_as_tuple() The Per argument is no longer used; it is only passed around. --- lib/asn1/src/asn1ct_check.erl | 48 +++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 5dda85c955..f94550b0a4 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -5303,47 +5303,37 @@ check_sequence(S,Type,Comps) -> %% the involved class removed, as the class of the object %% set. CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps2), - %% If encoding rule is in the PER family the Root Components - %% after the second extension mark should be encoded before - %% all extensions i.e together with the first Root components NewComps3 = textual_order(CompListWithTblInf), NewComps4 = simplify_comps(NewComps3), - CompListTuple = - complist_as_tuple(is_erule_per(S#state.erule), NewComps4), + CompListTuple = complist_as_tuple(NewComps4), {CRelInf,CompListTuple}; Dupl -> throw({error,{asn1,{duplicate_components,Dupl}}}) end. -complist_as_tuple(Per,CompList) -> - complist_as_tuple(Per,CompList,[],[],[],root). +complist_as_tuple(CompList) -> + complist_as_tuple(CompList, [], [], [], root). -complist_as_tuple(Per,[#'EXTENSIONMARK'{}|T],Acc,Ext,Acc2,root) -> - complist_as_tuple(Per,T,Acc,Ext,Acc2,ext); -complist_as_tuple(Per,[#'EXTENSIONMARK'{}|T],Acc,Ext,Acc2,ext) -> - complist_as_tuple(Per,T,Acc,Ext,Acc2,root2); -complist_as_tuple(_Per,[#'EXTENSIONMARK'{}|_T],_Acc,_Ext,_Acc2,root2) -> +complist_as_tuple([#'EXTENSIONMARK'{}|T], Acc, Ext, Acc2, root) -> + complist_as_tuple(T, Acc, Ext, Acc2, ext); +complist_as_tuple([#'EXTENSIONMARK'{}|T], Acc, Ext, Acc2, ext) -> + complist_as_tuple(T, Acc, Ext, Acc2, root2); +complist_as_tuple([#'EXTENSIONMARK'{}|_T], _Acc, _Ext, _Acc2, root2) -> throw({error,{asn1,{too_many_extension_marks}}}); -complist_as_tuple(Per,[C|T],Acc,Ext,Acc2,root) -> - complist_as_tuple(Per,T,[C|Acc],Ext,Acc2,root); -complist_as_tuple(Per,[C|T],Acc,Ext,Acc2,ext) -> - complist_as_tuple(Per,T,Acc,[C|Ext],Acc2,ext); -complist_as_tuple(Per,[C|T],Acc,Ext,Acc2,root2) -> - complist_as_tuple(Per,T,Acc,Ext,[C|Acc2],root2); -complist_as_tuple(_Per,[],Acc,_Ext,_Acc2,root) -> +complist_as_tuple([C|T], Acc, Ext, Acc2, root) -> + complist_as_tuple(T, [C|Acc], Ext, Acc2, root); +complist_as_tuple([C|T], Acc, Ext, Acc2, ext) -> + complist_as_tuple(T, Acc, [C|Ext], Acc2, ext); +complist_as_tuple([C|T], Acc, Ext, Acc2, root2) -> + complist_as_tuple(T, Acc, Ext, [C|Acc2], root2); +complist_as_tuple([], Acc, _Ext, _Acc2, root) -> lists:reverse(Acc); -complist_as_tuple(_Per,[],Acc,Ext,_Acc2,ext) -> +complist_as_tuple([], Acc, Ext, _Acc2, ext) -> {lists:reverse(Acc),lists:reverse(Ext)}; -%%complist_as_tuple(_Per = true,[],Acc,Ext,Acc2,root2) -> -%% {lists:reverse(Acc)++lists:reverse(Acc2),lists:reverse(Ext)}; -complist_as_tuple(_Per,[],Acc,Ext,Acc2,root2) -> +complist_as_tuple([], Acc, Ext, Acc2, root2) -> {lists:reverse(Acc),lists:reverse(Ext),lists:reverse(Acc2)}. -is_erule_per(per) -> true; -is_erule_per(uper) -> true; -is_erule_per(ber) -> false. - expand_components(S, [{'COMPONENTS OF',Type}|T]) -> CompList = expand_components2(S,get_referenced_type(S,Type#type.def)), expand_components(S,CompList) ++ expand_components(S,T); @@ -5387,7 +5377,7 @@ take_only_rootset([H|T]) -> [H|take_only_rootset(T)]. check_unique_sequence_tags(S,CompList) -> - TagComps = case complist_as_tuple(false,CompList) of + TagComps = case complist_as_tuple(CompList) of {R1,Ext,R2} -> R1 ++ [C#'ComponentType'{prop='OPTIONAL'}|| C = #'ComponentType'{} <- Ext]++R2; @@ -5710,7 +5700,7 @@ check_choice(S,Type,Components) when is_list(Components) -> end,NewComps), NewComps3 = simplify_comps(NewComps2), check_unique_tags(S, NewComps3), - complist_as_tuple(is_erule_per(S#state.erule), NewComps3); + complist_as_tuple(NewComps3); Dupl -> throw({error,{asn1,{duplicate_choice_alternatives,Dupl}}}) end; -- cgit v1.2.3 From 8bb2c7147979d30a06173fe2e08a456cc4745e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 21 May 2013 16:40:44 +0200 Subject: Improve tests of ENUMERATED --- lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 | 63 +++++++++++++++++------------- lib/asn1/test/testEnumExt.erl | 12 +++++- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 index 9ad1f6299e..3a727e46bb 100644 --- a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 @@ -1,28 +1,35 @@ -EnumExt DEFINITIONS AUTOMATIC TAGS ::= -BEGIN - -Ext ::= ENUMERATED { - blue(0), - red(1), - green(2), - ... -} - -Ext1 ::= ENUMERATED { - blue(0), - red(1), - green(2), - ..., - orange(7) -} - -Noext ::= ENUMERATED { - blue(0), - red(1), - green(2) -} - -Globalstate ::= ENUMERATED {def(1),com(2),preop(3),oper(4),noop(5),fail(6)} - -END - +EnumExt DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + +Ext ::= ENUMERATED { + blue(0), + red(1), + green(2), + ... +} + +Ext1 ::= ENUMERATED { + blue(0), + red(1), + green(2), + ..., + orange(7), + black(8), + magenta(9) +} + +Noext ::= ENUMERATED { + blue(0), + red(1), + green(2) +} + +Globalstate ::= ENUMERATED {def(1),com(2),preop(3),oper(4),noop(5),fail(6)} + +Seq ::= SEQUENCE { + e Ext1, + i INTEGER +} + +END + diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl index 0811f20571..8840ed6d2f 100644 --- a/lib/asn1/test/testEnumExt.erl +++ b/lib/asn1/test/testEnumExt.erl @@ -38,8 +38,7 @@ main(Rule) when Rule =:= per; Rule =:= uper -> %% ENUMERATED no extensionmark B64 = <<64>>, B64 = roundtrip('Noext', red), - ok; - + common(); main(ber) -> io:format("main(ber)~n",[]), %% ENUMERATED with extensionmark (value is in root set) @@ -57,6 +56,15 @@ main(ber) -> roundtrip('Globalstate', preop), roundtrip('Globalstate', com), + common(). + +common() -> + roundtrip('Seq', {'Seq',blue,42}), + roundtrip('Seq', {'Seq',red,42}), + roundtrip('Seq', {'Seq',green,42}), + roundtrip('Seq', {'Seq',orange,47}), + roundtrip('Seq', {'Seq',black,4711}), + roundtrip('Seq', {'Seq',magenta,4712}), ok. roundtrip(Type, Value) -> -- cgit v1.2.3 From 7340d9276e4a0391874b243a618692d23f9738a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 24 May 2013 07:44:13 +0200 Subject: Extend tests cases for BIT STRING --- lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 | 2 +- lib/asn1/test/testCompactBitString.erl | 2 + lib/asn1/test/testPrimStrings.erl | 89 +++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 index 64fa5f4cd4..08e7f94ab6 100644 --- a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 @@ -5,7 +5,7 @@ BEGIN Bs1 ::= BIT STRING Bs2 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (7)) Bs3 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..7)) - Bs4 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..32)) + Bs4 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } Bs5 ::= BIT STRING {su(0), mo(17), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..32)) Bs6 ::= BIT STRING {su(0), mo(17), tu(2), we(3), th(4), fr(5), sa(6)} (SIZE (16..32)) Bs7 ::= BIT STRING (SIZE (24)) diff --git a/lib/asn1/test/testCompactBitString.erl b/lib/asn1/test/testCompactBitString.erl index 96d9f0fdcb..28ab2464e9 100644 --- a/lib/asn1/test/testCompactBitString.erl +++ b/lib/asn1/test/testCompactBitString.erl @@ -150,4 +150,6 @@ roundtrip(Type, Val1, Val2) -> roundtrip_1(Mod, Type, In, Out) -> {ok,Encoded} = Mod:encode(Type, In), {ok,Out} = Mod:decode(Type, Encoded), + %% Test that compact BIT STRINGs can be encoded. + {ok,Encoded} = Mod:encode(Type, Out), ok. diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index a347453d20..e2322c92a9 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -80,7 +80,7 @@ bit_string(Rules) -> roundtrip('Bs3', [mo,tu,fr]), bs_roundtrip('Bs3', [0,1,1,0,0,1,0], [mo,tu,fr]), - + %%========================================================== %% Bs7 ::= BIT STRING (SIZE (24)) %%========================================================== @@ -173,7 +173,91 @@ bit_string(Rules) -> BSList1024 = BSmaker(BSmaker,0,1024,{1,0},[]), bs_roundtrip('BS1024', BSList1024), - bs_roundtrip('TransportLayerAddress', [0,1,1,0]). + bs_roundtrip('TransportLayerAddress', [0,1,1,0]), + + case Rules of + ber -> ok; + _ -> per_bs_strings() + end. + +%% The PER encoding rules requires that a BIT STRING with +%% named positions should never have any trailing zeroes +%% (except to reach the minimum number of bits as given by +%% a SIZE constraint). + +per_bs_strings() -> + bs_roundtrip('Bs3', [0,0,1,0,0,0,0], [tu]), + bs_roundtrip('Bs3', <<2#0010000:7>>, [tu]), + bs_roundtrip('Bs3', {1,<<2#00100000:8>>}, [tu]), + + bs_roundtrip('Bs4', [0,1,1,0,0,1,0], [mo,tu,fr]), + bs_roundtrip('Bs4', <<2#0110010:7>>, [mo,tu,fr]), + bs_roundtrip('Bs4', {1,<<2#01100100:8>>}, [mo,tu,fr]), + + bs_roundtrip('Bs4', [0,1,1,0,0,0,0], [mo,tu]), + bs_roundtrip('Bs4', <<2#011:3,0:32>>, [mo,tu]), + bs_roundtrip('Bs4', {5,<<2#011:3,0:32,0:5>>}, [mo,tu]), + + [per_trailing_zeroes(B) || B <- lists:seq(0, 255)], + ok. + +%% Trailing zeroes should be removed from BIT STRINGs with named +%% bit positions. + +per_trailing_zeroes(Byte) -> + L = lists:reverse(make_bit_list(Byte+16#10000)), + L = make_bit_list(Byte+16#10000, []), + Pos = positions(L, 0), + ExpectedSz = case lists:last(Pos) of + su -> 1; + {bit,LastBitPos} -> LastBitPos+1 + end, + + %% List of zeroes and ones. + named_roundtrip(L, Pos, ExpectedSz), + named_roundtrip(L++[0,0,0,0,0], Pos, ExpectedSz), + + %% Bitstrings. + Bs = << <> || B <- L >>, + Sz = bit_size(Bs), + named_roundtrip(Bs, Pos, ExpectedSz), + Bin = <>, + named_roundtrip(Bin, Pos, ExpectedSz), + + %% Compact bitstring. + named_roundtrip({7,Bin}, Pos, ExpectedSz), + + %% Integer bitstring (obsolete). + IntBs = intlist_to_integer(L, 0, 0), + named_roundtrip(IntBs, Pos, ExpectedSz), + + ok. + +make_bit_list(0) -> []; +make_bit_list(B) -> [B band 1|make_bit_list(B bsr 1)]. + +make_bit_list(0, Acc) -> Acc; +make_bit_list(B, Acc) -> make_bit_list(B bsr 1, [B band 1|Acc]). + +positions([1|T], 0) -> [su|positions(T, 1)]; +positions([1|T], Pos) -> [{bit,Pos}|positions(T, Pos+1)]; +positions([0|T], Pos) -> positions(T, Pos+1); +positions([], _) -> []. + +intlist_to_integer([B|T], Shift, Acc) -> + intlist_to_integer(T, Shift+1, (B bsl Shift) + Acc); +intlist_to_integer([], _, Acc) -> Acc. + +named_roundtrip(Value, Expected, ExpectedSz) -> + M = 'PrimStrings', + Type = 'Bs4', + {ok,Encoded} = M:encode(Type, Value), + {ok,Encoded} = M:encode(Type, Expected), + {ok,Expected} = M:decode(Type, Encoded), + + %% Verify the size in the first byte. + <> = Encoded, + ok. octet_string(Rules) -> @@ -628,6 +712,7 @@ bs_roundtrip(Type, Value) -> bs_roundtrip(Type, Value, Expected) -> M = 'PrimStrings', {ok,Encoded} = M:encode(Type, Value), + {ok,Encoded} = M:encode(Type, Expected), case M:decode(Type, Encoded) of {ok,Expected} -> ok; -- cgit v1.2.3 From 8cd10fdc48a6d2cc61d4143767206ad08ef27539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 31 May 2013 10:27:03 +0200 Subject: Clean up testSeqOf --- lib/asn1/test/testSeqOf.erl | 258 +++++++++++++------------------------------- 1 file changed, 76 insertions(+), 182 deletions(-) diff --git a/lib/asn1/test/testSeqOf.erl b/lib/asn1/test/testSeqOf.erl index 1aa1eab26d..771045f9af 100644 --- a/lib/asn1/test/testSeqOf.erl +++ b/lib/asn1/test/testSeqOf.erl @@ -28,193 +28,87 @@ -record('Seq3',{bool3, seq3 = asn1_DEFAULT, int3}). -record('Seq4',{seq41 = asn1_DEFAULT, seq42 = asn1_DEFAULT, seq43 = asn1_DEFAULT}). -record('SeqIn',{boolIn, intIn}). -%-record('SeqCho',{bool1, int1, seq1 = asn1_DEFAULT}). -%-record('SeqChoInline',{bool1, int1, seq1 = asn1_DEFAULT}). -%-record('SeqChoOfInline_SEQOF',{bool1, int1, seq1 = asn1_DEFAULT}). -record('SeqEmp',{seq1}). -record('Empty',{}). -main(Rules) -> - - ?line {ok,Bytes11} = - asn1_wrapper:encode('SeqOf','Seq1',#'Seq1'{bool1 = true, - int1 = 17}), - ?line {ok,{'Seq1',true,17,[]}} = - asn1_wrapper:decode('SeqOf','Seq1',lists:flatten(Bytes11)), - - - ?line {ok,Bytes12} = - asn1_wrapper:encode('SeqOf','Seq1',#'Seq1'{bool1 = true, - int1 = 17, - seq1 = [#'SeqIn'{boolIn = true, - intIn = 25}]}), - ?line {ok,{'Seq1',true,17,[{'SeqIn',true,25}]}} = - asn1_wrapper:decode('SeqOf','Seq1',lists:flatten(Bytes12)), - - - - ?line {ok,Bytes13} = - asn1_wrapper:encode('SeqOf','Seq1',#'Seq1'{bool1 = true, - int1 = 17, - seq1 = [#'SeqIn'{boolIn = true, - intIn = 25}, - #'SeqIn'{boolIn = false, - intIn = 125}, - #'SeqIn'{boolIn = false, - intIn = 225}]}), - ?line {ok,{'Seq1',true,17,[{'SeqIn',true,25},{'SeqIn',false,125},{'SeqIn',false,225}]}} = - asn1_wrapper:decode('SeqOf','Seq1',lists:flatten(Bytes13)), - - - - - - - ?line {ok,Bytes21} = - asn1_wrapper:encode('SeqOf','Seq2',#'Seq2'{bool2 = true, - int2 = 17}), - - ?line {ok,{'Seq2',[],true,17}} = - asn1_wrapper:decode('SeqOf','Seq2',lists:flatten(Bytes21)), - - - ?line {ok,Bytes22} = - asn1_wrapper:encode('SeqOf','Seq2',#'Seq2'{bool2 = true, - int2 = 17, - seq2 = [#'SeqIn'{boolIn = true, - intIn = 25}]}), - ?line {ok,{'Seq2',[{'SeqIn',true,25}],true,17}} = - asn1_wrapper:decode('SeqOf','Seq2',lists:flatten(Bytes22)), - - - ?line {ok,Bytes23} = - asn1_wrapper:encode('SeqOf','Seq2',#'Seq2'{bool2 = true, - int2 = 17, - seq2 = [#'SeqIn'{boolIn = true, - intIn = 25}, - #'SeqIn'{boolIn = false, - intIn = 125}, - #'SeqIn'{boolIn = false, - intIn = 225}]}), - ?line {ok,{'Seq2',[{'SeqIn',true,25},{'SeqIn',false,125},{'SeqIn',false,225}],true,17}} = - asn1_wrapper:decode('SeqOf','Seq2',lists:flatten(Bytes23)), - - - - - - - ?line {ok,Bytes31} = - asn1_wrapper:encode('SeqOf','Seq3',#'Seq3'{bool3 = true, - int3 = 17}), - ?line {ok,{'Seq3',true,[],17}} = - asn1_wrapper:decode('SeqOf','Seq3',lists:flatten(Bytes31)), - - - ?line {ok,Bytes32} = - asn1_wrapper:encode('SeqOf','Seq3',#'Seq3'{bool3 = true, - int3 = 17, - seq3 = [#'SeqIn'{boolIn = true, - intIn = 25}]}), - ?line {ok,{'Seq3',true,[{'SeqIn',true,25}],17}} = - asn1_wrapper:decode('SeqOf','Seq3',lists:flatten(Bytes32)), - - - ?line {ok,Bytes33} = - asn1_wrapper:encode('SeqOf','Seq3',#'Seq3'{bool3 = true, - int3 = 17, - seq3 = [#'SeqIn'{boolIn = true, - intIn = 25}, - #'SeqIn'{boolIn = false, - intIn = 125}, - #'SeqIn'{boolIn = false, - intIn = 225}]}), - ?line {ok,{'Seq3',true,[{'SeqIn',true,25},{'SeqIn',false,125},{'SeqIn',false,225}],17}} = - asn1_wrapper:decode('SeqOf','Seq3',lists:flatten(Bytes33)), - - +main(_Rules) -> + SeqIn3 = [#'SeqIn'{boolIn=true,intIn=25}, + #'SeqIn'{boolIn=false,intIn=125}, + #'SeqIn'{boolIn=false,intIn=225}], + + roundtrip('Seq1', #'Seq1'{bool1=true,int1=17}, + #'Seq1'{bool1=true,int1=17,seq1=[]}), + + roundtrip('Seq1', #'Seq1'{bool1=true,int1 = 17, + seq1=[#'SeqIn'{boolIn=true, + intIn=25}]}), + roundtrip('Seq1', #'Seq1'{bool1=true, + int1=17, + seq1=SeqIn3}), + + roundtrip('Seq2', #'Seq2'{bool2=true,int2=17}, + #'Seq2'{seq2=[],bool2=true,int2=17}), + roundtrip('Seq2',#'Seq2'{bool2=true,int2=17, + seq2=[#'SeqIn'{boolIn=true, + intIn=25}]}), + roundtrip('Seq2', #'Seq2'{bool2=true, + int2=17, + seq2=SeqIn3}), + + roundtrip('Seq3', #'Seq3'{bool3=true,int3=17}, + #'Seq3'{bool3=true,seq3=[],int3=17}), + roundtrip('Seq3',#'Seq3'{bool3=true, + int3=17, + seq3=[#'SeqIn'{boolIn=true, + intIn=25}]}), + roundtrip('Seq3', #'Seq3'{bool3=true,int3=17,seq3=SeqIn3}), + + roundtrip('Seq4', #'Seq4'{}, #'Seq4'{seq41=[],seq42=[],seq43=[]}), + + roundtrip('Seq4', #'Seq4'{seq41=[#'SeqIn'{boolIn=true,intIn=25}]}, + #'Seq4'{seq41=[#'SeqIn'{boolIn=true,intIn=25}], + seq42=[],seq43=[]}), + + roundtrip('Seq4', #'Seq4'{seq41=SeqIn3}, + #'Seq4'{seq41=SeqIn3,seq42=[],seq43=[]}), + roundtrip('Seq4', #'Seq4'{seq42=[#'SeqIn'{boolIn=true,intIn=25}]}, + #'Seq4'{seq41=[],seq42=[#'SeqIn'{boolIn=true,intIn=25}], + seq43=[]}), + roundtrip('Seq4', #'Seq4'{seq42=SeqIn3}, + #'Seq4'{seq41=[],seq42=SeqIn3,seq43=[]}), + + roundtrip('Seq4', #'Seq4'{seq43=[#'SeqIn'{boolIn=true,intIn=25}]}, + #'Seq4'{seq41=[],seq42=[], + seq43=[#'SeqIn'{boolIn=true,intIn=25}]}), + roundtrip('Seq4', #'Seq4'{seq43=SeqIn3}, + #'Seq4'{seq41=[],seq42=[], + seq43=SeqIn3}), + + roundtrip('SeqEmp', #'SeqEmp'{seq1=[#'Empty'{}]}), + + %% Test OTP-4590: correct encoding of the length of SEQUENC OF. + DayNames = ["Monday","Tuesday","Wednesday", + "Thursday","Friday","Saturday","Sunday"], + xroundtrip('DayNames1', 'DayNames3', DayNames), + xroundtrip('DayNames2', 'DayNames4', DayNames), + xroundtrip('DayNames2', 'DayNames4', [hd(DayNames)]), + xroundtrip('DayNames2', 'DayNames4', tl(DayNames)), + ok. +roundtrip(T, V) -> + roundtrip(T, V, V). - - - ?line {ok,Bytes41} = asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{}), - ?line {ok,{'Seq4',[],[],[]}} = asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes41)), - - - ?line {ok,Bytes42} = - asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{seq41 = [#'SeqIn'{boolIn = true, - intIn = 25}]}), - ?line {ok,{'Seq4',[{'SeqIn',true,25}],[],[]}} = - asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes42)), - - - ?line {ok,Bytes43} = - asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{seq41 = [#'SeqIn'{boolIn = true, - intIn = 25}, - #'SeqIn'{boolIn = false, - intIn = 125}, - #'SeqIn'{boolIn = false, - intIn = 225}]}), - ?line {ok,{'Seq4',[{'SeqIn',true,25},{'SeqIn',false,125},{'SeqIn',false,225}],[],[]}} = - asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes43)), - - - ?line {ok,Bytes44} = - asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{seq42 = [#'SeqIn'{boolIn = true, - intIn = 25}]}), - ?line {ok,{'Seq4',[],[{'SeqIn',true,25}],[]}} = - asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes44)), - - - ?line {ok,Bytes45} = - asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{seq42 = [#'SeqIn'{boolIn = true, - intIn = 25}, - #'SeqIn'{boolIn = false, - intIn = 125}, - #'SeqIn'{boolIn = false, - intIn = 225}]}), - ?line {ok,{'Seq4',[],[{'SeqIn',true,25},{'SeqIn',false,125},{'SeqIn',false,225}],[]}} = - asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes45)), - - - ?line {ok,Bytes46} = - asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{seq43 = [#'SeqIn'{boolIn = true, - intIn = 25}]}), - ?line {ok,{'Seq4',[],[],[{'SeqIn',true,25}]}} = - asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes46)), - - - ?line {ok,Bytes47} = - asn1_wrapper:encode('SeqOf','Seq4',#'Seq4'{seq43 = [#'SeqIn'{boolIn = true, - intIn = 25}, - #'SeqIn'{boolIn = false, - intIn = 125}, - #'SeqIn'{boolIn = false, - intIn = 225}]}), - ?line {ok,{'Seq4',[],[],[{'SeqIn',true,25},{'SeqIn',false,125},{'SeqIn',false,225}]}} = - asn1_wrapper:decode('SeqOf','Seq4',lists:flatten(Bytes47)), - - - ?line {ok,Bytes51} = asn1_wrapper:encode('SeqOf','SeqEmp',#'SeqEmp'{seq1 = [#'Empty'{}]}), - ?line {ok,{'SeqEmp',[{'Empty'}]}} = asn1_wrapper:decode('SeqOf','SeqEmp',lists:flatten(Bytes51)), - - %% tests of OTP-4590 - case Rules of - per -> - DayNames = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"], - ?line {ok,Bytes60} = asn1_wrapper:encode('XSeqOf','DayNames2',DayNames), - ?line {ok,Bytes60} = asn1_wrapper:encode('XSeqOf','DayNames4',DayNames), - ?line {ok,DayNames} = asn1_wrapper:decode('XSeqOf','DayNames2',Bytes60), - ?line {ok,DayNames} = asn1_wrapper:decode('XSeqOf','DayNames4',Bytes60), - ?line {ok,Bytes61} = asn1_wrapper:encode('XSeqOf','DayNames1',DayNames), - ?line {ok,Bytes61} = asn1_wrapper:encode('XSeqOf','DayNames3',DayNames), - ?line {ok,DayNames} = asn1_wrapper:decode('XSeqOf','DayNames1',Bytes61), - ?line {ok,DayNames} = asn1_wrapper:decode('XSeqOf','DayNames3',Bytes61); - _ -> - ok - end, - +roundtrip(Type, Val, Expected) -> + M = 'SeqOf', + {ok,Enc} = M:encode(Type, Val), + {ok,Expected} = M:decode(Type, Enc), ok. - +xroundtrip(T1, T2, Val) -> + M = 'XSeqOf', + {ok,Enc} = M:encode(T1, Val), + {ok,Enc} = M:encode(T2, Val), + {ok,Val} = M:decode(T1, Enc), + {ok,Val} = M:decode(T2, Enc), + ok. -- cgit v1.2.3 From d94474a632305049330ce578e2042b454c993abb Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 31 May 2013 15:31:54 +0200 Subject: [snmp/agent] Some restructuring of test suite Removed use of the "-compile(export_all)" attribute and instead explicitly export functions. This together with some renaming of internal functions avoids unpredictable ct side effects. Previously some test cases had "internal" (since the export_all made all functions exported, not so much) functions, with the same name but with arity 0, which did the actual test case. This made for confusing logs and possible unpredictable test behaviour. --- lib/snmp/test/snmp_agent_test.erl | 674 ++++++++++++++++++++++++++++++-------- 1 file changed, 545 insertions(+), 129 deletions(-) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index d298f9ac75..6fe97ccd25 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-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 @@ -24,7 +24,402 @@ %% * Test fault-tolerance (kill master etc) %% --compile(export_all). +-export([ + all/0, + groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + + %% all_tcs - misc + app_info/1, + info_test/1, + + %% all_tcs - test_v1 + simple/1, + db_notify_client/1, + v1_processing/1, + big/1, + big2/1, + loop_mib/1, + api/1, + subagent/1, + mnesia/1, + sa_register/1, + v1_trap/1, + sa_error/1, + next_across_sa/1, + undo/1, + sparse_table/1, + cnt_64/1, + opaque/1, + change_target_addr_config/1, + + %% all_tcs - test_v1 - multiple_reqs + mul_get/1, + mul_get_err/1, + mul_next/1, + mul_next_err/1, + mul_set/1, + mul_set_err/1, + + %% all_tcs - test_v1 - reported_bugs + otp_1128/1, + otp_1129/1, + otp_1131/1, + otp_1162/1, + otp_1222/1, + otp_1298/1, + otp_1331/1, + otp_1338/1, + otp_1342/1, + otp_1366/1, + otp_2776/1, + otp_2979/1, + otp_3187/1, + otp_3725/1, + + %% all_tcs - test_v1 - standard_mibs + snmp_standard_mib/1, + snmp_community_mib/1, + snmp_framework_mib/1, + snmp_target_mib/1, + snmp_notification_mib/1, + snmp_view_based_acm_mib/1, + + %% all_tcs - test_v2 + simple_2/1, + v2_processing/1, + big_2/1, + big2_2/1, + loop_mib_2/1, + api_2/1, + subagent_2/1, + mnesia_2/1, + sa_register_2/1, + v2_trap/1, + sa_error_2/1, + next_across_sa_2/1, + undo_2/1, + v2_types/1, + implied/1, + sparse_table_2/1, + cnt_64_2/1, + opaque_2/1, + v2_caps/1, + + %% all_tcs - test_v2 - multiple_reqs_2 + mul_get_2/1, + mul_get_err_2/1, + mul_next_2/1, + mul_next_err_2/1, + mul_set_2/1, + mul_set_err_2/1, + + %% all_tcs - test_v2 - v2_inform + v2_inform_i/1, + + %% all_tcs - test_v2 - reported_bugs_2 + otp_1128_2/1, + otp_1129_2/1, + otp_1131_2/1, + otp_1162_2/1, + otp_1222_2/1, + otp_1298_2/1, + otp_1331_2/1, + otp_1338_2/1, + otp_1342_2/1, + otp_1366_2/1, + otp_2776_2/1, + otp_2979_2/1, + otp_3187_2/1, + + %% all_tcs - test_v2 - standard_mibs_2 + snmpv2_mib_2/1, + snmp_community_mib_2/1, + snmp_framework_mib_2/1, + snmp_target_mib_2/1, + snmp_notification_mib_2/1, + snmp_view_based_acm_mib_2/1, + + %% all_tcs - test_v1_v2 + simple_bi/1, + + %% all_tcs - test_v3 + simple_3/1, + v3_processing/1, + big_3/1, + big2_3/1, + api_3/1, + subagent_3/1, + mnesia_3/1, + loop_mib_3/1, + sa_register_3/1, + v3_trap/1, + sa_error_3/1, + next_across_sa_3/1, + undo_3/1, + v2_types_3/1, + implied_3/1, + sparse_table_3/1, + cnt_64_3/1, + opaque_3/1, + v2_caps_3/1, + + %% all_tcs - test_v3 - multiple_reqs_3 + mul_get_3/1, + mul_get_err_3/1, + mul_next_3/1, + mul_next_err_3/1, + mul_set_3/1, + mul_set_err_3/1, + + %% all_tcs - test_v3 - v3_inform + v3_inform_i/1, + + %% all_tcs - test_v3 - reported_bugs_3 + otp_1128_3/1, + otp_1129_3/1, + otp_1131_3/1, + otp_1162_3/1, + otp_1222_3/1, + otp_1298_3/1, + otp_1331_3/1, + otp_1338_3/1, + otp_1342_3/1, + otp_1366_3/1, + otp_2776_3/1, + otp_2979_3/1, + otp_3187_3/1, + otp_3542/1, + + %% all_tcs - test_v3 - standard_mibs_3 + snmpv2_mib_3/1, + snmp_framework_mib_3/1, + snmp_mpd_mib_3/1, + snmp_target_mib_3/1, + snmp_notification_mib_3/1, + snmp_view_based_acm_mib_3/1, + snmp_user_based_sm_mib_3/1, + + %% all_tcs - test_v3 - v3_security + v3_crypto_basic/1, + v3_md5_auth/1, + v3_sha_auth/1, + v3_des_priv/1, + + %% all_tcs - test_multi_threaded + multi_threaded/1, + mt_trap/1, + + %% all_tcs - mib_storage - mib_storage_ets + mse_simple/1, + mse_v1_processing/1, + mse_big/1, + mse_big2/1, + mse_loop_mib/1, + mse_api/1, + mse_sa_register/1, + mse_v1_trap/1, + mse_sa_error/1, + mse_next_across_sa/1, + mse_undo/1, + mse_standard_mib/1, + mse_community_mib/1, + mse_framework_mib/1, + mse_target_mib/1, + mse_notification_mib/1, + mse_view_based_acm_mib/1, + mse_sparse_table/1, + mse_me_of/1, + mse_mib_of/1, + + %% all_tcs - mib_storage - mib_storage_dets + msd_simple/1, + msd_v1_processing/1, + msd_big/1, + msd_big2/1, + msd_loop_mib/1, + msd_api/1, + msd_sa_register/1, + msd_v1_trap/1, + msd_sa_error/1, + msd_next_across_sa/1, + msd_undo/1, + msd_standard_mib/1, + msd_community_mib/1, + msd_framework_mib/1, + msd_target_mib/1, + msd_notification_mib/1, + msd_view_based_acm_mib/1, + msd_sparse_table/1, + msd_me_of/1, + msd_mib_of/1, + + %% all_tcs - mib_storage - mib_storage_mnesia + msm_simple/1, + msm_v1_processing/1, + msm_big/1, + msm_big2/1, + msm_loop_mib/1, + msm_api/1, + msm_sa_register/1, + msm_v1_trap/1, + msm_sa_error/1, + msm_next_across_sa/1, + msm_undo/1, + msm_standard_mib/1, + msm_community_mib/1, + msm_framework_mib/1, + msm_target_mib/1, + msm_notification_mib/1, + msm_view_based_acm_mib/1, + msm_sparse_table/1, + msm_me_of/1, + msm_mib_of/1, + + %% all_tcs - mib_storage - mse_size_check + mse_size_check/1, + + %% all_tcs - mib_storage - msd_size_check + msd_size_check/1, + + %% all_tcs - mib_storage - msm_size_check + msm_size_check/1, + + %% all_tcs - mib_storage - varm_mib_storage_dets + msd_varm_mib_start/1, + + %% all_tcs - mib_storage - varm_mib_storage_mnesia + msm_varm_mib_start/1, + + %% all_tcs - tickets1 - otp4394 + otp_4394/1, + + %% all_tcs - tickets1 - otp7157 + otp_7157/1, + + %% tickets2 + otp8395/1, + otp9884/1 + + ]). + +%% Internal exports +-export([dummy_manager_init/2, + v3_sync/1, + v3_inform_sync/1, + v2_caps_i/1, + v1_proc/0, + v2_proc/0, + big_test/0, + big_test_2/0, + simple_standard_test/0, + db_notify_client_test/0, + notify/2, + multi_threaded_test/0, + mt_trap_test/1, + types_v2_test/0, + implied_test/1, + sparse_table_test/0, + cnt_64_test/1, + opaque_test/0, + api_test/1, + unreg_test/0, + load_test/0, + load_test_sa/0, + api_test2/0, + api_test3/0, + do_mul_get/0, + do_mul_get_err/0, + do_mul_next/0, + do_mul_next_err/0, + do_mul_set/0, + do_mul_set_err/0, + sa_mib/0, + ma_trap1/1, + ma_trap2/1, + ma_v2_2_v1_trap/1, + ma_v2_2_v1_trap2/1, + sa_trap1/1, + sa_trap2/1, + sa_trap3/1, + ma_v2_trap1/1, + ma_v2_trap2/1, + ma_v2_inform1/1, + ma_v2_inform2/1, + ma_v2_inform3/1, + delivery_targets/3, + delivery_info/4, + ma_v1_2_v2_trap/1, + ma_v1_2_v2_trap2/1, + sa_v1_2_v2_trap1/1, + sa_v1_2_v2_trap2/1, + sa_v1_2_v2_trap3/1, + sa_errs_bad_value/0, + sa_errs_gen_err/0, + sa_too_big/0, + next_across_sa_test/0, + undo_test/0, + bad_return/0, + standard_mib_a/0, + std_mib_read/0, + std_mib_write/0, + std_mib_init/0, + std_mib_finish/0, + standard_mib_test_finish/0, + std_mib_asn_err/0, + snmpv2_mib_test_finish/0, + std_mib_a/0, + std_mib_b/1, + std_mib_c/1, + snmpv2_mib_a/0, + snmp_community_mib_test/0, + snmp_framework_mib_test/0, + snmp_mpd_mib_a/0, + snmp_mpd_mib_b/0, + snmp_mpd_mib_c/1, + snmp_target_mib_test/0, + snmp_notification_mib_test/0, + do_set/1, + add_row/1, + del_row/1, + use_no_rights/0, + use_rights/0, + usm_add_user1/0, + usm_use_user/0, + usm_key_change1/2, + usm_key_change2/4, + usm_key_change3/4, + usm_read/0, + usm_del_user/0, + usm_bad/0, + loop_mib_1/0, + loop_mib_2/0, + otp_1129_i/1, + otp_1162_test/0, + otp_1131_test/0, + otp_1222_test/0, + otp_1298_test/0, + otp_1331_test/0, + otp_1338_test/0, + otp_1342_test/0, + otp_1366_test/0, + otp_1128_test/0, + otp_2776_test/0, + otp_2979_test/0, + otp_3542_test/0, + otp_3725_test/1, + otp_4394_test/0, + otp_7157_test/1, + otp9884_backup/4, + agent_log_validation/0, + mnesia_init/1, + mnesia_start/0, + mnesia_stop/0, + start_stdalone_agent/1, + do_info/1 + ]). -define(application, snmp). @@ -132,7 +527,9 @@ groups() -> {test_multi_threaded, [], mt_cases()}, {multiple_reqs, [], mul_cases()}, {multiple_reqs_2, [], mul_cases_2()}, + {multiple_reqs_3, [], mul_cases_3()}, {v2_inform, [], v2_inform_cases()}, + {v3_inform, [], v3_inform_cases()}, {v3_security, [], v3_security_cases()}, {standard_mibs, [], standard_mibs_cases()}, {standard_mibs_2, [], standard_mibs2_cases()}, @@ -142,8 +539,8 @@ groups() -> {reported_bugs_3, [], reported_bugs3_cases()}, {tickets1, [], tickets1_cases()}, {tickets2, [], tickets2_cases()}, - {otp_4394, [], [otp_4394_test]}, - {otp_7157, [], [otp_7157_test]} + {otp4394, [], [otp_4394]}, + {otp7157, [], [otp_7157]} ]. @@ -176,15 +573,19 @@ end_per_suite(Config) when is_list(Config) -> init_per_group(all_tcs = GroupName, Config) -> init_all(snmp_test_lib:init_group_top_dir(GroupName, Config)); -init_per_group(otp_7157 = GroupName, Config) -> - init_otp_7157(snmp_test_lib:init_group_top_dir(GroupName, Config)); -init_per_group(otp_4394 = GroupName, Config) -> - init_otp_4394(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(otp7157 = GroupName, Config) -> + otp_7157_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(otp4394 = GroupName, Config) -> + otp_4394_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); init_per_group(v2_inform = GroupName, Config) -> init_v2_inform(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(v3_inform = GroupName, Config) -> + init_v3_inform(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(multiple_reqs = GroupName, Config) -> + init_mul(snmp_test_lib:init_group_top_dir(GroupName, Config)); init_per_group(multiple_reqs_2 = GroupName, Config) -> init_mul(snmp_test_lib:init_group_top_dir(GroupName, Config)); -init_per_group(multiple_reqs = GroupName, Config) -> +init_per_group(multiple_reqs_3 = GroupName, Config) -> init_mul(snmp_test_lib:init_group_top_dir(GroupName, Config)); init_per_group(test_multi_threaded = GroupName, Config) -> init_mt(snmp_test_lib:init_group_top_dir(GroupName, Config)); @@ -224,16 +625,20 @@ init_per_group(GroupName, Config) -> end_per_group(all_tcs, Config) -> finish_all(Config); -end_per_group(otp_7157, Config) -> - finish_otp_7157(Config); -end_per_group(otp_4394, Config) -> - finish_otp_4394(Config); +end_per_group(otp7157, Config) -> + otp_7157_finish(Config); +end_per_group(otp4394, Config) -> + otp_4394_finish(Config); end_per_group(v2_inform, Config) -> - finish_v2_inform(Config); -end_per_group(multiple_reqs_2, Config) -> - finish_mul(Config); + finish_v2_inform(Config); +end_per_group(v3_inform, Config) -> + finish_v3_inform(Config); end_per_group(multiple_reqs, Config) -> finish_mul(Config); +end_per_group(multiple_reqs_2, Config) -> + finish_mul(Config); +end_per_group(multiple_reqs_3, Config) -> + finish_mul(Config); end_per_group(test_multi_threaded, Config) -> finish_mt(Config); end_per_group(test_v3, Config) -> @@ -285,7 +690,7 @@ init_per_testcase1(otp9884 = Case, Config) when is_list(Config) -> "~n Case: ~p" "~n Config: ~p", [Case, Config]), otp9884({init, init_per_testcase2(Case, Config)}); -init_per_testcase1(otp_7157_test = _Case, Config) when is_list(Config) -> +init_per_testcase1(otp_7157 = _Case, Config) when is_list(Config) -> ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), @@ -368,8 +773,8 @@ init_per_testcase2(Case, Config) -> {sub_agent_top_dir, SubAgentTopDir}, {manager_top_dir, ManagerTopDir} | Config]. -end_per_testcase2(_Case, Config) -> - Config. +%% end_per_testcase2(_Case, Config) -> +%% Config. cases() -> @@ -936,10 +1341,10 @@ varm_mib_start(Config) when is_list(Config) -> %% Perform the test(s) ?DBG("varm_mib_start -> perform the tests", []), - try_test(snmp_community_mib), - try_test(snmp_framework_mib), - try_test(snmp_target_mib), - try_test(snmp_notification_mib), + try_test(snmp_community_mib_test), + try_test(snmp_framework_mib_test), + try_test(snmp_target_mib_test), + try_test(snmp_notification_mib_test), %% Stop the agent (without deleting the stored files) ?DBG("varm_mib_start -> stop the agent", []), @@ -1253,10 +1658,10 @@ v3_cases() -> subagent_3, mnesia_3, loop_mib_3, - multiple_reqs_3, + {group, multiple_reqs_3}, sa_register_3, v3_trap, - v3_inform, + {group, v3_inform}, sa_error_3, next_across_sa_3, undo_3, @@ -1609,7 +2014,7 @@ change_target_addr_config(Config) when is_list(Config) -> dummy_manager_start(MA) -> ?DBG("dummy_manager_start -> entry",[]), - Pid = spawn(get(mgr_node), ?MODULE,dummy_manager_init,[self(),MA]), + Pid = spawn(get(mgr_node), ?MODULE, dummy_manager_init, [self(), MA]), ?DBG("dummy_manager_start -> Pid: ~p",[Pid]), await_dummy_manager_started(Pid). @@ -1807,12 +2212,13 @@ mul_cases() -> mul_get_err, mul_next, mul_next_err, + mul_set, mul_set_err ]. -multiple_reqs_3(_X) -> - {req, [], {conf, init_mul, mul_cases_3(), finish_mul}}. +%% multiple_reqs_3(_X) -> +%% {req, [], {conf, init_mul, mul_cases_3(), finish_mul}}. mul_cases_2() -> @@ -1821,6 +2227,7 @@ mul_cases_2() -> mul_get_err_2, mul_next_2, mul_next_err_2, + mul_set_2, mul_set_err_2 ]. @@ -1831,6 +2238,7 @@ mul_cases_3() -> mul_get_err_3, mul_next_3, mul_next_err_3, + mul_set_3, mul_set_err_3 ]. @@ -2054,26 +2462,28 @@ v3_trap(Config) when is_list(Config) -> trap2(Config). -v3_inform(_X) -> - {req, [], {conf, init_v3_inform, [v3_inform_i], finish_v3_inform}}. +v3_inform_cases() -> + [ + v3_inform_i + ]. + +init_v3_inform(X) -> + init_v2_inform(X). + +finish_v3_inform(X) -> + finish_v2_inform(X). + init_v2_inform(Config) when is_list(Config) -> _Dir = ?config(agent_conf_dir, Config), %% snmp_internal_mib:configure(Dir), Config. -init_v3_inform(X) -> - init_v2_inform(X). - finish_v2_inform(Config) when is_list(Config) -> _Dir = ?config(agent_conf_dir, Config), %% snmp_internal_mib:configure(Dir), Config. -finish_v3_inform(X) -> - finish_v2_inform(X). - - v2_inform_cases() -> [ v2_inform_i @@ -2177,7 +2587,7 @@ next_across_sa(Config) when is_list(Config) -> try_test(load_test_sa), ?P1("Testing next across subagent (endOfMibView from SA)..."), - try_test(next_across_sa), + try_test(next_across_sa_test), ?P1("Unloading mib (Klas1)"), snmpa:unload_mibs(SA, [MibDir ++ "Klas1"]), @@ -2187,7 +2597,7 @@ next_across_sa(Config) when is_list(Config) -> ?P1("Starting another subagent (2) "), ?line {ok, SA2} = start_subagent(SaNode, ?klas1, "Klas1"), ?P1("Testing next across subagent (wrong prefix from SA)..."), - try_test(next_across_sa), + try_test(next_across_sa_test), ?P1("stop subagent (1)..."), stop_subagent(SA), @@ -2871,7 +3281,7 @@ table_test() -> ?line ?expect1([{NewKeyc5, ?destroy}]), s([{NewKeyc3, 3}]), ?line ?expect3(?v1_2(noSuchName, inconsistentName), 1, [{NewKeyc3, 3}]), - otp_1128(). + otp_1128_test(). %% Req. system group simple_standard_test() -> @@ -2971,6 +3381,7 @@ db_notify_client_test() -> s([{[sysLocation, 0], "new_value"}]), ?line ?expect1([{[sysLocation, 0], "new_value"}]). +%% Callback function notify(Pid, What) -> ?DBG("notify(~p,~p) -> called",[Pid,What]), Pid ! {db_notify_test_reply, What}. @@ -3019,7 +3430,7 @@ big_test() -> s([{[friendsEntry, [3, 3]], i, ?destroy}]), ?line ?expect1([{[friendsEntry, [3, 3]], ?destroy}]), - otp_1131(), + otp_1131_test(), ?DBG("big_test -> adding two rows in subagent table with special INDEX", []), @@ -3065,7 +3476,7 @@ big_test_2() -> g([[fname2,0]]), ?line ?expect1([{[fname2,0], "test set"}]), - otp_1298(), + otp_1298_test(), ?P1("Testing next from last object in master to subagent (2)..."), gn([[?v1_2(sysServices, sysORLastChange),0]]), @@ -3986,7 +4397,8 @@ ma_v2_inform3(MA) -> command_handler(Commands). - + +%% snmpa_notification_delivery_info_receiver callback function delivery_targets(Tag, Addresses, Extra) -> io:format("~w:delivery_targets -> entry with" "~n Tag: ~p" @@ -4003,6 +4415,7 @@ delivery_targets(Tag, Addresses, Extra) -> end, ok. +%% snmpa_notification_delivery_info_receiver callback function delivery_info(Tag, Address, DeliveryResult, Extra) -> io:format("~w:delivery_info -> entry with" "~n Tag: ~p" @@ -4132,7 +4545,7 @@ sa_too_big() -> ?line ?expect1(tooBig). %% Req. Klas1, system group, snmp group (v1/v2) -next_across_sa() -> +next_across_sa_test() -> gn([[sysDescr],[klas1,5]]), ?line ?expect1([{[sysDescr,0], "Erlang SNMP agent"}, {[snmpInPkts, 0], any}]). @@ -4450,13 +4863,13 @@ snmp_community_mib(Config) when is_list(Config) -> ?P(snmp_community_mib), init_case(Config), ?line load_master_std("SNMP-COMMUNITY-MIB"), - try_test(snmp_community_mib), + try_test(snmp_community_mib_test), ?line unload_master("SNMP-COMMUNITY-MIB"). snmp_community_mib_2(X) -> ?P(snmp_community_mib_2), snmp_community_mib(X). %% Req. SNMP-COMMUNITY-MIB -snmp_community_mib() -> +snmp_community_mib_test() -> ?INF("NOT YET IMPLEMENTED", []), nyi. @@ -4468,7 +4881,7 @@ snmp_framework_mib(Config) when is_list(Config) -> ?P(snmp_framework_mib), init_case(Config), ?line load_master_std("SNMP-FRAMEWORK-MIB"), - try_test(snmp_framework_mib), + try_test(snmp_framework_mib_test), ?line unload_master("SNMP-FRAMEWORK-MIB"). snmp_framework_mib_2(X) -> ?P(snmp_framework_mib_2), snmp_framework_mib(X). @@ -4477,11 +4890,11 @@ snmp_framework_mib_3(suite) -> []; snmp_framework_mib_3(Config) when is_list(Config) -> ?P(snmp_framework_mib_3), init_case(Config), - try_test(snmp_framework_mib). + try_test(snmp_framework_mib_test). %% Req. SNMP-FRAMEWORK-MIB -snmp_framework_mib() -> +snmp_framework_mib_test() -> ?line ["agentEngine"] = get_req(1, [[snmpEngineID,0]]), ?line [EngineTime] = get_req(2, [[snmpEngineTime,0]]), ?SLEEP(5000), @@ -4580,14 +4993,14 @@ snmp_target_mib(Config) when is_list(Config) -> ?P(snmp_target_mib), init_case(Config), ?line load_master_std("SNMP-TARGET-MIB"), - try_test(snmp_target_mib), + try_test(snmp_target_mib_test), ?line unload_master("SNMP-TARGET-MIB"). snmp_target_mib_2(X) -> ?P(snmp_target_mib_2), snmp_target_mib(X). snmp_target_mib_3(X) -> ?P(snmp_target_mib_3), snmp_target_mib(X). -snmp_target_mib() -> +snmp_target_mib_test() -> ?INF("NOT YET IMPLEMENTED", []), nyi. @@ -4596,7 +5009,7 @@ snmp_notification_mib(Config) when is_list(Config) -> ?P(snmp_notification_mib), init_case(Config), ?line load_master_std("SNMP-NOTIFICATION-MIB"), - try_test(snmp_notification_mib), + try_test(snmp_notification_mib_test), ?line unload_master("SNMP-NOTIFICATION-MIB"). snmp_notification_mib_2(X) -> ?P(snmp_notification_mib_2), @@ -4605,7 +5018,7 @@ snmp_notification_mib_2(X) -> ?P(snmp_notification_mib_2), snmp_notification_mib_3(X) -> ?P(snmp_notification_mib_3), snmp_notification_mib(X). -snmp_notification_mib() -> +snmp_notification_mib_test() -> ?INF("NOT YET IMPLEMENTED", []), nyi. @@ -4881,15 +5294,15 @@ snmp_user_based_sm_mib_3(Config) when is_list(Config) -> %% Try some read requests ?line try_test(v3_sync, [[{usm_read, []}]], - [{sec_level, authPriv}, {user, "privDES"}]), + [{sec_level, authPriv}, {user, "privDES"}]), %% Delete the new user ?line try_test(v3_sync, [[{usm_del_user, []}]], - [{sec_level, authPriv}, {user, "privDES"}]), + [{sec_level, authPriv}, {user, "privDES"}]), %% Try some bad requests ?line try_test(v3_sync, [[{usm_bad, []}]], - [{sec_level, authPriv}, {user, "privDES"}]), + [{sec_level, authPriv}, {user, "privDES"}]), ?line unload_master("SNMP-USER-BASED-SM-MIB"). @@ -5278,6 +5691,7 @@ reported_bugs_cases() -> otp_1331, otp_1338, otp_1342, + otp_1366, otp_2776, otp_2979, otp_3187, @@ -5295,6 +5709,7 @@ reported_bugs2_cases() -> otp_1331_2, otp_1338_2, otp_1342_2, + otp_1366_2, otp_2776_2, otp_2979_2, otp_3187_2 @@ -5311,6 +5726,7 @@ reported_bugs3_cases() -> otp_1331_3, otp_1338_3, otp_1342_3, + otp_1366_3, otp_2776_3, otp_2979_3, otp_3187_3, @@ -5329,14 +5745,14 @@ otp_1128(Config) when is_list(Config) -> ?line load_master("OLD-SNMPEA-MIB"), ?line init_old(), - try_test(otp_1128), + try_test(otp_1128_test), ?line unload_master("OLD-SNMPEA-MIB"). otp_1128_2(X) -> ?P(otp_1128_2), otp_1128(X). otp_1128_3(X) -> ?P(otp_1128_3), otp_1128(X). -otp_1128() -> +otp_1128_test() -> io:format("Testing bug reported in ticket OTP-1128...~n"), NewKeyc3 = [intCommunityViewIndex,get(mip),is("test")], @@ -5354,6 +5770,7 @@ otp_1128() -> s([{NewKeyc5, ?destroy}]), ?line ?expect1([{NewKeyc5, ?destroy}]). + %%----------------------------------------------------------------- %% Ticket: OTP-1129, OTP-1169 %% Slogan: snmpa:int_to_enum crashes on bad oids @@ -5388,7 +5805,7 @@ otp_1131(Config) when is_list(Config) -> init_case(Config), ?line load_master("Klas1"), - try_test(otp_1131), + try_test(otp_1131_test), ?line unload_master("Klas1"). otp_1131_2(X) -> ?P(otp_1131_2), otp_1131(X). @@ -5431,7 +5848,7 @@ otp_1131_3(X) -> ?P(otp_1131_3), otp_1131(X). -otp_1131() -> +otp_1131_test() -> io:format("Testing bug reported in ticket OTP-1131...~n"), s([{[friendsEntry, [2, 3, 1]], s, "kompis3"}, {[friendsEntry, [3, 3, 1]], i, ?createAndGo}]), @@ -5447,14 +5864,14 @@ otp_1162(Config) when is_list(Config) -> ?P(otp_1162), {SaNode, _MgrNode, _MibDir} = init_case(Config), ?line {ok, SA} = start_subagent(SaNode, ?sa, "SA-MIB"), - try_test(otp_1162), + try_test(otp_1162_test), stop_subagent(SA). otp_1162_2(X) -> ?P(otp_1162_2), otp_1162(X). otp_1162_3(X) -> ?P(otp_1162_3), otp_1162(X). -otp_1162() -> +otp_1162_test() -> s([{[sa, [2,0]], 6}]), % wrongValue (i is_set_ok) ?line ?expect3(?v1_2(badValue, wrongValue), 1, any). @@ -5469,7 +5886,7 @@ otp_1222(Config) when is_list(Config) -> init_case(Config), ?line load_master("Klas3"), ?line load_master("Klas4"), - try_test(otp_1222), + try_test(otp_1222_test), ?line unload_master("Klas3"), ?line unload_master("Klas4"). @@ -5477,13 +5894,14 @@ otp_1222_2(X) -> ?P(otp_1222_2), otp_1222(X). otp_1222_3(X) -> ?P(otp_1222_3), otp_1222(X). -otp_1222() -> +otp_1222_test() -> io:format("Testing bug reported in ticket OTP-1222...~n"), s([{[fStatus4,1], 4}, {[fName4,1], 1}]), ?line ?expect3(genErr, 0, any), s([{[fStatus4,2], 4}, {[fName4,2], 1}]), ?line ?expect3(genErr, 0, any). + %%----------------------------------------------------------------- %% Ticket: OTP-1298 %% Slogan: Negative INTEGER values are treated as positive. @@ -5494,14 +5912,14 @@ otp_1298(Config) when is_list(Config) -> init_case(Config), ?line load_master("Klas2"), - try_test(otp_1298), + try_test(otp_1298_test), ?line unload_master("Klas2"). otp_1298_2(X) -> ?P(otp_1298_2), otp_1298(X). otp_1298_3(X) -> ?P(otp_1298_3), otp_1298(X). -otp_1298() -> +otp_1298_test() -> io:format("Testing bug reported in ticket OTP-1298...~n"), s([{[fint,0], -1}]), ?line ?expect1([{[fint,0], -1}]). @@ -5517,14 +5935,14 @@ otp_1331(Config) when is_list(Config) -> init_case(Config), ?line load_master("OLD-SNMPEA-MIB"), ?line init_old(), - try_test(otp_1331), + try_test(otp_1331_test), ?line unload_master("OLD-SNMPEA-MIB"). otp_1331_2(X) -> ?P(otp_1331_2), otp_1331(X). otp_1331_3(X) -> ?P(otp_1331_3), otp_1331(X). -otp_1331() -> +otp_1331_test() -> NewKeyc5 = [intCommunityStatus,[127,32,0,0],is("test")], s([{NewKeyc5, ?destroy}]), ?line ?expect1([{NewKeyc5, ?destroy}]). @@ -5540,19 +5958,20 @@ otp_1338(Config) when is_list(Config) -> init_case(Config), ?line load_master("Klas2"), - try_test(otp_1338), + try_test(otp_1338_test), ?line unload_master("Klas2"). otp_1338_2(X) -> ?P(otp_1338_2), otp_1338(X). otp_1338_3(X) -> ?P(otp_1338_3), otp_1338(X). -otp_1338() -> +otp_1338_test() -> s([{[kStatus2, 7], i, ?createAndGo}]), ?line ?expect1([{[kStatus2, 7], ?createAndGo}]), g([[kName2, 7]]), ?line ?expect1([{[kName2, 7], "JJJ"}]). + %%----------------------------------------------------------------- %% Ticket: OTP-1342 %% Slogan: default impl of snmp table can't handle bad index access, @@ -5563,14 +5982,14 @@ otp_1342(Config) when is_list(Config) -> ?P(otp_1342), init_case(Config), ?line load_master("Klas4"), - try_test(otp_1342), + try_test(otp_1342_test), ?line unload_master("Klas4"). otp_1342_2(X) -> ?P(otp_1342_2), otp_1342(X). otp_1342_3(X) -> ?P(otp_1342_3), otp_1342(X). -otp_1342() -> +otp_1342_test() -> s([{[fIndex5, 1], i, 1}, {[fName5, 1], i, 3}, {[fStatus5, 1], i, ?createAndGo}]), @@ -5589,17 +6008,18 @@ otp_1366(Config) when is_list(Config) -> init_case(Config), ?line load_master("OLD-SNMPEA-MIB"), ?line init_old(), - try_test(otp_1366), + try_test(otp_1366_test), ?line unload_master("OLD-SNMPEA-MIB"). otp_1366_2(X) -> ?P(otp_1366_2), otp_1366(X). otp_1366_3(X) -> ?P(otp_1366_3), otp_1366(X). -otp_1366() -> +otp_1366_test() -> ?INF("NOT YET IMPLEMENTED", []), 'NYI'. + %%----------------------------------------------------------------- %% Ticket: OTP-2776 %% Slogan: snmp:validate_date_and_time() fails when time is 00:00 @@ -5608,13 +6028,13 @@ otp_2776(suite) -> []; otp_2776(Config) when is_list(Config) -> ?P(otp_2776), init_case(Config), - try_test(otp_2776). + try_test(otp_2776_test). otp_2776_2(X) -> ?P(otp_2776_2), otp_2776(X). otp_2776_3(X) -> ?P(otp_2776_3), otp_2776(X). -otp_2776() -> +otp_2776_test() -> io:format("Testing bug reported in ticket OTP-2776...~n"), Dt01_valid = [19,98,9,1,1,0,23,0,43,0,0], @@ -5677,18 +6097,19 @@ otp_2979(Config) when is_list(Config) -> init_case(Config), ?line load_master("Test1"), ?line init_old(), - try_test(otp_2979), + try_test(otp_2979_test), ?line unload_master("Test1"). otp_2979_2(X) -> ?P(otp_2979_2), otp_2979(X). otp_2979_3(X) -> ?P(otp_2979_3), otp_2979(X). -otp_2979() -> +otp_2979_test() -> gn([[sparseDescr], [sparseStatus]]), ?line ?expect1([{[sparseStr,0], "slut"}, {[sparseStr,0], "slut"}]). + %%----------------------------------------------------------------- %% Ticket: OTP-3187 %% Slogan: get-next on vacmAccessTable for colums > 5 returns @@ -5699,14 +6120,14 @@ otp_3187(Config) when is_list(Config) -> ?P(otp_3187), init_case(Config), ?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"), - otp_3187(), + otp_3187_test(), ?line unload_master("SNMP-VIEW-BASED-ACM-MIB"). otp_3187_2(X) -> ?P(otp_3187_2), otp_3187(X). otp_3187_3(X) -> ?P(otp_3187_3), otp_3187(X). -otp_3187() -> +otp_3187_test() -> ?line Elements = snmp_view_based_acm_mib:vacmAccessTable(get_next,[],[4,5,6]), lists:foreach(fun(E) -> @@ -5724,9 +6145,9 @@ otp_3542(suite) -> []; otp_3542(Config) when is_list(Config) -> ?P(otp_3542), init_case(Config), - try_test(otp_3542). + try_test(otp_3542_test). -otp_3542() -> +otp_3542_test() -> io:format("SNMP v3 discovery...~n"), ?line Res = snmp_test_mgr:d(), io:format("SNMP v3 discovery result: ~p~n",[Res]). @@ -5797,13 +6218,13 @@ otp_3725_test(MaNode) -> tickets1_cases() -> [ - {group, otp_4394}, - {group, otp_7157} + {group, otp4394}, + {group, otp7157} ]. -init_otp_4394(Config) when is_list(Config) -> - ?DBG("init_otp_4394 -> entry with" +otp_4394_init(Config) when is_list(Config) -> + ?DBG("otp_4394_init -> entry with" "~n Config: ~p", [Config]), ?line AgentConfDir = ?config(agent_conf_dir, Config), ?line MgrDir = ?config(mgr_dir, Config), @@ -5856,35 +6277,35 @@ otp_4394_config(AgentConfDir, MgrDir, Ip0) -> -finish_otp_4394(Config) when is_list(Config) -> +otp_4394_finish(Config) when is_list(Config) -> ?DBG("finish_otp_4394 -> entry", []), C1 = stop_agent(Config), delete_files(C1), erase(mgr_node), lists:keydelete(vsn, 1, C1). -otp_4394_test(suite) -> []; -otp_4394_test(Config) -> - ?P(otp_4394_test), - ?DBG("otp_4394_test -> entry", []), +otp_4394(suite) -> []; +otp_4394(Config) -> + ?P(otp_4394), + ?DBG("otp_4394 -> entry", []), init_case(Config), - try_test(otp_4394_test1), - ?DBG("otp_4394_test -> done", []), + try_test(otp_4394_test), + ?DBG("otp_4394 -> done", []), ok. -otp_4394_test1() -> - ?DBG("otp_4394_test1 -> entry", []), +otp_4394_test() -> + ?DBG("otp_4394_test -> entry", []), gn([[1,1]]), Res = case snmp_test_mgr:expect(1, [{[sysDescr,0], "Erlang SNMP agent"}]) of %% {error, 1, {"?",[]}, {"~w",[timeout]}} {error, 1, _, {_, [timeout]}} -> - ?DBG("otp_4394_test1 -> expected result: timeout", []), + ?DBG("otp_4394_test -> expected result: timeout", []), ok; Else -> Else end, - ?DBG("otp_4394_test1 -> done with: ~p", [Res]), + ?DBG("otp_4394_test -> done with: ~p", [Res]), Res. @@ -5895,7 +6316,7 @@ otp_4394_test1() -> -init_otp_7157(Config) when is_list(Config) -> +otp_7157_init(Config) when is_list(Config) -> %% Skippable = [win32], Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, @@ -5915,30 +6336,30 @@ init_otp_7157(Config) when is_list(Config) -> [{vsn, v2} | start_v2_agent(Config, Opts)]. -finish_otp_7157(Config) when is_list(Config) -> +otp_7157_finish(Config) when is_list(Config) -> ?DBG("finish_otp_7157 -> entry", []), C1 = stop_agent(Config), delete_files(C1), erase(mgr_node), lists:keydelete(vsn, 1, C1). -otp_7157_test(suite) -> []; -otp_7157_test(Config) -> - ?P(otp_7157_test), - ?DBG("otp_7157_test -> entry", []), +otp_7157(suite) -> []; +otp_7157(Config) -> + ?P(otp_7157), + ?DBG("otp_7157 -> entry", []), init_case(Config), MA = whereis(snmp_master_agent), ?line load_master("Test1"), - try_test(otp_7157_test1, [MA]), + try_test(otp_7157_test, [MA]), ?line unload_master("Test1"), - ?DBG("otp_7157_test -> done", []), + ?DBG("otp_7157 -> done", []), ok. %% ts:run(snmp, snmp_agent_test, [batch]). -otp_7157_test1(MA) -> - ?LOG("start otp_7157_test1 test (~p)",[MA]), +otp_7157_test(MA) -> + ?LOG("start otp_7157_test test (~p)",[MA]), snmpa:verbosity(MA, trace), - ?LOG("start otp_7157_test1 test",[]), + ?LOG("start otp_7157_test test",[]), ?P1("Testing that varbinds in traps/notifications are not reordered"), ?DBG("send cntTrap",[]), @@ -6427,13 +6848,13 @@ process_options(Defaults, _Opts) -> %% {value, {Key, Value}} when is_list-> -snmp_app_env_init(Node, Entity, Conf) -> - rpc:call(Node, snmp_app_env_init, [Entity, Conf]). +%% snmp_app_env_init(Node, Entity, Conf) -> +%% rpc:call(Node, snmp_app_env_init, [Entity, Conf]). -snmp_app_env_init(Entity, Conf) -> - application:unload(snmp), - application:load(snmp), - application:set_env(snmp, Entity, Conf). +%% snmp_app_env_init(Entity, Conf) -> +%% application:unload(snmp), +%% application:load(snmp), +%% application:set_env(snmp, Entity, Conf). start_stdalone_agent(Node, Config) -> rpc:call(Node, ?MODULE, start_stdalone_agent, [Config]). @@ -6494,10 +6915,10 @@ info_test(Config) when is_list(Config) -> ?line load_master("OLD-SNMPEA-MIB"), ?line init_old(), - try_test(info_test1, [node()]), + try_test(do_info, [node()]), ?line unload_master("OLD-SNMPEA-MIB"). -info_test1(MaNode) -> +do_info(MaNode) -> ?line Info = rpc:call(MaNode, snmpa, info, []), ?DBG("info_test1 -> Info: ~n~p", [Info]), Keys = [vsns, @@ -6573,7 +6994,7 @@ verify_old_info([Key|Keys], Info) -> ?FAIL({missing_old_info, Key}) end. -%% string used in index +%% Index String - string used in index is(S) -> [length(S) | S]. try_test(Func) -> @@ -6591,16 +7012,11 @@ try_test(Func, A, Opts) -> %% Test manager wrapperfunctions: g(Oids) -> snmp_test_mgr:g(Oids). -gn() -> snmp_test_mgr:gn(). +%%gn() -> snmp_test_mgr:gn(). gn(OidsOrN) -> snmp_test_mgr:gn(OidsOrN). gb(NR, MR, Oids) -> snmp_test_mgr:gb(NR, MR, Oids). s(VAV) -> snmp_test_mgr:s(VAV). -%% expect(A, B) -> snmp_agent_test_lib:expect(A, B). -%% expect(A, B, C) -> snmp_agent_test_lib:expect(A, B, C). -%% expect(A, B, C, D) -> snmp_agent_test_lib:expect(A, B, C, D). -%% expect(A, B, C, D, E, F) -> snmp_agent_test_lib:expect(A, B, C, D, E, F). - get_req(Id, Vars) -> snmp_agent_test_lib:get_req(Id, Vars). @@ -6636,8 +7052,8 @@ rewrite_usm_mgr(Dir, ShaKey, DesKey) -> reset_usm_mgr(Dir) -> snmp_agent_test_lib:reset_usm_mgr(Dir). -update_community(Vsns, DIr) -> - snmp_agent_test_lib:update_community(Vsns, DIr). +%% update_community(Vsns, Dir) -> +%% snmp_agent_test_lib:update_community(Vsns, Dir). update_vacm(Vsn, Dir) -> snmp_agent_test_lib:update_vacm(Vsn, Dir). @@ -6670,8 +7086,8 @@ reset_target_params_conf(Dir) -> write_notify_conf(Dir) -> snmp_agent_test_lib:write_notify_conf(Dir). -write_view_conf(Dir) -> - snmp_agent_test_lib:write_view_conf(Dir). +%% write_view_conf(Dir) -> +%% snmp_agent_test_lib:write_view_conf(Dir). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -6833,8 +7249,8 @@ lists_key1search(Key, List) when is_atom(Key) -> end. -regs() -> - lists:sort(registered()). +%% regs() -> +%% lists:sort(registered()). %% ------ -- cgit v1.2.3 From 9be1fbb32ef0bbdf99c7c39fac4a845606156a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 31 May 2013 12:44:03 +0200 Subject: testSeqOf: Test constrained, extensible sizes --- lib/asn1/test/asn1_SUITE_data/SeqOf.asn1 | 9 +++++++++ lib/asn1/test/testSeqOf.erl | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/asn1/test/asn1_SUITE_data/SeqOf.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqOf.asn1 index 330944cf5c..888dbe5dd7 100644 --- a/lib/asn1/test/asn1_SUITE_data/SeqOf.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/SeqOf.asn1 @@ -62,4 +62,13 @@ Empty ::= SEQUENCE { } +SeqExt ::= SEQUENCE +{ + b1 BOOLEAN, + s1 SEQUENCE SIZE (1..3, ...) OF SeqIn, + b2 BOOLEAN, + s2 SEQUENCE SIZE (0..1024, ...) OF SeqIn, + magic INTEGER +} + END diff --git a/lib/asn1/test/testSeqOf.erl b/lib/asn1/test/testSeqOf.erl index 771045f9af..c1af0d7a32 100644 --- a/lib/asn1/test/testSeqOf.erl +++ b/lib/asn1/test/testSeqOf.erl @@ -86,6 +86,29 @@ main(_Rules) -> roundtrip('SeqEmp', #'SeqEmp'{seq1=[#'Empty'{}]}), + %% Test constrained, extensible size. + + SeqIn = #'SeqIn'{boolIn=true,intIn=978654321}, + roundtrip('SeqExt', {'SeqExt',true,[],true,[],789}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(1, SeqIn), + true,lists:duplicate(0, SeqIn),777}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(1, SeqIn), + true,lists:duplicate(1, SeqIn),777}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(1, SeqIn), + true,lists:duplicate(127, SeqIn),777}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(2, SeqIn), + true,lists:duplicate(128, SeqIn),1777}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(2, SeqIn), + true,lists:duplicate(255, SeqIn),7773}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(2, SeqIn), + true,lists:duplicate(256, SeqIn),77755}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(2, SeqIn), + true,lists:duplicate(257, SeqIn),8888}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(3, SeqIn), + true,lists:duplicate(1024, SeqIn),999988888}), + roundtrip('SeqExt', {'SeqExt',true,lists:duplicate(15, SeqIn), + true,lists:duplicate(2000, SeqIn),555555}), + %% Test OTP-4590: correct encoding of the length of SEQUENC OF. DayNames = ["Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday","Sunday"], -- cgit v1.2.3 From 9b04c2649ce98c32335c05e90d501c2e66bb2f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 3 Jun 2013 12:08:45 +0200 Subject: beam_lib: Correct wrong type specification The function was updated in 5805b576, but not the type specificatin. --- lib/stdlib/src/beam_lib.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 2e9ebece0e..121f9febed 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -300,7 +300,7 @@ clear_crypto_key_fun() -> call_crypto_server(clear_crypto_key_fun). -spec make_crypto_key(mode(), string()) -> - {binary(), binary(), binary(), binary()}. + {mode(), [binary()], binary(), integer()}. make_crypto_key(des3_cbc=Type, String) -> <> = First = erlang:md5(String), -- cgit v1.2.3