aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/manager
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/manager')
-rw-r--r--lib/snmp/src/manager/Makefile124
-rw-r--r--lib/snmp/src/manager/depend.mk75
-rw-r--r--lib/snmp/src/manager/modules.mk45
-rw-r--r--lib/snmp/src/manager/snmpm.erl1528
-rw-r--r--lib/snmp/src/manager/snmpm_atl.hrl21
-rw-r--r--lib/snmp/src/manager/snmpm_conf.erl396
-rw-r--r--lib/snmp/src/manager/snmpm_config.erl3116
-rw-r--r--lib/snmp/src/manager/snmpm_internal.hrl32
-rw-r--r--lib/snmp/src/manager/snmpm_misc_sup.erl112
-rw-r--r--lib/snmp/src/manager/snmpm_mpd.erl1024
-rw-r--r--lib/snmp/src/manager/snmpm_net_if.erl1125
-rw-r--r--lib/snmp/src/manager/snmpm_net_if_filter.erl53
-rw-r--r--lib/snmp/src/manager/snmpm_network_interface.erl37
-rw-r--r--lib/snmp/src/manager/snmpm_network_interface_filter.erl54
-rw-r--r--lib/snmp/src/manager/snmpm_server.erl3117
-rw-r--r--lib/snmp/src/manager/snmpm_server_sup.erl110
-rw-r--r--lib/snmp/src/manager/snmpm_supervisor.erl115
-rw-r--r--lib/snmp/src/manager/snmpm_user.erl97
-rw-r--r--lib/snmp/src/manager/snmpm_user_default.erl87
-rw-r--r--lib/snmp/src/manager/snmpm_user_old.erl33
-rw-r--r--lib/snmp/src/manager/snmpm_usm.erl512
-rw-r--r--lib/snmp/src/manager/snmpm_usm.hrl27
22 files changed, 11840 insertions, 0 deletions
diff --git a/lib/snmp/src/manager/Makefile b/lib/snmp/src/manager/Makefile
new file mode 100644
index 0000000000..c1d5703300
--- /dev/null
+++ b/lib/snmp/src/manager/Makefile
@@ -0,0 +1,124 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+include $(ERL_TOP)/make/target.mk
+
+EBIN = ../../ebin
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+
+VSN = $(SNMP_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+include modules.mk
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+HRL_FILES = $(INTERNAL_HRL_FILES:%=%.hrl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+
+# ----------------------------------------------------
+# SNMP FLAGS
+# ----------------------------------------------------
+ifeq ($(SNMP_DEFAULT_VERBOSITY),)
+ SNMP_FLAGS = -Ddefault_verbosity=silence
+else
+ SNMP_FLAGS = -Ddefault_verbosity=$(SNMP_DEFAULT_VERBOSITY)
+endif
+
+ifeq ($(SNMP_DEBUG),d)
+ SNMP_FLAGS += -Dsnmp_debug
+endif
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/snmp/ebin
+
+ifeq ($(WARN_UNUSED_VARS),true)
+ERL_COMPILE_FLAGS += +warn_unused_vars
+endif
+
+ERL_COMPILE_FLAGS += -I../../include \
+ -I../misc \
+ -Dversion=\"$(VSN)$(PRE_VSN)\" \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ -I$(ERL_TOP)/lib/stdlib \
+ $(SNMP_FLAGS)
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug:
+ @$(MAKE) TYPE=debug opt
+
+opt: $(TARGET_FILES)
+
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+info:
+ @echo "ERL_FILES: $(ERL_FILES)"
+ @echo "HRL_FILES: $(HRL_FILES)"
+ @echo "TARGET_FILES: $(TARGET_FILES)"
+ @echo ""
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/manager
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/manager
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+# $(INSTALL_DIR) $(RELSYSDIR)/include
+# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
+
+include depend.mk
diff --git a/lib/snmp/src/manager/depend.mk b/lib/snmp/src/manager/depend.mk
new file mode 100644
index 0000000000..0e7e9e3df7
--- /dev/null
+++ b/lib/snmp/src/manager/depend.mk
@@ -0,0 +1,75 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+$(EBIN)/snmpm_user.$(EMULATOR): \
+ snmpm_user.erl
+
+$(EBIN)/snmpm_network_interface.$(EMULATOR): \
+ snmpm_network_interface.erl
+
+$(EBIN)/snmpm.$(EMULATOR): \
+ snmpm.erl
+
+$(EBIN)/snmpm_config.$(EMULATOR): \
+ snmpm_config.erl \
+ ../../include/snmp_types.hrl \
+ ../misc/snmp_verbosity.hrl
+
+$(EBIN)/snmpm_mpd.$(EMULATOR): \
+ snmpm_mpd.erl \
+ ../../include/snmp_types.hrl \
+ ../../include/SNMP-MPD-MIB.hrl \
+ ../../include/SNMPv2-TM.hrl \
+ ../misc/snmp_verbosity.hrl
+
+$(EBIN)/snmpm_misc_sup.$(EMULATOR): \
+ snmpm_misc_sup.erl \
+ ../misc/snmp_debug.hrl
+
+$(EBIN)/snmpm_net_if.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ ../misc/snmp_debug.hrl \
+ ../misc/snmp_verbosity.hrl \
+ snmpm_net_if.erl \
+ snmpm_network_interface.erl
+
+$(EBIN)/snmpm_server.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ ../../include/STANDARD-MIB.hrl \
+ ../misc/snmp_verbosity.hrl \
+ snmpm_server.erl
+
+$(EBIN)/snmpm_server_sup.$(EMULATOR): \
+ snmpm_server_sup.erl
+
+$(EBIN)/snmpm_supervisor.$(EMULATOR): \
+ snmpm_supervisor.erl
+
+$(EBIN)/snmpm_user_default.$(EMULATOR): \
+ snmpm_user_default.erl \
+ snmpm_user.erl
+
+$(EBIN)/snmpm_usm.$(EMULATOR): \
+ snmpm_usm.erl \
+ snmpm_usm.hrl \
+ ../../include/snmp_types.hrl \
+ ../../include/SNMP-USER-BASED-SM-MIB.hrl \
+ ../misc/snmp_verbosity.hrl
+
+
diff --git a/lib/snmp/src/manager/modules.mk b/lib/snmp/src/manager/modules.mk
new file mode 100644
index 0000000000..79f3dd65d9
--- /dev/null
+++ b/lib/snmp/src/manager/modules.mk
@@ -0,0 +1,45 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+BEHAVIOUR_MODULES = \
+ snmpm_user \
+ snmpm_user_old \
+ snmpm_network_interface \
+ snmpm_network_interface_filter
+
+MODULES = \
+ $(BEHAVIOUR_MODULES) \
+ snmpm \
+ snmpm_conf \
+ snmpm_config \
+ snmpm_mpd \
+ snmpm_misc_sup \
+ snmpm_net_if \
+ snmpm_net_if_filter \
+ snmpm_server \
+ snmpm_server_sup \
+ snmpm_supervisor \
+ snmpm_user_default \
+ snmpm_usm
+
+INTERNAL_HRL_FILES = \
+ snmpm_usm \
+ snmpm_atl \
+ snmpm_internal
+
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
new file mode 100644
index 0000000000..141addf440
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -0,0 +1,1528 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm).
+
+%%----------------------------------------------------------------------
+%% This module implements a simple SNMP manager for Erlang.
+%%----------------------------------------------------------------------
+
+%% User interface
+-export([
+ %%
+ %% Management API
+ start/0, start/1,
+ start_link/0, start_link/1,
+ stop/0,
+
+ monitor/0, demonitor/1,
+ notify_started/1, cancel_notify_started/1,
+
+ backup/1,
+
+ load_mib/1, unload_mib/1,
+ which_mibs/0,
+ name_to_oid/1, oid_to_name/1, oid_to_type/1,
+
+ register_user/3, register_user/4,
+ register_user_monitor/3, register_user_monitor/4,
+ unregister_user/1,
+ which_users/0,
+
+ register_agent/2, register_agent/3, register_agent/4,
+ unregister_agent/2, unregister_agent/3,
+ which_agents/0, which_agents/1,
+ agent_info/2, update_agent_info/4,
+
+ register_usm_user/3, unregister_usm_user/2,
+ which_usm_users/0, which_usm_users/1,
+ usm_user_info/3, update_usm_user_info/4,
+
+ %%
+ %% Basic SNMP API
+ sync_get/3, sync_get/4, sync_get/5, sync_get/6,
+ async_get/3, async_get/4, async_get/5, async_get/6,
+ sync_get_next/3, sync_get_next/4, sync_get_next/5, sync_get_next/6,
+ async_get_next/3, async_get_next/4, async_get_next/5, async_get_next/6,
+ sync_set/3, sync_set/4, sync_set/5, sync_set/6,
+ async_set/3, async_set/4, async_set/5, async_set/6,
+ sync_get_bulk/5, sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8,
+ async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8,
+ cancel_async_request/2,
+
+ %%
+ %% Extended SNMP API
+ %% discovery/2, discovery/3, discovery/4, discovery/5, discovery/6,
+
+ %%
+ %% Logging
+ log_to_txt/2, log_to_txt/3, log_to_txt/4,
+ log_to_txt/5, log_to_txt/6, log_to_txt/7,
+ change_log_size/1,
+ get_log_type/0,
+ set_log_type/1,
+
+ reconfigure/0,
+
+ system_start_time/0,
+ sys_up_time/0,
+
+ info/0,
+ verbosity/2
+ ]).
+
+-export([format_reason/1, format_reason/2]).
+
+%% Backward compatibillity exports
+-export([
+ agent_info/3, update_agent_info/5,
+ g/3, g/4, g/5, g/6, g/7,
+ ag/3, ag/4, ag/5, ag/6, ag/7,
+ gn/3, gn/4, gn/5, gn/6, gn/7,
+ agn/3, agn/4, agn/5, agn/6, agn/7,
+ gb/5, gb/6, gb/7, gb/8, gb/9,
+ agb/5, agb/6, agb/7, agb/8, agb/9,
+ s/3, s/4, s/5, s/6, s/7,
+ as/3, as/4, as/5, as/6, as/7
+ ]).
+
+%% Application internal export
+-export([start_link/3, snmpm_start_verify/2, snmpm_start_verify/3]).
+
+
+-include("snmp_debug.hrl").
+-include("snmpm_atl.hrl").
+-include("snmp_types.hrl").
+
+-define(DEFAULT_AGENT_PORT, 161).
+-define(DEFAULT_CONTEXT, "").
+
+
+%% This function is called when the snmp application
+%% starts.
+start_link(Opts, normal, []) ->
+ start_link(Opts).
+
+
+simple_conf() ->
+ Vsns = [v1, v2, v3],
+ {ok, Cwd} = file:get_cwd(),
+ %% Check if the manager config file exist, if not create it
+ MgrConf = filename:join(Cwd, "manager.conf"),
+ case file:read_file_info(MgrConf) of
+ {ok, _} ->
+ ok;
+ _ ->
+ ok = snmp_config:write_manager_config(Cwd, "",
+ [{port, 5000},
+ {engine_id, "mgrEngine"},
+ {max_message_size, 484}])
+ end,
+ Conf = [{dir, Cwd}, {db_dir, Cwd}],
+ [{versions, Vsns}, {config, Conf}].
+
+%% Simple start. Start a manager with default values.
+start_link() ->
+ start_link(simple_conf()).
+
+%% This function is normally not used. Instead the manager is
+%% started as a consequence of a call to application:start(snmp)
+%% when {snmp, [{manager, Options}]} is present in the
+%% node config file.
+start_link(Opts) ->
+ %% This start the manager top supervisor, which in turn
+ %% starts the other processes.
+ {ok, _} = snmpm_supervisor:start_link(normal, Opts),
+ ok.
+
+%% Simple start. Start a manager with default values.
+start() ->
+ start(simple_conf()).
+
+start(Opts) ->
+ %% This start the manager top supervisor, which in turn
+ %% starts the other processes.
+ {ok, Pid} = snmpm_supervisor:start_link(normal, Opts),
+ unlink(Pid),
+ ok.
+
+stop() ->
+ snmpm_supervisor:stop().
+
+
+monitor() ->
+ erlang:monitor(process, snmpm_supervisor).
+
+demonitor(Ref) ->
+ erlang:demonitor(Ref).
+
+
+-define(NOTIFY_START_TICK_TIME, 500).
+
+notify_started(To) when is_integer(To) andalso (To > 0) ->
+ spawn_link(?MODULE, snmpm_start_verify, [self(), To]).
+
+cancel_notify_started(Pid) ->
+ Pid ! {cancel, self()},
+ ok.
+
+snmpm_start_verify(Parent, To) ->
+ ?d("starting", []),
+ snmpm_start_verify(Parent, monitor(), To).
+
+snmpm_start_verify(Parent, _Ref, To) when (To =< 0) ->
+ ?d("timeout", []),
+ unlink(Parent),
+ Parent ! {snmpm_start_timeout, self()};
+snmpm_start_verify(Parent, Ref, To) ->
+ T0 = t(),
+ receive
+ {cancel, Parent} ->
+ ?d("cancel", []),
+ demonitor(Ref),
+ unlink(Parent),
+ exit(normal);
+ {'EXIT', Parent, _} ->
+ exit(normal);
+ {'DOWN', Ref, process, _Object, _Info} ->
+ ?d("down", []),
+ sleep(?NOTIFY_START_TICK_TIME),
+ ?MODULE:snmpm_start_verify(Parent, monitor(), t(T0, To))
+ after ?NOTIFY_START_TICK_TIME ->
+ ?d("down timeout", []),
+ demonitor(Ref),
+ case snmpm_server:is_started() of
+ true ->
+ unlink(Parent),
+ Parent ! {snmpm_started, self()};
+ _ ->
+ ?MODULE:snmpm_start_verify(Parent, monitor(), t(T0, To))
+ end
+ end.
+
+t(T0, T) -> T - (t() - T0).
+t() -> snmp_misc:now(ms).
+sleep(To) -> snmp_misc:sleep(To).
+
+
+%% -- Misc --
+
+backup(BackupDir) ->
+ snmpm_config:backup(BackupDir).
+
+
+%% -- Mibs --
+
+%% Load a mib into the manager
+load_mib(MibFile) ->
+ snmpm_server:load_mib(MibFile).
+
+%% Unload a mib from the manager
+unload_mib(Mib) ->
+ snmpm_server:unload_mib(Mib).
+
+%% Which mib's are loaded
+which_mibs() ->
+ snmpm_config:which_mibs().
+
+%% Get all the possible oid's for the aliasname
+name_to_oid(Name) ->
+ snmpm_config:name_to_oid(Name).
+
+%% Get the aliasname for an oid
+oid_to_name(Oid) ->
+ snmpm_config:oid_to_name(Oid).
+
+%% Get the type for an oid
+oid_to_type(Oid) ->
+ snmpm_config:oid_to_type(Oid).
+
+
+%% -- Info --
+
+info() ->
+ snmpm_server:info().
+
+
+%% -- Verbosity --
+
+%% Change the verbosity of a process in the manager
+verbosity(config, V) ->
+ snmpm_config:verbosity(V);
+verbosity(server, V) ->
+ snmpm_server:verbosity(V);
+verbosity(net_if, V) ->
+ snmpm_server:verbosity(net_if, V);
+verbosity(note_store, V) ->
+ snmpm_server:verbosity(note_store, V);
+verbosity(all, V) ->
+ snmpm_config:verbosity(V),
+ snmpm_server:verbosity(V),
+ snmpm_server:verbosity(net_if, V),
+ snmpm_server:verbosity(note_store, V).
+
+
+%% -- Users --
+
+%% Register the 'user'.
+%% The manager entity responsible for a specific agent.
+%% Module is the callback module (snmpm_user behaviour) which
+%% will be called whenever something happens (detected
+%% agent, incomming reply or incomming trap/notification).
+%% Note that this could have already been done as a
+%% consequence of the node config.
+register_user(Id, Module, Data) ->
+ register_user(Id, Module, Data, []).
+
+%% Default config for agents registered by this user
+register_user(Id, Module, Data, DefaultAgentConfig) ->
+ snmpm_server:register_user(Id, Module, Data, DefaultAgentConfig).
+
+register_user_monitor(Id, Module, Data) ->
+ register_user_monitor(Id, Module, Data, []).
+
+register_user_monitor(Id, Module, Data, DefaultAgentConfig) ->
+ snmpm_server:register_user_monitor(Id, Module, Data, DefaultAgentConfig).
+
+unregister_user(Id) ->
+ snmpm_server:unregister_user(Id).
+
+which_users() ->
+ snmpm_config:which_users().
+
+
+%% -- Agents --
+
+%% Explicitly instruct the manager to handle this agent.
+%% Called to instruct the manager that this agent
+%% shall be handled. These functions is used when
+%% the user know's in advance which agents the
+%% manager shall handle.
+%% Note that there is an alternate way to do the same thing:
+%% Add the agent to the manager config files.
+%%
+%% UserId -> Id of the user responsible for this agent: term()
+%% TargetName -> Unique name for the agent: (string())
+%% Config -> Agent configuration: [config()]
+
+do_register_agent(UserId, TargetName, Config) ->
+ snmpm_config:register_agent(UserId, TargetName, Config).
+
+register_agent(UserId, TargetName, Config)
+ when (is_list(TargetName) andalso
+ (length(TargetName) > 0) andalso
+ is_list(Config)) ->
+ do_register_agent(UserId, TargetName, [{reg_type, target_name} | Config]);
+
+%% Backward compatibillity
+%% Note that the agent engine id is a mandatory config option,
+%% so this function *will* fail!
+register_agent(UserId, Addr, Port) when is_integer(Port) ->
+ register_agent(UserId, Addr, Port, []);
+
+%% Backward compatibillity
+register_agent(UserId, Addr, Config) when is_list(Config) ->
+ register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, Config).
+
+%% Backward compatibillity
+%% Note that the agent engine id is a mandatory config option,
+%% so this function *will* fail!
+register_agent(UserId, Addr) ->
+ register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, []).
+
+%% Backward compatibillity
+register_agent(UserId, Addr, Port, Config0) ->
+ case lists:keymember(target_name, 1, Config0) of
+ false ->
+ TargetName = mk_target_name(Addr, Port, Config0),
+ Config = [{reg_type, addr_port},
+ {address, Addr}, {port, Port} | Config0],
+ do_register_agent(UserId, TargetName, ensure_engine_id(Config));
+ true ->
+ {value, {_, TargetName}} =
+ lists:keysearch(target_name, 1, Config0),
+ Config1 = lists:keydelete(target_name, 1, Config0),
+ Config2 = [{reg_type, addr_port},
+ {address, Addr}, {port, Port} | Config1],
+ register_agent(UserId, TargetName, ensure_engine_id(Config2))
+ end.
+
+unregister_agent(UserId, TargetName) when is_list(TargetName) ->
+ snmpm_config:unregister_agent(UserId, TargetName);
+
+%% Backward compatibillity functions
+unregister_agent(UserId, Addr) ->
+ unregister_agent(UserId, Addr, ?DEFAULT_AGENT_PORT).
+
+unregister_agent(UserId, Addr, Port) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ unregister_agent(UserId, TargetName);
+ Error ->
+ Error
+ end.
+
+agent_info(TargetName, Item) ->
+ snmpm_config:agent_info(TargetName, Item).
+
+%% Backward compatibillity
+agent_info(Addr, Port, Item) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ agent_info(TargetName, Item);
+ Error ->
+ Error
+ end.
+
+update_agent_info(UserId, TargetName, Item, Val) ->
+%% p("update_agent_info -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Item: ~p"
+%% "~n Val: ~p", [UserId, TargetName, Item, Val]),
+ snmpm_config:update_agent_info(UserId, TargetName, Item, Val).
+
+%% Backward compatibillity functions
+update_agent_info(UserId, Addr, Port, Item, Val) ->
+%% p("update_agent_info -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Port: ~p"
+%% "~n Item: ~p"
+%% "~n Val: ~p", [UserId, Addr, Port, Item, Val]),
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+%% p("update_agent_info -> TargetName: ~p", [TargetName]),
+ update_agent_info(UserId, TargetName, Item, Val);
+ Error ->
+ Error
+ end.
+
+which_agents() ->
+ snmpm_config:which_agents().
+
+which_agents(UserId) ->
+ snmpm_config:which_agents(UserId).
+
+
+%% -- USM users --
+
+register_usm_user(EngineID, UserName, Conf)
+ when is_list(EngineID) andalso is_list(UserName) andalso is_list(Conf) ->
+ snmpm_config:register_usm_user(EngineID, UserName, Conf).
+
+unregister_usm_user(EngineID, UserName)
+ when is_list(EngineID) andalso is_list(UserName) ->
+ snmpm_config:unregister_usm_user(EngineID, UserName).
+
+usm_user_info(EngineID, UserName, Item)
+ when is_list(EngineID) andalso is_list(UserName) andalso is_atom(Item) ->
+ snmpm_config:usm_user_info(EngineID, UserName, Item).
+
+update_usm_user_info(EngineID, UserName, Item, Val)
+ when is_list(EngineID) andalso is_list(UserName) andalso is_atom(Item) ->
+ snmpm_config:update_usm_user_info(EngineID, UserName, Item, Val).
+
+which_usm_users() ->
+ snmpm_config:which_usm_users().
+
+which_usm_users(EngineID) when is_list(EngineID) ->
+ snmpm_config:which_usm_users(EngineID).
+
+
+%% -- Discovery --
+
+%% Start a discovery process
+%% discovery(UserId, BAddr) ->
+%% snmpm_server:discovery(UserId, BAddr).
+
+%% discovery(UserId, BAddr, ExpireOrConfig) ->
+%% snmpm_server:discovery(UserId, BAddr, ExpireOrConfig).
+
+%% discovery(UserId, BAddr, Config, Expire) ->
+%% snmpm_server:discovery(UserId, BAddr, Config, Expire).
+
+%% discovery(UserId, BAddr, Port, Config, Expire) ->
+%% snmpm_server:discovery(UserId, BAddr, Port, Config, Expire).
+
+%% discovery(UserId, BAddr, Port, Config, Expire, ExtraInfo) ->
+%% snmpm_server:discovery(UserId, BAddr, Port, Config, Expire, ExtraInfo).
+
+
+%% -- Requests --
+
+%% --- synchroneous get-request ---
+%%
+
+sync_get(UserId, TargetName, Oids) ->
+%% p("sync_get -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Oids: ~p", [UserId, TargetName, Oids]),
+ sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids).
+
+sync_get(UserId, TargetName, Context, Oids) when is_list(Oids) ->
+%% p("sync_get -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Context: ~p"
+%% "~n Oids: ~p", [UserId, TargetName, Context, Oids]),
+ snmpm_server:sync_get(UserId, TargetName, Context, Oids);
+
+sync_get(UserId, TargetName, Oids, Timeout) when is_integer(Timeout) ->
+%% p("sync_get -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p", [UserId, TargetName, Oids, Timeout]),
+ sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout).
+
+sync_get(UserId, TargetName, Context, Oids, Timeout) ->
+%% p("sync_get -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Context: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p", [UserId, TargetName, Context, Oids, Timeout]),
+ snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout).
+
+sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
+%% p("sync_get -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Context: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p"
+%% "~n ExtraInfo: ~p",
+%% [UserId, TargetName, Context, Oids, Timeout, ExtraInfo]),
+ snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout,
+ ExtraInfo).
+
+
+g(UserId, Addr, Oids) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Oids: ~p", [UserId, Addr, Oids]),
+ g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
+
+g(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n CtxName: ~p"
+%% "~n Oids: ~p", [UserId, Addr, CtxName, Oids]),
+ g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
+
+g(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Port: ~p"
+%% "~n Oids: ~p", [UserId, Addr, Port, Oids]),
+ g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
+
+g(UserId, Addr, Oids, Timeout)
+ when is_list(Oids) andalso is_integer(Timeout) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p", [UserId, Addr, Oids, Timeout]),
+ g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout).
+
+g(UserId, Addr, Port, CtxName, Oids)
+ when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Port: ~p"
+%% "~n Context: ~p"
+%% "~n Oids: ~p", [UserId, Addr, Port, CtxName, Oids]),
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+%% p("g -> TargetName: ~p", [TargetName]),
+ sync_get(UserId, TargetName, CtxName, Oids);
+ Error ->
+ Error
+ end;
+
+g(UserId, Addr, Port, Oids, Timeout)
+ when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p",
+%% [UserId, Addr, Oids, Timeout]),
+ g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout);
+
+g(UserId, Addr, CtxName, Oids, Timeout)
+ when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n CtxName: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p",
+%% [UserId, Addr, CtxName, Oids, Timeout]),
+ g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout).
+
+g(UserId, Addr, Port, CtxName, Oids, Timeout) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Port: ~p"
+%% "~n CtxName: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p",
+%% [UserId, Addr, Port, CtxName, Oids, Timeout]),
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+%% p("g -> TargetName: ~p", [TargetName]),
+ sync_get(UserId, TargetName, CtxName, Oids, Timeout);
+ Error ->
+ Error
+ end.
+
+g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) ->
+%% p("g -> entry with"
+%% "~n UserId: ~p"
+%% "~n Addr: ~p"
+%% "~n Port: ~p"
+%% "~n CtxName: ~p"
+%% "~n Oids: ~p"
+%% "~n Timeout: ~p"
+%% "~n ExtraInfo: ~p",
+%% [UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo]),
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+%% p("g -> TargetName: ~p", [TargetName]),
+ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+%% --- asynchroneous get-request ---
+%%
+%% The reply will be delivered to the user
+%% through a call to handle_pdu/5
+%%
+
+async_get(UserId, TargetName, Oids) ->
+ async_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids).
+
+async_get(UserId, TargetName, Context, Oids) when is_list(Oids) ->
+ snmpm_server:async_get(UserId, TargetName, Context, Oids);
+
+async_get(UserId, TargetName, Oids, Expire) when is_integer(Expire) ->
+ async_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Expire).
+
+async_get(UserId, TargetName, Context, Oids, Expire) ->
+ snmpm_server:async_get(UserId, TargetName, Context, Oids, Expire).
+
+async_get(UserId, TargetName, Context, Oids, Expire, ExtraInfo) ->
+ snmpm_server:async_get(UserId, TargetName, Context, Oids, Expire,
+ ExtraInfo).
+
+
+ag(UserId, Addr, Oids) ->
+ ag(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
+
+ag(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
+ ag(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
+
+ag(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
+ ag(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
+
+ag(UserId, Addr, Oids, Expire) when is_list(Oids) andalso is_integer(Expire) ->
+ ag(UserId, Addr, ?DEFAULT_AGENT_PORT, ?DEFAULT_CONTEXT, Oids, Expire).
+
+ag(UserId, Addr, Port, CtxName, Oids)
+ when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get(UserId, TargetName, CtxName, Oids);
+ Error ->
+ Error
+ end;
+
+ag(UserId, Addr, Port, Oids, Expire)
+ when is_integer(Port) andalso is_list(Oids) andalso is_integer(Expire) ->
+ ag(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Expire);
+
+ag(UserId, Addr, CtxName, Oids, Expire)
+ when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) ->
+ ag(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Expire).
+
+ag(UserId, Addr, Port, CtxName, Oids, Expire) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get(UserId, TargetName, CtxName, Oids, Expire);
+ Error ->
+ Error
+ end.
+
+ag(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+%% --- synchroneous get_next-request ---
+%%
+
+sync_get_next(UserId, TargetName, Oids) ->
+ sync_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids).
+
+sync_get_next(UserId, TargetName, Context, Oids)
+ when is_list(Context) andalso is_list(Oids) ->
+ snmpm_server:sync_get_next(UserId, TargetName, Context, Oids);
+
+sync_get_next(UserId, TargetName, Oids, Timeout)
+ when is_list(Oids) andalso is_integer(Timeout) ->
+ sync_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout).
+
+sync_get_next(UserId, TargetName, Context, Oids, Timeout) ->
+ snmpm_server:sync_get_next(UserId, TargetName, Context, Oids, Timeout).
+
+sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
+ snmpm_server:sync_get_next(UserId, TargetName, Context, Oids, Timeout,
+ ExtraInfo).
+
+
+gn(UserId, Addr, Oids) ->
+ gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
+
+gn(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
+ gn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
+
+gn(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
+ gn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
+
+gn(UserId, Addr, Oids, Timeout)
+ when is_list(Oids) andalso is_integer(Timeout) ->
+ gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout).
+
+gn(UserId, Addr, Port, CtxName, Oids)
+ when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_get_next(UserId, TargetName, CtxName, Oids);
+ Error ->
+ Error
+ end;
+
+gn(UserId, Addr, Port, Oids, Timeout)
+ when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) ->
+ gn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout);
+gn(UserId, Addr, CtxName, Oids, Timeout)
+ when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) ->
+ gn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout).
+
+gn(UserId, Addr, Port, CtxName, Oids, Timeout) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout);
+ Error ->
+ Error
+ end.
+
+gn(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+%% --- asynchroneous get_next-request ---
+%%
+
+async_get_next(UserId, TargetName, Oids) ->
+ async_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids).
+
+async_get_next(UserId, TargetName, Context, Oids)
+ when is_list(Context) andalso is_list(Oids) ->
+ snmpm_server:async_get_next(UserId, TargetName, Context, Oids);
+
+async_get_next(UserId, TargetName, Oids, Timeout)
+ when is_list(Oids) andalso is_integer(Timeout) ->
+ async_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout).
+
+async_get_next(UserId, TargetName, Context, Oids, Timeout) ->
+ snmpm_server:async_get_next(UserId, TargetName, Context, Oids, Timeout).
+
+async_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
+ snmpm_server:async_get_next(UserId, TargetName, Context, Oids, Timeout,
+ ExtraInfo).
+
+agn(UserId, Addr, Oids) ->
+ agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
+
+agn(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
+ agn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
+
+agn(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
+ agn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
+
+agn(UserId, Addr, Oids, Expire)
+ when is_list(Oids) andalso is_integer(Expire) ->
+ agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Expire).
+
+agn(UserId, Addr, Port, CtxName, Oids)
+ when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get_next(UserId, TargetName, CtxName, Oids);
+ Error ->
+ Error
+ end;
+
+agn(UserId, Addr, Port, Oids, Expire)
+ when is_integer(Port) andalso is_list(Oids) andalso is_integer(Expire) ->
+ agn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Expire);
+agn(UserId, Addr, CtxName, Oids, Expire)
+ when is_list(CtxName) andalso is_list(CtxName) andalso is_integer(Expire) ->
+ agn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Expire).
+
+agn(UserId, Addr, Port, CtxName, Oids, Expire) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get_next(UserId, TargetName, CtxName, Oids, Expire);
+ Error ->
+ Error
+ end.
+
+agn(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get_next(UserId, TargetName, CtxName, Oids, Expire,
+ ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+%% --- synchroneous set-request ---
+%%
+
+sync_set(UserId, TargetName, VarsAndVals) ->
+ sync_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals).
+
+sync_set(UserId, TargetName, Context, VarsAndVals)
+ when is_list(Context) andalso is_list(VarsAndVals) ->
+ snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals);
+
+sync_set(UserId, TargetName, VarsAndVals, Timeout)
+ when is_list(VarsAndVals) andalso is_integer(Timeout) ->
+ sync_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals, Timeout).
+
+sync_set(UserId, TargetName, Context, VarsAndVals, Timeout) ->
+ snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals, Timeout).
+
+sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) ->
+ snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals, Timeout,
+ ExtraInfo).
+
+
+s(UserId, Addr, VarsAndVals) ->
+ s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals).
+
+s(UserId, Addr, Port, VarsAndVals)
+ when is_integer(Port) andalso is_list(VarsAndVals) ->
+ s(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals);
+
+s(UserId, Addr, CtxName, VarsAndVals)
+ when is_list(CtxName) andalso is_list(VarsAndVals) ->
+ s(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals);
+
+s(UserId, Addr, VarsAndVals, Timeout)
+ when is_list(VarsAndVals) andalso is_integer(Timeout) ->
+ s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals, Timeout).
+
+s(UserId, Addr, Port, CtxName, VarsAndVals)
+ when is_integer(Port) andalso
+ is_list(CtxName) andalso
+ is_list(VarsAndVals) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals);
+ Error ->
+ Error
+ end;
+
+s(UserId, Addr, Port, VarsAndVals, Timeout)
+ when is_integer(Port) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Timeout) ->
+ s(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals, Timeout);
+
+s(UserId, Addr, CtxName, VarsAndVals, Timeout)
+ when is_list(CtxName) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Timeout) ->
+ s(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals, Timeout).
+
+s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout);
+ Error ->
+ Error
+ end.
+
+s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+%% --- asynchroneous set-request ---
+%%
+
+async_set(UserId, TargetName, VarsAndVals) ->
+ async_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals).
+
+async_set(UserId, TargetName, Context, VarsAndVals)
+ when is_list(Context) andalso is_list(VarsAndVals) ->
+ snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals);
+
+async_set(UserId, TargetName, VarsAndVals, Expire)
+ when is_list(VarsAndVals) andalso is_integer(Expire) ->
+ async_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals, Expire).
+
+async_set(UserId, TargetName, Context, VarsAndVals, Expire) ->
+ snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals, Expire).
+
+async_set(UserId, TargetName, Context, VarsAndVals, Expire, ExtraInfo) ->
+ snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals, Expire,
+ ExtraInfo).
+
+
+as(UserId, Addr, VarsAndVals) ->
+ as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals).
+
+as(UserId, Addr, Port, VarsAndVals)
+ when is_integer(Port) andalso is_list(VarsAndVals) ->
+ as(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals);
+
+as(UserId, Addr, CtxName, VarsAndVals)
+ when is_list(CtxName) andalso is_list(VarsAndVals) ->
+ as(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals);
+
+as(UserId, Addr, VarsAndVals, Expire)
+ when is_list(VarsAndVals) andalso is_integer(Expire) ->
+ as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals, Expire).
+
+as(UserId, Addr, Port, CtxName, VarsAndVals)
+ when is_integer(Port) andalso
+ is_list(CtxName) andalso
+ is_list(VarsAndVals) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals);
+ Error ->
+ Error
+ end;
+
+as(UserId, Addr, Port, VarsAndVals, Expire)
+ when is_integer(Port) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Expire) ->
+ as(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals, Expire);
+
+as(UserId, Addr, CtxName, VarsAndVals, Expire)
+ when is_list(CtxName) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Expire) ->
+ as(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals, Expire).
+
+as(UserId, Addr, Port, CtxName, VarsAndVals, Expire) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals, Expire);
+ Error ->
+ Error
+ end.
+
+as(UserId, Addr, Port, CtxName, VarsAndVals, Expire, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+
+%% --- synchroneous get-bulk ---
+%%
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) ->
+ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Context) andalso
+ is_list(Oids) ->
+ snmpm_server:sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep,
+ Context, Oids);
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Timeout)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ sync_get_bulk(UserId, TargetName, NonRep, MaxRep,
+ ?DEFAULT_CONTEXT, Oids, Timeout).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout) ->
+ snmpm_server:sync_get_bulk(UserId, TargetName, NonRep, MaxRep,
+ Context, Oids, Timeout).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout,
+ ExtraInfo) ->
+ snmpm_server:sync_get_bulk(UserId, TargetName, NonRep, MaxRep,
+ Context, Oids, Timeout, ExtraInfo).
+
+
+gb(UserId, Addr, NonRep, MaxRep, Oids) ->
+ gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids).
+
+gb(UserId, Addr, Port, NonRep, MaxRep, Oids)
+ when is_integer(Port) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) ->
+ gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids);
+
+gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) ->
+ gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids);
+
+gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Timeout).
+
+gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids)
+ when is_integer(Port) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids);
+ Error ->
+ Error
+ end;
+
+gb(UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout)
+ when is_integer(Port) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Timeout);
+
+gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids,
+ Timeout).
+
+gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Timeout);
+ Error ->
+ Error
+ end.
+
+gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+
+%% --- asynchroneous get-bulk ---
+%%
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) ->
+ async_get_bulk(UserId, TargetName, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Context) andalso
+ is_list(Oids) ->
+ snmpm_server:async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, Context, Oids);
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Expire).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire) ->
+ snmpm_server:async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, Context, Oids, Expire).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire,
+ ExtraInfo) ->
+ snmpm_server:async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep,
+ Context, Oids, Expire, ExtraInfo).
+
+
+agb(UserId, Addr, NonRep, MaxRep, Oids) ->
+ agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids).
+
+agb(UserId, Addr, Port, NonRep, MaxRep, Oids)
+ when is_integer(Port) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) ->
+ agb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids);
+
+agb(UserId, Addr, NonRep, MaxRep, CtxName, Oids)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) ->
+ agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids);
+
+agb(UserId, Addr, NonRep, MaxRep, Oids, Expire)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) ->
+ agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Expire).
+
+agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids)
+ when is_integer(Port) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep),
+ is_list(CtxName) andalso
+ is_list(Oids) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids);
+ Error ->
+ Error
+ end;
+
+agb(UserId, Addr, Port, NonRep, MaxRep, Oids, Expire)
+ when is_integer(Port) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) ->
+ agb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Expire);
+
+agb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Expire)
+ when is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) ->
+ agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids).
+
+agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Expire);
+ Error ->
+ Error
+ end.
+
+agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) ->
+ case target_name(Addr, Port) of
+ {ok, TargetName} ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Expire,
+ ExtraInfo);
+ Error ->
+ Error
+ end.
+
+
+cancel_async_request(UserId, ReqId) ->
+ snmpm_server:cancel_async_request(UserId, ReqId).
+
+
+%%%-----------------------------------------------------------------
+%%% Audit Trail Log functions (for backward compatibillity)
+%%%-----------------------------------------------------------------
+log_to_txt(LogDir, Mibs) ->
+ OutFile = "snmpm_log.txt",
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+log_to_txt(LogDir, Mibs, OutFile) ->
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+log_to_txt(LogDir, Mibs, OutFile, LogName) ->
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start).
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop).
+
+
+change_log_size(NewSize) ->
+ LogName = ?audit_trail_log_name,
+ snmp:change_log_size(LogName, NewSize).
+
+
+get_log_type() ->
+ snmpm_server:get_log_type().
+
+%% NewType -> atl_type()
+set_log_type(NewType) ->
+ snmpm_server:set_log_type(NewType).
+
+
+reconfigure() ->
+ snmpm_server:reconfigure().
+
+
+%%%-----------------------------------------------------------------
+
+system_start_time() ->
+ {ok, Time} = snmpm_config:system_start_time(),
+ Time.
+
+sys_up_time() ->
+ % time in 0.01 seconds.
+ StartTime = system_start_time(),
+ (snmp_misc:now(cs) - StartTime) rem (2 bsl 31).
+
+
+%%%-----------------------------------------------------------------
+%%% This is just some simple utility functions to create a pretty-
+%%% printable string of the error reason received from either:
+%%%
+%%% * If any of the sync/async get/get-next/set/get-bulk
+%%% returnes {error, Reason}
+%%% * The Reason parameter in the handle_error user callback
+%%% function
+%%%
+%%%-----------------------------------------------------------------
+
+format_reason(Reason) ->
+ format_reason("", Reason).
+
+format_reason(Prefix, Reason) when is_integer(Prefix) and (Prefix >= 0) ->
+ format_reason(lists:duplicate(Prefix, $ ), Reason);
+format_reason(Prefix, Reason) when is_list(Prefix) ->
+ case (catch do_format_reason(Prefix, Reason)) of
+ FL when is_list(FL) ->
+ FL;
+ _ ->
+ %% Crap, try it without any fancy formatting
+ case (catch io_lib:format("~sInternal manager error: ~n"
+ "~s ~p~n",
+ [Prefix, Prefix, Reason])) of
+ L1 when is_list(L1) ->
+ lists:flatten(L1);
+ _ ->
+ %% Really crap, try it without the prefix
+ case (catch io_lib:format("Internal manager error: ~n"
+ " ~p~n",
+ [Reason])) of
+ L2 when is_list(L2) ->
+ lists:flatten(L2);
+ _ ->
+ %% Ok, I give up
+ "Illegal input. Unable to format error reason"
+ end
+ end
+ end.
+
+
+do_format_reason(Prefix, {failed_generating_response, {RePdu, Reason}}) ->
+ FmtPdu = format_pdu(Prefix ++ " ", RePdu),
+ lists:flatten(io_lib:format("~sFailed generating response: ~n"
+ "~s"
+ "~s ~p~n",
+ [Prefix, FmtPdu, Prefix, Reason]));
+do_format_reason(Prefix, {failed_processing_message, Reason}) ->
+ lists:flatten(io_lib:format("~sFailed processing message: ~n"
+ "~s ~p~n",
+ [Prefix, Prefix, Reason]));
+do_format_reason(Prefix, {unexpected_pdu, SnmpInfo}) ->
+ FmtSnmpInfo = format_snmp_info(Prefix ++ " ", SnmpInfo),
+ lists:flatten(io_lib:format("~sUnexpected PDU: ~n~s",
+ [Prefix, FmtSnmpInfo]));
+do_format_reason(Prefix, {send_failed, ReqId, Reason}) ->
+ lists:flatten(io_lib:format("~sSend failed: ~n"
+ "~s Request id: ~w~n"
+ "~s Reason: ~p~n",
+ [Prefix, Prefix, ReqId, Prefix, Reason]));
+do_format_reason(Prefix, {invalid_sec_info, SecInfo, SnmpInfo}) ->
+ FmtSecInfo = format_sec_info(Prefix ++ " ", SecInfo),
+ FmtSnmpInfo = format_snmp_info(Prefix ++ " ", SnmpInfo),
+ lists:flatten(io_lib:format("~sInvalid security info: ~n"
+ "~s"
+ "~s",
+ [Prefix, FmtSecInfo, FmtSnmpInfo]));
+do_format_reason(Prefix, Reason) ->
+ lists:flatten(io_lib:format("~sInternal manager error: ~n"
+ "~s ~p~n", [Prefix, Prefix, Reason])).
+
+format_pdu(Prefix, #pdu{type = Type,
+ request_id = ReqId,
+ error_status = ES,
+ error_index = EI,
+ varbinds = VBs}) ->
+ FmtPdyType = format_pdu_type(Type),
+ FmtErrStatus = format_error_status(ES),
+ FmtErrIdx = format_error_index(EI),
+ FmtVBs = format_varbinds(Prefix ++ " ", VBs),
+ lists:flatten(io_lib:format("~s~s: ~n"
+ "~s Request-id: ~w~n"
+ "~s Error-status: ~s~n"
+ "~s Error-index: ~s~n"
+ "~s",
+ [Prefix, FmtPdyType,
+ Prefix, ReqId,
+ Prefix, FmtErrStatus,
+ Prefix, FmtErrIdx,
+ FmtVBs]));
+format_pdu(Prefix, #trappdu{enterprise = E,
+ agent_addr = AA,
+ generic_trap = GT,
+ specific_trap = ST,
+ time_stamp = TS,
+ varbinds = VBs}) ->
+ FmtVBs = format_varbinds(Prefix ++ " ", VBs),
+ lists:flatten(io_lib:format("~sTrap PDU: ~n"
+ "~s Enterprise: ~p~n"
+ "~s Agent address: ~p~n"
+ "~s Generic trap: ~p~n"
+ "~s Specific trap: ~p~n"
+ "~s Time stamp: ~p~n"
+ "~s",
+ [Prefix,
+ Prefix, E,
+ Prefix, AA,
+ Prefix, GT,
+ Prefix, ST,
+ Prefix, TS,
+ FmtVBs]));
+format_pdu(Prefix, PDU) ->
+ lists:flatten(io_lib:format("~s~p~n", [Prefix, PDU])).
+
+format_pdu_type('get-request') ->
+ "GetRequest-PDU";
+format_pdu_type('get-next-request') ->
+ "GetNextRequest-PDU";
+format_pdu_type('get-response') ->
+ "Response-PDU";
+format_pdu_type('set-request') ->
+ "SetRequest-PDU";
+format_pdu_type('get-bulk-request') ->
+ "GetBulkRequest-PDU";
+format_pdu_type('inform-request') ->
+ "InformRequest-PDU";
+format_pdu_type('snmpv2-trap') ->
+ "SNMPv2-Trap-PDU";
+format_pdu_type(report) ->
+ "Report-PDU";
+format_pdu_type(T) ->
+ lists:flatten(io_lib:format("~p", [T])).
+
+format_snmp_info(Prefix, {ES, EI, VBs}) ->
+ lists:flatten(io_lib:format("~sSNMP info: ~n"
+ "~s Error-status: ~s~n"
+ "~s Error-index: ~s~n"
+ "~s",
+ [Prefix,
+ Prefix, format_error_status(ES),
+ Prefix, format_error_index(EI),
+ format_varbinds(Prefix ++ " ", VBs)]));
+format_snmp_info(Prefix, JunkSnmpInfo) ->
+ lists:flatten(io_lib:format("~sJunk SNMP info: ~n"
+ "~s ~p~n",
+ [Prefix, Prefix, JunkSnmpInfo])).
+
+format_error_status(ES) ->
+ lists:flatten(io_lib:format("~p", [ES])).
+
+format_error_index(EI) ->
+ lists:flatten(io_lib:format("~p", [EI])).
+
+format_sec_info(Prefix, Info) ->
+ FmtSecInfo = do_format_sec_info(Prefix ++ " ", Info),
+ lists:flatten(io_lib:format("~sSecurity info: ~n~s",
+ [Prefix, FmtSecInfo])).
+
+do_format_sec_info(_Prefix, []) ->
+ "";
+do_format_sec_info(Prefix, [{Tag, ExpVal, Val}|T]) ->
+ format_sec_info(Prefix, Tag, ExpVal, Val) ++
+ do_format_sec_info(Prefix, T).
+
+
+format_sec_info(_Prefix, _Tag, Val, Val) ->
+ "";
+format_sec_info(Prefix, Tag, ExpVal, Val) ->
+ lists:flatten(io_lib:format("~s~s:~n"
+ "~s Expected value: ~p~n"
+ "~s Actual value: ~p~n",
+ [Prefix, format_sec_info_tag(Tag),
+ Prefix, ExpVal,
+ Prefix, Val])).
+
+format_sec_info_tag(sec_engine_id) ->
+ "Sec engine id";
+format_sec_info_tag(msg_sec_model) ->
+ "Msg sec model";
+format_sec_info_tag(sec_name) ->
+ "Sec name";
+format_sec_info_tag(sec_level) ->
+ "Sec level";
+format_sec_info_tag(ctx_engine_id) ->
+ "Context engine id";
+format_sec_info_tag(ctx_name) ->
+ "Context name";
+format_sec_info_tag(request_id) ->
+ "Request id";
+format_sec_info_tag(T) ->
+ lists:flatten(io_lib:format("~p", [T])).
+
+format_varbinds(Prefix, []) ->
+ lists:flatten(io_lib:format("~sVarbinds: []~n", [Prefix]));
+format_varbinds(Prefix, VBs) when is_list(VBs) ->
+ lists:flatten(io_lib:format("~sVarbinds: ~n~s",
+ [Prefix, format_vbs(Prefix ++ " ", VBs)]));
+format_varbinds(Prefix, VBs) ->
+ lists:flatten(io_lib:format("~sInvalid varbinds: ~n"
+ "~s ~p~n",
+ [Prefix, Prefix, VBs])).
+
+format_vbs(_Prefix, []) ->
+ "";
+format_vbs(Prefix, [VB|VBs]) ->
+ format_vb(Prefix, VB) ++ format_vbs(Prefix, VBs).
+
+format_vb(Prefix, #varbind{oid = Oid0,
+ variabletype = Type,
+ value = Val,
+ org_index = Idx}) ->
+ Oid =
+ case snmpm:oid_to_name(Oid0) of
+ {ok, O} ->
+ O;
+ _ ->
+ Oid0
+ end,
+ FmtVT = format_vb_variabletype(Prefix ++ " ", Type),
+ FmtVal = format_vb_value(Prefix ++ " ", Type, Val),
+ lists:flatten(io_lib:format("~s~w:~n"
+ "~s"
+ "~s"
+ "~s Org-index: ~p~n",
+ [Prefix, Oid,
+ FmtVT,
+ FmtVal,
+ Prefix, Idx]));
+format_vb(Prefix, JunkVB) ->
+ lists:flatten(io_lib:format("~sJunk varbind:~n"
+ "~s ~p~n", [Prefix, Prefix, JunkVB])).
+
+format_vb_variabletype(Prefix, Type) when is_atom(Type) ->
+ lists:flatten(io_lib:format("~sVariable-type: ~s~n",
+ [Prefix, atom_to_list(Type)]));
+format_vb_variabletype(Prefix, Type) ->
+ lists:flatten(io_lib:format("~sVariable-type: ~p~n", [Prefix, Type])).
+
+format_vb_value(Prefix, _Type, Val) ->
+ lists:flatten(io_lib:format("~sValue: ~p~n", [Prefix, Val])).
+
+
+%% ---------------------------------------------------------------------------
+%%
+%% --- Internal utility functions ---
+%%
+
+target_name(Addr, Port) ->
+ snmpm_config:agent_info(Addr, Port, target_name).
+
+mk_target_name(Addr, Port, Config) ->
+ snmpm_config:mk_target_name(Addr, Port, Config).
+
+ensure_engine_id(Config) ->
+ case lists:keymember(engine_id, 1, Config) of
+ true ->
+ Config;
+ false ->
+ DefaultEngineId = "agentEngine-default",
+ [{engine_id, DefaultEngineId} | Config]
+ end.
+
+
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
diff --git a/lib/snmp/src/manager/snmpm_atl.hrl b/lib/snmp/src/manager/snmpm_atl.hrl
new file mode 100644
index 0000000000..d99ee05ae6
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_atl.hrl
@@ -0,0 +1,21 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-define(audit_trail_log_name, "snmpm_log").
+-define(audit_trail_log_file, "snmpm.log").
diff --git a/lib/snmp/src/manager/snmpm_conf.erl b/lib/snmp/src/manager/snmpm_conf.erl
new file mode 100644
index 0000000000..75f9c09477
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_conf.erl
@@ -0,0 +1,396 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_conf).
+
+-include_lib("kernel/include/file.hrl").
+
+-export([
+ %% manager.conf
+ manager_entry/2,
+ write_manager_config/2, write_manager_config/3,
+ append_manager_config/2,
+ read_manager_config/1,
+
+ %% users.conf
+ users_entry/1, users_entry/2, users_entry/3,
+ write_users_config/2, write_users_config/3,
+ append_users_config/2,
+ read_users_config/1,
+
+ %% agents.conf
+ agents_entry/12,
+ write_agents_config/2, write_agents_config/3,
+ append_agents_config/2,
+ read_agents_config/1,
+
+ %% usm.conf
+ usm_entry/6, usm_entry/7,
+ write_usm_config/2, write_usm_config/3,
+ append_usm_config/2,
+ read_usm_config/1
+ ]).
+
+
+
+-define(MANAGER_CONF_FILE, "manager.conf").
+-define(USERS_CONF_FILE, "users.conf").
+-define(AGENTS_CONF_FILE, "agents.conf").
+-define(USM_USERS_CONF_FILE, "usm.conf").
+
+%%
+%% ------ manager.conf ------
+%%
+
+manager_entry(Tag, Val) ->
+ {Tag, Val}.
+
+
+write_manager_config(Dir, Conf) ->
+ Comment =
+"%% This file defines the Manager local configuration info\n"
+"%% Each row is a 2-tuple:\n"
+"%% {Variable, Value}.\n"
+"%% For example\n"
+"%% {port, 5000}.\n"
+"%% {address, [127,42,17,5]}.\n"
+"%% {engine_id, \"managerEngine\"}.\n"
+"%% {max_message_size, 484}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_manager_config(Dir, Hdr, Conf).
+
+write_manager_config(Dir, Hdr, Conf)
+ when is_list(Dir) andalso is_list(Hdr) andalso is_list(Conf) ->
+ Verify = fun() -> verify_manager_conf(Conf) end,
+ Write = fun(Fid) -> write_manager_conf(Fid, Hdr, Conf) end,
+ write_config_file(Dir, ?MANAGER_CONF_FILE, Verify, Write).
+
+
+append_manager_config(Dir, Conf)
+ when is_list(Dir) andalso is_list(Conf) ->
+ Verify = fun() -> verify_manager_conf(Conf) end,
+ Write = fun(Fid) -> write_manager_conf(Fid, Conf) end,
+ append_config_file(Dir, ?MANAGER_CONF_FILE, Verify, Write).
+
+
+read_manager_config(Dir) ->
+ Verify = fun(Entry) -> verify_manager_conf_entry(Entry) end,
+ read_config_file(Dir, ?MANAGER_CONF_FILE, Verify).
+
+
+verify_manager_conf([]) ->
+ ok;
+verify_manager_conf([H|T]) ->
+ verify_manager_conf_entry(H),
+ verify_manager_conf(T);
+verify_manager_conf(X) ->
+ error({bad_manager_config, X}).
+
+verify_manager_conf_entry(Entry) ->
+ case snmpm_config:check_manager_config(Entry) of
+ ok ->
+ ok;
+%% {ok, _} ->
+%% ok;
+ Error ->
+ throw(Error)
+ end.
+
+write_manager_conf(Fd, "", Conf) ->
+ write_manager_conf(Fd, Conf);
+write_manager_conf(Fd, Hdr, Conf) ->
+ io:format(Fd, "~s~n", [Hdr]),
+ write_manager_conf(Fd, Conf).
+
+write_manager_conf(_Fd, []) ->
+ ok;
+write_manager_conf(Fd, [H|T]) ->
+ do_write_manager_conf(Fd, H),
+ write_manager_conf(Fd, T).
+
+do_write_manager_conf(Fd, {address = Tag, Val}) ->
+ io:format(Fd, "{~w, ~w}.~n", [Tag, Val]);
+do_write_manager_conf(Fd, {port = Tag, Val} ) ->
+ io:format(Fd, "{~w, ~w}.~n", [Tag, Val]);
+do_write_manager_conf(Fd, {engine_id = Tag, Val} ) ->
+ io:format(Fd, "{~w, \"~s\"}.~n", [Tag, Val]);
+do_write_manager_conf(Fd, {max_message_size = Tag, Val} ) ->
+ io:format(Fd, "{~w, ~w}.~n", [Tag, Val]);
+do_write_manager_conf(_Fd, Crap) ->
+ error({bad_manager_config, Crap}).
+
+
+%%
+%% ------ users.conf ------
+%%
+
+users_entry(UserId) ->
+ users_entry(UserId, snmpm_user_default).
+
+users_entry(UserId, UserMod) ->
+ users_entry(UserId, UserMod, undefined).
+
+users_entry(UserId, UserMod, UserData) ->
+ users_entry(UserId, UserMod, UserData, []).
+
+users_entry(UserId, UserMod, UserData, DefaultAgentConfig) ->
+ {UserId, UserMod, UserData, DefaultAgentConfig}.
+
+
+write_users_config(Dir, Conf) ->
+ Comment =
+"%% This file defines the users the manager handles\n"
+"%% Each row is a 4-tuple:\n"
+"%% {UserId, UserMod, UserData, DefaultAgentConfig}.\n"
+"%% For example\n"
+"%% {kalle, kalle_callback_user_mod, \"dummy\", []}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_users_config(Dir, Hdr, Conf).
+
+write_users_config(Dir, Hdr, Conf)
+ when is_list(Dir) andalso is_list(Hdr) andalso is_list(Conf) ->
+ Verify = fun() -> verify_users_conf(Conf) end,
+ Write = fun(Fd) -> write_users_conf(Fd, Hdr, Conf) end,
+ write_config_file(Dir, ?USERS_CONF_FILE, Verify, Write).
+
+
+append_users_config(Dir, Conf)
+ when is_list(Dir) andalso is_list(Conf) ->
+ Verify = fun() -> verify_users_conf(Conf) end,
+ Write = fun(Fd) -> write_users_conf(Fd, Conf) end,
+ append_config_file(Dir, ?USERS_CONF_FILE, Verify, Write).
+
+
+read_users_config(Dir) when is_list(Dir) ->
+ Verify = fun(Entry) -> verify_users_conf_entry(Entry) end,
+ read_config_file(Dir, ?USERS_CONF_FILE, Verify).
+
+
+verify_users_conf([]) ->
+ ok;
+verify_users_conf([H|T]) ->
+ verify_users_conf_entry(H),
+ verify_users_conf(T);
+verify_users_conf(X) ->
+ error({bad_users_conf, X}).
+
+verify_users_conf_entry(Entry) ->
+ {ok, _} = snmpm_config:check_user_config(Entry),
+ ok.
+
+write_users_conf(Fd, "", Conf) ->
+ write_users_conf(Fd, Conf);
+write_users_conf(Fd, Hdr, Conf) ->
+ io:format(Fd, "~s~n", [Hdr]),
+ write_users_conf(Fd, Conf).
+
+write_users_conf(_Fd, []) ->
+ ok;
+write_users_conf(Fd, [H|T]) ->
+ do_write_users_conf(Fd, H),
+ write_users_conf(Fd, T).
+
+do_write_users_conf(Fd, {Id, Mod, Data}) ->
+ do_write_users_conf(Fd, {Id, Mod, Data, []});
+do_write_users_conf(Fd, {Id, Mod, Data, DefaultAgentConfig}) ->
+ io:format(Fd, "{~w, ~w, ~w, ~w}.~n", [Id, Mod, Data, DefaultAgentConfig]);
+do_write_users_conf(_Fd, Crap) ->
+ error({bad_users_config, Crap}).
+
+
+%%
+%% ------ agents.conf ------
+%%
+
+agents_entry(UserId, TargetName, Comm, Ip, Port, EngineID, Timeout,
+ MaxMessageSize, Version, SecModel, SecName, SecLevel) ->
+ {UserId, TargetName, Comm, Ip, Port, EngineID, Timeout,
+ MaxMessageSize, Version, SecModel, SecName, SecLevel}.
+
+
+write_agents_config(Dir, Conf) ->
+ Comment =
+"%% This file defines the agents the manager handles\n"
+"%% Each row is a 12-tuple:\n"
+"%% {UserId, \n"
+"%% TargetName, Comm, Ip, Port, EngineID, Timeout, \n"
+"%% MaxMessageSize, Version, SecModel, SecName, SecLevel}\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_agents_config(Dir, Hdr, Conf).
+
+write_agents_config(Dir, Hdr, Conf)
+ when is_list(Dir) andalso is_list(Hdr) andalso is_list(Conf) ->
+ Verify = fun() -> verify_agents_conf(Conf) end,
+ Write = fun(Fd) -> write_agents_conf(Fd, Hdr, Conf) end,
+ write_config_file(Dir, ?AGENTS_CONF_FILE, Verify, Write).
+
+
+append_agents_config(Dir, Conf)
+ when is_list(Dir) andalso is_list(Conf) ->
+ Verify = fun() -> verify_agents_conf(Conf) end,
+ Write = fun(Fd) -> write_agents_conf(Fd, Conf) end,
+ append_config_file(Dir, ?AGENTS_CONF_FILE, Verify, Write).
+
+
+read_agents_config(Dir) ->
+ Verify = fun(Entry) -> verify_agents_conf_entry(Entry) end,
+ read_config_file(Dir, ?AGENTS_CONF_FILE, Verify).
+
+
+verify_agents_conf([]) ->
+ ok;
+verify_agents_conf([H|T]) ->
+ verify_agents_conf_entry(H),
+ verify_agents_conf(T);
+verify_agents_conf(X) ->
+ error({bad_agents_config, X}).
+
+verify_agents_conf_entry(Entry) ->
+ {ok, _} = snmpm_config:check_agent_config(Entry),
+ ok.
+
+write_agents_conf(Fd, "", Conf) ->
+ write_agents_conf(Fd, Conf);
+write_agents_conf(Fd, Hdr, Conf) ->
+ io:format(Fd, "~s~n", [Hdr]),
+ write_agents_conf(Fd, Conf).
+
+write_agents_conf(_Fd, []) ->
+ ok;
+write_agents_conf(Fd, [H|T]) ->
+ do_write_agents_conf(Fd, H),
+ write_agents_conf(Fd, T).
+
+do_write_agents_conf(Fd,
+ {UserId,
+ TargetName, Comm, Ip, Port, EngineID,
+ Timeout, MaxMessageSize, Version,
+ SecModel, SecName, SecLevel} = _A) ->
+ io:format(Fd,
+ "{~w, \"~s\", \"~s\", ~w, ~w, \"~s\", ~w, ~w, ~w, ~w, \"~s\", ~w}.~n", [UserId, TargetName, Comm, Ip, Port, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel]);
+do_write_agents_conf(_Fd, Crap) ->
+ error({bad_agents_config, Crap}).
+
+
+%%
+%% ------ usm.conf -----
+%%
+
+usm_entry(EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey) ->
+ {EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey}.
+
+usm_entry(EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey) ->
+ {EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey}.
+
+
+write_usm_config(Dir, Conf) ->
+ Comment =
+"%% This file defines the usm users the manager handles\n"
+"%% Each row is a 6 or 7-tuple:\n"
+"%% {EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey}\n"
+"%% {EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey}\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_usm_config(Dir, Hdr, Conf).
+
+write_usm_config(Dir, Hdr, Conf)
+ when is_list(Dir) andalso is_list(Hdr) andalso is_list(Conf) ->
+ Verify = fun() -> verify_usm_conf(Conf) end,
+ Write = fun(Fd) -> write_usm_conf(Fd, Hdr, Conf) end,
+ write_config_file(Dir, ?USM_USERS_CONF_FILE, Verify, Write).
+
+
+append_usm_config(Dir, Conf)
+ when is_list(Dir) andalso is_list(Conf) ->
+ Verify = fun() -> verify_usm_conf(Conf) end,
+ Write = fun(Fd) -> write_usm_conf(Fd, Conf) end,
+ append_config_file(Dir, ?USM_USERS_CONF_FILE, Verify, Write).
+
+
+read_usm_config(Dir)
+ when is_list(Dir) ->
+ Verify = fun(Entry) -> verify_usm_conf_entry(Entry) end,
+ read_config_file(Dir, ?USM_USERS_CONF_FILE, Verify).
+
+
+verify_usm_conf([]) ->
+ ok;
+verify_usm_conf([H|T]) ->
+ verify_usm_conf_entry(H),
+ verify_usm_conf(T);
+verify_usm_conf(X) ->
+ error({bad_usm_conf, X}).
+
+verify_usm_conf_entry(Entry) ->
+ {ok, _} = snmpm_config:check_usm_user_config(Entry),
+ ok.
+
+write_usm_conf(Fd, "", Conf) ->
+ write_usm_conf(Fd, Conf);
+write_usm_conf(Fd, Hdr, Conf) ->
+ io:format(Fd, "~s~n", [Hdr]),
+ write_usm_conf(Fd, Conf).
+
+write_usm_conf(_Fd, []) ->
+ ok;
+write_usm_conf(Fd, [H|T]) ->
+ do_write_usm_conf(Fd, H),
+ write_usm_conf(Fd, T).
+
+do_write_usm_conf(Fd,
+ {EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey}) ->
+ io:format(Fd, "{\"~s\", \"~s\", ~w, ~w, ~w, ~w}.~n",
+ [EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey]);
+do_write_usm_conf(Fd,
+ {EngineID, UserName, SecName,
+ AuthP, AuthKey, PrivP, PrivKey}) ->
+ io:format(Fd, "{\"~s\", \"~s\", \"~s\", �~w, ~w, ~w, ~w}.~n",
+ [EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey]);
+do_write_usm_conf(_Fd, Crap) ->
+ error({bad_usm_conf, Crap}).
+
+
+%% ---- config file wrapper functions ----
+
+write_config_file(Dir, File, Verify, Write) ->
+ snmp_config:write_config_file(Dir, File, Verify, Write).
+
+append_config_file(Dir, File, Verify, Write) ->
+ snmp_config:append_config_file(Dir, File, Verify, Write).
+
+read_config_file(Dir, File, Verify) ->
+ snmp_config:read_config_file(Dir, File, Verify).
+
+
+%% ---- config file utility functions ----
+
+header() ->
+ {Y,Mo,D} = date(),
+ {H,Mi,S} = time(),
+ io_lib:format("%% This file was generated by "
+ "~w (version-~s) ~w-~2.2.0w-~2.2.0w "
+ "~2.2.0w:~2.2.0w:~2.2.0w\n",
+ [?MODULE, ?version, Y, Mo, D, H, Mi, S]).
+
+
+error(R) ->
+ throw({error, R}).
diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl
new file mode 100644
index 0000000000..1a5400bf8e
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_config.erl
@@ -0,0 +1,3116 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% -------------------------------------------------------------------------
+%%
+%% Some of the stuff stored here should really be persistent!!
+%% (e.g. snmp-engine-boot)
+%%
+%% -------------------------------------------------------------------------
+
+-module(snmpm_config).
+
+-behaviour(gen_server).
+
+%% External exports
+-export([start_link/1, stop/0, is_started/0]).
+-export([register_user/4, unregister_user/1,
+ which_users/0,
+ user_info/0, user_info/1, user_info/2,
+
+ register_agent/3, unregister_agent/2,
+ agent_info/0, agent_info/2, agent_info/3, update_agent_info/4,
+ which_agents/0, which_agents/1,
+
+ is_known_engine_id/2,
+ get_agent_engine_id/1,
+ get_agent_engine_max_message_size/1,
+ get_agent_version/1,
+ get_agent_mp_model/1,
+ get_agent_user_id/1, get_agent_user_id/2,
+ get_agent_user_info/2,
+
+ system_info/0, system_info/1,
+ %% update_system_info/2,
+ get_engine_id/0, get_engine_max_message_size/0,
+
+ register_usm_user/3, unregister_usm_user/2,
+ which_usm_users/0, which_usm_users/1,
+ usm_user_info/3, update_usm_user_info/4,
+ get_usm_user/2, get_usm_user_from_sec_name/2,
+ is_usm_engine_id_known/1,
+ get_engine_boots/0, get_engine_time/0,
+ set_engine_boots/1, set_engine_time/1,
+ get_usm_eboots/1, get_usm_etime/1, get_usm_eltime/1,
+ set_usm_eboots/2, set_usm_etime/2, set_usm_eltime/2,
+ reset_usm_cache/1,
+
+
+ cre_counter/2,
+ incr_counter/2,
+
+ cre_stats_counter/2,
+ maybe_cre_stats_counter/2,
+ incr_stats_counter/2,
+ reset_stats_counter/1,
+ get_stats_counters/0, get_stats_counter/1,
+
+ load_mib/1, unload_mib/1, which_mibs/0,
+ make_mini_mib/0,
+ name_to_oid/1, oid_to_name/1, oid_to_type/1,
+
+ system_start_time/0,
+
+ info/0,
+ verbosity/1,
+
+ backup/1,
+
+ mk_target_name/3
+
+ ]).
+
+%% Backward compatibillity exports
+-export([
+ register_user/3,
+ unregister_agent/3,
+ update_agent_info/5,
+ is_known_engine_id/3,
+ get_agent_engine_id/2,
+ get_agent_engine_max_message_size/2,
+ get_agent_version/2,
+ get_agent_mp_model/2
+ ]).
+
+-export([check_manager_config/1,
+ check_user_config/1,
+ check_agent_config/1,
+ check_usm_user_config/1]).
+
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+
+%% Includes:
+-include_lib("kernel/include/file.hrl").
+-include("snmp_types.hrl").
+-include("snmpm_internal.hrl").
+-include("snmpm_usm.hrl").
+-include("snmp_debug.hrl").
+-include("snmp_verbosity.hrl").
+
+
+%% Types:
+-record(user, {id, mod, data, default_agent_config}).
+
+-record(state, {backup}).
+
+
+%% Macros and Constants:
+-define(SERVER, ?MODULE).
+-define(BACKUP_DB, snmpm_config_backup).
+-define(CONFIG_DB, snmpm_config_db).
+
+-define(DEFAULT_USER, default_user).
+
+-define(DEFAULT_AGENT_PORT, 161).
+
+-define(IRB_DEFAULT, auto).
+%% -define(IRB_DEFAULT, {user, timer:seconds(15)}).
+
+-define(USER_MOD_DEFAULT, snmpm_user_default).
+-define(USER_DATA_DEFAULT, undefined).
+
+%% -define(DEF_ADDR_TAG, default_addr_tag).
+-define(DEFAULT_TARGETNAME, default_agent).
+-define(DEF_PORT_TAG, default_port_tag).
+
+-ifdef(snmp_debug).
+-define(GS_START_LINK(Opts),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts],
+ [{debug,[trace]}])).
+-else.
+-define(GS_START_LINK(Opts),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts], [])).
+-endif.
+
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+start_link(Opts) ->
+ ?d("start_link -> entry with"
+ "~n Opts: ~p", [Opts]),
+ ?GS_START_LINK(Opts).
+
+stop() ->
+ call(stop).
+
+is_started() ->
+ call(is_started, 1000).
+
+backup(BackupDir) when is_list(BackupDir) ->
+ call({backup, BackupDir}).
+
+%% Backward compatibillity
+register_user(UserId, UserMod, UserData) ->
+ register_user(UserId, UserMod, UserData, []).
+
+register_user(UserId, UserMod, UserData, DefaultAgentConfig)
+ when (UserId =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
+ case (catch verify_user_behaviour(UserMod)) of
+ ok ->
+ Config = default_agent_config(DefaultAgentConfig),
+ call({register_user, UserId, UserMod, UserData, Config});
+ Error ->
+ Error
+ end;
+register_user(UserId, _UserMod, _UserData, DefaultAgentConfig)
+ when (UserId =/= ?DEFAULT_USER) ->
+ {error, {bad_default_agent_config, DefaultAgentConfig}};
+register_user(UserId, _, _, _) ->
+ {error, {bad_user_id, UserId}}.
+
+default_agent_config(DefaultAgentConfig) ->
+ {ok, SystemDefaultAgentConfig} = agent_info(),
+ default_agent_config(SystemDefaultAgentConfig, DefaultAgentConfig).
+
+default_agent_config([], DefaultAgentConfig) ->
+ DefaultAgentConfig;
+default_agent_config([{Key, _} = Entry|T], DefaultAgentConfig) ->
+ case lists:keysearch(Key, 1, DefaultAgentConfig) of
+ {value, _} ->
+ default_agent_config(T, DefaultAgentConfig);
+ false ->
+ default_agent_config(T, [Entry|DefaultAgentConfig])
+ end.
+
+
+verify_user_behaviour(UserMod) ->
+ case snmp_misc:verify_behaviour(snmpm_user, UserMod) of
+ ok ->
+ ok;
+ Error ->
+ %% This user may implement the old behaviour, check it
+ case snmp_misc:verify_behaviour(snmpm_user_old, UserMod) of
+ ok ->
+ ok;
+ _ ->
+ throw(Error)
+ end
+ end.
+
+
+unregister_user(UserId) when UserId =/= ?DEFAULT_USER ->
+ call({unregister_user, UserId});
+unregister_user(BadUserId) ->
+ {error, {bad_user_id, BadUserId}}.
+
+
+which_users() ->
+ Pattern = #user{id = '$1', _ = '_'},
+ Match = ets:match(snmpm_user_table, Pattern),
+ [UserId || [UserId] <- Match, UserId =/= ?DEFAULT_USER].
+
+
+user_info() ->
+ UserId = ?DEFAULT_USER,
+ case user_info(UserId) of
+ {ok, Mod, Data} ->
+ {ok, UserId, Mod, Data};
+ Error ->
+ Error
+ end.
+
+user_info(UserId) ->
+ case ets:lookup(snmpm_user_table, UserId) of
+ [#user{mod = UserMod, data = UserData}] ->
+ {ok, UserMod, UserData};
+ _ ->
+ {error, not_found}
+ end.
+
+user_info(UserId, Item) ->
+ case (catch do_user_info(UserId, Item)) of
+ {'EXIT', _} ->
+ {error, {not_found, Item}};
+ Val ->
+ {ok, Val}
+ end.
+
+do_user_info(UserId, module) ->
+ ets:lookup_element(snmpm_user_table, UserId, #user.mod);
+do_user_info(UserId, data) ->
+ ets:lookup_element(snmpm_user_table, UserId, #user.data);
+do_user_info(UserId, default_agent_config) ->
+ ets:lookup_element(snmpm_user_table, UserId, #user.default_agent_config);
+do_user_info(_UserId, BadItem) ->
+ error({not_found, BadItem}).
+
+
+%% A target-name constructed in this way is a string with the following
+%% <IP-address>:<Port>-<Version>
+%%
+mk_target_name(Addr0, Port, Config) when is_list(Config) ->
+ Version =
+ case lists:keysearch(version, 1, Config) of
+ {value, {_, V}} ->
+ V;
+ false ->
+ select_lowest_supported_version()
+ end,
+%% p("mk_target_name -> Version: ~p", [Version]),
+ case normalize_address(Addr0) of
+ {A, B, C, D} ->
+ lists:flatten(
+ io_lib:format("~w.~w.~w.~w:~w-~w", [A, B, C, D, Port, Version]));
+ [A, B, C, D] ->
+ lists:flatten(
+ io_lib:format("~w.~w.~w.~w:~w-~w", [A, B, C, D, Port, Version]));
+ _ ->
+ lists:flatten(
+ io_lib:format("~p:~w-~w", [Addr0, Port, Version]))
+ end.
+
+select_lowest_supported_version() ->
+ {ok, Versions} = system_info(versions),
+ select_lowest_supported_version([v1, v2, v3], Versions).
+
+select_lowest_supported_version([], Versions) ->
+ error({bad_versions, Versions});
+select_lowest_supported_version([H|T], Versions) ->
+ case lists:member(H, Versions) of
+ true ->
+ H;
+ false ->
+ select_lowest_supported_version(T, Versions)
+ end.
+
+
+register_agent(UserId, _TargetName, _Config) when (UserId =:= user_id) ->
+ {error, {bad_user_id, UserId}};
+register_agent(UserId, TargetName, Config)
+ when (is_list(TargetName) andalso
+ (length(TargetName) > 0) andalso
+ is_list(Config)) ->
+
+%% p("register_agent -> entry with"
+%% "~n UserId: ~p"
+%% "~n TargetName: ~p"
+%% "~n Config: ~p", [UserId, TargetName, Config]),
+
+ %% Check:
+ %% 1) That the mandatory configs are present
+ %% 2) That the illegal config user_id (used internally) is
+ %% not present
+ %% 3) Check that there are no invalid or erroneous configs
+ %% 4) Chack that the manager is capable to use the selected version
+ case verify_agent_config(Config) of
+ ok ->
+ call({register_agent, UserId, TargetName, Config});
+ Error ->
+ Error
+ end.
+
+
+verify_agent_config(Conf) ->
+ ok = verify_mandatory(Conf, [engine_id, address, reg_type]),
+ case verify_invalid(Conf, [user_id]) of
+ ok ->
+ case verify_agent_config2(Conf) of
+ ok ->
+ {ok, Vsns} = system_info(versions),
+ Vsn =
+ case lists:keysearch(version, 1, Conf) of
+ {value, {version, V}} ->
+ V;
+ false ->
+ v1
+ end,
+ case lists:member(Vsn, Vsns) of
+ true ->
+ ok;
+ false ->
+ {error, {version_not_supported_by_manager, Vsn, Vsns}}
+ end
+ end;
+ Error ->
+ Error
+ end.
+
+verify_agent_config2(Conf) ->
+ verify_agent2(Conf).
+
+
+unregister_agent(UserId, TargetName) ->
+ call({unregister_agent, UserId, TargetName}).
+
+unregister_agent(UserId, Addr0, Port) ->
+ Addr = normalize_address(Addr0),
+ case do_agent_info(Addr, Port, target_name) of
+ {ok, TargetName} ->
+ unregister_agent(UserId, TargetName);
+ Error ->
+ Error
+ end.
+
+agent_info() ->
+ agent_info(?DEFAULT_TARGETNAME, all).
+
+agent_info(TargetName, all) ->
+ case ets:match_object(snmpm_agent_table, {{TargetName, '_'}, '_'}) of
+ [] ->
+ {error, not_found};
+ All ->
+ {ok, [{Item, Val} || {{_, Item}, Val} <- All]}
+ end;
+agent_info(TargetName, Item) ->
+ case ets:lookup(snmpm_agent_table, {TargetName, Item}) of
+ [{_, Val}] ->
+ {ok, Val};
+ [] ->
+ {error, not_found}
+ end.
+
+agent_info(Addr0, Port, Item) ->
+ Addr = normalize_address(Addr0),
+ do_agent_info(Addr, Port, Item).
+
+do_agent_info(Addr, Port, target_name = Item) ->
+ case ets:lookup(snmpm_agent_table, {Addr, Port, Item}) of
+ [{_, Val}] ->
+ {ok, Val};
+ [] ->
+ {error, not_found}
+ end;
+do_agent_info(Addr, Port, Item) ->
+ case do_agent_info(Addr, Port, target_name) of
+ {ok, TargetName} ->
+ agent_info(TargetName, Item);
+ Error ->
+ Error
+ end.
+
+
+which_agents() ->
+ which_agents('_').
+
+which_agents(UserId) ->
+ Pat = {{'$1', user_id}, UserId},
+ Agents = ets:match(snmpm_agent_table, Pat),
+ [TargetName || [TargetName] <- Agents].
+
+
+update_agent_info(UserId, TargetName, Item, Val0)
+ when (Item =/= user_id) ->
+ case (catch verify_val(Item, Val0)) of
+ {ok, Val} ->
+ call({update_agent_info, UserId, TargetName, Item, Val});
+ Error ->
+ Error
+ end.
+
+%% Backward compatibillity
+update_agent_info(UserId, Addr, Port, Item, Val) ->
+ case agent_info(Addr, Port, target_name) of
+ {ok, TargetName} ->
+ update_agent_info(UserId, TargetName, Item, Val);
+ Error ->
+ Error
+ end.
+
+is_known_engine_id(EngineID, TargetName) ->
+ case agent_info(TargetName, engine_id) of
+ {ok, EngineID} ->
+ true;
+ {ok, _OtherEngineID} ->
+ false;
+ _ ->
+ false
+ end.
+
+%% Backward compatibillity
+is_known_engine_id(EngineID, Addr, Port) ->
+ case agent_info(Addr, Port, target_name) of
+ {ok, TargetName} ->
+ is_known_engine_id(EngineID, TargetName);
+ _ ->
+ false
+ end.
+
+get_agent_engine_id(TargetName) ->
+ agent_info(TargetName, engine_id).
+
+%% Backward compatibillity
+get_agent_engine_id(Addr, Port) ->
+ agent_info(Addr, Port, engine_id).
+
+get_agent_engine_max_message_size(TargetName) ->
+ agent_info(TargetName, max_message_size).
+
+%% Backward compatibillity
+get_agent_engine_max_message_size(Addr, Port) ->
+ agent_info(Addr, Port, max_message_size).
+
+get_agent_version(TargetName) ->
+ agent_info(TargetName, version).
+
+%% Backward compatibillity
+get_agent_version(Addr, Port) ->
+ agent_info(Addr, Port, version).
+
+get_agent_mp_model(TargetName) ->
+ case agent_info(TargetName, version) of
+ {ok, v2} ->
+ {ok, v2c};
+ {ok, V} ->
+ {ok, V};
+ Err ->
+ Err
+ end.
+
+%% Backward compatibillity
+get_agent_mp_model(Addr, Port) ->
+ case agent_info(Addr, Port, target_name) of
+ {ok, TargetName} ->
+ get_agent_mp_model(TargetName);
+ Error ->
+ Error
+ end.
+
+get_agent_user_id(TargetName) ->
+ agent_info(TargetName, user_id).
+
+get_agent_user_id(Addr, Port) ->
+ agent_info(Addr, Port, user_id).
+
+get_agent_user_info(Addr, Port) ->
+ case agent_info(Addr, Port, target_name) of
+ {ok, Target} ->
+ case agent_info(Target, reg_type) of
+ {ok, RegType} ->
+ case agent_info(Target, user_id) of
+ {ok, UserId} ->
+ {ok, UserId, Target, RegType};
+ {error, not_found} ->
+ {error, {user_id_not_found, Target}}
+ end;
+ {error, not_found} ->
+ {error, {reg_type_not_found, Target}}
+ end;
+ {error, not_found} ->
+ {error, {target_name_not_found, Addr, Port}}
+ end.
+
+
+
+system_info() ->
+ system_info(all).
+
+system_info(all) ->
+ lists:sort(ets:tab2list(snmpm_config_table));
+system_info(Key) when is_atom(Key) ->
+ case ets:lookup(snmpm_config_table, Key) of
+ [{_, Val}] ->
+ {ok, Val};
+ _ ->
+ {error, not_found}
+ end.
+
+%% update_system_info(Key, Val) ->
+%% call({update_system_info, Key, Val}).
+
+system_start_time() ->
+ system_info(system_start_time).
+
+get_engine_id() ->
+ system_info(engine_id).
+
+get_engine_max_message_size() ->
+ system_info(max_message_size).
+
+get_engine_boots() ->
+ case dets:lookup(?CONFIG_DB, snmp_engine_boots) of
+ [{_, Boots}] ->
+ {ok, Boots};
+ _ ->
+ {error, not_found}
+ end.
+
+set_engine_boots(Boots) ->
+ case (whereis(?SERVER) =:= self()) of
+ false ->
+ call({set_engine_boots, Boots});
+ true ->
+ dets:insert(?CONFIG_DB, {snmp_engine_boots, Boots}),
+ ok
+ end.
+
+
+get_engine_time() ->
+ case system_info(snmp_engine_base) of
+ {ok, EngineBase} ->
+ {ok, snmp_misc:now(sec) - EngineBase};
+ Error ->
+ Error
+ end.
+
+get_usm_eboots(SnmpEngineID) ->
+ Key = {eboots, SnmpEngineID},
+ case get_usm_cache(Key) of
+ {ok, Boots} ->
+ {ok, Boots};
+ _ ->
+ {ok, 0}
+ end.
+
+get_usm_etime(SnmpEngineID) ->
+ Key = {etime, SnmpEngineID},
+ case get_usm_cache(Key) of
+ {ok, Diff} ->
+ {ok, snmp_misc:now(sec) - Diff};
+ _ ->
+ {ok, 0}
+ end.
+
+get_usm_eltime(SnmpEngineID) ->
+ Key = {eltime, SnmpEngineID},
+ case get_usm_cache(Key) of
+ {ok, Time} ->
+ {ok, Time};
+ _ ->
+ {ok, 0}
+ end.
+
+get_usm_cache(Key) ->
+ case ets:lookup(snmpm_usm_table, {usm_cache, Key}) of
+ [{_, Val}] ->
+ {ok, Val};
+ _ ->
+ {error, not_found}
+ end.
+
+set_usm_eboots(SnmpEngineID, EngineBoots) ->
+ set_usm_cache({eboots, SnmpEngineID}, EngineBoots).
+
+set_usm_etime(SnmpEngineID, Diff) ->
+ set_usm_cache({etime, SnmpEngineID}, Diff).
+
+set_usm_eltime(SnmpEngineID, Time) ->
+ set_usm_cache({eltime, SnmpEngineID}, Time).
+
+set_usm_cache(Key, Val) ->
+ call({set_usm_cache, Key, Val}).
+
+reset_usm_cache(SnmpEngineID) ->
+ case (whereis(?SERVER) =:= self()) of
+ false ->
+ call({reset_usm_cache, SnmpEngineID});
+ true ->
+ Pat = {{usm_cache, {'_', SnmpEngineID}}, '_'},
+ ets:match_delete(snmpm_usm_table, Pat),
+ ok
+ end.
+
+set_engine_time(Time) ->
+ call({set_engine_time, Time}).
+
+register_usm_user(EngineID, Name, Config)
+ when is_list(EngineID) andalso is_list(Name) ->
+ case verify_usm_user_config(EngineID, Name, Config) of
+ {ok, User} ->
+ call({register_usm_user, User});
+ Error ->
+ Error
+ end.
+
+unregister_usm_user(EngineID, Name)
+ when is_list(EngineID) andalso is_list(Name) ->
+ call({unregister_usm_user, EngineID, Name}).
+
+verify_usm_user_config(EngineID, Name, Config) ->
+ %% case verify_mandatory(Config, []) of
+ %% ok ->
+ %% case verify_invalid(Config, [engine_id, name]) of
+ %% ok ->
+ %% verify_usm_user_config2(EngineID, Name, Config);
+ %% Error ->
+ %% Error
+ %% end;
+ %% Error ->
+ %% Error
+ %% end.
+ ok = verify_mandatory(Config, []),
+ case verify_invalid(Config, [engine_id, name]) of
+ ok ->
+ verify_usm_user_config2(EngineID, Name, Config);
+ Error ->
+ Error
+ end.
+
+verify_usm_user_config2(EngineID, Name, Config) ->
+ SecName = verify_usm_user_get(sec_name, Name, Config),
+ Auth = verify_usm_user_get(auth, usmNoAuthProtocol, Config),
+ AuthKey = verify_usm_user_get(auth_key, [], Config),
+ Priv = verify_usm_user_get(priv, usmNoPrivProtocol, Config),
+ PrivKey = verify_usm_user_get(priv_key, [], Config),
+ User = {EngineID, Name, SecName, Auth, AuthKey, Priv, PrivKey},
+ verify_usm_user(User).
+
+verify_usm_user_get(Item, Default, Config) ->
+ case lists:keysearch(Item, 1, Config) of
+ {value, {_, Val}} ->
+ Val;
+ false ->
+ Default
+ end.
+
+which_usm_users() ->
+ Pattern = {usm_key('$1', '$2'), '_'},
+ Match = ets:match(snmpm_usm_table, Pattern),
+ [{EngineID, UserName} || [EngineID, UserName] <- Match].
+
+which_usm_users(EngineID) ->
+ Pattern = {usm_key(EngineID, '$1'), '_'},
+ Match = ets:match(snmpm_usm_table, Pattern),
+ [UserName || [UserName] <- Match].
+
+usm_user_info(EngineID, UserName, Item) ->
+ case ets:lookup(snmpm_usm_table, usm_key(EngineID, UserName)) of
+ [] ->
+ {error, not_found};
+ [{_Key, UsmUser}] ->
+ do_usm_user_info(UsmUser, Item)
+ end.
+
+do_usm_user_info(#usm_user{sec_name = SecName}, sec_name) ->
+ {ok, SecName};
+do_usm_user_info(#usm_user{auth = AuthP}, auth) ->
+ {ok, AuthP};
+do_usm_user_info(#usm_user{auth_key = AuthKey}, auth_key) ->
+ {ok, AuthKey};
+do_usm_user_info(#usm_user{priv = PrivP}, priv) ->
+ {ok, PrivP};
+do_usm_user_info(#usm_user{priv_key = PrivKey}, priv_key) ->
+ {ok, PrivKey};
+do_usm_user_info(#usm_user{engine_id = EngineID}, engine_id) ->
+ {ok, EngineID};
+do_usm_user_info(#usm_user{name = Name}, name) ->
+ {ok, Name};
+do_usm_user_info(_, Item) ->
+ {error, {bad_iten, Item}}.
+
+update_usm_user_info(EngineID, UserName, Item, Val)
+ when (Item =/= engine_id) andalso (Item =/= name) ->
+ call({update_usm_user_info, EngineID, UserName, Item, Val}).
+
+get_usm_user(EngineID, UserName) ->
+ Key = usm_key(EngineID, UserName),
+ case ets:lookup(snmpm_usm_table, Key) of
+ [{_, User}] ->
+ {ok, User};
+ _ ->
+ {error, not_found}
+ end.
+
+is_usm_engine_id_known(EngineID) ->
+ Pattern = {usm_key(EngineID, '$1'), '_'},
+ case ets:match(snmpm_usm_table, Pattern) of
+ [] ->
+ false;
+ _ ->
+ true
+ end.
+
+get_usm_user_from_sec_name(EngineID, SecName) ->
+ %% Since the normal mapping between UserName and SecName is the
+ %% identity-function, we first try to use the SecName as UserName,
+ %% and check the resulting row. If it doesn't match, we'll have to
+ %% loop through the entire table.
+ Key = usm_key(EngineID, SecName),
+ case ets:lookup(snmpm_usm_table, Key) of
+ [{Key, #usm_user{sec_name = SecName} = User}] ->
+ {ok, User};
+ _ ->
+ %% That did not work, so we have to search
+ Pattern = {usm_key(EngineID, '_'),
+ #usm_user{sec_name = SecName, _ = '_'}},
+ case ets:match_object(snmpm_usm_table, Pattern) of
+ [{_, User}|_] ->
+ {ok, User};
+ _ ->
+ {error, not_found}
+ end
+ end.
+
+
+%% Wrap-counters (wrapping at 2147483647 or 4294967295)
+cre_counter(Counter, Initial) ->
+ case (whereis(?SERVER) =:= self()) of
+ false ->
+ call({cre_counter, Counter, Initial});
+ true ->
+ ets:insert(snmpm_counter_table, {Counter, Initial}),
+ Initial
+ end.
+
+incr_counter(usm_salt, Incr) -> % Backward compatibillity (upgrade)
+ incr_counter(usm_des_salt, Incr); % Backward compatibillity (upgrade)
+incr_counter(usm_des_salt, Incr) ->
+ incr_counter(usm_des_salt, Incr, 4294967295);
+incr_counter(usm_aes_salt, Incr) ->
+ incr_counter(usm_aes_salt, Incr, 36893488147419103231);
+incr_counter(Counter, Incr) ->
+ incr_counter(Counter, Incr, 2147483647).
+
+incr_counter(Counter, Incr, Wrap) ->
+ case (catch ets:update_counter(snmpm_counter_table, Counter, Incr)) of
+ {'EXIT', _} ->
+ cre_counter(Counter, Incr);
+ NewVal when NewVal =< Wrap ->
+ NewVal;
+ N ->
+ cre_counter(Counter, N - Wrap)
+ end.
+
+
+maybe_cre_stats_counter(Counter, Initial) ->
+ case ets:lookup(snmpm_stats_table, Counter) of
+ [_] ->
+ ok;
+ _ ->
+ cre_stats_counter(Counter, Initial)
+ end.
+
+cre_stats_counter(Counter, Initial) ->
+ case (whereis(?SERVER) =:= self()) of
+ false ->
+ call({cre_stats_counter, Counter, Initial});
+ true ->
+ ets:insert(snmpm_stats_table, {Counter, Initial}),
+ Initial
+ end.
+
+incr_stats_counter(Counter, Incr) ->
+ case (catch ets:update_counter(snmpm_stats_table, Counter, Incr)) of
+ {'EXIT', _} ->
+ cre_counter(Counter, Incr);
+ NewVal ->
+ NewVal
+ end.
+
+reset_stats_counter(Counter) ->
+ case (whereis(?SERVER) =:= self()) of
+ false ->
+ call({reset_stats_counter, Counter});
+ true ->
+ ets:insert(snmpm_stats_table, {Counter, 0})
+ end,
+ ok.
+
+get_stats_counter(Counter) ->
+ case ets:lookup(snmpm_stats_table, Counter) of
+ [{Counter, Value}] ->
+ {ok, Value};
+ _ ->
+ {error, not_found}
+ end.
+
+get_stats_counters() ->
+ ets:tab2list(snmpm_stats_table).
+
+load_mib(Mib) when is_list(Mib) ->
+ call({load_mib, Mib}).
+
+unload_mib(Mib) when is_list(Mib) ->
+ call({unload_mib, Mib}).
+
+which_mibs() ->
+ Pattern = {{mib, '_'}, '$1', '$2'},
+ Mibs = ets:match(snmpm_mib_table, Pattern),
+ [list_to_tuple(X) || X <- Mibs].
+
+name_to_oid(Name) ->
+ Pat = {{mini_mib, '$1'}, Name, '_', '_'},
+ case ets:match(snmpm_mib_table, Pat) of
+ [] ->
+ {error, not_found};
+ X ->
+ Oids = [Oid || [Oid] <- X],
+ {ok, Oids}
+ end.
+
+oid_to_name(Oid) ->
+ case ets:lookup(snmpm_mib_table, {mini_mib, Oid}) of
+ [{_, Name, _, _}] ->
+ {ok, Name};
+ [] ->
+ {error, not_found}
+ end.
+
+oid_to_type(Oid) ->
+ case ets:lookup(snmpm_mib_table, {mini_mib, Oid}) of
+ [{_, _, Type, _}] ->
+ {ok, Type};
+ [] ->
+ {error, not_found}
+ end.
+
+make_mini_mib() ->
+ Pat = {{mini_mib, '$1'}, '$2', '$3', '_'},
+ MiniElems = ets:match(snmpm_mib_table, Pat),
+ lists:keysort(1, [list_to_tuple(MiniElem) || MiniElem <- MiniElems]).
+
+
+info() ->
+ call(info).
+
+verbosity(Verbosity) ->
+ case ?vvalidate(Verbosity) of
+ Verbosity ->
+ call({verbosity, Verbosity});
+ _ ->
+ {error, {invalid_verbosity, Verbosity}}
+ end.
+
+
+%%%-------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%-------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%--------------------------------------------------------------------
+init([Opts]) ->
+% put(sname, mconf),
+% put(verbosity, trace),
+ ?d("init -> entry with"
+ "~n Opts: ~p", [Opts]),
+ case (catch do_init(Opts)) of
+ ok ->
+ {ok, #state{}};
+ {error, Reason} ->
+ error_msg("init error: ~p", [Reason]),
+ {stop, Reason};
+ {'EXIT', Reason} ->
+ error_msg("init exit: ~p", [Reason]),
+ {stop, Reason};
+ Error ->
+ error_msg("init failed: ~p", [Error]),
+ {stop, Error}
+ end.
+
+do_init(Opts) ->
+ process_flag(trap_exit, true),
+ %% Mandatory = [versions, {config, [dir]}],
+ Mandatory = [{config, [dir, db_dir]}],
+ verify_options(Opts, Mandatory),
+
+ ets:new(snmpm_counter_table, [set, public, named_table, {keypos, 1}]),
+ ets:new(snmpm_stats_table, [set, public, named_table, {keypos, 1}]),
+ ets:new(snmpm_mib_table, [set, protected, named_table, {keypos, 1}]),
+ ets:new(snmpm_config_table, [set, protected, named_table, {keypos, 1}]),
+ ets:new(snmpm_agent_table, [set, protected, named_table, {keypos, 1}]),
+ ets:new(snmpm_user_table, [set, protected, named_table, {keypos, 2}]),
+ ets:new(snmpm_usm_table, [set, protected, named_table, {keypos, 1}]),
+
+ %% -- System start time --
+ ets:insert(snmpm_config_table, {system_start_time, snmp_misc:now(cs)}),
+
+ %% --- Own options (dir and db_dir mandatory) ---
+ ConfOpts = get_opt(config, Opts, []),
+ ConfVerb = get_opt(verbosity, ConfOpts, silence),
+ ConfDir = get_opt(dir, ConfOpts),
+ ConfDbDir = get_opt(db_dir, ConfOpts),
+ ConfDbInitErr = get_opt(db_init_error, ConfOpts, terminate),
+ ConfRep = get_opt(repair, ConfOpts, true),
+ ConfAs = get_opt(auto_save, ConfOpts, 5000),
+ ets:insert(snmpm_config_table, {config_verbosity, ConfVerb}),
+ ets:insert(snmpm_config_table, {config_dir, ConfDir}),
+ ets:insert(snmpm_config_table, {config_db_dir, ConfDbDir}),
+ ets:insert(snmpm_config_table, {config_db_init_error, ConfDbInitErr}),
+ ets:insert(snmpm_config_table, {config_repair, ConfRep}),
+ ets:insert(snmpm_config_table, {config_auto_save, ConfAs}),
+ put(sname, mconf),
+ put(verbosity, ConfVerb),
+ ?vlog("starting", []),
+
+ %% -- Create dets file used for storing persistent data --
+ dets_open(ConfDbDir, ConfDbInitErr, ConfRep, ConfAs),
+
+ %% -- Prio (optional) --
+ Prio = get_opt(priority, Opts, normal),
+ ets:insert(snmpm_config_table, {prio, Prio}),
+ process_flag(priority, Prio),
+
+ %% -- Server (optional) --
+ ServerOpts = get_opt(server, Opts, []),
+ ServerVerb = get_opt(verbosity, ServerOpts, silence),
+ ServerGct = get_opt(timeout, ServerOpts, 30000),
+ ServerMt = get_opt(multi_threaded, ServerOpts, true),
+ ets:insert(snmpm_config_table, {server_verbosity, ServerVerb}),
+ ets:insert(snmpm_config_table, {server_timeout, ServerGct}),
+ ets:insert(snmpm_config_table, {server_multi_threaded, ServerMt}),
+
+ %% -- Mibs (optional) --
+ ?vdebug("initiate mini mib", []),
+ Mibs = get_opt(mibs, Opts, []),
+ ets:insert(snmpm_config_table, {mibs, Mibs}),
+ init_mini_mib(Mibs),
+
+ %% -- Net-if (optional) --
+ ?vdebug("net_if options", []),
+ NetIfIrb =
+ case get_opt(inform_request_behaviour, Opts, ?IRB_DEFAULT) of
+ user ->
+ {user, timer:seconds(15)};
+ Irb ->
+ Irb
+ end,
+ NetIfOpts = get_opt(net_if, Opts, []),
+ NetIfMod = get_opt(module, NetIfOpts, snmpm_net_if),
+ NetIfVerb = get_opt(verbosity, NetIfOpts, silence),
+ NetIfOptions = get_opt(options, NetIfOpts, []),
+ ets:insert(snmpm_config_table, {net_if_module, NetIfMod}),
+ ets:insert(snmpm_config_table, {net_if_verbosity, NetIfVerb}),
+ ets:insert(snmpm_config_table, {net_if_irb, NetIfIrb}),
+ ets:insert(snmpm_config_table, {net_if_options, NetIfOptions}),
+
+ %% -- Versions (optional) --
+ %% -- Versions (mandatory) ???????????? --
+ ?vdebug("versions", []),
+ Vsns = get_opt(versions, Opts, [v1, v2, v3]),
+ ets:insert(snmpm_config_table, {versions, Vsns}),
+
+ %% -- Audit trail log (optional) --
+ ?vdebug("audit trail log", []),
+ case get_opt(audit_trail_log, Opts, []) of
+ [] ->
+ ?vtrace("no ATL", []),
+ ets:insert(snmpm_config_table, {audit_trail_log, false});
+ AuditTrailLogOpts ->
+ ?vtrace("ATL options: ~p", [AuditTrailLogOpts]),
+ ets:insert(snmpm_config_table, {audit_trail_log, true}),
+ LogDir = get_atl_dir(AuditTrailLogOpts),
+ LogType = get_atl_type(AuditTrailLogOpts),
+ LogSize = get_atl_size(AuditTrailLogOpts),
+ LogRep = get_atl_repair(AuditTrailLogOpts),
+ ets:insert(snmpm_config_table, {audit_trail_log_dir, LogDir}),
+ ets:insert(snmpm_config_table, {audit_trail_log_type, LogType}),
+ ets:insert(snmpm_config_table, {audit_trail_log_size, LogSize}),
+ ets:insert(snmpm_config_table, {audit_trail_log_repair, LogRep})
+ end,
+
+ %% -- System default agent config --
+ ?vdebug("system default agent config", []),
+ init_agent_default(),
+
+ %% -- User (optional) --
+ ?vdebug("default user", []),
+ DefUserMod = get_opt(def_user_mod, Opts, ?USER_MOD_DEFAULT),
+ DefUserData = get_opt(def_user_data, Opts, ?USER_DATA_DEFAULT),
+ ets:insert(snmpm_config_table, {def_user_mod, DefUserMod}),
+ ets:insert(snmpm_config_table, {def_user_data, DefUserData}),
+
+ {ok, SystemDefaultAgentConfig} = agent_info(),
+ DefUser = #user{id = ?DEFAULT_USER,
+ mod = DefUserMod,
+ data = DefUserData,
+ default_agent_config = SystemDefaultAgentConfig},
+ ok = handle_register_user(DefUser),
+
+ %% -- Note store --
+ ?vdebug("note store", []),
+ NoteStoreOpts = get_opt(note_store, Opts, []),
+ NoteStoreVerb = get_opt(verbosity, NoteStoreOpts, silence),
+ NoteStoreTimeout = get_opt(timeout, NoteStoreOpts, 30000),
+ ets:insert(snmpm_config_table, {note_store_verbosity, NoteStoreVerb}),
+ ets:insert(snmpm_config_table, {note_store_timeout, NoteStoreTimeout}),
+
+ %% -- Manager SNMP config --
+ ?vdebug("manager snmp config", []),
+ MgrConf = read_manager_config_file(ConfDir),
+ init_manager_config(MgrConf),
+
+ %% -- User config --
+ ?vdebug("users config", []),
+ Users = read_users_config_file(ConfDir),
+ init_users_config(Users),
+
+ %% -- Agents config --
+ ?vdebug("agents config", []),
+ Agents = read_agents_config_file(ConfDir),
+ init_agents_config(Agents),
+
+ %% -- USM config --
+ UsmUsers = read_usm_config_file(ConfDir),
+ init_usm_users_config(UsmUsers),
+
+ %% -- snmp engine init --
+ init_engine(),
+
+ ?vlog("started", []),
+ ok.
+
+
+dets_open(Dir, DbInitError, Repair, AutoSave) ->
+ Name = ?CONFIG_DB,
+ Filename = dets_filename(Name, Dir),
+ case file:read_file_info(Filename) of
+ {ok, _} ->
+ %% File exists
+ case do_dets_open(Name, Filename, Repair, AutoSave) of
+ {ok, _Dets} ->
+ ok;
+ {error, Reason1} ->
+ info_msg("Corrupt local database: ~p", [Filename]),
+ case DbInitError of
+ terminate ->
+ error({failed_reopen_dets, Filename, Reason1});
+ _ ->
+ Saved = Filename ++ ".saved",
+ file:rename(Filename, Saved),
+ case do_dets_open(Name, Filename,
+ Repair, AutoSave) of
+ {ok, _Dets} ->
+ ok;
+ {error, Reason2} ->
+ error({failed_open_dets, Filename,
+ Reason1, Reason2})
+ end
+ end
+ end;
+ _ ->
+ case do_dets_open(Name, Filename, Repair, AutoSave) of
+ {ok, _Dets} ->
+ ok;
+ {error, Reason} ->
+ error({failed_open_dets, Filename, Reason})
+ end
+ end.
+
+do_dets_open(Name, Filename, Repair, AutoSave) ->
+ Opts = [{repair, Repair},
+ {auto_save, AutoSave},
+ {file, Filename}],
+ dets:open_file(Name, Opts).
+
+
+dets_filename(Name, Dir) when is_atom(Name) ->
+ dets_filename(atom_to_list(Name), Dir);
+dets_filename(Name, Dir) ->
+ filename:join(dets_filename1(Dir), Name).
+
+dets_filename1([]) -> ".";
+dets_filename1(Dir) -> Dir.
+
+
+%% ------------------------------------------------------------------------
+
+init_engine() ->
+ case get_engine_boots() of
+ {ok, Val} when Val < 2147483647 ->
+ set_engine_boots(Val + 1);
+ {ok, _} ->
+ ok;
+ _ ->
+ set_engine_boots(1)
+ end,
+ reset_engine_base().
+
+reset_engine_base() ->
+ ets:insert(snmpm_config_table, {snmp_engine_base, snmp_misc:now(sec)}).
+
+
+%% ------------------------------------------------------------------------
+
+verify_options(Opts, Mandatory) ->
+ ?d("verify_options -> entry with"
+ "~n Opts: ~p"
+ "~n Mandatory: ~p", [Opts, Mandatory]),
+ verify_mandatory_options(Opts, Mandatory),
+ verify_options(Opts).
+
+%% mandatory() -> [mand()]
+%% mand() -> atom() | {atom, [atom()]}
+verify_mandatory_options(_Opts, []) ->
+ ok;
+verify_mandatory_options(Opts, [Mand|Mands]) ->
+ verify_mandatory_option(Opts, Mand),
+ verify_mandatory_options(Opts, Mands).
+
+verify_mandatory_option(Opts, {Mand, MandSubOpts}) ->
+ ?d("verify_mandatory_option -> entry with"
+ "~n Mand: ~p"
+ "~n MandSubObjs: ~p", [Mand, MandSubOpts]),
+ case lists:keysearch(Mand, 1, Opts) of
+ {value, {Mand, SubOpts}} ->
+ verify_mandatory_options(SubOpts, MandSubOpts);
+ false ->
+ ?d("missing mandatory option: ~w [~p]", [Mand, MandSubOpts]),
+ error({missing_mandatory, Mand, MandSubOpts})
+ end;
+verify_mandatory_option(Opts, Mand) ->
+ ?d("verify_mandatory_option -> entry with"
+ "~n Mand: ~p", [Mand]),
+ case lists:keymember(Mand, 1, Opts) of
+ true ->
+ ok;
+ false ->
+ ?d("missing mandatory option: ~w", [Mand]),
+ error({missing_mandatory, Mand})
+ end.
+
+verify_options([]) ->
+ ?d("verify_options -> done", []),
+ ok;
+verify_options([Opt|Opts]) ->
+ ?d("verify_options -> entry with"
+ "~n Opt: ~p", [Opt]),
+ verify_option(Opt),
+ verify_options(Opts).
+
+verify_option({prio, Prio}) ->
+ verify_prio(Prio);
+verify_option({mibs, Mibs}) ->
+ verify_mibs(Mibs);
+verify_option({inform_request_behaviour, IRB}) ->
+ verify_irb(IRB);
+verify_option({net_if, NetIfOpts}) ->
+ verify_net_if_opts(NetIfOpts);
+verify_option({server, ServerOpts}) ->
+ verify_server_opts(ServerOpts);
+verify_option({note_store, NoteStoreOpts}) ->
+ verify_note_store_opts(NoteStoreOpts);
+verify_option({config, ConfOpts}) ->
+ verify_config_opts(ConfOpts);
+verify_option({versions, Vsns}) ->
+ verify_versions(Vsns);
+verify_option({audit_trail_log, LogOpts}) ->
+ Mandatory = [dir, size],
+ case (catch verify_mandatory_options(LogOpts, Mandatory)) of
+ ok ->
+ verify_audit_trail_log_opts(LogOpts);
+ {error, {missing_mandatory, LogOpt}} ->
+ error({missing_mandatory, audit_trail_log, LogOpt})
+ end;
+verify_option({def_user_mod, Mod}) ->
+ verify_module(def_user_mod, Mod);
+verify_option({def_user_data, _Data}) ->
+ ok;
+verify_option(Opt) ->
+ {error, {invalid_option, Opt}}.
+
+verify_prio(Prio) when is_atom(Prio) ->
+ ok;
+verify_prio(Prio) ->
+ error({invalid_prio, Prio}).
+
+verify_irb(auto) ->
+ ok;
+verify_irb(user) ->
+ ok;
+verify_irb({user, To}) when is_integer(To) andalso (To > 0) ->
+ ok;
+verify_irb(IRB) ->
+ error({invalid_irb, IRB}).
+
+verify_mibs([]) ->
+ ok;
+verify_mibs([Mib|Mibs]) when is_list(Mib) ->
+ verify_mibs(Mibs);
+verify_mibs(Mibs) ->
+ error({invalid_mibs, Mibs}).
+
+verify_config_opts([]) ->
+ ok;
+verify_config_opts([{verbosity, Verbosity}|Opts]) ->
+ verify_verbosity(Verbosity),
+ verify_config_opts(Opts);
+verify_config_opts([{dir, Dir}|Opts]) ->
+ verify_conf_dir(Dir),
+ verify_config_opts(Opts);
+verify_config_opts([{db_dir, Dir}|Opts]) ->
+ verify_conf_db_dir(Dir),
+ verify_config_opts(Opts);
+verify_config_opts([{db_init_error, DbInitErr}|Opts]) ->
+ verify_conf_db_init_error(DbInitErr),
+ verify_config_opts(Opts);
+verify_config_opts([{repair, Repair}|Opts]) ->
+ verify_conf_repair(Repair),
+ verify_config_opts(Opts);
+verify_config_opts([{auto_save, AutoSave}|Opts]) ->
+ verify_conf_auto_save(AutoSave),
+ verify_config_opts(Opts);
+verify_config_opts([Opt|_]) ->
+ error({invalid_config_option, Opt}).
+
+verify_server_opts([]) ->
+ ok;
+verify_server_opts([{verbosity, Verbosity}|Opts]) ->
+ verify_verbosity(Verbosity),
+ verify_server_opts(Opts);
+verify_server_opts([{timeout, Timeout}|Opts]) ->
+ verify_server_timeout(Timeout),
+ verify_server_opts(Opts);
+verify_server_opts([Opt|_]) ->
+ error({invalid_server_option, Opt}).
+
+verify_server_timeout(T) when is_integer(T) andalso (T > 0) ->
+ ok;
+verify_server_timeout(T) ->
+ error({invalid_server_timeout, T}).
+
+verify_net_if_opts([]) ->
+ ok;
+verify_net_if_opts([{module, Mod}|Opts]) ->
+ verify_network_interface_behaviour(Mod),
+ verify_net_if_opts(Opts);
+verify_net_if_opts([{verbosity, Verbosity}|Opts]) ->
+ verify_verbosity(Verbosity),
+ verify_net_if_opts(Opts);
+verify_net_if_opts([{options, Options}|Opts]) when is_list(Options) ->
+ verify_net_if_opts(Opts);
+verify_net_if_opts([Opt|_]) ->
+ error({invalid_net_if_option, Opt}).
+
+verify_network_interface_behaviour(Mod) ->
+ case snmp_misc:verify_behaviour(snmpm_network_interface, Mod) of
+ ok ->
+ ok;
+ Error ->
+ throw(Error)
+ end.
+
+
+verify_note_store_opts([]) ->
+ ok;
+verify_note_store_opts([{verbosity, Verbosity}|Opts]) ->
+ verify_verbosity(Verbosity),
+ verify_note_store_opts(Opts);
+verify_note_store_opts([{timeout, Timeout}|Opts]) ->
+ verify_note_store_timeout(Timeout),
+ verify_note_store_opts(Opts);
+verify_note_store_opts([Opt|_]) ->
+ error({invalid_note_store_option, Opt}).
+
+verify_note_store_timeout(T) when is_integer(T) andalso (T > 0) ->
+ ok;
+verify_note_store_timeout(T) ->
+ error({invalid_note_store_timeout, T}).
+
+verify_conf_dir(Dir) ->
+ case (catch verify_dir(Dir)) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error({invalid_conf_dir, Dir, Reason});
+ _ ->
+ error({invalid_conf_dir, Dir})
+ end.
+
+verify_conf_db_dir(Dir) ->
+ case (catch verify_dir(Dir)) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error({invalid_conf_db_dir, Dir, Reason});
+ _ ->
+ error({invalid_conf_db_dir, Dir})
+ end.
+
+
+verify_conf_db_init_error(terminate) ->
+ ok;
+verify_conf_db_init_error(create) ->
+ ok;
+verify_conf_db_init_error(InvalidDbInitError) ->
+ error({invalid_conf_db_init_error, InvalidDbInitError}).
+
+
+verify_conf_repair(true) ->
+ ok;
+verify_conf_repair(false) ->
+ ok;
+verify_conf_repair(force) ->
+ ok;
+verify_conf_repair(InvalidRepair) ->
+ error({invalid_conf_db_repair, InvalidRepair}).
+
+
+verify_conf_auto_save(infinity) ->
+ ok;
+verify_conf_auto_save(AutoSave)
+ when is_integer(AutoSave) andalso (AutoSave > 0) ->
+ ok;
+verify_conf_auto_save(InvalidAutoSave) ->
+ error({invalid_conf_db_auto_save, InvalidAutoSave}).
+
+
+verify_versions([]) ->
+ ok;
+verify_versions([Vsn|Vsns]) ->
+ verify_version(Vsn),
+ verify_versions(Vsns).
+
+verify_version(v1) ->
+ ok;
+verify_version(v2) ->
+ ok;
+verify_version(v3) ->
+ ok;
+verify_version(Vsn) ->
+ error({invalid_version, Vsn}).
+
+verify_audit_trail_log_opts([]) ->
+ ok;
+verify_audit_trail_log_opts([{dir, Dir}|Opts]) ->
+ verify_log_dir(Dir),
+ verify_audit_trail_log_opts(Opts);
+verify_audit_trail_log_opts([{type, Type}|Opts]) ->
+ verify_log_type(Type),
+ verify_audit_trail_log_opts(Opts);
+verify_audit_trail_log_opts([{size, Size}|Opts]) ->
+ verify_log_size(Size),
+ verify_audit_trail_log_opts(Opts);
+verify_audit_trail_log_opts([{repair, Repair}|Opts]) ->
+ verify_log_repair(Repair),
+ verify_audit_trail_log_opts(Opts);
+verify_audit_trail_log_opts([Opt|_Opts]) ->
+ error({invalid_audit_trail_log_option, Opt}).
+
+verify_log_type(read) ->
+ ok;
+verify_log_type(write) ->
+ ok;
+verify_log_type(read_write) ->
+ ok;
+verify_log_type(Type) ->
+ error({invalid_audit_trail_log_type, Type}).
+
+verify_log_dir(Dir) ->
+ case (catch verify_dir(Dir)) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error({invalid_audit_trail_log_dir, Dir, Reason});
+ _ ->
+ error({invalid_audit_trail_log_dir, Dir})
+ end.
+
+verify_log_size(Sz) when is_integer(Sz) andalso (Sz > 0) ->
+ ok;
+verify_log_size(infinity) ->
+ ok;
+verify_log_size({MaxNoBytes, MaxNoFiles})
+ when (is_integer(MaxNoBytes) andalso
+ (MaxNoBytes > 0) andalso
+ is_integer(MaxNoFiles) andalso
+ (MaxNoFiles > 0) andalso
+ (MaxNoFiles < 65000)) ->
+ ok;
+verify_log_size(Sz) ->
+ error({invalid_audit_trail_log_size, Sz}).
+
+verify_log_repair(true) -> ok;
+verify_log_repair(false) -> ok;
+verify_log_repair(truncate) -> ok;
+verify_log_repair(Repair) ->
+ error({invalid_audit_trail_log_repair, Repair}).
+
+
+verify_module(_, Mod) when is_atom(Mod) ->
+ ok;
+verify_module(ReasonTag, Mod) ->
+ error({invalid_module, ReasonTag, Mod}).
+
+% verify_bool(_, true) ->
+% ok;
+% verify_bool(_, false) ->
+% ok;
+% verify_bool(ReasonTag, Bool) ->
+% error({invalid_bool, ReasonTag, Bool}).
+
+verify_dir(Dir) when is_list(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ ok;
+ {ok, _} ->
+ {error, not_directory};
+ {error, _Reason} ->
+ {error, not_found}
+ end;
+verify_dir(Dir) ->
+ {error, {invalid_log_dir, Dir}}.
+
+
+verify_verbosity(Verbosity) ->
+ case snmp_verbosity:validate(Verbosity) of
+ Verbosity ->
+ ok;
+ _ ->
+ error({invalid_verbosity, Verbosity})
+ end.
+
+%% ------------------------------------------------------------------------
+
+init_manager_config([]) ->
+ ok;
+init_manager_config([{Key, Val}|Confs]) ->
+ ets:insert(snmpm_config_table, {Key, Val}),
+ init_manager_config(Confs).
+
+
+
+init_agent_default() ->
+ %% The purpose of the default_agent is only to have a place
+ %% to store system wide default values related to agents.
+ %%
+
+ %% Port
+ init_agent_default(port, ?DEFAULT_AGENT_PORT),
+
+ %% Timeout
+ init_agent_default(timeout, 10000),
+
+ %% Max message (packet) size
+ init_agent_default(max_message_size, 484),
+
+ %% MPModel
+ init_agent_default(version, v2),
+
+ %% SecModel
+ init_agent_default(sec_model, v2c),
+
+ %% SecName
+ init_agent_default(sec_name, "initial"),
+
+ %% SecLevel
+ init_agent_default(sec_level, noAuthNoPriv),
+
+ %% Community
+ init_agent_default(community, "all-rights"),
+ ok.
+
+
+init_agent_default(Item, Val) when Item =/= user_id ->
+ case do_update_agent_info(default_agent, Item, Val) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error(Reason)
+ end.
+
+
+read_agents_config_file(Dir) ->
+ Check = fun(C) -> check_agent_config2(C) end,
+ case read_file(Dir, "agents.conf", Check, []) of
+ {ok, Conf} ->
+ Conf;
+ Error ->
+ ?vlog("agent config error: ~p", [Error]),
+ throw(Error)
+ end.
+
+check_agent_config2(Agent) ->
+ case (catch check_agent_config(Agent)) of
+ {ok, {UserId, TargetName, Conf, Version}} ->
+ {ok, Vsns} = system_info(versions),
+ case lists:member(Version, Vsns) of
+ true ->
+ {ok, {UserId, TargetName, Conf}};
+ false ->
+ error({version_not_supported_by_manager,
+ Version, Vsns})
+ end;
+ Err ->
+ throw(Err)
+ end.
+
+check_agent_config({UserId,
+ TargetName,
+ Community,
+ Ip, Port,
+ EngineId,
+ Timeout, MaxMessageSize,
+ Version, SecModel, SecName, SecLevel}) ->
+ ?vtrace("check_agent_config -> entry with"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n Community: ~p"
+ "~n Ip: ~p"
+ "~n Port: ~p"
+ "~n EngineId: ~p"
+ "~n Timeout: ~p"
+ "~n MaxMessageSize: ~p"
+ "~n Version: ~p"
+ "~n SecModel: ~p"
+ "~n SecName: ~p"
+ "~n SecLevel: ~p",
+ [UserId, TargetName, Community, Ip, Port,
+ EngineId, Timeout, MaxMessageSize,
+ Version, SecModel, SecName, SecLevel]),
+ Addr = normalize_address(Ip),
+ ?vtrace("check_agent_config -> Addr: ~p", [Addr]),
+ Agent = {UserId,
+ TargetName,
+ Community,
+ Addr, Port,
+ EngineId,
+ Timeout, MaxMessageSize,
+ Version, SecModel, SecName, SecLevel},
+ {ok, verify_agent(Agent)};
+check_agent_config(Agent) ->
+ error({bad_agent_config, Agent}).
+
+
+init_agents_config([]) ->
+ ok;
+init_agents_config([Agent|Agents]) ->
+ init_agent_config(Agent),
+ init_agents_config(Agents).
+
+init_agent_config({_UserId, ?DEFAULT_TARGETNAME = TargetName, _Config}) ->
+ throw({error, {invalid_target_name, TargetName}});
+init_agent_config({UserId, TargetName, Config}) ->
+ case handle_register_agent(UserId, TargetName, Config) of
+ ok ->
+ ok;
+ Error ->
+ throw(Error)
+ end.
+
+
+verify_agent({UserId,
+ TargetName,
+ Comm,
+ Ip, Port,
+ EngineId,
+ Timeout, MMS,
+ Version, SecModel, SecName, SecLevel}) ->
+ ?vtrace("verify_agent -> entry with"
+ "~n UserId: ~p"
+ "~n TargetName: ~p", [UserId, TargetName]),
+ snmp_conf:check_string(TargetName, {gt, 0}),
+ case verify_val(address, Ip) of
+ {ok, Addr} ->
+ snmp_conf:check_integer(Port, {gt, 0}),
+ Conf =
+ [{address, Addr},
+ {port, Port},
+ {community, Comm},
+ {engine_id, EngineId},
+ {timeout, Timeout},
+ {max_message_size, MMS},
+ {version, Version},
+ {sec_model, SecModel},
+ {sec_name, SecName},
+ {sec_level, SecLevel}
+ ],
+ case verify_agent2(Conf) of
+ ok ->
+ {UserId, TargetName, Conf, Version};
+ Err ->
+ throw(Err)
+ end;
+
+ Error ->
+ ?vlog("verify_agent -> failed: ~n ~p", [Error]),
+ throw(Error)
+ end.
+
+verify_agent2([]) ->
+ ok;
+verify_agent2([{Item, Val}|Items]) ->
+ case verify_val(Item, Val) of
+ {ok, _Val} ->
+ verify_agent2(Items);
+ Err ->
+ Err
+ end;
+verify_agent2([Bad|_]) ->
+ {error, {bad_agent_config, Bad}}.
+
+
+read_users_config_file(Dir) ->
+ Check = fun(C) -> check_user_config(C) end,
+ case read_file(Dir, "users.conf", Check, []) of
+ {ok, Conf} ->
+ Conf;
+ Error ->
+ ?vlog("failure reading users config file: ~n ~p", [Error]),
+ throw(Error)
+ end.
+
+
+check_user_config({Id, Mod, Data}) ->
+ check_user_config({Id, Mod, Data, []});
+check_user_config({Id, Mod, _Data, DefaultAgentConfig} = User)
+ when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
+ case (catch verify_user_behaviour(Mod)) of
+ ok ->
+ case verify_user_agent_config(DefaultAgentConfig) of
+ ok ->
+ {ok, User};
+ {error, Reason} ->
+ error({bad_default_agent_config, Reason})
+ end;
+ Error ->
+ throw(Error)
+ end;
+check_user_config({Id, _Mod, _Data, DefaultAgentConfig})
+ when (Id =/= ?DEFAULT_USER) ->
+ {error, {bad_default_agent_config, DefaultAgentConfig}};
+check_user_config({Id, _Mod, _Data, _DefaultAgentConfig}) ->
+ error({bad_user_id, Id});
+check_user_config(User) ->
+ error({bad_user_config, User}).
+
+init_users_config([]) ->
+ ok;
+init_users_config([User|Users]) ->
+ init_user_config(User),
+ init_users_config(Users).
+
+init_user_config(User) ->
+ case (catch verify_user(User)) of
+ {ok, UserRec} ->
+ case handle_register_user(UserRec) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed register user: "
+ "~n~w~n~w", [User, Reason])
+ end;
+ {error, Reason} ->
+ error_msg("user config check failed: "
+ "~n~w~n~w", [User, Reason])
+ end.
+
+verify_user({Id, UserMod, UserData}) ->
+ verify_user({Id, UserMod, UserData, []});
+verify_user({Id, UserMod, UserData, DefaultAgentConfig})
+ when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
+ ?d("verify_user -> entry with"
+ "~n Id: ~p"
+ "~n UserMod: ~p"
+ "~n UserData: ~p"
+ "~n DefaultAgentConfig: ~p",
+ [Id, UserMod, UserData, DefaultAgentConfig]),
+ case (catch verify_user_behaviour(UserMod)) of
+ ok ->
+ case verify_user_agent_config(DefaultAgentConfig) of
+ ok ->
+ Config = default_agent_config(DefaultAgentConfig),
+ {ok, #user{id = Id,
+ mod = UserMod,
+ data = UserData,
+ default_agent_config = Config}};
+ {error, Reason} ->
+ error({bad_default_agent_config, Reason})
+ end;
+ Error ->
+ throw(Error)
+ end;
+verify_user({Id, _UserMod, _UserData, DefaultAgentConfig})
+ when (Id =/= ?DEFAULT_USER) ->
+ {error, {bad_default_agent_config, DefaultAgentConfig}};
+verify_user({Id, _, _, _}) ->
+ {error, {bad_user_id, Id}}.
+
+verify_user_agent_config(Conf) ->
+ case verify_invalid(Conf, [user_id, engine_id, address]) of
+ ok ->
+ verify_agent_config2(Conf);
+ Error ->
+ Error
+ end.
+
+read_usm_config_file(Dir) ->
+ Check = fun(C) -> check_usm_user_config(C) end,
+ case read_file(Dir, "usm.conf", Check, []) of
+ {ok, Conf} ->
+ Conf;
+ Error ->
+ throw(Error)
+ end.
+
+%% Identity-function
+check_usm_user_config({EngineId, Name,
+ AuthP, AuthKey,
+ PrivP, PrivKey}) ->
+ User = {EngineId, Name, Name, AuthP, AuthKey, PrivP, PrivKey},
+ verify_usm_user(User);
+check_usm_user_config({_EngineId, _Name, _SecName,
+ _AuthP, _AuthKey,
+ _PrivP, _PrivKey} = User) ->
+ verify_usm_user(User);
+check_usm_user_config(User) ->
+ error({bad_usm_config, User}).
+
+init_usm_users_config([]) ->
+ ok;
+init_usm_users_config([User|Users]) ->
+ init_usm_user_config(User),
+ init_usm_users_config(Users).
+
+init_usm_user_config(User) when is_record(User, usm_user) ->
+ case handle_register_usm_user(User) of
+ ok ->
+ ok;
+ Error ->
+ throw(Error)
+ end;
+init_usm_user_config(BadUser) ->
+ error({bad_usm_user, BadUser}).
+
+
+verify_usm_user({EngineID, Name, SecName, AuthP, AuthKey, PrivP, PrivKey}) ->
+ ?d("verify_usm_user -> entry with"
+ "~n EngineID: ~p"
+ "~n Name: ~p"
+ "~n SecName: ~p"
+ "~n AuthP: ~p"
+ "~n AuthKey: ~p"
+ "~n PrivP: ~p"
+ "~n PrivKey: ~p",
+ [EngineID, Name, SecName, AuthP, AuthKey, PrivP, PrivKey]),
+ verify_usm_user_engine_id(EngineID),
+ verify_usm_user_name(Name),
+ verify_usm_user_sec_name(SecName),
+ verify_usm_user(AuthP, AuthKey, PrivP, PrivKey),
+ User = #usm_user{engine_id = EngineID,
+ name = Name,
+ sec_name = SecName,
+ auth = AuthP,
+ auth_key = AuthKey,
+ priv = PrivP,
+ priv_key = PrivKey},
+ {ok, User}.
+
+verify_usm_user_engine_id(EngineID) ->
+ case (catch snmp_conf:check_string(EngineID, {gt, 0})) of
+ ok ->
+ ok;
+ _ ->
+ error({bad_usm_engine_id, EngineID})
+ end.
+
+verify_usm_user_name(Name) ->
+ case (catch snmp_conf:check_string(Name, {gt, 0})) of
+ ok ->
+ ok;
+ _ ->
+ error({bad_usm_user_name, Name})
+ end.
+
+verify_usm_user_sec_name(Name) ->
+ case (catch snmp_conf:check_string(Name, {gt, 0})) of
+ ok ->
+ ok;
+ _ ->
+ error({bad_usm_sec_name, Name})
+ end.
+
+verify_usm_user(AuthP, AuthKey, PrivP, PrivKey) ->
+ verify_usm_user_auth(AuthP, AuthKey),
+ verify_usm_user_priv(PrivP, PrivKey),
+ ok.
+
+verify_usm_user_auth(usmNoAuthProtocol, AuthKey) ->
+ case (catch snmp_conf:check_string(AuthKey, any)) of
+ ok ->
+ ok;
+ _ ->
+ error({invalid_auth_key, usmNoAuthProtocol})
+ end;
+verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey)
+ when is_list(AuthKey) andalso (length(AuthKey) =:= 16) ->
+ case is_crypto_supported(md5_mac_96) of
+ true ->
+ case snmp_conf:all_integer(AuthKey) of
+ true ->
+ ok;
+ _ ->
+ error({invalid_auth_key, usmHMACMD5AuthProtocol})
+ end;
+ false ->
+ error({unsupported_crypto, md5_mac_96})
+ end;
+verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) when is_list(AuthKey) ->
+ Len = length(AuthKey),
+ error({invalid_auth_key, usmHMACMD5AuthProtocol, Len});
+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
+ true ->
+ case snmp_conf:all_integer(AuthKey) of
+ true ->
+ ok;
+ _ ->
+ error({invalid_auth_key, usmHMACSHAAuthProtocol})
+ end;
+ false ->
+ error({unsupported_crypto, sha_mac_96})
+ end;
+verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) when is_list(AuthKey) ->
+ Len = length(AuthKey),
+ error({invalid_auth_key, usmHMACSHAAuthProtocol, Len});
+verify_usm_user_auth(usmHMACSHAAuthProtocol, _AuthKey) ->
+ error({invalid_auth_key, usmHMACSHAAuthProtocol});
+verify_usm_user_auth(AuthP, _AuthKey) ->
+ error({invalid_auth_protocol, AuthP}).
+
+verify_usm_user_priv(usmNoPrivProtocol, PrivKey) ->
+ case (catch snmp_conf:check_string(PrivKey, any)) of
+ ok ->
+ ok;
+ _ ->
+ error({invalid_priv_key, usmNoPrivProtocol})
+ end;
+verify_usm_user_priv(usmDESPrivProtocol, PrivKey)
+ when (length(PrivKey) =:= 16) ->
+ case is_crypto_supported(des_cbc_decrypt) of
+ true ->
+ case snmp_conf:all_integer(PrivKey) of
+ true ->
+ ok;
+ _ ->
+ error({invalid_priv_key, usmDESPrivProtocol})
+ end;
+ false ->
+ error({unsupported_crypto, des_cbc_decrypt})
+ end;
+verify_usm_user_priv(usmDESPrivProtocol, PrivKey) when is_list(PrivKey) ->
+ Len = length(PrivKey),
+ error({invalid_priv_key, usmDESPrivProtocol, Len});
+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
+ true ->
+ case snmp_conf:all_integer(PrivKey) of
+ true ->
+ ok;
+ _ ->
+ error({invalid_priv_key, usmAesCfb128Protocol})
+ end;
+ false ->
+ error({unsupported_crypto, aes_cfb_128_decrypt})
+ end;
+verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) when is_list(PrivKey) ->
+ Len = length(PrivKey),
+ error({invalid_priv_key, usmAesCfb128Protocol, Len});
+verify_usm_user_priv(usmAesCfb128Protocol, _PrivKey) ->
+ error({invalid_priv_key, usmAesCfb128Protocol});
+verify_usm_user_priv(PrivP, _PrivKey) ->
+ error({invalid_priv_protocol, PrivP}).
+
+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.
+
+
+read_manager_config_file(Dir) ->
+ Check = fun(Conf) -> check_manager_config(Conf) end,
+ case read_file(Dir, "manager.conf", Check) of
+ {ok, Conf} ->
+ ?d("read_manager_config_file -> ok: "
+ "~n Conf: ~p", [Conf]),
+ %% If the address is not specified, then we assume
+ %% it should be the local host.
+ %% If the address is not possible to determine
+ %% that way, then we give up...
+ check_mandatory_manager_config(Conf),
+ ensure_manager_config(Conf);
+ Error ->
+ throw(Error)
+ end.
+
+default_manager_config() ->
+ {ok, HostName} = inet:gethostname(),
+ case inet:getaddr(HostName, inet) of
+ {ok, A} ->
+ [{address, tuple_to_list(A)}];
+ {error, _Reason} ->
+ ?d("default_manager_config -> failed getting address: "
+ "~n _Reason: ~p", [_Reason]),
+ []
+ end.
+
+check_manager_config({address, Addr}) ->
+ snmp_conf:check_ip(Addr);
+check_manager_config({port, Port}) ->
+ snmp_conf:check_integer(Port, {gt, 0});
+check_manager_config({engine_id, EngineID}) ->
+ snmp_conf:check_string(EngineID);
+check_manager_config({max_message_size, Max}) ->
+ snmp_conf:check_integer(Max, {gte, 484});
+check_manager_config(Conf) ->
+ {error, {unknown_config, Conf}}.
+
+
+check_mandatory_manager_config(Conf) ->
+ Mand = [port, engine_id, max_message_size],
+ check_mandatory_manager_config(Mand, Conf).
+
+check_mandatory_manager_config([], _Conf) ->
+ ok;
+check_mandatory_manager_config([Item|Mand], Conf) ->
+ case lists:keysearch(Item, 1, Conf) of
+ false ->
+ error({missing_mandatory_manager_config, Item});
+ _ ->
+ check_mandatory_manager_config(Mand, Conf)
+ end.
+
+
+ensure_manager_config(Confs) ->
+ ensure_manager_config(Confs, default_manager_config()).
+
+ensure_manager_config(Confs, []) ->
+ Confs;
+ensure_manager_config(Confs, [{Key,_} = DefKeyVal|Defs]) ->
+ case lists:keysearch(Key, 1, Confs) of
+ false ->
+ ensure_manager_config([DefKeyVal|Confs], Defs);
+ {value, _Conf} ->
+ ensure_manager_config(Confs, Defs)
+ end.
+
+% ensure_manager_config([], Defs, Confs) ->
+% Confs ++ Defs;
+% ensure_manager_config(Confs0, [{Key, DefVal}|Defs], Acc) ->
+% case lists:keysearch(Key, 1, Confs0) of
+% false ->
+% ensure_manager_config(Confs0, Defs, [{Key, DefVal}|Acc]);
+% {value, Conf} ->
+% Confs = lists:keydelete(Key, 1, Confs0),
+% ensure_manager_config(Confs, Defs, [Conf|Acc])
+% end.
+
+
+
+read_file(Dir, FileName, Check, Default) ->
+ File = filename:join(Dir, FileName),
+ case file:read_file_info(File) of
+ {ok, _} ->
+ case (catch do_read(File, Check)) of
+ {ok, Conf} ->
+ {ok, Conf};
+ Error ->
+ ?vtrace("read_file -> read failed:"
+ "~n Error: ~p", [Error]),
+ Error
+ end;
+ {error, Reason} ->
+ ?vlog("failed reading config from ~s: ~p", [FileName, Reason]),
+ {ok, Default}
+ end.
+
+read_file(Dir, FileName, Check) ->
+ File = filename:join(Dir, FileName),
+ case file:read_file_info(File) of
+ {ok, _} ->
+ case (catch do_read(File, Check)) of
+ {ok, Conf} ->
+ ?vtrace("read_file -> read ok"
+ "~n Conf: ~p", [Conf]),
+ {ok, Conf};
+ Error ->
+ ?vtrace("read_file -> read failed:"
+ "~n Error: ~p", [Error]),
+ Error
+ end;
+ {error, Reason} ->
+ error_msg("failed reading config from ~s: ~p", [FileName, Reason]),
+ {error, {failed_reading, FileName, Reason}}
+ end.
+
+do_read(File, Check) ->
+ {ok, snmp_conf:read(File, Check)}.
+
+
+%%--------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_call({register_user, UserId, UserMod, UserData, DefaultAgentConfig},
+ _From, State) ->
+ ?vlog("received register_user request: "
+ "~n UserId: ~p"
+ "~n UserMod: ~p"
+ "~n UserData: ~p"
+ "~n DefaultAgentConfig: ~p",
+ [UserId, UserMod, UserData, DefaultAgentConfig]),
+ User = #user{id = UserId,
+ mod = UserMod,
+ data = UserData,
+ default_agent_config = DefaultAgentConfig},
+ Reply = handle_register_user(User),
+ {reply, Reply, State};
+
+handle_call({unregister_user, UserId}, _From, State) ->
+ ?vlog("received unregister_user request: "
+ "~n UserId: ~p", [UserId]),
+ Reply = handle_unregister_user(UserId),
+ {reply, Reply, State};
+
+handle_call({register_agent, UserId, TargetName, Config}, _From, State) ->
+ ?vlog("received register_agent request: "
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p", [UserId, TargetName, Config]),
+ Reply = handle_register_agent(UserId, TargetName, Config),
+ {reply, Reply, State};
+
+handle_call({unregister_agent, UserId, TargetName}, _From, State) ->
+ ?vlog("received unregister_agent request: "
+ "~n UserId: ~p"
+ "~n TargetName: ~p", [UserId, TargetName]),
+ Reply = handle_unregister_agent(UserId, TargetName),
+ {reply, Reply, State};
+
+handle_call({update_agent_info, UserId, TargetName, Item, Val},
+ _From, State) ->
+ ?vlog("received update_agent_info request: "
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n Item: ~p"
+ "~n Val: ~p", [UserId, TargetName, Item, Val]),
+ Reply = handle_update_agent_info(UserId, TargetName, Item, Val),
+ {reply, Reply, State};
+
+handle_call({register_usm_user, User}, _From, State) ->
+ ?vlog("received register_usm_user request: "
+ "~n User: ~p", [User]),
+ Reply = handle_register_usm_user(User),
+ {reply, Reply, State};
+
+handle_call({unregister_usm_user, EngineID, Name}, _From, State) ->
+ ?vlog("received register_usm_user request: "
+ "~n EngineID: ~p"
+ "~n Name: ~p", [EngineID, Name]),
+ Reply = handle_unregister_usm_user(EngineID, Name),
+ {reply, Reply, State};
+
+handle_call({update_usm_user_info, EngineID, UserName, Item, Val},
+ _From, State) ->
+ ?vlog("received update_usm_user_info request: "
+ "~n EngineID: ~p"
+ "~n UserName: ~p"
+ "~n Item: ~p"
+ "~n Val: ~p", [EngineID, UserName, Item, Val]),
+ Reply = handle_update_usm_user_info(EngineID, UserName, Item, Val),
+ {reply, Reply, State};
+
+handle_call({cre_counter, Counter, Initial}, _From, State) ->
+ ?vlog("received cre_counter ~p -> ~w", [Counter, Initial]),
+ Reply = cre_counter(Counter, Initial),
+ {reply, Reply, State};
+
+handle_call({cre_stats_counter, Counter, Initial}, _From, State) ->
+ ?vlog("received cre_stats_counter ~p -> ~w", [Counter, Initial]),
+ Reply = cre_stats_counter(Counter, Initial),
+ {reply, Reply, State};
+
+handle_call({reset_stats_counter, Counter}, _From, State) ->
+ ?vlog("received reset_stats_counter ~p", [Counter]),
+ Reply = reset_stats_counter(Counter),
+ {reply, Reply, State};
+
+handle_call({load_mib, Mib}, _From, State) ->
+ ?vlog("received load_mib ~p", [Mib]),
+ case handle_load_mib(Mib) of
+ ok ->
+ {reply, ok, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({unload_mib, Mib}, _From, State) ->
+ ?vlog("received unload_mib ~p", [Mib]),
+ case handle_unload_mib(Mib) of
+ ok ->
+ {reply, ok, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({set_engine_boots, Boots}, _From, State) ->
+ ?vlog("received set_engine_boots ~p", [Boots]),
+ set_engine_boots(Boots),
+ {reply, ok, State};
+
+handle_call({set_engine_time, Time}, _From, State) ->
+ ?vlog("received set_engine_time ~p", [Time]),
+ Base = snmp_misc:now(sec) - Time,
+ ets:insert(snmpm_config_table, {snmp_engine_base, Base}),
+ {reply, ok, State};
+
+handle_call({set_usm_cache, Key, Val}, _From, State) ->
+ ?vlog("received set_usm_cache: ~w -> ~p", [Key, Val]),
+ ets:insert(snmpm_usm_table, {{usm_cache, Key}, Val}),
+ {reply, ok, State};
+
+handle_call({reset_usm_cache, EngineID}, _From, State) ->
+ ?vlog("received reset_usm_cache: ~p", [EngineID]),
+ reset_usm_cache(EngineID),
+ {reply, ok, State};
+
+handle_call({verbosity, Verbosity}, _From, State) ->
+ ?vlog("received verbosity request", []),
+ put(verbosity, Verbosity),
+ {reply, ok, State};
+
+handle_call(info, _From, State) ->
+ ?vlog("received info request", []),
+ Reply = get_info(),
+ {reply, Reply, State};
+
+handle_call({backup, BackupDir}, From, State) ->
+ ?vlog("backup to ~p", [BackupDir]),
+ Pid = self(),
+ V = get(verbosity),
+ case file:read_file_info(BackupDir) of
+ {ok, #file_info{type = directory}} ->
+ BackupServer =
+ erlang:spawn_link(
+ fun() ->
+ put(sname, mcbs),
+ put(verbosity, V),
+ Dir = filename:join([BackupDir]),
+ Reply = handle_backup(?CONFIG_DB, Dir),
+ Pid ! {backup_done, Reply},
+ unlink(Pid)
+ end),
+ ?vtrace("backup server: ~p", [BackupServer]),
+ {noreply, State#state{backup = {BackupServer, From}}};
+ {ok, _} ->
+ {reply, {error, not_a_directory}, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+%% handle_call({update_system_info, Key, Val}, _From, State) ->
+%% ?vlog("received update_system_info: ~p -> ~p", [Key, Val]),
+%% Reply = handle_update_system_info(Key, Val),
+%% {reply, Reply, State};
+
+
+handle_call(is_started, _From, State) ->
+ ?vlog("received is_started request", []),
+ {reply, true, State};
+
+
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+
+
+handle_call(Req, _From, State) ->
+ warning_msg("received unknown request: ~n~p", [Req]),
+ {reply, {error, unknown_request}, State}.
+
+
+%%--------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_cast(Msg, State) ->
+ warning_msg("received unknown message: ~n~p", [Msg]),
+ {noreply, State}.
+
+
+%%--------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_info({'EXIT', Pid, Reason}, #state{backup = {Pid, From}} = S) ->
+ ?vlog("backup server (~p) exited for reason ~n~p", [Pid, Reason]),
+ gen_server:reply(From, {error, Reason}),
+ {noreply, S#state{backup = undefined}};
+
+handle_info({'EXIT', Pid, Reason}, S) ->
+ %% The only other processes we should be linked to are
+ %% either the server or our supervisor, so die...
+ {stop, {received_exit, Pid, Reason}, S};
+
+handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) ->
+ ?vlog("backup done:"
+ "~n Reply: ~p", [Reply]),
+ gen_server:reply(From, Reply),
+ {noreply, S#state{backup = undefined}};
+
+handle_info(Info, State) ->
+ warning_msg("received unknown info: ~n~p", [Info]),
+ {noreply, State}.
+
+
+%%--------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%--------------------------------------------------------------------
+terminate(Reason, _State) ->
+ ?vdebug("terminate: ~p",[Reason]),
+ ok.
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+
+%% downgrade
+%%
+code_change({down, _Vsn}, S1, downgrade_to_pre_4_7) ->
+ #state{backup = B} = S1,
+ stop_backup_server(B),
+ S2 = {state},
+ {ok, S2};
+
+%% upgrade
+%%
+code_change(_Vsn, _S1, upgrade_from_pre_4_7) ->
+ %% {state} = S1,
+ S2 = #state{},
+ {ok, S2};
+
+code_change(_Vsn, State, _Extra) ->
+ {ok, State}.
+
+
+stop_backup_server(undefined) ->
+ ok;
+stop_backup_server({Pid, _}) when is_pid(Pid) ->
+ exit(Pid, kill).
+
+
+
+%%----------------------------------------------------------
+%% Update system info
+%%----------------------------------------------------------
+
+%% handle_update_system_info(audit_trail_log_type = Key, Val) ->
+%% case snmpm_config:system_info(audit_trail_log) of
+%% {ok, true} ->
+%% Value =
+%% case Val of
+%% read ->
+%% {ok, [read]};
+%% write ->
+%% {ok, [write]};
+%% read_write ->
+%% {ok, [read,write]};
+%% _ ->
+%% {error, {bad_value, Key, Val}}
+%% end,
+%% case Value of
+%% {ok, NewValue} ->
+%% ets:insert(snmpm_config_table, {Key, NewValue}),
+%% ok;
+%% false ->
+%% Value
+%% end;
+%% _ ->
+%% {error, audit_trail_log_not_enabled}
+%% end;
+%% handle_update_system_info(BadKey, Val) ->
+%% {error, {unsupported_update, BadKey, Val}}.
+
+
+%%----------------------------------------------------------
+%% Backup
+%%----------------------------------------------------------
+
+handle_backup(D, BackupDir) ->
+ %% First check that we do not wrote to the corrent db-dir...
+ ?vtrace("handle_backup -> entry with"
+ "~n D: ~p"
+ "~n BackupDir: ~p", [D, BackupDir]),
+ case dets:info(D, filename) of
+ undefined ->
+ ?vinfo("handle_backup -> no file to backup", []),
+ {error, no_file};
+ Filename ->
+ ?vinfo("handle_backup -> file to backup: ~n ~p", [Filename]),
+ case filename:dirname(Filename) of
+ BackupDir ->
+ ?vinfo("handle_backup -> backup dir and db dir the same",
+ []),
+ {error, db_dir};
+ _ ->
+ case file:read_file_info(BackupDir) of
+ {ok, #file_info{type = directory}} ->
+ ?vdebug("handle_backup -> backup dir ok", []),
+ %% All well so far...
+ Type = dets:info(D, type),
+ KP = dets:info(D, keypos),
+ dets_backup(D,
+ filename:basename(Filename),
+ BackupDir, Type, KP);
+ {ok, _} ->
+ ?vinfo("handle_backup -> backup dir not a dir",
+ []),
+ {error, not_a_directory};
+ Error ->
+ ?vinfo("handle_backup -> Error: ~p", [Error]),
+ Error
+ end
+ end
+ end.
+
+dets_backup(D, Filename, BackupDir, Type, KP) ->
+ ?vtrace("dets_backup -> entry with"
+ "~n D: ~p"
+ "~n Filename: ~p"
+ "~n BackupDir: ~p", [D, Filename, BackupDir]),
+ BackupFile = filename:join(BackupDir, Filename),
+ ?vtrace("dets_backup -> "
+ "~n BackupFile: ~p", [BackupFile]),
+ Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}],
+ case dets:open_file(?BACKUP_DB, Opts) of
+ {ok, B} ->
+ ?vtrace("dets_backup -> create fun", []),
+ F = fun(Arg) ->
+ dets_backup(Arg, start, D, B)
+ end,
+ dets:safe_fixtable(D, true),
+ Res = dets:init_table(?BACKUP_DB, F, [{format, bchunk}]),
+ dets:safe_fixtable(D, false),
+ ?vtrace("dets_backup -> Res: ~p", [Res]),
+ Res;
+ Error ->
+ ?vinfo("dets_backup -> open_file failed: "
+ "~n ~p", [Error]),
+ Error
+ end.
+
+
+dets_backup(close, _Cont, _D, B) ->
+ dets:close(B),
+ ok;
+dets_backup(read, Cont1, D, B) ->
+ case dets:bchunk(D, Cont1) of
+ {Cont2, Data} ->
+ F = fun(Arg) ->
+ dets_backup(Arg, Cont2, D, B)
+ end,
+ {Data, F};
+ '$end_of_table' ->
+ dets:close(B),
+ end_of_input;
+ Error ->
+ Error
+ end.
+
+
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+handle_register_user(#user{id = Id} = User) ->
+ ?vdebug("handle_register_user -> entry with"
+ "~n User: ~p", [User]),
+ case ets:lookup(snmpm_user_table, Id) of
+ [] ->
+ ets:insert(snmpm_user_table, User),
+ ok;
+ _ ->
+ {error, {already_registered, User}}
+ end.
+
+handle_unregister_user(UserId) ->
+ ?vdebug("handle_unregister_user -> entry with"
+ "~n UserId: ~p", [UserId]),
+ ets:delete(snmpm_user_table, UserId),
+ ok.
+
+
+handle_register_agent(UserId, TargetName, Config) ->
+ ?vdebug("handle_register_agent -> entry with"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p", [UserId, TargetName, Config]),
+ case (catch agent_info(TargetName, user_id)) of
+ {error, _} ->
+ case ets:lookup(snmpm_user_table, UserId) of
+ [#user{default_agent_config = DefConfig}] ->
+ do_handle_register_agent(TargetName, DefConfig),
+ do_handle_register_agent(TargetName,
+ [{user_id, UserId}|Config]),
+ %% <DIRTY-BACKWARD-COMPATIBILLITY>
+ %% And now for some (backward compatibillity)
+ %% dirty crossref stuff
+ {ok, Addr} = agent_info(TargetName, address),
+ {ok, Port} = agent_info(TargetName, port),
+ ets:insert(snmpm_agent_table,
+ {{Addr, Port, target_name}, TargetName}),
+ %% </DIRTY-BACKWARD-COMPATIBILLITY>
+ ok;
+ _ ->
+ {error, {not_found, UserId}}
+ end;
+ {ok, UserId} ->
+ ?vinfo("[~w] Agent (~p) already registered"
+ "~nwhen"
+ "~n Agents: ~p",
+ [UserId, TargetName, which_agents()]),
+ {error, {already_registered, TargetName}};
+ {ok, OtherUserId} ->
+ ?vinfo("[~w] Agent (~p) already registered to ~p"
+ "~nwhen"
+ "~n Agents: ~p",
+ [UserId, TargetName, OtherUserId, which_agents()]),
+ {error, {already_registered, TargetName, OtherUserId}}
+ end.
+
+do_handle_register_agent(_TargetName, []) ->
+ ok;
+do_handle_register_agent(TargetName, [{Item, Val}|Rest]) ->
+ case (catch do_update_agent_info(TargetName, Item, Val)) of
+ ok ->
+ do_handle_register_agent(TargetName, Rest);
+ {error, Reason} ->
+ ets:match_delete(snmpm_agent_table, {TargetName, '_'}),
+ {error, Reason}
+ end;
+do_handle_register_agent(TargetName, BadConfig) ->
+ error_msg("error during agent registration - bad config: ~n~p",
+ [BadConfig]),
+ ets:match_delete(snmpm_agent_table, {TargetName, '_'}),
+ {error, {bad_agent_config, TargetName, BadConfig}}.
+
+
+handle_unregister_agent(UserId, TargetName) ->
+ ?vdebug("handle_unregister_agent -> entry with"
+ "~n UserId: ~p"
+ "~n TargetName: ~p", [UserId, TargetName]),
+ case (catch agent_info(TargetName, user_id)) of
+ {ok, UserId} ->
+ {ok, EngineID} = agent_info(TargetName, engine_id),
+ reset_usm_cache(EngineID),
+ %% <DIRTY-BACKWARD-COMPATIBILLITY>
+ %% And now for some (backward compatibillity)
+ %% dirty crossref stuff
+ {ok, Addr} = agent_info(TargetName, address),
+ {ok, Port} = agent_info(TargetName, port),
+ ets:delete(snmpm_agent_table, {Addr, Port, target_name}),
+ %% </DIRTY-BACKWARD-COMPATIBILLITY>
+ ets:match_delete(snmpm_agent_table, {{TargetName, '_'}, '_'}),
+ ok;
+ {ok, OtherUserId} ->
+ {error, {not_owner, OtherUserId}};
+ Error ->
+ Error
+ end.
+
+
+handle_update_agent_info(UserId, TargetName, Item, Val) ->
+ ?vdebug("handle_update_agent_info -> entry with"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n Item: ~p"
+ "~n Val: ~p", [UserId, TargetName, Item, Val]),
+ case (catch agent_info(TargetName, user_id)) of
+ {ok, UserId} ->
+ do_update_agent_info(TargetName, Item, Val);
+ {ok, OtherUserId} ->
+ {error, {not_owner, OtherUserId}};
+ Error ->
+ Error
+ end.
+
+do_update_agent_info(TargetName, Item, Val0) ->
+%% p("do_update_agent_info -> entry with"
+%% "~n TargetName: ~p"
+%% "~n Item: ~p"
+%% "~n Val0: ~p", [TargetName, Item, Val0]),
+ case verify_val(Item, Val0) of
+ {ok, Val} ->
+%% p("do_update_agent_info -> verified value"
+%% "~n Val: ~p", [Val]),
+ ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}),
+ ok;
+ Error ->
+ ?vlog("do_update_agent_info -> verify value failed: "
+ "~n TargetName: ~p"
+ "~n Item: ~p"
+ "~n Val0: ~p"
+ "~n Error: ~p", [TargetName, Item, Val0, Error]),
+ {error, {bad_agent_val, TargetName, Item, Val0}}
+ end.
+
+
+handle_register_usm_user(#usm_user{engine_id = EngineID,
+ name = Name} = User) ->
+ ?vdebug("handle_register_usm_user -> entry with"
+ "~n User: ~p", [User]),
+ Key = usm_key(EngineID, Name),
+ case ets:lookup(snmpm_usm_table, Key) of
+ [] ->
+ do_update_usm_user_info(Key, User);
+ _ ->
+ {error, {already_registered, EngineID, Name}}
+ end;
+handle_register_usm_user(BadUsmUser) ->
+ {error, {bad_usm_user, BadUsmUser}}.
+
+handle_unregister_usm_user(EngineID, Name) ->
+ ?vdebug("handle_unregister_usm_user -> entry with"
+ "~n EngineID: ~p"
+ "~n Name: ~p", [EngineID, Name]),
+ Key = usm_key(EngineID, Name),
+ ets:delete(snmpm_usm_table, Key),
+ ok.
+
+
+handle_update_usm_user_info(EngineID, Name, Item, Val) ->
+ ?vdebug("handle_update_usm_user_info -> entry with"
+ "~n EngineID: ~p"
+ "~n Name: ~p"
+ "~n Item: ~p"
+ "~n Val: ~p", [EngineID, Name, Item, Val]),
+ Key = usm_key(EngineID, Name),
+ case ets:lookup(snmpm_usm_table, Key) of
+ [] ->
+ {error, not_found};
+ [{_Key, User}] ->
+ do_update_usm_user_info(Key, User, Item, Val)
+ end.
+
+do_update_usm_user_info(Key, User, sec_name, Val) ->
+ %% case verify_usm_user_sec_name(Val) of
+ %% ok ->
+ %% do_update_usm_user_info(Key, User#usm_user{sec_name = Val});
+ %% _ ->
+ %% {error, {invalid_usm_sec_name, Val}}
+ %% end;
+ ok = verify_usm_user_sec_name(Val),
+ do_update_usm_user_info(Key, User#usm_user{sec_name = Val});
+do_update_usm_user_info(Key, User, auth, Val)
+ when (Val =:= usmNoAuthProtocol) orelse
+ (Val =:= usmHMACMD5AuthProtocol) orelse
+ (Val =:= usmHMACSHAAuthProtocol) ->
+ do_update_usm_user_info(Key, User#usm_user{auth = Val});
+do_update_usm_user_info(_Key, _User, auth, Val) ->
+ {error, {invalid_auth_protocol, Val}};
+do_update_usm_user_info(Key,
+ #usm_user{auth = usmNoAuthProtocol} = User,
+ auth_key, Val) ->
+ case (catch snmp_conf:check_string(Val, any)) of
+ ok ->
+ do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
+ _ ->
+ {error, {invalid_auth_key, Val}}
+ end;
+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
+ true ->
+ do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
+ false ->
+ {error, {unsupported_crypto, md5_mac_96}}
+ end;
+do_update_usm_user_info(_Key,
+ #usm_user{auth = usmHMACMD5AuthProtocol},
+ auth_key, Val) when is_list(Val) ->
+ Len = length(Val),
+ {error, {invalid_auth_key_length, usmHMACMD5AuthProtocol, Len}};
+do_update_usm_user_info(_Key,
+ #usm_user{auth = usmHMACMD5AuthProtocol},
+ auth_key, Val) ->
+ {error, {invalid_auth_key, usmHMACMD5AuthProtocol, Val}};
+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
+ true ->
+ do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
+ false ->
+ {error, {unsupported_crypto, sha_mac_96}}
+ end;
+do_update_usm_user_info(_Key,
+ #usm_user{auth = usmHMACSHAAuthProtocol},
+ auth_key, Val) when is_list(Val) ->
+ Len = length(Val),
+ {error, {invalid_auth_key_length, usmHMACSHAAuthProtocol, Len}};
+do_update_usm_user_info(_Key,
+ #usm_user{auth = usmHMACSHAAuthProtocol},
+ auth_key, Val) ->
+ {error, {invalid_auth_key, usmHMACSHAAuthProtocol, Val}};
+do_update_usm_user_info(Key, User, priv, Val)
+ when (Val =:= usmNoPrivProtocol) orelse
+ (Val =:= usmDESPrivProtocol) orelse
+ (Val =:= usmAesCfb128Protocol) ->
+ do_update_usm_user_info(Key, User#usm_user{priv = Val});
+do_update_usm_user_info(_Key, _User, priv, Val) ->
+ {error, {invalid_priv_protocol, Val}};
+do_update_usm_user_info(Key,
+ #usm_user{priv = usmNoPrivProtocol} = User,
+ priv_key, Val) ->
+ case (catch snmp_conf:check_string(Val, any)) of
+ ok ->
+ do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
+ _ ->
+ {error, {invalid_priv_key, Val}}
+ end;
+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
+ true ->
+ do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
+ false ->
+ {error, {unsupported_crypto, des_cbc_decrypt}}
+ 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
+ true ->
+ do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
+ false ->
+ {error, {unsupported_crypto, aes_cfb_128_decrypt}}
+ end;
+do_update_usm_user_info(_Key,
+ #usm_user{auth = usmHMACSHAAuthProtocol},
+ priv_key, Val) when is_list(Val) ->
+ Len = length(Val),
+ {error, {invalid_priv_key_length, usmHMACSHAAuthProtocol, Len}};
+do_update_usm_user_info(_Key,
+ #usm_user{auth = usmHMACSHAAuthProtocol},
+ priv_key, Val) ->
+ {error, {invalid_priv_key, usmHMACSHAAuthProtocol, Val}};
+do_update_usm_user_info(_Key, _User, Item, Val) ->
+ {error, {bad_item, Item, Val}}.
+
+do_update_usm_user_info(Key, User) ->
+ ets:insert(snmpm_usm_table, {Key, User}),
+ ok.
+
+
+usm_key(EngineId, Name) ->
+ {usmUserTable, EngineId, Name}.
+
+
+%% ---------------------------------------------------------------------
+
+verify_mandatory(_, []) ->
+ ok;
+verify_mandatory(Conf, [Mand|Mands]) ->
+ case lists:keymember(Mand, 1, Conf) of
+ true ->
+ verify_mandatory(Conf, Mands);
+ false ->
+ {error, {missing_mandatory_config, Mand}}
+ end.
+
+verify_invalid(_, []) ->
+ ok;
+verify_invalid(Conf, [Inv|Invs]) ->
+ case lists:member(Inv, Conf) of
+ false ->
+ verify_invalid(Conf, Invs);
+ true ->
+ {error, {illegal_config, Inv}}
+ end.
+
+
+verify_val(user_id, UserId) ->
+ {ok, UserId};
+verify_val(reg_type, RegType)
+ when (RegType =:= addr_port) orelse (RegType =:= target_name) ->
+ {ok, RegType};
+verify_val(address, Addr0) ->
+ case normalize_address(Addr0) of
+ {_A1, _A2, _A3, _A4} = Addr ->
+ {ok, Addr};
+ _ when is_list(Addr0) ->
+ case (catch snmp_conf:check_ip(Addr0)) of
+ ok ->
+ {ok, list_to_tuple(Addr0)};
+ Err ->
+ Err
+ end;
+ _ ->
+ error({bad_address, Addr0})
+ end;
+verify_val(port, Port) ->
+ case (catch snmp_conf:check_integer(Port, {gt, 0})) of
+ ok ->
+ {ok, Port};
+ Err ->
+ Err
+ end;
+verify_val(community, Comm) ->
+ case (catch snmp_conf:check_string(Comm)) of
+ ok ->
+ {ok, Comm};
+ Err ->
+ Err
+ end;
+verify_val(engine_id, discovery = EngineId) ->
+ {ok, EngineId};
+verify_val(engine_id, EngineId) ->
+ case (catch snmp_conf:check_string(EngineId)) of
+ ok ->
+ {ok, EngineId};
+ Err ->
+ Err
+ end;
+verify_val(timeout, Timeout) ->
+ (catch snmp_conf:check_timer(Timeout));
+verify_val(max_message_size, MMS) ->
+ case (catch snmp_conf:check_packet_size(MMS)) of
+ ok ->
+ {ok, MMS};
+ Err ->
+ Err
+ end;
+verify_val(version, V)
+ when (V =:= v1) orelse (V =:= v2) orelse (V =:= v3) ->
+ {ok, V};
+verify_val(version, BadVersion) ->
+ error({bad_version, BadVersion});
+verify_val(sec_model, Model) ->
+ (catch snmp_conf:check_sec_model(Model));
+verify_val(sec_name, Name) when is_list(Name) ->
+ case (catch snmp_conf:check_string(Name)) of
+ ok ->
+ {ok, Name};
+ Err ->
+ Err
+ end;
+verify_val(sec_name, BadName) ->
+ error({bad_sec_name, BadName});
+verify_val(sec_level, Level) ->
+ (catch snmp_conf:check_sec_level(Level));
+verify_val(Item, _) ->
+ {error, {no_such_item, Item}}.
+
+
+%%%-------------------------------------------------------------------
+%%%
+%%% Mini MIB stuff
+%%%
+%%%-------------------------------------------------------------------
+
+init_mini_mib(MibFiles) ->
+ MiniMibs = lists:flatten([do_load_mib(MibFile) || MibFile <- MibFiles]),
+ MiniMIB = remove_duplicates(lists:keysort(1, MiniMibs), []),
+ init_mini_mib2(MiniMIB).
+
+remove_duplicates([], Res) ->
+ Res;
+remove_duplicates([X,X|T], Res) ->
+ remove_duplicates([X|T], Res);
+remove_duplicates([{Oid, Name, Type, _} = X, {Oid, Name, Type, _}|T], Res) ->
+ remove_duplicates([X|T], Res);
+remove_duplicates([X|T], Res) ->
+ remove_duplicates(T, [X|Res]).
+
+init_mini_mib2([]) ->
+ ok;
+init_mini_mib2([{Oid, Name, Type, MibName}|MiniMib]) ->
+ ?vtrace("init mini mib -> ~w: ~w [~w] from ~s",
+ [Name, Oid, Type,MibName ]),
+ ets:insert(snmpm_mib_table, {{mini_mib, Oid}, Name, Type, MibName}),
+ init_mini_mib2(MiniMib).
+
+
+handle_load_mib(Mib) ->
+ [{mibs, Mibs0}] = ets:lookup(snmpm_config_table, mibs),
+ case lists:member(Mib, Mibs0) of
+ true ->
+ {error, already_loaded};
+ false ->
+ Mibs = [Mib|Mibs0],
+ case (catch do_load_mib(Mib)) of
+ MiniElems when is_list(MiniElems) ->
+ ets:insert(snmpm_config_table, {mibs, Mibs}),
+ update_mini_mib(MiniElems),
+ ok;
+ Error ->
+ Error
+ end
+ end.
+
+update_mini_mib([]) ->
+ ok;
+update_mini_mib([{Oid, Name, Type, MibName}|Elems]) ->
+ Key = {mini_mib, Oid},
+ case ets:lookup(snmpm_mib_table, Key) of
+ [{Key, _Name, _Type, _AnotherMibName}] ->
+ %% Already loaded from another mib
+ update_mini_mib(Elems);
+ [] ->
+ %% Not yet loaded
+ ?vtrace("update mini mib -> ~w: ~w [~w] from ~s",
+ [Name, Oid, Type, MibName]),
+ ets:insert(snmpm_mib_table, {Key, Name, Type, MibName}),
+ update_mini_mib(Elems)
+ end.
+
+
+handle_unload_mib(Mib) ->
+ Key = {mib, Mib},
+ case ets:lookup(snmpm_mib_table, Key) of
+ [{Key, MibName, _MibFile}] ->
+ do_unload_mib(MibName),
+ [{mibs, Mibs0}] = ets:lookup(snmpm_config_table, mibs),
+ Mibs = lists:delete(Mib, Mibs0),
+ ets:insert(snmpm_config_table, {mibs, Mibs}),
+ ets:delete(snmpm_mib_table, Key),
+ ok;
+ _ ->
+ {error, not_loaded}
+ end.
+
+do_unload_mib(MibName) ->
+ Pat = {{mini_mib, '$1'}, '_', '_', MibName},
+ Oids = ets:match(snmpm_mib_table, Pat),
+ F = fun([Oid]) -> ets:delete(snmpm_mib_table, {mini_mib, Oid}) end,
+ lists:foreach(F, Oids).
+
+
+do_load_mib(MibFile) ->
+ ?vtrace("load mib ~s", [MibFile]),
+ F1 = snmp_misc:strip_extension_from_filename(MibFile, ".bin"),
+ ActualFileName = lists:append(F1, ".bin"),
+ case snmp_misc:read_mib(ActualFileName) of
+ {ok, #mib{name = Name, mes = MEs, traps = Traps}} ->
+ %% Check that the mib was not loaded or loaded
+ %% with a different filename:
+ %% e.g. /tmp/MYMIB.bin and /tmp/mibs/MYMIB.bin
+ Name1 = mib_name(Name),
+ Pattern = {{mib, '_'}, Name1, '$1'},
+ case ets:match(snmpm_mib_table, Pattern) of
+ [] ->
+
+ Rec = {{mib, MibFile}, Name1, ActualFileName},
+ ets:insert(snmpm_mib_table, Rec),
+ init_mini_mib_elems(Name1, MEs++Traps, []);
+
+ %% This means that the mib has already been loaded
+ [[ActualFileName]] ->
+ [];
+
+ %% This means that the mib was loaded before,
+ %% but under another filename
+ [[OtherMibFile]] ->
+ error({already_loaded, MibFile, OtherMibFile})
+ end;
+
+ {error, Reason} ->
+ error({failed_reading_mib, MibFile, Reason})
+ end.
+
+mib_name(N) when is_list(N) ->
+ list_to_atom(N);
+mib_name(N) ->
+ N.
+
+init_mini_mib_elems(_, [], Res) ->
+ Res;
+init_mini_mib_elems(MibName,
+ [#me{aliasname = N,
+ oid = Oid,
+ entrytype = variable,
+ asn1_type = #asn1_type{bertype = Type}} | T], Res) ->
+ init_mini_mib_elems(MibName, T, [{Oid, N, Type, MibName}|Res]);
+
+init_mini_mib_elems(MibName,
+ [#me{aliasname = N,
+ oid = Oid,
+ entrytype = table_column,
+ asn1_type = #asn1_type{bertype = Type}}|T], Res) ->
+ init_mini_mib_elems(MibName, T, [{Oid, N, Type, MibName}|Res]);
+
+init_mini_mib_elems(MibName,
+ [#me{aliasname = N,
+ oid = Oid,
+ asn1_type = undefined}|T], Res) ->
+ init_mini_mib_elems(MibName, T, [{Oid, N, undefined, MibName}|Res]);
+
+init_mini_mib_elems(MibName,
+ [#notification{trapname = N,
+ oid = Oid}|T], Res) ->
+ init_mini_mib_elems(MibName, T, [{Oid, N, undefined, MibName}|Res]);
+
+init_mini_mib_elems(MibName, [_|T], Res) ->
+ init_mini_mib_elems(MibName, T, Res).
+
+
+
+%%----------------------------------------------------------------------
+
+normalize_address(Addr) ->
+ case inet:getaddr(Addr, inet) of
+ {ok, Addr2} ->
+ Addr2;
+ _ when is_list(Addr) ->
+ case (catch snmp_conf:check_ip(Addr)) of
+ ok ->
+ list_to_tuple(Addr);
+ _ ->
+ Addr
+ end;
+ _ ->
+ Addr
+ end.
+
+
+%%----------------------------------------------------------------------
+
+call(Req) ->
+ call(Req, infinity).
+
+call(Req, To) ->
+ gen_server:call(?SERVER, Req, To).
+
+% cast(Msg) ->
+% gen_server:cast(snmpm_server, Msg).
+
+
+%%-------------------------------------------------------------------
+
+get_atl_dir(Opts) ->
+ get_opt(dir, Opts).
+
+get_atl_type(Opts) ->
+ case get_opt(type, Opts, read_write) of
+ read_write ->
+ [read,write];
+ read ->
+ [read];
+ write ->
+ [write]
+ end.
+
+get_atl_size(Opts) ->
+ get_opt(size, Opts).
+
+get_atl_repair(Opts) ->
+ get_opt(repair, Opts, truncate).
+
+
+%%----------------------------------------------------------------------
+
+get_opt(Key, Opts) ->
+ ?d("get option ~w from ~p", [Key, Opts]),
+ snmp_misc:get_option(Key, Opts).
+
+get_opt(Key, Opts, Def) ->
+ ?d("get option ~w with default ~p from ~p", [Key, Def, Opts]),
+ snmp_misc:get_option(Key, Opts, Def).
+
+
+%%----------------------------------------------------------------------
+
+get_info() ->
+ ProcSize = proc_mem(self()),
+ CntSz = tab_size(snmpm_counter_table),
+ StatsSz = tab_size(snmpm_stats_table),
+ MibSz = tab_size(snmpm_mib_table),
+ ConfSz = tab_size(snmpm_config_table),
+ AgentSz = tab_size(snmpm_agent_table),
+ UserSz = tab_size(snmpm_user_table),
+ UsmSz = tab_size(snmpm_usm_table),
+ [{process_memory, ProcSize},
+ {db_memory, [{counter, CntSz},
+ {stats, StatsSz},
+ {mib, MibSz},
+ {config, ConfSz},
+ {agent, AgentSz},
+ {user, UserSz},
+ {usm, UsmSz}]}].
+
+proc_mem(P) when is_pid(P) ->
+ case (catch erlang:process_info(P, memory)) of
+ {memory, Sz} when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end.
+%% proc_mem(_) ->
+%% undefined.
+
+tab_size(T) ->
+ case (catch ets:info(T, memory)) of
+ Sz when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end.
+
+
+%%----------------------------------------------------------------------
+
+error(Reason) ->
+ throw({error, Reason}).
+
+
+%%----------------------------------------------------------------------
+
+info_msg(F, A) ->
+ ?snmpm_info("Config server: " ++ F, A).
+
+warning_msg(F, A) ->
+ ?snmpm_warning("Config server: " ++ F, A).
+
+error_msg(F, A) ->
+ ?snmpm_error("Config server: " ++ F, A).
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
+
diff --git a/lib/snmp/src/manager/snmpm_internal.hrl b/lib/snmp/src/manager/snmpm_internal.hrl
new file mode 100644
index 0000000000..5d9b32e3f6
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_internal.hrl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-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%
+%%
+
+-ifndef(snmpm_internal).
+-define(snmpm_internal, true).
+
+-include_lib("snmp/src/app/snmp_internal.hrl").
+
+-define(snmpm_info(F, A), ?snmp_info("manager", F, A)).
+-define(snmpm_warning(F, A), ?snmp_warning("manager", F, A)).
+-define(snmpm_error(F, A), ?snmp_error("manager", F, A)).
+
+-endif. % -ifdef(snmpm_internal).
+
+
+
diff --git a/lib/snmp/src/manager/snmpm_misc_sup.erl b/lib/snmp/src/manager/snmpm_misc_sup.erl
new file mode 100644
index 0000000000..1a5d7676df
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_misc_sup.erl
@@ -0,0 +1,112 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_misc_sup).
+
+-include("snmp_debug.hrl").
+
+-behaviour(supervisor).
+
+%% External exports
+-export([
+ start_link/0,
+ start_net_if/2, stop_net_if/0,
+ start_note_store/2, stop_note_store/0
+ ]).
+
+%% Internal exports
+-export([init/1]).
+
+-define(SUP, ?MODULE).
+
+
+%%%-----------------------------------------------------------------
+%%% This is a supervisor for the mib and net_ifprocesses.
+%%% Each agent has one mib process.
+%%%-----------------------------------------------------------------
+
+start_link() ->
+ ?d("start_link -> entry", []),
+ SupName = {local,?SUP},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+
+start_net_if(Mod, NoteStore) ->
+ ?d("start_net_if -> entry with"
+ "~n Mod: ~p"
+ "~n NoteStore: ~p", [Mod, NoteStore]),
+ SupName = ?SUP,
+ Name = net_if,
+ Args = [self(), NoteStore],
+ start_sup_child(SupName, Name, Mod, Args).
+
+stop_net_if() ->
+ stop_sup_child(?SUP, net_if).
+
+
+%% The note store is a common code component, so we must
+%% pass all the arguments in a way that works both for
+%% the agent and the manager entities. I.e. as arguments
+%% to the start_link function.
+start_note_store(Prio, Opts) ->
+ ?d("start_note_store -> entry with"
+ "~n Prio: ~p"
+ "~n Opts: ~p", [Prio, Opts]),
+ SupName = ?MODULE,
+ Name = note_store,
+ Mod = snmp_note_store,
+ Args = [Prio, snmpm, Opts],
+ start_sup_child(SupName, Name, Mod, Args).
+
+stop_note_store() ->
+ stop_sup_child(?SUP, note_store).
+
+
+%% ---------------------------------------------------------------
+
+start_sup_child(SupName, Name, Mod, Args) ->
+ %% make sure we start from scratch...
+ Children = supervisor:which_children(SupName),
+ case lists:keysearch(Name, 1, Children) of
+ {value, {_, _Pid, _, _}} ->
+ stop_sup_child(SupName, Name);
+ _ ->
+ ok
+ end,
+ Spec = {Name,
+ {Mod, start_link, Args},
+ temporary, 2000, worker, [Mod]},
+ supervisor:start_child(SupName, Spec).
+
+stop_sup_child(SupName, Name) ->
+ case whereis(SupName) of
+ undefined ->
+ ok;
+ _ ->
+ supervisor:terminate_child(SupName, Name),
+ supervisor:delete_child(SupName, Name)
+ end.
+
+
+
+init([]) ->
+ ?d("init -> entry", []),
+ SupFlags = {one_for_all, 0, 3600},
+ {ok, {SupFlags, []}}.
+
diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl
new file mode 100644
index 0000000000..d76ad20051
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_mpd.erl
@@ -0,0 +1,1024 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_mpd).
+
+-export([init/1,
+
+ process_msg/7,
+ generate_msg/5, generate_response_msg/4,
+
+ next_msg_id/0,
+ next_req_id/0,
+
+ reset/1,
+ inc/1]).
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+-include("snmpm_internal.hrl").
+-include("SNMP-MPD-MIB.hrl").
+-include("SNMPv2-TM.hrl").
+
+-define(VMODULE,"MPD").
+-include("snmp_verbosity.hrl").
+
+-define(empty_msg_size, 24).
+
+-record(state, {v1 = false, v2c = false, v3 = false}).
+
+
+%%%-----------------------------------------------------------------
+%%% This module implemets the Message Processing and Dispatch part of
+%%% the multi-lingual SNMP agent.
+%%%
+%%% The MPD is responsible for:
+%%% *) call the security module (auth/priv).
+%%% *) decoding the message into a PDU.
+%%% *) decide a suitable Access Control Model, and provide it with
+%%% the data it needs.
+%%% *) maintaining SNMP counters.
+%%%
+%%% In order to take care of the different versions of counters, it
+%%% implements and maintains the union of all SNMP counters (i.e. from
+%%% rfc1213 and from rfc1907). It is up to the administrator of the
+%%% agent to load the correct MIB. Note that this module implements
+%%% the counters only, it does not provide instrumentation functions
+%%% for the counters.
+%%%
+%%% With the terms defined in rfc2271, this module implememts part
+%%% of the Dispatcher and the Message Processing functionality.
+%%%-----------------------------------------------------------------
+init(Vsns) ->
+ ?vdebug("init -> entry with ~p", [Vsns]),
+ {A,B,C} = erlang:now(),
+ random:seed(A,B,C),
+ snmpm_config:cre_counter(msg_id, random:uniform(2147483647)),
+ snmpm_config:cre_counter(req_id, random:uniform(2147483647)),
+ init_counters(),
+ State = init_versions(Vsns, #state{}),
+ init_usm(State#state.v3),
+ ?vtrace("init -> done when ~p", [State]),
+ State.
+
+reset(#state{v3 = V3}) ->
+ reset_counters(),
+ reset_usm(V3).
+
+
+%%-----------------------------------------------------------------
+%% Func: process_msg(Packet, TDomain, TAddress, State) ->
+%% {ok, SnmpVsn, Pdu, PduMS, ACMData} | {discarded, Reason}
+%% Types: Packet = binary()
+%% TDomain = snmpUDPDomain | atom()
+%% TAddress = {Ip, Udp}
+%% State = #state
+%% Purpose: This is the main Message Dispatching function. (see
+%% section 4.2.1 in rfc2272)
+%%-----------------------------------------------------------------
+process_msg(Msg, TDomain, Addr, Port, State, NoteStore, Logger) ->
+
+ inc(snmpInPkts),
+
+ case (catch snmp_pdus:dec_message_only(binary_to_list(Msg))) of
+
+ %% Version 1
+ #message{version = 'version-1', vsn_hdr = Community, data = Data}
+ when State#state.v1 =:= true ->
+ HS = ?empty_msg_size + length(Community),
+ process_v1_v2c_msg('version-1', NoteStore, Msg, TDomain,
+ Addr, Port,
+ Community, Data, HS, Logger);
+
+ %% Version 2
+ #message{version = 'version-2', vsn_hdr = Community, data = Data}
+ when State#state.v2c =:= true ->
+ HS = ?empty_msg_size + length(Community),
+ process_v1_v2c_msg('version-2', NoteStore, Msg, TDomain,
+ Addr, Port,
+ Community, Data, HS, Logger);
+
+ %% Version 3
+ #message{version = 'version-3', vsn_hdr = H, data = Data}
+ when State#state.v3 =:= true ->
+ ?vlog("v3:"
+ "~n msgID: ~p"
+ "~n msgFlags: ~p"
+ "~n msgSecModel: ~p",
+ [H#v3_hdr.msgID,H#v3_hdr.msgFlags,H#v3_hdr.msgSecurityModel]),
+ process_v3_msg(NoteStore, Msg, H, Data, Addr, Port, Logger);
+
+ %% Crap
+ {'EXIT', {bad_version, Vsn}} ->
+ ?vinfo("exit: bad version: ~p",[Vsn]),
+ inc(snmpInBadVersions),
+ {discarded, snmpInBadVersions};
+
+ %% More crap
+ {'EXIT', Reason} ->
+ ?vinfo("exit: ~p",[Reason]),
+ inc(snmpInASNParseErrs),
+ {discarded, Reason};
+
+ %% Really crap
+ Crap ->
+ ?vinfo("unknown message: "
+ "~n ~p",[Crap]),
+ inc(snmpInBadVersions),
+ {discarded, snmpInBadVersions}
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Handles a Community based message (v1 or v2c).
+%%-----------------------------------------------------------------
+process_v1_v2c_msg(Vsn, _NoteStore, Msg, snmpUDPDomain,
+ Addr, Port,
+ Community, Data, HS, Log) ->
+
+ ?vdebug("process_v1_v2c_msg -> entry with"
+ "~n Vsn: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Community: ~p"
+ "~n HS: ~p", [Vsn, Addr, Port, Community, HS]),
+
+ Max = get_max_message_size(),
+ AgentMax = get_agent_max_message_size(Addr, Port),
+ PduMS = pdu_ms(Max, AgentMax, HS),
+
+ ?vtrace("process_v1_v2c_msg -> PduMS: ~p", [PduMS]),
+
+ case (catch snmp_pdus:dec_pdu(Data)) of
+ Pdu when is_record(Pdu, pdu) ->
+ ?vtrace("process_v1_v2c_msg -> was a pdu", []),
+ Log(Msg),
+ inc_snmp_in(Pdu),
+ MsgData = {Community, sec_model(Vsn)},
+ {ok, Vsn, Pdu, PduMS, MsgData};
+
+ Trap when is_record(Trap, trappdu) ->
+ ?vtrace("process_v1_v2c_msg -> was a trap", []),
+ Log(Msg),
+ inc_snmp_in(Trap),
+ MsgData = {Community, sec_model(Vsn)},
+ {ok, Vsn, Trap, PduMS, MsgData};
+
+ {'EXIT', Reason} ->
+ ?vlog("process_v1_v2c_msg -> failed decoding PDU: "
+ "~n Reason: ~p", [Reason]),
+ inc(snmpInASNParseErrs),
+ {discarded, Reason}
+ end;
+process_v1_v2c_msg(_Vsn, _NoteStore, _Msg, TDomain,
+ _Addr, _Port,
+ _Comm, _HS, _Data, _Log) ->
+ {discarded, {badarg, TDomain}}.
+
+pdu_ms(MgrMMS, AgentMMS, HS) when AgentMMS < MgrMMS ->
+ AgentMMS - HS;
+pdu_ms(MgrMMS, _AgentMMS, HS) ->
+ MgrMMS - HS.
+
+sec_model('version-1') -> ?SEC_V1;
+sec_model('version-2') -> ?SEC_V2C.
+
+
+%%-----------------------------------------------------------------
+%% Handles a SNMPv3 Message, following the procedures in rfc2272,
+%% section 4.2 and 7.2
+%%-----------------------------------------------------------------
+process_v3_msg(NoteStore, Msg, Hdr, Data, Addr, Port, Log) ->
+
+ ?vdebug("process_v3_msg -> entry with"
+ "~n Hdr: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Hdr, Addr, Port]),
+
+ %% 7.2.3
+ #v3_hdr{msgID = MsgID,
+ msgMaxSize = MMS,
+ msgFlags = MsgFlags,
+ msgSecurityModel = MsgSecModel,
+ msgSecurityParameters = SecParams,
+ hdr_size = HdrSize} = Hdr,
+
+ %% 7.2.4
+ SecModule = get_security_module(MsgSecModel),
+ ?vtrace("process_v3_msg -> 7.2.4: "
+ "~n SecModule: ~p", [SecModule]),
+
+ %% 7.2.5
+ SecLevel = check_sec_level(MsgFlags),
+ IsReportable = is_reportable(MsgFlags),
+ ?vtrace("process_v3_msg -> 7.2.5: "
+ "~n SecLevel: ~p"
+ "~n IsReportable: ~p", [SecLevel, IsReportable]),
+
+ %% 7.2.6
+ SecRes = (catch SecModule:process_incoming_msg(Msg, Data,
+ SecParams, SecLevel)),
+ ?vtrace("process_v3_msg -> 7.2.6 - message processing result: "
+ "~n ~p",[SecRes]),
+ {SecEngineID, SecName, ScopedPDUBytes, SecData} =
+ check_sec_module_result(SecRes, Hdr, Data, IsReportable, Log),
+ ?vtrace("process_v3_msg -> 7.2.6 - checked module result: "
+ "~n SecEngineID: ~p"
+ "~n SecName: ~p",[SecEngineID, SecName]),
+
+ %% 7.2.7
+ #scopedPdu{contextEngineID = CtxEngineID,
+ contextName = CtxName,
+ data = PDU} =
+ case (catch snmp_pdus:dec_scoped_pdu(ScopedPDUBytes)) of
+ ScopedPDU when is_record(ScopedPDU, scopedPdu) ->
+ ScopedPDU;
+ {'EXIT', Reason} ->
+ ?vlog("failed decoding scoped pdu: "
+ "~n ~p",[Reason]),
+ inc(snmpInASNParseErrs),
+ discard(Reason)
+ end,
+
+ ?vlog("7.2.7"
+ "~n ContextEngineID: \"~s\" "
+ "~n context: \"~s\" ",
+ [CtxEngineID, CtxName]),
+ if
+ SecLevel == 3 -> % encrypted message - log decrypted pdu
+ Log({Hdr, ScopedPDUBytes});
+ true -> % otherwise, log binary
+ Log(Msg)
+ end,
+
+ %% Make sure a get_bulk doesn't get too big.
+ MgrMMS = get_max_message_size(),
+ %% PduMMS is supposed to be the maximum total length of the response
+ %% PDU we can send. From the MMS, we need to subtract everything before
+ %% the PDU, i.e. Message and ScopedPDU.
+ %% Message: [48, TotalLen, Vsn, [Tag, LH, Hdr], [Tag, LM, MsgSec], Data]
+ %% 1 3 <----------- HdrSize ----------->
+ %% HdrSize = everything up to and including msgSecurityParameters.
+ %% ScopedPduData follows. This is
+ %% [Tag, Len, [Tag, L1, CtxName], [Tag, L2, CtxEID]]
+ %% i.e. 6 + length(CtxName) + length(CtxEID)
+ %%
+ %% Total: 1 + TotalLenOctets + 3 + ScopedPduDataLen
+ TotMMS = tot_mms(MgrMMS, MMS),
+ TotalLenOctets = snmp_pdus:get_encoded_length(TotMMS - 1),
+ PduMMS = TotMMS - TotalLenOctets - 10 - HdrSize -
+ length(CtxName) - length(CtxEngineID),
+ ?vtrace("process_v3_msg -> PduMMS = ~p", [PduMMS]),
+ Type = PDU#pdu.type,
+ ?vdebug("process_v3_msg -> PDU type: ~p",[Type]),
+ case Type of
+ report ->
+ %% 7.2.10 & 11
+ %% BMK BMK BMK: discovery?
+ Note = snmp_note_store:get_note(NoteStore, MsgID),
+ case Note of
+ {SecEngineID, MsgSecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, _ReqId} ->
+ ?vtrace("process_v3_msg -> 7.2.11b: ok", []),
+ %% BMK BMK: Should we discard the cached info
+ %% BMK BMK: or do we let the gc deal with it?
+ {ok, 'version-3', PDU, PduMMS, ok};
+ _ when is_tuple(Note) ->
+ ?vlog("process_v3_msg -> 7.2.11b: error"
+ "~n Note: ~p", [Note]),
+ Recv = {SecEngineID, MsgSecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, PDU#pdu.request_id},
+ Err = sec_error(Note, Recv),
+ ACM = {invalid_sec_info, Err},
+ ReqId = element(size(Note), Note),
+ {ok, 'version-3', PDU, PduMMS, {error, ReqId, ACM}};
+ _NoFound ->
+ ?vtrace("process_v3_msg -> _NoFound: "
+ "~p", [_NoFound]),
+ inc(snmpUnknownPDUHandlers),
+ discard({no_outstanding_req, MsgID})
+ end;
+
+ 'get-response' ->
+ %% 7.2.10 & 12
+ case snmp_note_store:get_note(NoteStore, MsgID) of
+ {SecEngineID, MsgSecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, _} ->
+ %% 7.2.12.d
+ {ok, 'version-3', PDU, PduMMS, undefined};
+ _ ->
+ %% 7.2.12.b
+ %% BMK BMK: Should we not discard the cached info??
+ inc(snmpUnknownPDUHandlers),
+ discard({no_outstanding_req, MsgID})
+ end;
+
+ 'snmpv2-trap' ->
+ %% 7.2.14
+ {ok, 'version-3', PDU, PduMMS, undefined};
+
+ 'inform-request' ->
+ %% 7.2.13
+ SnmpEngineID = get_engine_id(),
+ case SecEngineID of
+ SnmpEngineID -> % 7.2.13.b
+ ?vtrace("valid securityEngineID: ~p", [SecEngineID]),
+ %% 4.2.2.1.1 - we don't handle proxys yet => we only
+ %% handle CtxEngineID to ourselves
+ %% Check that we actually know of an agent with this
+ %% CtxEngineID and Addr/Port
+ case is_known_engine_id(CtxEngineID, Addr, Port) of
+ true ->
+ ?vtrace("and the agent EngineID (~p) "
+ "is know to us", [CtxEngineID]),
+ %% Uses ACMData that snmpm_acm knows of.
+ %% BUGBUG BUGBUG
+ ACMData =
+ {MsgID, MsgSecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, SecData},
+ {ok, 'version-3', PDU, PduMMS, ACMData};
+ _ ->
+ %% 4.2.2.1.2
+ NIsReportable = snmp_misc:is_reportable_pdu(Type),
+ Val = inc(snmpUnknownPDUHandlers),
+ ErrorInfo =
+ {#varbind{oid = ?snmpUnknownPDUHandlers,
+ variabletype = 'Counter32',
+ value = Val},
+ SecName,
+ [{securityLevel, SecLevel},
+ {contextEngineID, CtxEngineID},
+ {contextName, CtxName}]},
+ case generate_v3_report_msg(MsgID,
+ MsgSecModel,
+ Data,
+ ErrorInfo,
+ Log) of
+ {ok, Report} when NIsReportable =:= true ->
+ discard(snmpUnknownPDUHandlers, Report);
+ _ ->
+ discard(snmpUnknownPDUHandlers)
+ end
+ end;
+ _ -> % 7.2.13.a
+ ?vinfo("invalid securityEngineID: ~p",[SecEngineID]),
+ discard({badSecurityEngineID, SecEngineID})
+ end;
+
+ _ ->
+ %% 7.2.13 - This would be the requests which we should not
+ %% receive since we are a manager, barring possible
+ %% proxy...
+ discard(Type)
+ end.
+
+
+sec_error(T1, T2)
+ when is_tuple(T1) andalso is_tuple(T2) andalso (size(T1) =:= size(T2)) ->
+ Tags = {sec_engine_id, msg_sec_model, sec_name, sec_level,
+ ctx_engine_id, ctx_name, request_id},
+ sec_error(size(T1), T1, T2, Tags, []);
+sec_error(T1, T2) ->
+ [{internal_error, T1, T2}].
+
+sec_error(0, _T1, _T2, _Tags, Acc) ->
+ Acc;
+sec_error(Idx, T1, T2, Tags, Acc) ->
+ case element(Idx, T1) =:= element(Idx, T2) of
+ true ->
+ sec_error(Idx - 1, T1, T2, Tags, Acc);
+ false ->
+ Elem = {element(Idx, Tags), element(Idx, T1), element(Idx, T2)},
+ sec_error(Idx - 1, T1, T2, Tags, [Elem|Acc])
+ end.
+
+tot_mms(MgrMMS, AgentMMS) when MgrMMS > AgentMMS -> AgentMMS;
+tot_mms(MgrMMS, _AgentMMS) -> MgrMMS.
+
+get_security_module(?SEC_USM) ->
+ snmpm_usm;
+get_security_module(_) ->
+ inc(snmpUnknownSecurityModels),
+ discard(snmpUnknownSecurityModels).
+
+check_sec_level([MsgFlag]) ->
+ SecLevel = MsgFlag band 3,
+ if
+ SecLevel == 2 ->
+ inc(snmpInvalidMsgs),
+ discard(snmpInvalidMsgs);
+ true ->
+ SecLevel
+ end;
+check_sec_level(_Unknown) ->
+ inc(snmpInvalidMsgs),
+ discard(snmpInvalidMsgs).
+
+is_reportable([MsgFlag]) ->
+ 4 == (MsgFlag band 4).
+
+
+check_sec_module_result({ok, X}, _, _, _, _) ->
+ X;
+check_sec_module_result({error, Reason, Info}, _, _, _, _)
+ when is_list(Info) ->
+ %% case 7.2.6 b
+ discard({securityError, Reason, Info});
+check_sec_module_result({error, Reason, ErrorInfo}, V3Hdr, Data, true, Log) ->
+ %% case 7.2.6 a
+ ?vtrace("security module result:"
+ "~n Reason: ~p"
+ "~n ErrorInfo: ~p", [Reason, ErrorInfo]),
+ #v3_hdr{msgID = MsgID, msgSecurityModel = MsgSecModel} = V3Hdr,
+ Pdu = get_scoped_pdu(Data),
+ case generate_v3_report_msg(MsgID, MsgSecModel, Pdu, ErrorInfo, Log) of
+ {ok, Report} ->
+ discard({securityError, Reason}, Report);
+ {discarded, _SomeOtherReason} ->
+ discard({securityError, Reason})
+ end;
+check_sec_module_result({error, Reason, _ErrorInfo}, _, _, _, _) ->
+ ?vtrace("security module result:"
+ "~n Reason: ~p"
+ "~n _ErrorInfo: ~p", [Reason, _ErrorInfo]),
+ discard({securityError, Reason});
+check_sec_module_result(Res, _, _, _, _) ->
+ ?vtrace("security module result:"
+ "~n Res: ~p", [Res]),
+ discard({securityError, Res}).
+
+get_scoped_pdu(D) when is_list(D) ->
+ (catch snmp_pdus:dec_scoped_pdu(D));
+get_scoped_pdu(D) ->
+ D.
+
+
+%%-----------------------------------------------------------------
+%% Generate a message
+%%-----------------------------------------------------------------
+generate_msg('version-3', NoteStore, Pdu,
+ {SecModel, SecName, SecLevel, CtxEngineID, CtxName,
+ TargetName}, Log) ->
+ generate_v3_msg(NoteStore, Pdu,
+ SecModel, SecName, SecLevel, CtxEngineID, CtxName,
+ TargetName, Log);
+generate_msg(Vsn, _NoteStore, Pdu, {Community, _SecModel}, Log) ->
+ generate_v1_v2c_msg(Vsn, Pdu, Community, Log).
+
+
+generate_v3_msg(NoteStore, Pdu,
+ SecModel, SecName, SecLevel, CtxEngineID, CtxName,
+ TargetName, Log) ->
+ %% rfc2272: 7.1.6
+ ?vdebug("generate_v3_msg -> 7.1.6", []),
+ ScopedPDU = #scopedPdu{contextEngineID = CtxEngineID,
+ contextName = CtxName,
+ data = Pdu},
+ case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of
+ {'EXIT', Reason} ->
+ user_err("failed encoding scoped pdu "
+ "~n pdu: ~w"
+ "~n contextName: ~w"
+ "~n reason: ~w", [Pdu, CtxName, Reason]),
+ {discarded, Reason};
+ ScopedPDUBytes ->
+ {ok, generate_v3_msg(NoteStore, Pdu, ScopedPDUBytes,
+ SecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, TargetName, Log)}
+ end.
+
+generate_v3_msg(NoteStore,
+ #pdu{type = Type} = Pdu, ScopedPduBytes,
+ SecModel, SecName, SecLevel, CtxEngineID, CtxName,
+ TargetName, Log) ->
+ %% 7.1.7
+ ?vdebug("generate_v3_msg -> 7.1.7", []),
+ MsgID = next_msg_id(),
+ MsgFlags = snmp_misc:mk_msg_flags(Type, SecLevel),
+ V3Hdr = #v3_hdr{msgID = MsgID,
+ msgMaxSize = get_max_message_size(),
+ msgFlags = MsgFlags,
+ msgSecurityModel = SecModel},
+ Message = #message{version = 'version-3',
+ vsn_hdr = V3Hdr,
+ data = ScopedPduBytes},
+ SecModule = sec_module(SecModel),
+
+ %% 7.1.9a
+ ?vdebug("generate_v3_msg -> 7.1.9a", []),
+ SecEngineID = sec_engine_id(TargetName),
+ ?vtrace("SecEngineID: ~p", [SecEngineID]),
+ %% 7.1.9b
+ ?vdebug("generate_v3_msg -> 7.1.9b", []),
+ case generate_v3_outgoing_msg(Message, SecModule, SecEngineID,
+ SecName, [], SecLevel) of
+ {ok, Packet} ->
+ %% 7.1.9c
+ %% Store in cache for 150 sec.
+ ?vdebug("generate_v3_msg -> 7.1.9c", []),
+ %% The request id is just in the case when we receive a
+ %% report with incorrect securityModel and/or securityLevel
+ CacheVal = {SecEngineID, SecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, Pdu#pdu.request_id},
+ snmp_note_store:set_note(NoteStore, 1500, MsgID, CacheVal),
+ Log(Packet),
+ inc_snmp_out(Pdu),
+ ?vdebug("generate_v3_msg -> done", []),
+ Packet;
+
+ Error ->
+ throw(Error)
+ end.
+
+
+sec_module(?SEC_USM) ->
+ snmpm_usm.
+
+%% 9) If the PDU is a GetRequest-PDU, GetNextRequest-PDU,
+%% GetBulkRequest-PDU, SetRequest-PDU, InformRequest-PDU, or or
+%% SNMPv2-Trap-PDU, then
+%%
+%% a) If the PDU is an SNMPv2-Trap-PDU, then securityEngineID is set
+%% to the value of this entity's snmpEngineID.
+%%
+%% Otherwise, the snmpEngineID of the target entity is determined,
+%% in an implementation-dependent manner, possibly using
+%% transportDomain and transportAddress. The value of
+%% securityEngineID is set to the value of the target entity's
+%% snmpEngineID.
+%%
+%% As we never send traps, the SecEngineID is allways the
+%% snmpEngineID of the target entity!
+sec_engine_id(TargetName) ->
+ case get_agent_engine_id(TargetName) of
+ {ok, EngineId} ->
+ EngineId;
+ _ ->
+ config_err("Can't find engineID for "
+ "snmpTargetAddrName ~p", [TargetName]),
+ %% this will trigger error in secmodule
+ ""
+ end.
+
+
+%% BMK BMK BMK
+%% Denna verkar v�ldigt lik generate_v1_v2c_response_msg!
+%% Gemensam? Borde det finnas olikheter?
+%%
+generate_v1_v2c_msg(Vsn, Pdu, Community, Log) ->
+ ?vdebug("generate_v1_v2c_msg -> encode pdu", []),
+ case (catch snmp_pdus:enc_pdu(Pdu)) of
+ {'EXIT', Reason} ->
+ user_err("failed encoding pdu: "
+ "(pdu: ~w, community: ~w): ~n~w",
+ [Pdu, Community, Reason]),
+ {discarded, Reason};
+ PduBytes ->
+ MMS = get_max_message_size(),
+ Message = #message{version = Vsn,
+ vsn_hdr = Community,
+ data = PduBytes},
+ case generate_v1_v2c_outgoing_msg(Message) of
+ {error, Reason} ->
+ user_err("failed encoding message "
+ "(pdu: ~w, community: ~w): ~n~w",
+ [Pdu, Community, Reason]),
+ {discarded, Reason};
+ {ok, Packet} when size(Packet) =< MMS ->
+ Log(Packet),
+ inc_snmp_out(Pdu),
+ {ok, Packet};
+ {ok, Packet} ->
+ ?vlog("packet max size exceeded: "
+ "~n MMS: ~p"
+ "~n Len: ~p",
+ [MMS, size(Packet)]),
+ {discarded, tooBig}
+ end
+ end.
+
+
+
+%% -----------------------------------------------------------------------
+
+generate_response_msg('version-3', Pdu,
+ {MsgID, SecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, SecData}, Log) ->
+ generate_v3_response_msg(Pdu, MsgID, SecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, SecData, Log);
+generate_response_msg(Vsn, Pdu, {Comm, _SecModel}, Log) ->
+ generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log).
+
+
+generate_v3_response_msg(#pdu{type = Type} = Pdu, MsgID,
+ SecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, SecData, Log) ->
+ %% rfc2272: 7.1 steps 6-8
+ ScopedPdu = #scopedPdu{contextEngineID = CtxEngineID,
+ contextName = CtxName,
+ data = Pdu},
+ case (catch snmp_pdus:enc_scoped_pdu(ScopedPdu)) of
+ {'EXIT', Reason} ->
+ user_err("failed encoded scoped pdu "
+ "(pdu: ~w, contextName: ~w): ~n~w",
+ [Pdu, CtxName, Reason]),
+ {discarded, Reason};
+ ScopedPduBytes ->
+ MMS = get_max_message_size(),
+ MsgFlags = snmp_misc:mk_msg_flags(Type, SecLevel),
+ V3Hdr = #v3_hdr{msgID = MsgID,
+ msgMaxSize = MMS,
+ msgFlags = MsgFlags,
+ msgSecurityModel = SecModel},
+ Message = #message{version = 'version-3',
+ vsn_hdr = V3Hdr,
+ data = ScopedPduBytes},
+ %% We know that the security model is valid when we
+ %% generate a response.
+ SecModule = sec_module(SecModel),
+ SecEngineID = get_engine_id(),
+ case generate_v3_outgoing_msg(Message, SecModule, SecEngineID,
+ SecName, SecData, SecLevel) of
+ %% Check the packet size. Send the msg even
+ %% if it's larger than the agent can handle -
+ %% it will be dropped. Just check against the
+ %% internal size.
+ {ok, Packet} when size(Packet) =< MMS ->
+ if
+ SecLevel == 3 ->
+ %% encrypted - log decrypted pdu
+ Log({V3Hdr, ScopedPduBytes});
+ true ->
+ %% otherwise log the entire msg
+ Log(Packet)
+ end,
+ inc_snmp_out(Pdu),
+ {ok, Packet};
+
+ {ok, _Packet} when Pdu#pdu.error_status =:= tooBig ->
+ ?vlog("packet max size exceeded (tooBog): "
+ "~n MMS: ~p", [MMS]),
+ inc(snmpSilentDrops),
+ {discarded, tooBig};
+ {ok, _Packet} ->
+ ?vlog("packet max size exceeded: "
+ "~n MMS: ~p", [MMS]),
+ TooBigPdu = Pdu#pdu{error_status = tooBig,
+ error_index = 0,
+ varbinds = []},
+ generate_v3_response_msg(TooBigPdu, MsgID,
+ SecModel, SecName, SecLevel,
+ CtxEngineID,
+ CtxName,
+ SecData, Log);
+ Error ->
+ Error
+ end
+ end.
+
+
+generate_v3_outgoing_msg(Message,
+ SecModule, SecEngineID, SecName, SecData, SecLevel) ->
+ case (catch SecModule:generate_outgoing_msg(Message,
+ SecEngineID,
+ SecName, SecData,
+ SecLevel)) of
+ {'EXIT', Reason} ->
+ config_err("~p (message: ~p)", [Reason, Message]),
+ {discarded, Reason};
+ {error, Reason} ->
+ config_err("~p (message: ~p)", [Reason, Message]),
+ {discarded, Reason};
+ Bin when is_binary(Bin) ->
+ {ok, Bin};
+ OutMsg when is_list(OutMsg) ->
+ case (catch list_to_binary(OutMsg)) of
+ Bin when is_binary(Bin) ->
+ {ok, Bin};
+ {'EXIT', Reason} ->
+ {error, Reason}
+ end
+ end.
+
+
+generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log) ->
+ case (catch snmp_pdus:enc_pdu(Pdu)) of
+ {'EXIT', Reason} ->
+ user_err("failed encoding pdu: "
+ "(pdu: ~w, community: ~w): ~n~w",
+ [Pdu, Comm, Reason]),
+ {discarded, Reason};
+ PduBytes ->
+ MMS = get_max_message_size(),
+ Message = #message{version = Vsn,
+ vsn_hdr = Comm,
+ data = PduBytes},
+ case generate_v1_v2c_outgoing_msg(Message) of
+ {error, Reason} ->
+ user_err("failed encoding message only "
+ "(pdu: ~w, community: ~w): ~n~w",
+ [Pdu, Comm, Reason]),
+ {discarded, Reason};
+
+ {ok, Packet} when size(Packet) =< MMS ->
+ Log(Packet),
+ inc_snmp_out(Pdu),
+ {ok, Packet};
+
+ {ok, Packet} -> %% Too big
+ too_big(Vsn, Pdu, Comm, MMS, size(Packet), Log)
+ end
+ end.
+
+
+too_big('version-1' = Vsn, #pdu{type = 'get-response'} = Pdu,
+ Comm, _MMS, _Len, Log) ->
+ %% In v1, the varbinds should be identical to the incoming
+ %% request. It isn't identical now! Make acceptable (?)
+ %% approximation.
+ V = set_vb_null(Pdu#pdu.varbinds),
+ TooBigPdu = Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = V},
+ too_big(Vsn, TooBigPdu, Comm, Log);
+too_big('version-2' = Vsn, #pdu{type = 'get-response'} = Pdu,
+ Comm, _MMS, _Len, Log) ->
+ %% In v2, varbinds should be empty (reasonable!)
+ TooBigPdu = Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = []},
+ too_big(Vsn, TooBigPdu, Comm, Log);
+too_big(_Vsn, Pdu, _Comm, _Log, MMS, Len) ->
+ user_err("encoded pdu, ~p bytes, exceeded "
+ "max message size of ~p bytes. Pdu: ~n~w",
+ [Len, MMS, Pdu]),
+ {discarded, tooBig}.
+
+
+too_big(Vsn, Pdu, Comm, Log) ->
+ case (catch snmp_pdus:enc_pdu(Pdu)) of
+ {'EXIT', Reason} ->
+ user_err("failed encoding pdu "
+ "(pdu: ~w, community: ~w): ~n~w",
+ [Pdu, Comm, Reason]),
+ {discarded, Reason};
+ PduBytes ->
+ Message = #message{version = Vsn,
+ vsn_hdr = Comm,
+ data = PduBytes},
+ case generate_v1_v2c_outgoing_msg(Message) of
+ {error, Reason} ->
+ user_err("failed encoding message only"
+ "(pdu: ~w, community: ~w): ~n~w",
+ [Pdu, Comm, Reason]),
+ {discarded, Reason};
+ {ok, Bin} ->
+ Log(Bin),
+ inc_snmp_out(Pdu),
+ {ok, Bin}
+ end
+ end.
+
+set_vb_null(Vbs) ->
+ [Vb#varbind{variabletype = 'NULL', value = 'NULL'} || Vb <- Vbs].
+
+
+generate_v1_v2c_outgoing_msg(Message) ->
+ ?vdebug("generate_v1_v2c_outgoing_msg -> encode message", []),
+ case (catch snmp_pdus:enc_message_only(Message)) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Bin when is_binary(Bin) ->
+ {ok, Bin};
+ Packet when is_list(Packet) ->
+ case (catch list_to_binary(Packet)) of
+ Bin when is_binary(Bin) ->
+ {ok, Bin};
+ {'EXIT', Reason} ->
+ {error, Reason}
+ end
+ end.
+
+
+
+generate_v3_report_msg(MsgID, SecModel, ScopedPdu, ErrInfo, Log)
+ when is_record(ScopedPdu, scopedPdu) ->
+ ReqID = (ScopedPdu#scopedPdu.data)#pdu.request_id,
+ generate_v3_report_msg2(MsgID, ReqID, SecModel, ErrInfo, Log);
+generate_v3_report_msg(MsgID, SecModel, _, ErrInfo, Log) ->
+ %% RFC2572, 7.1.3.c.4
+ generate_v3_report_msg2(MsgID, 0, SecModel, ErrInfo, Log).
+
+
+generate_v3_report_msg2(MsgID, ReqID, SecModel, ErrInfo, Log) ->
+ {Varbind, SecName, Opts} = ErrInfo,
+ Pdu = #pdu{type = report,
+ request_id = ReqID,
+ error_status = noError,
+ error_index = 0,
+ varbinds = [Varbind]},
+ SecLevel = snmp_misc:get_option(securityLevel, Opts, 0),
+ CtxEngineID = snmp_misc:get_option(contextEngineID, Opts, get_engine_id()),
+ CtxName = snmp_misc:get_option(contextName, Opts, ""),
+ SecData = snmp_misc:get_option(sec_data, Opts, []),
+ generate_v3_response_msg(Pdu,
+ MsgID, SecModel, SecName, SecLevel,
+ CtxEngineID, CtxName, SecData, Log).
+
+
+%%-----------------------------------------------------------------
+
+%% Get "our" (manager) MMS
+get_max_message_size() ->
+ case snmpm_config:get_engine_max_message_size() of
+ {ok, MMS} ->
+ MMS;
+ E ->
+ user_err("failed retreiving engine max message size: ~w", [E]),
+ 484
+ end.
+
+%% The the MMS of the agent
+get_agent_max_message_size(Addr, Port) ->
+ case snmpm_config:get_agent_engine_max_message_size(Addr, Port) of
+ {ok, MMS} ->
+ MMS;
+ _Error ->
+ ?vlog("unknown agent: ~w:~w", [Addr, Port]),
+ get_max_message_size()
+ end.
+
+%% Get "our" (manager) engine id
+get_engine_id() ->
+ case snmpm_config:get_engine_id() of
+ {ok, Id} ->
+ Id;
+ _Error ->
+ ""
+ end.
+
+%% The engine id of the agent
+get_agent_engine_id(Name) ->
+ snmpm_config:get_agent_engine_id(Name).
+
+is_known_engine_id(EngineID, Addr, Port) ->
+ snmpm_config:is_known_engine_id(EngineID, Addr, Port).
+
+% get_agent_engine_id(Addr, Port) ->
+% case snmpm_config:get_agent_engine_id(Addr, Port) of
+% {ok, Id} ->
+% Id;
+% _Error ->
+% ""
+% end.
+
+
+%%-----------------------------------------------------------------
+%% Sequence number (msg-id & req-id) functions
+%%-----------------------------------------------------------------
+next_msg_id() ->
+ next_id(msg_id).
+
+next_req_id() ->
+ next_id(req_id).
+
+next_id(Id) ->
+ snmpm_config:incr_counter(Id, 1).
+
+
+%%-----------------------------------------------------------------
+%% Version(s) functions
+%%-----------------------------------------------------------------
+init_versions([], S) ->
+ S;
+init_versions([v1|Vsns], S) ->
+ init_versions(Vsns, S#state{v1 = true});
+init_versions([v2|Vsns], S) ->
+ init_versions(Vsns, S#state{v2c = true});
+init_versions([v3|Vsns], S) ->
+ init_versions(Vsns, S#state{v3 = true}).
+
+init_usm(true) ->
+ snmpm_usm:init();
+init_usm(_) ->
+ ok.
+
+
+%%-----------------------------------------------------------------
+%% Counter functions
+%%-----------------------------------------------------------------
+init_counters() ->
+ F = fun(Counter) -> maybe_create_counter(Counter) end,
+ lists:map(F, counters()).
+
+reset_counters() ->
+ F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end,
+ lists:map(F, counters()).
+
+reset_usm(true) ->
+ snmpm_usm:reset();
+reset_usm(_) ->
+ ok.
+
+maybe_create_counter(Counter) ->
+ snmpm_config:maybe_cre_stats_counter(Counter, 0).
+
+counters() ->
+ [snmpInPkts,
+ snmpOutPkts,
+ snmpInBadVersions,
+ snmpInBadCommunityNames,
+ snmpInBadCommunityUses,
+ snmpInASNParseErrs,
+ snmpInTooBigs,
+ snmpInNoSuchNames,
+ snmpInBadValues,
+ snmpInReadOnlys,
+ snmpInGenErrs,
+ snmpInTotalReqVars,
+ snmpInTotalSetVars,
+ snmpInGetRequests,
+ snmpInGetNexts,
+ snmpInSetRequests,
+ snmpInGetResponses,
+ snmpInTraps,
+ snmpOutTooBigs,
+ snmpOutNoSuchNames,
+ snmpOutBadValues,
+ snmpOutGenErrs,
+ snmpOutGetRequests,
+ snmpOutGetNexts,
+ snmpOutSetRequests,
+ snmpOutGetResponses,
+ snmpOutTraps,
+ snmpSilentDrops,
+ snmpProxyDrops,
+ %% From SNMP-MPD-MIB
+ snmpUnknownSecurityModels,
+ snmpInvalidMsgs,
+ snmpUnknownPDUHandlers
+ ].
+
+
+%%-----------------------------------------------------------------
+%% inc(VariableName) increments the variable (Counter) in
+%% the local mib. (e.g. snmpInPkts)
+%%-----------------------------------------------------------------
+inc(Name) -> inc(Name, 1).
+inc(Name, N) -> snmpm_config:incr_stats_counter(Name, N).
+
+inc_snmp_in(#pdu{type = Type}) ->
+ inc_in_type(Type);
+inc_snmp_in(TrapPdu) when is_record(TrapPdu, trappdu) ->
+ inc(snmpInPkts),
+ inc(snmpInTraps).
+
+inc_snmp_out(#pdu{type = Type,
+ error_status = ErrorStatus}) ->
+ inc(snmpOutPkts),
+ inc_out_err(ErrorStatus),
+ inc_out_type(Type).
+
+inc_out_type('get-request') -> inc(snmpOutGetRequests);
+inc_out_type('get-next-request') -> inc(snmpOutGetNexts);
+inc_out_type('set-request') -> inc(snmpOutSetRequests);
+inc_out_type(_) -> ok.
+
+inc_out_err(genErr) -> inc(snmpOutGenErrs);
+inc_out_err(tooBig) -> inc(snmpOutTooBigs);
+inc_out_err(noSuchName) -> inc(snmpOutNoSuchNames);
+inc_out_err(badValue) -> inc(snmpOutBadValues);
+inc_out_err(_) -> ok.
+
+inc_in_type('get-response') -> inc(snmpInGetResponses);
+inc_in_type(_) -> ok.
+
+
+%%-----------------------------------------------------------------
+
+discard(Reason) ->
+ throw({discarded, Reason}).
+
+discard(Reason, Report) ->
+ throw({discarded, Reason, Report}).
+
+user_err(F, A) ->
+ error_msg("USER ERROR: " ++ F ++ "~n", A).
+
+config_err(F, A) ->
+ error_msg("CONFIG ERROR: " ++ F ++ "~n", A).
+
+error_msg(F, A) ->
+ ?snmpm_error("MPD: " ++ F, A).
diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl
new file mode 100644
index 0000000000..14d39933dc
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_net_if.erl
@@ -0,0 +1,1125 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_net_if).
+
+-behaviour(gen_server).
+-behaviour(snmpm_network_interface).
+
+
+%% Network Interface callback functions
+-export([
+ start_link/2,
+ stop/1,
+ send_pdu/6, % Backward compatibillity
+ send_pdu/7,
+
+ inform_response/4,
+
+ note_store/2,
+
+ info/1,
+ verbosity/2,
+ %% system_info_updated/2,
+ get_log_type/1, set_log_type/2,
+ filter_reset/1
+ ]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+-include("snmpm_internal.hrl").
+-include("snmpm_atl.hrl").
+-include("snmp_debug.hrl").
+
+%% -define(VMODULE,"NET_IF").
+-include("snmp_verbosity.hrl").
+
+-record(state,
+ {
+ server,
+ note_store,
+ sock,
+ mpd_state,
+ log,
+ irb = auto, % auto | {user, integer()}
+ irgc,
+ filter
+ }).
+
+
+-define(DEFAULT_FILTER_MODULE, snmpm_net_if_filter).
+-define(DEFAULT_FILTER_OPTS, [{module, ?DEFAULT_FILTER_MODULE}]).
+
+-ifdef(snmp_debug).
+-define(GS_START_LINK(Args),
+ gen_server:start_link(?MODULE, Args, [{debug,[trace]}])).
+-else.
+-define(GS_START_LINK(Args),
+ gen_server:start_link(?MODULE, Args, [])).
+-endif.
+
+
+-define(IRGC_TIMEOUT, timer:minutes(5)).
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+start_link(Server, NoteStore) ->
+ ?d("start_link -> entry with"
+ "~n Server: ~p"
+ "~n NoteStore: ~p", [Server, NoteStore]),
+ Args = [Server, NoteStore],
+ ?GS_START_LINK(Args).
+
+stop(Pid) ->
+ call(Pid, stop).
+
+send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port) ->
+ send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, undefined).
+
+send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo)
+ when is_record(Pdu, pdu) ->
+ ?d("send_pdu -> entry with"
+ "~n Pid: ~p"
+ "~n Pdu: ~p"
+ "~n Vsn: ~p"
+ "~n MsgData: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Pid, Pdu, Vsn, MsgData, Addr, Port]),
+ cast(Pid, {send_pdu, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo}).
+
+note_store(Pid, NoteStore) ->
+ call(Pid, {note_store, NoteStore}).
+
+inform_response(Pid, Ref, Addr, Port) ->
+ cast(Pid, {inform_response, Ref, Addr, Port}).
+
+info(Pid) ->
+ call(Pid, info).
+
+verbosity(Pid, V) ->
+ call(Pid, {verbosity, V}).
+
+%% system_info_updated(Pid, What) ->
+%% call(Pid, {system_info_updated, What}).
+
+get_log_type(Pid) ->
+ call(Pid, get_log_type).
+
+set_log_type(Pid, NewType) ->
+ call(Pid, {set_log_type, NewType}).
+
+filter_reset(Pid) ->
+ cast(Pid, filter_reset).
+
+
+%%%-------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%-------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%--------------------------------------------------------------------
+init([Server, NoteStore]) ->
+ ?d("init -> entry with"
+ "~n Server: ~p"
+ "~n NoteStore: ~p", [Server, NoteStore]),
+ case (catch do_init(Server, NoteStore)) of
+ {error, Reason} ->
+ {stop, Reason};
+ {ok, State} ->
+ {ok, State}
+ end.
+
+do_init(Server, NoteStore) ->
+ process_flag(trap_exit, true),
+
+ %% -- Prio --
+ {ok, Prio} = snmpm_config:system_info(prio),
+ process_flag(priority, Prio),
+
+ %% -- Create inform request table --
+ ets:new(snmpm_inform_request_table,
+ [set, protected, named_table, {keypos, 1}]),
+
+ %% -- Verbosity --
+ {ok, Verbosity} = snmpm_config:system_info(net_if_verbosity),
+ put(sname,mnif),
+ put(verbosity,Verbosity),
+ ?vlog("starting", []),
+
+ %% -- MPD --
+ {ok, Vsns} = snmpm_config:system_info(versions),
+ MpdState = snmpm_mpd:init(Vsns),
+
+ %% -- Module dependent options --
+ {ok, Opts} = snmpm_config:system_info(net_if_options),
+
+ %% -- Inform response behaviour --
+ {ok, IRB} = snmpm_config:system_info(net_if_irb),
+ IrGcRef = irgc_start(IRB),
+
+ %% -- Socket --
+ SndBuf = get_opt(Opts, sndbuf, default),
+ RecBuf = get_opt(Opts, recbuf, default),
+ BindTo = get_opt(Opts, bind_to, false),
+ NoReuse = get_opt(Opts, no_reuse, false),
+ {ok, Port} = snmpm_config:system_info(port),
+ {ok, Sock} = do_open_port(Port, SndBuf, RecBuf, BindTo, NoReuse),
+
+ %% Flow control --
+ FilterOpts = get_opt(Opts, filter, []),
+ FilterMod = create_filter(FilterOpts),
+ ?vdebug("FilterMod: ~w", [FilterMod]),
+
+ %% -- Audit trail log ---
+ {ok, ATL} = snmpm_config:system_info(audit_trail_log),
+ Log = do_init_log(ATL),
+
+ %% -- Initiate counters ---
+ init_counters(),
+
+ %% -- We are done ---
+ State = #state{server = Server,
+ note_store = NoteStore,
+ mpd_state = MpdState,
+ sock = Sock,
+ log = Log,
+ irb = IRB,
+ irgc = IrGcRef,
+ filter = FilterMod},
+ ?vdebug("started", []),
+ {ok, State}.
+
+
+%% Open port
+do_open_port(Port, SendSz, RecvSz, BindTo, NoReuse) ->
+ ?vtrace("do_open_port -> entry with"
+ "~n Port: ~p"
+ "~n SendSz: ~p"
+ "~n RecvSz: ~p"
+ "~n BindTo: ~p"
+ "~n NoReuse: ~p", [Port, SendSz, RecvSz, BindTo, NoReuse]),
+ IpOpts1 = bind_to(BindTo),
+ IpOpts2 = no_reuse(NoReuse),
+ IpOpts3 = recbuf(RecvSz),
+ IpOpts4 = sndbuf(SendSz),
+ IpOpts = [binary | IpOpts1 ++ IpOpts2 ++ IpOpts3 ++ IpOpts4],
+ OpenRes =
+ case init:get_argument(snmpm_fd) of
+ {ok, [[FdStr]]} ->
+ Fd = list_to_integer(FdStr),
+ gen_udp:open(0, [{fd, Fd}|IpOpts]);
+ error ->
+ gen_udp:open(Port, IpOpts)
+ end,
+ case OpenRes of
+ {error, _} = Error ->
+ throw(Error);
+ OK ->
+ OK
+ end.
+
+bind_to(true) ->
+ case snmpm_config:system_info(address) of
+ {ok, Addr} when is_list(Addr) ->
+ [{ip, list_to_tuple(Addr)}];
+ {ok, Addr} ->
+ [{ip, Addr}];
+ _ ->
+ []
+ end;
+bind_to(_) ->
+ [].
+
+no_reuse(false) ->
+ [{reuseaddr, true}];
+no_reuse(_) ->
+ [].
+
+recbuf(default) ->
+ [];
+recbuf(Sz) ->
+ [{recbuf, Sz}].
+
+sndbuf(default) ->
+ [];
+sndbuf(Sz) ->
+ [{sndbuf, Sz}].
+
+
+create_filter(Opts) when is_list(Opts) ->
+ case get_opt(Opts, module, ?DEFAULT_FILTER_MODULE) of
+ ?DEFAULT_FILTER_MODULE = Mod ->
+ Mod;
+ Module ->
+ snmpm_network_interface_filter:verify(Module),
+ Module
+ end;
+create_filter(BadOpts) ->
+ throw({error, {bad_filter_opts, BadOpts}}).
+
+
+%% Open log
+do_init_log(false) ->
+ ?vtrace("do_init_log(false) -> entry", []),
+ undefined;
+do_init_log(true) ->
+ ?vtrace("do_init_log(true) -> entry", []),
+ {ok, Type} = snmpm_config:system_info(audit_trail_log_type),
+ {ok, Dir} = snmpm_config:system_info(audit_trail_log_dir),
+ {ok, Size} = snmpm_config:system_info(audit_trail_log_size),
+ {ok, Repair} = snmpm_config:system_info(audit_trail_log_repair),
+ Name = ?audit_trail_log_name,
+ File = filename:absname(?audit_trail_log_file, Dir),
+ case snmp_log:create(Name, File, Size, Repair, true) of
+ {ok, Log} ->
+ {Log, Type};
+ {error, Reason} ->
+ throw({error, {failed_create_audit_log, Reason}})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_call({verbosity, Verbosity}, _From, State) ->
+ ?vlog("received verbosity request", []),
+ put(verbosity, Verbosity),
+ {reply, ok, State};
+
+%% handle_call({system_info_updated, What}, _From, State) ->
+%% ?vlog("received system_info_updated request with What = ~p", [What]),
+%% {NewState, Reply} = handle_system_info_updated(State, What),
+%% {reply, Reply, NewState};
+
+handle_call(get_log_type, _From, State) ->
+ ?vlog("received get-log-type request", []),
+ Reply = (catch handle_get_log_type(State)),
+ {reply, Reply, State};
+
+handle_call({set_log_type, NewType}, _From, State) ->
+ ?vlog("received set-log-type request with NewType = ~p", [NewType]),
+ {NewState, Reply} = (catch handle_set_log_type(State, NewType)),
+ {reply, Reply, NewState};
+
+handle_call({note_store, Pid}, _From, State) ->
+ ?vlog("received new note_store: ~w", [Pid]),
+ {reply, ok, State#state{note_store = Pid}};
+
+handle_call(stop, _From, State) ->
+ ?vlog("received stop request", []),
+ Reply = ok,
+ {stop, normal, Reply, State};
+
+handle_call(info, _From, State) ->
+ ?vlog("received info request", []),
+ Reply = get_info(State),
+ {reply, Reply, State};
+
+handle_call(Req, From, State) ->
+ warning_msg("received unknown request (from ~p): ~n~p", [Req, From]),
+ {reply, {error, {invalid_request, Req}}, State}.
+
+
+%%--------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_cast({send_pdu, Pdu, Vsn, MsgData, Addr, Port, _ExtraInfo}, State) ->
+ ?vlog("received send_pdu message with"
+ "~n Pdu: ~p"
+ "~n Vsn: ~p"
+ "~n MsgData: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Pdu, Vsn, MsgData, Addr, Port]),
+ maybe_handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, State),
+ {noreply, State};
+
+handle_cast({inform_response, Ref, Addr, Port}, State) ->
+ ?vlog("received inform_response message with"
+ "~n Ref: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Ref, Addr, Port]),
+ handle_inform_response(Ref, Addr, Port, State),
+ {noreply, State};
+
+handle_cast(filter_reset, State) ->
+ ?vlog("received filter_reset message", []),
+ reset_counters(),
+ {noreply, State};
+
+handle_cast(Msg, State) ->
+ warning_msg("received unknown message: ~n~p", [Msg]),
+ {noreply, State}.
+
+
+%%--------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_info({udp, Sock, Ip, Port, Bytes}, #state{sock = Sock} = State) ->
+ ?vlog("received ~w bytes from ~p:~p [~w]", [size(Bytes), Ip, Port, Sock]),
+ maybe_handle_recv_msg(Ip, Port, Bytes, State),
+ {noreply, State};
+
+handle_info(inform_response_gc, State) ->
+ ?vlog("received inform_response_gc message", []),
+ State2 = handle_inform_response_gc(State),
+ {noreply, State2};
+
+handle_info({disk_log, _Node, Log, Info}, State) ->
+ ?vlog("received disk_log message: "
+ "~n Info: ~p", [Info]),
+ State2 = handle_disk_log(Log, Info, State),
+ {noreply, State2};
+
+handle_info(Info, State) ->
+ warning_msg("received unknown info: ~n~p", [Info]),
+ {noreply, State}.
+
+
+%%--------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%--------------------------------------------------------------------
+terminate(Reason, #state{log = Log, irgc = IrGcRef}) ->
+ ?vdebug("terminate: ~p",[Reason]),
+ irgc_stop(IrGcRef),
+ %% Close logs
+ do_close_log(Log),
+ ok.
+
+
+do_close_log({Log, _Type}) ->
+ (catch snmp_log:sync(Log)),
+ (catch snmp_log:close(Log)),
+ ok;
+do_close_log(_) ->
+ ok.
+
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+
+code_change({down, _Vsn}, OldState, downgrade_to_pre45) ->
+ ?d("code_change(down) -> entry", []),
+ #state{server = Server,
+ note_store = NoteStore,
+ sock = Sock,
+ mpd_state = MpdState,
+ log = Log,
+ irgc = IrGcRef} = OldState,
+ irgc_stop(IrGcRef),
+ (catch ets:delete(snmpm_inform_request_table)),
+ State = {state, Server, NoteStore, Sock, MpdState, Log},
+ {ok, State};
+
+% upgrade
+code_change(_Vsn, OldState, upgrade_from_pre45) ->
+ ?d("code_change(up) -> entry", []),
+ {state, Server, NoteStore, Sock, MpdState, Log} = OldState,
+ State = #state{server = Server,
+ note_store = NoteStore,
+ sock = Sock,
+ mpd_state = MpdState,
+ log = Log,
+ irb = auto,
+ irgc = undefined},
+ ets:new(snmpm_inform_request_table,
+ [set, protected, named_table, {keypos, 1}]),
+ {ok, State};
+
+code_change(_Vsn, State, _Extra) ->
+ {ok, State}.
+
+
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+maybe_handle_recv_msg(Addr, Port, Bytes, #state{filter = FilterMod} = State) ->
+ case (catch FilterMod:accept_recv(Addr, Port)) of
+ false ->
+ %% Drop the received packet
+ inc(netIfMsgInDrops),
+ ok;
+ _ ->
+ handle_recv_msg(Addr, Port, Bytes, State)
+ end.
+
+
+handle_recv_msg(Addr, Port, Bytes, #state{server = Pid})
+ when is_binary(Bytes) andalso (size(Bytes) =:= 0) ->
+ Pid ! {snmp_error, {empty_message, Addr, Port}, Addr, Port},
+ ok;
+
+handle_recv_msg(Addr, Port, Bytes,
+ #state{server = Pid,
+ note_store = NoteStore,
+ mpd_state = MpdState,
+ sock = Sock,
+ log = Log} = State) ->
+ Logger = logger(Log, read, Addr, Port),
+ case (catch snmpm_mpd:process_msg(Bytes, snmpUDPDomain, Addr, Port,
+ MpdState, NoteStore, Logger)) of
+
+ {ok, Vsn, Pdu, MS, ACM} ->
+ maybe_handle_recv_pdu(Addr, Port, Vsn, Pdu, MS, ACM,
+ Logger, State);
+
+ {discarded, Reason, Report} ->
+ ?vdebug("discarded: ~p", [Reason]),
+ ErrorInfo = {failed_processing_message, Reason},
+ Pid ! {snmp_error, ErrorInfo, Addr, Port},
+ maybe_udp_send(State#state.filter, Sock, Addr, Port, Report),
+ ok;
+
+ {discarded, Reason} ->
+ ?vdebug("discarded: ~p", [Reason]),
+ ErrorInfo = {failed_processing_message, Reason},
+ Pid ! {snmp_error, ErrorInfo, Addr, Port},
+ ok;
+
+ Error ->
+ error_msg("processing of received message failed: "
+ "~n ~p", [Error]),
+ ok
+ end.
+
+
+maybe_handle_recv_pdu(Addr, Port,
+ Vsn, #pdu{type = Type} = Pdu, PduMS, ACM,
+ Logger,
+ #state{filter = FilterMod} = State) ->
+ case (catch FilterMod:accept_recv_pdu(Addr, Port, Type)) of
+ false ->
+ inc(netIfPduInDrops),
+ ok;
+ _ ->
+ handle_recv_pdu(Addr, Port, Vsn, Pdu, PduMS, ACM, Logger, State)
+ end;
+maybe_handle_recv_pdu(Addr, Port, Vsn, Trap, PduMS, ACM, Logger,
+ #state{filter = FilterMod} = State)
+ when is_record(Trap, trappdu) ->
+ case (catch FilterMod:accept_recv_pdu(Addr, Port, trappdu)) of
+ false ->
+ inc(netIfPduInDrops),
+ ok;
+ _ ->
+ handle_recv_pdu(Addr, Port, Vsn, Trap, PduMS, ACM, Logger, State)
+ end;
+maybe_handle_recv_pdu(Addr, Port, Vsn, Pdu, PduMS, ACM, Logger, State) ->
+ handle_recv_pdu(Addr, Port, Vsn, Pdu, PduMS, ACM, Logger, State).
+
+
+handle_recv_pdu(Addr, Port,
+ Vsn, #pdu{type = 'inform-request'} = Pdu, _PduMS, ACM,
+ Logger, #state{server = Pid, irb = IRB} = State) ->
+ handle_inform_request(IRB, Pid, Vsn, Pdu, ACM,
+ Addr, Port, Logger, State);
+handle_recv_pdu(Addr, Port,
+ _Vsn, #pdu{type = report} = Pdu, _PduMS, ok,
+ _Logger,
+ #state{server = Pid} = _State) ->
+ ?vtrace("received report - ok", []),
+ Pid ! {snmp_report, {ok, Pdu}, Addr, Port};
+handle_recv_pdu(Addr, Port,
+ _Vsn, #pdu{type = report} = Pdu, _PduMS,
+ {error, ReqId, Reason},
+ _Logger,
+ #state{server = Pid} = _State) ->
+ ?vtrace("received report - error", []),
+ Pid ! {snmp_report, {error, ReqId, Reason, Pdu}, Addr, Port};
+handle_recv_pdu(Addr, Port,
+ _Vsn, #pdu{type = 'snmpv2-trap'} = Pdu, _PduMS, _ACM,
+ _Logger,
+ #state{server = Pid} = _State) ->
+ ?vtrace("received snmpv2-trap", []),
+ Pid ! {snmp_trap, Pdu, Addr, Port};
+handle_recv_pdu(Addr, Port,
+ _Vsn, Trap, _PduMS, _ACM,
+ _Logger,
+ #state{server = Pid} = _State) when is_record(Trap, trappdu) ->
+ ?vtrace("received trappdu", []),
+ Pid ! {snmp_trap, Trap, Addr, Port};
+handle_recv_pdu(Addr, Port,
+ _Vsn, Pdu, _PduMS, _ACM,
+ _Logger,
+ #state{server = Pid} = _State) when is_record(Pdu, pdu) ->
+ ?vtrace("received pdu", []),
+ Pid ! {snmp_pdu, Pdu, Addr, Port};
+handle_recv_pdu(_Addr, _Port, _Vsn, Pdu, _PduMS, ACM, _Logger, _State) ->
+ ?vlog("received unexpected pdu: "
+ "~n Pdu: ~p"
+ "~n ACM: ~p", [Pdu, ACM]).
+
+
+handle_inform_request(auto, Pid, Vsn, Pdu, ACM, Addr, Port, Logger, State) ->
+ ?vtrace("received inform-request (true)", []),
+ Pid ! {snmp_inform, ignore, Pdu, Addr, Port},
+ RePdu = make_response_pdu(Pdu),
+ maybe_send_inform_response(RePdu, Vsn, ACM, Addr, Port, Logger, State);
+handle_inform_request({user, To}, Pid, Vsn, #pdu{request_id = ReqId} = Pdu,
+ ACM, Addr, Port, _Logger, _State) ->
+ ?vtrace("received inform-request (false)", []),
+
+ Pid ! {snmp_inform, ReqId, Pdu, Addr, Port},
+
+ %% Before we go any further, we need to check that we have not
+ %% already received this message (possible resend).
+
+ Key = {ReqId, Addr, Port},
+ case ets:lookup(snmpm_inform_request_table, Key) of
+ [_] ->
+ %% OK, we already know about this. We assume this
+ %% is a resend. Either the agent is really eager or
+ %% the user has not answered yet. Bad user!
+ ok;
+ [] ->
+ RePdu = make_response_pdu(Pdu),
+ Expire = t() + To,
+ Rec = {Key, Expire, {Vsn, ACM, RePdu}},
+ ets:insert(snmpm_inform_request_table, Rec)
+ end.
+
+handle_inform_response(Ref, Addr, Port, State) ->
+ Key = {Ref, Addr, Port},
+ case ets:lookup(snmpm_inform_request_table, Key) of
+ [{Key, _, {Vsn, ACM, RePdu}}] ->
+ Logger = logger(State#state.log, read, Addr, Port),
+ ets:delete(snmpm_inform_request_table, Key),
+ maybe_send_inform_response(RePdu, Vsn, ACM, Addr, Port,
+ Logger, State);
+ [] ->
+ %% Already acknowledged, or the user was to slow to reply...
+ ok
+ end,
+ ok.
+
+maybe_send_inform_response(RePdu, Vsn, ACM, Addr, Port, Logger,
+ #state{server = Pid,
+ sock = Sock,
+ filter = FilterMod}) ->
+ case (catch FilterMod:accept_send_pdu(Addr, Port, pdu_type_of(RePdu))) of
+ false ->
+ inc(netIfPduOutDrops),
+ ok;
+ _ ->
+ case snmpm_mpd:generate_response_msg(Vsn, RePdu, ACM, Logger) of
+ {ok, Msg} ->
+ maybe_udp_send(FilterMod, Sock, Addr, Port, Msg);
+ {discarded, Reason} ->
+ ?vlog("failed generating response message:"
+ "~n Reason: ~p", [Reason]),
+ ReqId = RePdu#pdu.request_id,
+ ErrorInfo = {failed_generating_response, {RePdu, Reason}},
+ Pid ! {snmp_error, ReqId, ErrorInfo, Addr, Port},
+ ok
+ end
+ end.
+
+handle_inform_response_gc(#state{irb = IRB} = State) ->
+ ets:safe_fixtable(snmpm_inform_request_table, true),
+ do_irgc(ets:first(snmpm_inform_request_table), t()),
+ ets:safe_fixtable(snmpm_inform_request_table, false),
+ State#state{irgc = irgc_start(IRB)}.
+
+%% We are deleting at the same time as we are traversing the table!!!
+do_irgc('$end_of_table', _) ->
+ ok;
+do_irgc(Key, Now) ->
+ Next = ets:next(snmpm_inform_request_table, Key),
+ case ets:lookup(snmpm_inform_request_table, Key) of
+ [{Key, BestBefore, _}] when BestBefore < Now ->
+ ets:delete(snmpm_inform_request_table, Key);
+ _ ->
+ ok
+ end,
+ do_irgc(Next, Now).
+
+irgc_start(auto) ->
+ undefined;
+irgc_start(_) ->
+ erlang:send_after(?IRGC_TIMEOUT, self(), inform_response_gc).
+
+irgc_stop(undefined) ->
+ ok;
+irgc_stop(Ref) ->
+ (catch erlang:cancel_timer(Ref)).
+
+
+maybe_handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port,
+ #state{filter = FilterMod} = State) ->
+ case (catch FilterMod:accept_send_pdu(Addr, Port, pdu_type_of(Pdu))) of
+ false ->
+ inc(netIfPduOutDrops),
+ ok;
+ _ ->
+ handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, State)
+ end.
+
+handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port,
+ #state{server = Pid,
+ note_store = NoteStore,
+ sock = Sock,
+ log = Log,
+ filter = FilterMod}) ->
+ Logger = logger(Log, write, Addr, Port),
+ case (catch snmpm_mpd:generate_msg(Vsn, NoteStore,
+ Pdu, MsgData, Logger)) of
+ {ok, Msg} ->
+ ?vtrace("handle_send_pdu -> message generated", []),
+ maybe_udp_send(FilterMod, Sock, Addr, Port, Msg);
+ {discarded, Reason} ->
+ ?vlog("PDU not sent: "
+ "~n PDU: ~p"
+ "~n Reason: ~p", [Pdu, Reason]),
+ Pid ! {snmp_error, Pdu, Reason},
+ ok
+ end.
+
+
+maybe_udp_send(FilterMod, Sock, Addr, Port, Msg) ->
+ case (catch FilterMod:accept_send(Addr, Port)) of
+ false ->
+ inc(netIfMsgOutDrops),
+ ok;
+ _ ->
+ udp_send(Sock, Addr, Port, Msg)
+ end.
+
+
+udp_send(Sock, Addr, Port, Msg) ->
+ case (catch gen_udp:send(Sock, Addr, Port, Msg)) of
+ ok ->
+ ?vdebug("sent ~w bytes to ~w:~w [~w]",
+ [sz(Msg), Addr, Port, Sock]),
+ ok;
+ {error, Reason} ->
+ error_msg("failed sending message to ~p:~p: "
+ "~n ~p",[Addr, Port, Reason]);
+ Error ->
+ error_msg("failed sending message to ~p:~p: "
+ "~n ~p",[Addr, Port, Error])
+ end.
+
+sz(B) when is_binary(B) ->
+ size(B);
+sz(L) when is_list(L) ->
+ length(L);
+sz(_) ->
+ undefined.
+
+
+handle_disk_log(_Log, {wrap, NoLostItems}, State) ->
+ ?vlog("Audit Trail Log - wrapped: ~w previously logged items where lost",
+ [NoLostItems]),
+ State;
+handle_disk_log(_Log, {truncated, NoLostItems}, State) ->
+ ?vlog("Audit Trail Log - truncated: ~w items where lost when truncating",
+ [NoLostItems]),
+ State;
+handle_disk_log(_Log, full, State) ->
+ error_msg("Failed to write to Audit Trail Log (full)", []),
+ State;
+handle_disk_log(_Log, {error_status, ok}, State) ->
+ State;
+handle_disk_log(_Log, {error_status, {error, Reason}}, State) ->
+ error_msg("Error status received from Audit Trail Log: "
+ "~n~p", [Reason]),
+ State;
+handle_disk_log(_Log, _Info, State) ->
+ State.
+
+
+%% mk_discovery_msg('version-3', Pdu, _VsnHdr, UserName) ->
+%% ScopedPDU = #scopedPdu{contextEngineID = "",
+%% contextName = "",
+%% data = Pdu},
+%% Bytes = snmp_pdus:enc_scoped_pdu(ScopedPDU),
+%% MsgID = get(msg_id),
+%% put(msg_id,MsgID+1),
+%% UsmSecParams =
+%% #usmSecurityParameters{msgAuthoritativeEngineID = "",
+%% msgAuthoritativeEngineBoots = 0,
+%% msgAuthoritativeEngineTime = 0,
+%% msgUserName = UserName,
+%% msgPrivacyParameters = "",
+%% msgAuthenticationParameters = ""},
+%% SecBytes = snmp_pdus:enc_usm_security_parameters(UsmSecParams),
+%% PduType = Pdu#pdu.type,
+%% Hdr = #v3_hdr{msgID = MsgID,
+%% msgMaxSize = 1000,
+%% msgFlags = snmp_misc:mk_msg_flags(PduType, 0),
+%% msgSecurityModel = ?SEC_USM,
+%% msgSecurityParameters = SecBytes},
+%% Msg = #message{version = 'version-3', vsn_hdr = Hdr, data = Bytes},
+%% case (catch snmp_pdus:enc_message_only(Msg)) of
+%% {'EXIT', Reason} ->
+%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
+%% error;
+%% L when list(L) ->
+%% {Msg, L}
+%% end;
+%% mk_discovery_msg(Version, Pdu, {Com, _, _, _, _}, UserName) ->
+%% Msg = #message{version = Version, vsn_hdr = Com, data = Pdu},
+%% case catch snmp_pdus:enc_message(Msg) of
+%% {'EXIT', Reason} ->
+%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
+%% error;
+%% L when list(L) ->
+%% {Msg, L}
+%% end.
+
+
+%% mk_msg('version-3', Pdu, {Context, User, EngineID, CtxEngineId, SecLevel},
+%% MsgData) ->
+%% %% Code copied from snmp_mpd.erl
+%% {MsgId, SecName, SecData} =
+%% if
+%% tuple(MsgData), Pdu#pdu.type == 'get-response' ->
+%% MsgData;
+%% true ->
+%% Md = get(msg_id),
+%% put(msg_id, Md + 1),
+%% {Md, User, []}
+%% end,
+%% ScopedPDU = #scopedPdu{contextEngineID = CtxEngineId,
+%% contextName = Context,
+%% data = Pdu},
+%% ScopedPDUBytes = snmp_pdus:enc_scoped_pdu(ScopedPDU),
+
+%% PduType = Pdu#pdu.type,
+%% V3Hdr = #v3_hdr{msgID = MsgId,
+%% msgMaxSize = 1000,
+%% msgFlags = snmp_misc:mk_msg_flags(PduType, SecLevel),
+%% msgSecurityModel = ?SEC_USM},
+%% Message = #message{version = 'version-3', vsn_hdr = V3Hdr,
+%% data = ScopedPDUBytes},
+%% SecEngineID = case PduType of
+%% 'get-response' -> snmp_framework_mib:get_engine_id();
+%% _ -> EngineID
+%% end,
+%% case catch snmp_usm:generate_outgoing_msg(Message, SecEngineID,
+%% SecName, SecData, SecLevel) of
+%% {'EXIT', Reason} ->
+%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
+%% error;
+%% {error, Reason} ->
+%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
+%% error;
+%% Packet ->
+%% Packet
+%% end;
+%% mk_msg(Version, Pdu, {Com, _User, _EngineID, _Ctx, _SecLevel}, _SecData) ->
+%% Msg = #message{version = Version, vsn_hdr = Com, data = Pdu},
+%% case catch snmp_pdus:enc_message(Msg) of
+%% {'EXIT', Reason} ->
+%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
+%% error;
+%% B when list(B) ->
+%% B
+%% end.
+
+
+%% handle_system_info_updated(#state{log = {Log, _OldType}} = State,
+%% audit_trail_log_type = _What) ->
+%% %% Just to make sure, check that ATL is actually enabled
+%% case snmpm_config:system_info(audit_trail_log) of
+%% {ok, true} ->
+%% {ok, Type} = snmpm_config:system_info(audit_trail_log_type),
+%% NewState = State#state{log = {Log, Type}},
+%% {NewState, ok};
+%% _ ->
+%% {State, {error, {adt_not_enabled}}}
+%% end;
+%% handle_system_info_updated(_State, _What) ->
+%% ok.
+
+handle_get_log_type(#state{log = {_Log, Value}} = State) ->
+ %% Just to make sure, check that ATL is actually enabled
+ case snmpm_config:system_info(audit_trail_log) of
+ {ok, true} ->
+ Type =
+ case {lists:member(read, Value), lists:member(write, Value)} of
+ {true, true} ->
+ read_write;
+ {true, false} ->
+ read;
+ {false, true} ->
+ write;
+ {false, false} ->
+ throw({State, {error, {bad_atl_type, Value}}})
+ end,
+ {ok, Type};
+ _ ->
+ {error, not_enabled}
+ end;
+handle_get_log_type(_State) ->
+ {error, not_enabled}.
+
+handle_set_log_type(#state{log = {Log, OldValue}} = State, NewType) ->
+ %% Just to make sure, check that ATL is actually enabled
+ case snmpm_config:system_info(audit_trail_log) of
+ {ok, true} ->
+ NewValue =
+ case NewType of
+ read ->
+ [read];
+ write ->
+ [write];
+ read_write ->
+ [read,write];
+ _ ->
+ throw({State, {error, {bad_atl_type, NewType}}})
+ end,
+ NewState = State#state{log = {Log, NewValue}},
+ OldType =
+ case {lists:member(read, OldValue),
+ lists:member(write, OldValue)} of
+ {true, true} ->
+ read_write;
+ {true, false} ->
+ read;
+ {false, true} ->
+ write;
+ {false, false} ->
+ throw({State, {error, {bad_atl_type, OldValue}}})
+ end,
+ {NewState, {ok, OldType}};
+ _ ->
+ {State, {error, not_enabled}}
+ end;
+handle_set_log_type(State, _NewType) ->
+ {State, {error, not_enabled}}.
+
+
+%% -------------------------------------------------------------------
+
+make_response_pdu(#pdu{request_id = ReqId, varbinds = Vbs}) ->
+ #pdu{type = 'get-response',
+ request_id = ReqId,
+ error_status = noError,
+ error_index = 0,
+ varbinds = Vbs}.
+
+
+%% ----------------------------------------------------------------
+
+pdu_type_of(#pdu{type = Type}) ->
+ Type;
+pdu_type_of(TrapPdu) when is_record(TrapPdu, trappdu) ->
+ trap.
+
+
+%% -------------------------------------------------------------------
+
+t() ->
+ {A,B,C} = erlang:now(),
+ A*1000000000+B*1000+(C div 1000).
+
+
+%% -------------------------------------------------------------------
+
+logger(undefined, _Type, _Addr, _Port) ->
+ fun(_) ->
+ ok
+ end;
+logger({Log, Types}, Type, Addr, Port) ->
+ case lists:member(Type, Types) of
+ true ->
+ fun(Msg) ->
+ snmp_log:log(Log, Msg, Addr, Port)
+ end;
+ false ->
+ fun(_) ->
+ ok
+ end
+ end.
+
+
+%% -------------------------------------------------------------------
+
+%% info_msg(F, A) ->
+%% ?snmpm_info("NET-IF server: " ++ F, A).
+
+warning_msg(F, A) ->
+ ?snmpm_warning("NET-IF server: " ++ F, A).
+
+error_msg(F, A) ->
+ ?snmpm_error("NET-IF server: " ++ F, A).
+
+
+
+%%%-------------------------------------------------------------------
+
+% get_opt(Key, Opts) ->
+% ?vtrace("get option ~w", [Key]),
+% snmp_misc:get_option(Key, Opts).
+
+get_opt(Opts, Key, Def) ->
+ ?vtrace("get option ~w with default ~p", [Key, Def]),
+ snmp_misc:get_option(Key, Opts, Def).
+
+
+%% -------------------------------------------------------------------
+
+get_info(#state{sock = Id}) ->
+ ProcSize = proc_mem(self()),
+ PortInfo = get_port_info(Id),
+ [{process_memory, ProcSize}, {port_info, PortInfo}].
+
+proc_mem(P) when is_pid(P) ->
+ case (catch erlang:process_info(P, memory)) of
+ {memory, Sz} when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end.
+%% proc_mem(_) ->
+%% undefined.
+
+
+get_port_info(Id) ->
+ PortInfo =
+ case (catch erlang:port_info(Id)) of
+ PI when is_list(PI) ->
+ [{port_info, PI}];
+ _ ->
+ []
+ end,
+ PortStatus =
+ case (catch prim_inet:getstatus(Id)) of
+ {ok, PS} ->
+ [{port_status, PS}];
+ _ ->
+ []
+ end,
+ PortAct =
+ case (catch inet:getopts(Id, [active])) of
+ {ok, PA} ->
+ [{port_act, PA}];
+ _ ->
+ []
+ end,
+ PortStats =
+ case (catch inet:getstat(Id)) of
+ {ok, Stat} ->
+ [{port_stats, Stat}];
+ _ ->
+ []
+ end,
+ IfList =
+ case (catch inet:getif(Id)) of
+ {ok, IFs} ->
+ [{interfaces, IFs}];
+ _ ->
+ []
+ end,
+ BufSz =
+ case (catch inet:getopts(Id, [recbuf, sndbuf, buffer])) of
+ {ok, Sz} ->
+ [{buffer_size, Sz}];
+ _ ->
+ []
+ end,
+ [{socket, Id}] ++
+ IfList ++
+ PortStats ++
+ PortInfo ++
+ PortStatus ++
+ PortAct ++
+ BufSz.
+
+
+%%-----------------------------------------------------------------
+%% Counter functions
+%%-----------------------------------------------------------------
+init_counters() ->
+ F = fun(Counter) -> maybe_create_counter(Counter) end,
+ lists:map(F, counters()).
+
+reset_counters() ->
+ F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end,
+ lists:map(F, counters()).
+
+maybe_create_counter(Counter) ->
+ snmpm_config:maybe_cre_stats_counter(Counter, 0).
+
+counters() ->
+ [
+ netIfMsgOutDrops,
+ netIfMsgInDrops,
+ netIfPduOutDrops,
+ netIfPduInDrops
+ ].
+
+inc(Name) -> inc(Name, 1).
+inc(Name, N) -> snmpm_config:incr_stats_counter(Name, N).
+
+%% get_counters() ->
+%% Counters = counters(),
+%% get_counters(Counters, []).
+
+%% get_counters([], Acc) ->
+%% lists:reverse(Acc);
+%% get_counters([Counter|Counters], Acc) ->
+%% case snmpm_config:get_stats_counter(Counter) of
+%% {ok, CounterVal} ->
+%% get_counters(Counters, [{Counter, CounterVal}|Acc]);
+%% _ ->
+%% get_counters(Counters, Acc)
+%% end.
+
+
+%% ----------------------------------------------------------------
+
+call(Pid, Req) ->
+ call(Pid, Req, infinity).
+
+call(Pid, Req, Timeout) ->
+ gen_server:call(Pid, Req, Timeout).
+
+cast(Pid, Msg) ->
+ gen_server:cast(Pid, Msg).
+
diff --git a/lib/snmp/src/manager/snmpm_net_if_filter.erl b/lib/snmp/src/manager/snmpm_net_if_filter.erl
new file mode 100644
index 0000000000..eb0c6efb11
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_net_if_filter.erl
@@ -0,0 +1,53 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(snmpm_net_if_filter).
+
+-export([accept_recv/2,
+ accept_send/2,
+ accept_recv_pdu/3,
+ accept_send_pdu/3]).
+
+-include("snmp_debug.hrl").
+
+accept_recv(_Addr, _Port) ->
+ ?d("accept_recv -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p", [_Addr, _Port]),
+ true.
+
+accept_send(_Addr, _Port) ->
+ ?d("accept_send -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p", [_Addr, _Port]),
+ true.
+
+accept_recv_pdu(_Addr, _Port, _PduType) ->
+ ?d("accept_recv_pdu -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n PduType: ~p", [_Addr, _Port, _PduType]),
+ true.
+
+accept_send_pdu(_Addr, _Port, _PduType) ->
+ ?d("accept_send_pdu -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n PduType: ~p", [_Addr, _Port, _PduType]),
+ true.
+
diff --git a/lib/snmp/src/manager/snmpm_network_interface.erl b/lib/snmp/src/manager/snmpm_network_interface.erl
new file mode 100644
index 0000000000..b830d45ce7
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_network_interface.erl
@@ -0,0 +1,37 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_network_interface).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [{start_link, 2},
+ {stop, 1},
+ {send_pdu, 7},
+ {inform_response, 4},
+ {note_store, 2},
+ {info, 1},
+ {verbosity, 2},
+ %% {system_info_updated, 2},
+ {get_log_type, 1},
+ {set_log_type, 2}];
+behaviour_info(_) ->
+ undefined.
+
diff --git a/lib/snmp/src/manager/snmpm_network_interface_filter.erl b/lib/snmp/src/manager/snmpm_network_interface_filter.erl
new file mode 100644
index 0000000000..ae8b7cfce1
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_network_interface_filter.erl
@@ -0,0 +1,54 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(snmpm_network_interface_filter).
+
+-export([behaviour_info/1]).
+-export([verify/1]).
+
+
+behaviour_info(callbacks) ->
+ [{accept_recv, 2},
+ {accept_send, 2},
+ {accept_recv_pdu, 3},
+ {accept_send_pdu, 3}];
+behaviour_info(_) ->
+ undefined.
+
+
+%% accept_recv(address(), port()) -> boolean()
+%% Called at the receiption of a message
+%% (before *any* processing has been done).
+%%
+%% accept_send(address(), port()) -> boolean()
+%% Called before the sending of a message
+%% (after *all* processing has been done).
+%%
+%% accept_recv_pdu(Addr, Port, pdu_type()) -> boolean()
+%% Called after the basic message processing (MPD) has been done,
+%% but before the pdu is handed over to the master-agent for
+%% primary processing.
+%%
+%% accept_send_pdu(Addr, Port, pdu_type()) -> boolean()
+%% Called before the basic message processing (MPD) is done,
+%% when a pdu has been received from the master-agent.
+%%
+
+
+verify(Module) ->
+ snmp_misc:verify_behaviour(?MODULE, Module).
diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl
new file mode 100644
index 0000000000..30aacc0ec3
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_server.erl
@@ -0,0 +1,3117 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_server).
+
+%%----------------------------------------------------------------------
+%% This module implements a simple SNMP manager for Erlang.
+%%
+%%----------------------------------------------------------------------
+
+%% User interface
+-export([start_link/0, stop/0,
+ is_started/0,
+
+ load_mib/1, unload_mib/1,
+
+ register_user/4, register_user_monitor/4, unregister_user/1,
+
+ sync_get/4, sync_get/5, sync_get/6,
+ async_get/4, async_get/5, async_get/6,
+ sync_get_next/4, sync_get_next/5, sync_get_next/6,
+ async_get_next/4, async_get_next/5, async_get_next/6,
+ sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8,
+ async_get_bulk/6, async_get_bulk/7, async_get_bulk/8,
+ sync_set/4, sync_set/5, sync_set/6,
+ async_set/4, async_set/5, async_set/6,
+ cancel_async_request/2,
+
+ %% discovery/2, discovery/3, discovery/4, discovery/5, discovery/6,
+
+ %% system_info_updated/2,
+ get_log_type/0, set_log_type/1,
+
+ reconfigure/0,
+
+ info/0,
+ verbosity/1, verbosity/2
+
+ ]).
+
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+%% GCT exports
+-export([gct_init/1, gct/2]).
+
+
+-include("snmpm_internal.hrl").
+-include("snmp_debug.hrl").
+-include("snmp_types.hrl").
+-include("STANDARD-MIB.hrl").
+-include("SNMP-FRAMEWORK-MIB.hrl").
+-include("snmp_verbosity.hrl").
+
+
+%%----------------------------------------------------------------------
+
+-define(SERVER, ?MODULE).
+
+-define(SYNC_GET_TIMEOUT, 5000).
+-define(SYNC_SET_TIMEOUT, 5000).
+-define(DEFAULT_ASYNC_EXPIRE, 5000).
+-define(EXTRA_INFO, undefined).
+
+-define(SNMP_AGENT_PORT, 161).
+
+
+-ifdef(snmp_debug).
+-define(GS_START_LINK(Args),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, Args,
+ [{debug,[trace]}])).
+-else.
+-define(GS_START_LINK(Args),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, Args, [])).
+-endif.
+
+
+
+%%----------------------------------------------------------------------
+
+-record(state,
+ {parent,
+ gct,
+ note_store,
+ note_store_ref,
+ net_if,
+ net_if_mod,
+ net_if_ref,
+ req, %% ???? Last request id in outgoing message
+ oid, %% ???? Last oid in request outgoing message
+ mini_mib
+ }
+ ).
+
+%% The active state is to ensure that nothing unpleasant happens
+%% during (after) a code_change. At the initial start of the
+%% application, this process (GCT) will make one run and then
+%% deactivate (unless some async request has been issued in the
+%% meantime).
+-record(gct, {parent, state = active, timeout}).
+
+-record(request,
+ {id,
+ user_id,
+ reg_type,
+ target,
+ addr,
+ port,
+ type,
+ data,
+ ref,
+ mon,
+ from,
+ discovery = false,
+ expire = infinity % When shall the request expire (time in ms)
+ }
+ ).
+
+-record(monitor,
+ {id,
+ mon,
+ proc
+ }
+ ).
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+
+start_link() ->
+ ?d("start_link -> entry", []),
+ Args = [],
+ ?GS_START_LINK(Args).
+
+stop() ->
+ call(stop).
+
+is_started() ->
+ case (catch call(is_started, 1000)) of
+ Bool when ((Bool =:= true) orelse (Bool =:= false)) ->
+ Bool;
+ _ ->
+ false
+ end.
+
+load_mib(MibFile) when is_list(MibFile) ->
+ call({load_mib, MibFile}).
+
+unload_mib(Mib) when is_list(Mib) ->
+ call({unload_mib, Mib}).
+
+
+register_user(UserId, UserMod, UserData, DefaultAgentConfig) ->
+ snmpm_config:register_user(UserId, UserMod, UserData, DefaultAgentConfig).
+
+register_user_monitor(Id, Module, Data, DefaultAgentConfig) ->
+ case register_user(Id, Module, Data, DefaultAgentConfig) of
+ ok ->
+ case call({monitor_user, Id, self()}) of
+ ok ->
+ ok;
+ Error ->
+ unregister_user(Id),
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+unregister_user(UserId) ->
+ call({unregister_user, UserId}).
+
+
+%% -- [sync] get --
+
+sync_get(UserId, TargetName, CtxName, Oids) ->
+ sync_get(UserId, TargetName, CtxName, Oids,
+ ?SYNC_GET_TIMEOUT).
+
+sync_get(UserId, TargetName, CtxName, Oids, Timeout) ->
+ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO).
+
+sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo)
+ when is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ call({sync_get, self(), UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}).
+
+%% -- [async] get --
+
+async_get(UserId, TargetName, CtxName, Oids) ->
+ async_get(UserId, TargetName, CtxName, Oids,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_get(UserId, TargetName, CtxName, Oids, Expire) ->
+ async_get(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO).
+
+async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo)
+ when (is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) andalso (Expire >= 0)) ->
+ call({async_get, self(), UserId, TargetName, CtxName, Oids, Expire,
+ ExtraInfo}).
+
+%% -- [sync] get-next --
+
+sync_get_next(UserId, TargetName, CtxName, Oids) ->
+ sync_get_next(UserId, TargetName, CtxName, Oids, ?SYNC_GET_TIMEOUT,
+ ?EXTRA_INFO).
+
+sync_get_next(UserId, TargetName, CtxName, Oids, Timeout) ->
+ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO).
+
+sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo)
+ when is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ call({sync_get_next, self(), UserId, TargetName, CtxName, Oids, Timeout,
+ ExtraInfo}).
+
+%% -- [async] get-next --
+
+async_get_next(UserId, TargetName, CtxName, Oids) ->
+ async_get_next(UserId, TargetName, CtxName, Oids,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_get_next(UserId, TargetName, CtxName, Oids, Expire) ->
+ async_get_next(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO).
+
+async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo)
+ when (is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) andalso (Expire >= 0)) ->
+ call({async_get_next, self(), UserId, TargetName, CtxName, Oids,
+ Expire, ExtraInfo}).
+
+%% -- [sync] get-bulk --
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) ->
+ sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ ?SYNC_GET_TIMEOUT, ?EXTRA_INFO).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout) ->
+ sync_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ Timeout, ?EXTRA_INFO).
+
+sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout,
+ ExtraInfo)
+ when is_list(TargetName) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Timeout) ->
+ call({sync_get_bulk, self(), UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}).
+
+%% -- [async] get-bulk --
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire) ->
+ async_get_bulk(UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids,
+ Expire, ?EXTRA_INFO).
+
+async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire,
+ ExtraInfo)
+ when is_list(TargetName) andalso
+ is_integer(NonRep) andalso
+ is_integer(MaxRep) andalso
+ is_list(CtxName) andalso
+ is_list(Oids) andalso
+ is_integer(Expire) ->
+ call({async_get_bulk, self(), UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo}).
+
+%% -- [sync] set --
+
+%% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value}
+sync_set(UserId, TargetName, CtxName, VarsAndVals) ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals,
+ ?SYNC_SET_TIMEOUT, ?EXTRA_INFO).
+
+sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout) ->
+ sync_set(UserId, TargetName, CtxName, VarsAndVals,
+ Timeout, ?EXTRA_INFO).
+
+sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo)
+ when is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Timeout) ->
+ call({sync_set, self(), UserId, TargetName,
+ CtxName, VarsAndVals, Timeout, ExtraInfo}).
+
+%% -- [async] set --
+
+async_set(UserId, TargetName, CtxName, VarsAndVals) ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals,
+ ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+async_set(UserId, TargetName, CtxName, VarsAndVals, Expire) ->
+ async_set(UserId, TargetName, CtxName, VarsAndVals,
+ Expire, ?EXTRA_INFO).
+
+async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo)
+ when (is_list(TargetName) andalso
+ is_list(CtxName) andalso
+ is_list(VarsAndVals) andalso
+ is_integer(Expire) andalso (Expire >= 0)) ->
+ call({async_set, self(), UserId, TargetName,
+ CtxName, VarsAndVals, Expire, ExtraInfo}).
+
+
+cancel_async_request(UserId, ReqId) ->
+ call({cancel_async_request, UserId, ReqId}).
+
+
+%% discovery(UserId, BAddr) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, [],
+%% ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Config) when is_list(Config) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, Config,
+%% ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO);
+
+%% discovery(UserId, BAddr, Expire) when is_integer(Expire) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, [], Expire, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Config, Expire) ->
+%% discovery(UserId, BAddr, ?SNMP_AGENT_PORT, Config, Expire, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Port, Config, Expire) ->
+%% discovery(UserId, BAddr, Port, Config, Expire, ?EXTRA_INFO).
+
+%% discovery(UserId, BAddr, Port, Config, Expire, ExtraInfo) ->
+%% call({discovery, self(), UserId, BAddr, Port, Config, Expire, ExtraInfo}).
+
+
+verbosity(Verbosity) ->
+ case ?vvalidate(Verbosity) of
+ Verbosity ->
+ call({verbosity, Verbosity});
+ _ ->
+ {error, {invalid_verbosity, Verbosity}}
+ end.
+
+info() ->
+ call(info).
+
+verbosity(net_if = Ref, Verbosity) ->
+ verbosity2(Ref, Verbosity);
+verbosity(note_store = Ref, Verbosity) ->
+ verbosity2(Ref, Verbosity).
+
+verbosity2(Ref, Verbosity) ->
+ case ?vvalidate(Verbosity) of
+ Verbosity ->
+ call({verbosity, Ref, Verbosity});
+ _ ->
+ {error, {invalid_verbosity, Verbosity}}
+ end.
+
+%% Target -> all | server | net_if
+%% system_info_updated(Target, What) ->
+%% call({system_info_updated, Target, What}).
+
+get_log_type() ->
+ call(get_log_type).
+
+set_log_type(NewType) ->
+ call({set_log_type, NewType}).
+
+reconfigure() ->
+ call(reconfigure).
+
+
+%%----------------------------------------------------------------------
+%% Options: List of
+%% {community, String ("public" is default}
+%% {mibs, List of Filenames}
+%% {trap_udp, integer() (default 5000)}
+%% {conf_dir, string()}
+%% {log_dir, string()}
+%% {db_dir, string()}
+%% {db_repair, true | false}
+%%----------------------------------------------------------------------
+init(_) ->
+ ?d("init -> entry", []),
+ case (catch do_init()) of
+ {ok, State} ->
+ {ok, State};
+ {error, Reason} ->
+ {stop, Reason}
+ end.
+
+
+%% Put all config stuff in a snmpm_config module/process.
+%% Tables should be protected so that it is cheap to
+%% read. Writing has to go through the interface...
+
+do_init() ->
+ process_flag(trap_exit, true),
+ {ok, Prio} = snmpm_config:system_info(prio),
+ process_flag(priority, Prio),
+
+ {ok, Verbosity} = snmpm_config:system_info(server_verbosity),
+ put(sname, mse),
+ put(verbosity, Verbosity),
+ ?vlog("starting", []),
+
+ %% Start the garbage collector timer process
+ {ok, Timeout} = snmpm_config:system_info(server_timeout),
+ {ok, GCT} = gct_start(Timeout),
+
+ %% -- Create request table --
+ ets:new(snmpm_request_table,
+ [set, protected, named_table, {keypos, #request.id}]),
+
+ %% -- Create monitor table --
+ ets:new(snmpm_monitor_table,
+ [set, protected, named_table, {keypos, #monitor.id}]),
+
+ %% -- Start the note-store and net-if processes --
+ {NoteStore, NoteStoreRef} = do_init_note_store(Prio),
+ {NetIf, NetIfModule, NetIfRef} = do_init_net_if(NoteStore),
+
+ MiniMIB = snmpm_config:make_mini_mib(),
+ State = #state{mini_mib = MiniMIB,
+ gct = GCT,
+ note_store = NoteStore,
+ note_store_ref = NoteStoreRef,
+ net_if = NetIf,
+ net_if_mod = NetIfModule,
+ net_if_ref = NetIfRef},
+ ?vlog("started", []),
+ {ok, State}.
+
+
+do_init_note_store(Prio) ->
+ ?vdebug("try start note store", []),
+ {ok, Verbosity} = snmpm_config:system_info(note_store_verbosity),
+ {ok, Timeout} = snmpm_config:system_info(note_store_timeout),
+ Opts = [{sname, mns},
+ {verbosity, Verbosity},
+ {timeout, Timeout}],
+ case snmpm_misc_sup:start_note_store(Prio, Opts) of
+ {ok, Pid} ->
+ ?vtrace("do_init_note_store -> Pid: ~p", [Pid]),
+ Ref = erlang:monitor(process, Pid),
+ {Pid, Ref};
+ {error, Reason} ->
+ ?vlog("failed starting note-store - Reason: "
+ "~n Reason: ~p"
+ "~n", [Reason]),
+ throw({error, {failed_starting_note_store, Reason}})
+ end.
+
+do_init_net_if(NoteStore) ->
+ ?vdebug("try start net if", []),
+ {ok, NetIfModule} = snmpm_config:system_info(net_if_module),
+ case snmpm_misc_sup:start_net_if(NetIfModule, NoteStore) of
+ {ok, Pid} ->
+ ?vtrace("do_init_net_if -> Pid: ~p", [Pid]),
+ Ref = erlang:monitor(process, Pid),
+ {Pid, NetIfModule, Ref};
+ {error, Reason} ->
+ ?vlog("failed starting net-if - Reason: "
+ "~n Reason: ~p"
+ "~n", [Reason]),
+ throw({error, {failed_starting_net_if, Reason}})
+ end.
+
+%% ---------------------------------------------------------------------
+%% ---------------------------------------------------------------------
+
+handle_call({monitor_user, Id, Pid}, _From, State) when is_pid(Pid) ->
+ ?vlog("received monitor_user request for ~w [~w]", [Id, Pid]),
+ Reply =
+ case ets:lookup(snmpm_monitor_table, Id) of
+ [#monitor{proc = Pid}] ->
+ ?vdebug("already monitored", []),
+ ok;
+
+ [#monitor{proc = OtherPid}] ->
+ ?vinfo("already registered to ~w", [OtherPid]),
+ {error, {already_monitored, OtherPid}};
+
+ [] ->
+ Ref = erlang:monitor(process, Pid),
+ ?vtrace("monitor ref: ~w", [Ref]),
+ Mon = #monitor{id = Id, mon = Ref, proc = Pid},
+ ets:insert(snmpm_monitor_table, Mon),
+ ok
+ end,
+ {reply, Reply, State};
+
+handle_call({unregister_user, UserId}, _From, State) ->
+ ?vlog("received request to unregister user ~p", [UserId]),
+
+ %% 1) If this user is monitored, then demonitor
+ ?vtrace("handle_call(unregister_user) -> maybe demonitor", []),
+ case ets:lookup(snmpm_monitor_table, UserId) of
+ [] ->
+ ok;
+ [#monitor{mon = M}] ->
+ maybe_demonitor(M), % This is really overkill (meybe_), but...
+ ok
+ end,
+
+ %% 2) Delete all outstanding requests from this user
+ ?vtrace("handle_call(unregister_user) -> "
+ "delete all outstanding requests for user", []),
+ Pat = #request{user_id = UserId,
+ id = '$1', ref = '$2', mon = '$3', _ = '_'},
+ Match = ets:match(snmpm_request_table, Pat),
+ ?vtrace("handle_call(unregister_user) -> Match: ~p", [Match]),
+ F1 = fun([ReqId, Ref, MonRef]) ->
+ ets:delete(snmpm_request_table, ReqId),
+ cancel_timer(Ref),
+ maybe_demonitor(MonRef),
+ ok
+ end,
+ lists:foreach(F1, Match),
+
+ %% 3) Unregister all agents registered by this user
+ ?vdebug("handle_call(unregister_user) -> "
+ "unregister all agents registered by user", []),
+ Agents = snmpm_config:which_agents(UserId),
+ ?vtrace("handle_call(unregister_user) -> Agents: ~p", [Agents]),
+ F2 = fun(TargetName) ->
+ snmpm_config:unregister_agent(UserId, TargetName)
+ end,
+ lists:foreach(F2, Agents),
+
+ %% 4) Unregister the user
+ ?vdebug("handle_call(unregister_user) -> unregister user", []),
+ Reply = snmpm_config:unregister_user(UserId),
+ ?vtrace("handle_call(unregister_user) -> Reply: ~p", [Reply]),
+ {reply, Reply, State};
+
+
+%% We will reply to this request later, when the reply comes in from the
+%% agent, or when the timeout hits (unless we get an error now).
+handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo},
+ From, State) ->
+ ?vlog("received sync_get [~p] request", [CtxName]),
+ case (catch handle_sync_get(Pid,
+ UserId, TargetName, CtxName, Oids,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) ->
+ ?vlog("received sync_get_next [~p] request", [CtxName]),
+ case (catch handle_sync_get_next(Pid,
+ UserId, TargetName, CtxName, Oids,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+%% Check agent version? This op not in v1
+handle_call({sync_get_bulk, Pid, UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo},
+ From, State) ->
+ ?vlog("received sync_get_bulk [~p] request", [CtxName]),
+ case (catch handle_sync_get_bulk(Pid,
+ UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({sync_set, Pid, UserId, TargetName,
+ CtxName, VarsAndVals, Timeout, ExtraInfo},
+ From, State) ->
+ ?vlog("received sync_set [~p] request", [CtxName]),
+ case (catch handle_sync_set(Pid,
+ UserId, TargetName, CtxName, VarsAndVals,
+ Timeout, ExtraInfo, From, State)) of
+ ok ->
+ {noreply, State};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({async_get, Pid, UserId, TargetName,
+ CtxName, Oids, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_get [~p] request", [CtxName]),
+ Reply = (catch handle_async_get(Pid, UserId, TargetName, CtxName, Oids,
+ Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+handle_call({async_get_next, Pid, UserId, TargetName,
+ CtxName, Oids, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_get_next [~p] request", [CtxName]),
+ Reply = (catch handle_async_get_next(Pid, UserId, TargetName, CtxName,
+ Oids, Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+%% Check agent version? This op not in v1
+handle_call({async_get_bulk, Pid, UserId, TargetName,
+ NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_get_bulk [~p] request", [CtxName]),
+ Reply = (catch handle_async_get_bulk(Pid,
+ UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids,
+ Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+handle_call({async_set, Pid, UserId, TargetName,
+ CtxName, VarsAndVals, Expire, ExtraInfo},
+ _From, State) ->
+ ?vlog("received async_set [~p] request", [CtxName]),
+ Reply = (catch handle_async_set(Pid, UserId, TargetName, CtxName,
+ VarsAndVals, Expire, ExtraInfo, State)),
+ {reply, Reply, State};
+
+
+handle_call({cancel_async_request, UserId, ReqId}, _From, State) ->
+ ?vlog("received cancel_async_request request", []),
+ Reply = (catch handle_cancel_async_request(UserId, ReqId, State)),
+ {reply, Reply, State};
+
+
+%% handle_call({discovery, Pid, UserId, BAddr, Port, Config, Expire, ExtraInfo},
+%% _From, State) ->
+%% ?vlog("received discovery request", []),
+%% Reply = (catch handle_discovery(Pid, UserId, BAddr, Port, Config,
+%% Expire, ExtraInfo, State)),
+%% {reply, Reply, State};
+
+
+handle_call({load_mib, Mib}, _From, State) ->
+ ?vlog("received load_mib request", []),
+ case snmpm_config:load_mib(Mib) of
+ ok ->
+ MiniMIB = snmpm_config:make_mini_mib(),
+ {reply, ok, State#state{mini_mib = MiniMIB}};
+ Error ->
+ {reply, Error, State}
+ end;
+
+
+handle_call({unload_mib, Mib}, _From, State) ->
+ ?vlog("received unload_mib request", []),
+ case snmpm_config:unload_mib(Mib) of
+ ok ->
+ MiniMIB = snmpm_config:make_mini_mib(),
+ {reply, ok, State#state{mini_mib = MiniMIB}};
+ Error ->
+ {reply, Error, State}
+ end;
+
+handle_call({verbosity, Verbosity}, _From, State) ->
+ ?vlog("received verbosity request", []),
+ put(verbosity, Verbosity),
+ {reply, ok, State};
+
+handle_call({verbosity, net_if, Verbosity}, _From,
+ #state{net_if = Pid, net_if_mod = Mod} = State) ->
+ ?vlog("received net_if verbosity request", []),
+ Mod:verbosity(Pid, Verbosity),
+ {reply, ok, State};
+
+handle_call({verbosity, note_store, Verbosity}, _From,
+ #state{note_store = Pid} = State) ->
+ ?vlog("received note_store verbosity request", []),
+ snmp_note_store:verbosity(Pid, Verbosity),
+ {reply, ok, State};
+
+handle_call(reconfigure, _From, State) ->
+ ?vlog("received reconfigure request", []),
+ Reply = {error, not_implemented},
+ {reply, Reply, State};
+
+handle_call(info, _From, State) ->
+ ?vlog("received info request", []),
+ Reply = get_info(State),
+ {reply, Reply, State};
+
+handle_call(is_started, _From, State) ->
+ ?vlog("received is_started request", []),
+ IsStarted = is_started(State),
+ {reply, IsStarted, State};
+
+%% handle_call({system_info_updated, Target, What}, _From, State) ->
+%% ?vlog("received system_info_updated request: "
+%% "~n Target: ~p"
+%% "~n What: ~p", [Target, What]),
+%% Reply = handle_system_info_updated(State, Target, What),
+%% {reply, Reply, State};
+
+handle_call(get_log_type, _From, State) ->
+ ?vlog("received get_log_type request", []),
+ Reply = handle_get_log_type(State),
+ {reply, Reply, State};
+
+handle_call({set_log_type, NewType}, _From, State) ->
+ ?vlog("received set_log_type request: "
+ "~n NewType: ~p", [NewType]),
+ Reply = handle_set_log_type(State, NewType),
+ {reply, Reply, State};
+
+handle_call(stop, _From, State) ->
+ ?vlog("received stop request", []),
+ {stop, normal, ok, State};
+
+
+handle_call(Req, _From, State) ->
+ warning_msg("received unknown request: ~n~p", [Req]),
+ {reply, {error, unknown_request}, State}.
+
+
+handle_cast(Msg, State) ->
+ warning_msg("received unknown message: ~n~p", [Msg]),
+ {noreply, State}.
+
+
+handle_info({sync_timeout, ReqId, From}, State) ->
+ ?vlog("received sync_timeout [~w] message", [ReqId]),
+ handle_sync_timeout(ReqId, From, State),
+ {noreply, State};
+
+
+handle_info({snmp_error, Pdu, Reason}, State) ->
+ ?vlog("received snmp_error message", []),
+ handle_snmp_error(Pdu, Reason, State),
+ {noreply, State};
+
+handle_info({snmp_error, Reason, Addr, Port}, State) ->
+ ?vlog("received snmp_error message", []),
+ handle_snmp_error(Addr, Port, -1, Reason, State),
+ {noreply, State};
+
+handle_info({snmp_error, ReqId, Reason, Addr, Port}, State) ->
+ ?vlog("received snmp_error message", []),
+ handle_snmp_error(Addr, Port, ReqId, Reason, State),
+ {noreply, State};
+
+%% handle_info({snmp_error, ReqId, Pdu, Reason, Addr, Port}, State) ->
+%% ?vlog("received snmp_error message", []),
+%% handle_snmp_error(Pdu, ReqId, Reason, Addr, Port, State),
+%% {noreply, State};
+
+
+handle_info({snmp_pdu, Pdu, Addr, Port}, State) ->
+ ?vlog("received snmp_pdu message", []),
+ handle_snmp_pdu(Pdu, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info({snmp_trap, Trap, Addr, Port}, State) ->
+ ?vlog("received snmp_trap message", []),
+ handle_snmp_trap(Trap, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info({snmp_inform, Ref, Pdu, Addr, Port}, State) ->
+ ?vlog("received snmp_inform message", []),
+ handle_snmp_inform(Ref, Pdu, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info({snmp_report, {ok, Pdu}, Addr, Port}, State) ->
+ handle_snmp_report(Pdu, Addr, Port, State),
+ {noreply, State};
+
+handle_info({snmp_report, {error, ReqId, Info, Pdu}, Addr, Port}, State) ->
+ handle_snmp_report(ReqId, Pdu, Info, Addr, Port, State),
+ {noreply, State};
+
+
+handle_info(gc_timeout, #state{gct = GCT} = State) ->
+ ?vlog("received gc_timeout message", []),
+ handle_gc(GCT),
+ {noreply, State};
+
+
+handle_info({'DOWN', _MonRef, process, Pid, _Reason},
+ #state{note_store = NoteStore,
+ net_if = Pid} = State) ->
+ ?vlog("received 'DOWN' message regarding net_if", []),
+ {NetIf, _, Ref} = do_init_net_if(NoteStore),
+ {noreply, State#state{net_if = NetIf, net_if_ref = Ref}};
+
+
+handle_info({'DOWN', _MonRef, process, Pid, _Reason},
+ #state{note_store = Pid,
+ net_if = NetIf,
+ net_if_mod = Mod} = State) ->
+ ?vlog("received 'DOWN' message regarding note_store", []),
+ {ok, Prio} = snmpm_config:system_info(prio),
+ {NoteStore, Ref} = do_init_note_store(Prio),
+ Mod:note_store(NetIf, NoteStore),
+ {noreply, State#state{note_store = NoteStore, note_store_ref = Ref}};
+
+
+handle_info({'DOWN', MonRef, process, Pid, Reason}, State) ->
+ ?vlog("received 'DOWN' message (~w) from ~w "
+ "~n Reason: ~p", [MonRef, Pid, Reason]),
+ handle_down(MonRef),
+ {noreply, State};
+
+
+handle_info({'EXIT', Pid, Reason}, #state{gct = Pid} = State) ->
+ ?vlog("received 'EXIT' message from the GCT (~w) process: "
+ "~n ~p", [Pid, Reason]),
+ {ok, Timeout} = snmpm_config:system_info(server_timeout),
+ {ok, GCT} = gct_start(Timeout),
+ {noreply, State#state{gct = GCT}};
+
+
+handle_info(Info, State) ->
+ warning_msg("received unknown info: ~n~p", [Info]),
+ {noreply, State}.
+
+
+%%----------------------------------------------------------
+%% Code change
+%%----------------------------------------------------------
+
+% downgrade
+code_change({down, _Vsn}, #state{gct = Pid} = State, _Extra) ->
+ ?d("code_change(down) -> entry", []),
+ gct_code_change(Pid),
+ {ok, State};
+
+% upgrade
+code_change(_Vsn, #state{gct = Pid} = State0, _Extra) ->
+ ?d("code_change(up) -> entry", []),
+ gct_code_change(Pid),
+ MiniMIB = snmpm_config:make_mini_mib(),
+ State = State0#state{mini_mib = MiniMIB},
+ {ok, State}.
+
+
+%%----------------------------------------------------------
+%% Terminate
+%%----------------------------------------------------------
+
+terminate(Reason, #state{gct = GCT}) ->
+ ?vdebug("terminate: ~p",[Reason]),
+ gct_stop(GCT),
+ snmpm_misc_sup:stop_note_store(),
+ snmpm_misc_sup:stop_net_if(),
+ ok.
+
+
+%%----------------------------------------------------------------------
+%%
+%%----------------------------------------------------------------------
+
+handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo,
+ From, State) ->
+ ?vtrace("handle_sync_get -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_get -> send a ~p message", [Vsn]),
+ ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port,
+ ExtraInfo, State),
+ ?vdebug("handle_sync_get -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_get -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+ Error ->
+ ?vinfo("failed retrieving agent data for get:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout,
+ ExtraInfo, From, State) ->
+ ?vtrace("handle_sync_get_next -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_get_next -> send a ~p message", [Vsn]),
+ ReqId = send_get_next_request(Oids, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_sync_get_next -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_get_next -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_next,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-next:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_sync_get_bulk(Pid, UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids, Timeout,
+ ExtraInfo, From, State) ->
+ ?vtrace("handle_sync_get_bulk -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n NonRep: ~p"
+ "~n MaxRep: ~p"
+ "~n Oids: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids,
+ Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_get_bulk -> send a ~p message", [Vsn]),
+ ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port,
+ NonRep, MaxRep, ExtraInfo, State),
+ ?vdebug("handle_sync_get_bulk -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_get_bulk -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_bulk,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-bulk:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout,
+ ExtraInfo, From, State) ->
+ ?vtrace("handle_sync_set -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n VarsAndVals: ~p"
+ "~n Timeout: ~p"
+ "~n From: ~p",
+ [Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, From]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_sync_set -> send a ~p message", [Vsn]),
+ ReqId = send_set_request(VarsAndVals, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_sync_set -> ReqId: ~p", [ReqId]),
+ Msg = {sync_timeout, ReqId, From},
+ Ref = erlang:send_after(Timeout, self(), Msg),
+ MonRef = erlang:monitor(process, Pid),
+ ?vtrace("handle_sync_set -> MonRef: ~p", [MonRef]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = set,
+ data = MsgData,
+ ref = Ref,
+ mon = MonRef,
+ from = From},
+ ets:insert(snmpm_request_table, Req),
+ ok;
+
+ Error ->
+ ?vinfo("failed retrieving agent data for set:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo,
+ State) ->
+ ?vtrace("handle_async_get -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_get -> send a ~p message", [Vsn]),
+ ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port,
+ ExtraInfo, State),
+ ?vdebug("handle_async_get -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get,
+ data = MsgData,
+ expire = t() + Expire},
+
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire,
+ ExtraInfo, State) ->
+ ?vtrace("handle_async_get_next -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n Oids: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, Oids, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_get_next -> send a ~p message", [Vsn]),
+ ReqId = send_get_next_request(Oids, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_async_get_next -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_next,
+ data = MsgData,
+ expire = t() + Expire},
+
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-next:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_get_bulk(Pid, UserId, TargetName, CtxName,
+ NonRep, MaxRep, Oids, Expire,
+ ExtraInfo, State) ->
+ ?vtrace("handle_async_get_bulk -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n NonRep: ~p"
+ "~n MaxRep: ~p"
+ "~n Oids: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_get_bulk -> send a ~p message", [Vsn]),
+ ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port,
+ NonRep, MaxRep, ExtraInfo, State),
+ ?vdebug("handle_async_get_bulk -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = get_bulk,
+ data = MsgData,
+ expire = t() + Expire},
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for get-bulk:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire,
+ ExtraInfo, State) ->
+ ?vtrace("handle_async_set -> entry with"
+ "~n Pid: ~p"
+ "~n UserId: ~p"
+ "~n TargetName: ~p"
+ "~n CtxName: ~p"
+ "~n VarsAndVals: ~p"
+ "~n Expire: ~p",
+ [Pid, UserId, TargetName, CtxName, VarsAndVals, Expire]),
+ case agent_data(TargetName, CtxName) of
+ {ok, RegType, Addr, Port, Vsn, MsgData} ->
+ ?vtrace("handle_async_set -> send a ~p message", [Vsn]),
+ ReqId = send_set_request(VarsAndVals, Vsn, MsgData,
+ Addr, Port, ExtraInfo, State),
+ ?vdebug("handle_async_set -> ReqId: ~p", [ReqId]),
+ Req = #request{id = ReqId,
+ user_id = UserId,
+ reg_type = RegType,
+ target = TargetName,
+ addr = Addr,
+ port = Port,
+ type = set,
+ data = MsgData,
+ expire = t() + Expire},
+
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ {ok, ReqId};
+
+ Error ->
+ ?vinfo("failed retrieving agent data for set:"
+ "~n TargetName: ~p"
+ "~n Error: ~p", [TargetName, Error]),
+ Error
+ end.
+
+
+handle_cancel_async_request(UserId, ReqId, _State) ->
+ ?vtrace("handle_cancel_async_request -> entry with"
+ "~n UserId: ~p"
+ "~n ReqId: ~p", [UserId, ReqId]),
+ case ets:lookup(snmpm_request_table, ReqId) of
+ [#request{user_id = UserId,
+ ref = Ref}] ->
+ ?vdebug("handle_cancel_async_request -> demonitor and cancel timer"
+ "~n Ref: ~p", [Ref]),
+ cancel_timer(Ref),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+ [#request{user_id = OtherUserId}] ->
+ ?vinfo("handle_cancel_async_request -> Not request owner"
+ "~n OtherUserId: ~p", [OtherUserId]),
+ {error, {not_owner, OtherUserId}};
+
+ [] ->
+ ?vlog("handle_cancel_async_request -> not found", []),
+ {error, not_found}
+ end.
+
+
+%% handle_system_info_updated(#state{net_if = Pid, net_if_mod = Mod} = _State,
+%% net_if = _Target, What) ->
+%% case (catch Mod:system_info_updated(Pid, What)) of
+%% {'EXIT', _} ->
+%% {error, not_supported};
+%% Else ->
+%% Else
+%% end;
+%% handle_system_info_updated(_State, Target, What) ->
+%% {error, {bad_target, Target, What}}.
+
+handle_get_log_type(#state{net_if = Pid, net_if_mod = Mod}) ->
+ case (catch Mod:get_log_type(Pid)) of
+ {'EXIT', _} ->
+ {error, not_supported};
+ Else ->
+ Else
+ end.
+
+handle_set_log_type(#state{net_if = Pid, net_if_mod = Mod}, NewType) ->
+ case (catch Mod:set_log_type(Pid, NewType)) of
+ {'EXIT', _} ->
+ {error, not_supported};
+ Else ->
+ Else
+ end.
+
+
+%% handle_discovery(Pid, UserId, BAddr, Port, Config, Expire, ExtraInfo, State) ->
+%% ?vtrace("handle_discovery -> entry with"
+%% "~n Pid: ~p"
+%% "~n UserId: ~p"
+%% "~n BAddr: ~p"
+%% "~n Port: ~p"
+%% "~n Config: ~p"
+%% "~n Expire: ~p",
+%% [Pid, UserId, BAddr, Port, Config, Expire]),
+%% case agent_data(default, default, "", Config) of
+%% {ok, Addr, Port, Vsn, MsgData} ->
+%% ?vtrace("handle_discovery -> send a ~p disco message", [Vsn]),
+%% ReqId = send_discovery(Vsn, MsgData, BAddr, Port, ExtraInfo,
+%% State),
+%% ?vdebug("handle_discovery -> ReqId: ~p", [ReqId]),
+%% MonRef = erlang:monitor(process, Pid),
+%% ?vtrace("handle_discovery -> MonRef: ~p", [MonRef]),
+%% Req = #request{id = ReqId,
+%% user_id = UserId,
+%% target = TargetName,
+%% addr = BAddr,
+%% port = Port,
+%% type = get,
+%% data = MsgData,
+%% mon = MonRef,
+%% discovery = true,
+%% expire = t() + Expire},
+%% ets:insert(snmpm_request_table, Req),
+%% gct_activate(State#state.gct),
+%% {ok, ReqId};
+
+%% Error ->
+%% ?vinfo("failed retrieving agent data for discovery (get):"
+%% "~n BAddr: ~p"
+%% "~n Port: ~p"
+%% "~n Error: ~p", [BAddr, Port, Error]),
+%% Error
+%% end.
+
+
+handle_sync_timeout(ReqId, From, State) ->
+ ?vtrace("handle_sync_timeout -> entry with"
+ "~n ReqId: ~p"
+ "~n From: ~p", [ReqId, From]),
+ case ets:lookup(snmpm_request_table, ReqId) of
+ [#request{mon = MonRef, from = From} = Req0] ->
+ ?vdebug("handle_sync_timeout -> "
+ "deliver reply (timeout) and demonitor: "
+ "~n Monref: ~p"
+ "~n From: ~p", [MonRef, From]),
+ gen_server:reply(From, {error, {timeout, ReqId}}),
+ maybe_demonitor(MonRef),
+
+ %%
+ %% Instead of deleting the request record now,
+ %% we leave it to the gc. But for that to work
+ %% we must update the expire value (which for
+ %% sync requests is infinity).
+ %%
+
+ Req = Req0#request{ref = undefined,
+ mon = undefined,
+ from = undefined,
+ expire = t()},
+ ets:insert(snmpm_request_table, Req),
+ gct_activate(State#state.gct),
+ ok;
+ _ ->
+ ok
+ end.
+
+
+handle_snmp_error(#pdu{request_id = ReqId} = Pdu, Reason, State) ->
+
+ ?vtrace("handle_snmp_error -> entry with"
+ "~n Reason: ~p"
+ "~n Pdu: ~p", [Reason, Pdu]),
+
+ case ets:lookup(snmpm_request_table, ReqId) of
+
+ %% Failed async request
+ [#request{user_id = UserId,
+ from = undefined,
+ ref = undefined,
+ mon = MonRef,
+ discovery = Disco}] ->
+
+ ?vdebug("handle_snmp_error -> "
+ "found corresponding request: "
+ "~n failed async request"
+ "~n UserId: ~p"
+ "~n ModRef: ~p"
+ "~n Disco: ~p", [UserId, MonRef, Disco]),
+
+ maybe_demonitor(MonRef),
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ handle_error(UserId, UserMod, Reason, ReqId,
+ UserData, State),
+ maybe_delete(Disco, ReqId);
+ _ ->
+ %% reply to outstanding request, for which there is no
+ %% longer any owner (the user has unregistered).
+ %% Therefor send it to the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason, ReqId,
+ DefData, State),
+ maybe_delete(Disco, ReqId);
+ _ ->
+ error_msg("failed retreiving the default user "
+ "info handling error [~w]: "
+ "~n~w", [ReqId, Reason])
+ end
+ end;
+
+
+ %% Failed sync request
+ %%
+ [#request{ref = Ref, mon = MonRef, from = From}] ->
+
+ ?vdebug("handle_snmp_error -> "
+ "found corresponding request: "
+ "~n failed sync request"
+ "~n Ref: ~p"
+ "~n ModRef: ~p"
+ "~n From: ~p", [Ref, MonRef, From]),
+
+ Remaining =
+ case (catch cancel_timer(Ref)) of
+ Rem when is_integer(Rem) ->
+ Rem;
+ _ ->
+ 0
+ end,
+
+ ?vtrace("handle_snmp_error -> Remaining: ~p", [Remaining]),
+
+ maybe_demonitor(MonRef),
+ Reply = {error, {send_failed, ReqId, Reason}},
+ ?vtrace("handle_snmp_error -> deliver (error-) reply",[]),
+ gen_server:reply(From, Reply),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+
+ %% A very old reply, see if this agent is handled by
+ %% a user. In that case send it there, else to the
+ %% default user.
+ _ ->
+
+ ?vdebug("handle_snmp_error -> no request?", []),
+
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ _ ->
+ error_msg("failed retreiving the default "
+ "user info handling error [~w]: "
+ "~n~w",[ReqId, Reason])
+ end
+ end;
+
+handle_snmp_error(CrapError, Reason, _State) ->
+ error_msg("received crap (snmp) error =>"
+ "~n~p~n~p", [CrapError, Reason]),
+ ok.
+
+handle_snmp_error(Addr, Port, ReqId, Reason, State) ->
+
+ ?vtrace("handle_snmp_error -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n ReqId: ~p"
+ "~n Reason: ~p", [Addr, Port, ReqId, Reason]),
+
+ case snmpm_config:get_agent_user_id(Addr, Port) of
+ {ok, UserId} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ handle_error(UserId, UserMod, Reason, ReqId,
+ UserData, State);
+ _Error ->
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ _Error ->
+ error_msg("failed retreiving the default user "
+ "info handling snmp error "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, ReqId, Reason])
+ end
+ end;
+ _Error ->
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ _Error ->
+ error_msg("failed retreiving the default user "
+ "info handling snmp error "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, ReqId, Reason])
+ end
+ end.
+
+
+handle_error(_UserId, Mod, Reason, ReqId, Data, _State) ->
+ ?vtrace("handle_error -> entry when"
+ "~n Mod: ~p", [Mod]),
+ F = fun() -> (catch Mod:handle_error(ReqId, Reason, Data)) end,
+ handle_callback(F),
+ ok.
+
+
+handle_snmp_pdu(#pdu{type = 'get-response', request_id = ReqId} = Pdu,
+ Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_pdu(get-response) -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Pdu: ~p", [Addr, Port, Pdu]),
+
+ case ets:lookup(snmpm_request_table, ReqId) of
+
+ %% Reply to a async request or
+ %% possibly a late reply to a sync request
+ %% (ref is also undefined)
+ [#request{user_id = UserId,
+ reg_type = RegType,
+ target = Target,
+ from = undefined,
+ ref = undefined,
+ mon = MonRef,
+ discovery = Disco}] ->
+
+ ?vdebug("handle_snmp_pdu(get-response) -> "
+ "found corresponding request: "
+ "~n reply to async request or late reply to sync request"
+ "~n UserId: ~p"
+ "~n ModRef: ~p"
+ "~n Disco: ~p", [UserId, MonRef, Disco]),
+
+ maybe_demonitor(MonRef),
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpResponse = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ handle_pdu(UserId, UserMod,
+ RegType, Target, Addr, Port,
+ ReqId, SnmpResponse, UserData, State),
+ maybe_delete(Disco, ReqId);
+ _Error ->
+ %% reply to outstanding request, for which there is no
+ %% longer any owner (the user has unregistered).
+ %% Therefor send it to the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_pdu(DefUserId, DefMod,
+ RegType, Target, Addr, Port,
+ ReqId, SnmpResponse, DefData, State),
+ maybe_delete(Disco, ReqId);
+ Error ->
+ error_msg("failed retreiving the default user "
+ "info handling pdu from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port, Error, Pdu])
+ end
+ end;
+
+
+ %% Reply to a sync request
+ %%
+ [#request{ref = Ref, mon = MonRef, from = From}] ->
+
+ ?vdebug("handle_snmp_pdu(get-response) -> "
+ "found corresponding request: "
+ "~n reply to sync request"
+ "~n Ref: ~p"
+ "~n ModRef: ~p"
+ "~n From: ~p", [Ref, MonRef, From]),
+
+ Remaining =
+ case (catch cancel_timer(Ref)) of
+ Rem when is_integer(Rem) ->
+ Rem;
+ _ ->
+ 0
+ end,
+
+ ?vtrace("handle_snmp_pdu(get-response) -> Remaining: ~p",
+ [Remaining]),
+
+ maybe_demonitor(MonRef),
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpReply = {EStatus, EIndex, Varbinds2},
+ Reply = {ok, SnmpReply, Remaining},
+ ?vtrace("handle_snmp_pdu(get-response) -> deliver reply",[]),
+ gen_server:reply(From, Reply),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+
+ %% A very old reply, see if this agent is handled by
+ %% a user. In that case send it there, else to the
+ %% default user.
+ _ ->
+
+ ?vdebug("handle_snmp_pdu(get-response) -> "
+ "no corresponding request: "
+ "~n a very old reply", []),
+
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpInfo = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:get_agent_user_id(Addr, Port) of
+ {ok, UserId} ->
+ %% A very late reply or a reply to a request
+ %% that has been cancelled.
+ %%
+ ?vtrace("handle_snmp_pdu(get-response) -> "
+ "a very late reply:"
+ "~n UserId: ~p",[UserId]),
+ case snmpm_config:user_info(UserId) of
+ {ok, UserMod, UserData} ->
+ Reason = {unexpected_pdu, SnmpInfo},
+ handle_error(UserId, UserMod, Reason, ReqId,
+ UserData, State);
+ _Error ->
+ %% Ouch, found an agent but not it's user!!
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ Reason = {unexpected_pdu, SnmpInfo},
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ Error ->
+ error_msg("failed retreiving the default "
+ "user info handling (old) "
+ "pdu from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error, Pdu])
+ end
+ end;
+
+ {error, _} ->
+ %% No agent, so either this agent has been
+ %% unregistered, or this is a very late reply
+ %% to a request (possibly a discovery), which
+ %% has since been cancelled (either because of
+ %% a timeout or that the user has unregistered
+ %% itself (and with it all it's requests)).
+ %% No way to know which.
+ %%
+ ?vtrace("handle_snmp_pdu(get-response) -> "
+ "no agent info found", []),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ pdu, ignore,
+ SnmpInfo, DefData, State);
+ Error ->
+ error_msg("failed retreiving the default user "
+ "info handling (old) pdu when no user "
+ "found from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error, Pdu])
+ end
+ end
+ end;
+
+handle_snmp_pdu(CrapPdu, Addr, Port, _State) ->
+ error_msg("received crap (snmp) Pdu from ~w:~w =>"
+ "~p", [Addr, Port, CrapPdu]),
+ ok.
+
+
+handle_pdu(_UserId, Mod, target_name = _RegType, TargetName, _Addr, _Port,
+ ReqId, SnmpResponse, Data, _State) ->
+ ?vtrace("handle_pdu(target_name) -> entry when"
+ "~n Mod: ~p", [Mod]),
+ F = fun() ->
+ (catch Mod:handle_pdu(TargetName, ReqId, SnmpResponse, Data))
+ end,
+ handle_callback(F),
+ ok;
+handle_pdu(_UserId, Mod, addr_port = _RegType, _TargetName, Addr, Port,
+ ReqId, SnmpResponse, Data, _State) ->
+ ?vtrace("handle_pdu(addr_port) -> entry when"
+ "~n Mod: ~p", [Mod]),
+ F = fun() ->
+ (catch Mod:handle_pdu(Addr, Port, ReqId, SnmpResponse, Data))
+ end,
+ handle_callback(F),
+ ok.
+
+
+handle_agent(UserId, Mod, Addr, Port, Type, Ref, SnmpInfo, Data, State) ->
+ ?vtrace("handle_agent -> entry when"
+ "~n UserId: ~p"
+ "~n Type: ~p"
+ "~n Mod: ~p", [UserId, Type, Mod]),
+ F = fun() ->
+ do_handle_agent(UserId, Mod, Addr, Port,
+ Type, Ref, SnmpInfo, Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+do_handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ Type, Ref,
+ SnmpInfo, DefData, State) ->
+ ?vdebug("do_handle_agent -> entry when"
+ "~n DefUserId: ~p", [DefUserId]),
+ case (catch DefMod:handle_agent(Addr, Port, Type, SnmpInfo, DefData)) of
+ {'EXIT', {undef, _}} when Type =:= pdu ->
+ %% Maybe, still on the old API
+ ?vdebug("do_handle_agent -> maybe still on the old api", []),
+ case (catch DefMod:handle_agent(Addr, Port, SnmpInfo, DefData)) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ TargetName = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, addr_port},
+ {address, Addr},
+ {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config2) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+ {register, UserId2, TargetName, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p",
+ [UserId2, TargetName, Config]),
+ Config2 = ensure_present([{address, Addr}, {port, Port}],
+ Config),
+ Config3 = [{reg_type, target_name} | Config2],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config3) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+ _Ignore ->
+ ?vdebug("do_handle_agent -> ignore", []),
+ ok
+ end;
+
+ {'EXIT', {undef, _}} ->
+ %% If the user does not implement the new API (but the
+ %% old), then this clause catches all non-pdu handle_agent
+ %% calls. These calls was previously never made,so we make
+ %% a best-effert call (using reg-type target_name) to the
+ %% various callback functions, and leave it to the user to
+ %% figure out
+
+ %% Backward compatibillity crap
+ RegType = target_name,
+ Target = mk_target_name(Addr, Port, default_agent_config()),
+ case Type of
+ report ->
+ SnmpInform = SnmpInfo,
+ handle_report(DefUserId, DefMod,
+ RegType, Target, Addr, Port,
+ SnmpInform, DefData, State);
+
+ inform ->
+ SnmpInform = SnmpInfo,
+ handle_inform(DefUserId, DefMod, Ref,
+ RegType, Target, Addr, Port,
+ SnmpInform, DefData, State);
+
+ trap ->
+ SnmpTrapInfo = SnmpInfo,
+ handle_trap(DefUserId, DefMod,
+ RegType, Target, Addr, Port,
+ SnmpTrapInfo, DefData, State);
+
+ _ ->
+ error_msg("failed delivering ~w info to default user - "
+ "regarding agent "
+ "<~p,~p>: ~n~w", [Type, Addr, Port, SnmpInfo])
+ end;
+
+ {register, UserId2, TargetName, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p",
+ [UserId2, TargetName, Config]),
+ Config2 = ensure_present([{address, Addr}, {port, Port}], Config),
+ Config3 = [{reg_type, target_name} | Config2],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config3) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+
+ _Ignore ->
+ ?vdebug("do_handle_agent -> ignore", []),
+ ok
+
+ end.
+
+ensure_present([], Config) ->
+ Config;
+ensure_present([{Key, _Val} = Elem|Ensure], Config) ->
+ case lists:keymember(Key, 1, Config) of
+ false ->
+ ensure_present(Ensure, [Elem|Config]);
+ true ->
+ ensure_present(Ensure, Config)
+ end.
+
+
+%% Retrieve user info for this agent.
+%% If this is an unknown agent, then use the default user
+handle_snmp_trap(#trappdu{enterprise = Enteprise,
+ generic_trap = Generic,
+ specific_trap = Spec,
+ time_stamp = Timestamp,
+ varbinds = Varbinds} = Trap,
+ Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_trap [trappdu] -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Trap: ~p", [Addr, Port, Trap]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpTrapInfo = {Enteprise, Generic, Spec, Timestamp, Varbinds2},
+ do_handle_snmp_trap(SnmpTrapInfo, Addr, Port, State);
+
+handle_snmp_trap(#pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Trap,
+ Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_trap [pdu] -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Trap: ~p", [Addr, Port, Trap]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpTrapInfo = {EStatus, EIndex, Varbinds2},
+ do_handle_snmp_trap(SnmpTrapInfo, Addr, Port, State);
+
+handle_snmp_trap(CrapTrap, Addr, Port, _State) ->
+ error_msg("received crap (snmp) trap from ~w:~w =>"
+ "~p", [Addr, Port, CrapTrap]),
+ ok.
+
+do_handle_snmp_trap(SnmpTrapInfo, Addr, Port, State) ->
+ case snmpm_config:get_agent_user_info(Addr, Port) of
+ {ok, UserId, Target, RegType} ->
+ ?vtrace("handle_snmp_trap -> found user: ~p", [UserId]),
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port,
+ SnmpTrapInfo, Data, State);
+
+ Error1 ->
+ %% User no longer exists, unregister agent
+ ?vlog("[trap] failed retreiving user info for "
+ "user ~p: "
+ "~n ~p", [UserId, Error1]),
+ case snmpm_config:unregister_agent(UserId, Target) of
+ ok ->
+ %% Try use the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ trap, ignore,
+ SnmpTrapInfo, DefData, State);
+ Error2 ->
+ error_msg("failed retreiving the default "
+ "user info handling report from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port,
+ Error2, SnmpTrapInfo])
+ end;
+ Error3 ->
+ %% Failed unregister agent,
+ %% now its getting messy...
+ warning_msg("failed unregister agent ~p <~p,~p> "
+ "belonging to non-existing "
+ "user ~p, handling trap: "
+ "~n Error: ~w"
+ "~n Trap info: ~w",
+ [Target, Addr, Port, UserId,
+ Error3, SnmpTrapInfo])
+ end
+ end;
+
+ Error4 ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[trap] failed retreiving user id for agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error4]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ trap, ignore,
+ SnmpTrapInfo, DefData, State);
+ Error5 ->
+ error_msg("failed retreiving "
+ "the default user info handling trap from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error5, SnmpTrapInfo])
+ end
+ end,
+ ok.
+
+
+handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port, SnmpTrapInfo, Data, State) ->
+ ?vtrace("handle_trap -> entry with"
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ F = fun() ->
+ do_handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port,
+ SnmpTrapInfo, Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+
+do_handle_trap(UserId, Mod,
+ RegType, Target, Addr, Port, SnmpTrapInfo, Data, _State) ->
+ ?vdebug("do_handle_trap -> entry with"
+ "~n UserId: ~p", [UserId]),
+ HandleTrap =
+ case RegType of
+ target_name ->
+ fun() -> Mod:handle_trap(Target, SnmpTrapInfo, Data) end;
+ addr_port ->
+ fun() -> Mod:handle_trap(Addr, Port, SnmpTrapInfo, Data) end
+ end,
+
+ case (catch HandleTrap()) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_trap -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ Target2 = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, target_name},
+ {address, Addr}, {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling trap "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ {register, UserId2, Target2, Config} ->
+ ?vtrace("do_handle_trap -> register: "
+ "~n UserId2: ~p"
+ "~n Target2: ~p"
+ "~n Config: ~p", [UserId2, Target2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Config2 = [{reg_type, target_name} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling trap "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ unregister ->
+ ?vtrace("do_handle_trap -> unregister", []),
+ case snmpm_config:unregister_agent(UserId,
+ Addr, Port) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed unregistering agent "
+ "handling trap "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ _Ignore ->
+ ?vtrace("do_handle_trap -> ignore", []),
+ ok
+ end.
+
+
+handle_snmp_inform(Ref,
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu, Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_inform -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Pdu: ~p", [Addr, Port, Pdu]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpInform = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:get_agent_user_info(Addr, Port) of
+ {ok, UserId, Target, RegType} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ ?vdebug("[inform] callback handle_inform with: "
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port,
+ SnmpInform, Data, State);
+ Error1 ->
+ %% User no longer exists, unregister agent
+ case snmpm_config:unregister_agent(UserId, Target) of
+ ok ->
+ %% Try use the default user
+ ?vlog("[inform] failed retreiving user "
+ "info for user ~p:"
+ "~n ~p", [UserId, Error1]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ inform, Ref,
+ SnmpInform, DefData, State);
+ Error2 ->
+ error_msg("failed retreiving the default "
+ "user info handling inform from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port,
+ Error2, Pdu])
+ end;
+ Error3 ->
+ %% Failed unregister agent,
+ %% now its getting messy...
+ warning_msg("failed unregister agent ~p <~p,~p> "
+ "~n belonging to non-existing "
+ "user ~p, handling inform: "
+ "~n Error: ~w"
+ "~n Pdu: ~w",
+ [Target, Addr, Port, UserId,
+ Error3, Pdu])
+ end
+ end;
+
+ Error4 ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[inform] failed retreiving user id for agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error4]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ inform, Ref,
+ SnmpInform, DefData, State);
+ Error5 ->
+ error_msg("failed retreiving "
+ "the default user info handling inform from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error5, Pdu])
+ end
+ end,
+ ok;
+
+handle_snmp_inform(_Ref, CrapInform, Addr, Port, _State) ->
+ error_msg("received crap (snmp) inform from ~w:~w =>"
+ "~p", [Addr, Port, CrapInform]),
+ ok.
+
+handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port, SnmpInform, Data, State) ->
+ ?vtrace("handle_inform -> entry with"
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ F = fun() ->
+ do_handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port, SnmpInform,
+ Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+do_handle_inform(UserId, Mod, Ref,
+ RegType, Target, Addr, Port, SnmpInform, Data, State) ->
+ ?vdebug("do_handle_inform -> entry with"
+ "~n UserId: ~p", [UserId]),
+ HandleInform =
+ case RegType of
+ target_name ->
+ fun() -> Mod:handle_inform(Target, SnmpInform, Data) end;
+ addr_port ->
+ fun() -> Mod:handle_inform(Addr, Port, SnmpInform, Data) end
+ end,
+
+ Rep =
+ case (catch HandleInform()) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_inform -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Target2 = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, target_name},
+ {address, Addr}, {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling inform "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ {register, UserId2, Target2, Config} ->
+ ?vtrace("do_handle_inform -> register: "
+ "~n UserId2: ~p"
+ "~n Target2: ~p"
+ "~n Config: ~p", [UserId2, Target2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Config2 = [{reg_type, target_name} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling inform "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ unregister ->
+ ?vtrace("do_handle_inform -> unregister", []),
+ case snmpm_config:unregister_agent(UserId,
+ Addr, Port) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed unregistering agent "
+ "handling inform "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ reply
+ end;
+ no_reply ->
+ ?vtrace("do_handle_inform -> no_reply", []),
+ no_reply;
+ _Ignore ->
+ ?vtrace("do_handle_inform -> ignore", []),
+ reply
+ end,
+ handle_inform_response(Rep, Ref, Addr, Port, State),
+ ok.
+
+
+handle_inform_response(_, ignore, _Addr, _Port, _State) ->
+ ignore;
+handle_inform_response(no_reply, _Ref, _Addr, _Port, _State) ->
+ no_reply;
+handle_inform_response(_, Ref, Addr, Port,
+ #state{net_if = Pid, net_if_mod = Mod}) ->
+ ?vdebug("handle_inform -> response", []),
+ (catch Mod:inform_response(Pid, Ref, Addr, Port)).
+
+handle_snmp_report(#pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu, Addr, Port, State) ->
+
+ ?vtrace("handle_snmp_report -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Pdu: ~p", [Addr, Port, Pdu]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpReport = {EStatus, EIndex, Varbinds2},
+ case snmpm_config:get_agent_user_info(Addr, Port) of
+ {ok, UserId, Target, RegType} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ ?vdebug("[report] callback handle_report with: "
+ "~n ~p"
+ "~n ~p"
+ "~n ~p"
+ "~n ~p", [UserId, Mod, Target, SnmpReport]),
+ handle_report(UserId, Mod,
+ RegType, Target, Addr, Port,
+ SnmpReport, Data, State);
+ Error1 ->
+ %% User no longer exists, unregister agent
+ ?vlog("[report] failed retreiving user info "
+ "for user ~p:"
+ " ~n ~p", [UserId, Error1]),
+ case snmpm_config:unregister_agent(UserId, Target) of
+ ok ->
+ %% Try use the default user
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ report, ignore,
+ SnmpReport, DefData, State);
+
+ Error2 ->
+ error_msg("failed retreiving the default "
+ "user info handling report from "
+ "~p <~p,~p>: ~n~w~n~w",
+ [Target, Addr, Port,
+ Error2, Pdu])
+ end;
+ Error3 ->
+ %% Failed unregister agent,
+ %% now its getting messy...
+ warning_msg("failed unregister agent ~p <~p,~p> "
+ "belonging to non-existing "
+ "user ~p, handling report: "
+ "~n Error: ~w"
+ "~n Report: ~w",
+ [Target, Addr, Port, UserId,
+ Error3, Pdu])
+ end
+ end;
+
+ Error4 ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[report] failed retreiving user id for agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error4]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_agent(DefUserId, DefMod,
+ Addr, Port,
+ report, ignore,
+ SnmpReport, DefData, State);
+ Error5 ->
+ error_msg("failed retreiving "
+ "the default user info handling report from "
+ "<~p,~p>: ~n~w~n~w",
+ [Addr, Port, Error5, Pdu])
+ end
+ end,
+ ok;
+
+handle_snmp_report(CrapReport, Addr, Port, _State) ->
+ error_msg("received crap (snmp) report from ~w:~w =>"
+ "~p", [Addr, Port, CrapReport]),
+ ok.
+
+%% This could be from a failed get-request, so we might have a user
+%% waiting for a reply here. If there is, we handle this as an failed
+%% get-response (except for tha data which is different). Otherwise,
+%% we handle it as an error (reported via the handle_error callback
+%% function).
+handle_snmp_report(ReqId,
+ #pdu{error_status = EStatus,
+ error_index = EIndex,
+ varbinds = Varbinds} = Pdu,
+ {ReportReason, Info} = Rep,
+ Addr, Port, State)
+ when is_integer(ReqId) ->
+
+ ?vtrace("handle_snmp_report -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n ReqId: ~p"
+ "~n Rep: ~p"
+ "~n Pdu: ~p", [Addr, Port, ReqId, Rep, Pdu]),
+
+ Varbinds2 = fix_vbs_BITS(Varbinds),
+ SnmpReport = {EStatus, EIndex, Varbinds2},
+ Reason = {ReportReason, Info, SnmpReport},
+
+ %% Check if there is someone waiting for this request
+
+ case ets:lookup(snmpm_request_table, ReqId) of
+
+ [#request{from = From,
+ ref = Ref,
+ mon = MonRef}] when (From =/= undefined) andalso
+ (Ref =/= undefined) ->
+
+ ?vdebug("handle_snmp_report -> "
+ "found corresponding request: "
+ "~n reply to sync request"
+ "~n Ref: ~p"
+ "~n ModRef: ~p"
+ "~n From: ~p", [Ref, MonRef, From]),
+
+ Remaining =
+ case (catch cancel_timer(Ref)) of
+ Rem when is_integer(Rem) ->
+ Rem;
+ _ ->
+ 0
+ end,
+
+ ?vtrace("handle_snmp_pdu(get-response) -> Remaining: ~p",
+ [Remaining]),
+
+ maybe_demonitor(MonRef),
+
+ Reply = {error, Reason},
+ ?vtrace("handle_snmp_report -> deliver reply",[]),
+ gen_server:reply(From, Reply),
+ ets:delete(snmpm_request_table, ReqId),
+ ok;
+
+ _ ->
+ %% Either not a sync request or no such request. Either
+ %% way, this is error info, so handle it as such.
+
+ case snmpm_config:get_agent_user_id(Addr, Port) of
+ {ok, UserId} ->
+ case snmpm_config:user_info(UserId) of
+ {ok, Mod, Data} ->
+ ?vdebug("[report] callback handle_error with: "
+ "~n ~p"
+ "~n ~p"
+ "~n ~p", [UserId, Mod, Reason]),
+ handle_error(UserId, Mod, Reason, ReqId,
+ Data, State);
+ Error ->
+ %% Oh crap, use the default user
+ ?vlog("[report] failed retreiving user info for "
+ "user ~p:"
+ " ~n ~p", [UserId, Error]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason,
+ ReqId, DefData, State);
+ Error ->
+ error_msg("failed retreiving the "
+ "default user "
+ "info handling report from "
+ "<~p,~p>: ~n~w~n~w~n~w",
+ [Addr, Port, Error,
+ ReqId, Reason])
+ end
+ end;
+ Error ->
+ %% Unknown agent, pass it on to the default user
+ ?vlog("[report] failed retreiving user id for "
+ "agent <~p,~p>: "
+ "~n ~p", [Addr, Port, Error]),
+ case snmpm_config:user_info() of
+ {ok, DefUserId, DefMod, DefData} ->
+ handle_error(DefUserId, DefMod, Reason, ReqId,
+ DefData, State);
+ Error ->
+ error_msg("failed retreiving "
+ "the default user info handling "
+ "report from "
+ "<~p,~p>: ~n~w~n~w~n~w",
+ [Addr, Port, Error, ReqId, Reason])
+ end
+ end
+ end,
+ ok;
+
+handle_snmp_report(CrapReqId, CrapReport, CrapInfo, Addr, Port, _State) ->
+ error_msg("received crap (snmp) report from ~w:~w =>"
+ "~n~p~n~p~n~p", [Addr, Port, CrapReqId, CrapReport, CrapInfo]),
+ ok.
+
+
+handle_report(UserId, Mod, RegType, Target, Addr, Port,
+ SnmpReport, Data, State) ->
+ ?vtrace("handle_report -> entry with"
+ "~n UserId: ~p"
+ "~n Mod: ~p", [UserId, Mod]),
+ F = fun() ->
+ do_handle_report(UserId, Mod, RegType, Target, Addr, Port,
+ SnmpReport, Data, State)
+ end,
+ handle_callback(F),
+ ok.
+
+do_handle_report(UserId, Mod,
+ RegType, Target, Addr, Port, SnmpReport, Data, _State) ->
+ ?vdebug("do_handle_report -> entry with"
+ "~n UserId: ~p", [UserId]),
+ HandleReport =
+ case RegType of
+ target_name ->
+ fun() -> Mod:handle_report(Target, SnmpReport, Data) end;
+ addr_port ->
+ fun() -> Mod:handle_report(Addr, Port, SnmpReport, Data) end
+ end,
+
+ case (catch HandleReport()) of
+ {register, UserId2, Config} ->
+ ?vtrace("do_handle_report -> register: "
+ "~n UserId2: ~p"
+ "~n Config: ~p", [UserId2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Target2 = mk_target_name(Addr, Port, Config),
+ Config2 = [{reg_type, target_name},
+ {address, Addr}, {port, Port} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling report "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ {register, UserId2, Target2, Config} ->
+ ?vtrace("do_handle_report -> register: "
+ "~n UserId2: ~p"
+ "~n Target2: ~p"
+ "~n Config: ~p", [UserId2, Target2, Config]),
+ %% The only user which would do this is the
+ %% default user
+ Config2 = [{reg_type, target_name} | Config],
+ case snmpm_config:register_agent(UserId2, Target2, Config2) of
+ ok ->
+ reply;
+ {error, Reason} ->
+ error_msg("failed registering agent "
+ "handling report "
+ "~p <~p,~p>: ~n~w",
+ [Target2, Addr, Port, Reason]),
+ reply
+ end;
+ unregister ->
+ ?vtrace("do_handle_trap -> unregister", []),
+ case snmpm_config:unregister_agent(UserId,
+ Addr, Port) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed unregistering agent "
+ "handling report "
+ "<~p,~p>: ~n~w",
+ [Addr, Port, Reason]),
+ ok
+ end;
+ _Ignore ->
+ ?vtrace("do_handle_report -> ignore", []),
+ ok
+ end.
+
+
+handle_callback(F) ->
+ V = get(verbosity),
+ erlang:spawn(
+ fun() ->
+ put(sname, msew),
+ put(verbosity, V),
+ F()
+ end).
+
+
+handle_down(MonRef) ->
+ (catch do_handle_down(MonRef)).
+
+do_handle_down(MonRef) ->
+ %% Clear out all requests from this client
+ handle_down_requests_cleanup(MonRef),
+
+ %%
+ %% Check also if this was a monitored user, and if so
+ %% unregister all agents registered by this user, and
+ %% finally unregister the user itself
+ %%
+ handle_down_user_cleanup(MonRef),
+
+ ok.
+
+
+handle_down_requests_cleanup(MonRef) ->
+ Pat = #request{id = '$1', ref = '$2', mon = MonRef, _ = '_'},
+ Match = ets:match(snmpm_request_table, Pat),
+ Fun = fun([Id, Ref]) ->
+ ?vtrace("delete request: ~p", [Id]),
+ ets:delete(snmpm_request_table, Id),
+ cancel_timer(Ref),
+ ok
+ end,
+ lists:foreach(Fun, Match).
+
+handle_down_user_cleanup(MonRef) ->
+ Pat = #monitor{id = '$1', mon = MonRef, _ = '_'},
+ Match = ets:match(snmpm_monitor_table, Pat),
+ Fun = fun([Id]) ->
+ Agents = snmpm_config:which_agents(Id),
+ lists:foreach(
+ fun({Addr,Port}) ->
+ %% ** Previous format **
+ %% Just in case this happens during code upgrade
+ ?vtrace("unregister agent of monitored user "
+ "~w: <~w,~w>", [Id,Addr,Port]),
+ snmpm_config:unregister_agent(Id, Addr, Port);
+ (TargetName) ->
+ ?vtrace("unregister agent of monitored user "
+ "~w: ~p", [Id,TargetName]),
+ snmpm_config:unregister_agent(Id, TargetName)
+ end,
+ Agents),
+ ?vtrace("unregister monitored user: ~w", [Id]),
+ ets:delete(snmpm_monitor_table, Id),
+ snmpm_config:unregister_user(Id),
+ ok
+ end,
+ lists:foreach(Fun, Match).
+
+cancel_timer(undefined) ->
+ ok;
+cancel_timer(Ref) ->
+ (catch erlang:cancel_timer(Ref)).
+
+handle_gc(GCT) ->
+ ets:safe_fixtable(snmpm_request_table, true),
+ case do_gc(ets:first(snmpm_request_table), t()) of
+ 0 ->
+ gct_deactivate(GCT);
+ _ ->
+ ok
+ end,
+ ets:safe_fixtable(snmpm_request_table, false).
+
+
+
+%% We are deleting at the same time as we are traversing the table!!!
+do_gc('$end_of_table', _) ->
+ ets:info(snmpm_request_table, size);
+do_gc(Key, Now) ->
+ Next = ets:next(snmpm_request_table, Key),
+ case ets:lookup(snmpm_request_table, Key) of
+ [#request{expire = BestBefore}] when (BestBefore < Now) ->
+ ets:delete(snmpm_request_table, Key);
+ _ ->
+ ok
+ end,
+ do_gc(Next, Now).
+
+
+
+%%----------------------------------------------------------------------
+%%
+%%----------------------------------------------------------------------
+
+send_get_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo,
+ #state{net_if = NetIf,
+ net_if_mod = Mod,
+ mini_mib = MiniMIB}) ->
+ Pdu = make_pdu(get, Oids, MiniMIB),
+ ?vtrace("send_get_request -> send get-request:"
+ "~n Mod: ~p"
+ "~n NetIf: ~p"
+ "~n Pdu: ~p"
+ "~n Vsn: ~p"
+ "~n MsgData: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Mod, NetIf, Pdu, Vsn, MsgData, Addr, Port]),
+ (catch Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo)),
+ Pdu#pdu.request_id.
+
+send_get_next_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo,
+ #state{mini_mib = MiniMIB,
+ net_if = NetIf,
+ net_if_mod = Mod}) ->
+ Pdu = make_pdu(get_next, Oids, MiniMIB),
+ Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+ Pdu#pdu.request_id.
+
+send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port,
+ NonRep, MaxRep, ExtraInfo,
+ #state{mini_mib = MiniMIB,
+ net_if = NetIf,
+ net_if_mod = Mod}) ->
+ Pdu = make_pdu(bulk, {NonRep, MaxRep, Oids}, MiniMIB),
+ Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+ Pdu#pdu.request_id.
+
+send_set_request(VarsAndVals, Vsn, MsgData, Addr, Port, ExtraInfo,
+ #state{mini_mib = MiniMIB,
+ net_if = NetIf,
+ net_if_mod = Mod}) ->
+ Pdu = make_pdu(set, VarsAndVals, MiniMIB),
+ Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+ Pdu#pdu.request_id.
+
+%% send_discovery(Vsn, MsgData, Addr, Port, ExtraInfo,
+%% #state{net_if = NetIf,
+%% net_if_mod = Mod}) ->
+%% Pdu = make_discovery_pdu(),
+%% Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo),
+%% Pdu#pdu.request_id.
+
+
+
+%%----------------------------------------------------------------------
+%%
+%%----------------------------------------------------------------------
+
+%% make_discovery_pdu() ->
+%% Oids = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance],
+%% make_pdu_impl(get, Oids).
+
+make_pdu(set, VarsAndVals, MiniMIB) ->
+ VBs = [var_and_value_to_varbind(VAV, MiniMIB) || VAV <- VarsAndVals],
+ make_pdu_impl(set, VBs);
+
+make_pdu(bulk, {NonRepeaters, MaxRepetitions, Oids}, MiniMIB) ->
+ Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
+ #pdu{type = 'get-bulk-request',
+ request_id = request_id(),
+ error_status = NonRepeaters,
+ error_index = MaxRepetitions,
+ varbinds = [make_vb(Foid) || Foid <- Foids]};
+
+make_pdu(Op, Oids, MiniMIB) ->
+ Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
+ make_pdu_impl(Op, Foids).
+
+
+make_pdu_impl(get, Oids) ->
+ #pdu{type = 'get-request',
+ request_id = request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = [make_vb(Oid) || Oid <- Oids]};
+
+make_pdu_impl(get_next, Oids) ->
+ #pdu{type = 'get-next-request',
+ request_id = request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = [make_vb(Oid) || Oid <- Oids]};
+
+make_pdu_impl(set, Varbinds) ->
+ #pdu{type = 'set-request',
+ request_id = request_id(),
+ error_status = noError,
+ error_index = 0,
+ varbinds = Varbinds}.
+
+
+fix_vbs_BITS(Varbinds) ->
+ [fix_vb_BITS(Varbind) || Varbind <- Varbinds].
+
+fix_vb_BITS(#varbind{oid = Oid,
+ variabletype = 'OCTET STRING' = _Type,
+ value = Value} = Varbind) ->
+ %% BITS are encoded as OCTET STRING, so this could be a BITS
+ %% check with the MiniMIB
+ case type_of_oid(Oid) of
+ {error, _} ->
+ Varbind;
+ {ok, NewType = 'BITS'} ->
+ NewValue = snmp_pdus:octet_str_to_bits(Value),
+ Varbind#varbind{variabletype = NewType,
+ value = NewValue};
+ _ ->
+ Varbind
+ end;
+fix_vb_BITS(Vb) ->
+ Vb.
+
+type_of_oid(Oid) ->
+ Oid2 = case lists:reverse(Oid) of
+ [0|T] ->
+ lists:reverse(T);
+ _ ->
+ Oid
+ end,
+ snmpm_config:oid_to_type(Oid2).
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Unnesting of oids like [myTable, 3, 4, "hej", 45] to
+%% [1,2,3,3,4,104,101,106,45]
+%%----------------------------------------------------------------------
+
+flatten_oid([A|T], MiniMIB) when is_atom(A) ->
+ Oid = [alias2oid(A, MiniMIB)|T],
+ check_is_pure_oid(lists:flatten(Oid));
+flatten_oid(Oid, _) when is_list(Oid) ->
+ check_is_pure_oid(lists:flatten(Oid));
+flatten_oid(Shit, _) ->
+ throw({error, {invalid_oid, Shit}}).
+
+check_is_pure_oid([]) -> [];
+check_is_pure_oid([X | T]) when is_integer(X) andalso (X >= 0) ->
+ [X | check_is_pure_oid(T)];
+check_is_pure_oid([X | _T]) ->
+ throw({error, {invalid_oid, X}}).
+
+
+var_and_value_to_varbind({Oid, Type, Value}, MiniMIB) ->
+ Oid2 = flatten_oid(Oid, MiniMIB),
+ #varbind{oid = Oid2,
+ variabletype = char_to_type(Type),
+ value = Value};
+var_and_value_to_varbind({Oid, Value}, MiniMIB) ->
+ Oid2 = flatten_oid(Oid, MiniMIB),
+ #varbind{oid = Oid2,
+ variabletype = oid2type(Oid2, MiniMIB),
+ value = Value}.
+
+char_to_type(i) ->
+ 'INTEGER';
+char_to_type(u) ->
+ 'Unsigned32';
+char_to_type(g) -> % Gauge, Gauge32
+ 'Unsigned32';
+char_to_type(b) ->
+ 'BITS';
+char_to_type(ip) ->
+ 'IpAddress';
+char_to_type(ia) ->
+ 'IpAddress';
+char_to_type(op) ->
+ 'Opaque';
+char_to_type(c32) ->
+ 'Counter32';
+char_to_type(c64) ->
+ 'Counter64';
+char_to_type(tt) ->
+ 'TimeTicks';
+char_to_type(o) ->
+ 'OBJECT IDENTIFIER';
+char_to_type(s) ->
+ 'OCTET STRING';
+char_to_type(C) ->
+ throw({error, {invalid_value_type, C}}).
+
+
+alias2oid(AliasName, MiniMIB) when is_atom(AliasName) ->
+ case lists:keysearch(AliasName, 2, MiniMIB) of
+ {value, {Oid, _Aliasname, _Type}} ->
+ Oid;
+ false ->
+ throw({error, {unknown_aliasname, AliasName}})
+ end.
+
+oid2type(Oid, MiniMIB) ->
+ Oid2 = case lists:reverse(Oid) of
+ [0|T] ->
+ lists:reverse(T);
+ _ ->
+ Oid
+ end,
+ oid2type(Oid2, MiniMIB, utter_nonsense).
+
+oid2type(_Oid, [], utter_nonsense) ->
+ throw({error, no_type});
+oid2type(_Oid, [], Type) ->
+ Type;
+oid2type(Oid, [{Oid2, _, Type}|MiniMIB], Res) when (Oid2 =< Oid) ->
+ case lists:prefix(Oid2, Oid) of
+ true ->
+ oid2type(Oid, MiniMIB, Type); % A better guess
+ false ->
+ oid2type(Oid, MiniMIB, Res)
+ end;
+oid2type(_Oid, _MiniMIB, utter_nonsense) ->
+ throw({error, no_type});
+oid2type(_Oid, _MiniMIB, Type) ->
+ Type.
+
+
+make_vb(Oid) ->
+ #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
+
+
+%%----------------------------------------------------------------------
+
+request_id() ->
+ snmpm_mpd:next_req_id().
+
+
+%%----------------------------------------------------------------------
+
+agent_data(TargetName, CtxName) ->
+ agent_data(TargetName, CtxName, []).
+
+agent_data(TargetName, CtxName, Config) ->
+ case snmpm_config:agent_info(TargetName, all) of
+ {ok, Info} ->
+ {value, {_, Version}} = lists:keysearch(version, 1, Info),
+ MsgData =
+ case Version of
+ v3 ->
+ DefSecModel = agent_data_item(sec_model, Info),
+ DefSecName = agent_data_item(sec_name, Info),
+ DefSecLevel = agent_data_item(sec_level, Info),
+
+ EngineId = agent_data_item(engine_id, Info),
+
+ SecModel = agent_data_item(sec_model,
+ Config,
+ DefSecModel),
+ SecName = agent_data_item(sec_name,
+ Config,
+ DefSecName),
+ SecLevel = agent_data_item(sec_level,
+ Config,
+ DefSecLevel),
+
+ {SecModel, SecName, mk_sec_level_flag(SecLevel),
+ EngineId, CtxName, TargetName};
+ _ ->
+ DefComm = agent_data_item(community, Info),
+ DefSecModel = agent_data_item(sec_model, Info),
+
+ Comm = agent_data_item(community,
+ Config,
+ DefComm),
+ SecModel = agent_data_item(sec_model,
+ Config,
+ DefSecModel),
+
+ {Comm, SecModel}
+ end,
+ Addr = agent_data_item(address, Info),
+ Port = agent_data_item(port, Info),
+ RegType = agent_data_item(reg_type, Info),
+ {ok, RegType, Addr, Port, version(Version), MsgData};
+ Error ->
+ Error
+ end.
+
+agent_data_item(Item, Info) ->
+ {value, {_, Val}} = lists:keysearch(Item, 1, Info),
+ Val.
+
+agent_data_item(Item, Info, Default) ->
+ case lists:keysearch(Item, 1, Info) of
+ {value, {_, Val}} ->
+ Val;
+ false ->
+ Default
+ end.
+
+
+version(v1) ->
+ 'version-1';
+version(v2) ->
+ 'version-2';
+version(v3) ->
+ 'version-3'.
+
+
+%%-----------------------------------------------------------------
+%% Convert the SecurityLevel into a flag value used by snmpm_mpd
+%%-----------------------------------------------------------------
+mk_sec_level_flag(?'SnmpSecurityLevel_noAuthNoPriv') -> 0;
+mk_sec_level_flag(?'SnmpSecurityLevel_authNoPriv') -> 1;
+mk_sec_level_flag(?'SnmpSecurityLevel_authPriv') -> 3.
+
+
+%%----------------------------------------------------------------------
+%% Request Garbage Collector timer
+%%----------------------------------------------------------------------
+
+gct_start(Timeout) ->
+ ?vdebug("start gc timer process (~p)", [Timeout]),
+ State = #gct{parent = self(), timeout = Timeout},
+ proc_lib:start_link(?MODULE, gct_init, [State]).
+
+gct_stop(GCT) ->
+ GCT ! {stop, self()}.
+
+gct_activate(GCT) ->
+ GCT ! {activate, self()}.
+
+gct_deactivate(GCT) ->
+ GCT ! {deactivate, self()}.
+
+gct_code_change(GCT) ->
+ GCT ! {code_change, self()}.
+
+gct_init(#gct{parent = Parent, timeout = Timeout} = State) ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ gct(State, Timeout).
+
+gct(#gct{parent = Parent, state = active} = State, Timeout) ->
+ T = t(),
+ receive
+ {stop, Parent} ->
+ ok;
+
+ %% This happens when a new request is received.
+ {activate, Parent} ->
+ ?MODULE:gct(State, new_timeout(Timeout, T));
+
+ {deactivate, Parent} ->
+ %% Timeout is of no consequence in the idle state,
+ %% but just to be sure
+ NewTimeout = State#gct.timeout,
+ ?MODULE:gct(State#gct{state = idle}, NewTimeout);
+
+ {code_change, Parent} ->
+ %% Let the server take care of this
+ exit(normal);
+
+ {'EXIT', Parent, _Reason} ->
+ ok;
+
+ _ -> % Crap
+ ?MODULE:gct(State, Timeout)
+
+ after Timeout ->
+ Parent ! gc_timeout,
+ NewTimeout = State#gct.timeout,
+ ?MODULE:gct(State, NewTimeout)
+ end;
+
+gct(#gct{parent = Parent, state = idle} = State, Timeout) ->
+ receive
+ {stop, Parent} ->
+ ok;
+
+ {deactivate, Parent} ->
+ ?MODULE:gct(State, Timeout);
+
+ {activate, Parent} ->
+ NewTimeout = State#gct.timeout,
+ ?MODULE:gct(State#gct{state = active}, NewTimeout);
+
+ {code_change, Parent} ->
+ %% Let the server take care of this
+ exit(normal);
+
+ {'EXIT', Parent, _Reason} ->
+ ok;
+
+ _ -> % Crap
+ ?MODULE:gct(State, Timeout)
+
+ after Timeout ->
+ ?MODULE:gct(State, Timeout)
+ end.
+
+new_timeout(T1, T2) ->
+ case T1 - (t() - T2) of
+ T when (T > 0) ->
+ T;
+ _ ->
+ 0
+ end.
+
+
+%%----------------------------------------------------------------------
+
+maybe_delete(false, ReqId) ->
+ ets:delete(snmpm_request_table, ReqId);
+maybe_delete(true, _) ->
+ ok.
+
+maybe_demonitor(undefined) ->
+ ok;
+maybe_demonitor(MonRef) ->
+ erlang:demonitor(MonRef).
+
+%% Time in milli seconds
+t() ->
+ {A,B,C} = erlang:now(),
+ A*1000000000+B*1000+(C div 1000).
+
+mk_target_name(Addr, Port, Config) ->
+ snmpm_config:mk_target_name(Addr, Port, Config).
+
+default_agent_config() ->
+ case snmpm_config:agent_info() of
+ {ok, Config} ->
+ Config;
+ _ ->
+ []
+ end.
+
+
+%%----------------------------------------------------------------------
+
+is_started(#state{net_if = _Pid, net_if_mod = _Mod}) ->
+ %% Mod:is_started(Pid) and snmpm_config:is_started().
+ case snmpm_config:is_started() of
+ true ->
+ true;
+ _ ->
+ false
+ end.
+
+
+%%----------------------------------------------------------------------
+
+call(Req) ->
+ call(Req, infinity).
+
+call(Req, To) ->
+ gen_server:call(?SERVER, Req, To).
+
+%% cast(Msg) ->
+%% gen_server:cast(?SERVER, Msg).
+
+%% info_msg(F, A) ->
+%% ?snmpm_info("Server: " ++ F, A).
+
+warning_msg(F, A) ->
+ ?snmpm_warning("Server: " ++ F, A).
+
+error_msg(F, A) ->
+ ?snmpm_error("Server: " ++ F, A).
+
+
+%%----------------------------------------------------------------------
+
+get_info(#state{gct = GCT,
+ net_if = NI, net_if_mod = NIMod,
+ note_store = NS}) ->
+ Info = [{server, server_info(GCT)},
+ {config, config_info()},
+ {net_if, net_if_info(NI, NIMod)},
+ {note_store, note_store_info(NS)},
+ {stats_counters, get_stats_counters()}],
+ Info.
+
+server_info(GCT) ->
+ ProcSize = proc_mem(self()),
+ GCTSz = proc_mem(GCT),
+ RTSz = tab_size(snmpm_request_table),
+ MTSz = tab_size(snmpm_monitor_table),
+ [{process_memory, [{server, ProcSize}, {gct, GCTSz}]},
+ {db_memory, [{request, RTSz}, {monitor, MTSz}]}].
+
+proc_mem(P) when is_pid(P) ->
+ case (catch erlang:process_info(P, memory)) of
+ {memory, Sz} when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end;
+proc_mem(_) ->
+ undefined.
+
+tab_size(T) ->
+ case (catch ets:info(T, memory)) of
+ Sz when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end.
+
+config_info() ->
+ case (catch snmpm_config:info()) of
+ Info when is_list(Info) ->
+ Info;
+ E ->
+ [{error, E}]
+ end.
+
+get_stats_counters() ->
+ lists:sort(snmpm_config:get_stats_counters()).
+
+
+net_if_info(Pid, Mod) ->
+ case (catch Mod:info(Pid)) of
+ Info when is_list(Info) ->
+ Info;
+ E ->
+ [{error, E}]
+ end.
+
+note_store_info(Pid) ->
+ case (catch snmp_note_store:info(Pid)) of
+ Info when is_list(Info) ->
+ Info;
+ E ->
+ [{error, E}]
+ end.
+
+
+%%----------------------------------------------------------------------
+
+
+%%----------------------------------------------------------------------
+%% Debug
+%%----------------------------------------------------------------------
+
+% sz(L) when is_list(L) ->
+% length(lists:flatten(L));
+% sz(B) when is_binary(B) ->
+% size(B).
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
+
diff --git a/lib/snmp/src/manager/snmpm_server_sup.erl b/lib/snmp/src/manager/snmpm_server_sup.erl
new file mode 100644
index 0000000000..16238e4aaf
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_server_sup.erl
@@ -0,0 +1,110 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_server_sup).
+
+-behaviour(supervisor).
+
+
+%% External exports
+-export([start_link/2, stop/0]).
+
+%% supervisor callbacks
+-export([init/1]).
+
+
+-define(SERVER, ?MODULE).
+
+-include("snmp_debug.hrl").
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+start_link(_Type, Opts) ->
+ ?d("start_link -> entry with"
+ "~n Opts: ~p", [Opts]),
+ SupName = {local, ?MODULE},
+ supervisor:start_link(SupName, ?MODULE, [Opts]).
+
+stop() ->
+ ?d("stop -> entry", []),
+ case whereis(?SERVER) of
+ Pid when is_pid(Pid) ->
+ ?d("stop -> Pid: ~p", [Pid]),
+ exit(Pid, shutdown),
+ ?d("stop -> stopped", []),
+ ok;
+ _ ->
+ ?d("stop -> not running", []),
+ not_running
+ end.
+
+
+%%%-------------------------------------------------------------------
+%%% Callback functions from supervisor
+%%%-------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, {SupFlags, [ChildSpec]}} |
+%% ignore |
+%% {error, Reason}
+%%--------------------------------------------------------------------
+init([Opts]) ->
+ ?d("init -> entry with"
+ "~n Opts: ~p", [Opts]),
+ Restart = get_restart(Opts),
+ Flags = {one_for_all, 5, 500},
+ Server = worker_spec(snmpm_server, [], Restart, [gen_server]),
+ Sups = [Server],
+ {ok, {Flags, Sups}}.
+
+
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+get_restart(Opts) ->
+ get_opt(Opts, restart_type, transient).
+
+get_opt(Opts, Key, Def) ->
+ snmp_misc:get_option(Key, Opts, Def).
+
+%% sup_spec(Name, Args, Restart) ->
+%% ?d("sup_spec -> entry with"
+%% "~n Name: ~p"
+%% "~n Args: ~p"
+%% "~n Restart: ~p", [Name, Args, Restart]),
+%% {Name,
+%% {Name, start_link, Args},
+%% Restart, 2000, supervisor, [Name,supervisor]}.
+
+worker_spec(Name, Args, Restart, Modules) ->
+ ?d("worker_spec -> entry with"
+ "~n Name: ~p"
+ "~n Args: ~p"
+ "~n Restart: ~p"
+ "~n Modules: ~p", [Name, Args, Restart, Modules]),
+ {Name,
+ {Name, start_link, Args},
+ Restart, 2000, worker, [Name] ++ Modules}.
+
+
+
diff --git a/lib/snmp/src/manager/snmpm_supervisor.erl b/lib/snmp/src/manager/snmpm_supervisor.erl
new file mode 100644
index 0000000000..8f43310c14
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_supervisor.erl
@@ -0,0 +1,115 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_supervisor).
+
+-behaviour(supervisor).
+
+
+%% External exports
+-export([start_link/2, stop/0]).
+
+%% supervisor callbacks
+-export([init/1]).
+
+
+-define(SERVER, ?MODULE).
+
+-include("snmp_debug.hrl").
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+start_link(Type, Opts) ->
+ ?d("start_link -> entry with"
+ "~n Opts: ~p", [Opts]),
+ SupName = {local, ?MODULE},
+ supervisor:start_link(SupName, ?MODULE, [Type, Opts]).
+
+stop() ->
+ ?d("stop -> entry", []),
+ case whereis(?SERVER) of
+ Pid when is_pid(Pid) ->
+ ?d("stop -> Pid: ~p", [Pid]),
+ exit(Pid, shutdown),
+ ?d("stop -> stopped", []),
+ ok;
+ _ ->
+ ?d("stop -> not running", []),
+ not_running
+ end.
+
+
+%%%-------------------------------------------------------------------
+%%% Callback functions from supervisor
+%%%-------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, {SupFlags, [ChildSpec]}} |
+%% ignore |
+%% {error, Reason}
+%%--------------------------------------------------------------------
+init([Opts]) when is_list(Opts) -> %% OTP-5963: Due to the addition
+ init([normal, Opts]); %% OTP-5963: of server_sup
+init([Type, Opts]) ->
+ ?d("init -> entry with"
+ "~n Type: ~p"
+ "~n Opts: ~p", [Type, Opts]),
+ Restart = get_restart(Opts),
+ Flags = {one_for_all, 0, 3600},
+ Config = worker_spec(snmpm_config, [Opts], Restart, [gen_server]),
+ MiscSup = sup_spec(snmpm_misc_sup, [], Restart),
+ ServerSup = sup_spec(snmpm_server_sup, [Type, Opts], Restart),
+ Sups = [Config, MiscSup, ServerSup],
+ {ok, {Flags, Sups}}.
+
+
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+get_restart(Opts) ->
+ get_opt(Opts, restart_type, transient).
+
+get_opt(Opts, Key, Def) ->
+ snmp_misc:get_option(Key, Opts, Def).
+
+sup_spec(Name, Args, Restart) ->
+ ?d("sup_spec -> entry with"
+ "~n Name: ~p"
+ "~n Args: ~p"
+ "~n Restart: ~p", [Name, Args, Restart]),
+ {Name,
+ {Name, start_link, Args},
+ Restart, 2000, supervisor, [Name,supervisor]}.
+
+worker_spec(Name, Args, Restart, Modules) ->
+ ?d("worker_spec -> entry with"
+ "~n Name: ~p"
+ "~n Args: ~p"
+ "~n Restart: ~p"
+ "~n Modules: ~p", [Name, Args, Restart, Modules]),
+ {Name,
+ {Name, start_link, Args},
+ Restart, 2000, worker, [Name] ++ Modules}.
+
+
+
diff --git a/lib/snmp/src/manager/snmpm_user.erl b/lib/snmp/src/manager/snmpm_user.erl
new file mode 100644
index 0000000000..78aa560b2e
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_user.erl
@@ -0,0 +1,97 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_user).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [{handle_error, 3},
+ {handle_agent, 5},
+ {handle_pdu, 4},
+ {handle_trap, 3},
+ {handle_inform, 3},
+ {handle_report, 3}];
+behaviour_info(_) ->
+ undefined.
+
+
+%% handle_error(ReqId, Reason, UserData) -> Reply
+%% ReqId -> integer()
+%% Reason -> term()
+%% UserData -> term() (supplied when the user register)
+%% Reply -> ignore
+
+%% handle_agent(Addr, Port, Type, SnmpInfo, UserData) -> Reply
+%% Addr -> term()
+%% Port -> integer()
+%% Type -> pdu | trap | inform | report
+%% SnmpInfo -> {ErrorStatus, ErrorIndex, Varbinds}
+%% UserId -> term()
+%% ErrorStatus -> atom()
+%% ErrorIndex -> integer()
+%% Varbinds -> [varbind()]
+%% UserData -> term() (supplied when the user register)
+%% Reply -> ignore | {register, UserId, agent_info()}
+%% agent_info() -> [{agent_info_item(), agent_info_value()}]
+%% This is the same info as in update_agent_info/4
+
+%% handle_pdu(TargetName, ReqId, SnmpResponse, UserData) -> Reply
+%% TargetName -> target_name()
+%% ReqId -> term() (returned when calling ag(...), ...)
+%% SnmpResponse -> {ErrorStatus, ErrorIndex, Varbinds}
+%% ErrorStatus -> atom()
+%% ErrorIndex -> integer()
+%% Varbinds -> [varbind()]
+%% UserData -> term() (supplied when the user register)
+%% Reply -> ignore
+
+%% handle_trap(TargetName, SnmpTrapInfo, UserData) -> Reply
+%% TargetName -> target_name()
+%% SnmpTrapInfo -> {Enteprise, Generic, Spec, Timestamp, Varbinds} |
+%% {ErrorStatus, ErrorIndex, Varbinds}
+%% Enteprise -> oid()
+%% Generic -> integer()
+%% Spec -> integer()
+%% Timestamp -> integer()
+%% ErrorStatus -> atom()
+%% ErrorIndex -> integer()
+%% Varbinds -> [varbind()]
+%% UserData -> term() (supplied when the user register)
+%% Reply -> ignore | unregister | {register, UserId, agent_info()}
+
+%% handle_inform(TargetName, SnmpInform, UserData) -> Reply
+%% TargetName -> target_name()
+%% SnmpInform -> {ErrorStatus, ErrorIndex, Varbinds}
+%% ErrorStatus -> atom()
+%% ErrorIndex -> integer()
+%% Varbinds -> [varbind()]
+%% UserData -> term() (supplied when the user register)
+%% Reply -> ignore | unregister | {register, UserId, agent_info()}
+%%
+
+%% handle_report(TargetName, SnmpReport, UserData) -> Reply
+%% TargetName -> target_name()
+%% SnmpReport -> {ErrorStatus, ErrorIndex, Varbinds}
+%% ErrorStatus -> integer()
+%% ErrorIndex -> integer()
+%% Varbinds -> [varbind()]
+%% UserData -> term() (supplied when the user register)
+%% Reply -> ignore | unregister | {register, UserId, agent_info()}
+
diff --git a/lib/snmp/src/manager/snmpm_user_default.erl b/lib/snmp/src/manager/snmpm_user_default.erl
new file mode 100644
index 0000000000..d90fc3f258
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_user_default.erl
@@ -0,0 +1,87 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_user_default).
+
+-behaviour(snmpm_user).
+
+-export([handle_error/3,
+ handle_agent/5,
+ handle_pdu/4,
+ handle_trap/3,
+ handle_inform/3,
+ handle_report/3]).
+
+handle_error(ReqId, Reason, UserData) ->
+ info("received handle_error:"
+ "~n ReqId: ~p"
+ "~n Reason: ~p"
+ "~n UserData: ~p", [ReqId, Reason, UserData]),
+ ignore.
+
+
+handle_agent(Addr, Port, Type, SnmpInfo, UserData) ->
+ info("received handle_agent:"
+ "~n Addr: ~p"
+ "~n Port: ~p"
+ "~n Type: ~p"
+ "~n SnmpInfo: ~p"
+ "~n UserData: ~p", [Addr, Port, Type, SnmpInfo, UserData]),
+ ignore.
+
+
+handle_pdu(TargetName, ReqId, SnmpResponse, UserData) ->
+ info("received handle_pdu:"
+ "~n TargetName: ~p"
+ "~n ReqId: ~p"
+ "~n SnmpResponse: ~p"
+ "~n UserData: ~p",
+ [TargetName, ReqId, SnmpResponse, UserData]),
+ ignore.
+
+
+handle_trap(TargetName, SnmpTrap, UserData) ->
+ info("received handle_trap:"
+ "~n TargetName: ~p"
+ "~n SnmpTrap: ~p"
+ "~n UserData: ~p",
+ [TargetName, SnmpTrap, UserData]),
+ ok.
+
+
+handle_inform(TargetName, SnmpInform, UserData) ->
+ info("received handle_inform:"
+ "~n TargetName: ~p"
+ "~n SnmpInform: ~p"
+ "~n UserData: ~p",
+ [TargetName, SnmpInform, UserData]),
+ no_reply.
+
+
+handle_report(TargetName, SnmpReport, UserData) ->
+ info("received handle_inform:"
+ "~n TargetName: ~p"
+ "~n SnmpReport: ~p"
+ "~n UserData: ~p",
+ [TargetName, SnmpReport, UserData]),
+ ok.
+
+
+info(F, A) ->
+ error_logger:info_msg("SNMPM default user callback " ++ F ++ "~n", A).
diff --git a/lib/snmp/src/manager/snmpm_user_old.erl b/lib/snmp/src/manager/snmpm_user_old.erl
new file mode 100644
index 0000000000..69b1470790
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_user_old.erl
@@ -0,0 +1,33 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpm_user_old).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [{handle_error, 3},
+ {handle_agent, 4},
+ {handle_pdu, 5},
+ {handle_trap, 4},
+ {handle_inform, 4},
+ {handle_report, 4}];
+behaviour_info(_) ->
+ undefined.
+
diff --git a/lib/snmp/src/manager/snmpm_usm.erl b/lib/snmp/src/manager/snmpm_usm.erl
new file mode 100644
index 0000000000..8cb3062d4b
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_usm.erl
@@ -0,0 +1,512 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%-----------------------------------------------------------------
+%% This module implements the User Based Security Model for SNMP,
+%% as defined in rfc2274.
+%%-----------------------------------------------------------------
+
+-module(snmpm_usm).
+
+-export([init/0,
+ reset/0,
+ process_incoming_msg/4, generate_outgoing_msg/5]).
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+-include("snmpm_usm.hrl").
+-include("SNMP-USER-BASED-SM-MIB.hrl").
+-include("SNMP-USM-AES-MIB.hrl").
+% -include("SNMPv2-TC.hrl").
+
+-define(VMODULE,"M-USM").
+-include("snmp_verbosity.hrl").
+
+
+%%-----------------------------------------------------------------
+
+-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).
+-define(i64(Int), (Int bsr 56) band 255, (Int bsr 48) band 255, (Int bsr 40) band 255, (Int bsr 32) band 255, (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).
+
+
+init() ->
+ init_counters().
+
+
+%%-----------------------------------------------------------------
+%% Func: process_incoming_msg(Packet, Data, SecParams, SecLevel) ->
+%% {ok, {SecEngineID, SecName, ScopedPDUBytes, SecData}} |
+%% {error, Reason} | {error, Reason, ErrorInfo}
+%% Return value may be throwed.
+%% Types: Reason -> term()
+%% Purpose:
+%%-----------------------------------------------------------------
+process_incoming_msg(Packet, Data, SecParams, SecLevel) ->
+ %% 3.2.1
+ ?vtrace("process_incoming_msg -> [3.2.1] check security parms",[]),
+ UsmSecParams =
+ case (catch snmp_pdus:dec_usm_security_parameters(SecParams)) of
+ {'EXIT', Reason} ->
+ inc(snmpInASNParseErrs),
+ error({parseError, Reason}, []);
+ Res ->
+ Res
+ end,
+
+ %% Part of 3.2.2
+ #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID,
+ msgUserName = MsgUserName} = UsmSecParams,
+ ?vlog("process_incoming_msg -> [3.2.2]"
+ "~n authEngineID: ~p"
+ "~n userName: ~p", [MsgAuthEngineID, MsgUserName]),
+
+ %% 3.2.3 (b)
+ ?vtrace("process_incoming_msg -> [3.2.3-b] check engine id",[]),
+ case snmpm_config:is_usm_engine_id_known(MsgAuthEngineID) of
+ true ->
+ ok;
+ false ->
+ SecData1 = [MsgUserName],
+ error(usmStatsUnknownEngineIDs,
+ ?usmStatsUnknownEngineIDs_instance,
+ undefined, [{sec_data, SecData1}])
+ end,
+
+ %% 3.2.4
+ ?vtrace("process_incoming_msg -> [3.2.4] retrieve usm user",[]),
+ SecUser =
+ case snmpm_config:get_usm_user(MsgAuthEngineID, MsgUserName) of
+ {ok, User} ->
+ User;
+ _ -> % undefined user
+ SecData2 = [MsgUserName],
+ error(usmStatsUnknownUserNames,
+ ?usmStatsUnknownUserNames_instance, %% OTP-3542
+ undefined, [{sec_data, SecData2}])
+ end,
+
+ %% 3.2.5 - implicit in following checks
+ %% 3.2.6 - 3.2.7
+ ?vtrace("process_incoming_msg -> "
+ "[3.2.5 - 3.2.7] authenticate incoming",[]),
+ authenticate_incoming(Packet, UsmSecParams, SecUser, SecLevel),
+
+ %% 3.2.8
+ ?vtrace("process_incoming_msg -> [3.2.8] decrypt scoped data",[]),
+ ScopedPDUBytes = decrypt(Data, SecUser, UsmSecParams, SecLevel),
+
+ %% 3.2.9
+ %% Means that if AuthKey/PrivKey are changed; the old values
+ %% will be used.
+ CachedSecData = {MsgUserName,
+ SecUser#usm_user.auth,
+ SecUser#usm_user.auth_key,
+ SecUser#usm_user.priv,
+ SecUser#usm_user.priv_key},
+ SecName = SecUser#usm_user.sec_name,
+ {ok, {MsgAuthEngineID, SecName, ScopedPDUBytes, CachedSecData}}.
+
+
+authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel) ->
+ %% 3.2.6
+ ?vtrace("authenticate incoming: 3.2.6",[]),
+ #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID,
+ msgAuthoritativeEngineBoots = MsgAuthEngineBoots,
+ msgAuthoritativeEngineTime = MsgAuthEngineTime,
+ msgAuthenticationParameters = MsgAuthParams} =
+ UsmSecParams,
+ case snmp_misc:is_auth(SecLevel) of
+ true ->
+ SecName = UsmUser#usm_user.sec_name,
+ case is_auth(UsmUser#usm_user.auth,
+ UsmUser#usm_user.auth_key,
+ MsgAuthParams,
+ Packet,
+ SecName,
+ MsgAuthEngineID,
+ MsgAuthEngineBoots,
+ MsgAuthEngineTime) of
+ true ->
+ ok;
+ false ->
+ error(usmStatsWrongDigests,
+ ?usmStatsWrongDigests_instance, SecName)
+ end;
+ false -> % noAuth
+ ok
+ end.
+
+
+
+is_auth(usmNoAuthProtocol, _, _, _, SecName, _, _, _) -> % 3.2.5
+ error(usmStatsUnsupportedSecLevels,
+ ?usmStatsUnsupportedSecLevels_instance, SecName);
+is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName,
+ MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime) ->
+ case auth_in(AuthProtocol, AuthKey, AuthParams, Packet) of
+ true ->
+ %% 3.2.7
+ ?vtrace("retrieve EngineBoots and EngineTime: 3.2.7",[]),
+ SnmpEngineID = get_engine_id(),
+ ?vtrace("SnmpEngineID: ~p",[SnmpEngineID]),
+ case MsgAuthEngineID of
+ SnmpEngineID -> %% 3.2.7a
+ ?vtrace("we are authoritative: 3.2.7a",[]),
+ SnmpEngineBoots = get_engine_boots(),
+ ?vtrace("SnmpEngineBoots: ~p",[SnmpEngineBoots]),
+ SnmpEngineTime = get_engine_time(),
+ ?vtrace("SnmpEngineTime: ~p",[SnmpEngineTime]),
+ InTimeWindow =
+ if
+ SnmpEngineBoots == 2147483647 -> false;
+ MsgAuthEngineBoots /= SnmpEngineBoots -> false;
+ MsgAuthEngineTime + 150 < SnmpEngineTime -> false;
+ MsgAuthEngineTime - 150 > SnmpEngineTime -> false;
+ true -> true
+ end,
+ case InTimeWindow of
+ true ->
+ true;
+ %% OTP-4090 (OTP-3542)
+ false ->
+ error(usmStatsNotInTimeWindows,
+ ?usmStatsNotInTimeWindows_instance,
+ SecName,
+ [{securityLevel, 1}]) % authNoPriv
+ end;
+ _ -> %% 3.2.7b - we're non-authoritative
+ ?vtrace("we are non-authoritative: 3.2.7b",[]),
+ SnmpEngineBoots = get_engine_boots(MsgAuthEngineID),
+ ?vtrace("SnmpEngineBoots: ~p",[SnmpEngineBoots]),
+ SnmpEngineTime = get_engine_time(MsgAuthEngineID),
+ ?vtrace("SnmpEngineTime: ~p",[SnmpEngineTime]),
+ LatestRecvTime = get_engine_latest_time(MsgAuthEngineID),
+ ?vtrace("LatestRecvTime: ~p",[LatestRecvTime]),
+ UpdateLCD =
+ if
+ MsgAuthEngineBoots > SnmpEngineBoots -> true;
+ MsgAuthEngineBoots == SnmpEngineBoots,
+ MsgAuthEngineTime > LatestRecvTime -> true;
+ true -> false
+ end,
+ case UpdateLCD of
+ true -> %% 3.2.7b1
+ ?vtrace("update msgAuthoritativeEngineID: 3.2.7b1",
+ []),
+ set_engine_boots(MsgAuthEngineID,
+ MsgAuthEngineBoots),
+ set_engine_time(MsgAuthEngineID,
+ MsgAuthEngineTime),
+ set_engine_latest_time(MsgAuthEngineID,
+ MsgAuthEngineTime);
+ false ->
+ ok
+ end,
+ %% 3.2.7.b2
+ ?vtrace("check if message is outside time window: 3.2.7b2",
+ []),
+ InTimeWindow =
+ if
+ SnmpEngineBoots == 2147483647 ->
+ {false, [{engine, SnmpEngineID},
+ {boots, at_max}]};
+ MsgAuthEngineBoots < SnmpEngineBoots ->
+ {false, [{engine, MsgAuthEngineID},
+ {boots, MsgAuthEngineBoots}]};
+ MsgAuthEngineBoots == SnmpEngineBoots,
+ MsgAuthEngineTime < (SnmpEngineTime - 150) ->
+ {false, [{engine, MsgAuthEngineID},
+ {time, MsgAuthEngineTime}]};
+ true -> true
+ end,
+ case InTimeWindow of
+ {false, Reason} ->
+ ?vinfo("not in time window[3.2.7b2]: ~p",
+ [Reason]),
+ error(notInTimeWindow, Reason);
+ true ->
+ ok
+ end,
+ true
+ end;
+ false ->
+ false
+ end.
+
+
+decrypt(Data, UsmUser, UsmSecParams, SecLevel) ->
+ case snmp_misc:is_priv(SecLevel) of
+ true ->
+ do_decrypt(Data, UsmUser, UsmSecParams);
+ false ->
+ Data
+ end.
+
+do_decrypt(Data, #usm_user{sec_name = SecName,
+ priv = PrivP,
+ priv_key = PrivKey},
+ #usmSecurityParameters{msgPrivacyParameters = PrivParms}) ->
+ EncryptedPDU = snmp_pdus:dec_scoped_pdu_data(Data),
+ try_decrypt(PrivP, PrivKey, PrivParms, EncryptedPDU, SecName).
+
+try_decrypt(usmNoPrivProtocol, _, _, _, SecName) -> % 3.2.5
+ error(usmStatsUnsupportedSecLevels,
+ ?usmStatsUnsupportedSecLevels_instance, SecName);
+try_decrypt(usmDESPrivProtocol,
+ PrivKey, MsgPrivParams, EncryptedPDU, SecName) ->
+ case (catch des_decrypt(PrivKey, MsgPrivParams, EncryptedPDU)) of
+ {ok, DecryptedData} ->
+ DecryptedData;
+ _ ->
+ error(usmStatsDecryptionErrors,
+ ?usmStatsDecryptionErrors, SecName)
+ end;
+try_decrypt(usmAesCfb128Protocol,
+ PrivKey, UsmSecParams, EncryptedPDU, SecName) ->
+ case (catch aes_decrypt(PrivKey, UsmSecParams, EncryptedPDU)) of
+ {ok, DecryptedData} ->
+ DecryptedData;
+ _ ->
+ error(usmStatsDecryptionErrors,
+ ?usmStatsDecryptionErrors, SecName)
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Func: process_outgoing_msg(Message, SecEngineID, SecName,
+%% SecData, SecLevel) ->
+%% {ok, {SecEngineID, SecName, ScopedPDUBytes, SecData}} |
+%% {error, Reason} | {error, Reason, ErrorInfo}
+%% Return value may be throwed.
+%% Types: Reason -> term()
+%% Purpose:
+%%-----------------------------------------------------------------
+generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
+ %% 3.1.1
+ ?vtrace("generate_outgoing_msg -> entry (3.1.1)",[]),
+ {UserName, AuthProtocol, AuthKey, PrivProtocol, PrivKey} =
+ case SecData of
+ [] -> % 3.1.1b
+ %% Not a response - read from LCD
+ case snmpm_config:get_usm_user_from_sec_name(SecEngineID,
+ SecName) of
+ {ok, User} ->
+ {User#usm_user.name,
+ User#usm_user.auth,
+ User#usm_user.auth_key,
+ User#usm_user.priv,
+ User#usm_user.priv_key};
+ _ ->
+ error(unknownSecurityName)
+ end;
+ [MsgUserName] ->
+ %% This means the user at the engine is unknown
+ {MsgUserName, usmNoAuthProtocol, "", usmNoPrivProtocol, ""};
+ _ -> % 3.1.1a
+ SecData
+ end,
+ %% 3.1.4
+ ?vtrace("generate_outgoing_msg -> (3.1.4)",[]),
+ ScopedPduBytes = Message#message.data,
+ {ScopedPduData, MsgPrivParams} =
+ encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel),
+ SnmpEngineID = get_engine_id(),
+ ?vtrace("SnmpEngineID: ~p (3.1.6)",[SnmpEngineID]),
+ %% 3.1.6
+ {MsgAuthEngineBoots, MsgAuthEngineTime} =
+ case snmp_misc:is_auth(SecLevel) of
+ false when SecData == [] -> % not a response
+ {0, 0};
+ true when SecEngineID /= SnmpEngineID ->
+ {get_engine_boots(SecEngineID), get_engine_time(SecEngineID)};
+ _ ->
+ {get_engine_boots(), get_engine_time()}
+ end,
+ %% 3.1.5 - 3.1.7
+ ?vtrace("generate_outgoing_msg -> (3.1.5 - 3.1.7)",[]),
+ UsmSecParams =
+ #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID,
+ msgAuthoritativeEngineBoots = MsgAuthEngineBoots,
+ msgAuthoritativeEngineTime = MsgAuthEngineTime,
+ msgUserName = UserName,
+ msgPrivacyParameters = MsgPrivParams},
+ Message2 = Message#message{data = ScopedPduData},
+ %% 3.1.8
+ ?vtrace("generate_outgoing_msg -> (3.1.8)",[]),
+ authenticate_outgoing(Message2, UsmSecParams,
+ AuthKey, AuthProtocol, SecLevel).
+
+
+%% Ret: {ScopedPDU, MsgPrivParams} - both are already encoded as OCTET STRINGs
+encrypt(Data, PrivProtocol, PrivKey, SecLevel) ->
+ case snmp_misc:is_priv(SecLevel) of
+ false -> % 3.1.4b
+ {Data, []};
+ true -> % 3.1.4a
+ case (catch try_encrypt(PrivProtocol, PrivKey, Data)) of
+ {ok, ScopedPduData, MsgPrivParams} ->
+ {snmp_pdus:enc_oct_str_tag(ScopedPduData), MsgPrivParams};
+ {error, Reason} ->
+ error(Reason);
+ _ ->
+ error(encryptionError)
+ end
+ end.
+
+try_encrypt(usmNoPrivProtocol, _PrivKey, _Data) -> % 3.1.2
+ error(unsupportedSecurityLevel);
+try_encrypt(usmDESPrivProtocol, PrivKey, Data) ->
+ des_encrypt(PrivKey, Data);
+try_encrypt(usmAesCfb128Protocol, PrivKey, Data) ->
+ aes_encrypt(PrivKey, Data).
+
+authenticate_outgoing(Message, UsmSecParams,
+ AuthKey, AuthProtocol, SecLevel) ->
+ Message2 =
+ case snmp_misc:is_auth(SecLevel) of
+ true ->
+ auth_out(AuthProtocol, AuthKey, Message, UsmSecParams);
+ false ->
+ set_msg_auth_params(Message, UsmSecParams)
+ end,
+ snmp_pdus:enc_message_only(Message2).
+
+
+
+%%-----------------------------------------------------------------
+%% Auth and priv algorithms
+%%-----------------------------------------------------------------
+auth_in(AuthProtocol, AuthKey, AuthParams, Packet) ->
+ snmp_usm:auth_in(AuthProtocol, AuthKey, AuthParams, Packet).
+
+auth_out(AuthProtocol, AuthKey, Message, UsmSecParams) ->
+ snmp_usm:auth_out(AuthProtocol, AuthKey, Message, UsmSecParams).
+
+set_msg_auth_params(Message, UsmSecParams) ->
+ snmp_usm:set_msg_auth_params(Message, UsmSecParams, []).
+
+des_encrypt(PrivKey, Data) ->
+ snmp_usm:des_encrypt(PrivKey, Data, fun get_des_salt/0).
+
+des_decrypt(PrivKey, MsgPrivParams, EncData) ->
+ snmp_usm:des_decrypt(PrivKey, MsgPrivParams, EncData).
+
+get_des_salt() ->
+ SaltInt = snmpm_config:incr_counter(usm_des_salt, 1),
+ EngineBoots = get_engine_boots(),
+ [?i32(EngineBoots), ?i32(SaltInt)].
+
+aes_encrypt(PrivKey, Data) ->
+ snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0).
+
+aes_decrypt(PrivKey, UsmSecParams, EncData) ->
+ #usmSecurityParameters{msgPrivacyParameters = MsgPrivParams,
+ msgAuthoritativeEngineTime = EngineTime,
+ msgAuthoritativeEngineBoots = EngineBoots} =
+ UsmSecParams,
+ snmp_usm:aes_decrypt(PrivKey, MsgPrivParams, EncData,
+ EngineBoots, EngineTime).
+
+get_aes_salt() ->
+ SaltInt = snmpm_config:incr_counter(usm_aes_salt, 1),
+ [?i64(SaltInt)].
+
+%%-----------------------------------------------------------------
+
+get_engine_id() ->
+ {ok, EngineID} = snmpm_config:get_engine_id(),
+ EngineID.
+
+get_engine_boots() ->
+ {ok, Boots} = snmpm_config:get_engine_boots(),
+ Boots.
+
+get_engine_time() ->
+ {ok, Diff} = snmpm_config:get_engine_time(),
+ Diff.
+
+
+%%-----------------------------------------------------------------
+%% We cache the local values of all non-auth engines we know.
+%% See section 2.3 (Time Synchronization) of the RFC.
+%%-----------------------------------------------------------------
+get_engine_boots(SnmpEngineID) ->
+ {ok, Boots} = snmpm_config:get_usm_eboots(SnmpEngineID),
+ Boots.
+
+get_engine_time(SnmpEngineID) ->
+ {ok, Diff} = snmpm_config:get_usm_etime(SnmpEngineID),
+ Diff.
+
+get_engine_latest_time(SnmpEngineID) ->
+ {ok, Time} = snmpm_config:get_usm_eltime(SnmpEngineID),
+ Time.
+
+
+set_engine_boots(SnmpEngineID, EngineBoots) ->
+ snmpm_config:set_usm_eboots(SnmpEngineID, EngineBoots).
+
+set_engine_time(SnmpEngineID, EngineTime) ->
+ Diff = snmp_misc:now(sec) - EngineTime,
+ snmpm_config:set_usm_etime(SnmpEngineID, Diff).
+
+set_engine_latest_time(SnmpEngineID, EngineTime) ->
+ snmpm_config:set_usm_eltime(SnmpEngineID, EngineTime).
+
+
+%%-----------------------------------------------------------------
+%% Utility functions
+%%-----------------------------------------------------------------
+error(Reason) ->
+ throw({error, Reason}).
+
+error(Reason, ErrorInfo) ->
+ throw({error, Reason, ErrorInfo}).
+
+error(Variable, Oid, SecName) ->
+ error(Variable, Oid, SecName, []).
+error(Variable, Oid, SecName, Opts) ->
+ Val = inc(Variable),
+ ErrorInfo = {#varbind{oid = Oid,
+ variabletype = 'Counter32',
+ value = Val},
+ SecName,
+ Opts},
+ throw({error, Variable, ErrorInfo}).
+
+
+%%-----------------------------------------------------------------
+
+init_counters() ->
+ F = fun(Counter) -> snmpm_config:maybe_cre_stats_counter(Counter, 0) end,
+ lists:map(F, counters()).
+
+reset() ->
+ F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end,
+ lists:map(F, counters()).
+
+counters() ->
+ [usmStatsUnsupportedSecLevels,
+ usmStatsNotInTimeWindows,
+ usmStatsUnknownUserNames,
+ usmStatsUnknownEngineIDs,
+ usmStatsWrongDigests,
+ usmStatsDecryptionErrors].
+
+inc(Name) -> snmpm_config:incr_stats_counter(Name, 1).
+
diff --git a/lib/snmp/src/manager/snmpm_usm.hrl b/lib/snmp/src/manager/snmpm_usm.hrl
new file mode 100644
index 0000000000..1939ad6ed9
--- /dev/null
+++ b/lib/snmp/src/manager/snmpm_usm.hrl
@@ -0,0 +1,27 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-record(usm_user, {engine_id,
+ name,
+ sec_name, % Default value is name
+ auth = usmNoAuthProtocol,
+ auth_key = [],
+ priv = usmNoPrivProtocol,
+ priv_key = []}).
+