1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-2020. 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(SERVER_OPT_VERB_DEFAULT, silence).
151-define(SERVER_OPT_GCT_DEFAULT,  30000).
152-define(SERVER_OPT_MT_DEFAULT,   true).
153-define(SERVER_OPT_CBP_DEFAULT,  temporary). % permanent
154-define(SERVER_OPT_NIS_DEFAULT,  none).
155
156%% -define(DEF_ADDR_TAG,       default_addr_tag).
157-define(DEFAULT_TARGETNAME, default_agent).
158-define(DEF_PORT_TAG,       default_port_tag).
159-define(SUPPORTED_DOMAINS,  [transportDomainUdpIpv4, transportDomainUdpIpv6]).
160
161-ifdef(snmp_debug).
162-define(GS_START_LINK(Opts),
163	gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts],
164			      [{debug,[trace]}])).
165-else.
166-define(GS_START_LINK(Opts),
167	gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts], [])).
168-endif.
169
170
171
172%%%-------------------------------------------------------------------
173%%% API
174%%%-------------------------------------------------------------------
175
176default_transport_domain() ->
177    snmpUDPDomain.
178
179
180start_link(Opts) ->
181    ?d("start_link -> entry with"
182       "~n   Opts: ~p", [Opts]),
183    ?GS_START_LINK(Opts).
184
185stop() ->
186    call(stop).
187
188is_started() ->
189    call(is_started, 1000).
190
191backup(BackupDir) when is_list(BackupDir) ->
192    call({backup, BackupDir}).
193
194%% Backward compatibillity
195register_user(UserId, UserMod, UserData) ->
196    register_user(UserId, UserMod, UserData, []).
197
198register_user(UserId, UserMod, UserData, DefaultAgentConfig)
199  when (UserId =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
200    case (catch verify_user_behaviour(UserMod)) of
201	ok ->
202	    {ok, SystemDefaultAgentConfig} = agent_info(),
203	    Config =
204		ensure_config(SystemDefaultAgentConfig,
205			      DefaultAgentConfig),
206%%	    Config = default_agent_config(DefaultAgentConfig),
207	    call({register_user, UserId, UserMod, UserData, Config});
208	Error ->
209	    Error
210    end;
211register_user(UserId, _UserMod, _UserData, DefaultAgentConfig)
212  when (UserId =/= ?DEFAULT_USER) ->
213    {error, {bad_default_agent_config, DefaultAgentConfig}};
214register_user(UserId, _, _, _) ->
215    {error, {bad_user_id, UserId}}.
216
217%% default_agent_config(DefaultAgentConfig) ->
218%%     {ok, SystemDefaultAgentConfig} = agent_info(),
219%%     default_agent_config(SystemDefaultAgentConfig, DefaultAgentConfig).
220
221%% default_agent_config([], DefaultAgentConfig) ->
222%%     DefaultAgentConfig;
223%% default_agent_config([{Key, _} = Entry|T], DefaultAgentConfig) ->
224%%     case lists:keymember(Key, 1, DefaultAgentConfig) of
225%% 	true ->
226%% 	    default_agent_config(T, DefaultAgentConfig);
227%% 	false ->
228%% 	    default_agent_config(T, [Entry|DefaultAgentConfig])
229%%     end.
230
231
232verify_user_behaviour(UserMod) ->
233    case snmp_misc:verify_behaviour(snmpm_user, UserMod) of
234	ok ->
235	    ok;
236	Error ->
237	    %% This user may implement the old behaviour, check it
238	    case snmp_misc:verify_behaviour(snmpm_user_old, UserMod) of
239		ok ->
240		    ok;
241		_ ->
242		    throw(Error)
243	    end
244    end.
245
246
247unregister_user(UserId) when UserId =/= ?DEFAULT_USER ->
248    call({unregister_user, UserId});
249unregister_user(BadUserId) ->
250    {error, {bad_user_id, BadUserId}}.
251
252
253which_users() ->
254    Pattern = #user{id = '$1', _ = '_'},
255    Match   = ets:match(snmpm_user_table, Pattern),
256    [UserId || [UserId] <- Match, UserId =/= ?DEFAULT_USER].
257
258
259user_info() ->
260    UserId = ?DEFAULT_USER,
261    case user_info(UserId) of
262	{ok, Mod, Data} ->
263	    {ok, UserId, Mod, Data};
264	Error ->
265	    Error
266    end.
267
268user_info(UserId) ->
269    case ets:lookup(snmpm_user_table, UserId) of
270	[#user{mod = UserMod, data = UserData}] ->
271	    {ok, UserMod, UserData};
272	_ ->
273	    {error, not_found}
274    end.
275
276user_info(UserId, Item) ->
277    case (catch do_user_info(UserId, Item)) of
278	{'EXIT', _} ->
279	    {error, {not_found, Item}};
280	Val ->
281	    {ok, Val}
282    end.
283
284do_user_info(UserId, module) ->
285    ets:lookup_element(snmpm_user_table, UserId, #user.mod);
286do_user_info(UserId, data) ->
287    ets:lookup_element(snmpm_user_table, UserId, #user.data);
288do_user_info(UserId, default_agent_config) ->
289    ets:lookup_element(snmpm_user_table, UserId, #user.default_agent_config);
290do_user_info(_UserId, BadItem) ->
291    error({not_found, BadItem}).
292
293
294%% A target-name constructed in this way is a string with the following:
295%% <IP-address>:<Port>-<Version>
296%% This is intended for backward compatibility and therefore has
297%% only support for IPv4 addresses and *no* other transport domain.
298mk_target_name(Domain, Addr, Config)
299  when is_atom(Domain), is_list(Config) ->
300    Version =
301	case lists:keysearch(version, 1, Config) of
302	    {value, {_, V}} ->
303		V;
304	    false ->
305		select_lowest_supported_version()
306	end,
307    try
308	lists:flatten(
309	  io_lib:format(
310	    "~s-~w", [snmp_conf:mk_addr_string({Domain, Addr}), Version]))
311    catch
312	_ ->
313	    lists:flatten(
314	      io_lib:format("~p-~w", [Addr, Version]))
315    end;
316mk_target_name(Ip, Port, Config)
317  when is_integer(Port), is_list(Config) ->
318    Domain = default_transport_domain(),
319    try fix_address(Domain, {Ip, Port}) of
320	Address ->
321	    mk_target_name(Domain, Address, Config)
322    catch
323	_ ->
324	    Version =
325		case lists:keysearch(version, 1, Config) of
326		    {value, {_, V}} ->
327			V;
328		    false ->
329			select_lowest_supported_version()
330		end,
331	    lists:flatten(
332	      io_lib:format("~p:~w-~w", [Ip, Port, Version]))
333    end.
334
335
336select_lowest_supported_version() ->
337    {ok, Versions} = system_info(versions),
338    select_lowest_supported_version([v1, v2, v3], Versions).
339
340select_lowest_supported_version([], Versions) ->
341    error({bad_versions, Versions});
342select_lowest_supported_version([H|T], Versions) ->
343    case lists:member(H, Versions) of
344	true ->
345	    H;
346	false ->
347	    select_lowest_supported_version(T, Versions)
348    end.
349
350
351register_agent(UserId, _TargetName, _Config0) when (UserId =:= user_id) ->
352    {error, {bad_user_id, UserId}};
353register_agent(UserId, TargetName, Config0)
354  when (is_list(TargetName) andalso
355	(length(TargetName) > 0) andalso
356	is_list(Config0)) ->
357
358    ?vtrace("register_agent -> entry with"
359	    "~n   UserId:     ~p"
360	    "~n   TargetName: ~p"
361	    "~n   Config0:    ~p", [UserId, TargetName, Config0]),
362
363    %% Check:
364    %%   1) That the mandatory configs are present
365    %%   2) That no illegal config, e.g. user_id (used internally),
366    %%      is not present
367    %%   3) Check that there are no invalid or erroneous configs
368    %%   4) Check that the manager is capable of using the selected version
369    try
370	verify_mandatory(Config0, [engine_id, reg_type]),
371	verify_someof(Config0, [address, taddress]),
372	verify_illegal(Config0, [user_id]),
373	Config = verify_agent_config(Config0),
374	Vsns = versions(),
375	Vsn = which_version(Config),
376	verify_version(Vsn, Vsns),
377	call({register_agent, UserId, TargetName, Config})
378    catch
379	Error ->
380	    Error
381    end.
382
383versions() ->
384    case system_info(versions) of
385	{ok, Vsns} ->
386	    Vsns;
387	{error, _} = ERROR ->
388	    throw(ERROR)
389    end.
390
391which_version(Conf) ->
392    case lists:keysearch(version, 1, Conf) of
393	{value, {version, V}} ->
394	    V;
395	false ->
396	    v1
397    end.
398
399verify_version(Vsn, Vsns) ->
400    case lists:member(Vsn, Vsns) of
401	true ->
402	    ok;
403	false ->
404	    Reason = {version_not_supported_by_manager, Vsn, Vsns},
405	    error(Reason)
406    end.
407
408
409
410unregister_agent(UserId, TargetName) ->
411    call({unregister_agent, UserId, TargetName}).
412
413%% This is the old style agent unregistration (using Addr and Port).
414unregister_agent(UserId, Domain, Address) when is_atom(Domain) ->
415    try fix_address(Domain, Address) of
416	NAddress ->
417	    do_unregister_agent(UserId, Domain, NAddress)
418    catch
419	_ ->
420	    {error, not_found}
421    end;
422unregister_agent(UserId, Ip, Port) when is_integer(Port) ->
423    Domain = default_transport_domain(),
424    try fix_address(Domain, {Ip, Port}) of
425	Address ->
426	    do_unregister_agent(UserId, Domain, Address)
427    catch
428	_ ->
429	    {error, not_found}
430    end.
431
432do_unregister_agent(UserId, Domain, Address) ->
433    case do_agent_info(Domain, Address, target_name) of
434	{ok, TargetName} ->
435	    unregister_agent(UserId, TargetName);
436	Error ->
437	    Error
438    end.
439
440
441
442agent_info() ->
443    agent_info(?DEFAULT_TARGETNAME, all).
444
445agent_info(TargetName, all) ->
446    case ets:match_object(snmpm_agent_table, {{TargetName, '_'}, '_'}) of
447	[] ->
448	    {error, not_found};
449	All ->
450	    {ok, [{Item, Val} || {{_, Item}, Val} <- All]}
451    end;
452%% Begin backwards compatibility
453agent_info(TargetName, address) ->
454    case agent_info({TargetName, taddress}) of
455	{ok, Val} ->
456	    {Addr, _} = Val,
457	    {ok, Addr};
458	_ ->
459	    %% This should be redundant since 'taddress' should exist
460	    agent_info({TargetName, address})
461    end;
462agent_info(TargetName, port) ->
463    case agent_info({TargetName, taddress}) of
464	{ok, Val} ->
465	    {_, Port} = Val,
466	    {ok, Port};
467	_ ->
468	    %% This should be redundant since 'taddress' should exist
469	    agent_info({TargetName, port})
470    end;
471%% End backwards compatibility
472agent_info(TargetName, Item) ->
473    agent_info({TargetName, Item}).
474
475agent_info(Key) ->
476    case ets:lookup(snmpm_agent_table, Key) of
477	[{_, Val}] ->
478	    {ok, Val};
479	[] ->
480	    {error, not_found}
481    end.
482
483agent_info(Domain, Address, Item) when is_atom(Domain) ->
484    try fix_address(Domain, Address) of
485	NAddress ->
486	    do_agent_info(Domain, NAddress, Item)
487    catch
488	_C:_E:_S ->
489	    {error, not_found}
490    end;
491agent_info(Ip, Port, Item) when is_integer(Port) ->
492    %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) entry~n",
493    %%   [Ip, Port, Item]),
494    Domain = default_transport_domain(),
495    try fix_address(Domain, {Ip, Port}) of
496	Address ->
497	    do_agent_info(Domain, Address, Item)
498    catch
499	_C:_E:_S ->
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, ?SERVER_OPT_VERB_DEFAULT),
1121    ServerGct  = get_opt(timeout,        ServerOpts, ?SERVER_OPT_GCT_DEFAULT),
1122    ServerMt   = get_opt(multi_threaded, ServerOpts, ?SERVER_OPT_MT_DEFAULT),
1123    ServerCBP  = get_opt(cbproxy,        ServerOpts, ?SERVER_OPT_CBP_DEFAULT),
1124    ServerNIS  = get_opt(netif_sup,      ServerOpts, ?SERVER_OPT_NIS_DEFAULT),
1125    ets:insert(snmpm_config_table, {server_verbosity,      ServerVerb}),
1126    ets:insert(snmpm_config_table, {server_timeout,        ServerGct}),
1127    ets:insert(snmpm_config_table, {server_multi_threaded, ServerMt}),
1128    ets:insert(snmpm_config_table, {server_cbproxy,        ServerCBP}),
1129    ets:insert(snmpm_config_table, {server_nis,            ServerNIS}),
1130
1131    %% -- Mibs (optional) --
1132    ?vdebug("initiate mini mib", []),
1133    Mibs = get_opt(mibs, Opts, []),
1134    ets:insert(snmpm_config_table, {mibs, Mibs}),
1135    init_mini_mib(Mibs),
1136
1137    %% -- Net-if (optional) --
1138    ?vdebug("net-if options", []),
1139    NetIfIrb     =
1140	case get_opt(inform_request_behaviour, Opts, ?IRB_DEFAULT) of
1141	    user ->
1142		{user, timer:seconds(15)};
1143	    Irb ->
1144		Irb
1145	end,
1146    NetIfOpts    = get_opt(net_if,                    Opts,      []),
1147    NetIfMod     = get_opt(module,                    NetIfOpts, snmpm_net_if),
1148    NetIfVerb    = get_opt(verbosity,                 NetIfOpts, silence),
1149    NetIfOptions = get_opt(options,                   NetIfOpts, []),
1150    ets:insert(snmpm_config_table, {net_if_module,    NetIfMod}),
1151    ets:insert(snmpm_config_table, {net_if_verbosity, NetIfVerb}),
1152    ets:insert(snmpm_config_table, {net_if_irb,       NetIfIrb}),
1153    ets:insert(snmpm_config_table, {net_if_options,   NetIfOptions}),
1154
1155    %% -- Versions (optional) --
1156    %% -- Versions (mandatory) ???????????? --
1157    ?vdebug("versions", []),
1158    Vsns = get_opt(versions, Opts, [v1, v2, v3]),
1159    ets:insert(snmpm_config_table, {versions, Vsns}),
1160
1161    %% -- Audit trail log (optional) --
1162    ?vdebug("audit trail log", []),
1163    case get_opt(audit_trail_log, Opts, []) of
1164	[] ->
1165	    ?vtrace("no ATL", []),
1166	    ets:insert(snmpm_config_table, {audit_trail_log, false});
1167	AuditTrailLogOpts ->
1168	    ?vtrace("ATL options: ~p", [AuditTrailLogOpts]),
1169	    ets:insert(snmpm_config_table, {audit_trail_log, true}),
1170	    LogDir   = get_atl_dir(AuditTrailLogOpts),
1171	    LogType  = get_atl_type(AuditTrailLogOpts),
1172	    LogSize  = get_atl_size(AuditTrailLogOpts),
1173	    LogRep   = get_atl_repair(AuditTrailLogOpts),
1174	    LogSeqNo = get_atl_seqno(AuditTrailLogOpts),
1175	    ets:insert(snmpm_config_table, {audit_trail_log_dir,    LogDir}),
1176	    ets:insert(snmpm_config_table, {audit_trail_log_type,   LogType}),
1177	    ets:insert(snmpm_config_table, {audit_trail_log_size,   LogSize}),
1178	    ets:insert(snmpm_config_table, {audit_trail_log_repair, LogRep}),
1179	    ets:insert(snmpm_config_table, {audit_trail_log_seqno,  LogSeqNo})
1180    end,
1181
1182    %% -- System default agent config --
1183    ?vdebug("system default agent config", []),
1184    init_agent_default(),
1185
1186    %% -- User (optional) --
1187    ?vdebug("default user", []),
1188    DefUserMod  = get_opt(def_user_mod,  Opts, ?USER_MOD_DEFAULT),
1189    DefUserData = get_opt(def_user_data, Opts, ?USER_DATA_DEFAULT),
1190    ets:insert(snmpm_config_table, {def_user_mod,  DefUserMod}),
1191    ets:insert(snmpm_config_table, {def_user_data, DefUserData}),
1192
1193    {ok, SystemDefaultAgentConfig}       = agent_info(),
1194    DefUser = #user{id                   = ?DEFAULT_USER,
1195		    mod                  = DefUserMod,
1196		    data                 = DefUserData,
1197		    default_agent_config = SystemDefaultAgentConfig},
1198    ok = handle_register_user(DefUser),
1199
1200    %% -- Note store --
1201    ?vdebug("note store", []),
1202    NoteStoreOpts    = get_opt(note_store, Opts, []),
1203    NoteStoreVerb    = get_opt(verbosity,  NoteStoreOpts, silence),
1204    NoteStoreTimeout = get_opt(timeout,    NoteStoreOpts, 30000),
1205    ets:insert(snmpm_config_table, {note_store_verbosity, NoteStoreVerb}),
1206    ets:insert(snmpm_config_table, {note_store_timeout,   NoteStoreTimeout}),
1207
1208    %% -- Manager SNMP config --
1209    ?vdebug("manager snmp config", []),
1210    MgrConf = read_manager_config_file(ConfDir),
1211    init_manager_config(MgrConf),
1212
1213    %% -- User config --
1214    ?vdebug("users config", []),
1215    Users = read_users_config_file(ConfDir),
1216    init_users_config(Users),
1217
1218    %% -- Agents config --
1219    ?vdebug("agents config", []),
1220    Agents = read_agents_config_file(ConfDir),
1221    init_agents_config(Agents),
1222
1223    %% -- USM config --
1224    UsmUsers = read_usm_config_file(ConfDir),
1225    init_usm_users_config(UsmUsers),
1226
1227    %% -- snmp engine init --
1228    init_engine(),
1229
1230    ?vlog("started", []),
1231    ok.
1232
1233
1234dets_open(Dir, DbInitError, Repair, AutoSave) ->
1235    Name     = ?CONFIG_DB,
1236    Filename = dets_filename(Name, Dir),
1237    case file:read_file_info(Filename) of
1238	{ok, _} ->
1239	    %% File exists
1240	    case do_dets_open(Name, Filename, Repair, AutoSave) of
1241		{ok, _Dets} ->
1242		    ok;
1243		{error, Reason1} ->
1244                    info_msg("Corrupt local database: ~p", [Filename]),
1245		    case DbInitError of
1246			terminate ->
1247			    error({failed_reopen_dets, Filename, Reason1});
1248			_ ->
1249			    Saved = Filename ++ ".saved",
1250			    file:rename(Filename, Saved),
1251			    case do_dets_open(Name, Filename,
1252					      Repair, AutoSave) of
1253				{ok, _Dets} ->
1254				    ok;
1255				{error, Reason2} ->
1256				    error({failed_open_dets, Filename,
1257					   Reason1, Reason2})
1258			    end
1259		    end
1260	    end;
1261	_ ->
1262	    case DbInitError of
1263		create_db_and_dir ->
1264		    ok = filelib:ensure_dir(Filename);
1265		_ ->
1266		    ok
1267	    end,
1268	    case do_dets_open(Name, Filename, Repair, AutoSave) of
1269		{ok, _Dets} ->
1270		    ok;
1271		{error, Reason} ->
1272		    error({failed_open_dets, Filename, Reason})
1273	    end
1274    end.
1275
1276do_dets_open(Name, Filename, Repair, AutoSave) ->
1277    Opts = [{repair,    Repair},
1278	    {auto_save, AutoSave},
1279	    {file,      Filename}],
1280    dets:open_file(Name, Opts).
1281
1282
1283dets_filename(Name, Dir) when is_atom(Name) ->
1284    dets_filename(atom_to_list(Name), Dir);
1285dets_filename(Name, Dir) ->
1286    filename:join(dets_filename1(Dir), Name).
1287
1288dets_filename1([])  -> ".";
1289dets_filename1(Dir) -> Dir.
1290
1291
1292%% ------------------------------------------------------------------------
1293
1294init_engine() ->
1295    case get_engine_boots() of
1296	{ok, Val} when Val < 2147483647 ->
1297	    set_engine_boots(Val + 1);
1298	{ok, _} ->
1299	    ok;
1300	_ ->
1301	    set_engine_boots(1)
1302    end,
1303    reset_engine_base().
1304
1305reset_engine_base() ->
1306    ets:insert(snmpm_config_table, {snmp_engine_base, snmp_misc:now(sec)}).
1307
1308
1309%% ------------------------------------------------------------------------
1310
1311verify_options(Opts, Mandatory) ->
1312    ?d("verify_options -> entry with"
1313       "~n   Opts:      ~p"
1314       "~n   Mandatory: ~p", [Opts, Mandatory]),
1315    verify_mandatory_options(Opts, Mandatory),
1316    verify_options(Opts).
1317
1318verify_options([]) ->
1319    ?d("verify_options -> done", []),
1320    ok;
1321verify_options([Opt|Opts]) ->
1322    ?d("verify_options -> entry with"
1323       "~n   Opt: ~p", [Opt]),
1324    verify_option(Opt),
1325    verify_options(Opts).
1326
1327verify_option({prio, Prio}) ->
1328    verify_prio(Prio);
1329verify_option({mibs, Mibs}) ->
1330    verify_mibs(Mibs);
1331verify_option({inform_request_behaviour, IRB}) ->
1332    verify_irb(IRB);
1333verify_option({net_if, NetIfOpts}) ->
1334    verify_net_if_opts(NetIfOpts);
1335verify_option({server, ServerOpts}) ->
1336    verify_server_opts(ServerOpts);
1337verify_option({note_store, NoteStoreOpts}) ->
1338    verify_note_store_opts(NoteStoreOpts);
1339verify_option({config, ConfOpts0}) ->
1340    %% Make sure any db_dir option is first in the options list to make it
1341    %% easier to check if the db_init_error option specifies that a missing
1342    %% db_dir should be created.
1343    ConfOpts = case lists:keytake(db_dir, 1, ConfOpts0) of
1344                   false -> ConfOpts0;
1345                   {value, Result, OtherOpts} -> [Result|OtherOpts]
1346               end,
1347    verify_config_opts(ConfOpts);
1348verify_option({versions, Vsns}) ->
1349    verify_versions(Vsns);
1350verify_option({audit_trail_log, LogOpts}) ->
1351    Mandatory = [dir, size],
1352    case (catch verify_mandatory_options(LogOpts, Mandatory)) of
1353	ok ->
1354	    verify_audit_trail_log_opts(LogOpts);
1355	{error, {missing_mandatory, LogOpt}} ->
1356	    error({missing_mandatory, audit_trail_log, LogOpt})
1357    end;
1358verify_option({def_user_mod, Mod}) ->
1359    verify_module(def_user_mod, Mod);
1360verify_option({def_user_data, _Data}) ->
1361    ok;
1362verify_option(Opt) ->
1363    {error, {invalid_option, Opt}}.
1364
1365verify_prio(Prio) when is_atom(Prio) ->
1366    ok;
1367verify_prio(Prio) ->
1368    error({invalid_prio, Prio}).
1369
1370verify_irb(auto) ->
1371    ok;
1372verify_irb(user) ->
1373    ok;
1374verify_irb({user, To}) when is_integer(To) andalso (To > 0) ->
1375    ok;
1376verify_irb(IRB) ->
1377    error({invalid_irb, IRB}).
1378
1379verify_mibs([]) ->
1380    ok;
1381verify_mibs([Mib|Mibs]) when is_list(Mib) ->
1382    verify_mibs(Mibs);
1383verify_mibs(Mibs) ->
1384    error({invalid_mibs, Mibs}).
1385
1386verify_config_opts([]) ->
1387    ok;
1388verify_config_opts([{verbosity, Verbosity}|Opts]) ->
1389    verify_verbosity(Verbosity),
1390    verify_config_opts(Opts);
1391verify_config_opts([{dir, Dir}|Opts]) ->
1392    verify_conf_dir(Dir),
1393    verify_config_opts(Opts);
1394verify_config_opts([{db_dir, Dir}|Opts]) ->
1395    case lists:keyfind(db_init_error, 1, Opts) of
1396        {db_init_error, create_db_and_dir} ->
1397            verify_conf_db_dir(Dir, false);
1398        _ ->
1399            verify_conf_db_dir(Dir, true)
1400    end,
1401    verify_config_opts(Opts);
1402verify_config_opts([{db_init_error, DbInitErr}|Opts]) ->
1403    verify_conf_db_init_error(DbInitErr),
1404    verify_config_opts(Opts);
1405verify_config_opts([{repair, Repair}|Opts]) ->
1406    verify_conf_repair(Repair),
1407    verify_config_opts(Opts);
1408verify_config_opts([{auto_save, AutoSave}|Opts]) ->
1409    verify_conf_auto_save(AutoSave),
1410    verify_config_opts(Opts);
1411verify_config_opts([Opt|_]) ->
1412    error({invalid_config_option, Opt}).
1413
1414verify_server_opts([]) ->
1415    ok;
1416verify_server_opts([{verbosity, Verbosity}|Opts]) ->
1417    verify_verbosity(Verbosity),
1418    verify_server_opts(Opts);
1419verify_server_opts([{timeout, Timeout}|Opts]) ->
1420    verify_server_timeout(Timeout),
1421    verify_server_opts(Opts);
1422verify_server_opts([{multi_threaded, MT}|Opts]) when is_boolean(MT) ->
1423    verify_server_opts(Opts);
1424verify_server_opts([{cbproxy, CBP}|Opts]) ->
1425    verify_server_cbproxy(CBP),
1426    verify_server_opts(Opts);
1427verify_server_opts([{netif_sup, NIS}|Opts]) ->
1428    verify_server_nis(NIS),
1429    verify_server_opts(Opts);
1430verify_server_opts([Opt|_]) ->
1431    error({invalid_server_option, Opt}).
1432
1433verify_server_timeout(T) when is_integer(T) andalso (T > 0) ->
1434    ok;
1435verify_server_timeout(T) ->
1436    error({invalid_server_timeout, T}).
1437
1438verify_server_cbproxy(temporary) ->
1439    ok;
1440verify_server_cbproxy(permanent) ->
1441    ok;
1442verify_server_cbproxy(CBP) ->
1443    error({invalid_server_cbproxy, CBP}).
1444
1445verify_server_nis(none) ->
1446    ok;
1447verify_server_nis({PingTo, PongTo} = V) when is_integer(PingTo) andalso
1448                                             (PingTo > 0) andalso
1449                                             is_integer(PongTo) andalso
1450                                             (PongTo > 0) ->
1451    ok;
1452verify_server_nis(NIS) ->
1453    error({invalid_server_netif_sup, NIS}).
1454
1455
1456verify_net_if_opts([]) ->
1457    ok;
1458verify_net_if_opts([{module, Mod}|Opts]) ->
1459    verify_network_interface_behaviour(Mod),
1460    verify_net_if_opts(Opts);
1461verify_net_if_opts([{verbosity, Verbosity}|Opts]) ->
1462    verify_verbosity(Verbosity),
1463    verify_net_if_opts(Opts);
1464verify_net_if_opts([{options, Options}|Opts]) when is_list(Options) ->
1465    verify_net_if_opts(Opts);
1466verify_net_if_opts([Opt|_]) ->
1467    error({invalid_net_if_option, Opt}).
1468
1469verify_network_interface_behaviour(Mod) ->
1470    case snmp_misc:verify_behaviour(snmpm_network_interface, Mod) of
1471	ok ->
1472	    ok;
1473	Error ->
1474	    throw(Error)
1475    end.
1476
1477
1478verify_note_store_opts([]) ->
1479    ok;
1480verify_note_store_opts([{verbosity, Verbosity}|Opts]) ->
1481    verify_verbosity(Verbosity),
1482    verify_note_store_opts(Opts);
1483verify_note_store_opts([{timeout, Timeout}|Opts]) ->
1484    verify_note_store_timeout(Timeout),
1485    verify_note_store_opts(Opts);
1486verify_note_store_opts([Opt|_]) ->
1487    error({invalid_note_store_option, Opt}).
1488
1489verify_note_store_timeout(T) when is_integer(T) andalso (T > 0) ->
1490    ok;
1491verify_note_store_timeout(T) ->
1492    error({invalid_note_store_timeout, T}).
1493
1494verify_conf_dir(Dir) ->
1495    case (catch verify_dir(Dir)) of
1496	ok ->
1497	    ok;
1498	{error, Reason} ->
1499	    error({invalid_conf_dir, Dir, Reason});
1500	_ ->
1501	    error({invalid_conf_dir, Dir})
1502    end.
1503
1504verify_conf_db_dir(Dir, true) ->
1505    case (catch verify_dir(Dir)) of
1506	ok ->
1507	    ok;
1508	{error, Reason} ->
1509	    error({invalid_conf_db_dir, Dir, Reason});
1510	_ ->
1511	    error({invalid_conf_db_dir, Dir})
1512    end;
1513verify_conf_db_dir(_Dir, false) ->
1514    ok.
1515
1516verify_conf_db_init_error(terminate) ->
1517    ok;
1518verify_conf_db_init_error(create) ->
1519    ok;
1520verify_conf_db_init_error(create_db_and_dir) ->
1521    ok;
1522verify_conf_db_init_error(InvalidDbInitError) ->
1523    error({invalid_conf_db_init_error, InvalidDbInitError}).
1524
1525
1526verify_conf_repair(true) ->
1527    ok;
1528verify_conf_repair(false) ->
1529    ok;
1530verify_conf_repair(force) ->
1531    ok;
1532verify_conf_repair(InvalidRepair) ->
1533    error({invalid_conf_db_repair, InvalidRepair}).
1534
1535
1536verify_conf_auto_save(infinity) ->
1537    ok;
1538verify_conf_auto_save(AutoSave)
1539  when is_integer(AutoSave) andalso (AutoSave > 0) ->
1540    ok;
1541verify_conf_auto_save(InvalidAutoSave) ->
1542    error({invalid_conf_db_auto_save, InvalidAutoSave}).
1543
1544
1545verify_versions([]) ->
1546    ok;
1547verify_versions([Vsn|Vsns]) ->
1548    verify_version(Vsn),
1549    verify_versions(Vsns);
1550verify_versions(Vsns) ->
1551    error({invalid_versions, Vsns}).
1552
1553verify_version(v1) ->
1554    ok;
1555verify_version(v2) ->
1556    ok;
1557verify_version(v3) ->
1558    ok;
1559verify_version(Vsn) ->
1560    error({invalid_version, Vsn}).
1561
1562verify_audit_trail_log_opts([]) ->
1563    ok;
1564verify_audit_trail_log_opts([{dir, Dir}|Opts]) ->
1565    verify_log_dir(Dir),
1566    verify_audit_trail_log_opts(Opts);
1567verify_audit_trail_log_opts([{type, Type}|Opts]) ->
1568    verify_log_type(Type),
1569    verify_audit_trail_log_opts(Opts);
1570verify_audit_trail_log_opts([{size, Size}|Opts]) ->
1571    verify_log_size(Size),
1572    verify_audit_trail_log_opts(Opts);
1573verify_audit_trail_log_opts([{repair, Repair}|Opts]) ->
1574    verify_log_repair(Repair),
1575    verify_audit_trail_log_opts(Opts);
1576verify_audit_trail_log_opts([{seqno, SeqNo}|Opts]) ->
1577    verify_log_seqno(SeqNo),
1578    verify_audit_trail_log_opts(Opts);
1579verify_audit_trail_log_opts([Opt|_Opts]) ->
1580    error({invalid_audit_trail_log_option, Opt}).
1581
1582verify_log_type(read) ->
1583    ok;
1584verify_log_type(write) ->
1585    ok;
1586verify_log_type(read_write) ->
1587    ok;
1588verify_log_type(Type) ->
1589    error({invalid_audit_trail_log_type, Type}).
1590
1591verify_log_dir(Dir) ->
1592    case (catch verify_dir(Dir)) of
1593	ok ->
1594	    ok;
1595	{error, Reason} ->
1596	    error({invalid_audit_trail_log_dir, Dir, Reason});
1597	_ ->
1598	    error({invalid_audit_trail_log_dir, Dir})
1599    end.
1600
1601verify_log_size(Sz) when is_integer(Sz) andalso (Sz > 0) ->
1602    ok;
1603verify_log_size(infinity) ->
1604    ok;
1605verify_log_size({MaxNoBytes, MaxNoFiles})
1606  when (is_integer(MaxNoBytes) andalso
1607	(MaxNoBytes > 0) andalso
1608	is_integer(MaxNoFiles) andalso
1609	(MaxNoFiles > 0) andalso
1610	(MaxNoFiles < 65000)) ->
1611    ok;
1612verify_log_size(Sz) ->
1613    error({invalid_audit_trail_log_size, Sz}).
1614
1615verify_log_repair(true) -> ok;
1616verify_log_repair(false) -> ok;
1617verify_log_repair(truncate) -> ok;
1618verify_log_repair(Repair) ->
1619    error({invalid_audit_trail_log_repair, Repair}).
1620
1621verify_log_seqno(true) -> ok;
1622verify_log_seqno(false) -> ok;
1623verify_log_seqno(SeqNo) ->
1624    error({invalid_audit_trail_log_seqno, SeqNo}).
1625
1626
1627verify_module(_, Mod) when is_atom(Mod) ->
1628    ok;
1629verify_module(ReasonTag, Mod) ->
1630    error({invalid_module, ReasonTag, Mod}).
1631
1632% verify_bool(_, true) ->
1633%     ok;
1634% verify_bool(_, false) ->
1635%     ok;
1636% verify_bool(ReasonTag, Bool) ->
1637%     error({invalid_bool, ReasonTag, Bool}).
1638
1639verify_dir(Dir) when is_list(Dir) ->
1640    case file:read_file_info(Dir) of
1641	{ok, #file_info{type = directory}} ->
1642	    ok;
1643	{ok, _} ->
1644	    {error, not_directory};
1645	{error, _Reason} ->
1646	    {error, not_found}
1647    end;
1648verify_dir(Dir) ->
1649    {error, {invalid_log_dir, Dir}}.
1650
1651
1652verify_verbosity(Verbosity) ->
1653    case snmp_verbosity:validate(Verbosity) of
1654	Verbosity ->
1655	    ok;
1656	_ ->
1657	    error({invalid_verbosity, Verbosity})
1658    end.
1659
1660
1661%% mandatory() -> [mand()]
1662%% mand() -> atom() | {atom, [atom()]}
1663verify_mandatory_options(_Opts, []) ->
1664    ok;
1665verify_mandatory_options(Opts, [Mand|Mands]) ->
1666    verify_mandatory_option(Opts, Mand),
1667    verify_mandatory_options(Opts, Mands).
1668
1669verify_mandatory_option(Opts, {Mand, MandSubOpts}) ->
1670    ?d("verify_mandatory_option -> entry with"
1671       "~n   Mand:        ~p"
1672       "~n   MandSubObjs: ~p", [Mand, MandSubOpts]),
1673    case lists:keysearch(Mand, 1, Opts) of
1674	{value, {Mand, SubOpts}} ->
1675	    verify_mandatory_options(SubOpts, MandSubOpts);
1676	false ->
1677	    ?d("missing mandatory option: ~w [~p]", [Mand, MandSubOpts]),
1678	    error({missing_mandatory, Mand, MandSubOpts})
1679    end;
1680verify_mandatory_option(Opts, Mand) ->
1681    ?d("verify_mandatory_option -> entry with"
1682       "~n   Mand:        ~p", [Mand]),
1683    case lists:keymember(Mand, 1, Opts) of
1684	true ->
1685	    ok;
1686	false ->
1687	    ?d("missing mandatory option: ~w", [Mand]),
1688	    error({missing_mandatory, Mand})
1689    end.
1690
1691%% ------------------------------------------------------------------------
1692
1693init_manager_config([]) ->
1694    ok;
1695init_manager_config([{Key, Val}|Confs]) ->
1696    ets:insert(snmpm_config_table, {Key, Val}),
1697    init_manager_config(Confs).
1698
1699
1700
1701init_agent_default() ->
1702    %% The purpose of the default_agent is only to have a place
1703    %% to store system wide default values related to agents.
1704    %%
1705    AgentDefaultConfig =
1706	[{port, ?DEFAULT_AGENT_PORT}, % Port
1707	 {timeout, 10000},            % Timeout
1708	 {max_message_size, 484},     % Max message (packet) size
1709	 {version, v2},               % MPModel
1710	 {sec_model, v2c},            % SecModel
1711	 {sec_name, "initial"},       % SecName
1712	 {sec_level, noAuthNoPriv},   % SecLevel
1713	 {community, "all-rights"}],  % Community
1714    do_update_agent_info(default_agent, AgentDefaultConfig).
1715
1716read_agents_config_file(Dir) ->
1717    Order = fun snmp_conf:no_order/2,
1718    Check = fun check_agent_config/2,
1719    try read_file(Dir, "agents.conf", Order, Check, [])
1720    catch
1721	throw:E:S ->
1722	    ?vlog("agent config error: "
1723                  "~n   ~p", [E]),
1724	    erlang:raise(throw, E, S)
1725    end.
1726
1727check_agent_config(Agent, State) ->
1728    {ok, {UserId, TargetName, Conf, Version}} = check_agent_config(Agent),
1729    {ok, Vsns} = system_info(versions),
1730    case lists:member(Version, Vsns) of
1731	true ->
1732	    {{ok, {UserId, TargetName, Conf}}, State};
1733	false ->
1734	    error({version_not_supported_by_manager, Version, Vsns})
1735    end.
1736
1737%% For backward compatibility
1738check_agent_config(
1739  {UserId, TargetName, Community, Domain, Addr,
1740   EngineId, Timeout, MaxMessageSize,
1741   Version, SecModel, SecName, SecLevel}) when is_atom(Domain) ->
1742    check_agent_config(
1743      UserId, TargetName, Community, Domain, Addr,
1744      EngineId, Timeout, MaxMessageSize,
1745      Version, SecModel, SecName, SecLevel);
1746check_agent_config(
1747  {UserId, TargetName, Community, Ip, Port,
1748   EngineId, Timeout, MaxMessageSize,
1749   Version, SecModel, SecName, SecLevel}) when is_integer(Port) ->
1750    Domain = default_transport_domain(),
1751    Addr = {Ip, Port},
1752    check_agent_config(
1753      UserId, TargetName, Community, Domain, Addr,
1754      EngineId, Timeout, MaxMessageSize,
1755      Version, SecModel, SecName, SecLevel);
1756check_agent_config(
1757  {_UserId, _TargetName, _Community, Domain, Addr,
1758   _EngineId, _Timeout, _MaxMessageSize,
1759   _Version, _SecModel, _SecName, _SecLevel}) ->
1760    error({bad_address, {Domain, Addr}});
1761check_agent_config(
1762  {UserId, TargetName, Community, Domain, Ip, Port,
1763   EngineId, Timeout, MaxMessageSize,
1764   Version, SecModel, SecName, SecLevel}) ->
1765    Addr = {Ip, Port},
1766    check_agent_config(
1767      UserId, TargetName, Community, Domain, Addr,
1768      EngineId, Timeout, MaxMessageSize,
1769      Version, SecModel, SecName, SecLevel);
1770check_agent_config(Agent) ->
1771    error({bad_agent_config, Agent}).
1772
1773check_agent_config(
1774  UserId, TargetName, Comm, Domain, Addr,
1775  EngineId, Timeout, MMS,
1776  Version, SecModel, SecName, SecLevel) ->
1777    ?vdebug("check_agent_config -> entry with"
1778	    "~n   UserId:     ~p"
1779	    "~n   TargetName: ~p", [UserId, TargetName]),
1780    snmp_conf:check_string(TargetName, {gt, 0}),
1781    %% Note that the order of Conf *is* important.
1782    %% Some properties may depend on others, so that
1783    %% in order to verify one property, another must
1784    %% be already verified (and present). An example
1785    %% of this is the property 'taddress', for which
1786    %% the property tdomain is needed.
1787    Conf =
1788	[{reg_type,         target_name},
1789	 {tdomain,          Domain},
1790	 {taddress,         fix_address(Domain, Addr)},
1791	 {community,        Comm},
1792	 {engine_id,        EngineId},
1793	 {timeout,          Timeout},
1794	 {max_message_size, MMS},
1795	 {version,          Version},
1796	 {sec_model,        SecModel},
1797	 {sec_name,         SecName},
1798	 {sec_level,        SecLevel}
1799	],
1800    {ok, {UserId, TargetName, verify_agent_config(Conf), Version}}.
1801
1802
1803
1804init_agents_config([]) ->
1805    ok;
1806init_agents_config([Agent|Agents]) ->
1807    init_agent_config(Agent),
1808    init_agents_config(Agents).
1809
1810init_agent_config({_UserId, ?DEFAULT_TARGETNAME = TargetName, _Config}) ->
1811    error({invalid_target_name, TargetName});
1812init_agent_config({UserId, TargetName, Config}) ->
1813    case handle_register_agent(UserId, TargetName, Config) of
1814	ok ->
1815	    ok;
1816	Error ->
1817	    throw(Error)
1818    end.
1819
1820
1821
1822%% Sort 'tdomain' first then 'port' to ensure both
1823%% sorts before 'taddress'.  Keep the order of other items.
1824order_agent(ItemA, ItemB) ->
1825    snmp_conf:keyorder(1, ItemA, ItemB, [tdomain, port]).
1826
1827fix_agent_config(Conf) ->
1828    ?vdebug("fix_agent_config -> entry with"
1829	    "~n      Conf: ~p", [Conf]),
1830    fix_agent_config(lists:sort(fun order_agent/2, Conf), []).
1831
1832fix_agent_config([], FixedConf) ->
1833    Ret = lists:reverse(FixedConf),
1834    ?vdebug("fix_agent_config -> done when (fixed config):"
1835	    "~n      ~p", [Ret]),
1836    Ret;
1837fix_agent_config([{taddress = Item, Address} = Entry|Conf], FixedConf) ->
1838    {value, {tdomain, TDomain}} = lists:keysearch(tdomain, 1, FixedConf),
1839    {value, {port, DefaultPort}} = lists:keysearch(port, 1, FixedConf),
1840    case snmp_conf:check_address(TDomain, Address, DefaultPort) of
1841	ok ->
1842	    fix_agent_config(Conf, [Entry|FixedConf]);
1843	{ok, NAddress} ->
1844	    fix_agent_config(Conf, [{Item, NAddress}|FixedConf])
1845    end;
1846fix_agent_config([Entry|Conf], FixedConf) ->
1847    fix_agent_config(Conf, [Entry|FixedConf]).
1848
1849
1850
1851verify_agent_config(Conf) ->
1852    verify_agent_config(lists:sort(fun order_agent/2, Conf), []).
1853
1854verify_agent_config([], VerifiedConf) ->
1855    Ret = lists:reverse(VerifiedConf),
1856    ?vdebug("verify_agent_config -> returns:~n"
1857	    "   ~p", [Ret]),
1858    Ret;
1859verify_agent_config([{Item, _} = Entry|Conf], VerifiedConf) ->
1860    verify_illegal(VerifiedConf, [Item]), % Duplicates are hereby illegal
1861    verify_agent_config(Conf, VerifiedConf, Entry);
1862verify_agent_config([Bad|_], _VerifiedConf) ->
1863    error({bad_agent_config, Bad}).
1864
1865verify_agent_config(
1866  Conf, VerifiedConf, {taddress = Item, Address} = Entry) ->
1867    verify_illegal(VerifiedConf, [address]),
1868    {TDomain, VC} =
1869	case lists:keysearch(tdomain, 1, VerifiedConf) of
1870	    {value, {tdomain,TD}} ->
1871		{TD, VerifiedConf};
1872	    _ ->
1873		%% Insert tdomain since it is missing
1874		%% Note: not default_transport_domain() since
1875		%% taddress is the new format hence the application
1876		%% should be tdomain aware and therefore addresses
1877		%% on the Domain, Addr format should be used and understood.
1878		TD = transportDomainUdpIpv4,
1879		{TD, [{tdomain, TD}|VerifiedConf]}
1880	end,
1881    case snmp_conf:check_address(TDomain, Address, 0) of
1882	ok ->
1883	    verify_agent_config(Conf, [Entry|VC]);
1884	{ok, NAddress} ->
1885	    verify_agent_config(Conf, [{Item, NAddress}|VC])
1886    end;
1887verify_agent_config(Conf, VerifiedConf, {address, Address}) ->
1888    Item = taddress,
1889    verify_illegal(VerifiedConf, [Item]),
1890    {TDomain, VC} =
1891	case lists:keysearch(tdomain, 1, VerifiedConf) of
1892	    {value, {tdomain, TD}} ->
1893		{TD, VerifiedConf};
1894	    _ ->
1895		%% Insert tdomain since it is missing
1896		TD = default_transport_domain(),
1897		{TD, [{tdomain, TD}|VerifiedConf]}
1898	end,
1899    case snmp_conf:check_address(TDomain, Address, 0) of
1900	ok ->
1901	    verify_agent_config(Conf, [{Item, Address}|VC]);
1902	{ok, NAddress} ->
1903	    verify_agent_config(Conf, [{Item, NAddress}|VC])
1904    end;
1905verify_agent_config(Conf, VerifiedConf, {Item, Val} = Entry) ->
1906    case verify_agent_entry(Item, Val) of
1907	ok ->
1908	    verify_agent_config(Conf, [Entry|VerifiedConf]);
1909	{ok, NewVal} ->
1910	    verify_agent_config(Conf, [{Item, NewVal}|VerifiedConf])
1911    end.
1912
1913verify_agent_entry(user_id, _UserId) ->
1914    ok;
1915verify_agent_entry(reg_type, RegType) ->
1916    if
1917	RegType =:= addr_port;
1918	RegType =:= target_name ->
1919	    ok;
1920	true ->
1921	    error({bad_reg_type, RegType})
1922    end;
1923verify_agent_entry(tdomain, TDomain) ->
1924    snmp_conf:check_domain(TDomain);
1925verify_agent_entry(port, Port) ->
1926    snmp_conf:check_port(Port);
1927verify_agent_entry(community, Comm) ->
1928    snmp_conf:check_string(Comm);
1929verify_agent_entry(engine_id, EngineId) ->
1930    case EngineId of
1931	discovery ->
1932	    ok;
1933	_ ->
1934	    snmp_conf:check_string(EngineId)
1935    end;
1936verify_agent_entry(timeout, Timeout) ->
1937    snmp_conf:check_timer(Timeout);
1938verify_agent_entry(max_message_size, MMS) ->
1939    snmp_conf:check_packet_size(MMS);
1940verify_agent_entry(version, V) ->
1941    if
1942	V =:= v1;
1943	V =:= v2;
1944	V =:= v3 ->
1945	    ok;
1946	true ->
1947	    error({bad_version, V})
1948    end;
1949verify_agent_entry(sec_model, Model) ->
1950    snmp_conf:check_sec_model(Model);
1951verify_agent_entry(sec_name, Name) ->
1952    try snmp_conf:check_string(Name)
1953    catch
1954	_ ->
1955	    error({bad_sec_name, Name})
1956    end;
1957verify_agent_entry(sec_level, Level) ->
1958    snmp_conf:check_sec_level(Level);
1959verify_agent_entry(Item, _) ->
1960    error({unknown_item, Item}).
1961
1962
1963
1964read_users_config_file(Dir) ->
1965    Order = fun snmp_conf:no_order/2,
1966    Check = fun (User, State) -> {check_user_config(User), State} end,
1967    try read_file(Dir, "users.conf", Order, Check, [])
1968    catch
1969	throw:E:S ->
1970	    ?vlog("failure reading users config file: "
1971                  "~n   ~p", [E]),
1972	    erlang:raise(throw, E, S)
1973    end.
1974
1975check_user_config({Id, Mod, Data}) ->
1976    ?vtrace("check_user_config -> entry with"
1977	    "~n   Id:   ~p"
1978	    "~n   Mod:  ~p"
1979	    "~n   Data: ~p", [Id, Mod, Data]),
1980    check_user_config({Id, Mod, Data, []});
1981check_user_config({Id, Mod, Data, DefaultAgentConfig} = _User)
1982  when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
1983    ?vtrace("check_user_config -> entry with"
1984	    "~n   Id:                 ~p"
1985	    "~n   Mod:                ~p"
1986	    "~n   Data:               ~p"
1987	    "~n   DefaultAgentConfig: ~p",
1988	    [Id, Mod, Data, DefaultAgentConfig]),
1989    case (catch verify_user_behaviour(Mod)) of
1990	ok ->
1991	    ?vtrace("check_user_config -> user behaviour verified", []),
1992	    DefAgentConf =
1993		verify_default_agent_config(DefaultAgentConfig),
1994	    ?vtrace("check_user_config -> "
1995		    "user agent (default) config verified", []),
1996	    User2 = {Id, Mod, Data, DefAgentConf},
1997	    {ok, User2};
1998	Error ->
1999	    throw(Error)
2000    end;
2001check_user_config({Id, _Mod, _Data, DefaultAgentConfig})
2002  when (Id =/= ?DEFAULT_USER) ->
2003    error({bad_default_agent_config, DefaultAgentConfig});
2004check_user_config({Id, _Mod, _Data, _DefaultAgentConfig}) ->
2005    error({bad_user_id, Id});
2006check_user_config(User) ->
2007    error({bad_user_config, User}).
2008
2009init_users_config([]) ->
2010    ok;
2011init_users_config([User|Users]) ->
2012    init_user_config(User),
2013    init_users_config(Users).
2014
2015init_user_config(User) ->
2016    case (catch verify_user(User)) of
2017	{ok, UserRec} ->
2018	    case handle_register_user(UserRec) of
2019		ok ->
2020		    ok;
2021		{error, Reason} ->
2022		    error_msg("failed register user: "
2023			      "~n~w~n~w", [User, Reason])
2024	    end;
2025	{error, Reason} ->
2026	    error_msg("user config check failed: "
2027		      "~n~w~n~w", [User, Reason])
2028    end.
2029
2030verify_user({Id, UserMod, UserData}) ->
2031    verify_user({Id, UserMod, UserData, []});
2032verify_user({Id, UserMod, UserData, DefaultAgentConfig})
2033  when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) ->
2034    ?d("verify_user -> entry with"
2035       "~n   Id:                 ~p"
2036       "~n   UserMod:            ~p"
2037       "~n   UserData:           ~p"
2038       "~n   DefaultAgentConfig: ~p",
2039       [Id, UserMod, UserData, DefaultAgentConfig]),
2040    case (catch verify_user_behaviour(UserMod)) of
2041	ok ->
2042	    try
2043		{ok, SystemDefaultAgentConfig} = agent_info(),
2044		Config =
2045		    ensure_config(
2046		      SystemDefaultAgentConfig,
2047		      verify_default_agent_config(DefaultAgentConfig)),
2048%%		Config =
2049%%		    default_agent_config(
2050%%		      verify_default_agent_config(DefaultAgentConfig)),
2051		{ok, #user{id                   = Id,
2052			   mod                  = UserMod,
2053			   data                 = UserData,
2054			   default_agent_config = Config}}
2055	    catch
2056		Error ->
2057		    ?vdebug("verify_user default_agent_config -> throw"
2058			    "~n   Error: ~p", [Error]),
2059		    error({bad_default_agent_config, Error})
2060	    end;
2061	Error ->
2062	    throw(Error)
2063    end;
2064verify_user({Id, _UserMod, _UserData, DefaultAgentConfig})
2065  when (Id =/= ?DEFAULT_USER) ->
2066    {error, {bad_default_agent_config, DefaultAgentConfig}};
2067verify_user({Id, _, _, _}) ->
2068    {error, {bad_user_id, Id}}.
2069
2070verify_default_agent_config(Conf) ->
2071    try
2072	verify_illegal(
2073	  Conf,
2074	  [user_id, engine_id, address, tdomain, taddress]),
2075	verify_agent_config(Conf)
2076    catch
2077	Error ->
2078	    ?vdebug("verify_default_agent_config -> throw"
2079		    "~n   Error: ~p", [Error]),
2080	    error({bad_default_agent_config, Error})
2081    end.
2082
2083
2084read_usm_config_file(Dir) ->
2085    Order = fun snmp_conf:no_order/2,
2086    Check = fun (User, State) -> {check_usm_user_config(User), State} end,
2087    read_file(Dir, "usm.conf", Order, Check, []).
2088
2089%% Identity-function
2090check_usm_user_config({EngineId, Name,
2091		       AuthP, AuthKey,
2092		       PrivP, PrivKey}) ->
2093    User = {EngineId, Name, Name, AuthP, AuthKey, PrivP, PrivKey},
2094    verify_usm_user(User);
2095check_usm_user_config({_EngineId, _Name, _SecName,
2096		       _AuthP, _AuthKey,
2097		       _PrivP, _PrivKey} = User) ->
2098    verify_usm_user(User);
2099check_usm_user_config(User) ->
2100    error({bad_usm_config, User}).
2101
2102init_usm_users_config([]) ->
2103    ok;
2104init_usm_users_config([User|Users]) ->
2105    init_usm_user_config(User),
2106    init_usm_users_config(Users).
2107
2108init_usm_user_config(User) when is_record(User, usm_user) ->
2109    case handle_register_usm_user(User) of
2110	ok ->
2111	    ok;
2112	Error ->
2113	    throw(Error)
2114    end;
2115init_usm_user_config(BadUser) ->
2116    error({bad_usm_user, BadUser}).
2117
2118
2119verify_usm_user({EngineID, Name, SecName, AuthP, AuthKey, PrivP, PrivKey}) ->
2120    ?d("verify_usm_user -> entry with"
2121       "~n   EngineID: ~p"
2122       "~n   Name:     ~p"
2123       "~n   SecName:  ~p"
2124       "~n   AuthP:    ~p"
2125       "~n   AuthKey:  ~p"
2126       "~n   PrivP:    ~p"
2127       "~n   PrivKey:  ~p",
2128       [EngineID, Name, SecName, AuthP, AuthKey, PrivP, PrivKey]),
2129    verify_usm_user_engine_id(EngineID),
2130    verify_usm_user_name(Name),
2131    verify_usm_user_sec_name(SecName),
2132    verify_usm_user(AuthP, AuthKey, PrivP, PrivKey),
2133    User = #usm_user{engine_id = EngineID,
2134		     name      = Name,
2135		     sec_name  = SecName,
2136		     auth      = AuthP,
2137		     auth_key  = AuthKey,
2138		     priv      = PrivP,
2139		     priv_key  = PrivKey},
2140    {ok, User}.
2141
2142verify_usm_user_engine_id(EngineID) ->
2143    case (catch snmp_conf:check_string(EngineID, {gt, 0})) of
2144	ok ->
2145	    ok;
2146	_ ->
2147	    error({bad_usm_engine_id, EngineID})
2148    end.
2149
2150verify_usm_user_name(Name) ->
2151    case (catch snmp_conf:check_string(Name, {gt, 0})) of
2152	ok ->
2153	    ok;
2154	_ ->
2155	    error({bad_usm_user_name, Name})
2156    end.
2157
2158verify_usm_user_sec_name(Name) ->
2159    case (catch snmp_conf:check_string(Name, {gt, 0})) of
2160	ok ->
2161	    ok;
2162	_ ->
2163	    error({bad_usm_sec_name, Name})
2164    end.
2165
2166verify_usm_user(AuthP, AuthKey, PrivP, PrivKey) ->
2167    verify_usm_user_auth(AuthP, AuthKey),
2168    verify_usm_user_priv(PrivP, PrivKey),
2169    ok.
2170
2171verify_usm_user_auth(usmNoAuthProtocol, AuthKey) ->
2172    case (catch snmp_conf:check_string(AuthKey, any)) of
2173	ok ->
2174	    ok;
2175	_ ->
2176	    error({invalid_auth_key, usmNoAuthProtocol})
2177    end;
2178verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey)
2179  when is_list(AuthKey) andalso (length(AuthKey) =:= 16) ->
2180    case is_crypto_supported(md5) of
2181	true ->
2182	    case snmp_conf:all_integer(AuthKey) of
2183		true ->
2184		    ok;
2185		_ ->
2186		    error({invalid_auth_key, usmHMACMD5AuthProtocol})
2187	    end;
2188	false ->
2189	    error({unsupported_crypto, md5})
2190    end;
2191verify_usm_user_auth(usmHMACMD5AuthProtocol, AuthKey) when is_list(AuthKey) ->
2192    Len = length(AuthKey),
2193    error({invalid_auth_key, usmHMACMD5AuthProtocol, Len});
2194verify_usm_user_auth(usmHMACMD5AuthProtocol, _AuthKey) ->
2195    error({invalid_auth_key, usmHMACMD5AuthProtocol});
2196verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey)
2197  when is_list(AuthKey) andalso (length(AuthKey) =:= 20) ->
2198    case is_crypto_supported(sha) of
2199	true ->
2200	    case snmp_conf:all_integer(AuthKey) of
2201		true ->
2202		    ok;
2203		_ ->
2204		    error({invalid_auth_key, usmHMACSHAAuthProtocol})
2205	    end;
2206	false ->
2207	    error({unsupported_crypto, sha})
2208    end;
2209verify_usm_user_auth(usmHMACSHAAuthProtocol, AuthKey) when is_list(AuthKey) ->
2210    Len = length(AuthKey),
2211    error({invalid_auth_key, usmHMACSHAAuthProtocol, Len});
2212verify_usm_user_auth(usmHMACSHAAuthProtocol, _AuthKey) ->
2213    error({invalid_auth_key, usmHMACSHAAuthProtocol});
2214verify_usm_user_auth(AuthP, _AuthKey) ->
2215    error({invalid_auth_protocol, AuthP}).
2216
2217verify_usm_user_priv(usmNoPrivProtocol, PrivKey) ->
2218    case (catch snmp_conf:check_string(PrivKey, any)) of
2219	ok ->
2220	    ok;
2221	_ ->
2222	    error({invalid_priv_key, usmNoPrivProtocol})
2223    end;
2224verify_usm_user_priv(usmDESPrivProtocol, PrivKey)
2225  when (length(PrivKey) =:= 16) ->
2226    case is_crypto_supported(des_cbc) of
2227	true ->
2228	    case snmp_conf:all_integer(PrivKey) of
2229		true ->
2230		    ok;
2231		_ ->
2232		    error({invalid_priv_key, usmDESPrivProtocol})
2233	    end;
2234	false ->
2235	    error({unsupported_crypto, des_cbc})
2236    end;
2237verify_usm_user_priv(usmDESPrivProtocol, PrivKey) when is_list(PrivKey) ->
2238    Len = length(PrivKey),
2239    error({invalid_priv_key, usmDESPrivProtocol, Len});
2240verify_usm_user_priv(usmDESPrivProtocol, _PrivKey) ->
2241    error({invalid_priv_key, usmDESPrivProtocol});
2242verify_usm_user_priv(usmAesCfb128Protocol, PrivKey)
2243  when (length(PrivKey) =:= 16) ->
2244    case is_crypto_supported(aes_cfb128) of
2245	true ->
2246	    case snmp_conf:all_integer(PrivKey) of
2247		true ->
2248		    ok;
2249		_ ->
2250		    error({invalid_priv_key, usmAesCfb128Protocol})
2251	    end;
2252	false ->
2253	    error({unsupported_crypto, aes_cfb128})
2254    end;
2255verify_usm_user_priv(usmAesCfb128Protocol, PrivKey) when is_list(PrivKey) ->
2256    Len = length(PrivKey),
2257    error({invalid_priv_key, usmAesCfb128Protocol, Len});
2258verify_usm_user_priv(usmAesCfb128Protocol, _PrivKey) ->
2259    error({invalid_priv_key, usmAesCfb128Protocol});
2260verify_usm_user_priv(PrivP, _PrivKey) ->
2261    error({invalid_priv_protocol, PrivP}).
2262
2263
2264-compile({inline, [{is_crypto_supported,1}]}).
2265is_crypto_supported(Func) ->
2266    snmp_misc:is_crypto_supported(Func).
2267
2268
2269read_manager_config_file(Dir) ->
2270    Order = fun order_manager_config/2,
2271    Check = fun check_manager_config/2,
2272    Conf = read_file(Dir, "manager.conf", Order, Check),
2273    ?d("read_manager_config_file -> ok: "
2274       "~n   Conf: ~p", [Conf]),
2275    %% If the address is not specified, then we assume
2276    %% it should be the local host.
2277    %% If the address is not possible to determine
2278    %% that way, then we give up...
2279    verify_someof(Conf, [port, transports]),
2280    verify_mandatory(Conf, [engine_id, max_message_size]),
2281    default_manager_config(Conf).
2282
2283default_manager_config(Conf) ->
2284    %% Ensure valid transports entry
2285    case lists:keyfind(transports, 1, Conf) of
2286	false ->
2287	    {port, Port} = lists:keyfind(port, 1, Conf),
2288	    Domain =
2289		case lists:keyfind(domain, 1, Conf) of
2290		    false ->
2291			default_transport_domain();
2292		    {_, D} ->
2293			D
2294		end,
2295	    Family = snmp_conf:tdomain_to_family(Domain),
2296	    {ok, Hostname} = inet:gethostname(),
2297	    case inet:getaddr(Hostname, Family) of
2298		{ok, Address} ->
2299		    lists:sort(
2300		      fun order_manager_config/2,
2301		      [{transports, [{Domain, {Address, Port}}]} | Conf]);
2302		{error, _Reason} ->
2303		    ?d("default_manager_config -> "
2304		       "failed getting ~w address for ~s:~n"
2305		       "   _Reason: ~p", [Family, Hostname, _Reason]),
2306		    Conf
2307	    end;
2308	_ ->
2309	    Conf
2310    end.
2311
2312order_manager_config(EntryA, EntryB) ->
2313    snmp_conf:keyorder(1, EntryA, EntryB, [domain, port]).
2314
2315check_manager_config(Entry, undefined) ->
2316    check_manager_config(Entry, {default_transport_domain(), undefined});
2317check_manager_config({domain, Domain}, {_, Port}) ->
2318    {snmp_conf:check_domain(Domain), {Domain, Port}};
2319check_manager_config({port, Port}, {Domain, _}) ->
2320    {ok = snmp_conf:check_port(Port), {Domain, Port}};
2321check_manager_config({address, _}, {_, undefined}) ->
2322    error({missing_mandatory, port});
2323check_manager_config({address = Tag, Ip} = Entry, {Domain, Port} = State) ->
2324    {case snmp_conf:check_ip(Domain, Ip) of
2325	 ok ->
2326	     [Entry,
2327	      {transports, [{Domain, {Ip, Port}}]}];
2328	 {ok, FixedIp} ->
2329	     [{Tag, FixedIp},
2330	      {transports, [{Domain, {FixedIp, Port}}]}]
2331     end, State};
2332check_manager_config({transports = Tag, Transports}, {_, Port} = State)
2333  when is_list(Transports) ->
2334    CheckedTransports =
2335	[case Transport of
2336	     {Domain, Address} ->
2337		 case
2338		     case Port of
2339			 undefined ->
2340			     snmp_conf:check_address(Domain, Address);
2341			 _ ->
2342			     snmp_conf:check_address(Domain, Address, Port)
2343		     end
2344		 of
2345		     ok ->
2346			 Transport;
2347		     {ok, FixedAddress} ->
2348			 {Domain, FixedAddress}
2349		 end;
2350	     _Domain when Port =:= undefined->
2351		 error({missing_mandatory, port});
2352	     Domain ->
2353		 Family = snmp_conf:tdomain_to_family(Domain),
2354		 {ok, Hostname} = inet:gethostname(),
2355		 case inet:getaddr(Hostname, Family) of
2356		     {ok, IpAddr} ->
2357			 {Domain, {IpAddr, Port}};
2358		     {error, _} ->
2359			 error({bad_address, {Domain, Hostname}})
2360		 end
2361	 end
2362	 || Transport <- Transports],
2363    {{ok, {Tag, CheckedTransports}}, State};
2364check_manager_config(Entry, State) ->
2365    {check_manager_config(Entry), State}.
2366
2367check_manager_config({engine_id, EngineID}) ->
2368    snmp_conf:check_string(EngineID);
2369check_manager_config({max_message_size, Max}) ->
2370    snmp_conf:check_integer(Max, {gte, 484});
2371check_manager_config(Conf) ->
2372    error({unknown_config, Conf}).
2373
2374
2375read_file(Dir, FileName, Order, Check, Default) ->
2376    try snmp_conf:read(filename:join(Dir, FileName), Order, Check)
2377    catch
2378	{error, Reason} when element(1, Reason) =:= failed_open ->
2379	    ?vlog("failed reading config from ~s: ~p", [FileName, Reason]),
2380	    Default
2381    end.
2382
2383read_file(Dir, FileName, Order, Check) ->
2384    try snmp_conf:read(filename:join(Dir, FileName), Order, Check)
2385    catch
2386	throw:{error, Reason} = E:S
2387	  when element(1, Reason) =:= failed_open ->
2388	    error_msg("failed reading config from ~s: "
2389                      "~n   ~p", [FileName, Reason]),
2390	    erlang:raise(throw, E, S)
2391    end.
2392
2393%%--------------------------------------------------------------------
2394%% Func: handle_call/3
2395%% Returns: {reply, Reply, State}          |
2396%%          {reply, Reply, State, Timeout} |
2397%%          {noreply, State}               |
2398%%          {noreply, State, Timeout}      |
2399%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
2400%%          {stop, Reason, State}            (terminate/2 is called)
2401%%--------------------------------------------------------------------
2402handle_call({register_user, UserId, UserMod, UserData, DefaultAgentConfig},
2403	    _From, State) ->
2404    ?vlog("received register_user request: "
2405	  "~n   UserId:             ~p"
2406	  "~n   UserMod:            ~p"
2407	  "~n   UserData:           ~p"
2408	  "~n   DefaultAgentConfig: ~p",
2409	  [UserId, UserMod, UserData, DefaultAgentConfig]),
2410    User  = #user{id                   = UserId,
2411		  mod                  = UserMod,
2412		  data                 = UserData,
2413		  default_agent_config = DefaultAgentConfig},
2414    Reply = handle_register_user(User),
2415    {reply, Reply, State};
2416
2417handle_call({unregister_user, UserId}, _From, State) ->
2418    ?vlog("received unregister_user request: "
2419	  "~n   UserId: ~p", [UserId]),
2420    Reply = handle_unregister_user(UserId),
2421    {reply, Reply, State};
2422
2423handle_call({register_agent, UserId, TargetName, Config}, _From, State) ->
2424    ?vlog("received register_agent request: "
2425	  "~n   UserId:     ~p"
2426	  "~n   TargetName: ~p"
2427	  "~n   Config:     ~p", [UserId, TargetName, Config]),
2428    Reply = handle_register_agent(UserId, TargetName, Config),
2429    {reply, Reply, State};
2430
2431handle_call({unregister_agent, UserId, TargetName}, _From, State) ->
2432    ?vlog("received unregister_agent request: "
2433	  "~n   UserId:     ~p"
2434	  "~n   TargetName: ~p", [UserId, TargetName]),
2435    Reply = handle_unregister_agent(UserId, TargetName),
2436    {reply, Reply, State};
2437
2438handle_call({update_agent_info, UserId, TargetName, Info},
2439	    _From, State) ->
2440    ?vlog("received update_agent_info request: "
2441	  "~n   UserId:     ~p"
2442	  "~n   TargetName: ~p"
2443	  "~n   Info:       ~p", [UserId, TargetName, Info]),
2444    Reply = handle_update_agent_info(UserId, TargetName, Info),
2445    {reply, Reply, State};
2446
2447%% <BACKWARD-COMPAT>
2448handle_call({update_agent_info, UserId, TargetName, Item, Val},
2449	    _From, State) ->
2450    ?vlog("received update_agent_info request: "
2451	  "~n   UserId:     ~p"
2452	  "~n   TargetName: ~p"
2453	  "~n   Item:       ~p"
2454	  "~n   Val:        ~p", [UserId, TargetName, Item, Val]),
2455    Reply = handle_update_agent_info(UserId, TargetName, Item, Val),
2456    {reply, Reply, State};
2457%% </BACKWARD-COMPAT>
2458
2459handle_call({register_usm_user, User}, _From, State) ->
2460    ?vlog("received register_usm_user request: "
2461	  "~n   User: ~p", [User]),
2462    Reply = handle_register_usm_user(User),
2463    {reply, Reply, State};
2464
2465handle_call({unregister_usm_user, EngineID, Name}, _From, State) ->
2466    ?vlog("received register_usm_user request: "
2467	  "~n   EngineID: ~p"
2468	  "~n   Name:     ~p", [EngineID, Name]),
2469    Reply = handle_unregister_usm_user(EngineID, Name),
2470    {reply, Reply, State};
2471
2472handle_call({update_usm_user_info, EngineID, UserName, Item, Val},
2473	    _From, State) ->
2474    ?vlog("received update_usm_user_info request: "
2475	  "~n   EngineID: ~p"
2476	  "~n   UserName: ~p"
2477	  "~n   Item:     ~p"
2478	  "~n   Val:      ~p", [EngineID, UserName, Item, Val]),
2479    Reply = handle_update_usm_user_info(EngineID, UserName, Item, Val),
2480    {reply, Reply, State};
2481
2482handle_call({cre_counter, Counter, Initial}, _From, State) ->
2483    ?vlog("received cre_counter ~p -> ~w", [Counter, Initial]),
2484    Reply = cre_counter(Counter, Initial),
2485    {reply, Reply, State};
2486
2487handle_call({cre_stats_counter, Counter, Initial}, _From, State) ->
2488    ?vlog("received cre_stats_counter ~p -> ~w", [Counter, Initial]),
2489    Reply = cre_stats_counter(Counter, Initial),
2490    {reply, Reply, State};
2491
2492handle_call({reset_stats_counter, Counter}, _From, State) ->
2493    ?vlog("received reset_stats_counter ~p", [Counter]),
2494    Reply = reset_stats_counter(Counter),
2495    {reply, Reply, State};
2496
2497handle_call({load_mib, Mib}, _From, State) ->
2498    ?vlog("received load_mib ~p", [Mib]),
2499    case handle_load_mib(Mib) of
2500	ok ->
2501	    {reply, ok, State};
2502	Error ->
2503	    {reply, Error, State}
2504    end;
2505
2506
2507handle_call({unload_mib, Mib}, _From, State) ->
2508    ?vlog("received unload_mib ~p", [Mib]),
2509    case handle_unload_mib(Mib) of
2510	ok ->
2511	    {reply, ok, State};
2512	Error ->
2513	    {reply, Error, State}
2514    end;
2515
2516
2517handle_call({set_engine_boots, Boots}, _From, State) ->
2518    ?vlog("received set_engine_boots ~p", [Boots]),
2519    set_engine_boots(Boots),
2520    {reply, ok, State};
2521
2522handle_call({set_engine_time, Time}, _From, State) ->
2523    ?vlog("received set_engine_time ~p", [Time]),
2524    Base = snmp_misc:now(sec) - Time,
2525    ets:insert(snmpm_config_table, {snmp_engine_base, Base}),
2526    {reply, ok, State};
2527
2528handle_call({set_usm_cache, Key, Val}, _From, State) ->
2529    ?vlog("received set_usm_cache: ~w -> ~p", [Key, Val]),
2530    ets:insert(snmpm_usm_table, {{usm_cache, Key}, Val}),
2531    {reply, ok, State};
2532
2533handle_call({reset_usm_cache, EngineID}, _From, State) ->
2534    ?vlog("received reset_usm_cache: ~p", [EngineID]),
2535    reset_usm_cache(EngineID),
2536    {reply, ok, State};
2537
2538handle_call({verbosity, Verbosity}, _From, State) ->
2539    ?vlog("received verbosity request", []),
2540    put(verbosity, Verbosity),
2541    {reply, ok, State};
2542
2543handle_call(info, _From, State) ->
2544    ?vlog("received info request", []),
2545    Reply = get_info(),
2546    {reply, Reply, State};
2547
2548handle_call({backup, BackupDir}, From, State) ->
2549    ?vlog("backup to ~p", [BackupDir]),
2550    Pid = self(),
2551    V   = get(verbosity),
2552    case file:read_file_info(BackupDir) of
2553        {ok, #file_info{type = directory}} ->
2554            BackupServer =
2555                erlang:spawn_link(
2556                  fun() ->
2557                          put(sname, mcbs),
2558                          put(verbosity, V),
2559                          Dir   = filename:join([BackupDir]),
2560                          Reply = handle_backup(?CONFIG_DB, Dir),
2561                          Pid ! {backup_done, Reply},
2562                          unlink(Pid)
2563                  end),
2564            ?vtrace("backup server: ~p", [BackupServer]),
2565            {noreply, State#state{backup = {BackupServer, From}}};
2566        {ok, _} ->
2567            {reply, {error, not_a_directory}, State};
2568        Error ->
2569            {reply, Error, State}
2570    end;
2571
2572
2573%% handle_call({update_system_info, Key, Val}, _From, State) ->
2574%%     ?vlog("received update_system_info: ~p -> ~p", [Key, Val]),
2575%%     Reply = handle_update_system_info(Key, Val),
2576%%     {reply, Reply, State};
2577
2578
2579handle_call(is_started, _From, State) ->
2580    ?vlog("received is_started request", []),
2581    {reply, true, State};
2582
2583
2584handle_call(stop, _From, State) ->
2585    {stop, normal, ok, State};
2586
2587
2588handle_call(Req, _From, State) ->
2589    warning_msg("received unknown request: ~n~p", [Req]),
2590    {reply, {error, unknown_request}, State}.
2591
2592
2593%%--------------------------------------------------------------------
2594%% Func: handle_cast/2
2595%% Returns: {noreply, State}          |
2596%%          {noreply, State, Timeout} |
2597%%          {stop, Reason, State}            (terminate/2 is called)
2598%%--------------------------------------------------------------------
2599handle_cast(Msg, State) ->
2600    warning_msg("received unknown message: ~n~p", [Msg]),
2601    {noreply, State}.
2602
2603
2604%%--------------------------------------------------------------------
2605%% Func: handle_info/2
2606%% Returns: {noreply, State}          |
2607%%          {noreply, State, Timeout} |
2608%%          {stop, Reason, State}            (terminate/2 is called)
2609%%--------------------------------------------------------------------
2610handle_info({'EXIT', Pid, Reason}, #state{backup = {Pid, From}} = S) ->
2611    ?vlog("backup server (~p) exited for reason ~n~p", [Pid, Reason]),
2612    gen_server:reply(From, {error, Reason}),
2613    {noreply, S#state{backup = undefined}};
2614
2615handle_info({'EXIT', Pid, Reason}, S) ->
2616    %% The only other processes we should be linked to are
2617    %% either the server or our supervisor, so die...
2618    {stop, {received_exit, Pid, Reason}, S};
2619
2620handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) ->
2621    ?vlog("backup done:"
2622          "~n   Reply: ~p", [Reply]),
2623    gen_server:reply(From, Reply),
2624    {noreply, S#state{backup = undefined}};
2625
2626handle_info(Info, State) ->
2627    warning_msg("received unknown info: ~n~p", [Info]),
2628    {noreply, State}.
2629
2630
2631%%--------------------------------------------------------------------
2632%% Func: terminate/2
2633%% Purpose: Shutdown the server
2634%% Returns: any (ignored by gen_server)
2635%%--------------------------------------------------------------------
2636terminate(Reason, _State) ->
2637    ?vdebug("terminate: ~p",[Reason]),
2638    ok.
2639
2640%%----------------------------------------------------------------------
2641%% Func: code_change/3
2642%% Purpose: Convert process state when code is changed
2643%% Returns: {ok, NewState}
2644%%----------------------------------------------------------------------
2645
2646%% downgrade
2647%%
2648code_change({down, _Vsn}, S1, downgrade_to_pre_4_7) ->
2649    #state{backup = B} = S1,
2650    stop_backup_server(B),
2651    S2 = {state},
2652    {ok, S2};
2653
2654%% upgrade
2655%%
2656code_change(_Vsn, _S1, upgrade_from_pre_4_7) ->
2657    %% {state} = S1,
2658    S2 = #state{},
2659    {ok, S2};
2660
2661code_change(_Vsn, State, _Extra) ->
2662    {ok, State}.
2663
2664
2665stop_backup_server(undefined) ->
2666    ok;
2667stop_backup_server({Pid, _}) when is_pid(Pid) ->
2668    exit(Pid, kill).
2669
2670
2671
2672%%----------------------------------------------------------
2673%% Update system info
2674%%----------------------------------------------------------
2675
2676%% handle_update_system_info(audit_trail_log_type = Key, Val) ->
2677%%     case snmpm_config:system_info(audit_trail_log) of
2678%% 	{ok, true} ->
2679%% 	    Value =
2680%% 		case Val of
2681%% 		    read ->
2682%% 			{ok, [read]};
2683%% 		    write ->
2684%% 			{ok, [write]};
2685%% 		    read_write ->
2686%% 			{ok, [read,write]};
2687%% 		    _ ->
2688%% 			{error, {bad_value, Key, Val}}
2689%% 		end,
2690%% 	    case Value of
2691%% 		{ok, NewValue} ->
2692%% 		    ets:insert(snmpm_config_table, {Key, NewValue}),
2693%% 		    ok;
2694%% 		false ->
2695%% 		    Value
2696%% 	    end;
2697%% 	_ ->
2698%% 	    {error, audit_trail_log_not_enabled}
2699%%     end;
2700%% handle_update_system_info(BadKey, Val) ->
2701%%     {error, {unsupported_update, BadKey, Val}}.
2702
2703
2704%%----------------------------------------------------------
2705%% Backup
2706%%----------------------------------------------------------
2707
2708handle_backup(D, BackupDir) ->
2709    %% First check that we do not wrote to the corrent db-dir...
2710    ?vtrace("handle_backup -> entry with"
2711        "~n   D:         ~p"
2712        "~n   BackupDir: ~p", [D, BackupDir]),
2713    case dets:info(D, filename) of
2714        undefined ->
2715            ?vinfo("handle_backup -> no file to backup", []),
2716            {error, no_file};
2717        Filename ->
2718            ?vinfo("handle_backup -> file to backup: ~n   ~p", [Filename]),
2719            case filename:dirname(Filename) of
2720                BackupDir ->
2721                    ?vinfo("handle_backup -> backup dir and db dir the same",
2722                           []),
2723                    {error, db_dir};
2724                _ ->
2725                    case file:read_file_info(BackupDir) of
2726                        {ok, #file_info{type = directory}} ->
2727                            ?vdebug("handle_backup -> backup dir ok", []),
2728                            %% All well so far...
2729                            Type = dets:info(D, type),
2730                            KP   = dets:info(D, keypos),
2731                            dets_backup(D,
2732                                        filename:basename(Filename),
2733                                        BackupDir, Type, KP);
2734                        {ok, _} ->
2735                            ?vinfo("handle_backup -> backup dir not a dir",
2736                                   []),
2737                            {error, not_a_directory};
2738                        Error ->
2739                            ?vinfo("handle_backup -> Error: ~p", [Error]),
2740                            Error
2741                    end
2742            end
2743    end.
2744
2745dets_backup(D, Filename, BackupDir, Type, KP) ->
2746    ?vtrace("dets_backup -> entry with"
2747            "~n   D:         ~p"
2748            "~n   Filename:  ~p"
2749            "~n   BackupDir: ~p", [D, Filename, BackupDir]),
2750    BackupFile = filename:join(BackupDir, Filename),
2751    ?vtrace("dets_backup -> "
2752            "~n   BackupFile: ~p", [BackupFile]),
2753    Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}],
2754    case dets:open_file(?BACKUP_DB, Opts) of
2755        {ok, B} ->
2756            ?vtrace("dets_backup -> create fun", []),
2757            F = fun(Arg) ->
2758                        dets_backup(Arg, start, D, B)
2759                end,
2760            dets:safe_fixtable(D, true),
2761            Res = dets:init_table(?BACKUP_DB, F, [{format, bchunk}]),
2762            dets:safe_fixtable(D, false),
2763            ?vtrace("dets_backup -> Res: ~p", [Res]),
2764            Res;
2765        Error ->
2766            ?vinfo("dets_backup -> open_file failed: "
2767                   "~n   ~p", [Error]),
2768            Error
2769    end.
2770
2771
2772dets_backup(close, _Cont, _D, B) ->
2773    dets:close(B),
2774    ok;
2775dets_backup(read, Cont1, D, B) ->
2776    case dets:bchunk(D, Cont1) of
2777        {error, _} = ERROR ->
2778            ERROR;
2779        '$end_of_table' ->
2780            dets:close(B),
2781            end_of_input;
2782        {Cont2, Data} ->
2783            F = fun(Arg) ->
2784                        dets_backup(Arg, Cont2, D, B)
2785                end,
2786            {Data, F}
2787    end.
2788
2789
2790%%%-------------------------------------------------------------------
2791%%% Internal functions
2792%%%-------------------------------------------------------------------
2793
2794handle_register_user(#user{id = Id} = User) ->
2795    ?vdebug("handle_register_user -> entry with"
2796	    "~n   User: ~p", [User]),
2797    case ets:lookup(snmpm_user_table, Id) of
2798	[] ->
2799	    ets:insert(snmpm_user_table, User),
2800	    ok;
2801	_ ->
2802	    {error, {already_registered, User}}
2803    end.
2804
2805handle_unregister_user(UserId) ->
2806    ?vdebug("handle_unregister_user -> entry with"
2807	    "~n   UserId: ~p", [UserId]),
2808    ets:delete(snmpm_user_table, UserId),
2809    ok.
2810
2811
2812handle_register_agent(UserId, TargetName, Config) ->
2813    ?vdebug("handle_register_agent -> entry with"
2814	    "~n   UserId:     ~p"
2815	    "~n   TargetName: ~p"
2816	    "~n   Config:     ~p", [UserId, TargetName, Config]),
2817    case (catch agent_info(TargetName, user_id)) of
2818	{error, _} ->
2819	    ?vtrace(
2820	       "handle_register_agent -> user_id not found in config", []),
2821	    case ets:lookup(snmpm_user_table, UserId) of
2822		[#user{default_agent_config = DefConfig}] ->
2823		    ?vtrace("handle_register_agent ->~n"
2824			    "   DefConfig: ~p", [DefConfig]),
2825		    FixedConfig =
2826			fix_agent_config(ensure_config(DefConfig, Config)),
2827		    ?vtrace("handle_register_agent ->~n"
2828			    "   FixedConfig: ~p", [FixedConfig]),
2829		    do_handle_register_agent(
2830		      TargetName, [{user_id, UserId}|FixedConfig]),
2831		    %% <DIRTY-BACKWARD-COMPATIBILLITY>
2832		    %% And now for some (backward compatibillity)
2833		    %% dirty crossref stuff
2834		    {value, {_, Domain}} =
2835			lists:keysearch(tdomain, 1, FixedConfig),
2836		    {value, {_, Address}} =
2837			lists:keysearch(taddress, 1, FixedConfig),
2838		    ?vtrace(
2839		       "handle_register_agent -> register cross-ref fix", []),
2840		    ets:insert(snmpm_agent_table,
2841			       {{Domain, Address, target_name}, TargetName}),
2842		    %% </DIRTY-BACKWARD-COMPATIBILLITY>
2843
2844%%		    %% First, insert this users default config
2845%%		    ?vtrace("handle_register_agent -> store default config", []),
2846%%		    do_handle_register_agent(TargetName, DefConfig),
2847%%		    %% Second, insert the config for this agent
2848%%		    ?vtrace("handle_register_agent -> store config", []),
2849%%		    do_handle_register_agent(TargetName,
2850%%					     [{user_id, UserId}|Config]),
2851%%		    %% <DIRTY-BACKWARD-COMPATIBILLITY>
2852%%		    %% And now for some (backward compatibillity)
2853%%		    %% dirty crossref stuff
2854%%		    ?vtrace("handle_register_agent -> lookup taddress", []),
2855%%		    {ok, {Addr, Port} = TAddress} =
2856%%			agent_info(TargetName, taddress),
2857%%		    ?vtrace("handle_register_agent -> taddress: ~p",
2858%%			    [TAddress]),
2859%%		    ?vtrace("handle_register_agent -> register cross-ref fix", []),
2860%%		    ets:insert(snmpm_agent_table,
2861%%			       {{Addr, Port, target_name}, TargetName}),
2862%%		    %% </DIRTY-BACKWARD-COMPATIBILLITY>
2863		    ok;
2864		_ ->
2865		    {error, {not_found, UserId}}
2866	    end;
2867	{ok, UserId} ->
2868	    ?vinfo("[~w] Agent (~p) already registered"
2869		   "~nwhen"
2870		   "~n   Agents: ~p",
2871		   [UserId, TargetName, which_agents()]),
2872	    {error, {already_registered, TargetName}};
2873	{ok, OtherUserId} ->
2874	    ?vinfo("[~w] Agent (~p) already registered to ~p"
2875		   "~nwhen"
2876		   "~n   Agents: ~p",
2877		   [UserId, TargetName, OtherUserId, which_agents()]),
2878	    {error, {already_registered, TargetName, OtherUserId}}
2879    end.
2880
2881do_handle_register_agent(_TargetName, []) ->
2882    ok;
2883do_handle_register_agent(TargetName, [{Item, Val}|Rest]) ->
2884    ?vtrace("do_handle_register_agent -> entry with"
2885	    "~n   TargetName: ~p"
2886	    "~n   Item:       ~p"
2887	    "~n   Val:        ~p"
2888	    "~n   Rest:       ~p", [TargetName, Item, Val, Rest]),
2889    case (catch do_update_agent_info(TargetName, Item, Val)) of
2890	ok ->
2891	    do_handle_register_agent(TargetName, Rest);
2892	{error, Reason} ->
2893	    ?vtrace("do_handle_register_agent -> failed updating ~p"
2894		    "~n   Item:   ~p"
2895		    "~n   Reason: ~p", [Item, Reason]),
2896	    ets:match_delete(snmpm_agent_table, {TargetName, '_'}),
2897	    {error, Reason}
2898    end;
2899do_handle_register_agent(TargetName, BadConfig) ->
2900    error_msg("error during agent registration - bad config: ~n~p",
2901	      [BadConfig]),
2902    ets:match_delete(snmpm_agent_table, {TargetName, '_'}),
2903    {error, {bad_agent_config, TargetName, BadConfig}}.
2904
2905
2906handle_unregister_agent(UserId, TargetName) ->
2907    ?vdebug("handle_unregister_agent -> entry with"
2908	    "~n   UserId:     ~p"
2909	    "~n   TargetName: ~p", [UserId, TargetName]),
2910    case (catch agent_info(TargetName, user_id)) of
2911	{ok, UserId} ->
2912	    {ok, EngineID} = agent_info(TargetName, engine_id),
2913	    reset_usm_cache(EngineID),
2914	    %% <DIRTY-BACKWARD-COMPATIBILLITY>
2915	    %% And now for some (backward compatibillity)
2916	    %% dirty crossref stuff
2917	    {ok, Domain} = agent_info(TargetName, tdomain),
2918	    {ok, Address} = agent_info(TargetName, taddress),
2919	    ets:delete(snmpm_agent_table, {Domain, Address, target_name}),
2920	    %% </DIRTY-BACKWARD-COMPATIBILLITY>
2921	    ets:match_delete(snmpm_agent_table, {{TargetName, '_'}, '_'}),
2922	    ok;
2923	{ok, OtherUserId} ->
2924	    {error, {not_owner, OtherUserId}};
2925	Error ->
2926	    Error
2927    end.
2928
2929
2930handle_update_agent_info(UserId, TargetName, Info) ->
2931    ?vdebug("handle_update_agent_info -> entry with"
2932	    "~n   UserId:     ~p"
2933	    "~n   TargetName: ~p"
2934	    "~n   Info:       ~p", [UserId, TargetName, Info]),
2935    %% Verify ownership
2936    case (catch agent_info(TargetName, user_id)) of
2937	{ok, UserId} ->
2938	    handle_update_agent_info(TargetName, Info);
2939	{ok, OtherUserId} ->
2940	    {error, {not_owner, OtherUserId}};
2941	Error ->
2942	    Error
2943    end.
2944
2945handle_update_agent_info(TargetName, Info) ->
2946    ?vtrace("handle_update_agent_info -> entry with"
2947	    "~n   TargetName: ~p"
2948	    "~n   Info:      ~p", [TargetName, Info]),
2949    %% Verify info
2950    try
2951	verify_illegal(Info, [user_id]),
2952	%% If port or domain is part of the info, then use it.
2953	%% If not, lookup what is already stored for
2954	%% this agent and use that.
2955	do_update_agent_info(
2956	  TargetName,
2957	  fix_agent_config(
2958	    verify_agent_config(
2959	      ensure_agent_info(TargetName, [port,tdomain], Info))))
2960    catch
2961	Error ->
2962	    Error;
2963	T:E ->
2964	    {error, {failed_info_verification, Info, T, E}}
2965    end.
2966
2967handle_update_agent_info(UserId, TargetName, Item, Val) ->
2968    ?vdebug("handle_update_agent_info -> entry with"
2969	    "~n   UserId:     ~p"
2970	    "~n   TargetName: ~p"
2971	    "~n   Item:       ~p"
2972	    "~n   Val:        ~p", [UserId, TargetName, Item, Val]),
2973    handle_update_agent_info(TargetName, [{Item, Val}]).
2974
2975do_update_agent_info(TargetName, Info) ->
2976    ?vtrace("do_update_agent_info -> entry with~n"
2977	    "   TargetName: ~p~n"
2978	    "   Info:       ~p", [TargetName,Info]),
2979    InsertItem =
2980	fun({Item, Val}) ->
2981		ets:insert(snmpm_agent_table, {{TargetName, Item}, Val})
2982	end,
2983    lists:foreach(InsertItem, Info).
2984
2985do_update_agent_info(TargetName, Item, Val) ->
2986    ?vtrace("do_update_agent_info -> entry with"
2987	    "~n   TargetName: ~p"
2988	    "~n   Item:       ~p"
2989	    "~n   Val:       ~p", [TargetName, Item, Val]),
2990    ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}),
2991    ok.
2992
2993
2994handle_register_usm_user(#usm_user{engine_id = EngineID,
2995				   name      = Name} = User) ->
2996    ?vdebug("handle_register_usm_user -> entry with"
2997	    "~n   User: ~p", [User]),
2998    Key = usm_key(EngineID, Name),
2999    case ets:lookup(snmpm_usm_table, Key) of
3000	[] ->
3001	    do_update_usm_user_info(Key, User);
3002	_ ->
3003	    {error, {already_registered, EngineID, Name}}
3004    end;
3005handle_register_usm_user(BadUsmUser) ->
3006    {error, {bad_usm_user, BadUsmUser}}.
3007
3008handle_unregister_usm_user(EngineID, Name) ->
3009    ?vdebug("handle_unregister_usm_user -> entry with"
3010	    "~n   EngineID: ~p"
3011	    "~n   Name:     ~p", [EngineID, Name]),
3012    Key = usm_key(EngineID, Name),
3013    ets:delete(snmpm_usm_table, Key),
3014    ok.
3015
3016
3017handle_update_usm_user_info(EngineID, Name, Item, Val) ->
3018    ?vdebug("handle_update_usm_user_info -> entry with"
3019	    "~n   EngineID: ~p"
3020	    "~n   Name:     ~p"
3021	    "~n   Item:     ~p"
3022	    "~n   Val:      ~p", [EngineID, Name, Item, Val]),
3023    Key = usm_key(EngineID, Name),
3024    case ets:lookup(snmpm_usm_table, Key) of
3025	[] ->
3026	    {error, not_found};
3027	[{_Key, User}] ->
3028	    do_update_usm_user_info(Key, User, Item, Val)
3029    end.
3030
3031do_update_usm_user_info(Key, User, sec_name, Val) ->
3032    %% case verify_usm_user_sec_name(Val) of
3033    %%     ok ->
3034    %% 	do_update_usm_user_info(Key, User#usm_user{sec_name = Val});
3035    %%     _ ->
3036    %% 	{error, {invalid_usm_sec_name, Val}}
3037    %% end;
3038    ok = verify_usm_user_sec_name(Val),
3039    do_update_usm_user_info(Key, User#usm_user{sec_name = Val});
3040do_update_usm_user_info(Key, User, auth, Val)
3041  when (Val =:= usmNoAuthProtocol) orelse
3042       (Val =:= usmHMACMD5AuthProtocol) orelse
3043       (Val =:= usmHMACSHAAuthProtocol) ->
3044    do_update_usm_user_info(Key, User#usm_user{auth = Val});
3045do_update_usm_user_info(_Key, _User, auth, Val) ->
3046    {error, {invalid_auth_protocol, Val}};
3047do_update_usm_user_info(Key,
3048			#usm_user{auth = usmNoAuthProtocol} = User,
3049			auth_key, Val) ->
3050    case (catch snmp_conf:check_string(Val, any)) of
3051	ok ->
3052	    do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
3053	_ ->
3054	    {error, {invalid_auth_key, Val}}
3055    end;
3056do_update_usm_user_info(Key,
3057			#usm_user{auth = usmHMACMD5AuthProtocol} = User,
3058			auth_key, Val)
3059  when length(Val) =:= 16 ->
3060    case is_crypto_supported(md5) of
3061	true ->
3062	    do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
3063	false ->
3064	    {error, {unsupported_crypto, md5}}
3065    end;
3066do_update_usm_user_info(_Key,
3067			#usm_user{auth = usmHMACMD5AuthProtocol},
3068			auth_key, Val) when is_list(Val) ->
3069    Len = length(Val),
3070    {error, {invalid_auth_key_length, usmHMACMD5AuthProtocol, Len}};
3071do_update_usm_user_info(_Key,
3072			#usm_user{auth = usmHMACMD5AuthProtocol},
3073			auth_key, Val) ->
3074    {error, {invalid_auth_key, usmHMACMD5AuthProtocol, Val}};
3075do_update_usm_user_info(Key,
3076			#usm_user{auth = usmHMACSHAAuthProtocol} = User,
3077			auth_key, Val)
3078  when length(Val) =:= 20 ->
3079    case is_crypto_supported(sha) of
3080	true ->
3081	    do_update_usm_user_info(Key, User#usm_user{auth_key = Val});
3082	false ->
3083	    {error, {unsupported_crypto, sha}}
3084    end;
3085do_update_usm_user_info(_Key,
3086			#usm_user{auth = usmHMACSHAAuthProtocol},
3087			auth_key, Val) when is_list(Val) ->
3088    Len = length(Val),
3089    {error, {invalid_auth_key_length, usmHMACSHAAuthProtocol, Len}};
3090do_update_usm_user_info(_Key,
3091			#usm_user{auth = usmHMACSHAAuthProtocol},
3092			auth_key, Val) ->
3093    {error, {invalid_auth_key, usmHMACSHAAuthProtocol, Val}};
3094do_update_usm_user_info(Key, User, priv, Val)
3095  when (Val =:= usmNoPrivProtocol) orelse
3096       (Val =:= usmDESPrivProtocol) orelse
3097       (Val =:= usmAesCfb128Protocol) ->
3098    do_update_usm_user_info(Key, User#usm_user{priv = Val});
3099do_update_usm_user_info(_Key, _User, priv, Val) ->
3100    {error, {invalid_priv_protocol, Val}};
3101do_update_usm_user_info(Key,
3102			#usm_user{priv = usmNoPrivProtocol} = User,
3103			priv_key, Val) ->
3104    case (catch snmp_conf:check_string(Val, any)) of
3105	ok ->
3106	    do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
3107	_ ->
3108	    {error, {invalid_priv_key, Val}}
3109    end;
3110do_update_usm_user_info(Key,
3111			#usm_user{priv = usmDESPrivProtocol} = User,
3112			priv_key, Val)
3113  when length(Val) =:= 16 ->
3114    case is_crypto_supported(des_cbc) of
3115	true ->
3116	    do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
3117	false ->
3118	    {error, {unsupported_crypto, des_cbc}}
3119    end;
3120do_update_usm_user_info(Key,
3121			#usm_user{priv = usmAesCfb128Protocoll} = User,
3122			priv_key, Val)
3123  when length(Val) =:= 16 ->
3124    case is_crypto_supported(aes_cfb128) of
3125	true ->
3126	    do_update_usm_user_info(Key, User#usm_user{priv_key = Val});
3127	false ->
3128	    {error, {unsupported_crypto, aes_cfb128}}
3129    end;
3130do_update_usm_user_info(_Key,
3131			#usm_user{auth = usmHMACSHAAuthProtocol},
3132			priv_key, Val) when is_list(Val) ->
3133    Len = length(Val),
3134    {error, {invalid_priv_key_length, usmHMACSHAAuthProtocol, Len}};
3135do_update_usm_user_info(_Key,
3136			#usm_user{auth = usmHMACSHAAuthProtocol},
3137			priv_key, Val) ->
3138    {error, {invalid_priv_key, usmHMACSHAAuthProtocol, Val}};
3139do_update_usm_user_info(_Key, _User, Item, Val) ->
3140    {error, {bad_item, Item, Val}}.
3141
3142do_update_usm_user_info(Key, User) ->
3143    ets:insert(snmpm_usm_table, {Key, User}),
3144    ok.
3145
3146
3147usm_key(EngineId, Name) ->
3148    {usmUserTable, EngineId, Name}.
3149
3150
3151%% ---------------------------------------------------------------------
3152
3153verify_mandatory(_, []) ->
3154    ok;
3155verify_mandatory(Conf, [Mand|Mands]) ->
3156    case lists:keymember(Mand, 1, Conf) of
3157	true ->
3158	    verify_mandatory(Conf, Mands);
3159	false ->
3160	    error({missing_mandatory_config, Mand})
3161    end.
3162
3163verify_illegal(_, []) ->
3164    ok;
3165verify_illegal(Conf, [Inv|Invs]) ->
3166    case lists:member(Inv, Conf) of
3167	false ->
3168	    verify_illegal(Conf, Invs);
3169	true ->
3170	    error({illegal_config, Inv})
3171    end.
3172
3173verify_someof(Conf, [Mand|Mands]) ->
3174    case lists:keymember(Mand, 1, Conf) of
3175	true ->
3176	    ok;
3177	false ->
3178	    case Mands of
3179		[] ->
3180		    error({missing_mandatory_config, Mand});
3181		_ ->
3182		    verify_someof(Conf, Mands)
3183	    end
3184    end.
3185
3186ensure_config([], Config) ->
3187    Config;
3188ensure_config([Default|Defaults], Config) ->
3189    case lists:keymember(element(1, Default), 1, Config) of
3190	true ->
3191	    ensure_config(Defaults, Config);
3192	false ->
3193	    ensure_config(Defaults, [Default|Config])
3194    end.
3195
3196
3197
3198%%%-------------------------------------------------------------------
3199%%%
3200%%% Mini MIB stuff
3201%%%
3202%%%-------------------------------------------------------------------
3203
3204init_mini_mib(MibFiles) ->
3205    MiniMibs = lists:flatten([do_load_mib(MibFile) || MibFile <- MibFiles]),
3206    MiniMIB  = remove_duplicates(lists:keysort(1, MiniMibs), []),
3207    init_mini_mib2(MiniMIB).
3208
3209remove_duplicates([], Res) ->
3210    Res;
3211remove_duplicates([X,X|T], Res) ->
3212    remove_duplicates([X|T], Res);
3213remove_duplicates([{Oid, Name, Type, _} = X, {Oid, Name, Type, _}|T], Res) ->
3214    remove_duplicates([X|T], Res);
3215remove_duplicates([X|T], Res) ->
3216    remove_duplicates(T, [X|Res]).
3217
3218init_mini_mib2([]) ->
3219    ok;
3220init_mini_mib2([{Oid, Name, Type, MibName}|MiniMib]) ->
3221    ?vtrace("init mini mib -> ~w: ~w [~w] from ~s",
3222	    [Name, Oid, Type,MibName ]),
3223    ets:insert(snmpm_mib_table, {{mini_mib, Oid}, Name, Type, MibName}),
3224    init_mini_mib2(MiniMib).
3225
3226
3227handle_load_mib(Mib) ->
3228    [{mibs, Mibs0}] = ets:lookup(snmpm_config_table, mibs),
3229    case lists:member(Mib, Mibs0) of
3230	true ->
3231	    {error, already_loaded};
3232	false ->
3233	    Mibs = [Mib|Mibs0],
3234	    case (catch do_load_mib(Mib)) of
3235		MiniElems when is_list(MiniElems) ->
3236		    ets:insert(snmpm_config_table, {mibs, Mibs}),
3237		    update_mini_mib(MiniElems),
3238		    ok;
3239		Error ->
3240		    Error
3241	    end
3242    end.
3243
3244update_mini_mib([]) ->
3245    ok;
3246update_mini_mib([{Oid, Name, Type, MibName}|Elems]) ->
3247    Key = {mini_mib, Oid},
3248    case ets:lookup(snmpm_mib_table, Key) of
3249	[{Key, _Name, _Type, _AnotherMibName}] ->
3250	    %% Already loaded from another mib
3251	    update_mini_mib(Elems);
3252	[] ->
3253	    %% Not yet loaded
3254	    ?vtrace("update mini mib -> ~w: ~w [~w] from ~s",
3255		    [Name, Oid, Type, MibName]),
3256	    ets:insert(snmpm_mib_table, {Key, Name, Type, MibName}),
3257	    update_mini_mib(Elems)
3258    end.
3259
3260
3261handle_unload_mib(Mib) ->
3262    Key = {mib, Mib},
3263    case ets:lookup(snmpm_mib_table, Key) of
3264	[{Key, MibName, _MibFile}] ->
3265	    do_unload_mib(MibName),
3266	    [{mibs, Mibs0}] = ets:lookup(snmpm_config_table, mibs),
3267	    Mibs = lists:delete(Mib, Mibs0),
3268	    ets:insert(snmpm_config_table, {mibs, Mibs}),
3269	    ets:delete(snmpm_mib_table, Key),
3270	    ok;
3271	_ ->
3272	    {error, not_loaded}
3273    end.
3274
3275do_unload_mib(MibName) ->
3276    Pat  = {{mini_mib, '$1'}, '_', '_', MibName},
3277    Oids = ets:match(snmpm_mib_table, Pat),
3278    F    = fun([Oid]) -> ets:delete(snmpm_mib_table, {mini_mib, Oid}) end,
3279    lists:foreach(F, Oids).
3280
3281
3282do_load_mib(MibFile) ->
3283    ?vtrace("load mib ~s", [MibFile]),
3284    F1 = snmp_misc:strip_extension_from_filename(MibFile, ".bin"),
3285    ActualFileName = lists:append(F1, ".bin"),
3286    case snmp_misc:read_mib(ActualFileName) of
3287        {ok, #mib{name = Name, mes = MEs, traps = Traps}} ->
3288	    %% Check that the mib was not loaded or loaded
3289	    %% with a different filename:
3290	    %% e.g. /tmp/MYMIB.bin and /tmp/mibs/MYMIB.bin
3291	    Name1   = mib_name(Name),
3292	    Pattern = {{mib, '_'}, Name1, '$1'},
3293	    case ets:match(snmpm_mib_table, Pattern) of
3294		[] ->
3295
3296		    Rec = {{mib, MibFile}, Name1, ActualFileName},
3297		    ets:insert(snmpm_mib_table, Rec),
3298		    init_mini_mib_elems(Name1, MEs++Traps, []);
3299
3300		%% This means that the mib has already been loaded
3301		[[ActualFileName]] ->
3302		    [];
3303
3304		%% This means that the mib was loaded before,
3305		%% but under another filename
3306		[[OtherMibFile]] ->
3307		    error({already_loaded, MibFile, OtherMibFile})
3308	    end;
3309
3310        {error, Reason} ->
3311	    error({failed_reading_mib, MibFile, Reason})
3312    end.
3313
3314mib_name(N) when is_list(N) ->
3315    list_to_atom(N);
3316mib_name(N) ->
3317    N.
3318
3319init_mini_mib_elems(_, [], Res) ->
3320    Res;
3321init_mini_mib_elems(MibName,
3322		    [#me{aliasname = N,
3323			 oid       = Oid,
3324			 entrytype = variable,
3325			 asn1_type = #asn1_type{bertype = Type}} | T], Res) ->
3326    init_mini_mib_elems(MibName, T, [{Oid, N, Type, MibName}|Res]);
3327
3328init_mini_mib_elems(MibName,
3329		    [#me{aliasname = N,
3330			 oid       = Oid,
3331			 entrytype = table_column,
3332			 asn1_type = #asn1_type{bertype = Type}}|T], Res) ->
3333    init_mini_mib_elems(MibName, T, [{Oid, N, Type, MibName}|Res]);
3334
3335init_mini_mib_elems(MibName,
3336		    [#me{aliasname = N,
3337			 oid       = Oid,
3338			 asn1_type = undefined}|T], Res) ->
3339    init_mini_mib_elems(MibName, T, [{Oid, N, undefined, MibName}|Res]);
3340
3341init_mini_mib_elems(MibName,
3342		    [#notification{trapname = N,
3343				   oid      = Oid}|T], Res) ->
3344    init_mini_mib_elems(MibName, T, [{Oid, N, undefined, MibName}|Res]);
3345
3346init_mini_mib_elems(MibName, [_|T], Res) ->
3347    init_mini_mib_elems(MibName, T, Res).
3348
3349
3350
3351%%----------------------------------------------------------------------
3352
3353fix_address(Domain, Address) ->
3354    case snmp_conf:check_address(Domain, Address) of
3355	ok ->
3356	    Address;
3357	{ok, NAddress} ->
3358	    NAddress
3359    end.
3360
3361%%----------------------------------------------------------------------
3362
3363call(Req) ->
3364    call(Req, infinity).
3365
3366call(Req, To) ->
3367    gen_server:call(?SERVER, Req, To).
3368
3369% cast(Msg) ->
3370%     gen_server:cast(snmpm_server, Msg).
3371
3372
3373%%-------------------------------------------------------------------
3374
3375get_atl_dir(Opts) ->
3376    get_opt(dir, Opts).
3377
3378get_atl_type(Opts) ->
3379    case get_opt(type, Opts, read_write) of
3380	read_write ->
3381	    [read,write];
3382	read ->
3383	    [read];
3384	write ->
3385	    [write]
3386    end.
3387
3388get_atl_size(Opts) ->
3389    get_opt(size, Opts).
3390
3391get_atl_repair(Opts) ->
3392    get_opt(repair, Opts, truncate).
3393
3394get_atl_seqno(Opts) ->
3395    get_opt(seqno, Opts, false).
3396
3397
3398%%----------------------------------------------------------------------
3399
3400get_opt(Key, Opts) ->
3401    ?d("get option ~w from ~p", [Key, Opts]),
3402    snmp_misc:get_option(Key, Opts).
3403
3404get_opt(Key, Opts, Def) ->
3405    ?d("get option ~w with default ~p from ~p", [Key, Def, Opts]),
3406    snmp_misc:get_option(Key, Opts, Def).
3407
3408
3409%%----------------------------------------------------------------------
3410
3411get_info() ->
3412    ProcSize = proc_mem(self()),
3413    CntSz    = tab_size(snmpm_counter_table),
3414    StatsSz  = tab_size(snmpm_stats_table),
3415    MibSz    = tab_size(snmpm_mib_table),
3416    ConfSz   = tab_size(snmpm_config_table),
3417    AgentSz  = tab_size(snmpm_agent_table),
3418    UserSz   = tab_size(snmpm_user_table),
3419    UsmSz    = tab_size(snmpm_usm_table),
3420    [{process_memory, ProcSize},
3421     {db_memory, [{counter, CntSz},
3422		  {stats,   StatsSz},
3423		  {mib,     MibSz},
3424		  {config,  ConfSz},
3425		  {agent,   AgentSz},
3426		  {user,    UserSz},
3427		  {usm,     UsmSz}]}].
3428
3429proc_mem(P) when is_pid(P) ->
3430    case (catch erlang:process_info(P, memory)) of
3431	{memory, Sz} when is_integer(Sz) ->
3432	    Sz;
3433	_ ->
3434	    undefined
3435    end.
3436%% proc_mem(_) ->
3437%%     undefined.
3438
3439tab_size(T) ->
3440    case (catch ets:info(T, memory)) of
3441	Sz when is_integer(Sz) ->
3442	    Sz;
3443	_ ->
3444	    undefined
3445    end.
3446
3447
3448%%----------------------------------------------------------------------
3449
3450error(Reason) ->
3451    throw({error, Reason}).
3452
3453
3454%%----------------------------------------------------------------------
3455
3456info_msg(F, A) ->
3457    ?snmpm_info("Config server: " ++ F, A).
3458
3459warning_msg(F, A) ->
3460    ?snmpm_warning("Config server: " ++ F, A).
3461
3462error_msg(F, A) ->
3463    ?snmpm_error("Config server: " ++ F, A).
3464
3465%% p(F) ->
3466%%     p(F, []).
3467
3468%% p(F, A) ->
3469%%     io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
3470