aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/manager/snmpm_config.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/manager/snmpm_config.erl')
-rw-r--r--lib/snmp/src/manager/snmpm_config.erl3116
1 files changed, 3116 insertions, 0 deletions
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]).
+