%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2012. 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
%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
-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/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,
increment_counter/3, increment_counter/4,
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,
default_transport_domain/0
]).
%% 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).
-define(SUPPORTED_DOMAINS, [transportDomainUdpIpv4, transportDomainUdpIpv6]).
-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
%%%-------------------------------------------------------------------
default_transport_domain() ->
transportDomainUdpIpv4.
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>
%% This is intended for backward compatibility and therefor has
%% only support for IPv4 addresses and *no* other transport domain.
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,
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, _Config0) when (UserId =:= user_id) ->
{error, {bad_user_id, UserId}};
register_agent(UserId, TargetName, Config0)
when (is_list(TargetName) andalso
(length(TargetName) > 0) andalso
is_list(Config0)) ->
?vtrace("register_agent -> entry with"
"~n UserId: ~p"
"~n TargetName: ~p"
"~n Config0: ~p", [UserId, TargetName, Config0]),
%% Check:
%% 1) That the mandatory configs are present
%% 2) That no illegal config, e.g. user_id (used internally),
%% is not present
%% 3) Check that there are no invalid or erroneous configs
%% 4) Check that the manager is capable of using the selected version
case verify_agent_config(Config0) of
{ok, Config} ->
call({register_agent, UserId, TargetName, Config});
Error ->
Error
end.
verify_agent_config(Conf0) ->
try
begin
verify_mandatory(Conf0, [engine_id, address, reg_type]),
verify_invalid(Conf0, [user_id]),
Conf = verify_agent_config3(Conf0),
Vsns = versions(),
Vsn = which_version(Conf),
verify_version(Vsn, Vsns),
{ok, Conf}
end
catch
throw:Error ->
Error
end.
versions() ->
case system_info(versions) of
{ok, Vsns} ->
Vsns;
{error, _} = ERROR ->
throw(ERROR)
end.
which_version(Conf) ->
case lists:keysearch(version, 1, Conf) of
{value, {version, V}} ->
V;
false ->
v1
end.
verify_version(Vsn, Vsns) ->
case lists:member(Vsn, Vsns) of
true ->
ok;
false ->
Reason = {version_not_supported_by_manager, Vsn, Vsns},
throw({error, Reason})
end.
verify_agent_config3(Conf0) ->
%% Fix (transport) address and domain
{TDomain, Conf1} =
case lists:keysearch(tdomain, 1, Conf0) of
{value, {tdomain, Dom}} ->
{Dom, Conf0};
false ->
Dom = default_transport_domain(),
{Dom, [{tdomain, Dom} | Conf0]}
end,
Conf2 = case lists:keysearch(address, 1, Conf1) of
{value, {address, Address}} ->
lists:keyreplace(address, 1, Conf1,
{address, {TDomain, Address}});
false ->
%% This is a mandatory config option,
%% a later test will detect this
Conf1
end,
case verify_agent2(Conf2) of
{ok, Conf} ->
Conf;
{error, _} = ERROR ->
throw(ERROR)
end.
verify_agent_config2(Conf) ->
verify_agent2(Conf).
unregister_agent(UserId, TargetName) ->
call({unregister_agent, UserId, TargetName}).
%% This is the old style agent unregistration (using Addr and Port).
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].
verify_agent_info(TargetName, Info0) ->
try
begin
verify_invalid(Info0, [user_id]),
%% Check if address is part of the list and
%% if so update it with the domain info.
Info =
case lists:keysearch(address, 1, Info0) of
{value, {address, Addr}} ->
%% If domain is part of the info, then use it.
%% If not, lookup what is already stored for
%% this agent and use that.
Domain =
case lists:keysearch(tdomain, 1, Info0) of
{value, {tdomain, Dom}} ->
Dom;
false ->
{ok, Dom} =
agent_info(TargetName, tdomain),
Dom
end,
Addr2 = {Domain, Addr},
lists:keyreplace(address, 1, Info0, {address, Addr2});
false ->
Info0
end,
verify_agent2(Info)
end
catch
throw:Error ->
Error
end.
update_agent_info(UserId, TargetName, Info) ->
call({update_agent_info, UserId, TargetName, Info}).
%% <BACKWARD-COMPAT-2>
%% This is wrapped in the interface module, so this function is
%% only here to catch code-upgrade problems.
update_agent_info(UserId, TargetName, Item, Val) ->
update_agent_info(UserId, TargetName, [{Item, Val}]).
%% </BACKWARD-COMPAT-2>
%% <BACKWARD-COMPAT-1>
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.
%% </BACKWARD-COMPAT-1>
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) ->
try
begin
verify_mandatory(Config, []),
verify_invalid(Config, [engine_id, name]),
verify_usm_user_config2(EngineID, Name, Config)
end
catch
throw: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.
%% <ATL Sequence Number>
increment_counter(Counter, Initial, Max) ->
Increment = 1,
increment_counter(Counter, Initial, Increment, Max).
increment_counter(Counter, Initial, Increment, Max) ->
%% This is to make sure no one else increments our counter
Key = {Counter, self()},
%% Counter data
Position = 2,
Threshold = Max,
SetValue = Initial,
UpdateOp = {Position, Increment, Threshold, SetValue},
%% And now for the actual increment
Tab = snmpm_counter_table,
case (catch ets:update_counter(Tab, Key, UpdateOp)) of
{'EXIT', {badarg, _}} ->
%% Oups, first time
ets:insert(Tab, {Key, Initial}),
Initial;
Next when is_integer(Next) ->
Next
end.
%% </ATL Sequence Number>
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),
LogSeqNo = get_atl_seqno(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}),
ets:insert(snmpm_config_table, {audit_trail_log_seqno, LogSeqNo})
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([{seqno, SeqNo}|Opts]) ->
verify_log_seqno(SeqNo),
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_log_seqno(true) -> ok;
verify_log_seqno(false) -> ok;
verify_log_seqno(SeqNo) ->
error({invalid_audit_trail_log_seqno, SeqNo}).
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.
%% For backward compatibility
check_agent_config({UserId,
TargetName,
Community,
Ip, Port,
EngineId,
Timeout, MaxMessageSize,
Version, SecModel, SecName, SecLevel}) ->
TDomain = default_transport_domain(),
check_agent_config({UserId,
TargetName,
Community,
TDomain, Ip, Port,
EngineId,
Timeout, MaxMessageSize,
Version, SecModel, SecName, SecLevel});
check_agent_config({UserId,
TargetName,
Community,
TDomain, 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 TDomain: ~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,
TDomain, Ip, Port,
EngineId, Timeout, MaxMessageSize,
Version, SecModel, SecName, SecLevel]),
Addr = normalize_address(TDomain, Ip),
?vtrace("check_agent_config -> Addr: ~p", [Addr]),
Agent = {UserId,
TargetName,
Community,
TDomain, 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.
%% For backward compatibility
verify_agent({UserId,
TargetName,
Comm,
Ip, Port,
EngineId,
Timeout, MMS,
Version, SecModel, SecName, SecLevel}) ->
TDomain = default_transport_domain(),
verify_agent({UserId,
TargetName,
Comm,
TDomain, Ip, Port,
EngineId,
Timeout, MMS,
Version, SecModel, SecName, SecLevel});
verify_agent({UserId,
TargetName,
Comm,
TDomain, Ip, Port,
EngineId,
Timeout, MMS,
Version, SecModel, SecName, SecLevel}) ->
?vdebug("verify_agent -> entry with"
"~n UserId: ~p"
"~n TargetName: ~p", [UserId, TargetName]),
snmp_conf:check_string(TargetName, {gt, 0}),
snmp_conf:check_integer(Port, {gt, 0}),
%% Note that the order of Conf *is* important.
%% Some properties may depend on others, so that
%% in order to verify one property, another must
%% be already verified (and present). An example
%% of this is the property 'address', for which
%% the property tdomain is needed.
Conf0 =
[{reg_type, target_name},
{tdomain, TDomain},
%% This should be taddress, but what the*...
{address, {TDomain, Ip}},
{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(Conf0) of
{ok, Conf} ->
{UserId, TargetName, Conf, Version};
Err ->
throw(Err)
end.
verify_agent2(Conf) ->
verify_agent2(Conf, []).
verify_agent2([], VerifiedConf) ->
{ok, VerifiedConf};
verify_agent2([{Item, Val0}|Items], VerifiedConf) ->
case verify_val(Item, Val0) of
{ok, Val} ->
verify_agent2(Items, [{Item, Val} | VerifiedConf]);
Err ->
Err
end;
verify_agent2([Bad|_], _VerifiedConf) ->
{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}) ->
?vtrace("check_user_config -> entry with"
"~n Id: ~p"
"~n Mod: ~p"
"~n Data: ~p", [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) ->
?vtrace("check_user_config -> entry with"
"~n Id: ~p"
"~n Mod: ~p"
"~n Data: ~p"
"~n DefaultAgentConfig: ~p",
[Id, Mod, Data, DefaultAgentConfig]),
case (catch verify_user_behaviour(Mod)) of
ok ->
?vtrace("check_user_config -> user behaviour verified", []),
case verify_user_agent_config(DefaultAgentConfig) of
{ok, DefAgentConf} ->
?vtrace("check_user_config -> "
"user agent (default) config verified", []),
User2 = {Id, Mod, Data, DefAgentConf},
{ok, User2};
{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, DefAgentConf} ->
Config = default_agent_config(DefAgentConf),
{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) ->
try
begin
verify_invalid(Conf, [user_id, engine_id, address]),
verify_agent_config2(Conf)
end
catch
throw:Error ->
?vdebug("verify_user_agent_config -> throw"
"~n Error: ~p", [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, Info},
_From, State) ->
?vlog("received update_agent_info request: "
"~n UserId: ~p"
"~n TargetName: ~p"
"~n Info: ~p", [UserId, TargetName, Info]),
Reply = handle_update_agent_info(UserId, TargetName, Info),
{reply, Reply, State};
%% <BACKWARD-COMPAT>
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};
%% </BACKWARD-COMPAT>
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, _} ->
?vtrace("handle_register_agent -> user_id not found in config", []),
case ets:lookup(snmpm_user_table, UserId) of
[#user{default_agent_config = DefConfig}] ->
?vtrace("handle_register_agent -> "
"~n DefConfig: ~p", [DefConfig]),
%% First, insert this users default config
?vtrace("handle_register_agent -> store default config", []),
do_handle_register_agent(TargetName, DefConfig),
%% Second, insert the config for this agent
?vtrace("handle_register_agent -> store config", []),
do_handle_register_agent(TargetName,
[{user_id, UserId}|Config]),
%% <DIRTY-BACKWARD-COMPATIBILLITY>
%% And now for some (backward compatibillity)
%% dirty crossref stuff
?vtrace("handle_register_agent -> lookup address", []),
{ok, Addr} = agent_info(TargetName, address),
?vtrace("handle_register_agent -> Addr: ~p, lookup Port",
[Addr]),
{ok, Port} = agent_info(TargetName, port),
?vtrace("handle_register_agent -> register cross-ref fix", []),
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]) ->
?vtrace("handle_register_agent -> entry with"
"~n TargetName: ~p"
"~n Item: ~p"
"~n Val: ~p"
"~n Rest: ~p", [TargetName, Item, Val, Rest]),
case (catch do_update_agent_info(TargetName, Item, Val)) of
ok ->
do_handle_register_agent(TargetName, Rest);
{error, Reason} ->
?vtrace("handle_register_agent -> failed updating ~p"
"~n Item: ~p"
"~n Reason: ~p", [Item, 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, Info) ->
?vdebug("handle_update_agent_info -> entry with"
"~n UserId: ~p"
"~n TargetName: ~p"
"~n Info: ~p", [UserId, TargetName, Info]),
%% Verify ownership
case (catch agent_info(TargetName, user_id)) of
{ok, UserId} ->
handle_update_agent_info(TargetName, Info);
{ok, OtherUserId} ->
{error, {not_owner, OtherUserId}};
Error ->
Error
end.
handle_update_agent_info(TargetName, Info0) ->
?vtrace("handle_update_agent_info -> entry with"
"~n TargetName: ~p"
"~n Info0: ~p", [TargetName, Info0]),
%% Verify info
try verify_agent_info(TargetName, Info0) of
{ok, Info} ->
do_update_agent_info(TargetName, Info);
Error ->
Error
catch
throw:Error ->
Error;
T:E ->
{error, {failed_info_verification, Info0, T, E}}
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]),
handle_update_agent_info(TargetName, [{Item, Val}]).
do_update_agent_info(TargetName, Info) ->
InsertItem =
fun({Item, Val}) ->
ets:insert(snmpm_agent_table, {{TargetName, Item}, Val})
end,
lists:foreach(InsertItem, Info).
do_update_agent_info(TargetName, Item, Val) ->
?vtrace("do_update_agent_info -> entry with"
"~n TargetName: ~p"
"~n Item: ~p"
"~n Val: ~p", [TargetName, Item, Val]),
ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}),
ok.
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 ->
throw({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 ->
throw({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(tdomain = Item, snmpUDPDomain = _Domain) ->
verify_val(Item, transportDomainUdpIpv4);
verify_val(tdomain, Domain) ->
case lists:member(Domain, ?SUPPORTED_DOMAINS) of
true ->
{ok, Domain};
false ->
case lists:member(Domain, snmp_conf:all_domains()) of
true ->
error({unsupported_domain, Domain});
false ->
error({unknown_domain, Domain})
end
end;
verify_val(address, {Domain, Addr0}) ->
case normalize_address(Domain, Addr0) of
{_A1, _A2, _A3, _A4} = Addr ->
{ok, Addr};
{_A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8} = 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(address, BadAddress) ->
error({bad_address, BadAddress});
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, {unknown_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) ->
normalize_address(snmpUDPDomain, Addr).
normalize_address(snmpUDPDomain, Addr) ->
normalize_address(transportDomainUdpIpv4, Addr);
normalize_address(Domain, Addr) ->
case inet:getaddr(Addr, td2fam(Domain)) of
{ok, Addr2} ->
Addr2;
_ when is_list(Addr) ->
case (catch snmp_conf:check_ip(Domain, Addr)) of
ok ->
list_to_tuple(Addr);
_ ->
Addr
end;
_ ->
Addr
end.
td2fam(transportDomainUdpIpv4) -> inet;
td2fam(transportDomainUdpIpv6) -> inet6.
%%----------------------------------------------------------------------
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_atl_seqno(Opts) ->
get_opt(seqno, Opts, false).
%%----------------------------------------------------------------------
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]).