PageRenderTime 18ms CodeModel.GetById 4ms app.highlight 211ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/snmp/src/manager/snmpm_config.erl

https://github.com/bsmr-erlang/otp
Erlang | 3436 lines | 2667 code | 443 blank | 326 comment | 12 complexity | fc2c18c5dd9e4923635fb9af07858fcd MD5 | raw file
   1%% 
   2%% %CopyrightBegin%
   3%%
   4%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
   5%%
   6%% Licensed under the Apache License, Version 2.0 (the "License");
   7%% you may not use this file except in compliance with the License.
   8%% You may obtain a copy of the License at
   9%%
  10%%     http://www.apache.org/licenses/LICENSE-2.0
  11%%
  12%% Unless required by applicable law or agreed to in writing, software
  13%% distributed under the License is distributed on an "AS IS" BASIS,
  14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15%% See the License for the specific language governing permissions and
  16%% limitations under the License.
  17%%
  18%% %CopyrightEnd%
  19%% 
  20%% -------------------------------------------------------------------------
  21%%
  22%% Some of the stuff stored here should really be persistent!!
  23%% (e.g. snmp-engine-boot)
  24%%
  25%% -------------------------------------------------------------------------
  26
  27-module(snmpm_config).
  28
  29-behaviour(gen_server).
  30
  31%% External exports
  32%% Avoid warning for local function error/1 clashing with autoimported BIF.
  33-compile({no_auto_import,[error/1]}).
  34-export([start_link/1, stop/0, is_started/0]).
  35-export([register_user/4, unregister_user/1, 
  36	 which_users/0, 
  37	 user_info/0, user_info/1, user_info/2, 
  38
  39	 register_agent/3, unregister_agent/2, 
  40	 agent_info/0, agent_info/2, agent_info/3, 
  41	 update_agent_info/3, update_agent_info/4, 
  42	 which_agents/0, which_agents/1, 
  43
  44	 is_known_engine_id/2, 
  45	 get_agent_engine_id/1, 
  46	 get_agent_engine_max_message_size/1, 
  47	 get_agent_version/1, 
  48	 get_agent_mp_model/1, 
  49	 get_agent_user_id/1, get_agent_user_id/2, 
  50	 get_agent_user_info/2, 
  51	 
  52	 system_info/0, system_info/1, 
  53	 %% update_system_info/2, 
  54	 get_engine_id/0, get_engine_max_message_size/0,
  55
  56	 register_usm_user/3, unregister_usm_user/2, 
  57	 which_usm_users/0, which_usm_users/1, 
  58	 usm_user_info/3, update_usm_user_info/4, 
  59	 get_usm_user/2, get_usm_user_from_sec_name/2, 
  60	 is_usm_engine_id_known/1,
  61	 get_engine_boots/0, get_engine_time/0, 
  62	 set_engine_boots/1, set_engine_time/1, 
  63	 get_usm_eboots/1, get_usm_etime/1, get_usm_eltime/1, 
  64	 set_usm_eboots/2, set_usm_etime/2, set_usm_eltime/2, 
  65	 reset_usm_cache/1, 
  66	 
  67
  68	 cre_counter/2,
  69	 incr_counter/2,
  70	 increment_counter/3, increment_counter/4, 
  71
  72	 cre_stats_counter/2,
  73	 maybe_cre_stats_counter/2,
  74	 incr_stats_counter/2,
  75	 reset_stats_counter/1,
  76	 get_stats_counters/0, get_stats_counter/1,
  77
  78	 load_mib/1, unload_mib/1, which_mibs/0, 
  79	 make_mini_mib/0,
  80	 name_to_oid/1, oid_to_name/1, oid_to_type/1, 
  81
  82	 system_start_time/0,
  83
  84	 info/0, 
  85	 verbosity/1,
  86
  87	 backup/1,
  88
  89	 mk_target_name/3,
  90	 
  91	 default_transport_domain/0
  92
  93	]).
  94
  95%% Backward compatibillity exports
  96-export([
  97	 register_user/3,
  98	 unregister_agent/3, 
  99	 update_agent_info/5,
 100	 is_known_engine_id/3, 
 101	 get_agent_engine_id/2, 
 102	 get_agent_engine_max_message_size/2, 
 103	 get_agent_version/2, 
 104	 get_agent_mp_model/2
 105	]).
 106
 107-export([
 108	 order_manager_config/2,
 109	 check_manager_config/2,
 110	 check_user_config/1,
 111	 check_agent_config/1,
 112	 check_usm_user_config/1]).
 113
 114
 115%% gen_server callbacks
 116-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 
 117	 code_change/3, terminate/2]).
 118
 119
 120%% Includes:
 121-include_lib("kernel/include/file.hrl").
 122-include("snmp_types.hrl").
 123-include("snmpm_internal.hrl").
 124-include("snmpm_usm.hrl").
 125-include("snmp_debug.hrl").
 126-include("snmp_verbosity.hrl").
 127
 128
 129%% Types:
 130-record(user, {id, mod, data, default_agent_config}).
 131
 132-record(state, {backup}).
 133
 134
 135%% Macros and Constants:
 136-define(SERVER,             ?MODULE).
 137-define(BACKUP_DB,          snmpm_config_backup).
 138-define(CONFIG_DB,          snmpm_config_db).
 139
 140-define(DEFAULT_USER,       default_user).
 141
 142-define(DEFAULT_AGENT_PORT, 161).
 143
 144-define(IRB_DEFAULT,        auto).
 145%% -define(IRB_DEFAULT,        {user, timer:seconds(15)}).
 146
 147-define(USER_MOD_DEFAULT,   snmpm_user_default).
 148-define(USER_DATA_DEFAULT,  undefined).
 149
 150%% -define(DEF_ADDR_TAG, default_addr_tag).
 151-define(DEFAULT_TARGETNAME, default_agent).
 152-define(DEF_PORT_TAG,       default_port_tag).
 153-define(SUPPORTED_DOMAINS,  [transportDomainUdpIpv4, transportDomainUdpIpv6]).
 154
 155-ifdef(snmp_debug).
 156-define(GS_START_LINK(Opts),
 157	gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts], 
 158			      [{debug,[trace]}])).
 159-else.
 160-define(GS_START_LINK(Opts),
 161	gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts], [])).
 162-endif.
 163
 164
 165
 166%%%-------------------------------------------------------------------
 167%%% API
 168%%%-------------------------------------------------------------------
 169
 170default_transport_domain() ->
 171    snmpUDPDomain.
 172
 173
 174start_link(Opts) -> 
 175    ?d("start_link -> entry with"
 176       "~n   Opts: ~p", [Opts]),
 177    ?GS_START_LINK(Opts).
 178
 179stop() ->
 180    call(stop).
 181
 182is_started() ->
 183    call(is_started, 1000).
 184
 185backup(BackupDir) when is_list(BackupDir) ->
 186    call({backup, BackupDir}).
 187
 188%% Backward compatibillity
 189register_user(UserId, UserMod, UserData) ->
 190    register_user(UserId, UserMod, UserData, []).
 191
 192register_user(UserId, UserMod, UserData, DefaultAgentConfig) 
 193  when (UserId =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
 194    case (catch verify_user_behaviour(UserMod)) of
 195	ok ->
 196	    {ok, SystemDefaultAgentConfig} = agent_info(),
 197	    Config =
 198		ensure_config(SystemDefaultAgentConfig,
 199			      DefaultAgentConfig),
 200%%	    Config = default_agent_config(DefaultAgentConfig),
 201	    call({register_user, UserId, UserMod, UserData, Config});
 202	Error ->
 203	    Error
 204    end;
 205register_user(UserId, _UserMod, _UserData, DefaultAgentConfig) 
 206  when (UserId =/= ?DEFAULT_USER) ->
 207    {error, {bad_default_agent_config, DefaultAgentConfig}};
 208register_user(UserId, _, _, _) ->
 209    {error, {bad_user_id, UserId}}.
 210
 211%% default_agent_config(DefaultAgentConfig) ->
 212%%     {ok, SystemDefaultAgentConfig} = agent_info(),
 213%%     default_agent_config(SystemDefaultAgentConfig, DefaultAgentConfig).
 214
 215%% default_agent_config([], DefaultAgentConfig) ->
 216%%     DefaultAgentConfig;
 217%% default_agent_config([{Key, _} = Entry|T], DefaultAgentConfig) ->
 218%%     case lists:keymember(Key, 1, DefaultAgentConfig) of
 219%% 	true ->
 220%% 	    default_agent_config(T, DefaultAgentConfig);
 221%% 	false ->
 222%% 	    default_agent_config(T, [Entry|DefaultAgentConfig])
 223%%     end.
 224
 225
 226verify_user_behaviour(UserMod) ->
 227    case snmp_misc:verify_behaviour(snmpm_user, UserMod) of
 228	ok ->
 229	    ok;
 230	Error ->
 231	    %% This user may implement the old behaviour, check it
 232	    case snmp_misc:verify_behaviour(snmpm_user_old, UserMod) of
 233		ok ->
 234		    ok;
 235		_ ->
 236		    throw(Error)
 237	    end
 238    end.
 239
 240
 241unregister_user(UserId) when UserId =/= ?DEFAULT_USER ->
 242    call({unregister_user, UserId});
 243unregister_user(BadUserId) ->
 244    {error, {bad_user_id, BadUserId}}.
 245
 246
 247which_users() ->
 248    Pattern = #user{id = '$1', _ = '_'},
 249    Match   = ets:match(snmpm_user_table, Pattern),
 250    [UserId || [UserId] <- Match, UserId =/= ?DEFAULT_USER].
 251
 252
 253user_info() ->
 254    UserId = ?DEFAULT_USER,
 255    case user_info(UserId) of
 256	{ok, Mod, Data} ->
 257	    {ok, UserId, Mod, Data};
 258	Error ->
 259	    Error
 260    end.
 261
 262user_info(UserId) ->
 263    case ets:lookup(snmpm_user_table, UserId) of
 264	[#user{mod = UserMod, data = UserData}] ->
 265	    {ok, UserMod, UserData};
 266	_ ->
 267	    {error, not_found}
 268    end.
 269
 270user_info(UserId, Item) ->
 271    case (catch do_user_info(UserId, Item)) of
 272	{'EXIT', _} ->
 273	    {error, {not_found, Item}};
 274	Val ->
 275	    {ok, Val}
 276    end.
 277
 278do_user_info(UserId, module) ->
 279    ets:lookup_element(snmpm_user_table, UserId, #user.mod);
 280do_user_info(UserId, data) ->
 281    ets:lookup_element(snmpm_user_table, UserId, #user.data);
 282do_user_info(UserId, default_agent_config) ->
 283    ets:lookup_element(snmpm_user_table, UserId, #user.default_agent_config);
 284do_user_info(_UserId, BadItem) ->
 285    error({not_found, BadItem}).
 286
 287
 288%% A target-name constructed in this way is a string with the following: 
 289%% <IP-address>:<Port>-<Version>
 290%% This is intended for backward compatibility and therefore has
 291%% only support for IPv4 addresses and *no* other transport domain.
 292mk_target_name(Domain, Addr, Config)
 293  when is_atom(Domain), is_list(Config) ->
 294    Version = 
 295	case lists:keysearch(version, 1, Config) of
 296	    {value, {_, V}} ->
 297		V;
 298	    false ->
 299		select_lowest_supported_version()
 300	end,
 301    try
 302	lists:flatten(
 303	  io_lib:format(
 304	    "~s-~w", [snmp_conf:mk_addr_string({Domain, Addr}), Version]))
 305    catch
 306	_ ->
 307	    lists:flatten(
 308	      io_lib:format("~p-~w", [Addr, Version]))
 309    end;
 310mk_target_name(Ip, Port, Config)
 311  when is_integer(Port), is_list(Config) ->
 312    Domain = default_transport_domain(),
 313    try fix_address(Domain, {Ip, Port}) of
 314	Address ->
 315	    mk_target_name(Domain, Address, Config)
 316    catch
 317	_ ->
 318	    Version =
 319		case lists:keysearch(version, 1, Config) of
 320		    {value, {_, V}} ->
 321			V;
 322		    false ->
 323			select_lowest_supported_version()
 324		end,
 325	    lists:flatten(
 326	      io_lib:format("~p:~w-~w", [Ip, Port, Version]))
 327    end.
 328
 329
 330select_lowest_supported_version() ->
 331    {ok, Versions} = system_info(versions),
 332    select_lowest_supported_version([v1, v2, v3], Versions).
 333
 334select_lowest_supported_version([], Versions) ->
 335    error({bad_versions, Versions});
 336select_lowest_supported_version([H|T], Versions) ->
 337    case lists:member(H, Versions) of
 338	true ->
 339	    H;
 340	false ->
 341	    select_lowest_supported_version(T, Versions)
 342    end.
 343
 344
 345register_agent(UserId, _TargetName, _Config0) when (UserId =:= user_id) ->
 346    {error, {bad_user_id, UserId}};
 347register_agent(UserId, TargetName, Config0) 
 348  when (is_list(TargetName) andalso 
 349	(length(TargetName) > 0) andalso 
 350	is_list(Config0)) ->
 351
 352    ?vtrace("register_agent -> entry with"
 353	    "~n   UserId:     ~p"
 354	    "~n   TargetName: ~p"
 355	    "~n   Config0:    ~p", [UserId, TargetName, Config0]),
 356
 357    %% Check: 
 358    %%   1) That the mandatory configs are present
 359    %%   2) That no illegal config, e.g. user_id (used internally), 
 360    %%      is not present
 361    %%   3) Check that there are no invalid or erroneous configs
 362    %%   4) Check that the manager is capable of using the selected version
 363    try
 364	verify_mandatory(Config0, [engine_id, reg_type]),
 365	verify_someof(Config0, [address, taddress]),
 366	verify_illegal(Config0, [user_id]),
 367	Config = verify_agent_config(Config0),
 368	Vsns = versions(),
 369	Vsn = which_version(Config),
 370	verify_version(Vsn, Vsns),
 371	call({register_agent, UserId, TargetName, Config})
 372    catch
 373	Error ->
 374	    Error
 375    end.
 376
 377versions() ->
 378    case system_info(versions) of
 379	{ok, Vsns} ->
 380	    Vsns;
 381	{error, _} = ERROR ->
 382	    throw(ERROR)
 383    end.
 384
 385which_version(Conf) ->
 386    case lists:keysearch(version, 1, Conf) of
 387	{value, {version, V}} ->
 388	    V;
 389	false ->
 390	    v1
 391    end.
 392
 393verify_version(Vsn, Vsns) ->
 394    case lists:member(Vsn, Vsns) of
 395	true ->
 396	    ok;
 397	false ->
 398	    Reason = {version_not_supported_by_manager, Vsn, Vsns}, 
 399	    error(Reason)
 400    end.
 401
 402
 403
 404unregister_agent(UserId, TargetName) ->
 405    call({unregister_agent, UserId, TargetName}).
 406
 407%% This is the old style agent unregistration (using Addr and Port).
 408unregister_agent(UserId, Domain, Address) when is_atom(Domain) ->
 409    try fix_address(Domain, Address) of
 410	NAddress ->
 411	    do_unregister_agent(UserId, Domain, NAddress)
 412    catch
 413	_ ->
 414	    {error, not_found}
 415    end;
 416unregister_agent(UserId, Ip, Port) when is_integer(Port) ->
 417    Domain = default_transport_domain(),
 418    try fix_address(Domain, {Ip, Port}) of
 419	Address ->
 420	    do_unregister_agent(UserId, Domain, Address)
 421    catch
 422	_ ->
 423	    {error, not_found}
 424    end.
 425
 426do_unregister_agent(UserId, Domain, Address) ->
 427    case do_agent_info(Domain, Address, target_name) of
 428	{ok, TargetName} ->
 429	    unregister_agent(UserId, TargetName);
 430	Error ->
 431	    Error
 432    end.
 433
 434
 435
 436agent_info() ->
 437    agent_info(?DEFAULT_TARGETNAME, all).
 438
 439agent_info(TargetName, all) ->
 440    case ets:match_object(snmpm_agent_table, {{TargetName, '_'}, '_'}) of
 441	[] ->
 442	    {error, not_found};
 443	All ->
 444	    {ok, [{Item, Val} || {{_, Item}, Val} <- All]}
 445    end;
 446%% Begin backwards compatibility
 447agent_info(TargetName, address) ->
 448    case agent_info({TargetName, taddress}) of
 449	{ok, Val} ->
 450	    {Addr, _} = Val,
 451	    {ok, Addr};
 452	_ ->
 453	    %% This should be redundant since 'taddress' should exist
 454	    agent_info({TargetName, address})
 455    end;
 456agent_info(TargetName, port) ->
 457    case agent_info({TargetName, taddress}) of
 458	{ok, Val} ->
 459	    {_, Port} = Val,
 460	    {ok, Port};
 461	_ ->
 462	    %% This should be redundant since 'taddress' should exist
 463	    agent_info({TargetName, port})
 464    end;
 465%% End backwards compatibility
 466agent_info(TargetName, Item) ->
 467    agent_info({TargetName, Item}).
 468
 469agent_info(Key) ->
 470    case ets:lookup(snmpm_agent_table, Key) of
 471	[{_, Val}] ->
 472	    {ok, Val};
 473	[] ->
 474	    {error, not_found}
 475    end.
 476
 477agent_info(Domain, Address, Item) when is_atom(Domain) ->
 478    try fix_address(Domain, Address) of
 479	NAddress ->
 480	    do_agent_info(Domain, NAddress, Item)
 481    catch
 482	_Thrown ->
 483	    %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n"
 484	    %%   "    ~p",
 485	    %%   [Domain, Address, Item, _Thrown, erlang:get_stacktrace()]),
 486	    {error, not_found}
 487    end;
 488agent_info(Ip, Port, Item) when is_integer(Port) ->
 489    %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) entry~n",
 490    %%   [Ip, Port, Item]),
 491    Domain = default_transport_domain(),
 492    try fix_address(Domain, {Ip, Port}) of
 493	Address ->
 494	    do_agent_info(Domain, Address, Item)
 495    catch
 496	_Thrown ->
 497	    %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n"
 498	    %%   "    ~p",
 499	    %%   [Ip, Port, Item, _Thrown, erlang:get_stacktrace()]),
 500	    {error, not_found}
 501    end.
 502
 503do_agent_info(Domain, Address, target_name = Item) ->
 504    %% p(?MODULE_STRING":do_agent_info(~p, ~p, ~p) entry~n",
 505    %%   [Domain, Address, Item]),
 506    case ets:lookup(snmpm_agent_table, {Domain, Address, Item}) of
 507	[{_, Val}] ->
 508	    {ok, Val};
 509	[] ->
 510	    {error, not_found}
 511    end;
 512do_agent_info(Domain, Address, Item) ->
 513    %% p(?MODULE_STRING":do_agent_info(~p, ~p, ~p) entry~n",
 514    %%   [Domain, Address, Item]),
 515    case do_agent_info(Domain, Address, target_name) of
 516	{ok, TargetName} ->
 517	    agent_info(TargetName, Item);
 518	Error ->
 519	    Error
 520    end.
 521
 522
 523ensure_agent_info(_, [], Info) ->
 524    Info;
 525ensure_agent_info(TargetName, [Item|Items], Info) ->
 526    case lists:keymember(Item, 1, Info) of
 527	true ->
 528	    ensure_agent_info(TargetName, Items, Info);
 529	false ->
 530	    {ok, Value} = agent_info(TargetName, Item),
 531	    ensure_agent_info(TargetName, Items, [{Item, Value}|Info])
 532    end.
 533
 534
 535
 536which_agents() ->
 537    which_agents('_').
 538
 539which_agents(UserId) ->
 540    Pat = {{'$1', user_id}, UserId},
 541    Agents = ets:match(snmpm_agent_table, Pat),
 542    [TargetName || [TargetName] <- Agents].
 543
 544
 545
 546update_agent_info(UserId, TargetName, Info) ->
 547    call({update_agent_info, UserId, TargetName, Info}).
 548
 549%% <BACKWARD-COMPAT-2>
 550%% This is wrapped in the interface module, so this function is
 551%% only here to catch code-upgrade problems.
 552update_agent_info(UserId, TargetName, Item, Val) ->
 553    update_agent_info(UserId, TargetName, [{Item, Val}]).
 554%% </BACKWARD-COMPAT-2>
 555
 556%% <BACKWARD-COMPAT-1>
 557update_agent_info(UserId, Addr, Port, Item, Val)  ->
 558    case agent_info(Addr, Port, target_name) of
 559	{ok, TargetName} ->
 560	    update_agent_info(UserId, TargetName, Item, Val);
 561	Error ->
 562	    Error
 563    end.
 564%% </BACKWARD-COMPAT-1>
 565
 566is_known_engine_id(EngineID, TargetName) ->
 567    case agent_info(TargetName, engine_id) of
 568	{ok, EngineID} ->
 569	    true;
 570	{ok, _OtherEngineID} ->
 571	    false;
 572	_ ->
 573	    false
 574    end.
 575    
 576%% Backward compatibillity
 577is_known_engine_id(EngineID, Addr, Port) ->
 578    case agent_info(Addr, Port, target_name) of
 579	{ok, TargetName} ->
 580	    is_known_engine_id(EngineID, TargetName);
 581	_ ->
 582	    false
 583    end.
 584
 585get_agent_engine_id(TargetName) ->
 586    agent_info(TargetName, engine_id).
 587
 588%% Backward compatibillity
 589get_agent_engine_id(Addr, Port) ->
 590    agent_info(Addr, Port, engine_id).
 591
 592get_agent_engine_max_message_size(TargetName) ->
 593    agent_info(TargetName, max_message_size).
 594
 595%% Backward compatibillity
 596get_agent_engine_max_message_size(Addr, Port) ->
 597    agent_info(Addr, Port, max_message_size).
 598
 599get_agent_version(TargetName) ->
 600    agent_info(TargetName, version). 
 601
 602%% Backward compatibillity
 603get_agent_version(Addr, Port) ->
 604    agent_info(Addr, Port, version). 
 605
 606get_agent_mp_model(TargetName) ->
 607    case agent_info(TargetName, version) of
 608	{ok, v2} ->
 609	    {ok, v2c};
 610	{ok, V} ->
 611	    {ok, V};
 612	Err ->
 613	    Err
 614    end.
 615
 616%% Backward compatibillity
 617get_agent_mp_model(Addr, Port) ->
 618    case agent_info(Addr, Port, target_name) of
 619	{ok, TargetName} ->
 620	    get_agent_mp_model(TargetName);
 621	Error ->
 622	    Error
 623    end.
 624
 625get_agent_user_id(TargetName) ->
 626    agent_info(TargetName, user_id).
 627
 628get_agent_user_id(Addr, Port) ->
 629    agent_info(Addr, Port, user_id).
 630
 631get_agent_user_info(Addr, Port) ->
 632    case agent_info(Addr, Port, target_name) of
 633	{ok, Target} ->
 634	    case agent_info(Target, reg_type) of
 635		{ok, RegType} ->
 636		    case agent_info(Target, user_id) of
 637			{ok, UserId} ->
 638			    {ok, UserId, Target, RegType};
 639			{error, not_found} ->
 640			    {error, {user_id_not_found, Target}}
 641		    end;
 642		{error, not_found} ->
 643		    {error, {reg_type_not_found, Target}}
 644	    end;
 645	{error, not_found} ->
 646	    {error, {target_name_not_found, Addr, Port}}
 647    end.
 648	    
 649
 650
 651system_info() ->
 652    system_info(all).
 653
 654system_info(all) ->
 655    lists:sort(ets:tab2list(snmpm_config_table));
 656system_info(Key) when is_atom(Key) ->
 657    case ets:lookup(snmpm_config_table, Key) of
 658	[{_, Val}] ->
 659	    {ok, Val};
 660	_ ->
 661	    {error, not_found}
 662    end.
 663
 664%% update_system_info(Key, Val) ->
 665%%     call({update_system_info, Key, Val}).
 666
 667system_start_time() ->
 668    system_info(system_start_time).
 669
 670get_engine_id() ->
 671    system_info(engine_id).
 672
 673get_engine_max_message_size() ->
 674    system_info(max_message_size).
 675
 676get_engine_boots() ->
 677    case dets:lookup(?CONFIG_DB, snmp_engine_boots) of
 678	[{_, Boots}] ->
 679	    {ok, Boots};
 680	_ ->
 681	    {error, not_found}
 682    end.
 683
 684set_engine_boots(Boots) ->
 685    case (whereis(?SERVER) =:= self()) of
 686	false ->
 687	    call({set_engine_boots, Boots});
 688	true ->
 689	    dets:insert(?CONFIG_DB, {snmp_engine_boots, Boots}),
 690	    ok
 691    end.
 692    
 693
 694get_engine_time() ->
 695    case system_info(snmp_engine_base) of
 696	{ok, EngineBase} ->
 697	    {ok, snmp_misc:now(sec) - EngineBase};
 698	Error ->
 699	    Error
 700    end.
 701
 702get_usm_eboots(SnmpEngineID) ->
 703    Key = {eboots, SnmpEngineID},
 704    case get_usm_cache(Key) of
 705	{ok, Boots} ->
 706	    {ok, Boots};
 707	_ ->
 708	    {ok, 0}
 709    end.
 710
 711get_usm_etime(SnmpEngineID) ->
 712    Key = {etime, SnmpEngineID},
 713    case get_usm_cache(Key) of
 714	{ok, Diff} ->
 715	    {ok, snmp_misc:now(sec) - Diff};
 716	_ ->
 717	    {ok, 0}
 718    end.
 719
 720get_usm_eltime(SnmpEngineID) ->
 721    Key = {eltime, SnmpEngineID},
 722    case get_usm_cache(Key) of
 723	{ok, Time} ->
 724	    {ok, Time};
 725	_ ->
 726	    {ok, 0}
 727    end.
 728
 729get_usm_cache(Key) ->
 730    case ets:lookup(snmpm_usm_table, {usm_cache, Key}) of
 731	[{_, Val}] ->
 732	    {ok, Val};
 733	_ ->
 734	    {error, not_found}
 735    end.
 736    
 737set_usm_eboots(SnmpEngineID, EngineBoots) ->
 738    set_usm_cache({eboots, SnmpEngineID}, EngineBoots).
 739
 740set_usm_etime(SnmpEngineID, Diff) ->
 741    set_usm_cache({etime, SnmpEngineID}, Diff).
 742
 743set_usm_eltime(SnmpEngineID, Time) ->
 744    set_usm_cache({eltime, SnmpEngineID}, Time).
 745
 746set_usm_cache(Key, Val) ->
 747    call({set_usm_cache, Key, Val}).
 748
 749reset_usm_cache(SnmpEngineID) ->
 750    case (whereis(?SERVER) =:= self()) of
 751	false ->
 752	    call({reset_usm_cache, SnmpEngineID});
 753	true ->
 754	    Pat = {{usm_cache, {'_', SnmpEngineID}}, '_'},
 755	    ets:match_delete(snmpm_usm_table, Pat),
 756	    ok
 757    end.
 758
 759set_engine_time(Time) ->
 760    call({set_engine_time, Time}).
 761
 762register_usm_user(EngineID, Name, Config) 
 763  when is_list(EngineID) andalso is_list(Name) ->
 764    case verify_usm_user_config(EngineID, Name, Config) of
 765	{ok, User} ->
 766	    call({register_usm_user, User});
 767	Error ->
 768	    Error
 769    end.
 770
 771unregister_usm_user(EngineID, Name) 
 772  when is_list(EngineID) andalso is_list(Name) ->
 773    call({unregister_usm_user, EngineID, Name}).
 774
 775verify_usm_user_config(EngineID, Name, Config) ->
 776    try
 777	begin
 778	    verify_mandatory(Config, []), 
 779	    verify_illegal(Config, [engine_id, name]),
 780	    verify_usm_user_config2(EngineID, Name, Config)
 781	end
 782    catch 
 783	throw:Error ->
 784	    Error
 785    end.
 786
 787verify_usm_user_config2(EngineID, Name, Config) ->
 788    SecName = verify_usm_user_get(sec_name, Name,              Config),
 789    Auth    = verify_usm_user_get(auth,     usmNoAuthProtocol, Config),
 790    AuthKey = verify_usm_user_get(auth_key, [],                Config),
 791    Priv    = verify_usm_user_get(priv,     usmNoPrivProtocol, Config),
 792    PrivKey = verify_usm_user_get(priv_key, [],                Config),
 793    User = {EngineID, Name, SecName, Auth, AuthKey, Priv, PrivKey},
 794    verify_usm_user(User).
 795	
 796verify_usm_user_get(Item, Default, Config) ->	
 797    case lists:keysearch(Item, 1, Config) of
 798	{value, {_, Val}} ->
 799	    Val;
 800	false ->
 801	    Default
 802    end.
 803
 804which_usm_users() ->
 805    Pattern = {usm_key('$1', '$2'), '_'},
 806    Match   = ets:match(snmpm_usm_table, Pattern),
 807    [{EngineID, UserName} || [EngineID, UserName] <- Match].
 808
 809which_usm_users(EngineID) ->
 810    Pattern = {usm_key(EngineID, '$1'), '_'},
 811    Match   = ets:match(snmpm_usm_table, Pattern),
 812    [UserName || [UserName] <- Match].
 813
 814usm_user_info(EngineID, UserName, Item) ->
 815    case ets:lookup(snmpm_usm_table, usm_key(EngineID, UserName)) of
 816	[] ->
 817	    {error, not_found};
 818	[{_Key, UsmUser}] ->
 819	    do_usm_user_info(UsmUser, Item)
 820    end.
 821
 822do_usm_user_info(#usm_user{sec_name = SecName}, sec_name) ->
 823    {ok, SecName};
 824do_usm_user_info(#usm_user{auth = AuthP}, auth) ->
 825    {ok, AuthP};
 826do_usm_user_info(#usm_user{auth_key = AuthKey}, auth_key) ->
 827    {ok, AuthKey};
 828do_usm_user_info(#usm_user{priv = PrivP}, priv) ->
 829    {ok, PrivP};
 830do_usm_user_info(#usm_user{priv_key = PrivKey}, priv_key) ->
 831    {ok, PrivKey};
 832do_usm_user_info(#usm_user{engine_id = EngineID}, engine_id) ->
 833    {ok, EngineID};
 834do_usm_user_info(#usm_user{name = Name}, name) ->
 835    {ok, Name};
 836do_usm_user_info(_, Item) ->
 837    {error, {bad_iten, Item}}.
 838
 839update_usm_user_info(EngineID, UserName, Item, Val) 
 840  when (Item =/= engine_id) andalso (Item =/= name) ->
 841    call({update_usm_user_info, EngineID, UserName, Item, Val}).
 842
 843get_usm_user(EngineID, UserName) ->
 844    Key = usm_key(EngineID, UserName),
 845    case ets:lookup(snmpm_usm_table, Key) of
 846	[{_, User}] ->
 847	    {ok, User};
 848	_ ->
 849	    {error, not_found}
 850    end.
 851
 852is_usm_engine_id_known(EngineID) ->
 853    Pattern = {usm_key(EngineID, '$1'), '_'},
 854    case ets:match(snmpm_usm_table, Pattern) of
 855	[] ->
 856	    false;
 857	_ ->
 858	    true
 859    end.
 860
 861get_usm_user_from_sec_name(EngineID, SecName) ->
 862    %% Since the normal mapping between UserName and SecName is the
 863    %% identity-function, we first try to use the SecName as UserName,
 864    %% and check the resulting row.  If it doesn't match, we'll have to
 865    %% loop through the entire table.
 866    Key = usm_key(EngineID, SecName),
 867    case ets:lookup(snmpm_usm_table, Key) of
 868	[{Key, #usm_user{sec_name = SecName} = User}] ->
 869	    {ok, User};
 870	_ ->
 871	    %% That did not work, so we have to search
 872	    Pattern = {usm_key(EngineID, '_'), 
 873		       #usm_user{sec_name = SecName, _ = '_'}},
 874	    case ets:match_object(snmpm_usm_table, Pattern) of
 875		[{_, User}|_] ->
 876		    {ok, User};
 877		_ ->
 878		    {error, not_found}
 879	    end
 880    end.
 881
 882
 883%% Wrap-counters (wrapping at 2147483647 or 4294967295)
 884cre_counter(Counter, Initial) ->
 885    case (whereis(?SERVER) =:= self()) of
 886	false ->
 887	    call({cre_counter, Counter, Initial});
 888	true ->
 889	    ets:insert(snmpm_counter_table, {Counter, Initial}),
 890	    Initial
 891    end.
 892
 893incr_counter(usm_salt, Incr) ->        % Backward compatibillity (upgrade)
 894    incr_counter(usm_des_salt, Incr);  % Backward compatibillity (upgrade)
 895incr_counter(usm_des_salt, Incr) ->
 896    incr_counter(usm_des_salt, Incr, 4294967295);
 897incr_counter(usm_aes_salt, Incr) ->
 898    incr_counter(usm_aes_salt, Incr, 36893488147419103231);
 899incr_counter(Counter, Incr) ->
 900    incr_counter(Counter, Incr, 2147483647).
 901
 902incr_counter(Counter, Incr, Wrap) ->
 903    case (catch ets:update_counter(snmpm_counter_table, Counter, Incr)) of
 904	{'EXIT', _} ->
 905	    cre_counter(Counter, Incr);
 906	NewVal when NewVal =< Wrap ->
 907	    NewVal;
 908	N ->
 909	    cre_counter(Counter, N - Wrap)
 910    end.
 911
 912
 913%% <ATL Sequence Number>
 914increment_counter(Counter, Initial, Max) ->
 915    Increment = 1, 
 916    increment_counter(Counter, Initial, Increment, Max).
 917
 918increment_counter(Counter, Initial, Increment, Max) ->
 919    %% This is to make sure no one else increments our counter
 920    Key = {Counter, self()},
 921
 922    %% Counter data
 923    Position  = 2,
 924    Threshold = Max,
 925    SetValue  = Initial,
 926    UpdateOp  = {Position, Increment, Threshold, SetValue},
 927
 928    %% And now for the actual increment
 929    Tab = snmpm_counter_table, 
 930    case (catch ets:update_counter(Tab, Key, UpdateOp)) of
 931        {'EXIT', {badarg, _}} ->
 932            %% Oups, first time
 933            ets:insert(Tab, {Key, Initial}),
 934            Initial;
 935        Next when is_integer(Next) ->
 936            Next
 937    end.
 938%% </ATL Sequence Number>
 939
 940
 941maybe_cre_stats_counter(Counter, Initial) ->
 942    case ets:lookup(snmpm_stats_table, Counter) of
 943	[_] ->
 944	    ok;
 945	_ ->
 946	    cre_stats_counter(Counter, Initial)
 947    end.
 948			      
 949cre_stats_counter(Counter, Initial) ->
 950    case (whereis(?SERVER) =:= self()) of
 951	false ->
 952	    call({cre_stats_counter, Counter, Initial});
 953	true ->
 954	    ets:insert(snmpm_stats_table, {Counter, Initial}),
 955	    Initial
 956    end.
 957
 958incr_stats_counter(Counter, Incr) ->
 959    case (catch ets:update_counter(snmpm_stats_table, Counter, Incr)) of
 960	{'EXIT', _} ->
 961	    cre_counter(Counter, Incr);
 962	NewVal ->
 963	    NewVal
 964    end.
 965
 966reset_stats_counter(Counter) ->
 967    case (whereis(?SERVER) =:= self()) of
 968	false ->
 969	    call({reset_stats_counter, Counter});
 970	true ->
 971	    ets:insert(snmpm_stats_table, {Counter, 0})
 972    end,
 973    ok.
 974    
 975get_stats_counter(Counter) ->
 976    case ets:lookup(snmpm_stats_table, Counter) of
 977	[{Counter, Value}] ->
 978	    {ok, Value};
 979	_ ->
 980	    {error, not_found}
 981    end.
 982    
 983get_stats_counters() ->
 984    ets:tab2list(snmpm_stats_table).
 985    
 986load_mib(Mib) when is_list(Mib) ->
 987    call({load_mib, Mib}).
 988
 989unload_mib(Mib) when is_list(Mib) ->
 990    call({unload_mib, Mib}).
 991
 992which_mibs() ->
 993    Pattern = {{mib, '_'}, '$1', '$2'},
 994    Mibs = ets:match(snmpm_mib_table, Pattern),
 995    [list_to_tuple(X) || X <- Mibs].
 996
 997name_to_oid(Name) ->
 998    Pat = {{mini_mib, '$1'}, Name, '_', '_'},
 999    case ets:match(snmpm_mib_table, Pat) of
1000	[] ->
1001	    {error, not_found};
1002	X ->
1003	    Oids = [Oid || [Oid] <- X],
1004	    {ok, Oids}
1005    end.
1006
1007oid_to_name(Oid) ->
1008    case ets:lookup(snmpm_mib_table, {mini_mib, Oid}) of
1009	[{_, Name, _, _}] ->
1010	    {ok, Name};
1011	[] ->
1012	    {error, not_found}
1013    end.
1014
1015oid_to_type(Oid) ->
1016    case ets:lookup(snmpm_mib_table, {mini_mib, Oid}) of
1017	[{_, _, Type, _}] ->
1018	    {ok, Type};
1019	[] ->
1020	    {error, not_found}
1021    end.
1022
1023make_mini_mib() ->
1024    Pat = {{mini_mib, '$1'}, '$2', '$3', '_'},
1025    MiniElems = ets:match(snmpm_mib_table, Pat),
1026    lists:keysort(1, [list_to_tuple(MiniElem) || MiniElem <- MiniElems]).
1027
1028
1029info() ->
1030    call(info).
1031
1032verbosity(Verbosity) ->
1033    case ?vvalidate(Verbosity) of
1034	Verbosity ->
1035	    call({verbosity, Verbosity});
1036	_ ->
1037	    {error, {invalid_verbosity, Verbosity}}
1038    end.
1039
1040
1041%%%-------------------------------------------------------------------
1042%%% Callback functions from gen_server
1043%%%-------------------------------------------------------------------
1044
1045%%--------------------------------------------------------------------
1046%% Func: init/1
1047%% Returns: {ok, State}          |
1048%%          {ok, State, Timeout} |
1049%%          ignore               |
1050%%          {stop, Reason}
1051%%--------------------------------------------------------------------
1052init([Opts]) ->
1053%     put(sname, mconf),
1054%     put(verbosity, trace),
1055    ?d("init -> entry with"
1056       "~n   Opts: ~p", [Opts]),
1057    case (catch do_init(Opts)) of
1058	ok ->
1059	    {ok, #state{}};
1060	{error, Reason} ->
1061	    error_msg("init error: ~p", [Reason]),
1062	    {stop, Reason};
1063	{'EXIT', Reason} ->
1064	    error_msg("init exit: ~p", [Reason]),
1065	    {stop, Reason};
1066	Error ->
1067	    error_msg("init failed: ~p", [Error]),
1068	    {stop, Error}
1069    end.
1070
1071do_init(Opts) ->
1072    process_flag(trap_exit, true),
1073    %% Mandatory = [versions, {config, [dir]}],
1074    Mandatory = [{config, [dir, db_dir]}],
1075    verify_options(Opts, Mandatory),
1076
1077    ets:new(snmpm_counter_table, [set, public,    named_table, {keypos, 1}]),
1078    ets:new(snmpm_stats_table,   [set, public,    named_table, {keypos, 1}]),
1079    ets:new(snmpm_mib_table,     [set, protected, named_table, {keypos, 1}]),
1080    ets:new(snmpm_config_table,  [set, protected, named_table, {keypos, 1}]),
1081    ets:new(snmpm_agent_table,   [set, protected, named_table, {keypos, 1}]),
1082    ets:new(snmpm_user_table,    [set, protected, named_table, {keypos, 2}]),
1083    ets:new(snmpm_usm_table,     [set, protected, named_table, {keypos, 1}]),
1084
1085    %% -- System start time --
1086    ets:insert(snmpm_config_table, {system_start_time, snmp_misc:now(cs)}),
1087    
1088    %% --- Own options (dir and db_dir mandatory) ---
1089    ConfOpts      = get_opt(config,        Opts,     []),
1090    ConfVerb      = get_opt(verbosity,     ConfOpts, silence),
1091    ConfDir       = get_opt(dir,           ConfOpts),
1092    ConfDbDir     = get_opt(db_dir,        ConfOpts),
1093    ConfDbInitErr = get_opt(db_init_error, ConfOpts, terminate),
1094    ConfRep       = get_opt(repair,        ConfOpts, true),
1095    ConfAs        = get_opt(auto_save,     ConfOpts, 5000),
1096    ets:insert(snmpm_config_table, {config_verbosity,     ConfVerb}),
1097    ets:insert(snmpm_config_table, {config_dir,           ConfDir}),
1098    ets:insert(snmpm_config_table, {config_db_dir,        ConfDbDir}),
1099    ets:insert(snmpm_config_table, {config_db_init_error, ConfDbInitErr}),
1100    ets:insert(snmpm_config_table, {config_repair,        ConfRep}),
1101    ets:insert(snmpm_config_table, {config_auto_save,     ConfAs}),
1102    put(sname, mconf),
1103    put(verbosity, ConfVerb),
1104    ?vlog("starting", []),
1105
1106    %% -- Create dets file used for storing persistent data --
1107    dets_open(ConfDbDir, ConfDbInitErr, ConfRep, ConfAs),
1108    
1109    %% -- Prio (optional) --
1110    Prio = get_opt(priority, Opts, normal),
1111    ets:insert(snmpm_config_table, {prio, Prio}),
1112    try process_flag(priority, Prio)
1113    catch
1114	error:badarg ->
1115	    error({invalid_priority,Prio})
1116    end,
1117
1118    %% -- Server (optional) --
1119    ServerOpts = get_opt(server,         Opts,      []),
1120    ServerVerb = get_opt(verbosity,      ServerOpts, silence),
1121    ServerGct  = get_opt(timeout,        ServerOpts, 30000),
1122    ServerMt   = get_opt(multi_threaded, ServerOpts, true),
1123    ets:insert(snmpm_config_table, {server_verbosity,      ServerVerb}),
1124    ets:insert(snmpm_config_table, {server_timeout,        ServerGct}),
1125    ets:insert(snmpm_config_table, {server_multi_threaded, ServerMt}),
1126   
1127    %% -- Mibs (optional) --
1128    ?vdebug("initiate mini mib", []),
1129    Mibs = get_opt(mibs, Opts, []),
1130    ets:insert(snmpm_config_table, {mibs, Mibs}),
1131    init_mini_mib(Mibs),
1132
1133    %% -- Net-if (optional) --
1134    ?vdebug("net_if options", []),
1135    NetIfIrb     = 
1136	case get_opt(inform_request_behaviour, Opts, ?IRB_DEFAULT) of
1137	    user ->
1138		{user, timer:seconds(15)};
1139	    Irb ->
1140		Irb
1141	end,
1142    NetIfOpts    = get_opt(net_if,                    Opts,      []),
1143    NetIfMod     = get_opt(module,                    NetIfOpts, snmpm_net_if),
1144    NetIfVerb    = get_opt(verbosity,                 NetIfOpts, silence),
1145    NetIfOptions = get_opt(options,                   NetIfOpts, []),
1146    ets:insert(snmpm_config_table, {net_if_module,    NetIfMod}),
1147    ets:insert(snmpm_config_table, {net_if_verbosity, NetIfVerb}),
1148    ets:insert(snmpm_config_table, {net_if_irb,       NetIfIrb}),
1149    ets:insert(snmpm_config_table, {net_if_options,   NetIfOptions}),
1150
1151    %% -- Versions (optional) --
1152    %% -- Versions (mandatory) ???????????? --
1153    ?vdebug("versions", []),
1154    Vsns = get_opt(versions, Opts, [v1, v2, v3]),
1155    ets:insert(snmpm_config_table, {versions, Vsns}),
1156
1157    %% -- Audit trail log (optional) --
1158    ?vdebug("audit trail log", []),
1159    case get_opt(audit_trail_log, Opts, []) of
1160	[] ->
1161	    ?vtrace("no ATL", []),
1162	    ets:insert(snmpm_config_table, {audit_trail_log, false});
1163	AuditTrailLogOpts ->
1164	    ?vtrace("ATL options: ~p", [AuditTrailLogOpts]),
1165	    ets:insert(snmpm_config_table, {audit_trail_log, true}),
1166	    LogDir   = get_atl_dir(AuditTrailLogOpts),
1167	    LogType  = get_atl_type(AuditTrailLogOpts),
1168	    LogSize  = get_atl_size(AuditTrailLogOpts),
1169	    LogRep   = get_atl_repair(AuditTrailLogOpts),
1170	    LogSeqNo = get_atl_seqno(AuditTrailLogOpts),
1171	    ets:insert(snmpm_config_table, {audit_trail_log_dir,    LogDir}),
1172	    ets:insert(snmpm_config_table, {audit_trail_log_type,   LogType}),
1173	    ets:insert(snmpm_config_table, {audit_trail_log_size,   LogSize}),
1174	    ets:insert(snmpm_config_table, {audit_trail_log_repair, LogRep}),
1175	    ets:insert(snmpm_config_table, {audit_trail_log_seqno,  LogSeqNo})
1176    end,
1177
1178    %% -- System default agent config --
1179    ?vdebug("system default agent config", []),
1180    init_agent_default(),
1181
1182    %% -- User (optional) --
1183    ?vdebug("default user", []),
1184    DefUserMod  = get_opt(def_user_mod,  Opts, ?USER_MOD_DEFAULT),
1185    DefUserData = get_opt(def_user_data, Opts, ?USER_DATA_DEFAULT),
1186    ets:insert(snmpm_config_table, {def_user_mod,  DefUserMod}),
1187    ets:insert(snmpm_config_table, {def_user_data, DefUserData}),
1188    
1189    {ok, SystemDefaultAgentConfig}       = agent_info(),
1190    DefUser = #user{id                   = ?DEFAULT_USER, 
1191		    mod                  = DefUserMod, 
1192		    data                 = DefUserData,
1193		    default_agent_config = SystemDefaultAgentConfig},
1194    ok = handle_register_user(DefUser),
1195    
1196    %% -- Note store --
1197    ?vdebug("note store", []),
1198    NoteStoreOpts    = get_opt(note_store, Opts, []),
1199    NoteStoreVerb    = get_opt(verbosity,  NoteStoreOpts, silence),
1200    NoteStoreTimeout = get_opt(timeout,    NoteStoreOpts, 30000),
1201    ets:insert(snmpm_config_table, {note_store_verbosity, NoteStoreVerb}),
1202    ets:insert(snmpm_config_table, {note_store_timeout,   NoteStoreTimeout}),
1203    
1204    %% -- Manager SNMP config --
1205    ?vdebug("manager snmp config", []),
1206    MgrConf = read_manager_config_file(ConfDir),
1207    init_manager_config(MgrConf),
1208
1209    %% -- User config --
1210    ?vdebug("users config", []),
1211    Users = read_users_config_file(ConfDir),
1212    init_users_config(Users),
1213
1214    %% -- Agents config --
1215    ?vdebug("agents config", []),
1216    Agents = read_agents_config_file(ConfDir),
1217    init_agents_config(Agents),
1218
1219    %% -- USM config --
1220    UsmUsers = read_usm_config_file(ConfDir),
1221    init_usm_users_config(UsmUsers),
1222
1223    %% -- snmp engine init --
1224    init_engine(),
1225
1226    ?vlog("started", []),
1227    ok.
1228
1229
1230dets_open(Dir, DbInitError, Repair, AutoSave) ->
1231    Name     = ?CONFIG_DB, 
1232    Filename = dets_filename(Name, Dir),
1233    case file:read_file_info(Filename) of
1234	{ok, _} ->
1235	    %% File exists
1236	    case do_dets_open(Name, Filename, Repair, AutoSave) of
1237		{ok, _Dets} ->
1238		    ok;
1239		{error, Reason1} ->
1240                    info_msg("Corrupt local database: ~p", [Filename]),
1241		    case DbInitError of
1242			terminate ->
1243			    error({failed_reopen_dets, Filename, Reason1});
1244			_ ->
1245			    Saved = Filename ++ ".saved",
1246			    file:rename(Filename, Saved),
1247			    case do_dets_open(Name, Filename, 
1248					      Repair, AutoSave) of
1249				{ok, _Dets} ->
1250				    ok;
1251				{error, Reason2} ->
1252				    error({failed_open_dets, Filename, 
1253					   Reason1, Reason2})
1254			    end
1255		    end
1256	    end;
1257	_ ->
1258	    case DbInitError of
1259		create_db_and_dir ->
1260		    ok = filelib:ensure_dir(Filename);
1261		_ ->
1262		    ok
1263	    end,
1264	    case do_dets_open(Name, Filename, Repair, AutoSave) of
1265		{ok, _Dets} ->
1266		    ok;
1267		{error, Reason} ->
1268		    error({failed_open_dets, Filename, Reason})
1269	    end
1270    end.
1271		
1272do_dets_open(Name, Filename, Repair, AutoSave) ->
1273    Opts = [{repair,    Repair}, 
1274	    {auto_save, AutoSave}, 
1275	    {file,      Filename}],
1276    dets:open_file(Name, Opts).
1277
1278
1279dets_filename(Name, Dir) when is_atom(Name) ->
1280    dets_filename(atom_to_list(Name), Dir);
1281dets_filename(Name, Dir) ->
1282    filename:join(dets_filename1(Dir), Name).
1283
1284dets_filename1([])  -> ".";
1285dets_filename1(Dir) -> Dir.
1286
1287
1288%% ------------------------------------------------------------------------
1289
1290init_engine() ->
1291    case get_engine_boots() of
1292	{ok, Val} when Val < 2147483647 ->
1293	    set_engine_boots(Val + 1);
1294	{ok, _} ->
1295	    ok;
1296	_ ->
1297	    set_engine_boots(1)
1298    end,
1299    reset_engine_base().
1300
1301reset_engine_base() ->
1302    ets:insert(snmpm_config_table, {snmp_engine_base, snmp_misc:now(sec)}).
1303
1304
1305%% ------------------------------------------------------------------------
1306
1307verify_options(Opts, Mandatory) ->
1308    ?d("verify_options -> entry with"
1309       "~n   Opts:      ~p"
1310       "~n   Mandatory: ~p", [Opts, Mandatory]),
1311    verify_mandatory_options(Opts, Mandatory),
1312    verify_options(Opts).
1313
1314verify_options([]) ->
1315    ?d("verify_options -> done", []),
1316    ok;
1317verify_options([Opt|Opts]) ->
1318    ?d("verify_options -> entry with"
1319       "~n   Opt: ~p", [Opt]),
1320    verify_option(Opt),
1321    verify_options(Opts).
1322
1323verify_option({prio, Prio}) ->
1324    verify_prio(Prio);
1325verify_option({mibs, Mibs}) ->
1326    verify_mibs(Mibs);
1327verify_option({inform_request_behaviour, IRB}) ->
1328    verify_irb(IRB);
1329verify_option({net_if, NetIfOpts}) ->
1330    verify_net_if_opts(NetIfOpts);
1331verify_option({server, ServerOpts}) ->
1332    verify_server_opts(ServerOpts);
1333verify_option({note_store, NoteStoreOpts}) ->
1334    verify_note_store_opts(NoteStoreOpts);
1335verify_option({config, ConfOpts0}) ->
1336    %% Make sure any db_dir option is first in the options list to make it
1337    %% easier to check if the db_init_error option specifies that a missing
1338    %% db_dir should be created.
1339    ConfOpts = case lists:keytake(db_dir, 1, ConfOpts0) of
1340                   false -> ConfOpts0;
1341                   {value, Result, OtherOpts} -> [Result|OtherOpts]
1342               end,
1343    verify_config_opts(ConfOpts);
1344verify_option({versions, Vsns}) ->
1345    verify_versions(Vsns);
1346verify_option({audit_trail_log, LogOpts}) ->
1347    Mandatory = [dir, size],
1348    case (catch verify_mandatory_options(LogOpts, Mandatory)) of
1349	ok ->
1350	    verify_audit_trail_log_opts(LogOpts);
1351	{error, {missing_mandatory, LogOpt}} ->
1352	    error({missing_mandatory, audit_trail_log, LogOpt})
1353    end;
1354verify_option({def_user_mod, Mod}) ->
1355    verify_module(def_user_mod, Mod);
1356verify_option({def_user_data, _Data}) ->
1357    ok;
1358verify_option(Opt) ->
1359    {error, {invalid_option, Opt}}.
1360
1361verify_prio(Prio) when is_atom(Prio) ->
1362    ok;
1363verify_prio(Prio) ->
1364    error({invalid_prio, Prio}).
1365
1366verify_irb(auto) ->
1367    ok;
1368verify_irb(user) ->
1369    ok;
1370verify_irb({user, To}) when is_integer(To) andalso (To > 0) ->
1371    ok;
1372verify_irb(IRB) ->
1373    error({invalid_irb, IRB}).
1374
1375verify_mibs([]) ->
1376    ok;
1377verify_mibs([Mib|Mibs]) when is_list(Mib) ->
1378    verify_mibs(Mibs);
1379verify_mibs(Mibs) ->
1380    error({invalid_mibs, Mibs}).
1381
1382verify_config_opts([]) ->
1383    ok;
1384verify_config_opts([{verbosity, Verbosity}|Opts]) ->
1385    verify_verbosity(Verbosity),
1386    verify_config_opts(Opts);
1387verify_config_opts([{dir, Dir}|Opts]) ->
1388    verify_conf_dir(Dir),
1389    verify_config_opts(Opts);
1390verify_config_opts([{db_dir, Dir}|Opts]) ->
1391    case lists:keyfind(db_init_error, 1, Opts) of
1392        {db_init_error, create_db_and_dir} ->
1393            verify_conf_db_dir(Dir, false);
1394        _ ->
1395            verify_conf_db_dir(Dir, true)
1396    end,
1397    verify_config_opts(Opts);
1398verify_config_opts([{db_init_error, DbInitErr}|Opts]) ->
1399    verify_conf_db_init_error(DbInitErr),
1400    verify_config_opts(Opts);
1401verify_config_opts([{repair, Repair}|Opts]) ->
1402    verify_conf_repair(Repair),
1403    verify_config_opts(Opts);
1404verify_config_opts([{auto_save, AutoSave}|Opts]) ->
1405    verify_conf_auto_save(AutoSave),
1406    verify_config_opts(Opts);
1407verify_config_opts([Opt|_]) ->
1408    error({invalid_config_option, Opt}).
1409
1410verify_server_opts([]) ->
1411    ok;
1412verify_server_opts([{verbosity, Verbosity}|Opts]) ->
1413    verify_verbosity(Verbosity),
1414    verify_server_opts(Opts);
1415verify_server_opts([{timeout, Timeout}|Opts]) ->
1416    verify_server_timeout(Timeout),
1417    verify_server_opts(Opts);
1418verify_server_opts([Opt|_]) ->
1419    error({invalid_server_option, Opt}).
1420
1421verify_server_timeout(T) when is_integer(T) andalso (T > 0) ->
1422    ok;
1423verify_server_timeout(T) ->
1424    error({invalid_server_timeout, T}).
1425
1426verify_net_if_opts([]) ->
1427    ok;
1428verify_net_if_opts([{module, Mod}|Opts]) ->
1429    verify_network_interface_behaviour(Mod),
1430    verify_net_if_opts(Opts);
1431verify_net_if_opts([{verbosity, Verbosity}|Opts]) ->
1432    verify_verbosity(Verbosity),
1433    verify_net_if_opts(Opts);
1434verify_net_if_opts([{options, Options}|Opts]) when is_list(Options) ->
1435    verify_net_if_opts(Opts);
1436verify_net_if_opts([Opt|_]) ->
1437    error({invalid_net_if_option, Opt}).
1438
1439verify_network_interface_behaviour(Mod) ->
1440    case snmp_misc:verify_behaviour(snmpm_network_interface, Mod) of
1441	ok ->
1442	    ok;
1443	Error ->
1444	    throw(Error)
1445    end.
1446    
1447    
1448verify_note_store_opts([]) ->
1449    ok;
1450verify_note_store_opts([{verbosity, Verbosity}|Opts]) ->
1451    verify_verbosity(Verbosity),
1452    verify_note_store_opts(Opts);
1453verify_note_store_opts([{timeout, Timeout}|Opts]) ->
1454    verify_note_store_timeout(Timeout),
1455    verify_note_store_opts(Opts);
1456verify_note_store_opts([Opt|_]) ->
1457    error({invalid_note_store_option, Opt}).
1458
1459verify_note_store_timeout(T) when is_integer(T) andalso (T > 0) ->
1460    ok;
1461verify_note_store_timeout(T) ->
1462    error({invalid_note_store_timeout, T}).
1463
1464verify_conf_dir(Dir) ->
1465    case (catch verify_dir(Dir)) of
1466	ok ->
1467	    ok;
1468	{error, Reason} ->
1469	    error({invalid_conf_dir, Dir, Reason});
1470	_ ->
1471	    error({invalid_conf_dir, Dir})
1472    end.
1473
1474verify_conf_db_dir(Dir, true) ->
1475    case (catch verify_dir(Dir)) of
1476	ok ->
1477	    ok;
1478	{error, Reason} ->
1479	    error({invalid_conf_db_dir, Dir, Reason});
1480	_ ->
1481	    error({invalid_conf_db_dir, Dir})
1482    end;
1483verify_conf_db_dir(_Dir, false) ->
1484    ok.
1485
1486verify_conf_db_init_error(terminate) ->
1487    ok;
1488verify_conf_db_init_error(create) ->
1489    ok;
1490verify_conf_db_init_error(create_db_and_dir) ->
1491    ok;
1492verify_conf_db_init_error(InvalidDbInitError) ->
1493    error({invalid_conf_db_init_error, InvalidDbInitError}).
1494
1495
1496verify_conf_repair(true) ->
1497    ok;
1498verify_conf_repair(false) ->
1499    ok;
1500verify_conf_repair(force) ->
1501    ok;
1502verify_conf_repair(InvalidRepair) ->
1503    error({invalid_conf_db_repair, InvalidRepair}).
1504
1505
1506verify_conf_auto_save(infinity) ->
1507    ok;
1508verify_conf_auto_save(AutoSave) 
1509  when is_integer(AutoSave) andalso (AutoSave > 0) ->
1510    ok;
1511verify_conf_auto_save(InvalidAutoSave) ->
1512    error({invalid_conf_db_auto_save, InvalidAutoSave}).
1513
1514
1515verify_versions([]) ->
1516    ok;
1517verify_versions([Vsn|Vsns]) ->
1518    verify_version(Vsn),
1519    verify_versions(Vsns);
1520verify_versions(Vsns) ->
1521    error({invalid_versions, Vsns}).
1522
1523verify_version(v1) ->
1524    ok;
1525verify_version(v2) ->
1526    ok;
1527verify_version(v3) ->
1528    ok;
1529verify_version(Vsn) ->
1530    error({invalid_version, Vsn}).
1531
1532verify_audit_trail_log_opts([]) ->
1533    ok;
1534verify_audit_trail_log_opts([{dir, Dir}|Opts]) ->
1535    verify_log_dir(Dir),
1536    verify_audit_trail_log_opts(Opts);
1537verify_audit_trail_log_opts([{type, Type}|Opts]) ->
1538    verify_log_type(Type),
1539    verify_audit_trail_log_opts(Opts);
1540verify_audit_trail_log_opts([{size, Size}|Opts]) ->
1541    verify_log_size(Size),
1542    verify_audit_trail_log_opts(Opts);
1543verify_audit_trail_log_opts([{repair, Repair}|Opts]) ->
1544    verify_log_repair(Repair),
1545    verify_audit_trail_log_opts(Opts);
1546verify_audit_trail_log_opts([{seqno, SeqNo}|Opts]) ->
1547    verify_log_seqno(SeqNo),
1548    verify_audit_trail_log_opts(Opts);
1549verify_audit_trail_log_opts([Opt|_Opts]) ->
1550    error({invalid_audit_trail_log_option, Opt}).
1551
1552verify_log_type(read) ->
1553    ok;
1554verify_log_type(write) ->
1555    ok;
1556verify_log_type(read_write) ->
1557    ok;
1558verify_log_type(Type) ->
1559    error({invalid_audit_trail_log_type, Type}).
1560
1561verify_log_dir(Dir) ->
1562    case (catch verify_dir(Dir)) of
1563	ok ->
1564	    ok;
1565	{error, Reason} ->
1566	    error({invalid_audit_trail_log_dir, Dir, Reason});
1567	_ ->
1568	    error({invalid_audit_trail_log_dir, Dir})
1569    end.
1570
1571verify_log_size(Sz) when is_integer(Sz) andalso (Sz > 0) ->
1572    ok;
1573verify_log_size(infinity) ->
1574    ok;
1575verify_log_size({MaxNoBytes, MaxNoFiles}) 
1576  when (is_integer(MaxNoBytes) andalso 
1577	(MaxNoBytes > 0) andalso 
1578	is_integer(MaxNoFiles) andalso 
1579	(MaxNoFiles > 0) andalso 
1580	(MaxNoFiles < 65000)) ->
1581    ok;
1582verify_log_size(Sz) ->
1583    error({invalid_audit_trail_log_size, Sz}).
1584
1585verify_log_repair(true) -> ok;
1586verify_log_repair(false) -> ok;
1587verify_log_repair(truncate) -> ok;
1588verify_log_repair(Repair) ->
1589    error({invalid_audit_trail_log_repair, Repair}).
1590
1591verify_log_seqno(true) -> ok;
1592verify_log_seqno(false) -> ok;
1593verify_log_seqno(SeqNo) ->
1594    error({invalid_audit_trail_log_seqno, SeqNo}).
1595
1596
1597verify_module(_, Mod) when is_atom(Mod) ->
1598    ok;
1599verify_module(ReasonTag, Mod) ->
1600    error({invalid_module, ReasonTag, Mod}).
1601
1602% verify_bool(_, true) ->
1603%     ok;
1604% verify_bool(_, false) ->
1605%     ok;
1606% verify_bool(ReasonTag, Bool) ->
1607%     error({invalid_bool, ReasonTag, Bool}).
1608
1609verify_dir(Dir) when is_list(Dir) ->
1610    case file:read_file_info(Dir) of
1611	{ok, #file_info{type = directory}} ->
1612	    ok;
1613	{ok, _} ->
1614	    {error, not_directory};
1615	{error, _Reason} ->
1616	    {error, not_found}
1617    end;
1618verify_dir(Dir) ->
1619    {error, {invalid_log_dir, Dir}}.
1620
1621
1622verify_verbosity(Verbosity) ->
1623    case snmp_verbosity:validate(Verbosity) of
1624	Verbosity ->
1625	    ok;
1626	_ ->
1627	    error({invalid_verbosity, Verbosity})
1628    end.
1629
1630    
1631%% mandatory() -> [mand()]
1632%% mand() -> atom() | {atom, [atom()]}
1633verify_mandatory_options(_Opts, []) ->
1634    ok;
1635verify_mandatory_options(Opts, [Mand|Mands]) ->
1636    verify_mandatory_option(Opts, Mand),
1637    verify_mandatory_options(Opts, Mands).
1638
1639verify_mandatory_option(Opts, {Mand, MandSubOpts}) ->
1640    ?d("verify_mandatory_option -> entry with"
1641       "~n   Mand:        ~p"
1642       "~n   MandSubObjs: ~p", [Mand, MandSubOpts]),
1643    case lists:keysearch(Mand, 1, Opts) of
1644	{value, {Mand, SubOpts}} ->
1645	    verify_mandatory_options(SubOpts, MandSubOpts);
1646	false ->
1647	    ?d("missing mandatory option: ~w [~p]", [Mand, MandSubOpts]),
1648	    error({missing_mandatory, Mand, MandSubOpts})
1649    end;
1650verify_mandatory_option(Opts, Mand) ->
1651    ?d("verify_mandatory_option -> entry with"
1652       "~n   Mand:        ~p", [Mand]),
1653    case lists:keymember(Mand, 1, Opts) of
1654	true ->
1655	    ok;
1656	false ->
1657	    ?d("missing mandatory option: ~w", [Mand]),
1658	    error({missing_mandatory, Mand})
1659    end.
1660
1661%% ------------------------------------------------------------------------
1662
1663init_manager_config([]) ->
1664    ok;
1665init_manager_config([{Key, Val}|Confs]) ->
1666    ets:insert(snmpm_config_table, {Key, Val}),
1667    init_manager_config(Confs).
1668
1669
1670
1671init_agent_default() ->
1672    %% The purpose of the default_agent is only to have a place
1673    %% to store system wide default values related to agents.
1674    %% 
1675    AgentDefaultConfig =
1676	[{port, ?DEFAULT_AGENT_PORT}, % Port
1677	 {timeout, 10000},            % Timeout
1678	 {max_message_size, 484},     % Max message (packet) size
1679	 {version, v2},               % MPModel
1680	 {sec_model, v2c},            % SecModel
1681	 {sec_name, "initial"},       % SecName
1682	 {sec_level, noAuthNoPriv},   % SecLevel
1683	 {community, "all-rights"}],  % Community
1684    do_update_agent_info(default_agent, AgentDefaultConfig).
1685
1686read_agents_config_file(Dir) ->
1687    Order = fun snmp_conf:no_order/2,
1688    Check = fun check_agent_config/2,
1689    try read_file(Dir, "agents.conf", Order, Check, [])
1690    catch
1691	throw:Error ->
1692	    ?vlog("agent config error: ~p", [Error]),
1693	    erlang:raise(throw, Error, erlang:get_stacktrace())
1694    end.
1695
1696check_agent_config(Agent, State) ->
1697    {ok, {UserId, TargetName, Conf, Version}} = check_agent_config(Agent),
1698    {ok, Vsns} = system_info(versions),
1699    case lists:member(Version, Vsns) of
1700	true ->
1701	    {{ok, {UserId, TargetName, Conf}}, State};
1702	false ->
1703	    error({version_not_supported_by_manager, Version, Vsns})
1704    end.
1705
1706%% For backward compatibility
1707check_agent_config(
1708  {UserId, TargetName, Community, Domain, Addr,
1709   EngineId, Timeout, MaxMessageSize,
1710   Version, SecModel, SecName, SecLevel}) when is_atom(Domain) ->
1711    check_agent_config(
1712      UserId, TargetName, Community, Domain, Addr,
1713      EngineId, Timeout, MaxMessageSize,
1714      Version, SecModel, SecName, SecLevel);
1715check_agent_config(
1716  {UserId, TargetName, Community, Ip, Port,
1717   EngineId, Timeout, MaxMessageSize,
1718   Version, SecModel, SecName, SecLevel}) when is_integer(Port) ->
1719    Domain = default_transport_domain(),
1720    Addr = {Ip, Port},
1721    check_agent_config(
1722      UserId, TargetName, Community, Domain, Addr,
1723      EngineId, Timeout, MaxMessageSize,
1724      Version, SecModel, SecName, SecLevel);
1725check_agent_config(
1726  {_UserId, _TargetName, _Community, Domain, Addr,
1727   _EngineId, _Timeout, _MaxMessageSize,
1728   _Version, _SecModel, _SecName, _SecLevel}) ->
1729    error({bad_address, {Domain, Addr}});
1730check_agent_config(
1731  {UserId, TargetName, Community, Domain, Ip, Port,
1732   EngineId, Timeout, MaxMessageSize,
1733   Version, SecModel, SecName, SecLevel}) ->
1734    Addr = {Ip, Port},
1735    check_agent_config(
1736      UserId, TargetName, Community, Domain, Addr,
1737      EngineId, Timeout, MaxMessageSize,
1738      Version, SecModel, SecName, SecLevel);
1739check_agent_config(Agent) ->
1740    error({bad_agent_config, Agent}).
1741
1742check_agent_config(
1743  UserId, TargetName, Comm, Domain, Addr,
1744  EngineId, Timeout, MMS,
1745  Version, SecModel, SecName, SecLevel) ->
1746    ?vdebug("check_agent_config -> entry with"
1747	    "~n   UserId:     ~p"
1748	    "~n   TargetName: ~p", [UserId, TargetName]),
1749    snmp_conf:check_string(TargetName, {gt, 0}),
1750    %% Note that the order of Conf *is* important.
1751    %% Some properties may depend on others, so that
1752    %% in order to verify one property, another must
1753    %% be already verified (and present). An example 
1754    %% of this is the property 'taddress', for which
1755    %% the property tdomain is needed.
1756    Conf =
1757	[{reg_type,         target_name},
1758	 {tdomain,          Domain},
1759	 {taddress,         fix_address(Domain, Addr)},
1760	 {community,        Comm}, 
1761	 {engine_id,        EngineId},
1762	 {timeout,          Timeout},
1763	 {max_message_size, MMS},
1764	 {version,          Version},
1765	 {sec_model,        SecModel},
1766	 {sec_name,         SecName},
1767	 {sec_level,        SecLevel}
1768	],
1769    {ok, {UserId, TargetName, verify_agent_config(Conf), Version}}.
1770
1771
1772
1773init_agents_config([]) ->
1774    ok;
1775init_agents_config([Agent|Agents]) ->
1776    init_agent_config(Agent),
1777    init_agents_config(Agents).
1778
1779init_agent_config({_UserId, ?DEFAULT_TARGETNAME = TargetName, _Config}) ->
1780    error({invalid_target_name, TargetName});
1781init_agent_config({UserId, TargetName, Config}) ->
1782    case handle_register_agent(UserId, TargetName, Config) of
1783	ok ->
1784	    ok;
1785	Error ->
1786	    throw(Error)
1787    end.
1788
1789
1790
1791%% Sort 'tdomain' first then 'port' to ensure both
1792%% sorts before 'taddress'.  Keep the order of other items.
1793order_agent(ItemA, ItemB) ->
1794    snmp_conf:keyorder(1, ItemA, ItemB, [tdomain, port]).
1795
1796fix_agent_config(Conf) ->
1797    ?vdebug("fix_agent_config -> entry with~n~n"
1798	    "   Conf: ~p", [Conf]),
1799    fix_agent_config(lists:sort(fun order_agent/2, Conf), []).
1800
1801fix_agent_config([], FixedConf) ->
1802    Ret = lists:reverse(FixedConf),
1803    ?vdebug("fix_agent_config -> returns:~n"
1804	    "   ~p", [Ret]),
1805    Ret;
1806fix_agent_config([{taddress = Item, Address} = Entry|Conf], FixedConf) ->
1807    {value, {tdomain, TDomain}} = lists:keysearch(tdomain, 1, FixedConf),
1808    {value, {port, DefaultPort}} = lists:keysearch(port, 1, FixedConf),
1809    case snmp_conf:check_address(TDomain, Address, DefaultPort) of
1810	ok ->
1811	    fix_agent_config(Conf, [Entry|FixedConf]);
1812	{ok, NAddress} ->
1813	    fix_agent_config(Conf, [{Item, NAddress}|FixedConf])
1814    end;
1815fix_agent_config([Entry|Conf], FixedConf) ->
1816    fix_agent_config(Conf, [Entry|FixedConf]).
1817
1818
1819
1820verify_agent_config(Conf) ->
1821    verify_agent_config(lists:sort(fun order_agent/2, Conf), []).
1822
1823verify_agent_config([], VerifiedConf) ->
1824    Ret = lists:reverse(VerifiedConf),
1825    ?vdebug("verify_agent_config -> returns:~n"
1826	    "   ~p", [Ret]),
1827    Ret;
1828verify_agent_config([{Item, _} = Entry|Conf], VerifiedConf) ->
1829    verify_illegal(VerifiedConf, [Item]), % Duplicates are hereby illegal
1830    verify_agent_config(Conf, VerifiedConf, Entry);
1831verify_agent_config([Bad|_], _VerifiedConf) ->
1832    error({bad_agent_config, Bad}).
1833
1834verify_agent_config(
1835  Conf, VerifiedConf, {taddress = Item, Address} = Entry) ->
1836    verify_illegal(VerifiedConf, [address]),
1837    {TDomain, VC} =
1838	case lists:keysearch(tdomain, 1, VerifiedConf) of
1839	    {value, {tdomain,TD}} ->
1840		{TD, VerifiedConf};
1841	    _ ->
1842		%% Insert tdomain since it is missing
1843		%% Note: not default_transport_domain() since
1844		%% taddress is the new format hence the application
1845		%% should be tdomain aware and therefore addresses
1846		%% on the Domain, Addr format should be used and understood.
1847		TD = transportDomainUdpIpv4,
1848		{TD, [{tdomain, TD}|VerifiedConf]}
1849	end,
1850    case snmp_conf:check_address(TDomain, Address, 0) of
1851	ok ->
1852	    verify_agent_config(Conf, [Entry|VC]);
1853	{ok, NAddress} ->
1854	    verify_agent_config(Conf, [{Item, NAddress}|VC])
1855    end;
1856verify_agent_config(Conf, VerifiedConf, {address, Address}) ->
1857    Item = taddress,
1858    verify_illegal(VerifiedConf, [Item]),
1859    {TDomain, VC} =
1860	case lists:keysearch(tdomain, 1, VerifiedConf) of
1861	    {value, {tdomain, TD}} ->
1862		{TD, VerifiedConf};
1863	    _ ->
1864		%% Insert tdomain since it is missing
1865		TD = default_transport_domain(),
1866		{TD, [{tdomain, TD}|VerifiedConf]}
1867	end,
1868    case snmp_conf:check_address(TDomain, Address, 0) of
1869	ok ->
1870	    verify_agent_config(Conf, [{Item, Address}|VC]);
1871	{ok, NAddress} ->
1872	    verify_agent_config(Conf, [{Item, NAddress}|VC])
1873    end;
1874verify_agent_config(Conf, VerifiedConf, {Item, Val} = Entry) ->
1875    case verify_agent_entry(Item, Val) of
1876	ok ->
1877	    verify_agent_config(Conf, [Entry|VerifiedConf]);
1878	{ok, NewVal} ->
1879	    verify_agent_config(Conf, [{Item, NewVal}|VerifiedConf])
1880    end.
1881
1882verify_agent_entry(user_id, _UserId) ->
1883    ok;
1884verify_agent_entry(reg_type, RegType) ->
1885    if
1886	RegType =:= addr_port;
1887	RegType =:= target_name ->
1888	    ok;
1889	true ->
1890	    error({bad_reg_type, RegType})
1891    end;
1892verify_agent_entry(tdomain, TDomain) ->
1893    snmp_conf:check_domain(TDomain);
1894verify_agent_entry(port, Port) ->
1895    snmp_conf:check_port(Port);
1896verify_agent_entry(community, Comm) ->
1897    snmp_conf:check_string(Comm);
1898verify_agent_entry(engine_id, EngineId) ->
1899    case EngineId of
1900	discovery ->
1901	    ok;
1902	_ ->
1903	    snmp_conf:check_string(EngineId)
1904    end;
1905verify_agent_entry(timeout, Timeout) ->
1906    snmp_conf:check_timer(Timeout);
1907verify_agent_entry(max_message_size, MMS) ->
1908    snmp_conf:check_packet_size(MMS);
1909verify_agent_entry(version, V) ->
1910    if
1911	V =:= v1;
1912	V =:= v2;
1913	V =:= v3 ->
1914	    ok;
1915	true ->
1916	    error({bad_version, V})
1917    end;
1918verify_agent_entry(sec_model, Model) ->
1919    snmp_conf:check_sec_model(Model);
1920verify_agent_entry(sec_name, Name) ->
1921    try snmp_conf:check_string(Name)
1922    catch
1923	_ ->
1924	    error({bad_sec_name, Name})
1925    end;
1926verify_agent_entry(sec_level, Level) ->
1927    snmp_conf:check_sec_level(Level);
1928verify_agent_entry(Item, _) ->
1929    error({unknown_item, Item}).
1930
1931
1932
1933read_users_config_file(Dir) ->
1934    Order = fun snmp_conf:no_order/2,
1935    Check = fun (User, State) -> {check_user_config(User), State} end,
1936    try read_file(Dir, "users.conf", Order, Check, [])
1937    catch
1938	throw:Error ->
1939	    ?vlog("failure reading users config file: ~n   ~p", [Error]),
1940	    erlang:raise(throw, Error, erlang:get_stacktrace())
1941    end.
1942
1943check_user_config({Id, Mod, Data}) ->
1944    ?vtrace("check_user_config -> entry with"
1945	    "~n   Id:   ~p"
1946	    "~n   Mod:  ~p"
1947	    "~n   Data: ~p", [Id, Mod, Data]),
1948    check_user_config({Id, Mod, Data, []});
1949check_user_config({Id, Mod, Data, DefaultAgentConfig} = _User) 
1950  when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
1951    ?vtrace("check_user_config -> entry with"
1952	    "~n   Id:                 ~p"
1953	    "~n   Mod:                ~p"
1954	    "~n   Data:               ~p"
1955	    "~n   DefaultAgentConfig: ~p", 
1956	    [Id, Mod, Data, DefaultAgentConfig]),
1957    case (catch verify_user_behaviour(Mod)) of
1958	ok ->
1959	    ?vtrace("check_user_config -> user behaviour verified", []),
1960	    DefAgentConf =
1961		verify_default_agent_config(DefaultAgentConfig),
1962	    ?vtrace("check_user_config -> "
1963		    "user agent (default) config verified", []),
1964	    User2 = {Id, Mod, Data, DefAgentConf},
1965	    {ok, User2};
1966	Error ->
1967	    throw(Error)
1968    end;
1969check_user_config({Id, _Mod, _Data, DefaultAgentConfig}) 
1970  when (Id =/= ?DEFAULT_USER) ->
1971    error({bad_default_agent_config, DefaultAgentConfig});
1972check_user_config({Id, _Mod, _Data, _DefaultAgentConfig}) ->
1973    error({bad_user_id, Id});
1974check_user_config(User) ->
1975    error({bad_user_config, User}).
1976
1977init_users_config([]) ->
1978    ok;
1979init_users_config([User|Users]) ->
1980    init_user_config(User),
1981    init_users_config(Users).
1982
1983init_user_config(User) ->
1984    case (catch verify_user(User)) of
1985	{ok, UserRec} ->
1986	    case handle_register_user(UserRec) of
1987		ok ->
1988		    ok;
1989		{error, Reason} ->
1990		    error_msg("failed register user: "
1991			      "~n~w~n~w", [User, Reason])
1992	    end;
1993	{error, Reason} ->
1994	    error_msg("user config check failed: "
1995		      "~n~w~n~w", [User, Reason])
1996    end.
1997
1998verify_user({Id, UserMod, UserData}) ->
1999    verify_user({Id, UserMod, UserData, []});
2000verify_user({Id, UserMod, UserData, DefaultAgentConfig}) 
2001  when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
2002    ?d("verify_user -> entry with"
2003       "~n   Id:                 ~p"
2004       "~n   UserMod:            ~p"
2005       "~n   UserData:           ~p"
2006       "~n   DefaultAgentConfig: ~p", 
2007       [Id, UserMod, UserData, DefaultAgentConfig]),
2008    case (catch verify_user_behaviour(UserMod)) of
2009	ok ->
2010	    try
2011		{ok, SystemDefaultAgentConfig} = agent_info(),
2012		Config =
2013		    ensure_config(
2014		      SystemDefaultAgentConfig,
2015		      verify_default_agent_config(DefaultAgentConfig)),
2016%%		Config =
2017%%		    default_agent_config(
2018%%		      verify_default_agent_config(DefaultAgentConfig)),
2019		{ok, #user{id                   = Id,
2020			   mod                  = UserMod,
2021			   data                 = UserData,
2022			   default_agent_config = Config}}
2023	    catch
2024		Error ->
2025		    ?vdebug("verify_user default_agent_config -> throw"
2026			    "~n   Error: ~p", [Error]),
2027		    error({bad_default_agent_config, Error})
2028	    end;
2029	Error ->
2030	    throw(Error)
2031    end;
2032verify_user({Id, _UserMod, _UserData, DefaultAgentConfig}) 
2033  when (Id =/= ?DEFAULT_USER) ->
2034    {error, {bad_default_agent_config, DefaultAgentConfig}};
2035verify_user({Id, _, _, _}) ->
2036    {error, {bad_user_id, Id}}.
2037
2038verify_default_agent_config(Conf) ->
2039    try
2040	verify_illegal(
2041	  Conf,
2042	  [user_id, engine_id, address, tdomain, taddress]),
2043	verify_agent_config(Conf)
2044    catch
2045	Error ->
2046	    ?vdebug("verify_default_agent_config -> throw"
2047		    "~n   Error: ~p", [Error]),
2048	    error({bad_default_agent_config, Error})
2049    end.
2050
2051
2052read_usm_config_file(Dir) ->
2053    Order = fun snmp_conf:no_order/2,
2054    Check = fun (User, State) -> {check_usm_user_config(User), State} end,
2055    read_file(Dir, "usm.conf", Order, Check, []).
2056
2057%% Identity-function
2058check_usm_user_config({EngineId, Name, 
2059		       AuthP, AuthKey, 
2060		       PrivP, PrivKey}) ->
2061    User = {EngineId, Name, Name, AuthP, AuthKey, PrivP, PrivKey},
2062    verify_usm_user(User);
2063check_usm_user_config({_EngineId, _Name, _SecName, 
2064		       _AuthP, _AuthKey, 
2065		       _PrivP, _PrivKey} = User) ->
2066    verify_usm_user(User);
2067check_usm_user_config(User) ->
2068    error({bad_usm_config, User}).
2069
2070init_usm_users_config([]) ->
2071    ok;
2072init_usm_users_config([User|Users]) ->
2073    init_usm_user_config(User),
2074    init_usm_users_config(Users).
2075
2076init_usm_user_config(User) when is_record(User, usm_user) ->
2077    case handle_register_usm_user(User) of
2078	ok ->
2079	    ok;
2080	Error ->
2081	    throw(Error)
2082    end;
2083init_usm_user_config(BadUser) ->
2084    error({bad_usm_user, BadUser}).
2085
2086
2087verify_usm_user({EngineID, Name, SecName, AuthP, AuthKey, PrivP, PrivKey}) ->
2088    ?d("verify_usm_user -> entry with"
2089       "~n   EngineID: ~p"
2090       "~n   Name:     ~p"
2091       "~n   SecName:  ~p"
2092       "~n   AuthP:    ~p"
2093       "~n   AuthKey:  ~p"
2094       "~n   PrivP:    ~p"
2095       "~n   PrivKey:  ~p", 
2096       [EngineID, Name, SecName, AuthP, AuthKey, PrivP, PrivKey]),
2097    verify_usm_user_engine_id(EngineID),
2098    verify_usm_user_name(Name),
2099    verify_usm_user_sec_name(SecName),
2100    verify_usm_user(AuthP, AuthKey, PrivP, PrivKey),
2101    User = #usm_user{engine_id = EngineID,
2102		     name      = Name,
2103		     sec_name  = SecName,
2104		     auth      = AuthP, 
2105		     auth_key  = AuthKey,
2106		     priv      = PrivP, 
2107		     priv_key  = PrivKey},
2108    {ok, User}.
2109
2110verify_usm_user_engine_id(EngineID) ->
2111    case (catch snmp_conf:check_string(EngineID, {gt, 0})) of
2112	ok ->
2113	    ok;
2114	_ ->
2115	    error({bad_usm_engine_id, EngineID})
2116    end.
2117
2118verify_usm_user_name(Name) ->
2119    case (catch snmp_conf:check_string(Name, {gt, 0})) of
2120	ok ->
2121	    ok;
2122	_ ->
2123	    error({bad_usm_user_name, Name})
2124    end.
2125
2126verify_usm_user_sec_name(Name) ->
2127    case (catch snmp_conf:check_string(Name, {gt, 0})) of
2128	ok ->
2129	    ok;
2130	_ ->
2131	    error({bad_usm_sec_name, Name})
2132    end.
2133
2134verify_usm_user(AuthP, AuthKey, PrivP, PrivKey) ->
2135    verify_usm_user_auth(AuthP, AuthKey),
2136    verify_usm_user_priv(PrivP, PrivKey),
2137    ok.
2138
2139verify_usm_user_auth(usmNoAuthProtocol, AuthKey) ->
2140    case (catch snmp_conf:check_string(AuthKey, any)) of
2141	ok ->
2142	    ok;
2143	_ ->
2144	    error({invalid_auth_key, usmNoAuthProtocol})
2145    end;
2146verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) 
2147  when is_list(AuthKey) andalso (length(AuthKey) =:= 16) ->
2148    case is_crypto_supported(md5) of
2149	true -> 
2150	    case snmp_conf:all_integer(AuthKey) of
2151		true ->
2152		    ok;
2153		_ ->
2154		    error({invalid_auth_key, usmHMACMD5AuthProtocol})
2155	    end;
2156	false -> 
2157	    error({unsupported_crypto, md5})
2158    end;    
2159verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) when is_list(AuthKey) ->
2160    Len = length(AuthKey),
2161    error({invalid_auth_key, usmHMACMD5AuthProtocol, Len});
2162verify_usm_user_auth(usmHMACMD5AuthProtocol, _AuthKey) ->
2163    error({invalid_auth_key, usmHMACMD5AuthProtocol});
2164verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) 
2165  when is_list(AuthKey) andalso (length(AuthKey) =:= 20) ->
2166    case is_crypto_supported(sha) of
2167	true -> 
2168	    case snmp_conf:all_integer(AuthKey) of
2169		true ->
2170		    ok;
2171		_ ->
2172		    error({invalid_auth_key, usmHMACSHAAuthProtocol})
2173	    end;
2174	false -> 
2175	    error({unsupported_crypto, sha})
2176    end;
2177verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) when is_list(AuthKey) ->
2178    Len = length(AuthKey),
2179    error({invalid_auth_key, usmHMACSHAAuthProtocol, Len});
2180verify_usm_user_auth(usmHMACSHAAuthProtocol, _AuthKey) ->
2181    error({invalid_auth_key, usmHMACSHAAuthProtocol});
2182verify_usm_user_auth(AuthP, _AuthKey) ->
2183    error({invalid_auth_protocol, AuthP}).
2184    
2185verify_usm_user_priv(usmNoPrivProtocol, PrivKey) ->
2186    case (catch snmp_conf:check_string(PrivKey, any)) of
2187	ok ->
2188	    ok;
2189	_ ->
2190	    error({invalid_priv_key, usmNoPrivProtocol})
2191    end;
2192verify_usm_user_priv(usmDESPrivProtocol, PrivKey) 
2193  when (length(PrivKey) =:= 16) ->
2194    case is_crypto_supported(des_cbc) of
2195	true -> 
2196	    case snmp_conf:all_integer(PrivKey) of
2197		true ->
2198		    ok;
2199		_ ->
2200		    error({invalid_priv_key, usmDESPrivProtocol})
2201	    end;
2202	false -> 
2203	    error({unsupported_crypto, des_cbc})
2204    end;
2205verify_usm_user_priv(usmDESPrivProtocol, PrivKey) when is_list(PrivKey) ->
2206    Len = length(PrivKey),
2207    error({invalid_priv_key, usmDESPrivProtocol, Len});
2208verify_usm_user_priv(usmDESPrivProtocol, _PrivKey) ->
2209    error({invalid_priv_key, usmDESPrivProtocol});
2210verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) 
2211  when (length(PrivKey) =:= 16) ->
2212    case is_crypto_supported(aes_cfb128) of
2213	true -> 
2214	    case snmp_conf:all_integer(PrivKey) of
2215		true ->
2216		    ok;
2217		_ ->
2218		    error({invalid_priv_key, usmAesCfb128Protocol})
2219	    end;
2220	false -> 
2221	    error({unsupported_crypto, aes_cfb128})
2222    end;
2223verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) when is_list(PrivKey) ->
2224    Len = length(PrivKey),
2225    error({invalid_priv_key, usmAesCfb128Protocol, Len});
2226verify_usm_user_priv(usmAesCfb128Protocol, _PrivKey) ->
2227    error({invalid_priv_key, usmAesCfb128Protocol});
2228verify_usm_user_priv(PrivP, _PrivKey) ->
2229    error({invalid_priv_protocol, PrivP}).
2230    
2231
2232-compile({inline, [{is_crypto_supported,1}]}).
2233is_crypto_supported(Func) ->
2234    snmp_misc:is_crypto_supported(Func). 
2235 
2236
2237read_manager_config_file(Dir) ->
2238    Order = fun order_manager_config/2,
2239    Check = fun check_manager_config/2,
2240    Conf = read_file(Dir, "manager.conf", Order, Check),
2241    ?d("read_manager_config_file -> ok: "
2242       "~n   Conf: ~p", [Conf]),
2243    %% If the address is not specified, then we assume
2244    %% it should be the local host.
2245    %% If the address is not possible to determine
2246    %% that way, then we give up...
2247    verify_someof(Conf, [port, transports]),
2248    verify_mandatory(Conf, [engine_id, max_message_size]),
2249    default_manager_config(Conf).
2250
2251default_manager_config(Conf) ->
2252    %% Ensure valid transports entry
2253    case lists:keyfind(transports, 1, Conf) of
2254	false ->
2255	    {port, Port} = lists:keyfind(port, 1, Conf),
2256	    Domain =
2257		case lists:keyfind(domain, 1, Conf) of
2258		    false ->
2259			default_transport_domain();
2260		    {_, D} ->
2261			D
2262		end,
2263	    Family = snmp_conf:tdomain_to_family(Domain),
2264	    {ok, Hostname} = inet:gethostname(),
2265	    case inet:getaddr(Hostname, Family) of
2266		{ok, Address} ->
2267		    lists:sort(
2268		      fun order_manager_config/2,
2269		      [{transports, [{Domain, {Address, Port}}]} | Conf]);
2270		{error, _Reason} ->
2271		    ?d("default_manager_config -> "
2272		       "failed getting ~w address for ~s:~n"
2273		       "   _Reason: ~p", [Family, Hostname, _Reason]),
2274		    Conf
2275	    end;
2276	_ ->
2277	    Conf
2278    end.
2279
2280order_manager_config(EntryA, EntryB) ->
2281    snmp_conf:keyorder(1, EntryA, EntryB, [domain, port]).
2282
2283check_manager_config(Entry, undefined) ->
2284    check_manager_config(Entry, {default_transport_domain(), undefined});
2285check_manager_config({domain, Domain}, {_, Port}) ->
2286    {snmp_conf:check_domain(Domain), {Domain, Port}};
2287check_manager_config({port, Port}, {Domain, _}) ->
2288    {ok = snmp_conf:check_port(Port), {Domain, Port}};
2289check_manager_config({address, _}, {_, undefined}) ->
2290    error({missing_mandatory, port});
2291check_manager_config({address = Tag, Ip} = Entry, {Domain, Port} = State) ->
2292    {case snmp_conf:check_ip(Domain, Ip) of
2293	 ok ->
2294	     [Entry,
2295	      {transports, [{Domain, {Ip, Port}}]}];
2296	 {ok, FixedIp} ->
2297	     [{Tag, FixedIp},
2298	      {transports, [{Domain, {FixedIp, Port}}]}]
2299     end, State};
2300check_manager_config({transports = Tag, Transports}, {_, Port} = State)
2301  when is_list(Transports) ->
2302    CheckedTransports =
2303	[case Transport of
2304	     {Domain, Address} ->
2305		 case
2306		     case Port of
2307			 undefined ->
2308			     snmp_conf:check_address(Domain, Address);
2309			 _ ->
2310			     snmp_conf:check_address(Domain, Address, Port)
2311		     end
2312		 of
2313		     ok ->
2314			 Transport;
2315		     {ok, FixedAddress} ->
2316			 {Domain, FixedAddress}
2317		 end;
2318	     _Domain when Port =:= undefined->
2319		 error({missing_mandatory, port});
2320	     Domain ->
2321		 Family = snmp_conf:tdomain_to_family(Domain),
2322		 {ok, Hostname} = inet:gethostname(),
2323		 case inet:getaddr(Hostname, Family) of
2324		     {ok, IpAddr} ->
2325			 {Domain, {IpAddr, Port}};
2326		     {error, _} ->
2327			 error({bad_address, {Domain, Hostname}})
2328		 end
2329	 end
2330	 || Transport <- Transports],
2331    {{ok, {Tag, CheckedTransports}}, State};
2332check_manager_config(Entry, State) ->
2333    {check_manager_config(Entry), State}.
2334
2335check_manager_config({engine_id, EngineID}) ->
2336    snmp_conf:check_string(EngineID);
2337check_manager_config({max_message_size, Max}) ->
2338    snmp_conf:check_integer(Max, {gte, 484});
2339check_manager_config(Conf) ->
2340    error({unknown_config, Conf}).
2341
2342
2343read_file(Dir, FileName, Order, Check, Default) ->
2344    try snmp_conf:read(filename:join(Dir, FileName), Order, Check)
2345    catch
2346	{error, Reason} when element(1, Reason) =:= failed_open ->
2347	    ?vlog("failed reading config from ~s: ~p", [FileName, Reason]),
2348	    Default
2349    end.
2350
2351read_file(Dir, FileName, Order, Check) ->
2352    try snmp_conf:read(filename:join(Dir, FileName), Order, Check)
2353    catch
2354	throw:{error, Reason} = Error
2355	  when element(1, Reason) =:= failed_open ->
2356	    error_msg("failed reading config from ~s: ~p", [FileName, Reason]),
2357	    erlang:raise(throw, Error, erlang:get_stacktrace())
2358    end.
2359
2360%%--------------------------------------------------------------------
2361%% Func: handle_call/3
2362%% Returns: {reply, Reply, State}          |
2363%%          {reply, Reply, State, Timeout} |
2364%%          {noreply, State}               |
2365%%          {noreply, State, Timeout}      |
2366%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
2367%%          {stop, Reason, State}            (terminate/2 is called)
2368%%--------------------------------------------------------------------
2369handle_call({register_user, UserId, UserMod, UserData, DefaultAgentConfig}, 
2370	    _From, State) ->
2371    ?vlog("received register_user request: "
2372	  "~n   UserId:             ~p"
2373	  "~n   UserMod:            ~p"
2374	  "~n   UserData:           ~p"
2375	  "~n   DefaultAgentConfig: ~p", 
2376	  [UserId, UserMod, UserData, DefaultAgentConfig]),
2377    User  = #user{id                   = UserId, 
2378		  mod                  = UserMod, 
2379		  data                 = UserData,
2380		  default_agent_config = DefaultAgentConfig},
2381    Reply = handle_register_user(User),
2382    {reply, Reply, State};
2383
2384handle_call({unregister_user, UserId}, _From, State) ->
2385    ?vlog("received unregister_user request: "
2386	  "~n   UserId: ~p", [UserId]),
2387    Reply = handle_unregister_user(UserId),
2388    {reply, Reply, State};
2389
2390handle_call({register_agent, UserId, TargetName, Config}, _From, State) ->
2391    ?vlog("received register_agent request: "
2392	  "~n   UserId:     ~p"
2393	  "~n   TargetName: ~p"
2394	  "~n   Config:     ~p", [UserId, TargetName, Config]),
2395    Reply = handle_register_agent(UserId, TargetName, Config),
2396    {reply, Reply, State};
2397
2398handle_call({unregister_agent, UserId, TargetName}, _From, State) ->
2399    ?vlog("received unregister_agent request: "
2400	  "~n   UserId:     ~p"
2401	  "~n   TargetName: ~p", [UserId, TargetName]),
2402    Reply = handle_unregister_agent(UserId, TargetName),
2403    {reply, Reply, State};
2404
2405handle_call({update_agent_info, UserId, TargetName, Info}, 
2406	    _From, State) ->
2407    ?vlog("received update_agent_info request: "
2408	  "~n   UserId:     ~p"
2409	  "~n   TargetName: ~p"
2410	  "~n   Info:       ~p", [UserId, TargetName, Info]),
2411    Reply = handle_update_agent_info(UserId, TargetName, Info),
2412    {reply, Reply, State};
2413
2414%% <BACKWARD-COMPAT>
2415handle_call({update_agent_info, UserId, TargetName, Item, Val}, 
2416	    _From, State) ->
2417    ?vlog("received update_agent_info request: "
2418	  "~n   UserId:     ~p"
2419	  "~n   TargetName: ~p"
2420	  "~n   Item:       ~p"
2421	  "~n   Val:        ~p", [UserId, TargetName, Item, Val]),
2422    Reply = handle_update_agent_info(UserId, TargetName, Item, Val),
2423    {reply, Reply, State};
2424%% </BACKWARD-COMPAT>
2425
2426handle_call({register_usm_user, User}, _From, State) ->
2427    ?vlog("received register_usm_user request: "
2428	  "~n   User: ~p", [User]),
2429    Reply = handle_register_usm_user(User),
2430    {reply, Reply, State};
2431
2432handle_call({unregister_usm_user, EngineID, Name}, _From, State) ->
2433    ?vlog("received register_usm_user request: "
2434	  "~n   EngineID: ~p"
2435	  "~n   Name:     ~p", [EngineID, Name]),
2436    Reply = handle_unregister_usm_user(EngineID, Name),
2437    {reply, Reply, State};
2438
2439handle_call({update_usm_user_info, EngineID, UserName, Item, Val}, 
2440	    _From, State) ->
2441    ?vlog("received update_usm_user_info request: "
2442	  "~n   EngineID: ~p"
2443	  "~n   UserName: ~p"
2444	  "~n   Item:     ~p"
2445	  "~n   Val:      ~p", [EngineID, UserName, Item, Val]),
2446    Reply = handle_update_usm_user_info(EngineID, UserName, Item, Val),
2447    {reply, Reply, State};
2448
2449handle_call({cre_counter, Counter, Initial}, _From, State) ->
2450    ?vlog("received cre_counter ~p -> ~w", [Counter, Initial]),
2451    Reply = cre_counter(Counter, Initial),
2452    {reply, Reply, State};
2453
2454handle_call({cre_stats_counter, Counter, Initial}, _From, State) ->
2455    ?vlog("received cre_stats_counter ~p -> ~w", [Counter, Initial]),
2456    Reply = cre_stats_counter(Counter, Initial),
2457    {reply, Reply, State};
2458
2459handle_call({reset_stats_counter, Counter}, _From, State) ->
2460    ?vlog("received reset_stats_counter ~p", [Counter]),
2461    Reply = reset_stats_counter(Counter),
2462    {reply, Reply, State};
2463
2464handle_call({load_mib, Mib}, _From, State) ->
2465    ?vlog("received load_mib ~p", [Mib]),
2466    case handle_load_mib(Mib) of
2467	ok ->
2468	    {reply, ok, State};
2469	Error ->
2470	    {reply, Error, State}
2471    end;
2472
2473
2474handle_call({unload_mib, Mib}, _From, State) ->
2475    ?vlog("received unload_mib ~p", [Mib]),
2476    case handle_unload_mib(Mib) of
2477	ok ->
2478	    {reply, ok, State};
2479	Error ->
2480	    {reply, Error, State}
2481    end;
2482
2483
2484handle_call({set_engine_boots, Boots}, _From, State) ->
2485    ?vlog("received set_engine_boots ~p", [Boots]),
2486    set_engine_boots(Boots),
2487    {reply, ok, State};
2488
2489handle_call({set_engine_time, Time}, _From, State) ->
2490    ?vlog("received set_engine_time ~p", [Time]),
2491    Base = snmp_misc:now(sec) - Time,
2492    ets:insert(snmpm_config_table, {snmp_engine_base, Base}),
2493    {reply, ok, State};
2494
2495handle_call({set_usm_cache, Key, Val}, _From, State) ->
2496    ?vlog("received set_usm_cache: ~w -> ~p", [Key, Val]),
2497    ets:insert(snmpm_usm_table, {{usm_cache, Key}, Val}),
2498    {reply, ok, State};
2499
2500handle_call({reset_usm_cache, EngineID}, _From, State) ->
2501    ?vlog("received reset_usm_cache: ~p", [EngineID]),
2502    reset_usm_cache(EngineID),
2503    {reply, ok, State};
2504
2505handle_call({verbosity, Verbosity}, _From, State) ->
2506    ?vlog("received verbosity request", []),
2507    put(verbosity, Verbosity),
2508    {reply, ok, State};
2509
2510handle_call(info, _From, State) ->
2511    ?vlog("received info request", []),
2512    Reply = get_info(),
2513    {reply, Reply, State};
2514
2515handle_call({backup, BackupDir}, From, State) ->
2516    ?vlog("backup to ~p", [BackupDir]),
2517    Pid = self(),
2518    V   = get(verbosity),
2519    case file:read_file_info(BackupDir) of
2520        {ok, #file_info{type = directory}} ->
2521            BackupServer =
2522                erlang:spawn_link(
2523                  fun() ->
2524                          put(sname, mcbs),
2525                          put(verbosity, V),
2526                          Dir   = filename:join([BackupDir]),
2527                          Reply = handle_backup(?CONFIG_DB, Dir),
2528                          Pid ! {backup_done, Reply},
2529                          unlink(Pid)
2530                  end),
2531            ?vtrace("backup server: ~p", [BackupServer]),
2532            {noreply, State#state{backup = {BackupServer, From}}};
2533        {ok, _} ->
2534            {reply, {error, not_a_directory}, State};
2535        Error ->
2536            {reply, Error, State}
2537    end;
2538
2539
2540%% handle_call({update_system_info, Key, Val}, _From, State) ->
2541%%     ?vlog("received update_system_info: ~p -> ~p", [Key, Val]),
2542%%     Reply = handle_update_system_info(Key, Val),
2543%%     {reply, Reply, State};
2544
2545
2546handle_call(is_started, _From, State) ->
2547    ?vlog("received is_started request", []),
2548    {reply, true, State};
2549
2550
2551handle_call(stop, _From, State) ->
2552    {stop, normal, ok, State};
2553
2554
2555handle_call(Req, _From, State) ->
2556    warning_msg("received unknown request: ~n~p", [Req]),
2557    {reply, {error, unknown_request}, State}.
2558
2559
2560%%--------------------------------------------------------------------
2561%% Func: handle_cast/2
2562%% Returns: {noreply, State}          |
2563%%          {noreply, State, Timeout} |
2564%%          {stop, Reason, State}            (terminate/2 is called)
2565%%--------------------------------------------------------------------
2566handle_cast(Msg, State) ->
2567    warning_msg("received unknown message: ~n~p", [Msg]),
2568    {noreply, State}.
2569
2570
2571%%--------------------------------------------------------------------
2572%% Func: handle_info/2
2573%% Returns: {noreply, State}          |
2574%%          {noreply, State, Timeout} |
2575%%          {stop, Reason, State}            (terminate/2 is called)
2576%%--------------------------------------------------------------------
2577handle_info({'EXIT', Pid, Reason}, #state{backup = {Pid, From}} = S) ->
2578    ?vlog("backup server (~p) exited for reason ~n~p", [Pid, Reason]),
2579    gen_server:reply(From, {error, Reason}),
2580    {noreply, S#state{backup = undefined}};
2581
2582handle_info({'EXIT', Pid, Reason}, S) ->
2583    %% The only other processes we should be linked to are
2584    %% either the server or our supervisor, so die...
2585    {stop, {received_exit, Pid, Reason}, S};
2586
2587handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) ->
2588    ?vlog("backup done:"
2589          "~n   Reply: ~p", [Reply]),
2590    gen_server:reply(From, Reply),
2591    {noreply, S#state{backup = undefined}};
2592
2593handle_info(Info, State) ->
2594    warning_msg("received unknown info: ~n~p", [Info]),
2595    {noreply, State}.
2596
2597
2598%%--------------------------------------------------------------------
2599%% Func: terminate/2
2600%% Purpose: Shutdown the server
2601%% Returns: any (ignored by gen_server)
2602%%--------------------------------------------------------------------
2603terminate(Reason, _State) ->
2604    ?vdebug("terminate: ~p",[Reason]),
2605    ok.
2606
2607%%----------------------------------------------------------------------
2608%% Func: code_change/3
2609%% Purpose: Convert process state when code is changed
2610%% Returns: {ok, NewState}
2611%%----------------------------------------------------------------------
2612
2613%% downgrade
2614%%
2615code_change({down, _Vsn}, S1, downgrade_to_pre_4_7) ->
2616    #state{backup = B} = S1,
2617    stop_backup_server(B),
2618    S2 = {state},
2619    {ok, S2};
2620
2621%% upgrade
2622%%
2623code_change(_Vsn, _S1, upgrade_from_pre_4_7) ->
2624    %% {state} = S1,
2625    S2 = #state{},
2626    {ok, S2};
2627
2628code_change(_Vsn, State, _Extra) ->
2629    {ok, State}.
2630
2631
2632stop_backup_server(undefined) ->
2633    ok;
2634stop_backup_server({Pid, _}) when is_pid(Pid) ->
2635    exit(Pid, kill).
2636
2637
2638
2639%%----------------------------------------------------------
2640%% Update system info
2641%%----------------------------------------------------------
2642
2643%% handle_update_system_info(audit_trail_log_type = Key, Val) ->
2644%%     case snmpm_config:system_info(audit_trail_log) of
2645%% 	{ok, true} ->
2646%% 	    Value = 
2647%% 		case Val of
2648%% 		    read ->
2649%% 			{ok, [read]};
2650%% 		    write ->
2651%% 			{ok, [write]};
2652%% 		    read_write ->
2653%% 			{ok, [read,write]};
2654%% 		    _ ->
2655%% 			{error, {bad_value, Key, Val}}
2656%% 		end,
2657%% 	    case Value of
2658%% 		{ok, NewValue} ->
2659%% 		    ets:insert(snmpm_config_table, {Key, NewValue}),
2660%% 		    ok;
2661%% 		false ->
2662%% 		    Value
2663%% 	    end;
2664%% 	_ ->
2665%% 	    {error, audit_trail_log_not_enabled}
2666%%     end;
2667%% handle_update_system_info(BadKey, Val) ->
2668%%     {error, {unsupported_update, BadKey, Val}}.
2669
2670
2671%%----------------------------------------------------------
2672%% Backup
2673%%----------------------------------------------------------
2674
2675handle_backup(D, BackupDir) ->
2676    %% First check that we do not wrote to the corrent db-dir...
2677    ?vtrace("handle_backup -> entry with"
2678        "~n   D:         ~p"
2679        "~n   BackupDir: ~p", [D, BackupDir]),
2680    case dets:info(D, filename) of
2681        undefined ->
2682            ?vinfo("handle_backup -> no file to backup", []),
2683            {error, no_file};
2684        Filename ->
2685            ?vinfo("handle_backup -> file to backup: ~n   ~p", [Filename]),
2686            case filename:dirname(Filename) of
2687                BackupDir ->
2688                    ?vinfo("handle_backup -> backup dir and db dir the same",
2689                           []),
2690                    {error, db_dir};
2691                _ ->
2692                    case file:read_file_info(BackupDir) of
2693                        {ok, #file_info{type = directory}} ->
2694                            ?vdebug("handle_backup -> backup dir ok", []),
2695                            %% All well so far...
2696                            Type = dets:info(D, type),
2697                            KP   = dets:info(D, keypos),
2698                            dets_backup(D,
2699                                        filename:basename(Filename),
2700                                        BackupDir, Type, KP);
2701                        {ok, _} ->
2702                            ?vinfo("handle_backup -> backup dir not a dir",
2703                                   []),
2704                            {error, not_a_directory};
2705                        Error ->
2706                            ?vinfo("handle_backup -> Error: ~p", [Error]),
2707                            Error
2708                    end
2709            end
2710    end.
2711
2712dets_backup(D, Filename, BackupDir, Type, KP) ->
2713    ?vtrace("dets_backup -> entry with"
2714            "~n   D:         ~p"
2715            "~n   Filename:  ~p"
2716            "~n   BackupDir: ~p", [D, Filename, BackupDir]),
2717    BackupFile = filename:join(BackupDir, Filename),
2718    ?vtrace("dets_backup -> "
2719            "~n   BackupFile: ~p", [BackupFile]),
2720    Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}],
2721    case dets:open_file(?BACKUP_DB, Opts) of
2722        {ok, B} ->
2723            ?vtrace("dets_backup -> create fun", []),
2724            F = fun(Arg) ->
2725                        dets_backup(Arg, start, D, B)
2726                end,
2727            dets:safe_fixtable(D, true),
2728            Res = dets:init_table(?BACKUP_DB, F, [{format, bchunk}]),
2729            dets:safe_fixtable(D, false),
2730            ?vtrace("dets_backup -> Res: ~p", [Res]),
2731            Res;
2732        Error ->
2733            ?vinfo("dets_backup -> open_file failed: "
2734                   "~n   ~p", [Error]),
2735            Error
2736    end.
2737
2738
2739dets_backup(close, _Cont, _D, B) ->
2740    dets:close(B),
2741    ok;
2742dets_backup(read, Cont1, D, B) ->
2743    case dets:bchunk(D, Cont1) of
2744        {Cont2, Data} ->
2745            F = fun(Arg) ->
2746                        dets_backup(Arg, Cont2, D, B)
2747                end,
2748            {Data, F};
2749        '$end_of_table' ->
2750            dets:close(B),
2751            end_of_input;
2752        Error ->
2753            Error
2754    end.
2755
2756
2757%%%-------------------------------------------------------------------
2758%%% Internal functions
2759%%%-------------------------------------------------------------------
2760
2761handle_register_user(#user{id = Id} = User) ->
2762    ?vdebug("handle_register_user -> entry with"
2763	    "~n   User: ~p", [User]),
2764    case ets:lookup(snmpm_user_table, Id) of
2765	[] ->
2766	    ets:insert(snmpm_user_table, User),
2767	    ok;
2768	_ ->
2769	    {error, {already_registered, User}}
2770    end.
2771
2772handle_unregister_user(UserId) ->
2773    ?vdebug("handle_unregister_user -> entry with"
2774	    "~n   UserId: ~p", [UserId]),
2775    ets:delete(snmpm_user_table, UserId),
2776    ok.
2777
2778
2779handle_register_agent(UserId, TargetName, Config) ->
2780    ?vdebug("handle_register_agent -> entry with"
2781	    "~n   UserId:     ~p"
2782	    "~n   TargetName: ~p"
2783	    "~n   Config:     ~p", [UserId, TargetName, Config]),
2784    case (catch agent_info(TargetName, user_id)) of
2785	{error, _} ->
2786	    ?vtrace(
2787	       "handle_register_agent -> user_id not found in config", []),
2788	    case ets:lookup(snmpm_user_table, UserId) of
2789		[#user{default_agent_config = DefConfig}] ->
2790		    ?vtrace("handle_register_agent ->~n"
2791			    "   DefConfig: ~p", [DefConfig]),
2792		    FixedConfig =
2793			fix_agent_config(ensure_config(DefConfig, Config)),
2794		    ?vtrace("handle_register_agent ->~n"
2795			    "   FixedConfig: ~p", [FixedConfig]),
2796		    do_handle_register_agent(
2797		      TargetName, [{user_id, UserId}|FixedConfig]),
2798		    %% <DIRTY-BACKWARD-COMPATIBILLITY>
2799		    %% And now for some (backward compatibillity)
2800		    %% dirty crossref stuff
2801		    {value, {_, Domain}} =
2802			lists:keysearch(tdomain, 1, FixedConfig),
2803		    {value, {_, Address}} =
2804			lists:keysearch(taddress, 1, FixedConfig),
2805		    ?vtrace(
2806		       "handle_register_agent -> register cross-ref fix", []),
2807		    ets:insert(snmpm_agent_table,
2808			       {{Domain, Address, target_name}, TargetName}),
2809		    %% </DIRTY-BACKWARD-COMPATIBILLITY>
2810
2811%%		    %% First, insert this users default config
2812%%		    ?vtrace("handle_register_agent -> store default config", []),
2813%%		    do_handle_register_agent(TargetName, DefConfig),
2814%%		    %% Second, insert the config for this agent
2815%%		    ?vtrace("handle_register_agent -> store config", []),
2816%%		    do_handle_register_agent(TargetName,
2817%%					     [{user_id, UserId}|Config]),
2818%%		    %% <DIRTY-BACKWARD-COMPATIBILLITY>
2819%%		    %% And now for some (backward compatibillity)
2820%%		    %% dirty crossref stuff
2821%%		    ?vtrace("handle_register_agent -> lookup taddress", []),
2822%%		    {ok, {Addr, Port} = TAddress} =
2823%%			agent_info(TargetName, taddress),
2824%%		    ?vtrace("handle_register_agent -> taddress: ~p",
2825%%			    [TAddress]),
2826%%		    ?vtrace("handle_register_agent -> register cross-ref fix", []),
2827%%		    ets:insert(snmpm_agent_table,
2828%%			       {{Addr, Port, target_name}, TargetName}),
2829%%		    %% </DIRTY-BACKWARD-COMPATIBILLITY>
2830		    ok;
2831		_ ->
2832		    {error, {not_found, UserId}}
2833	    end;
2834	{ok, UserId} ->
2835	    ?vinfo("[~w] Agent (~p) already registered"
2836		   "~nwhen"
2837		   "~n   Agents: ~p", 
2838		   [UserId, TargetName, which_agents()]),
2839	    {error, {already_registered, TargetName}};
2840	{ok, OtherUserId} ->
2841	    ?vinfo("[~w] Agent (~p) already registered to ~p"
2842		   "~nwhen"
2843		   "~n   Agents: ~p", 
2844		   [UserId, TargetName, OtherUserId, which_agents()]),
2845	    {error, {already_registered, TargetName, OtherUserId}}
2846    end.
2847
2848do_handle_register_agent(_TargetName, []) ->
2849    ok;
2850do_handle_register_agent(TargetName, [{Item, Val}|Rest]) ->
2851    ?vtrace("do_handle_register_agent -> entry with"
2852	    "~n   TargetName: ~p"
2853	    "~n   Item:       ~p"
2854	    "~n   Val:        ~p"
2855	    "~n   Rest:       ~p", [TargetName, Item, Val, Rest]),
2856    case (catch do_update_agent_info(TargetName, Item, Val)) of
2857	ok ->
2858	    do_handle_register_agent(TargetName, Rest);
2859	{error, Reason} ->
2860	    ?vtrace("do_handle_register_agent -> failed updating ~p"
2861		    "~n   Item:   ~p"
2862		    "~n   Reason: ~p", [Item, Reason]),
2863	    ets:match_delete(snmpm_agent_table, {TargetName, '_'}),
2864	    {error, Reason}
2865    end;
2866do_handle_register_agent(TargetName, BadConfig) ->
2867    error_msg("error during agent registration - bad config: ~n~p", 
2868	      [BadConfig]),
2869    ets:match_delete(snmpm_agent_table, {TargetName, '_'}),
2870    {error, {bad_agent_config, TargetName, BadConfig}}.
2871
2872
2873handle_unregister_agent(UserId, TargetName) ->
2874    ?vdebug("handle_unregister_agent -> entry with"
2875	    "~n   UserId:     ~p"
2876	    "~n   TargetName: ~p", [UserId, TargetName]),
2877    case (catch agent_info(TargetName, user_id)) of
2878	{ok, UserId} ->
2879	    {ok, EngineID} = agent_info(TargetName, engine_id),
2880	    reset_usm_cache(EngineID),
2881	    %% <DIRTY-BACKWARD-COMPATIBILLITY>
2882	    %% And now for some (backward compatibillity) 
2883	    %% dirty crossref stuff
2884	    {ok, Domain} = agent_info(TargetName, tdomain),
2885	    {ok, Address} = agent_info(TargetName, taddress),
2886	    ets:delete(snmpm_agent_table, {Domain, Address, target_name}),
2887	    %% </DIRTY-BACKWARD-COMPATIBILLITY>
2888	    ets:match_delete(snmpm_agent_table, {{TargetName, '_'}, '_'}),
2889	    ok;
2890	{ok, OtherUserId} ->
2891	    {error, {not_owner, OtherUserId}};
2892	Error ->
2893	    Error
2894    end.
2895
2896
2897handle_update_agent_info(UserId, TargetName, Info) ->
2898    ?vdebug("handle_update_agent_info -> entry with"
2899	    "~n   UserId:     ~p"
2900	    "~n   TargetName: ~p"
2901	    "~n   Info:       ~p", [UserId, TargetName, Info]),
2902    %% Verify ownership
2903    case (catch agent_info(TargetName, user_id)) of
2904	{ok, UserId} -> 
2905	    handle_update_agent_info(TargetName, Info);
2906	{ok, OtherUserId} ->
2907	    {error, {not_owner, OtherUserId}};
2908	Error ->
2909	    Error
2910    end.
2911
2912handle_update_agent_info(TargetName, Info) ->
2913    ?vtrace("handle_update_agent_info -> entry with"
2914	    "~n   TargetName: ~p"
2915	    "~n   Info:      ~p", [TargetName, Info]),
2916    %% Verify info
2917    try
2918	verify_illegal(Info, [user_id]),
2919	%% If port or domain is part of the info, then use it.
2920	%% If not, lookup what is already stored for
2921	%% this agent and use that.
2922	do_update_agent_info(
2923	  TargetName,
2924	  fix_agent_config(
2925	    verify_agent_config(
2926	      ensure_agent_info(TargetName, [port,tdomain], Info))))
2927    catch
2928	Error ->
2929	    Error;
2930	T:E ->
2931	    {error, {failed_info_verification, Info, T, E}}
2932    end.
2933
2934handle_update_agent_info(UserId, TargetName, Item, Val) ->
2935    ?vdebug("handle_update_agent_info -> entry with"
2936	    "~n   UserId:     ~p"
2937	    "~n   TargetName: ~p"
2938	    "~n   Item:       ~p"
2939	    "~n   Val:        ~p", [UserId, TargetName, Item, Val]),
2940    handle_update_agent_info(TargetName, [{Item, Val}]).
2941
2942do_update_agent_info(TargetName, Info) ->
2943    ?vtrace("do_update_agent_info -> entry with~n"
2944	    "   TargetName: ~p~n"
2945	    "   Info:       ~p", [TargetName,Info]),
2946    InsertItem = 
2947	fun({Item, Val}) -> 
2948		ets:insert(snmpm_agent_table, {{TargetName, Item}, Val})
2949	end,
2950    lists:foreach(InsertItem, Info).
2951
2952do_update_agent_info(TargetName, Item, Val) ->
2953    ?vtrace("do_update_agent_info -> entry with"
2954	    "~n   TargetName: ~p"
2955	    "~n   Item:       ~p"
2956	    "~n   Val:       ~p", [TargetName, Item, Val]),
2957    ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}),
2958    ok.
2959
2960
2961handle_register_usm_user(#usm_user{engine_id = EngineID, 
2962				   name      = Name} = User) ->
2963    ?vdebug("handle_register_usm_user -> entry with"
2964	    "~n   User: ~p", [User]),
2965    Key = usm_key(EngineID, Name),
2966    case ets:lookup(snmpm_usm_table, Key) of
2967	[] ->
2968	    do_update_usm_user_info(Key, User);
2969	_ ->
2970	    {error, {already_registered, EngineID, Name}}
2971    end;
2972handle_register_usm_user(BadUsmUser) ->
2973    {error, {bad_usm_user, BadUsmUser}}.
2974	
2975handle_unregister_usm_user(EngineID, Name) ->
2976    ?vdebug("handle_unregister_usm_user -> entry with"
2977	    "~n   EngineID: ~p"
2978	    "~n   Name:     ~p", [EngineID, Name]),
2979    Key = usm_key(EngineID, Name),
2980    ets:delete(snmpm_usm_table, Key),
2981    ok.
2982	
2983
2984handle_update_usm_user_info(EngineID, Name, Item, Val) ->
2985    ?vdebug("handle_update_usm_user_info -> entry with"
2986	    "~n   EngineID: ~p"
2987	    "~n   Name:     ~p"
2988	    "~n   Item:     ~p"
2989	    "~n   Val:      ~p", [EngineID, Name, Item, Val]),
2990    Key = usm_key(EngineID, Name),
2991    case ets:lookup(snmpm_usm_table, Key) of
2992	[] ->
2993	    {error, not_found};
2994	[{_Key, User}] ->
2995	    do_update_usm_user_info(Key, User, Item, Val)
2996    end.
2997
2998do_update_usm_user_info(Key, User, sec_name, Val) ->
2999    %% case verify_usm_user_sec_name(Val) of
3000    %%     ok ->
3001    %% 	do_update_usm_user_info(Key, User#usm_user{sec_name = Val});
3002    %%     _ ->
3003    %% 	{error, {invalid_usm_sec_name, Val}}
3004    %% end;
3005    ok = verify_usm_user_sec_name(Val),
3006    do_update_usm_user_info(Key, User#usm_user{sec_name = Val});
3007do_update_usm_user_info(Key, User, auth, Val) 
3008  when (Val =:= usmNoAuthProtocol) orelse 
3009       (Val =:= usmHMACMD5AuthProtocol) orelse
3010       (Val =:= usmHMACSHAAuthProtocol) ->
3011    do_update_usm_user_info(Key, User#usm_user{auth = Val});
3012do_update_usm_user_info(_Key, _User, auth, Val) ->
3013    {error, {invalid_auth_protocol, Val}};
3014do_update_usm_user_info(Key, 
3015			#usm_user{auth = usmNoAuthProtocol} = User, 
3016			auth_key, Val) ->
3017    case (catch snmp_conf:check_string(Val, any)) of
3018	ok ->
3019	    do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
3020	_ ->
3021	    {error, {invalid_auth_key, Val}}
3022    end;
3023do_update_usm_user_info(Key, 
3024			#usm_user{auth = usmHMACMD5AuthProtocol} = User, 
3025			auth_key, Val) 
3026  when length(Val) =:= 16 ->
3027    case is_crypto_supported(md5) of
3028	true -> 
3029	    do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
3030	false -> 
3031	    {error, {unsupported_crypto, md5}}
3032    end;    
3033do_update_usm_user_info(_Key, 
3034			#usm_user{auth = usmHMACMD5AuthProtocol}, 
3035			auth_key, Val) when is_list(Val) ->
3036    Len = length(Val),
3037    {error, {invalid_auth_key_length, usmHMACMD5AuthProtocol, Len}};
3038do_update_usm_user_info(_Key, 
3039			#usm_user{auth = usmHMACMD5AuthProtocol}, 
3040			auth_key, Val) ->
3041    {error, {invalid_auth_key, usmHMACMD5AuthProtocol, Val}};
3042do_update_usm_user_info(Key, 
3043			#usm_user{auth = usmHMACSHAAuthProtocol} = User, 
3044			auth_key, Val) 
3045  when length(Val) =:= 20 ->
3046    case is_crypto_supported(sha) of
3047	true -> 
3048	    do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
3049	false -> 
3050	    {error, {unsupported_crypto, sha}}
3051    end;    
3052do_update_usm_user_info(_Key, 
3053			#usm_user{auth = usmHMACSHAAuthProtocol}, 
3054			auth_key, Val) when is_list(Val) ->
3055    Len = length(Val),
3056    {error, {invalid_auth_key_length, usmHMACSHAAuthProtocol, Len}};
3057do_update_usm_user_info(_Key, 
3058			#usm_user{auth = usmHMACSHAAuthProtocol}, 
3059			auth_key, Val) ->
3060    {error, {invalid_auth_key, usmHMACSHAAuthProtocol, Val}};
3061do_update_usm_user_info(Key, User, priv, Val) 
3062  when (Val =:= usmNoPrivProtocol) orelse 
3063       (Val =:= usmDESPrivProtocol) orelse
3064       (Val =:= usmAesCfb128Protocol) ->
3065    do_update_usm_user_info(Key, User#usm_user{priv = Val});
3066do_update_usm_user_info(_Key, _User, priv, Val) ->
3067    {error, {invalid_priv_protocol, Val}};
3068do_update_usm_user_info(Key, 
3069			#usm_user{priv = usmNoPrivProtocol} = User, 
3070			priv_key, Val) ->
3071    case (catch snmp_conf:check_string(Val, any)) of
3072	ok ->
3073	    do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
3074	_ ->
3075	    {error, {invalid_priv_key, Val}}
3076    end;
3077do_update_usm_user_info(Key, 
3078			#usm_user{priv = usmDESPrivProtocol} = User, 
3079			priv_key, Val) 
3080  when length(Val) =:= 16 ->
3081    case is_crypto_supported(des_cbc) of
3082	true -> 
3083	    do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
3084	false -> 
3085	    {error, {unsupported_crypto, des_cbc}}
3086    end;    
3087do_update_usm_user_info(Key, 
3088			#usm_user{priv = usmAesCfb128Protocoll} = User, 
3089			priv_key, Val) 
3090  when length(Val) =:= 16 ->
3091    case is_crypto_supported(aes_cfb128) of
3092	true -> 
3093	    do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
3094	false -> 
3095	    {error, {unsupported_crypto, aes_cfb128}}
3096    end;    
3097do_update_usm_user_info(_Key, 
3098			#usm_user{auth = usmHMACSHAAuthProtocol}, 
3099			priv_key, Val) when is_list(Val) ->
3100    Len = length(Val),
3101    {error, {invalid_priv_key_length, usmHMACSHAAuthProtocol, Len}};
3102do_update_usm_user_info(_Key, 
3103			#usm_user{auth = usmHMACSHAAuthProtocol}, 
3104			priv_key, Val) ->
3105    {error, {invalid_priv_key, usmHMACSHAAuthProtocol, Val}};
3106do_update_usm_user_info(_Key, _User, Item, Val) ->
3107    {error, {bad_item, Item, Val}}.
3108
3109do_update_usm_user_info(Key, User) ->
3110    ets:insert(snmpm_usm_table, {Key, User}),
3111    ok.
3112
3113
3114usm_key(EngineId, Name) ->
3115    {usmUserTable, EngineId, Name}.
3116
3117
3118%% ---------------------------------------------------------------------
3119
3120verify_mandatory(_, []) ->
3121    ok;
3122verify_mandatory(Conf, [Mand|Mands]) ->
3123    case lists:keymember(Mand, 1, Conf) of
3124	true ->
3125	    verify_mandatory(Conf, Mands);
3126	false ->
3127	    error({missing_mandatory_config, Mand})
3128    end.
3129
3130verify_illegal(_, []) ->
3131    ok;
3132verify_illegal(Conf, [Inv|Invs]) ->
3133    case lists:member(Inv, Conf) of
3134	false ->
3135	    verify_illegal(Conf, Invs);
3136	true ->
3137	    error({illegal_config, Inv})
3138    end.
3139
3140verify_someof(Conf, [Mand|Mands]) ->
3141    case lists:keymember(Mand, 1, Conf) of
3142	true ->
3143	    ok;
3144	false ->
3145	    case Mands of
3146		[] ->
3147		    error({missing_mandatory_config, Mand});
3148		_ ->
3149		    verify_someof(Conf, Mands)
3150	    end
3151    end.
3152
3153ensure_config([], Config) ->
3154    Config;
3155ensure_config([Default|Defaults], Config) ->
3156    case lists:keymember(element(1, Default), 1, Config) of
3157	true ->
3158	    ensure_config(Defaults, Config);
3159	false ->
3160	    ensure_config(Defaults, [Default|Config])
3161    end.
3162
3163
3164
3165%%%-------------------------------------------------------------------
3166%%%
3167%%% Mini MIB stuff
3168%%%
3169%%%-------------------------------------------------------------------
3170
3171init_mini_mib(MibFiles) ->
3172    MiniMibs = lists:flatten([do_load_mib(MibFile) || MibFile <- MibFiles]),
3173    MiniMIB  = remove_duplicates(lists:keysort(1, MiniMibs), []),
3174    init_mini_mib2(MiniMIB).
3175
3176remove_duplicates([], Res) -> 
3177    Res;
3178remove_duplicates([X,X|T], Res) -> 
3179    remove_duplicates([X|T], Res);
3180remove_duplicates([{Oid, Name, Type, _} = X, {Oid, Name, Type, _}|T], Res) -> 
3181    remove_duplicates([X|T], Res);
3182remove_duplicates([X|T], Res) -> 
3183    remove_duplicates(T, [X|Res]).
3184
3185init_mini_mib2([]) ->
3186    ok;
3187init_mini_mib2([{Oid, Name, Type, MibName}|MiniMib]) ->
3188    ?vtrace("init mini mib -> ~w: ~w [~w] from ~s", 
3189	    [Name, Oid, Type,MibName ]),    
3190    ets:insert(snmpm_mib_table, {{mini_mib, Oid}, Name, Type, MibName}),
3191    init_mini_mib2(MiniMib).
3192
3193
3194handle_load_mib(Mib) ->
3195    [{mibs, Mibs0}] = ets:lookup(snmpm_config_table, mibs),
3196    case lists:member(Mib, Mibs0) of
3197	true ->
3198	    {error, already_loaded};
3199	false ->
3200	    Mibs = [Mib|Mibs0],
3201	    case (catch do_load_mib(Mib)) of
3202		MiniElems when is_list(MiniElems) ->
3203		    ets:insert(snmpm_config_table, {mibs, Mibs}),
3204		    update_mini_mib(MiniElems),
3205		    ok;
3206		Error ->
3207		    Error
3208	    end
3209    end.
3210
3211update_mini_mib([]) ->
3212    ok;
3213update_mini_mib([{Oid, Name, Type, MibName}|Elems]) ->
3214    Key = {mini_mib, Oid},
3215    case ets:lookup(snmpm_mib_table, Key) of
3216	[{Key, _Name, _Type, _AnotherMibName}] ->
3217	    %% Already loaded from another mib
3218	    update_mini_mib(Elems);
3219	[] ->
3220	    %% Not yet loaded
3221	    ?vtrace("update mini mib -> ~w: ~w [~w] from ~s", 
3222		    [Name, Oid, Type, MibName]),    
3223	    ets:insert(snmpm_mib_table, {Key, Name, Type, MibName}),
3224	    update_mini_mib(Elems)
3225    end.
3226
3227
3228handle_unload_mib(Mib) ->
3229    Key = {mib, Mib},
3230    case ets:lookup(snmpm_mib_table, Key) of
3231	[{Key, MibName, _MibFile}] ->
3232	    do_unload_mib(MibName),
3233	    [{mibs, Mibs0}] = ets:lookup(snmpm_config_table, mibs),
3234	    Mibs = lists:delete(Mib, Mibs0),
3235	    ets:insert(snmpm_config_table, {mibs, Mibs}),
3236	    ets:delete(snmpm_mib_table, Key),
3237	    ok;
3238	_ ->
3239	    {error, not_loaded}
3240    end.
3241
3242do_unload_mib(MibName) ->
3243    Pat  = {{mini_mib, '$1'}, '_', '_', MibName},
3244    Oids = ets:match(snmpm_mib_table, Pat),
3245    F    = fun([Oid]) -> ets:delete(snmpm_mib_table, {mini_mib, Oid}) end,
3246    lists:foreach(F, Oids).
3247    
3248
3249do_load_mib(MibFile) ->
3250    ?vtrace("load mib ~s", [MibFile]),
3251    F1 = snmp_misc:strip_extension_from_filename(MibFile, ".bin"),
3252    ActualFileName = lists:append(F1, ".bin"),
3253    case snmp_misc:read_mib(ActualFileName) of
3254        {ok, #mib{name = Name, mes = MEs, traps = Traps}} -> 
3255	    %% Check that the mib was not loaded or loaded
3256	    %% with a different filename: 
3257	    %% e.g. /tmp/MYMIB.bin and /tmp/mibs/MYMIB.bin
3258	    Name1   = mib_name(Name),
3259	    Pattern = {{mib, '_'}, Name1, '$1'},
3260	    case ets:match(snmpm_mib_table, Pattern) of
3261		[] ->
3262		    
3263		    Rec = {{mib, MibFile}, Name1, ActualFileName}, 
3264		    ets:insert(snmpm_mib_table, Rec),
3265		    init_mini_mib_elems(Name1, MEs++Traps, []);
3266
3267		%% This means that the mib has already been loaded
3268		[[ActualFileName]] ->
3269		    [];
3270
3271		%% This means that the mib was loaded before,
3272		%% but under another filename
3273		[[OtherMibFile]] ->
3274		    error({already_loaded, MibFile, OtherMibFile})
3275	    end;
3276		
3277        {error, Reason} -> 
3278	    error({failed_reading_mib, MibFile, Reason})
3279    end.
3280
3281mib_name(N) when is_list(N) ->
3282    list_to_atom(N);
3283mib_name(N) ->
3284    N.
3285
3286init_mini_mib_elems(_, [], Res) -> 
3287    Res;
3288init_mini_mib_elems(MibName, 
3289		    [#me{aliasname = N, 
3290			 oid       = Oid, 
3291			 entrytype = variable,
3292			 asn1_type = #asn1_type{bertype = Type}} | T], Res) ->
3293    init_mini_mib_elems(MibName, T, [{Oid, N, Type, MibName}|Res]);
3294
3295init_mini_mib_elems(MibName, 
3296		    [#me{aliasname = N, 
3297			 oid       = Oid, 
3298			 entrytype = table_column,
3299			 asn1_type = #asn1_type{bertype = Type}}|T], Res) ->
3300    init_mini_mib_elems(MibName, T, [{Oid, N, Type, MibName}|Res]);
3301
3302init_mini_mib_elems(MibName, 
3303		    [#me{aliasname = N, 
3304			 oid       = Oid,
3305			 asn1_type = undefined}|T], Res) ->
3306    init_mini_mib_elems(MibName, T, [{Oid, N, undefined, MibName}|Res]);
3307
3308init_mini_mib_elems(MibName, 
3309		    [#notification{trapname = N, 
3310				   oid      = Oid}|T], Res) ->
3311    init_mini_mib_elems(MibName, T, [{Oid, N, undefined, MibName}|Res]);
3312
3313init_mini_mib_elems(MibName, [_|T], Res) ->
3314    init_mini_mib_elems(MibName, T, Res).
3315
3316
3317
3318%%----------------------------------------------------------------------
3319
3320fix_address(Domain, Address) ->
3321    case snmp_conf:check_address(Domain, Address) of
3322	ok ->
3323	    Address;
3324	{ok, NAddress} ->
3325	    NAddress
3326    end.
3327
3328%%----------------------------------------------------------------------
3329
3330call(Req) ->
3331    call(Req, infinity).
3332
3333call(Req, To) ->
3334    gen_server:call(?SERVER, Req, To).
3335
3336% cast(Msg) ->
3337%     gen_server:cast(snmpm_server, Msg).
3338
3339
3340%%-------------------------------------------------------------------
3341
3342get_atl_dir(Opts) ->
3343    get_opt(dir, Opts).
3344
3345get_atl_type(Opts) ->
3346    case get_opt(type, Opts, read_write) of
3347	read_write ->
3348	    [read,write];
3349	read ->
3350	    [read];
3351	write ->
3352	    [write]
3353    end.
3354
3355get_atl_size(Opts) ->
3356    get_opt(size, Opts).
3357
3358get_atl_repair(Opts) ->
3359    get_opt(repair, Opts, truncate).
3360
3361get_atl_seqno(Opts) ->
3362    get_opt(seqno, Opts, false).
3363
3364
3365%%----------------------------------------------------------------------
3366
3367get_opt(Key, Opts) ->
3368    ?d("get option ~w from ~p", [Key, Opts]),
3369    snmp_misc:get_option(Key, Opts).
3370
3371get_opt(Key, Opts, Def) ->
3372    ?d("get option ~w with default ~p from ~p", [Key, Def, Opts]),
3373    snmp_misc:get_option(Key, Opts, Def).
3374
3375
3376%%----------------------------------------------------------------------
3377
3378get_info() ->
3379    ProcSize = proc_mem(self()),
3380    CntSz    = tab_size(snmpm_counter_table),
3381    StatsSz  = tab_size(snmpm_stats_table),
3382    MibSz    = tab_size(snmpm_mib_table),
3383    ConfSz   = tab_size(snmpm_config_table),
3384    AgentSz  = tab_size(snmpm_agent_table),
3385    UserSz   = tab_size(snmpm_user_table),
3386    UsmSz    = tab_size(snmpm_usm_table),
3387    [{process_memory, ProcSize},
3388     {db_memory, [{counter, CntSz}, 
3389		  {stats,   StatsSz},
3390		  {mib,     MibSz}, 
3391		  {config,  ConfSz},
3392		  {agent,   AgentSz}, 
3393		  {user,    UserSz}, 
3394		  {usm,     UsmSz}]}].
3395
3396proc_mem(P) when is_pid(P) ->
3397    case (catch erlang:process_info(P, memory)) of
3398	{memory, Sz} when is_integer(Sz) ->
3399	    Sz;
3400	_ ->
3401	    undefined
3402    end.
3403%% proc_mem(_) ->
3404%%     undefined.
3405
3406tab_size(T) ->
3407    case (catch ets:info(T, memory)) of
3408	Sz when is_integer(Sz) ->
3409	    Sz;
3410	_ ->
3411	    undefined
3412    end.
3413
3414
3415%%----------------------------------------------------------------------
3416
3417error(Reason) ->
3418    throw({error, Reason}).
3419
3420
3421%%----------------------------------------------------------------------
3422
3423info_msg(F, A) ->
3424    ?snmpm_info("Config server: " ++ F, A).
3425
3426warning_msg(F, A) ->
3427    ?snmpm_warning("Config server: " ++ F, A).
3428
3429error_msg(F, A) ->
3430    ?snmpm_error("Config server: " ++ F, A).
3431
3432%% p(F) ->
3433%%     p(F, []).
3434
3435%% p(F, A) ->
3436%%     io:format("~w:" ++ F ++ "~n", [?MODULE | A]).