%% 
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1998-2024. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%% 
-module(snmp_target_mib).
-moduledoc """
Instrumentation Functions for SNMP-TARGET-MIB

The module `snmp_target_mib` implements the instrumentation functions for the
SNMP-TARGET-MIB, and functions for configuring the database.

The configuration files are described in the SNMP User's Manual.

Legacy API functions [`add_addr/10`](`add_addr/10`) that does not specify
transport domain, and `add_addr/11` that has got separate `IpAddr` and
`PortNumber` arguments still work as before for backwards compatibility reasons.

## DATA TYPES

See the [data types in `snmpa_conf`](`m:snmpa_conf#types`).
""".

%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1,
	 snmpTargetSpinLock/1, snmpTargetSpinLock/2,
	 snmpTargetAddrTable/1, snmpTargetAddrTable/3,
	 snmpTargetParamsTable/1, snmpTargetParamsTable/3,
	 get_target_addrs/0, get_target_engine_id/1, set_target_engine_id/2,
	 is_valid_tag/3, get/3, table_next/2]).
-export([add_addr/10, add_addr/11, delete_addr/1,
	 add_params/5, delete_params/1]).
-export([check_target_addr/1, check_target_params/1]).
-export([default_domain/0]).

-export_type([
              name/0,
              tag_value/0,
              retry_count/0,
              tag_list/0,
              params/0,
              tmask/0,
              mms/0
             ]).

-include_lib("snmp/include/snmp_types.hrl").
-include_lib("snmp/include/snmp_tables.hrl").
-include_lib("snmp/include/SNMP-TARGET-MIB.hrl").
-include_lib("snmp/include/SNMPv2-TC.hrl").
-include_lib("snmp/include/SNMPv2-TM.hrl").
-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl").
-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl").

-define(VMODULE,"TARGET-MIB").
-include("snmp_verbosity.hrl").
-include("snmpa_internal.hrl").


%% *** name ***
-doc """
> #### Note {: .info }
>
> "The locally arbitrary, but unique identifier associated with this
> snmpTargetAddrEntry."

`SnmpAdminString (SIZE(1..32))`
""".
-type name()             :: snmp_framework_mib:admin_string().

%% *** tag_value ***
-doc """
> #### Note {: .info }
>
> "An octet string containing a tag value. Tag values are preferably in
> human-readable form."

`OCTET STRING (SIZE (0..255))`
""".
-type tag_value()        :: string().

%% *** retry_count ***
-doc """
> #### Note {: .info }
>
> "This object specifies a default number of retries to be attempted when a
> response is not received for a generated message."

`Integer32 (0..255)`
""".
-type retry_count()      :: 0 .. 255.

%% *** tag_list ***
-doc """
> #### Note {: .info }
>
> "An octet string containing a list of tag values. Tag values are preferably in
> human-readable form."
>
> "To facilitate internationalization, this information is represented using the
> ISO/IEC IS 10646-1 character set, encoded as an octet string using the UTF-8
> character encoding scheme described in RFC 2279."

`OCTET STRING (SIZE (0..255))`
""".
-type tag_list()         :: string().

%% *** params ***
-doc """
> #### Note {: .info }
>
> "The value of this object identifies an entry in the snmpTargetParamsTable."

`SnmpAdminString (SIZE(1..32))`
""".
-type params()           :: snmp_framework_mib:admin_string().

%% *** tmask ***
-doc """
> #### Note {: .info }
>
> "The mask value associated with an entry in the snmpTargetAddrTable. The value
> of this object must have the same length as the corresponding instance of
> snmpTargetAddrTAddress, or must have length 0."

`OCTET STRING (SIZE (0..255))`
""".
-type tmask()            :: snmpa_conf:transportAddressMask().

%% *** mms ***
-doc """
> #### Note {: .info }
>
> "The maximum message size value associated with an entry in the
> snmpTargetAddrTable.".

`Integer32 (484..65535)`
""".
-type mms()              :: 484 .. 65535. % Actually defined in COMMUNITY-MIB


%% Column not accessible via SNMP - needed when the agent sends informs
-define(snmpTargetAddrEngineId, 10).
%% Extra comlumns for the augmented table snmpTargetAddrExtTable
-define(snmpTargetAddrTMask,    11).
-define(snmpTargetAddrMMS,      12).

-ifdef(snmp_extended_verbosity).
-define(vt(F,A), ?vtrace(F, A)).
-else.
-define(vt(_F, _A), ok).
-endif.


%%-----------------------------------------------------------------

-doc false.
default_domain() ->
    snmpUDPDomain.


%%-----------------------------------------------------------------
%% Func: configure/1
%% Args: Dir is the directory where the configuration files are found.
%% Purpose: If the tables doesn't exist, this function reads
%%          the config-files for the target tables, and
%%          inserts the data.  This means that the data in the tables
%%          survive a reboot.  However, the StorageType column is
%%          checked for each row.  If volatile, the row is deleted.
%% Returns: ok
%% Fails: exit(configuration_error)
%%-----------------------------------------------------------------

-doc """
This function is called from the supervisor at system start-up.

Inserts all data in the configuration files into the database and destroys all
old rows with StorageType `volatile`. The rows created from the configuration
file will have StorageType `nonVolatile`.

All `snmp` counters are set to zero.

If an error is found in the configuration file, it is reported using the
function `config_err/2` of the error report module, and the function fails with
the reason `configuration_error`.

`ConfDir` is a string which points to the directory where the configuration
files are found.

The configuration files read are: `target_addr.conf` and `target_params.conf`.

""".
-spec configure(ConfDir) -> snmp:void() when
      ConfDir :: string().

configure(ConfDir) ->
    set_sname(),
    case db(snmpTargetParamsTable) of
        {_, mnesia} ->
            ?vdebug("tables in mnesia: init vars & cleanup",[]),
            init_vars(),
            gc_tabs();
	TabDb ->
	    case snmpa_local_db:table_exists(TabDb) of
		true ->
		    ?vdebug("tables already exist: init vars & cleanup",[]),
		    init_vars(),
		    gc_tabs();
		false ->
		    ?vdebug("no tables: reconfigure",[]),
		    reconfigure(ConfDir)
	    end
    end.


%%-----------------------------------------------------------------
%% Func: reconfigure/1
%% Args: Dir is the directory where the configuration files are found.
%% Purpose: Reads the config-files for the target tables, and
%%          inserts the data.  Makes sure that all old data in
%%          the tables are deleted, and the new data inserted.
%%          This function makes sure that all (and only) 
%%          config-file-data are in the tables. 
%% Returns: ok
%% Fails: exit(configuration_error)
%%-----------------------------------------------------------------

-doc """
Inserts all data in the configuration files into the database and destroys all
old data, including the rows with StorageType `nonVolatile`. The rows created
from the configuration file will have StorageType `nonVolatile`.

Thus, the data in the SNMP-TARGET-MIB, after this function has been called, is
the data from the configuration files.

All `snmp` counters are set to zero.

If an error is found in the configuration file, it is reported using the
function `config_err/2` of the , and the function fails with the reason
`configuration_error`.

`ConfDir` is a string which points to the directory where the configuration
files are found.

The configuration files read are: `target_addr.conf` and `target_params.conf`.

""".
-spec reconfigure(ConfDir) -> snmp:void() when
      ConfDir :: string().

reconfigure(ConfDir) ->
    set_sname(),
    case (catch do_reconfigure(ConfDir)) of
	ok ->
	    ok;
	{error, Reason} ->
	    ?vinfo("reconfigure error: ~p", [Reason]),
	    config_err("reconfigure failed: ~p", [Reason]),
	    exit(configuration_error);
	Error ->
	    ?vinfo("reconfigure failed: ~p", [Error]),
	    config_err("reconfigure failed: ~p", [Error]),
	    exit(configuration_error)
    end.

do_reconfigure(Dir) ->
    ?vdebug("read target configuration files",[]),
    {Addrs, Params} = read_target_config_files(Dir),
    ?vdebug("initiate tables",[]),
    init_tabs(Addrs, Params),
    ?vdebug("initiate vars",[]),
    init_vars(),
    ?vdebug("invalidate cache for notification mib",[]),
    snmp_notification_mib:invalidate_cache(),
    ok.


read_target_config_files(Dir) ->
    ?vdebug("check target address and parameter config file(s)",[]),

    TAName   = "target_addr.conf",
    TACheck  = fun (Entry, State) -> {check_target_addr(Entry), State} end,

    TPName   = "target_params.conf",
    TPCheck  = fun (Entry, State) -> {check_target_params(Entry), State} end,

    NoGen    = fun snmp_conf:no_gen/2,
    NoOrder  = fun snmp_conf:no_order/2,
    NoFilter = fun snmp_conf:no_filter/1,

    [Addrs, Params] = 
        snmp_conf:read_files(
	  Dir,
	  [{TAName, NoGen, NoOrder, TACheck, NoFilter},
	   {TPName, NoGen, NoOrder, TPCheck, NoFilter}]),
    {Addrs, Params}.


%%-----------------------------------------------------------------
%%  TargetAddr
%%  {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId,
%%   TMask, MMS}
%%-----------------------------------------------------------------

-doc false.
check_target_addr(
  {Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, Params,
   EngineId, TMask, MMS}) -> % Arity 11
    Address = {Ip, Udp},
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      EngineId, TMask, MMS);
check_target_addr(
  {Name, Domain, Address, Timeout, RetryCount, TagList, Params,
   EngineId, TMask, MMS}) % Arity 10
  when is_atom(Domain) ->
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      EngineId, TMask, MMS);
check_target_addr(
  {Name, Ip, Udp, Timeout, RetryCount, TagList, Params,
   EngineId, TMask, MMS})
  when is_integer(Udp) -> % Arity 10
    Domain = default_domain(),
    Address = {Ip, Udp},
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      EngineId, TMask, MMS);
check_target_addr(
  {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params,
   _EngineId, _TMask, _MMS}) -> % Arity 10
    error({bad_address, {Domain, Address}});
check_target_addr(
  {Name, Domain, Address, Timeout, RetryCount, TagList, Params,
   EngineId}) % Arity 8
  when is_atom(Domain) ->
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      EngineId);
check_target_addr(
  {Name, Ip, Udp, Timeout, RetryCount, TagList, Params,
   EngineId}) % Arity 8
  when is_integer(Udp) ->
    Domain = default_domain(),
    Address = {Ip, Udp},
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      EngineId);
check_target_addr(
  {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params,
   _EngineId}) ->% Arity 8
    error({bad_address, {Domain, Address}});
%% Use dummy engine id if the old style is found
check_target_addr(
  {Name, Domain, Address, Timeout, RetryCount, TagList, Params}) % Arity 7
  when is_atom(Domain) ->
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params);
check_target_addr(
  {Name, Ip, Udp, Timeout, RetryCount, TagList, Params})  % Arity 7
  when is_integer(Udp) ->
    Domain = default_domain(),
    Address = {Ip, Udp},
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params);
check_target_addr(
  {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params}) -> % Arity 7
    error({bad_address, {Domain, Address}});
%% Use dummy engine id if the old style is found
check_target_addr(
  {Name, Domain, Address, Timeout, RetryCount, TagList, Params,
   TMask, MMS}) % Arity 9
  when is_atom(Domain) ->
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params, TMask, MMS);
check_target_addr(
  {Name, Ip, Udp, Timeout, RetryCount, TagList, Params,
   TMask, MMS}) % Arity 9
  when is_integer(Udp) ->
    Domain = default_domain(),
    Address = {Ip, Udp},
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params, TMask, MMS);
check_target_addr(
  {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params,
   _TMask, _MMS}) -> % Arity 9
    error({bad_address, {Domain, Address}});
check_target_addr(X) ->
    error({invalid_target_addr, X}).

check_target_addr(
  Name, Domain, Address, Timeout, RetryCount, TagList, Params) -> % Arity 7
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      "dummy").
%%
check_target_addr(
  Name, Domain, Address, Timeout, RetryCount, TagList, Params,
  EngineId) -> % Arity 8
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      EngineId, [], 2048).
%%
check_target_addr(
  Name, Domain, Address, Timeout, RetryCount, TagList, Params,
  TMask, MMS) -> % Arity 9
    check_target_addr(
      Name, Domain, Address, Timeout, RetryCount, TagList, Params,
      "dummy", TMask, MMS).
%%
check_target_addr(
  Name, Domain, Address, Timeout, RetryCount, TagList, Params,
  EngineId, Mask, MMS) -> % Arity 10
    ?vtrace("check target address with:"
	    "~n   Name:       ~s"
	    "~n   Domain:     ~p"
	    "~n   Address:    ~p"
	    "~n   Timeout:    ~p"
	    "~n   RetryCount: ~p"
	    "~n   TagList:    ~p"
	    "~n   Params:     ~p"
	    "~n   EngineId:   ~p"
	    "~n   Mask:      ~p"
	    "~n   MMS:        ~p",
	    [Name, Domain, Address,
	     Timeout, RetryCount,
	     TagList, Params, EngineId, Mask, MMS]),
    snmp_conf:check_string(Name,{gt,0}),
    snmp_conf:check_domain(Domain),
    NAddress = check_address(Domain, Address),
    snmp_conf:check_integer(Timeout, {gte, 0}),
    snmp_conf:check_integer(RetryCount, {gte,0}),
    snmp_conf:check_string(TagList),
    snmp_conf:check_string(Params),
    check_engine_id(EngineId),
    NMask = check_mask(Domain, Mask),
    TDomain  = snmp_conf:mk_tdomain(Domain), 
    TAddress = snmp_conf:mk_taddress(Domain, NAddress),
    TMask    = snmp_conf:mk_taddress(Domain, NMask),
    snmp_conf:check_packet_size(MMS),
    ?vtrace("check target address done",[]),
    Addr = {Name, TDomain, TAddress, Timeout,
	    RetryCount, TagList, Params,
	    ?'StorageType_nonVolatile', ?'RowStatus_active', EngineId,
            TMask, MMS}, % Values for Augmenting table in SNMP-COMMUNITY-MIB
    {ok, Addr}.

check_engine_id(discovery) ->
    ok;
check_engine_id(EngineId) ->
    snmp_conf:check_string(EngineId).

check_address(Domain, Address) ->
    case snmp_conf:check_address(Domain, Address, 162) of
	ok ->
	    Address;
	{ok, NAddress} ->
	    NAddress
    end.

check_mask(_Domain, [] = Mask) ->
    Mask;
check_mask(Domain, Mask) ->
    try snmp_conf:check_address(Domain, Mask) of
	ok ->
	    Mask;
	{ok, NMask} ->
	    NMask
    catch
	{error, {bad_address, Info}} ->
	    error({bad_mask, Info})
    end.


%%-----------------------------------------------------------------
%%  TargetParams
%%  {Name, MPModel, SecurityModel, SecurityName, SecurityLevel}
%%-----------------------------------------------------------------
-doc false.
check_target_params({Name, MPModel, SecModel, SecName, SecLevel}) ->
    snmp_conf:check_string(Name,{gt,0}),
    {ok, MP} = snmp_conf:check_atom(MPModel, [{v1,  ?MP_V1},
					      {v2c, ?MP_V2C},
					      {v3,  ?MP_V3}]),
    {ok, SecM} = snmp_conf:check_sec_model(SecModel, [any]),
    snmp_conf:check_string(SecName),
    {ok, SecL} = snmp_conf:check_sec_level(SecLevel),
    Params = {Name, MP, SecM, SecName, SecL, 
	      ?'StorageType_nonVolatile', ?'RowStatus_active'},
    {ok, Params};
check_target_params(X) ->
    error({invalid_target_params, X}).


init_tabs(Addrs, Params) ->
    ?vdebug("create target address table",[]),
    AddrDB = db(snmpTargetAddrTable),
    snmpa_local_db:table_delete(AddrDB),
    snmpa_local_db:table_create(AddrDB),
    init_addr_table(Addrs),
    ?vdebug("create target params table",[]),
    ParmDB = db(snmpTargetParamsTable),
    snmpa_local_db:table_delete(ParmDB),
    snmpa_local_db:table_create(ParmDB),
    init_params_table(Params).

init_addr_table([Row | T]) ->
    Key = element(1, Row),
    snmpa_local_db:table_create_row(db(snmpTargetAddrTable), Key, Row),
    init_addr_table(T);
init_addr_table([]) -> true.

init_params_table([Row | T]) ->
    Key = element(1, Row),
    snmpa_local_db:table_create_row(db(snmpTargetParamsTable), Key, Row),
    init_params_table(T);
init_params_table([]) -> true.

table_cre_row(Tab, Key, Row) ->
    snmpa_mib_lib:table_cre_row(db(Tab), Key, Row).

table_del_row(Tab, Key) ->
    snmpa_mib_lib:table_del_row(db(Tab), Key).


-doc """
Adds a target address definition to the agent config. Equivalent to one line in
the `target_addr.conf` file.
""".
-spec add_addr(Name, TDomain, TAddr, Timeout, Retry, TagList, Params,
               EngineId, TMask, MMS) -> {ok, Key} | {error, Reason} when
      Name     :: name(),
      TDomain  :: snmpa_conf:transportDomain(),
      TAddr    :: snmpa_conf:transportAddress(),
      Timeout  :: snmp:time_interval(),
      Retry    :: integer(),
      TagList  :: tag_list(),
      Params   :: params(),
      EngineId :: snmp_framework_mib:engine_id(),
      TMask    :: tmask(),
      MMS      :: snmp_framework_mib:max_message_size(),
      Key      :: term(),
      Reason   :: term();
              (Name, Ip, Port, Timeout, Retry, TagList, Params,
               EngineId, TMask, MMS) -> {ok, Key} | {error, Reason} when
      Name     :: name(),
      Ip       :: snmpa_conf:transportAddressWithoutPort(),
      Port     :: inet:port_number(),
      Timeout  :: snmp:time_interval(),
      Retry    :: integer(),
      TagList  :: tag_list(),
      Params   :: params(),
      EngineId :: snmp_framework_mib:engine_id(),
      TMask    :: tmask(),
      MMS      :: snmp_framework_mib:max_message_size(),
      Key      :: term(),
      Reason   :: term().
      
add_addr(
  Name, TDomain, TAddr, Timeout, Retry, TagList, Params,
  EngineId, TMask, MMS) when is_atom(TDomain) ->
    add_addr(
      {Name, TDomain, TAddr, Timeout, Retry, TagList, Params,
       EngineId, TMask, MMS});
add_addr(
  Name, Ip, Port, Timeout, Retry, TagList, Params,
  EngineId, TMask, MMS) ->
    add_addr(
      {Name, Ip, Port, Timeout, Retry, TagList, Params,
       EngineId, TMask, MMS}).
%%

-doc false.
-spec add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, Params,
               EngineId, TMask, MMS) -> {ok, Key} | {error, Reason} when
      Name     :: name(),
      Domain   :: snmpa_conf:transportDomain(),
      Ip       :: snmpa_conf:transportAddressWithoutPort(),
      Port     :: inet:port_number(),
      Timeout  :: snmp:time_interval(),
      Retry    :: integer(),
      TagList  :: tag_list(),
      Params   :: params(),
      EngineId :: snmp_framework_mib:engine_id(),
      TMask    :: tmask(),
      MMS      :: snmp_framework_mib:max_message_size(),
      Key      :: term(),
      Reason   :: term().
      
add_addr(
  Name, Domain, Ip, Port, Timeout, Retry, TagList, Params,
  EngineId, TMask, MMS) ->
    add_addr(
      {Name, Domain, Ip, Port, Timeout, Retry, TagList, Params,
       EngineId, TMask, MMS}).
%%
add_addr(Addr) ->
    case (catch check_target_addr(Addr)) of
	{ok, Row} ->
	    Key = element(1, Row),
	    case table_cre_row(snmpTargetAddrTable, Key, Row) of
		true ->
		    {ok, Key};
		false ->
		    {error, create_failed}
	    end;
	{error, Reason} ->
	    {error, Reason};
	Error ->
	    {error, Error}
    end.


-doc """
Delete a target address definition from the agent config.
""".
-spec delete_addr(Key) -> ok | {error, Reason} when
      Key    :: term(),
      Reason :: term().

delete_addr(Key) ->
    case table_del_row(snmpTargetAddrTable, Key) of
	true ->
	    ok;
	false ->
	    {error, delete_failed}
    end.


-doc """
Adds a target parameter definition to the agent config. Equivalent to one line
in the `target_params.conf` file.

""".
-spec add_params(Name, MPModel, SecModel, SecName, SecLevel) ->
          {ok, Key} | {error, Reason} when
      Name     :: name(),
      MPModel  :: snmp_framework_mib:message_processing_model(),
      SecModel :: snmp_framework_mib:security_model(),
      SecName  :: snmp_framework_mib:admin_string(),
      SecLevel :: snmp_framework_mib:security_level(),
      Key      :: term(),
      Reason   :: term().

add_params(Name, MPModel, SecModel, SecName, SecLevel) ->
    Params = {Name, MPModel, SecModel, SecName, SecLevel},
    case (catch check_target_params(Params)) of
	{ok, Row} ->
	    Key = element(1, Row),
	    case table_cre_row(snmpTargetParamsTable, Key, Row) of
		true ->
		    {ok, Key};
		false ->
		    {error, create_failed}
	    end;
	{error, Reason} ->
	    {error, Reason};
	Error ->
	    {error, Error}
    end.

-doc "Delete a target parameter definition from the agent config.".
-spec delete_params(Key) -> ok | {error, Reason} when
      Key    :: term(),
      Reason :: term().

delete_params(Key) ->
    case table_del_row(snmpTargetParamsTable, Key) of
	true ->
	    ok;
	false ->
	    {error, delete_failed}
    end.

    
gc_tabs() ->
    AddrDB  = db(snmpTargetAddrTable),
    AddrSTC = stc(snmpTargetAddrTable),
    AddrFOI = foi(snmpTargetAddrTable),
    snmpa_mib_lib:gc_tab(AddrDB, AddrSTC, AddrFOI),
    ParmDB  = db(snmpTargetParamsTable),
    ParmSTC = stc(snmpTargetParamsTable),
    ParmFOI = foi(snmpTargetParamsTable),
    snmpa_mib_lib:gc_tab(ParmDB, ParmSTC, ParmFOI),
    ok.


%%-----------------------------------------------------------------
%% Counter functions
%%-----------------------------------------------------------------
init_vars() -> 
    ?vtrace("initiate vars",[]),
    lists:map(fun maybe_create_var/1, vars()).

maybe_create_var(Var) ->
    case ets:lookup(snmp_agent_table, Var) of
	[_] -> ok;
	_ -> init_var(Var)
    end.

init_var(Var) -> ets:insert(snmp_agent_table, {Var, 0}).

vars() ->
    [
     snmpUnavailableContexts,
     snmpUnknownContexts
    ].


%%-----------------------------------------------------------------
%% API functions
%%-----------------------------------------------------------------
-doc false.
is_valid_tag("", _TDomain, _TAddress) ->
    true;
is_valid_tag(Tag, TDomain, TAddress) ->
    is_valid_tag(TDomain, TAddress, Tag, []).

is_valid_tag(TDomain, TAddress, Tag, Key) ->
    ?vtrace("is_valid_tag -> entry with"
	    "~n   TDomain:  ~p"
	    "~n   TAddress: ~p"
	    "~n   Tag:      ~p"
	    "~n   Key:      ~p", [TDomain, TAddress, Tag, Key]),
    case table_next(snmpTargetAddrTable, Key) of
	endOfTable -> 
	    ?vtrace("is_valid_tag -> endOfTable", []),
	    false;
	NextKey -> 
	    ?vtrace("is_valid_tag -> next key found"
		    "~n   NextKey: ~p", [NextKey]),
	    case get(snmpTargetAddrTable, NextKey, [?snmpTargetAddrTDomain,
						    ?snmpTargetAddrTAddress,
						    ?snmpTargetAddrTagList,
						    ?snmpTargetAddrTMask]) of
		[{value, TDomain},  % must match exactly
		 {value, TAddress}, % RFC2576: chapters 5.2.1 & 5.3
		 {value, TagList}, 
		 {value, []}] ->
		    ?vtrace("is_valid_tag -> found with exact match"
			    "~n   TagList: ~p", [TagList]),
		    case snmp_misc:is_tag_member(Tag, TagList) of
			true ->
			    ?vtrace("is_valid_tag -> exact: "
				    "tag IS member of taglist", []),
			    true;
			false ->
			    ?vtrace("is_valid_tag -> exact: "
				    "tag is NOT member of taglist", []),
			    is_valid_tag(TDomain, TAddress,
					 Tag, NextKey)
		    end;
		[{value, TDomain},   % must match exactly
		 {value, TAddress2},
		 {value, TagList}, 
		 {value, TMask}] when TMask =/= [] ->
		    ?vtrace("is_valid_tag -> found with exact match"
			    "~n   TagList: ~p"
			    "~n   TMask:   ~p", [TagList, TMask]),
		    case snmp_misc:is_tmask_match(TAddress, TAddress2, 
						  TMask) of
			true ->
			    ?vtrace("is_valid_tag -> "
				    "tmask match - now check tag member", []),
			    case snmp_misc:is_tag_member(Tag, TagList) of
				true ->
				    ?vtrace("is_valid_tag -> masked: "
					    "tag IS member of taglist", []),
				    true;
				false ->
				    ?vtrace("is_valid_tag -> masked: "
					    "tag is NOT member of taglist",[]),
				    is_valid_tag(TDomain, TAddress,
						 Tag, NextKey)
			    end;
			false ->
			    ?vtrace("is_valid_tag -> tmask NO match", []),
			    is_valid_tag(TDomain, TAddress,
					 Tag, NextKey)
		    end;
		_ ->
		    ?vtrace("is_valid_tag -> not found - try next", []),
		    is_valid_tag(TDomain, TAddress, Tag, NextKey)
	    end
    end.
    

%% TargAddrs =
%%     [{TagList, TargetAddr, TargetAddrName, TargetParams, Timeout, Retry}]
%%   TargetAddr = {TDomain, TAddr}; e.g. {?snmpUDPDomain, IpAndUdpPortAsList}
-doc false.
get_target_addrs() ->
    get_target_addrs([], db(snmpTargetAddrTable), []).


get_target_addrs(Key, {Tab, _} = TabDb, Acc) ->
    ?vt("get_target_addrs -> entry with"
	"~n   Key: ~p", [Key]),
    case table_next(Tab, Key) of
	endOfTable -> 
	    ?vt("get_target_addrs -> endOfTable when"
		"~n   Acc: ~p", [Acc]),
	    Acc;
	NextKey ->
	    ?vt("get_target_addrs -> next ok: "
		"~n   NextKey: ~p", [NextKey]),
	    case get_target_addr(TabDb, NextKey) of
		{ok, Targ} ->
		    get_target_addrs(NextKey, TabDb, [Targ| Acc]);
		{error, Reason} ->
		    ?vlog("failed getting target address: "
			  "~n   Reason: ~p", [Reason]),
		    get_target_addrs(NextKey, TabDb, Acc)
	    end
    end.

get_target_addr({Tab, mnesia}, Key) ->
    case mnesia:snmp_get_row(Tab, Key) of
	{ok, #snmpTargetAddrTable{
	   snmpTargetAddrTDomain    = TDomain,
	   snmpTargetAddrTAddress   = TAddress,
	   snmpTargetAddrTimeout    = Timeout,
	   snmpTargetAddrRetryCount = RetryCount,
	   snmpTargetAddrTagList    = TagList,
	   snmpTargetAddrParams     = Params,
	   snmpTargetAddrRowStatus  = ?'RowStatus_active'}} ->
	    case get_target_params(Params) of
		undefined ->
		    config_err("Failed retrieving target params [~p]"
			       "~n   for ~p [~p]", [Params, Key, TAddress]),
		    {error, {not_found, {target_params, Key, Params}}};
		TargetParams ->
		    Targ = {TagList, {TDomain, TAddress}, Key,
			    TargetParams, Timeout, RetryCount},
		    {ok, Targ}
	    end;
	_ ->
	    {error, {not_found, {target_addr, Key}}}
    end;
get_target_addr(TabDb, Key) ->
    case snmpa_local_db:table_get_row(TabDb, Key) of
	{_Key, TDomain, TAddress, Timeout, RetryCount, TagList, Params,
	 _Storage, ?'RowStatus_active', _TargetEngineId,_TMask,_MMS} ->
	    case get_target_params(Params) of
		undefined ->
		    config_err("Failed retrieving target params [~p]"
			       "~n   for target ~p [~p]", 
			       [Params, Key, TAddress]),
		    {error, {not_found, {target_params, Key, Params}}};
		TargetParams ->
		    Targ = {TagList, {TDomain, TAddress}, Key,
			    TargetParams, Timeout, RetryCount},
		    {ok, Targ}
	    end;
	_ ->
	    {error, {not_found, {target_addr, Key}}}
     end.
    

get_target_params(Params) ->
    case snmpTargetParamsTable(get, Params, [?snmpTargetParamsMPModel,
					     ?snmpTargetParamsSecurityModel,
					     ?snmpTargetParamsSecurityName,
					     ?snmpTargetParamsSecurityLevel,
					     ?snmpTargetParamsRowStatus]) of
	[{value, MpModel}, 
	 {value, SecModel}, {value, SecName}, {value, SecLevel},
	 {value, ?'RowStatus_active'}] ->
	    {MpModel, SecModel, SecName, SecLevel};
	_ ->
	    undefined
    end.

-doc false.
get_target_engine_id(TargetAddrName) ->
    case db(snmpTargetAddrTable) of
        {_, mnesia} ->
            case mnesia:snmp_get_row(snmpTargetAddrTable, TargetAddrName) of
                {ok, T} -> 
		    {ok, T#snmpTargetAddrTable.snmpTargetAddrEngineId};
                _ -> 
		    undefined
            end;
        TabDb ->
	    case snmpa_local_db:table_get_element(TabDb,
						  TargetAddrName,
						  ?snmpTargetAddrEngineId) of
		{value, Val} -> 
		    {ok, Val};
		_ ->
		    undefined
	    end
    end.


-doc """
Changes the engine id for a target in the `snmpTargetAddrTable`. If
notifications are sent as Inform requests to a target, its engine id must be
set.

""".
-spec set_target_engine_id(TargetAddrName, EngineId) -> boolean() when
      TargetAddrName :: name(),
      EngineId       :: snmp_framework_mib:engine_id().

set_target_engine_id(TargetAddrName, EngineId) ->
    snmp_generic:table_set_elements(db(snmpTargetAddrTable),
				    TargetAddrName,
				    [{?snmpTargetAddrEngineId, EngineId}]).


%%-----------------------------------------------------------------
%% Instrumentation Functions
%%-----------------------------------------------------------------
-doc false.
snmpTargetSpinLock(print) ->
    VarAndValue = [{snmpTargetSpinLock, snmpTargetSpinLock(get)}],
    snmpa_mib_lib:print_variables(VarAndValue);
    
snmpTargetSpinLock(new) ->
    snmp_generic:variable_func(new, {snmpTargetSpinLock, volatile}),
    ?SNMP_RAND_SEED(),
    %% rand:seed(exrop,
    %%           {erlang:phash2([node()]),
    %%            erlang:monotonic_time(),
    %%            erlang:unique_integer()}),
    Val = rand:uniform(2147483648) - 1,
    snmp_generic:variable_func(set, Val, {snmpTargetSpinLock, volatile});

snmpTargetSpinLock(delete) ->
    ok;

snmpTargetSpinLock(get) ->
    snmp_generic:variable_func(get, {snmpTargetSpinLock, volatile}).

-doc false.
snmpTargetSpinLock(is_set_ok, NewVal) ->
    case snmp_generic:variable_func(get, {snmpTargetSpinLock, volatile}) of
	{value, NewVal} -> noError;
	_ -> inconsistentValue
    end;
snmpTargetSpinLock(set, NewVal) ->
    snmp_generic:variable_func(set, (NewVal + 1) rem 2147483648,
			       {snmpTargetSpinLock, volatile}).


%% Op = print - Used for debugging purposes
-doc false.
snmpTargetAddrTable(print) ->
    Table = snmpTargetAddrTable, 
    DB    = db(Table),
    FOI   = foi(Table),
    PrintRow = 
	fun(Prefix, Row) ->
		TDomain = element(?snmpTargetAddrTDomain, Row),
		Domain =
		    try snmp_conf:tdomain_to_domain(TDomain)
		    catch
			{error, {bad_tdomain, _}} ->
			    undefined
		    end,
		TAddress = element(?snmpTargetAddrTAddress, Row),
		AddrString =
		    try snmp_conf:mk_addr_string({Domain, TAddress})
		    catch
			{error, {bad_address, _}} ->
			    "-"
		    end,
		lists:flatten(
		  io_lib:format("~sName:              ~p"
				"~n~sTDomain:           ~p (~w)"
				"~n~sTAddress:          ~p (~s)"
				"~n~sTimeout:           ~p"
				"~n~sRetryCount:        ~p"
				"~n~sTagList:           ~p"
				"~n~sParams:            ~p"
				"~n~sStorageType:       ~p (~w)"
				"~n~sStatus:            ~p (~w)"
				"~n~s[NonAcc] EngineID: ~p"
				"~n~s[Ext] TMask:       ~p"
				"~n~s[Ext] MMS:         ~p", 
				[Prefix, element(?snmpTargetAddrName, Row),
				 Prefix, TDomain, Domain,
				 Prefix, TAddress, AddrString,
				 Prefix, element(?snmpTargetAddrTimeout, Row),
				 Prefix, element(?snmpTargetAddrRetryCount, Row),
				 Prefix, element(?snmpTargetAddrTagList, Row),
				 Prefix, element(?snmpTargetAddrParams, Row),
				 Prefix, element(?snmpTargetAddrStorageType, Row),
				 case element(?snmpTargetAddrStorageType, Row) of
				     ?'snmpTargetAddrStorageType_readOnly' -> readOnly;
				     ?'snmpTargetAddrStorageType_permanent' -> permanent;
				     ?'snmpTargetAddrStorageType_nonVolatile' -> nonVolatile;
				     ?'snmpTargetAddrStorageType_volatile' -> volatile;
				     ?'snmpTargetAddrStorageType_other' -> other;
				     _ -> undefined
				 end,
				 Prefix, element(?snmpTargetAddrRowStatus, Row),
				 case element(?snmpTargetAddrRowStatus, Row) of
				     ?'snmpTargetAddrRowStatus_destroy' -> destroy;
				     ?'snmpTargetAddrRowStatus_createAndWait' -> createAndWait;
				     ?'snmpTargetAddrRowStatus_createAndGo' -> createAndGo;
				     ?'snmpTargetAddrRowStatus_notReady' -> notReady;
				     ?'snmpTargetAddrRowStatus_notInService' -> notInService;
				     ?'snmpTargetAddrRowStatus_active' -> active;
				     _ -> undefined
				 end,
				 Prefix, element(?snmpTargetAddrEngineId, Row),
				 Prefix, element(?snmpTargetAddrTMask, Row),
				 Prefix, element(?snmpTargetAddrMMS, Row)]))
	end,
    snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow);
%% Op == new | delete
snmpTargetAddrTable(Op) ->
    snmp_generic:table_func(Op, db(snmpTargetAddrTable)).

%% Op == get | is_set_ok | set | get_next
-doc false.
snmpTargetAddrTable(get, RowIndex, Cols) ->
    get(snmpTargetAddrTable, RowIndex, Cols);
snmpTargetAddrTable(get_next, RowIndex, Cols) ->
    next(snmpTargetAddrTable, RowIndex, Cols);
snmpTargetAddrTable(set, RowIndex, Cols0) ->
    %% BMK BMK
    case (catch verify_targetAddrTable_cols(Cols0)) of
	{ok, Cols} ->
	    snmp_notification_mib:invalidate_cache(),
	    %% Add columns for augmenting table snmpTargetAddrExtTable and for
	    %% target engine ID. Target engine ID is set to "". The function
	    %% get_target_engine_id will return "" unless a value is set using
	    %% set_target_engine_id. If it is "" Informs can't be sent to the
	    %% target.
	    NCols = Cols ++ [{?snmpTargetAddrEngineId, ""},
			     {?snmpTargetAddrTMask, []},
			     {?snmpTargetAddrMMS, 2048}],
	    Db = db(snmpTargetAddrTable), 
	    snmp_generic:table_func(set, RowIndex, NCols, Db);
	Error ->
	    Error
    end;
snmpTargetAddrTable(is_set_ok, RowIndex, Cols0) ->
    case (catch verify_targetAddrTable_cols(Cols0)) of
	{ok, Cols} ->
	    %% Add columns for augmenting table snmpTargetAddrExtTable and for
	    %% target engine ID. Target engine ID is set to "". The function
	    %% get_target_engine_id will return "" unless a value is set using
	    %% set_target_engine_id. If it is "" Informs can't be sent to the
	    %% target.
	    NCols = Cols ++ [{?snmpTargetAddrEngineId, ""},
			     {?snmpTargetAddrTMask, []},
			     {?snmpTargetAddrMMS, 2048}],
	    Db = db(snmpTargetAddrTable), 
	    snmp_generic:table_func(is_set_ok, RowIndex, NCols, Db);
	Error ->
	    Error
    end;
snmpTargetAddrTable(Op, Arg1, Arg2) ->
    Db = db(snmpTargetAddrTable), 
    snmp_generic:table_func(Op, Arg1, Arg2, Db).

verify_targetAddrTable_cols(Cols) ->
    ValidCols0 = verify_targetAddrTable_cols(Cols, []),
    %% Make a last pass to verify TDomain and TAddress.
    ValidCols0.

verify_targetAddrTable_cols([], Cols) ->
    {ok, lists:reverse(Cols)};
verify_targetAddrTable_cols([{Col, Val0}|Cols], ValidCols) ->
    Val = verify_targetAddrTable_col(Col, Val0, ValidCols),
    verify_targetAddrTable_cols(Cols, [{Col, Val}|ValidCols]).

verify_targetAddrTable_col(?snmpTargetAddrName, Name, _) ->
    case (catch snmp_conf:check_string(Name)) of
	ok ->
	    Name;
	_ ->
	    wrongValue(?snmpTargetAddrName)
    end;
verify_targetAddrTable_col(?snmpTargetAddrTDomain, TDomain, _) ->
    case (catch snmp_conf:check_tdomain(TDomain)) of
	ok ->
	    TDomain;
	_ ->
	    wrongValue(?snmpTargetAddrTDomain)
    end;
%% In order to (properly) validate the TAddress, 
%% the TDomain must already have been validated 
%% (the format of TAddress depends on TDomain).
verify_targetAddrTable_col(?snmpTargetAddrTAddress, TAddress, ValidCols) ->
    case lists:keysearch(?snmpTargetAddrTDomain, 1, ValidCols) of
	{value, {?snmpTargetAddrTDomain, TDomain}} ->
	    case (catch snmp_conf:check_taddress(TDomain, TAddress)) of
		ok ->
		    TAddress;
		_ ->
		    wrongValue(?snmpTargetAddrTAddress)
	    end;
	false ->
	    %% The user did not provide us with a TDomain, which 
	    %% must mean that he/she intends to use the old domain. 
	    TDomain = snmp_conf:mk_tdomain(default_domain()),
	    case (catch snmp_conf:check_taddress(TDomain, TAddress)) of
		ok ->
		    TAddress;
		_ ->
		    wrongValue(?snmpTargetAddrTAddress)
	    end	    
    end;
verify_targetAddrTable_col(?snmpTargetAddrTimeout, Timeout, _) ->
    case (catch snmp_conf:check_integer(Timeout)) of
	ok when Timeout >= 0 ->
	    Timeout;
	_ ->
	    wrongValue(?snmpTargetAddrTimeout)
    end;
verify_targetAddrTable_col(?snmpTargetAddrRetryCount, Retry, _) ->
    case (catch snmp_conf:check_integer(Retry)) of
	ok when Retry >= 0 ->
	    Retry;
	_ ->
	    wrongValue(?snmpTargetAddrRetryCount)
    end;
verify_targetAddrTable_col(?snmpTargetAddrTagList, TagList, _) ->
    case (catch snmp_conf:check_string(TagList)) of
	ok ->
	    TagList;
	_ ->
	    wrongValue(?snmpTargetAddrTagList)
    end;
verify_targetAddrTable_col(?snmpTargetAddrParams, Params, _) ->
    case (catch snmp_conf:check_string(Params)) of
	ok ->
	    Params;	
	_ ->
	    wrongValue(?snmpTargetAddrParams)
    end;
verify_targetAddrTable_col(_, Val, _) ->
    Val.
    

%% Op = print - Used for debugging purposes
-doc false.
snmpTargetParamsTable(print) ->
    Table = snmpTargetParamsTable, 
    DB    = db(Table),
    FOI   = foi(Table),
    PrintRow = 
	fun(Prefix, Row) ->
		lists:flatten(
		  io_lib:format("~sName:          ~p"
				"~n~sMPModel:       ~p (~w)"
				"~n~sSecurityModel: ~p (~w)"
				"~n~sSecurityName:  ~p"
				"~n~sSecurityLevel: ~p (~w)"
				"~n~sStorageType:   ~p (~w)"
				"~n~sStatus:        ~p (~w)", 
				[Prefix, 
				 element(?snmpTargetParamsName, Row),
				 Prefix, 
				 element(?snmpTargetParamsMPModel, Row),
				 case element(?snmpTargetParamsMPModel, Row) of
				     ?MP_V1 -> v1;
				     ?MP_V2C -> v2c;
				     ?MP_V3 -> v3;
				     _ -> undefined
				 end,
				 Prefix, 
				 element(?snmpTargetParamsSecurityModel, Row),
				 case element(?snmpTargetParamsSecurityModel, Row) of
				     ?SEC_ANY -> any;
				     ?SEC_V1 -> v1;
				     ?SEC_V2C -> v2c;
				     ?SEC_USM -> usm;
				     _ -> undefined
				 end,
				 Prefix, 
				 element(?snmpTargetParamsSecurityName, Row),
				 Prefix, 
				 element(?snmpTargetParamsSecurityLevel, Row),
				 case element(?snmpTargetParamsSecurityLevel, Row) of
				     ?'SnmpSecurityLevel_noAuthNoPriv' -> noAuthNoPriv;
				     ?'SnmpSecurityLevel_authNoPriv' -> authNoPriv;
				     ?'SnmpSecurityLevel_authPriv' -> authPriv;
				     _ -> undefined
				 end,
				 Prefix, 
				 element(?snmpTargetParamsStorageType, Row),
				 case element(?snmpTargetParamsStorageType, Row) of
				     ?'snmpTargetParamsStorageType_readOnly' -> readOnly;
				     ?'snmpTargetParamsStorageType_permanent' -> permanent;
				     ?'snmpTargetParamsStorageType_nonVolatile' -> nonVolatile;
				     ?'snmpTargetParamsStorageType_volatile' -> volatile;
				     ?'snmpTargetParamsStorageType_other' -> other;
				     _ -> undefined
				 end,
				 Prefix, 
				 element(?snmpTargetParamsRowStatus, Row),
				 case element(?snmpTargetParamsRowStatus, Row) of
				     ?'snmpTargetParamsRowStatus_destroy' -> destroy;
				     ?'snmpTargetParamsRowStatus_createAndWait' -> createAndWait;
				     ?'snmpTargetParamsRowStatus_createAndGo' -> createAndGo;
				     ?'snmpTargetParamsRowStatus_notReady' -> notReady;
				     ?'snmpTargetParamsRowStatus_notInService' -> notInService;
				     ?'snmpTargetParamsRowStatus_active' -> active;
				     _ -> undefined
				 end]))
	end,
    snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow);
%% Op == new | delete
snmpTargetParamsTable(Op) ->
    snmp_generic:table_func(Op, db(snmpTargetParamsTable)).

%% Op == get | is_set_ok | set | get_next
-doc false.
snmpTargetParamsTable(get, RowIndex, Cols) ->
    %% BMK BMK BMK
    get(snmpTargetParamsTable, RowIndex, Cols);
snmpTargetParamsTable(get_next, RowIndex, Cols) ->
    %% BMK BMK BMK
    next(snmpTargetParamsTable, RowIndex, Cols);
snmpTargetParamsTable(set, RowIndex, Cols0) ->
    %% BMK BMK BMK
    case (catch verify_snmpTargetParamsTable_cols(Cols0, [])) of
	{ok, Cols} ->
	    snmp_notification_mib:invalidate_cache(),
	    snmp_generic:table_func(set, RowIndex, Cols, 
				    db(snmpTargetParamsTable));
	Error ->
	    Error
    end;
snmpTargetParamsTable(Op, Arg1, Arg2) ->
    snmp_generic:table_func(Op, Arg1, Arg2, db(snmpTargetParamsTable)).

verify_snmpTargetParamsTable_cols([], Cols) ->
    {ok, lists:reverse(Cols)};
verify_snmpTargetParamsTable_cols([{Col, Val0}|Cols], Acc) ->
    Val = verify_snmpTargetParamsTable_col(Col, Val0),
    verify_snmpTargetParamsTable_cols(Cols, [{Col, Val}|Acc]).

verify_snmpTargetParamsTable_col(?snmpTargetParamsName, Name) ->
    case (catch snmp_conf:check_string(Name)) of
	ok ->
	    Name;
	_ ->
	    wrongValue(?snmpTargetParamsName)
    end;
verify_snmpTargetParamsTable_col(?snmpTargetParamsMPModel, Model) ->
    case Model of
	v1      -> ?MP_V1;
	v2c     -> ?MP_V2C;
	v3      -> ?MP_V3;
	?MP_V1  -> ?MP_V1;
	?MP_V2C -> ?MP_V2C;
	?MP_V3  -> ?MP_V3;
	_       -> wrongValue(?snmpTargetParamsMPModel)
    end;
verify_snmpTargetParamsTable_col(?snmpTargetParamsSecurityModel, Model) ->
    case Model of
	v1       -> ?SEC_V1;
	v2c      -> ?SEC_V2C;
	usm      -> ?SEC_USM;
	?SEC_V1  -> ?SEC_V1;
	?SEC_V2C -> ?SEC_V2C;
	?SEC_USM -> ?SEC_USM;
	_        -> wrongValue(?snmpTargetParamsSecurityModel)
    end;
verify_snmpTargetParamsTable_col(?snmpTargetParamsSecurityName, Name) ->
    case (catch snmp_conf:check_string(Name)) of
	ok ->
	    Name;
	_ ->
	    wrongValue(?snmpTargetParamsSecurityName)
    end;
verify_snmpTargetParamsTable_col(?snmpTargetParamsSecurityLevel, Level) ->
    case Level of
	noAuthNoPriv -> 1;
	authNoPriv   -> 2;
	authPriv     -> 3;
	1            -> 1;
	2            -> 2;
	3            -> 3;
	_            -> wrongValue(?snmpTargetParamsSecurityLevel)
    end;
verify_snmpTargetParamsTable_col(_, Val) ->
    Val.

db(X) -> snmpa_agent:db(X).

fa(snmpTargetAddrTable) -> ?snmpTargetAddrTDomain;
fa(snmpTargetParamsTable) -> ?snmpTargetParamsMPModel.
 
foi(snmpTargetAddrTable) -> ?snmpTargetAddrName;
foi(snmpTargetParamsTable) -> ?snmpTargetParamsName.
 
noc(snmpTargetAddrTable) -> 9;
noc(snmpTargetParamsTable) -> 7.

stc(snmpTargetAddrTable) -> ?snmpTargetAddrStorageType;
stc(snmpTargetParamsTable) -> ?snmpTargetParamsStorageType.
 
next(Name, RowIndex, Cols) ->
    snmp_generic:handle_table_next(db(Name), RowIndex, Cols,
                                   fa(Name), foi(Name), noc(Name)).

-doc false.
table_next(Name, RestOid) ->
    snmp_generic:table_next(db(Name), RestOid).

 
-doc false.
get(Name, RowIndex, Cols) ->
    snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)).


wrongValue(V) -> throw({wrongValue, V}).


%% -----

set_sname() ->
    set_sname(get(sname)).

set_sname(undefined) ->
    put(sname,conf);
set_sname(_) -> %% Keep it, if already set.
    ok.
    
error(Reason) ->
    throw({error, Reason}).

config_err(F, A) ->
    snmpa_error:config_err("[TARGET-MIB]: " ++ F, A).
