1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-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-module(snmp).
21
22
23%%----------------------------------------------------------------------
24%% This module contains the user interface to the snmp toolkit.
25%%----------------------------------------------------------------------
26
27%% Application exports
28-export([start/0, start/1, stop/0,
29	 start_agent/0,   start_agent/1,
30	 start_manager/0, start_manager/1,
31	 config/0,
32
33         versions1/0,      versions2/0,
34	 print_versions/1, print_versions/2,
35	 print_version_info/0, print_version_info/1,
36
37	 date_and_time/0,
38	 universal_time_to_date_and_time/1,
39	 local_time_to_date_and_time_dst/1,
40	 date_and_time_to_universal_time_dst/1,
41	 validate_date_and_time/1, validate_date_and_time/2,
42	 date_and_time_to_string/1, date_and_time_to_string/2,
43	 date_and_time_to_string2/1,
44
45	 str_apply/1,
46
47	 sys_up_time/1, system_start_time/1,
48
49         passwd2localized_key/3, localize_key/3,
50
51	 read_mib/1,
52
53	 log_to_txt/5, log_to_txt/6, log_to_txt/7, log_to_txt/8,
54	 log_to_io/4,  log_to_io/5,  log_to_io/6,  log_to_io/7,
55	 change_log_size/2,
56
57	 octet_string_to_bits/1, bits_to_octet_string/1,
58
59	 enable_trace/0, disable_trace/0,
60	 set_trace/1, reset_trace/1,
61	 set_trace/2, set_trace/3]).
62
63%% Compiler exports
64-export([c/1, c/2, is_consistent/1, mib_to_hrl/1,
65	 compile/3]).
66
67%% Agent exports (Dont use these, they will be removed in OTP 24)
68-export([current_request_id/0, current_community/0, current_address/0,
69	 current_context/0, current_net_if_data/0,
70
71	 get_symbolic_store_db/0,
72	 name_to_oid/1, name_to_oid/2,
73	 oid_to_name/1, oid_to_name/2,
74	 int_to_enum/2, int_to_enum/3,
75	 enum_to_int/2, enum_to_int/3,
76
77	 get/2,
78	 info/1,
79	 load_mibs/2, unload_mibs/2, dump_mibs/0, dump_mibs/1,
80
81	 register_subagent/3, unregister_subagent/2,
82
83	 send_notification/3, send_notification/4, send_notification/5,
84	 send_notification/6,
85	 send_trap/3, send_trap/4,
86
87	 add_agent_caps/2, del_agent_caps/1, get_agent_caps/0,
88
89	 log_to_txt/2, log_to_txt/3, log_to_txt/4,
90	 change_log_size/1
91
92	]).
93
94-export_type([
95	      dir/0,
96	      snmp_timer/0,
97
98              atl_type/0,
99              verbosity/0,
100
101	      engine_id/0,
102	      tdomain/0,
103	      community/0,
104	      mms/0,
105	      version/0,
106	      sec_model/0,
107	      sec_name/0,
108	      sec_level/0,
109
110	      oid/0,
111	      varbind/0,
112	      ivarbind/0,
113	      asn1_type/0,
114	      table_info/0,
115	      variable_info/0,
116	      me/0,
117	      trap/0,
118	      notification/0,
119	      pdu/0,
120	      trappdu/0,
121	      mib/0,
122	      mib_name/0,
123
124              error_status/0,
125              error_index/0,
126
127	      void/0
128	     ]).
129
130
131%% This is for XREF
132-deprecated(
133   [
134    {c,                     1, "use snmpc:compile/1 instead."},
135    {c,                     2, "use snmpc:compile/2 instead."},
136    {compile,               3, "use snmpc:compile/3 instead."},
137    {is_consistent,         1, "use snmpc:is_consistent/1 instead."},
138    {mib_to_hrl,            1, "use snmpc:mib_to_hrl/1 instead."},
139
140    {change_log_size,       1, "use snmpa:change_log_size/1 instead."},
141    {log_to_txt,            2, "use snmpa:log_to_txt/2 instead."},
142    {log_to_txt,            3, "use snmpa:log_to_txt/3 instead."},
143    {log_to_txt,            4, "use snmpa:log_to_txt/4 instead."},
144
145    {current_request_id,    0, "use snmpa:current_request_id/0 instead."},
146    {current_community,     0, "use snmpa:current_community/0 instead."},
147    {current_address,       0, "use snmpa:current_address/0 instead."},
148    {current_context,       0, "use snmpa:current_context/0 instead."},
149    {current_net_if_data,   0, "use snmpa:current_net_if_data/0 instead."},
150
151    {get_symbolic_store_db, 0, "use snmpa:get_symbolic_store_db/0 instead."},
152    {name_to_oid,           1, "use snmpa:name_to_oid/1 instead."},
153    {name_to_oid,           2, "use snmpa:name_to_oid/2 instead."},
154    {oid_to_name,           1, "use snmpa:oid_to_name/1 instead."},
155    {oid_to_name,           2, "use snmpa:oid_to_name/2 instead."},
156    {int_to_enum,           2, "use snmpa:int_to_enum/2 instead."},
157    {int_to_enum,           3, "use snmpa:int_to_enum/3 instead."},
158    {enum_to_int,           2, "use snmpa:enum_to_int/2 instead."},
159    {enum_to_int,           3, "use snmpa:enum_to_int/3 instead."},
160
161    {get,                   2, "use snmpa:get/2 instead."},
162    {info,                  1, "use snmpa:info/1 instead."},
163    {load_mibs,             2, "use snmpa:load_mibs/2 instead."},
164    {unload_mibs,           2, "use snmpa:unload_mibs/2 instead."},
165    {dump_mibs,             0, "use snmpa:dump_mibs/0 instead."},
166    {dump_mibs,             1, "use snmpa:dump_mibs/1 instead."},
167
168    {register_subagent,     3, "use snmpa:register_subagent/3 instead."},
169    {unregister_subagent,   2, "use snmpa:unregister_subagent/2 instead."},
170
171    {send_notification,     3, "use snmpa:send_notification/3 instead."},
172    {send_notification,     4, "use snmpa:send_notification/4 instead."},
173    {send_notification,     5, "use snmpa:send_notification/5 instead."},
174    {send_notification,     6, "use snmpa:send_notification/6 instead."},
175    {send_trap,             3, "use snmpa:send_trap/3 instead."},
176    {send_trap,             4, "use snmpa:send_trap/4 instead."},
177
178    {add_agent_caps,        2, "use snmpa:add_agent_caps/2 instead."},
179    {del_agent_caps,        1, "use snmpa:del_agent_caps/1 instead."},
180    {get_agent_caps,        0, "use snmpa:get_agent_caps/0 instead."}
181   ]).
182
183
184-define(APPLICATION, snmp).
185-define(ATL_BLOCK_DEFAULT, true).
186
187-include_lib("snmp/include/snmp_types.hrl").
188
189
190%%-----------------------------------------------------------------
191%% Types
192%%-----------------------------------------------------------------
193
194-type dir()           :: string().
195-type snmp_timer()    :: #snmp_incr_timer{}.
196
197-type atl_type()      :: read | write | read_write.
198-type verbosity()     :: info | log | debug | trace | silence.
199
200-type engine_id()     :: string().
201-type tdomain()       :: transportDomainUdpIpv4 | transportDomainUdpIpv6.
202-type community()     :: string().
203-type mms()           :: non_neg_integer().
204-type version()       :: v1 | v2 | v3.
205-type sec_model()     :: any | v1 | v2c | usm.
206-type sec_name()      :: string().
207-type sec_level()     :: noAuthNoPriv | authNoPriv | authPriv.
208
209-type oid()           :: [non_neg_integer()].
210-type varbind()       :: #varbind{}.
211-type ivarbind()      :: #ivarbind{}.
212-type asn1_type()     :: #asn1_type{}.
213-type table_info()    :: #table_info{}.
214-type variable_info() :: #variable_info{}.
215-type me()            :: #me{}.
216-type trap()          :: #trap{}.
217-type notification()  :: #notification{}.
218-type mib()           :: #mib{}.
219-type mib_name()      :: string().
220-type pdu()           :: #pdu{}.
221-type trappdu()       :: #trappdu{}.
222
223%% We should really specify all of these, but they are so numerous...
224%% See the validate_err/1 function in the snmpa_agent.
225%% Here are a number of them:
226%% badValue |
227%% commitFailed |
228%% genErr |
229%% inconsistentName | inconsistentValue |
230%% noAccess | noCreation |
231%% noSuchInstance | noSuchName | noSuchObject |
232%% notWritable |
233%% resourceUnavailable |
234%% undoFailed |
235%% wrongValue
236
237-type error_status()  :: atom().
238-type error_index()   :: pos_integer().
239
240-type void()          :: term().
241
242
243%%-----------------------------------------------------------------
244%% Application
245%%-----------------------------------------------------------------
246
247start() ->
248    application:start(?APPLICATION).
249
250stop() ->
251    application:stop(?APPLICATION).
252
253start(p) ->
254    start(permanent);
255start(tr) ->
256    start(transient);
257start(te) ->
258    start(temporary);
259start(Type) ->
260    application:start(?APPLICATION, Type).
261
262
263start_agent() ->
264    snmp_app:start_agent().
265
266start_agent(Type) ->
267    snmp_app:start_agent(Type).
268
269start_manager() ->
270    snmp_app:start_manager().
271
272start_manager(Type) ->
273    snmp_app:start_manager(Type).
274
275
276config() -> snmp_config:config().
277
278
279%%-----------------------------------------------------------------
280
281enable_trace() ->
282    HandleSpec = {fun handle_trace_event/2, dummy},
283    dbg:tracer(process, HandleSpec).
284
285disable_trace() ->
286    dbg:stop().
287
288set_trace(Module) when is_atom(Module) ->
289    set_trace([Module]);
290set_trace(Modules) when is_list(Modules) ->
291    Opts = [], % Use default values for all options
292    set_trace(Modules, Opts).
293
294reset_trace(Module) when is_atom(Module) ->
295    set_trace(Module, disable);
296reset_trace(Modules) when is_list(Modules) ->
297    set_trace(Modules, disable).
298
299set_trace(Module, disable) when is_atom(Module) ->
300    dbg:ctp(Module);
301set_trace(Module, Opts) when is_atom(Module) andalso is_list(Opts) ->
302    (catch set_trace(all, Module, Opts));
303set_trace(Modules, Opts) when is_list(Modules) ->
304    (catch set_trace(all, Modules, Opts)).
305
306set_trace(Item, Module, Opts) when is_atom(Module) ->
307    set_trace(Item, [{Module, []}], Opts);
308set_trace(_Item, Modules, disable) when is_list(Modules) ->
309    DisableTrace =
310	fun(Module) when is_atom(Module) ->
311		dbg:ctp(Module);
312	   (_) ->
313		ok
314	end,
315    lists:foreach(DisableTrace, Modules);
316set_trace(Item, Modules, Opts) when is_list(Modules) ->
317    Mods = parse_modules(Modules, Opts),
318    SetTrace =
319	fun({Module, ModOpts}) ->
320		set_module_trace(Module, ModOpts)
321	end,
322    lists:foreach(SetTrace, Mods),
323    Flags =
324	case lists:keysearch(timestamp, 1, Opts) of
325	    {value, {timestamp, false}} ->
326		[call];
327	    _ ->
328		[call, timestamp]
329	end,
330    case dbg:p(Item, Flags) of
331	{ok, _} ->
332	    ok;
333	Error ->
334	    Error
335    end.
336
337set_module_trace(Module, disable) ->
338    dbg:ctp(Module);
339set_module_trace(Module, Opts) ->
340    ReturnTrace =
341	case lists:keysearch(return_trace, 1, Opts) of
342	    {value, {return_trace, false}} ->
343		[];
344	    _ ->
345		%% Default is allways  to include return values
346		[{return_trace}]
347	end,
348    TraceRes =
349	case lists:keysearch(scope, 1, Opts) of
350	    {value, {scope, all_functions}} ->
351		dbg:tpl(Module, [{'_', [], ReturnTrace}]);
352	    {value, {scope, exported_functions}}  ->
353		dbg:tp(Module, [{'_', [], ReturnTrace}]);
354	    {value, {scope, Func}}  when is_atom(Func) ->
355		dbg:tpl(Module, Func, [{'_', [], ReturnTrace}]);
356	    {value, {scope, {Func, Arity}}}  when is_atom(Func) andalso
357						  is_integer(Arity) ->
358		dbg:tpl(Module, Func, Arity, [{'_', [], ReturnTrace}]);
359	    false ->
360	    %% Default scope is exported functions
361		dbg:tp(Module, [{'_', [], ReturnTrace}])
362	end,
363    case TraceRes of
364	{error, Reason} ->
365	    throw({error, {failed_enabling_trace, Module, Opts, Reason}});
366	_ ->
367	    ok
368    end.
369
370
371parse_modules(Modules, Opts) ->
372    parse_modules(Modules, Opts, []).
373
374parse_modules([], _Opts, Acc) ->
375    lists:reverse(Acc);
376
377parse_modules([Module|Modules], Opts, Acc)
378  when is_atom(Module) andalso is_list(Opts) ->
379    parse_modules(Modules, Opts, [{Module, Opts}|Acc]);
380
381parse_modules([{Module, ModOpts}|Modules], Opts, Acc)
382  when is_atom(Module) andalso is_list(ModOpts) andalso is_list(Opts) ->
383    NewModOpts = update_trace_options(Opts, ModOpts),
384    parse_modules(Modules, Opts, [{Module, NewModOpts}|Acc]);
385
386parse_modules([_|Modules], Opts, Acc) ->
387    parse_modules(Modules, Opts, Acc).
388
389
390update_trace_options([], Opts) ->
391    Opts;
392update_trace_options([{Key, _} = Opt|Opts], ModOpts) ->
393    case lists:keysearch(Key, 1, ModOpts) of
394	{value, _} ->
395	    update_trace_options(Opts, ModOpts);
396	_ ->
397	    update_trace_options(Opts, [Opt|ModOpts])
398    end;
399update_trace_options([_|Opts], ModOpts) ->
400    update_trace_options(Opts, ModOpts).
401
402
403handle_trace_event({trace, Who, call, Event}, Data) ->
404    io:format("*** call trace event *** "
405	      "~n   Who:   ~p"
406	      "~n   Event: ~p"
407	      "~n", [Who, Event]),
408    Data;
409handle_trace_event({trace, Who, return_from, Func, Value}, Data) ->
410    io:format("*** return trace event *** "
411	      "~n   Who:      ~p"
412	      "~n   Function: ~p"
413	      "~n   Value:    ~p"
414	      "~n", [Who, Func, Value]),
415    Data;
416handle_trace_event({trace_ts, Who, call, {Mod, Func, Args}, Ts}, Data)
417  when is_atom(Mod) andalso is_atom(Func) andalso is_list(Args) ->
418    io:format("*** call trace event ~s *** "
419	      "~n   Who:  ~p"
420	      "~n   Mod:  ~p"
421	      "~n   Func: ~p"
422	      "~n   Args: ~p"
423	      "~n", [format_timestamp(Ts), Who, Mod, Func, Args]),
424    Data;
425handle_trace_event({trace_ts, Who, call, Event, Ts}, Data) ->
426    io:format("*** call trace event ~s *** "
427	      "~n   Who:   ~p"
428	      "~n   Event: ~p"
429	      "~n", [format_timestamp(Ts), Who, Event]),
430    Data;
431handle_trace_event({trace_ts, Who, return_from, {Mod, Func, Arity}, Value, Ts},
432		   Data)
433  when is_atom(Mod) andalso is_atom(Func) andalso is_integer(Arity) ->
434    io:format("*** return trace event ~s *** "
435	      "~n   Who:   ~p"
436	      "~n   Mod:   ~p"
437	      "~n   Func:  ~p"
438	      "~n   Arity: ~p"
439	      "~n   Value: ~p"
440	      "~n", [format_timestamp(Ts), Who, Mod, Func, Arity, Value]),
441    Data;
442handle_trace_event({trace_ts, Who, return_from, Func, Value, Ts}, Data) ->
443    io:format("*** return trace event ~s *** "
444	      "~n   Who:      ~p"
445	      "~n   Function: ~p"
446	      "~n   Value:    ~p"
447	      "~n", [format_timestamp(Ts), Who, Func, Value]),
448    Data;
449handle_trace_event(TraceEvent, Data) ->
450    io:format("*** trace event *** "
451	      "~n   TraceEvent: ~p"
452	      "~n", [TraceEvent]),
453    Data.
454
455format_timestamp({_N1, _N2, N3} = Now) ->
456    {Date, Time}   = calendar:now_to_datetime(Now),
457    {YYYY,MM,DD}   = Date,
458    {Hour,Min,Sec} = Time,
459    FormatDate =
460        io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
461                      [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
462    lists:flatten(FormatDate).
463
464
465%%-----------------------------------------------------------------
466%% {ok, Vs} = snmp:versions1(), snmp:print_versions(Vs).
467
468print_version_info() ->
469    {ok, Vs} = versions1(),
470    print_versions(Vs).
471
472print_version_info(Prefix) ->
473    {ok, Vs} = versions1(),
474    print_versions(Prefix, Vs).
475
476print_versions(Versions) ->
477    print_versions("", Versions).
478
479print_versions(Prefix, Versions)
480  when is_list(Prefix) andalso is_list(Versions) ->
481    do_print_versions(Prefix, Versions);
482print_versions(Prefix, Versions)
483  when (is_integer(Prefix) andalso (Prefix >= 0)) andalso is_list(Versions) ->
484    do_print_versions(lists:duplicate(Prefix, $ ), Versions);
485print_versions(Prefix, BadVersions)
486  when is_list(Prefix) orelse (is_integer(Prefix) andalso (Prefix >= 0)) ->
487    {error, {bad_versions, BadVersions}};
488print_versions(Prefix, BadVersions)
489  when is_list(BadVersions) ->
490    {error, {bad_prefix, Prefix}};
491print_versions(Prefix, BadVersions) ->
492    {error, {bad_args, Prefix, BadVersions}}.
493
494do_print_versions(Prefix, Versions) ->
495    print_sys_info(Prefix, Versions),
496    print_os_info(Prefix, Versions),
497    print_mods_info(Prefix, Versions).
498
499print_sys_info(Prefix, Versions) ->
500    case key1search(sys_info, Versions) of
501        {value, SysInfo} when is_list(SysInfo) ->
502            {value, Arch} = key1search(arch, SysInfo, "Not found"),
503            {value, Ver}  = key1search(ver, SysInfo, "Not found"),
504            io:format("~sSystem info: "
505                      "~n~s   Arch: ~s"
506                      "~n~s   Ver:  ~s"
507                      "~n", [Prefix,
508			     Prefix, Arch,
509			     Prefix, Ver]),
510            ok;
511        _ ->
512            io:format("System info: Not found~n", []),
513            not_found
514    end.
515
516print_os_info(Prefix, Versions) ->
517    case key1search(os_info, Versions) of
518        {value, OsInfo} when is_list(OsInfo) ->
519            Fam =
520                case key1search(fam, OsInfo, "Not found") of
521                    {value, F} when is_atom(F) ->
522                        atom_to_list(F);
523                    {value, LF} when is_list(LF) ->
524                        LF;
525                    {value, XF} ->
526                        lists:flatten(io_lib:format("~p", [XF]))
527                end,
528            Name =
529                case key1search(name, OsInfo) of
530                    {value, N} when is_atom(N) ->
531                        "[" ++ atom_to_list(N) ++ "]";
532                    {value, LN} when is_list(LN) ->
533                        "[" ++ LN ++ "]";
534                    not_found ->
535                        ""
536                end,
537            Ver =
538                case key1search(ver, OsInfo, "Not found") of
539                    {value, T} when is_tuple(T) ->
540                        tversion(T);
541                    {value, LV} when is_list(LV) ->
542                        LV;
543                    {value, XV} ->
544                        lists:flatten(io_lib:format("~p", [XV]))
545                end,
546            io:format("~sOS info: "
547                      "~n~s   Family: ~s ~s"
548                      "~n~s   Ver:    ~s"
549                      "~n", [Prefix,
550			     Prefix, Fam, Name,
551			     Prefix, Ver]),
552            ok;
553        _ ->
554            io:format("~sOS info:     Not found~n", [Prefix]),
555            not_found
556    end.
557
558tversion(T) ->
559    L = tuple_to_list(T),
560    lversion(L).
561
562lversion([]) ->
563    "";
564lversion([A]) ->
565    integer_to_list(A);
566lversion([A|R]) ->
567    integer_to_list(A) ++ "." ++ lversion(R).
568
569print_mods_info(Prefix, Versions) ->
570    case key1search(mod_info, Versions) of
571        {value, ModsInfo} when is_list(ModsInfo) ->
572            io:format("~sModule info: ~n", [Prefix]),
573	    F = fun(MI) -> print_mod_info(Prefix, MI) end,
574            lists:foreach(F, ModsInfo);
575        _ ->
576            io:format("~sModule info: Not found~n", [Prefix]),
577            not_found
578    end.
579
580print_mod_info(Prefix, {Module, Info}) ->
581    Vsn =
582        case key1search(vsn, Info) of
583            {value, I} when is_integer(I) ->
584                integer_to_list(I);
585            _ ->
586                "Not found"
587        end,
588    AppVsn =
589        case key1search(app_vsn, Info) of
590            {value, S1} when is_list(S1) ->
591                S1;
592            _ ->
593                "Not found"
594        end,
595    CompVer =
596        case key1search(compiler_version, Info) of
597            {value, S2} when is_list(S2) ->
598                S2;
599            _ ->
600                "Not found"
601        end,
602    Digest =
603        case key1search(md5, Info) of
604            {value, MD5} when is_binary(MD5) ->
605		[io_lib:format("~2.16.0b", [Byte]) || <<Byte>> <= MD5];
606            _ ->
607                "Not found"
608        end,
609    io:format("~s   ~w:~n"
610              "~s      Vsn:          ~s~n"
611              "~s      App vsn:      ~s~n"
612              "~s      Compiler ver: ~s~n"
613              "~s      MD5 digest:   ~s~n",
614              [Prefix, Module,
615	       Prefix, Vsn,
616	       Prefix, AppVsn,
617	       Prefix, CompVer,
618	       Prefix, Digest]),
619    ok.
620
621key1search(Key, Vals) ->
622    case lists:keysearch(Key, 1, Vals) of
623        {value, {Key, Val}} ->
624            {value, Val};
625        false ->
626            not_found
627    end.
628
629key1search(Key, Vals, Def) ->
630    case key1search(Key, Vals) of
631	not_found ->
632	    {value, Def};
633	Value ->
634	    Value
635    end.
636
637
638%%-----------------------------------------------------------------
639
640versions1() ->
641    case ms1() of
642        {ok, Mods} ->
643            {ok, version_info(Mods)};
644        Error ->
645            Error
646    end.
647
648versions2() ->
649    case ms2() of
650        {ok, Mods} ->
651            {ok, version_info(Mods)};
652        Error ->
653            Error
654    end.
655
656version_info(Mods) ->
657    SysInfo = sys_info(),
658    OsInfo  = os_info(),
659    ModInfo = [mod_version_info(Mod) || Mod <- Mods],
660    [{sys_info, SysInfo}, {os_info, OsInfo}, {mod_info, ModInfo}].
661
662mod_version_info(Mod) ->
663    Info = Mod:module_info(),
664    {Mod,
665     case key1search(attributes, Info) of
666	 {value, Attr} ->
667	     case key1search(vsn, Attr) of
668		 {value, [Vsn]} ->
669		     [{vsn, Vsn}];
670		 not_found ->
671		     []
672	     end ++
673		 case key1search(app_vsn, Attr) of
674		     {value, AppVsn} ->
675			 [{app_vsn, AppVsn}];
676		     not_found ->
677			 []
678		 end;
679	 not_found ->
680	     []
681     end ++
682	 case key1search(compile, Info) of
683	     {value, Comp} ->
684		 case key1search(version, Comp) of
685		     {value, Ver} ->
686			 [{compiler_version, Ver}];
687		     not_found ->
688			 []
689		 end ++
690		     case key1search(time, Comp) of
691			 {value, Ver} ->
692			     [{compile_time, Ver}];
693			 not_found ->
694			     []
695		     end;
696	     not_found ->
697		 []
698	 end ++
699	 case key1search(md5, Info) of
700	     {value, Bin} ->
701		 [{md5, Bin}];
702	     not_found ->
703		 []
704	 end}.
705
706sys_info() ->
707    SysArch = string:strip(erlang:system_info(system_architecture),right,$\n),
708    SysVer  = string:strip(erlang:system_info(system_version),right,$\n),
709    [{arch, SysArch}, {ver, SysVer}].
710
711os_info() ->
712    {OsFam, OsName} = os:type(),
713    [{fam, OsFam}, {name, OsName}, {ver, os:version()}].
714
715ms1() ->
716    App    = ?APPLICATION,
717    LibDir = code:lib_dir(App),
718    File   = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
719    case file:consult(File) of
720        {ok, [{application, App, AppFile}]} ->
721            case lists:keysearch(modules, 1, AppFile) of
722                {value, {modules, Mods}} ->
723                    {ok, Mods};
724                _ ->
725                    {error, {invalid_format, modules}}
726            end;
727        Error ->
728            {error, {invalid_format, Error}}
729    end.
730
731ms2() ->
732    application:get_key(?APPLICATION, modules).
733
734
735%%-----------------------------------------------------------------
736%% Returns: current time as a DateAndTime type (defined in rfc1903)
737%%-----------------------------------------------------------------
738date_and_time() ->
739    UTC   = calendar:universal_time(),
740    Local = calendar:universal_time_to_local_time(UTC),
741    date_and_time(Local, UTC).
742
743date_and_time(Local, UTC) ->
744    DiffSecs = calendar:datetime_to_gregorian_seconds(Local) -
745	calendar:datetime_to_gregorian_seconds(UTC),
746    short_time(Local) ++ diff(DiffSecs).
747
748short_time({{Y,M,D},{H,Mi,S}}) ->
749    [y1(Y), y2(Y), M, D, H, Mi, S, 0].
750
751%% This function will only be called if there has been some
752%% validation error, and as it is strict, it allways returns
753%% false.
754strict_validation(_What, _Data) ->
755    false.
756
757kiribati_validation(diff, Diff) ->
758    check_kiribati_diff(Diff);
759kiribati_validation(_What, _Data) ->
760    false.
761
762check_kiribati_diff([$+, H, M])
763  when ((0 =< H) andalso (H < 14) andalso (0 =< M) andalso (M < 60)) orelse
764       ((H =:= 14) andalso (M =:= 0)) ->
765    true;
766check_kiribati_diff([$-, H, M])
767  when ((0 =< H) andalso (H < 14) andalso (0 =< M) andalso (M < 60)) orelse
768       ((H =:= 14) andalso (M =:= 0)) ->
769    true;
770check_kiribati_diff(_) ->
771    false.
772
773
774date_and_time_to_string2(DAT) ->
775    Validate = fun(What, Data) -> kiribati_validation(What, Data) end,
776    date_and_time_to_string(DAT, Validate).
777
778date_and_time_to_string(DAT) ->
779    Validate = fun(What, Data) -> strict_validation(What, Data) end,
780    date_and_time_to_string(DAT, Validate).
781date_and_time_to_string(DAT, Validate) when is_function(Validate) ->
782    case validate_date_and_time(DAT, Validate) of
783	true ->
784	        dat2str(DAT);
785	false ->
786	        exit({badarg, {?MODULE, date_and_time_to_string, [DAT]}})
787    end.
788
789dat2str([Y1,Y2, Mo, D, H, M, S, Ds | Diff]) ->
790    lists:flatten(io_lib:format("~w-~w-~w,~w:~w:~w.~w",
791				[y(Y1,Y2),Mo,D,H,M,S,Ds]) ++
792		    case Diff of
793			      [Sign,Hd,Md] ->
794			      io_lib:format(",~c~w:~w",
795					    [Sign,Hd,Md]);
796			      _ -> []
797		      end).
798
799
800y1(Y) -> (Y bsr 8) band 255.
801y2(Y) -> Y band 255.
802
803y(Y1, Y2) -> 256 * Y1 + Y2.
804
805diff(Secs) ->
806    case calendar:seconds_to_daystime(Secs) of
807	{0, {H, M,_}} ->
808	        [$+, H, M];
809	{-1, _} ->
810	        {0, {H, M, _}} = calendar:seconds_to_daystime(-Secs),
811	        [$-, H, M]
812    end.
813
814universal_time_to_date_and_time(UTC) ->
815    short_time(UTC) ++ [$+, 0, 0].
816
817local_time_to_date_and_time_dst(Local) ->
818    case calendar:local_time_to_universal_time_dst(Local) of
819	[] ->
820	    [];
821	[UTC] ->
822	    [date_and_time(Local, UTC)];
823	[UTC1, UTC2] ->
824	    [date_and_time(Local, UTC1), date_and_time(Local, UTC2)]
825    end.
826
827date_and_time_to_universal_time_dst([Y1, Y2, Mo, D, H, M, S, _Ds]) ->
828    %% Local time specified, convert to UTC
829    Local = {{y(Y1,Y2), Mo, D}, {H, M, S}},
830    calendar:local_time_to_universal_time_dst(Local);
831date_and_time_to_universal_time_dst([Y1, Y2, Mo, D, H, M, S, _Ds, Sign, Hd, Md]) ->
832     %% Time specified as local time + diff from UTC. Conv to UTC.
833    Local = {{y(Y1,Y2), Mo, D}, {H, M, S}},
834    LocalSecs = calendar:datetime_to_gregorian_seconds(Local),
835    Diff = (Hd*60 + Md)*60,
836    UTCSecs = if Sign == $+ -> LocalSecs - Diff;
837		 Sign == $- -> LocalSecs + Diff
838	      end,
839    [calendar:gregorian_seconds_to_datetime(UTCSecs)].
840
841
842validate_date_and_time(DateAndTime) ->
843    Validate = fun(What, Data) -> strict_validation(What, Data) end,
844    validate_date_and_time(DateAndTime, Validate).
845
846validate_date_and_time(DateAndTime, Validate) when is_function(Validate) ->
847    do_validate_date_and_time(DateAndTime, Validate).
848
849do_validate_date_and_time([Y1,Y2, Mo, D, H, M, S, Ds | Diff], Validate)
850  when ((0 =< Y1) andalso (0 =< Y2)) andalso
851       ((0 < Mo) andalso (Mo < 13)) andalso
852       ((0 < D) andalso (D < 32) andalso (0 =< H)) andalso
853       (H < 24) andalso
854       ((0 =< M) andalso (M < 60)) andalso
855       ((0 =< S) andalso (S < 61)) andalso
856       ((0 =< Ds) andalso (Ds < 10)) ->
857    case check_diff(Diff, Validate) of
858	true ->
859	    Year = y(Y1,Y2),
860	    case calendar:valid_date(Year, Mo, D) of
861		true ->
862		    true;
863		_ ->
864		    Validate(valid_date, {Year, Mo, D})
865	    end;
866	false ->
867	    false
868    end;
869do_validate_date_and_time([Y1,Y2, Mo, D, H, M, S, Ds | Diff], Validate) ->
870    Valid =
871	Validate(year,         {Y1, Y2}) andalso
872	Validate(month,        Mo)       andalso
873	Validate(day,          D)        andalso
874	Validate(hour,         H)        andalso
875	Validate(minute,       M)        andalso
876	Validate(seconds,      S)        andalso
877	Validate(deci_seconds, Ds),
878    if
879	Valid =:= true ->
880	    case check_diff(Diff, Validate) of
881		true ->
882		    Year = y(Y1,Y2),
883		    case calendar:valid_date(Year, Mo, D) of
884			true ->
885			    true;
886			_ ->
887			    Validate(valid_date, {Year, Mo, D})
888		    end;
889		false ->
890		    false
891	    end;
892	true ->
893	    false
894    end;
895do_validate_date_and_time(_, _) ->
896    false.
897
898%% OTP-4206 (now according to RFC-2579)
899check_diff([], _) ->
900    true;
901check_diff([$+, H, M], _)
902  when (0 =< H) andalso (H < 14) andalso (0 =< M) andalso (M < 60) ->
903    true;
904check_diff([$-, H, M], _)
905  when (0 =< H) andalso (H < 14) andalso (0 =< M) andalso (M < 60) ->
906    true;
907check_diff(Diff, Validate) ->
908    Validate(diff, Diff).
909
910
911%%-----------------------------------------------------------------
912%% System start- and up-time
913%%-----------------------------------------------------------------
914
915system_start_time(agent) ->
916    snmpa:system_start_time();
917system_start_time(manager) ->
918    snmpm:system_start_time().
919
920sys_up_time(agent) ->
921    snmpa:sys_up_time();
922sys_up_time(manager) ->
923    snmpm:sys_up_time().
924
925
926
927%%-----------------------------------------------------------------
928%% Utility functions for OCTET-STRING / BITS conversion.
929%%-----------------------------------------------------------------
930
931octet_string_to_bits(S) ->
932    snmp_pdus:octet_str_to_bits(S).
933
934bits_to_octet_string(B) ->
935    snmp_pdus:bits_to_str(B).
936
937
938%%%-----------------------------------------------------------------
939%%% USM functions
940%%%-----------------------------------------------------------------
941
942passwd2localized_key(Alg, Passwd, EngineID) ->
943    snmp_usm:passwd2localized_key(Alg, Passwd, EngineID).
944
945localize_key(Alg, Key, EngineID) ->
946    snmp_usm:localize_key(Alg, Key, EngineID).
947
948
949%%%-----------------------------------------------------------------
950%%% Read a mib
951%%%-----------------------------------------------------------------
952
953read_mib(FileName) ->
954    snmp_misc:read_mib(FileName).
955
956
957%%%-----------------------------------------------------------------
958%%% Audit Trail Log functions
959%%%-----------------------------------------------------------------
960
961log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) ->
962    Block = ?ATL_BLOCK_DEFAULT,
963    Start = null,
964    Stop  = null,
965    log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
966
967log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block)
968  when ((Block =:= true) orelse (Block =:= false)) ->
969    Start = null,
970    Stop  = null,
971    log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop);
972log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) ->
973    Block = ?ATL_BLOCK_DEFAULT,
974    Stop  = null,
975    log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
976
977log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start)
978  when ((Block =:= true) orelse (Block =:= false)) ->
979    Stop  = null,
980    log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop);
981log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) ->
982    Block = ?ATL_BLOCK_DEFAULT,
983    log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
984
985log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) ->
986    snmp_log:log_to_txt(LogName, Block, LogFile, LogDir, Mibs, OutFile,
987			Start, Stop).
988
989
990log_to_io(LogDir, Mibs, LogName, LogFile) ->
991    Block = ?ATL_BLOCK_DEFAULT,
992    Start = null,
993    Stop  = null,
994    log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
995
996log_to_io(LogDir, Mibs, LogName, LogFile, Block)
997  when ((Block =:= true) orelse (Block =:= false)) ->
998    Start = null,
999    Stop  = null,
1000    log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop);
1001log_to_io(LogDir, Mibs, LogName, LogFile, Start) ->
1002    Block = ?ATL_BLOCK_DEFAULT,
1003    Stop  = null,
1004    log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
1005
1006log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start)
1007  when ((Block =:= true) orelse (Block =:= false)) ->
1008    Stop  = null,
1009    log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop);
1010log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) ->
1011    Block = ?ATL_BLOCK_DEFAULT,
1012    log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
1013
1014log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) ->
1015    snmp_log:log_to_io(LogName, Block, LogFile, LogDir, Mibs, Start, Stop).
1016
1017change_log_size(LogName, NewSize) ->
1018    snmp_log:change_size(LogName, NewSize).
1019
1020
1021%%%-----------------------------------------------------------------
1022%%% Misc
1023%%%-----------------------------------------------------------------
1024
1025%% Usage: erl -s snmp str_apply '{Mod,Func,ArgList}'
1026str_apply([Atom]) ->
1027    Str = atom_to_list(Atom),
1028    {Mod, Func, Args} = to_erlang_term(Str),
1029    apply(Mod, Func, Args).
1030
1031to_erlang_term(String) ->
1032    {ok, Tokens, _} = erl_scan:string(lists:append([String, ". "])),
1033    {ok, Term}      = erl_parse:parse_term(Tokens),
1034    Term.
1035
1036
1037%%%-----------------------------------------------------------------
1038%%% BACKWARD COMPATIBILLITY CRAP
1039%%%-----------------------------------------------------------------
1040
1041c(File) -> snmpc:compile(File).
1042c(File, Options) -> snmpc:compile(File, Options).
1043
1044is_consistent(Filenames) ->
1045    snmpc:is_consistent(Filenames).
1046
1047mib_to_hrl(MibName) ->
1048    snmpc:mib_to_hrl(MibName).
1049
1050compile(Input, Output, Options) ->
1051    snmpc:compile(Input, Output, Options).
1052
1053get_symbolic_store_db() -> snmpa:get_symbolic_store_db().
1054
1055name_to_oid(Name)           -> snmpa:name_to_oid(Name).
1056name_to_oid(Db, Name)       -> snmpa:name_to_oid(Db, Name).
1057oid_to_name(OID)            -> snmpa:oid_to_name(OID).
1058oid_to_name(Db, OID)        -> snmpa:oid_to_name(Db, OID).
1059enum_to_int(Name, Enum)     -> snmpa:enum_to_int(Name, Enum).
1060enum_to_int(Db, Name, Enum) -> snmpa:enum_to_int(Db, Name, Enum).
1061int_to_enum(Name, Int)      -> snmpa:int_to_enum(Name, Int).
1062int_to_enum(Db, Name, Int)  -> snmpa:int_to_enum(Db, Name, Int).
1063
1064current_request_id()  -> snmpa:current_request_id().
1065current_context()     -> snmpa:current_context().
1066current_community()   -> snmpa:current_community().
1067current_address()     -> snmpa:current_address().
1068current_net_if_data() -> snmpa:current_net_if_data().
1069
1070get(Agent, Vars) -> snmpa:get(Agent, Vars).
1071info(Agent) -> snmpa:info(Agent).
1072dump_mibs()     -> snmpa:dump_mibs().
1073dump_mibs(File) -> snmpa:dump_mibs(File).
1074load_mibs(Agent, Mibs) -> snmpa:load_mibs(Agent, Mibs).
1075unload_mibs(Agent, Mibs) -> snmpa:unload_mibs(Agent, Mibs).
1076send_notification(Agent, Notification, Recv) ->
1077    snmpa:send_notification(Agent, Notification, Recv).
1078send_notification(Agent, Notification, Recv, Varbinds) ->
1079    snmpa:send_notification(Agent, Notification, Recv, Varbinds).
1080send_notification(Agent, Notification, Recv, NotifyName, Varbinds) ->
1081    snmpa:send_notification(Agent, Notification, Recv, NotifyName, Varbinds).
1082send_notification(Agent, Notification, Recv, NotifyName,
1083		  ContextName, Varbinds) ->
1084    snmpa:send_notification(Agent, Notification, Recv, NotifyName,
1085			    ContextName, Varbinds).
1086send_trap(Agent, Trap, Community) ->
1087    snmpa:send_trap(Agent, Trap, Community).
1088send_trap(Agent, Trap, Community, Varbinds) ->
1089    snmpa:send_trap(Agent, Trap, Community, Varbinds).
1090register_subagent(Agent, SubTree, SubAgent) ->
1091    snmpa:register_subagent(Agent, SubTree, SubAgent).
1092unregister_subagent(Agent, SubOidOrPid) ->
1093    snmpa:unregister_subagent(Agent, SubOidOrPid).
1094
1095add_agent_caps(Oid, Descr) -> snmpa:add_agent_caps(Oid, Descr).
1096del_agent_caps(Index) -> snmpa:del_agent_caps(Index).
1097get_agent_caps() -> snmpa:get_agent_caps().
1098
1099log_to_txt(LogDir, Mibs) ->
1100    snmpa:log_to_txt(LogDir, Mibs).
1101log_to_txt(LogDir, Mibs, OutFile) ->
1102    snmpa:log_to_txt(LogDir, Mibs, OutFile).
1103log_to_txt(LogDir, Mibs, OutFile, LogName) ->
1104    snmpa:log_to_txt(LogDir, Mibs, OutFile, LogName).
1105change_log_size(NewSize) ->
1106    snmpa:change_log_size(NewSize).
1107
1108
1109
1110