diff options
Diffstat (limited to 'lib/snmp/src/compile/snmpc.erl')
-rw-r--r-- | lib/snmp/src/compile/snmpc.erl | 1358 |
1 files changed, 1358 insertions, 0 deletions
diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl new file mode 100644 index 0000000000..8a1f15d4a4 --- /dev/null +++ b/lib/snmp/src/compile/snmpc.erl @@ -0,0 +1,1358 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(snmpc). + +%% API +-export([compile/1, compile/2, compile/3, + mib_to_hrl/1, mib_to_hrl/3, + is_consistent/1]). + +%% Debug +-export([look_at/1]). + +%% Internal Exports +-export([init/3]). + +-include_lib("stdlib/include/erl_compile.hrl"). +-include("snmp_types.hrl"). +-include("snmpc.hrl"). +-include("snmpc_lib.hrl"). + + +look_at(Mib) -> + io:format("~p ~n", [snmpc_lib:look_at(Mib)]). + + +%%----------------------------------------------------------------- +%% Misc compiler stuff +%%----------------------------------------------------------------- + +is_consistent(Filenames) -> + snmpc_lib:is_consistent(Filenames). + +mib_to_hrl(MibName) -> + snmpc_mib_to_hrl:convert(MibName). + +mib_to_hrl(MibName, HrlFile, Opts) -> + snmpc_mib_to_hrl:compile(MibName, HrlFile, Opts). + + +%%%----------------------------------------------------------------- +%%% Interface for erl_compile. +%%%----------------------------------------------------------------- + +compile(Input, _Output, Options) -> + case compile(Input, make_options(Options)) of + {ok, _} -> + ok; + {error, Reason} -> + io:format("~p", [Reason]), + error + end. + +%% Converts generic options to format expected by compile/2 + +make_options(#options{includes = Incs, + outdir = Outdir, + warning = Warning, + specific = Spec}) -> + + OutdirOpt = {outdir, Outdir}, + + WarningOpt = + case Warning of + 0 -> {warnings, false}; + _ -> {warnings, true} + end, + + IncludeOpt = + {i, case Incs of + [] -> + [""]; + _ -> + lists:map(fun(Dir) -> Dir++"/" end, Incs) + end}, + + [WarningOpt, OutdirOpt, IncludeOpt | Spec]. + +%% Returns: {ok, File}|{error, Reason} +compile([AtomFilename]) when is_atom(AtomFilename) -> + compile(atom_to_list(AtomFilename), []), % from cmd line + halt(); +compile(FileName) -> + compile(FileName, []). + + +%%---------------------------------------------------------------------- +%% Options: +%% {deprecated, bool()} true +%% {group_check, bool()} true +%% {db, volatile|persistent|mnesia} volatile +%% {i, [import_dir_string()]} ["./"] +%% {il, [import_lib_dir_string()]} [] +%% {warnings, bool()} true +%% {outdir, string()} "./" +%% description +%% reference +%% imports +%% module_identity +%% {module, string()} +%% no_defs +%% (hidden) {verbosity, trace|debug|log|info|silence} silence +%% (hidden) version +%% (hidden) options +%%---------------------------------------------------------------------- + +compile(FileName, Options) when is_list(FileName) -> + true = snmpc_misc:is_string(FileName), + DefOpts = [{deprecated, true}, + {group_check, true}, + {i, ["./"]}, + {db, volatile}, + {warnings, true}, + {outdir, "./"}, + {il, []}], + Opts = update_options(DefOpts, Options), + case check_options(Opts) of + ok -> + maybe_display_version(Opts), + maybe_display_options(Opts), + Pid = spawn_link(?MODULE,init,[self(),FileName,Opts]), + receive + {compile_result,R} -> R; + {'EXIT',Pid, Reason} when Reason =/= normal -> + exit(Reason) + end; + {error, Reason} -> + {error, Reason} + end. + +maybe_display_version(Opts) -> + case lists:member(version, Opts) of + true -> + Vsn = (catch get_version()), + io:format("version: ~s~n", [Vsn]); + false -> + ok + end. + +get_version() -> + MI = ?MODULE:module_info(), + Attr = get_info(attributes, MI), + Vsn = get_info(app_vsn, Attr), + Comp = get_info(compile, MI), + Time = get_info(time, Comp), + {Year, Month, Day, Hour, Min, Sec} = Time, + io_lib:format("~s [~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w]", + [Vsn, Year, Month, Day, Hour, Min, Sec]). + +maybe_display_options(Opts) -> + case lists:member(options, Opts) of + true -> + {F, A} = get_options(Opts, [], []), + io:format("options: " ++ F ++ "~n", A); + false -> + ok + end. + +get_options([], Formats, Args) -> + {lists:concat(lists:reverse(Formats)), lists:reverse(Args)}; +get_options([{deprecated, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n deprecated: ~w"|Formats], [Val|Args]); +get_options([{group_check, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n group_check: ~w"|Formats], [Val|Args]); +get_options([{db, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n db: ~w"|Formats], [Val|Args]); +get_options([{i, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n i: ~p"|Formats], [Val|Args]); +get_options([{il, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n il: ~p"|Formats], [Val|Args]); +get_options([{outdir, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n outdir: ~s"|Formats], [Val|Args]); +get_options([{description, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n description: ~w"|Formats], [Val|Args]); +get_options([description|Opts], Formats, Args) -> + get_options(Opts, ["~n description"|Formats], Args); +get_options([{reference, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n reference: ~w"|Formats], [Val|Args]); +get_options([reference|Opts], Formats, Args) -> + get_options(Opts, ["~n reference"|Formats], Args); +get_options([{warnings, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n warnings: ~w"|Formats], [Val|Args]); +get_options([{verbosity, Val}|Opts], Formats, Args) -> + get_options(Opts, ["~n verbosity: ~w"|Formats], [Val|Args]); +get_options([imports|Opts], Formats, Args) -> + get_options(Opts, ["~n imports"|Formats], Args); +get_options([module_identity|Opts], Formats, Args) -> + get_options(Opts, ["~n module_identity"|Formats], Args); +get_options([_|Opts], Formats, Args) -> + get_options(Opts, Formats, Args). + + +get_info(Key, Info) -> + case lists:keysearch(Key, 1, Info) of + {value, {Key, Val}} -> + Val; + false -> + throw("undefined") + end. + +% p(F, A) -> +% io:format("DBG: " ++ F ++ "~n", A). + +update_options([], Options) -> + Options; +update_options([{Key,DefVal}|DefOpts], Options) -> + case snmpc_misc:assq(Key, Options) of + false -> + update_options(DefOpts, [{Key,DefVal}|Options]); + {value, Val} when Key =:= i -> + Options1 = + lists:keyreplace(Key, 1, Options, {Key, Val++DefVal}), + update_options(DefOpts, Options1); + {value, Val} when Key =:= il -> + Options1 = + lists:keyreplace(Key, 1, Options, {Key, Val++DefVal}), + update_options(DefOpts, Options1); + {value, DefVal} -> %% Same value, no need to update + update_options(DefOpts, Options); + {value, Val} -> %% New value, so update + Options1 = + lists:keyreplace(Key, 1, Options, {Key, Val}), + update_options(DefOpts, Options1) + end. + +check_options([]) -> ok; +check_options([no_symbolic_info|T]) -> check_options(T); +check_options([{outdir, Str} | T]) when is_list(Str) -> + check_options(T); +check_options([{debug, Atom} | T]) when is_atom(Atom) -> + check_options(T); +check_options([{deprecated, Atom} | T]) when is_atom(Atom) -> + check_options(T); +check_options([{group_check, Atom} | T]) when is_atom(Atom) -> + check_options(T); +check_options([{warnings, Bool} | T]) -> + check_bool(warnings, Bool), + check_options(T); +check_options([{db, volatile} | T]) -> + check_options(T); +check_options([{db, persistent} | T]) -> + check_options(T); +check_options([{db, mnesia} | T]) -> + check_options(T); +check_options([{i, [Str|_]} | T]) when is_list(Str) -> + check_options(T); +check_options([{il, []} | T]) -> + check_options(T); +check_options([{il, [Str|_]} | T]) when is_list(Str) -> + check_options(T); +check_options([{description, Bool}| T]) -> + check_bool(description, Bool), + check_options(T); +check_options([description| T]) -> %% same as {description, true} + check_options(T); +check_options([{reference, Bool}| T]) -> + check_bool(reference, Bool), + check_options(T); +check_options([reference| T]) -> %% same as {reference, true} + check_options(T); +check_options([{verbosity, V} | T]) when is_atom(V) -> + snmpc_lib:vvalidate(V), + check_options(T); +check_options([version| T]) -> + check_options(T); +check_options([options| T]) -> + check_options(T); +check_options([imports| T]) -> + check_options(T); +check_options([module_identity| T]) -> + check_options(T); +check_options([{module, M} | T]) when is_atom(M) -> + check_options(T); +check_options([no_defs| T]) -> + check_options(T); +check_options([Opt|_]) -> + {error, {invalid_option, Opt}}. + + +check_bool(_Key, Bool) when (Bool =:= true) orelse (Bool =:= false) -> + ok; +check_bool(Key, Val) -> + {error, {invalid_option, {Key, Val}}}. + +get_group_check(Options) -> + snmpc_lib:key1search(group_check, Options, true). + +get_deprecated(Options) -> + snmpc_lib:key1search(deprecated, Options, true). + +get_description(Options) -> + get_bool_option(description, Options). + +get_reference(Options) -> + get_bool_option(reference, Options). + +get_bool_option(Option, Options) -> + case lists:member(Option, Options) of + false -> + snmpc_lib:key1search(Option, Options, false); + true -> + true + end. + +make_description(Message) -> + case get(description) of + true -> + Message; + _ -> + undefined + end. + +make_reference(undefined) -> + []; +make_reference(Reference) -> + case get(reference) of + true -> + [{reference, Reference}]; + _ -> + [] + end. + + + +%%---------------------------------------------------------------------- +%% verbosity stuff +%%---------------------------------------------------------------------- + +%% Verbosity level is selected from three (historical reasons) +%% options: warnings, debug and verbosity +%% - If warnings is true, then verbosity is _atleast_ warning +%% (even if the verbosity flag is set to silence) +%% - If debug is true, the verbosity is _atleast_ log +%% - Otherwise, verbosity is used as is. +get_verbosity(Options) -> + WarningsSeverity = + case snmpc_lib:key1search(warnings, Options) of + true -> + warning; + _ -> + silence + end, + case snmpc_lib:key1search(verbosity, Options) of + undefined -> + %% Backward compatible: If not defined then try debug and convert + case snmpc_lib:key1search(debug, Options, false) of + true -> + log; + false -> + WarningsSeverity + end; + silence -> + WarningsSeverity; + Verbosity -> + Verbosity + end. + + +%%---------------------------------------------------------------------- +%% The compile process. +%%---------------------------------------------------------------------- + +init(From, MibFileName, Options) -> + {A,B,C} = now(), + random:seed(A,B,C), + put(options, Options), + put(verbosity, get_verbosity(Options)), + put(description, get_description(Options)), + put(reference, get_reference(Options)), + File = filename:rootname(MibFileName, ".mib"), + put(filename, filename:basename(File ++ ".mib")), + R = case catch c_impl(File) of + {ok, OutFile} -> {ok, OutFile}; + {'EXIT',error} -> {error, compilation_failed}; + Error -> exit(Error) + end, + From ! {compile_result, R}. + + +c_impl(File) -> + {ok, PData} = parse(File), + ?vtrace("Syntax analysis:" + "~n PData: ~p", [PData]), + MibName = compile_parsed_data(PData), + ?vtrace("Compiler output:" + "~n CDATA: ~p", [get(cdata)]), + save(File, MibName, get(options)). + +compile_parsed_data(#pdata{mib_name = MibName, + imports = Imports, + defs = Definitions}) -> + snmpc_lib:import(Imports), + update_imports(Imports), + Deprecated = get_deprecated(get(options)), + definitions_loop(Definitions, Deprecated), + MibName. + +update_imports(Imports) -> + case lists:member(imports, get(options)) of + true -> + IMPs = do_update_imports(Imports, []), + CDATA = get(cdata), + put(cdata, CDATA#cdata{imports = IMPs}); + false -> + ok + end. + +do_update_imports([], Acc) -> + lists:reverse(Acc); +do_update_imports([{{Mib, ImportsFromMib0},_Line}|Imports], Acc) -> + ImportsFromMib = [Name || {_, Name} <- ImportsFromMib0], + Import = {Mib, ImportsFromMib}, + do_update_imports(Imports, [Import|Acc]). + + +update_status(Name, Status) -> + #cdata{status_ets = Ets} = get(cdata), + ets:insert(Ets, {Name, Status}). + + +%% A deprecated object +definitions_loop([{#mc_object_type{name = ObjName, status = deprecated}, + Line}|T], + false) -> + %% May be implemented but the compiler chooses not to. + ?vinfo2("object_type ~w is deprecated => ignored", [ObjName], Line), + update_status(ObjName, deprecated), + definitions_loop(T, false); + +%% A obsolete object +definitions_loop([{#mc_object_type{name = ObjName, status = obsolete}, + Line}|T], + Deprecated) -> + ?vlog2("object_type ~w is obsolete => ignored", [ObjName], Line), + %% No need to implement a obsolete object + update_status(ObjName, obsolete), + ensure_macro_imported('OBJECT-TYPE', Line), + definitions_loop(T, Deprecated); + +%% Defining a table +definitions_loop([{#mc_object_type{name = NameOfTable, + syntax = {{sequence_of, SeqName}, _}, + max_access = Taccess, + kind = Kind, + status = Tstatus, + description = Desc1, + units = Tunits, + reference = Ref, + name_assign = Tindex}, + Tline}, + {#mc_object_type{name = NameOfEntry, + syntax = {{type, SeqName}, TEline}, + max_access = 'not-accessible', + kind = {table_entry, IndexingInfo}, + status = Estatus, + description = Desc2, + units = Eunits, + name_assign = {NameOfTable,[1]}}, + Eline}, + {#mc_sequence{name = SeqName, + fields = FieldList}, + Sline}|ColsEtc], + Deprecated) -> + ?vlog("defloop -> " + "[object_type(sequence_of),object_type(type,[1]),sequence]:" + "~n NameOfTable: ~p" + "~n SeqName: ~p" + "~n Taccess: ~p" + "~n Kind: ~p" + "~n Tstatus: ~p" + "~n Tindex: ~p" + "~n Tunits: ~p" + "~n Tline: ~p" + "~n NameOfEntry: ~p" + "~n TEline: ~p" + "~n IndexingInfo: ~p" + "~n Estatus: ~p" + "~n Eunits: ~p" + "~n Ref: ~p" + "~n Eline: ~p" + "~n FieldList: ~p" + "~n Sline: ~p", + [NameOfTable,SeqName,Taccess,Kind,Tstatus, + Tindex,Tunits,Tline, + NameOfEntry,TEline,IndexingInfo,Estatus,Eunits,Ref,Eline, + FieldList,Sline]), + update_status(NameOfTable, Tstatus), + update_status(NameOfEntry, Estatus), + update_status(SeqName, undefined), + ensure_macro_imported('OBJECT-TYPE', Tline), + test_table(NameOfTable,Taccess,Kind,Tindex,Tline), + {Tfather,Tsubindex} = Tindex, + snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex), + Description1 = make_description(Desc1), + TableME = #me{aliasname = NameOfTable, + entrytype = table, + access = 'not-accessible', + description = Description1, + units = Tunits}, + snmpc_lib:register_oid(TEline,NameOfEntry,NameOfTable,[1]), + Description2 = make_description(Desc2), + TableEntryME = #me{aliasname = NameOfEntry, + entrytype = table_entry, + assocList = [{table_entry_with_sequence, SeqName}], + access = 'not-accessible', + description = Description2, + units = Eunits}, + {ColMEs, RestObjs} = + define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []), + TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable, + IndexingInfo, ColMEs), + snmpc_lib:add_cdata(#cdata.mes, + [TableEntryME, + TableME#me{assocList=[{table_info, + TableInfo} | make_reference(Ref)]} | + ColMEs]), + definitions_loop(RestObjs, Deprecated); + +definitions_loop([{#mc_object_type{name = NameOfTable, + syntax = {{sequence_of, SeqName},_}, + max_access = Taccess, + kind = Kind, + status = Tstatus, + description = Desc1, + units = Tunits, + reference = Ref, + name_assign = Tindex}, Tline}, + {#mc_object_type{name = NameOfEntry, + syntax = {{type, SeqName},_}, + max_access = 'not-accessible', + kind = {table_entry,IndexingInfo}, + status = Estatus, + description = Desc2, + units = Eunits, + name_assign = BadOID}, Eline}, + {#mc_sequence{name = SeqName, + fields = FieldList}, Sline}|ColsEtc], + Deprecated) -> + ?vlog("defloop -> " + "[object_type(sequence_of),object_type(type),sequence(fieldList)]:" + "~n NameOfTable: ~p" + "~n SeqName: ~p" + "~n Taccess: ~p" + "~n Kind: ~p" + "~n Tstatus: ~p" + "~n Tindex: ~p" + "~n Tunits: ~p" + "~n Tline: ~p" + "~n NameOfEntry: ~p" + "~n IndexingInfo: ~p" + "~n Estatus: ~p" + "~n BadOID: ~p" + "~n Eunits: ~p" + "~n Ref: ~p" + "~n Eline: ~p" + "~n FieldList: ~p" + "~n Sline: ~p", + [NameOfTable,SeqName,Taccess,Kind,Tstatus, + Tindex,Tunits,Tline, + NameOfEntry,IndexingInfo,Estatus,BadOID,Eunits,Ref,Eline, + FieldList,Sline]), + update_status(NameOfTable, Tstatus), + update_status(NameOfEntry, Estatus), + update_status(SeqName, undefined), + ensure_macro_imported('OBJECT-TYPE', Tline), + snmpc_lib:print_error("Bad TableEntry OID definition (~w)", + [BadOID],Eline), + test_table(NameOfTable,Taccess,Kind,Tindex,Tline), + {Tfather,Tsubindex} = Tindex, + snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex), + Description1 = make_description(Desc1), + TableME = #me{aliasname = NameOfTable, + entrytype = table, + access = 'not-accessible', + description = Description1, + units = Tunits}, + Description2 = make_description(Desc2), + TableEntryME = #me{aliasname = NameOfEntry, + entrytype = table_entry, + access = 'not-accessible', + assocList = [{table_entry_with_sequence,SeqName}], + description = Description2, + units = Eunits}, + {ColMEs, RestObjs} = + define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []), + TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable, + IndexingInfo, ColMEs), + snmpc_lib:add_cdata(#cdata.mes, + [TableEntryME, + TableME#me{assocList=[{table_info, + TableInfo} | make_reference(Ref)]} | + ColMEs]), + definitions_loop(RestObjs, Deprecated); + +definitions_loop([{#mc_new_type{name = NewTypeName, + macro = Macro, + syntax = OldType, + display_hint = DisplayHint},Line}|T], + Deprecated) -> + ?vlog2("defloop -> new_type:" + "~n Macro: ~p" + "~n NewTypeName: ~p" + "~n OldType: ~p" + "~n DisplayHint: ~p", + [Macro, NewTypeName, OldType, DisplayHint], Line), + ensure_macro_imported(Macro,Line), + Types = (get(cdata))#cdata.asn1_types, + case lists:keysearch(NewTypeName, #asn1_type.aliasname, Types) of + {value,_} -> + snmpc_lib:print_error("Type ~w already defined.", + [NewTypeName],Line); + false -> + %% NameOfOldType = element(2,OldType), + ASN1 = snmpc_lib:make_ASN1type(OldType), + snmpc_lib:add_cdata(#cdata.asn1_types, + [ASN1#asn1_type{aliasname = NewTypeName, + imported = false, + display_hint = DisplayHint}]) + end, + definitions_loop(T, Deprecated); + +%% Plain variable +definitions_loop([{#mc_object_type{name = NewVarName, + syntax = Type, + max_access = Access, + kind = {variable, DefVal}, + status = Status, + description = Desc1, + units = Units, + name_assign = {Parent,SubIndex}},Line} |T], + Deprecated) -> + ?vlog2("defloop -> object_type (variable):" + "~n NewVarName: ~p" + "~n Type: ~p" + "~n Access: ~p" + "~n DefVal: ~p" + "~n Status: ~p" + "~n Units: ~p" + "~n Parent: ~p" + "~n SubIndex: ~p", + [NewVarName, Type, Access, DefVal, + Status, Units, Parent, SubIndex], Line), + update_status(NewVarName, Status), + snmpc_lib:test_father(Parent, NewVarName, SubIndex, Line), + ASN1type = snmpc_lib:make_ASN1type(Type), + snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex), + Description1 = make_description(Desc1), + NewME = #me{aliasname = NewVarName, + asn1_type = ASN1type, + entrytype = variable, + access = Access, + description = Description1, + units = Units, + assocList = DefVal}, + NewME2 = snmpc_lib:resolve_defval(NewME), + %% hmm, should this be done in resolve_defval? + VI = snmpc_lib:make_variable_info(NewME2), + snmpc_lib:add_cdata(#cdata.mes, + [NewME2#me{assocList = [{variable_info, VI}]}]), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_module_identity{name = NewVarName, + last_updated = LU, + organization = Org, + contact_info = CI, + description = Desc, + revisions = Revs0, + name_assign = {Parent, SubIndex}}, + Line}|T], + Deprecated) -> + ?vlog2("defloop -> module-identity: " + "~n NewVarName: ~p" + "~n LU: ~p" + "~n Org: ~p" + "~n CI: ~p" + "~n Desc: ~p" + "~n Revs0: ~p" + "~n Parent: ~p" + "~n SubIndex: ~p", + [NewVarName, LU, Org, CI, Desc, Revs0, Parent, SubIndex], Line), + ensure_macro_imported('MODULE-IDENTITY', Line), + snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex), + Revs = [{R,D}||#mc_revision{revision = R,description = D} <- Revs0], + MI = #module_identity{last_updated = LU, + organization = Org, + contact_info = CI, + description = Desc, + revisions = Revs}, + CDATA = get(cdata), + put(cdata, CDATA#cdata{module_identity = MI}), + snmpc_lib:add_cdata( + #cdata.mes, + [snmpc_lib:makeInternalNode2(false, NewVarName)]), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_internal{name = NewVarName, + macro = Macro, + parent = Parent, + sub_index = SubIndex},Line}|T], + Deprecated) -> + ?vlog2("defloop -> internal:" + "~n NewVarName: ~p" + "~n Macro: ~p" + "~n Parent: ~p" + "~n SubIndex: ~p", + [NewVarName, Macro, Parent, SubIndex], Line), + ensure_macro_imported(Macro, Line), + snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex), + snmpc_lib:add_cdata( + #cdata.mes, + [snmpc_lib:makeInternalNode2(false, NewVarName)]), + definitions_loop(T, Deprecated); + +%% A trap message +definitions_loop([{#mc_trap{name = TrapName, + enterprise = EnterPrise, + vars = Variables, + description = Desc1, + num = SpecificCode}, Line}|T], + Deprecated) -> + ?vlog2("defloop -> trap:" + "~n TrapName: ~p" + "~n EnterPrise: ~p" + "~n Variables: ~p" + "~n SpecificCode: ~p", + [TrapName, EnterPrise, Variables, SpecificCode], Line), + update_status(TrapName, undefined), + CDATA = get(cdata), + snmpc_lib:check_trap_name(EnterPrise, Line, CDATA#cdata.mes), + Descriptions = make_description(Desc1), + Trap = #trap{trapname = TrapName, + enterpriseoid = EnterPrise, + specificcode = SpecificCode, + %% oidobjects: Store Variables temporary here. + %% This will be replaced later in the + %% get_final_mib function by a call to + %% the update_trap_objects function. + oidobjects = Variables, + description = Descriptions}, + lists:foreach(fun(Trap2) -> snmpc_lib:check_trap(Trap2, Trap, Line) end, + CDATA#cdata.traps), + snmpc_lib:add_cdata(#cdata.traps, [Trap]), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_object_type{name = NameOfEntry, + syntax = Type, + max_access = Eaccess, + kind = {table_entry, Index}, + status = Estatus, + name_assign = SubIndex},Eline}|T], + Deprecated) -> + ?vlog("defloop -> object_type (table_entry):" + "~n NameOfEntry: ~p" + "~n Type: ~p" + "~n Eaccess: ~p" + "~n Index: ~p" + "~n Estatus: ~p" + "~n SubIndex: ~p" + "~n SubIndex: ~p" + "~n Eline: ~p", + [NameOfEntry, Type, Eaccess, Index, Estatus, SubIndex, Eline]), + update_status(NameOfEntry, Estatus), + snmpc_lib:print_error("Misplaced TableEntry definition (~w)", + [NameOfEntry], Eline), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_notification{name = TrapName, + status = deprecated}, Line}|T], + false) -> + ?vinfo2("defloop -> notification ~w is deprecated => ignored", + [TrapName], Line), + update_status(TrapName, deprecated), + ensure_macro_imported('NOTIFICATION-TYPE', Line), + definitions_loop(T, false); + +definitions_loop([{#mc_notification{name = TrapName, + status = obsolete}, Line}|T], + Deprecated) -> + ?vlog2("defloop -> notification ~w is obsolete => ignored", + [TrapName], Line), + update_status(TrapName, obsolete), + ensure_macro_imported('NOTIFICATION-TYPE', Line), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_notification{name = TrapName, + vars = Variables, + status = Status, + description = Desc, + name_assign = {Parent, SubIndex}},Line}|T], + Deprecated) -> + ?vlog2("defloop -> notification:" + "~n TrapName: ~p" + "~n Variables: ~p" + "~n Status: ~p" + "~n Parent: ~p" + "~n SubIndex: ~p", + [TrapName, Variables, Status, Parent, SubIndex], Line), + update_status(TrapName, Status), + ensure_macro_imported('NOTIFICATION-TYPE', Line), + CDATA = get(cdata), + snmpc_lib:register_oid(Line, TrapName, Parent, SubIndex), + Descriptions = make_description(Desc), + Notif = #notification{trapname = TrapName, + description = Descriptions, + %% oidobjects: Store Variables temporary here. + %% This will be replaced later in the + %% get_final_mib function by a call to + %% the update_trap_objects function. + oidobjects = Variables}, + snmpc_lib:check_notification(Notif, Line, CDATA#cdata.traps), + snmpc_lib:add_cdata(#cdata.traps, [Notif]), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_module_compliance{name = Name},Line}|T], Deprecated) -> + ?vlog2("defloop -> module_compliance:" + "~n Name: ~p", [Name], Line), + ensure_macro_imported('MODULE-COMPLIANCE', Line), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_object_group{name = Name, + objects = GroupObjects, + status = Status, + description = Desc, + reference = Ref, + name_assign = {Parent,SubIndex}}, Line}|T], + Deprecated) -> + ?vlog2("defloop -> object_group ~p:" + "~n GroupObjects: ~p" + "~n Status: ~p" + "~n Desc: ~p" + "~n Ref: ~p" + "~n Parent: ~p" + "~n SubIndex: ~p", + [Name, GroupObjects, Status, Desc, Ref, Parent, SubIndex], Line), + ensure_macro_imported('OBJECT-GROUP', Line), + GroupBool = get_group_check(get(options)), + case GroupBool of + true -> + snmpc_lib:add_cdata(#cdata.objectgroups, + [{Name,GroupObjects,Line}]), + %% Check that the group members has been defined + %% and that they have the correct status + snmpc_lib:check_object_group(Name, GroupObjects, + Line, Status); + _ -> + ok + end, + + update_status(Name, Status), + snmpc_lib:test_father(Parent, Name, SubIndex, Line), + snmpc_lib:register_oid(Line, Name, Parent, SubIndex), + Description = make_description(Desc), + NewME = #me{aliasname = Name, + entrytype = group, + access = 'not-accessible', + description = Description, + assocList = [{kind, object}, + {objects, GroupObjects}]}, + snmpc_lib:add_cdata(#cdata.mes, [NewME]), + + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_notification_group{name = Name, + objects = GroupObjects, + status = Status, + description = Desc, + reference = Ref, + name_assign = {Parent,SubIndex}}, + Line} + |T], Deprecated) -> + ?vlog2("defloop -> notification_group ~p:" + "~n GroupObjects: ~p" + "~n Status: ~p" + "~n Desc: ~p" + "~n Ref: ~p" + "~n Parent: ~p" + "~n SubIndex: ~p", + [Name, GroupObjects, Status, Desc, Ref, Parent, SubIndex], Line), + ensure_macro_imported('NOTIFICATION-GROUP', Line), + GroupBool = get_group_check(get(options)), + case GroupBool of + true -> + snmpc_lib:add_cdata(#cdata.notificationgroups, + [{Name,GroupObjects,Line}]), + + %% Check that the group members has been defined + %% and that they have the correct status + snmpc_lib:check_notification_group(Name, GroupObjects, + Line, Status); + _ -> + ok + end, + + update_status(Name, Status), + snmpc_lib:test_father(Parent, Name, SubIndex, Line), + snmpc_lib:register_oid(Line, Name, Parent, SubIndex), + Description = make_description(Desc), + NewME = #me{aliasname = Name, + entrytype = group, + access = 'not-accessible', + description = Description, + assocList = [{kind, notification}, + {objects, GroupObjects}]}, + snmpc_lib:add_cdata(#cdata.mes, [NewME]), + + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_object_type{name = NameOfTable, + syntax = {{sequence_of, SeqName},_}, + status = Tstatus},Tline}, + Entry, Seq|T], + Deprecated) -> + ?vlog("defloop -> object_type (sequence_of): " + "~n NameOfTable: ~p" + "~n SeqName: ~p" + "~n Tline: ~p" + "~n Entry: ~p" + "~n Seq: ~p", + [NameOfTable, SeqName, Tline, Entry, Seq]), + update_status(NameOfTable, Tstatus), + case Entry of + {#mc_object_type{syntax = {{type, SeqName},_line}, + max_access = 'not-accessible', + kind = {table_entry, _IndexingInfo}, + name_assign = {_NameOfTable,[1]}}, _Eline} -> + case Seq of + {#mc_sequence{name = SeqName}, Sline} -> + snmpc_lib:error("Internal error. Correct incorrect " + "table (~p,~w).",[SeqName,Sline], + Tline); + _ -> + ?vinfo("defloop -> Invalid sequence: ~p", [Seq]), + snmpc_lib:print_error( + "Invalid SEQUENCE OF '~p'.", + [safe_elem(1,safe_elem(2,Seq))],Tline) + end; + Else -> + ?vinfo("defloop -> Invalid table entry: " + "~n ~p", [Else]), + snmpc_lib:print_error( + "Invalid TableEntry '~p' (check STATUS, Sequence name, Oid)", + [safe_elem(1,safe_elem(2,Entry))],Tline) + end, + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_object_type{name = NameOfTable, + syntax = {{sequence_of, SeqName},_}, + status = Tstatus},Tline}|T], + Deprecated) -> + ?vlog("defloop -> object_type (sequence_of):" + "~n object_type: ~p" + "~n sequence_of: ~p" + "~n Tline: ~p", [NameOfTable, SeqName, Tline]), + update_status(NameOfTable, Tstatus), + snmpc_lib:print_error("Invalid statements following table ~p.", + [NameOfTable],Tline), + definitions_loop(T, Deprecated); + +definitions_loop([{#mc_sequence{name = SeqName, + fields = _FieldList},Line}|T], + Deprecated) -> + ?vwarning2("Unexpected SEQUENCE ~w => ignoring", [SeqName], Line), + definitions_loop(T, Deprecated); + +definitions_loop([{Obj,Line}|T], Deprecated) -> + ?vinfo2("defloop -> unknown error" + "~n Obj: ~p", [Obj], Line), + snmpc_lib:print_error("Unknown Error in MIB. " + "Can't describe the error better than this: ~999p ignored." + " Please send a trouble report to [email protected].", + [Obj], Line), + definitions_loop(T, Deprecated); + +definitions_loop([], _Deprecated) -> + ?vlog("defloop -> done", []), + ok. + +safe_elem(N,T) -> + case catch(element(N,T)) of + {'EXIT',_} -> + "no more information available"; + X -> X + end. + +%% A correct column +define_cols([{#mc_object_type{name = NameOfCol, + syntax = Type1, + max_access = Access, + kind = {variable,Defval}, + status = Status, + description = Desc, + units = Units, + name_assign = {NameOfEntry,[SubIndex]}}, + Oline}|Rest], + SubIndex, + [{NameOfCol,Type2}|Fields], NameOfEntry, TableName, ColMEs) -> + ?vlog("defcols -> object_type (variable):" + "~n NameOfCol: ~p" + "~n Type1: ~p" + "~n Access: ~p" + "~n Defval: ~p" + "~n Status ~p" + "~n Units ~p" + "~n NameOfEntry ~p" + "~n Oline: ~p", + [NameOfCol, Type1, Access, Defval, Status, Units, + NameOfEntry, Oline]), + update_status(NameOfCol, Status), + Deprecated = get_deprecated(get(options)), + ASN1type = snmpc_lib:make_ASN1type(Type1), + case (snmpc_lib:make_ASN1type(Type2))#asn1_type.bertype of + T2 when T2 == ASN1type#asn1_type.bertype -> ok; + _Else -> + snmpc_lib:error( + "Types for ~p differs from the SEQUENCE definition. ", + [NameOfCol],Oline) + end, + NewAccess = % a simple way to get the obsolete behaviour + if + Status =:= obsolete -> + %% Be quiet and don't implement + 'not-accessible'; + (Status =:= deprecated) andalso (Deprecated =:= false) -> + %% The compiler chooses not to implement the column. + ?vinfo2("object_type ~w is deprecated => ignored", + [NameOfCol], Oline), + 'not-accessible'; + true -> Access + end, + snmpc_lib:register_oid(Oline,NameOfCol,NameOfEntry,[SubIndex]), + Description = make_description(Desc), + ColumnME = snmpc_lib:resolve_defval( + #me{oid = SubIndex, + aliasname = NameOfCol, + asn1_type = ASN1type, + entrytype = table_column, + access = NewAccess, + description = Description, + units = Units, %% Propably not usefull + assocList = [{table_name,TableName} | Defval]}), + define_cols(Rest,SubIndex+1,Fields,NameOfEntry,TableName, + [ColumnME|ColMEs]); + +%% A "hole" (non-consecutive columns) in the table. +%% Implemented as a not-accessible column so Col always is index in +%% row tuple. +define_cols([{#mc_object_type{name = NameOfCol, + syntax = Type1, + max_access = Access, + kind = Kind, + status = Status, + name_assign = {NameOfEntry,[SubIndex]}}, + Oline}|Rest], + ExpectedSubIndex, Fields, NameOfEntry, TableName, ColMEs) + when SubIndex > ExpectedSubIndex -> + ?vlog("defcols -> object_type (non consecutive cols):" + "~n NameOfCol: ~p" + "~n Type1: ~p" + "~n Access: ~p" + "~n Status ~p" + "~n NameOfEntry ~p" + "~n Oline: ~p", + [NameOfCol, Type1, Access, Status, NameOfEntry, Oline]), + update_status(NameOfCol, Status), + Int = {{type, 'INTEGER'},Oline}, + GeneratedColumn = + %% be sure to use an invalid column name here! + {#mc_object_type{name = '$no_name$', + syntax = Int, + max_access = 'not-accessible', + kind = {variable, [{defval,0}]}, + status = current, + description = undefined, + name_assign = {NameOfEntry, [ExpectedSubIndex]}}, + Oline}, + define_cols([GeneratedColumn, + {#mc_object_type{name = NameOfCol, + syntax = Type1, + max_access = Access, + kind = Kind, + status = Status, + description = undefined, + name_assign = {NameOfEntry,[SubIndex]}}, + Oline}|Rest], ExpectedSubIndex, + [{'$no_name$', Int}|Fields], NameOfEntry, TableName,ColMEs) ; + +%% Ok. done. All fields are eaten. +define_cols(Rest, _SubIndex, [], _NameOfEntry, _TableName, ColMEs) -> + {ColMEs, Rest}; + + +%% Error Handling + +%% The name of the field and object is the same +define_cols([{#mc_object_type{name = NameOfCol, + kind = Kind, + name_assign = SubIndex}, Oline}|Rest], + SubIndex2, [{NameOfCol, _Type2}|Fields], + NameOfEntry, TableName, ColMEs) -> + ?vlog("defcols -> object_type (name of field and object is the same):" + "~n NameOfCol: ~p" + "~n Kind: ~p" + "~n SubIndex: ~p" + "~n Oline: ~p" + "~n SubIndex2: ~p" + "~n NameOfEntry ~p" + "~n TableName ~p", + [NameOfCol,Kind,SubIndex,Oline,SubIndex2,NameOfEntry,TableName]), + SIok = case SubIndex of + {Parent,[_SI]} when Parent =/= NameOfEntry -> + snmpc_lib:print_error( + "Invalid parent ~p for table column ~p (should be ~p).", + [Parent,NameOfCol,NameOfEntry],Oline), + error; + {NameOfEntry,[SubIndex2]} -> + ok; + {NameOfEntry,[SI]} -> + snmpc_lib:print_error( + "Invalid column number ~p for column ~p.", + [SI, NameOfCol], Oline), + error; + _Q -> + snmpc_lib:print_error( + "Invalid parent for column ~p.",[NameOfCol],Oline), + error + end, + Kok = case Kind of + {variable,_} -> + ok; + _Q2 -> + snmpc_lib:print_error( + "Expected a table column.",[],Oline), + error + end, + case {SIok, Kok} of + {ok, ok} -> + snmpc_lib:print_error("Invalid table column definition for" + " ~p.",[NameOfCol],Oline); + _Q4 -> + done % already reported + end, + define_cols(Rest,SubIndex2+1,Fields,NameOfEntry,TableName,ColMEs); + +%% It's an object-type but everything else is wrong +define_cols([{#mc_object_type{name = NameOfCol},Oline}|Rest],SubIndex2,Fields, + NameOfEntry,TableName,ColMEs) -> + snmpc_lib:print_error( + "Number of columns differs from SEQUENCE definition (object:~p).", + [NameOfCol],Oline), + define_cols(Rest,SubIndex2+1,Fields,NameOfEntry,TableName,ColMEs); + +define_cols([{Obj,Line}|Tl], _SubIndex,_,_,_,ColMEs) -> + snmpc_lib:print_error("Corrupt table definition.",[],Line), + {ColMEs,[{Obj,Line}|Tl]}; +define_cols(Rest, _SubIndex,_,_,_,ColMEs) -> + snmpc_lib:print_error("Corrupt table definition.",[]), + {ColMEs,Rest}. + +ensure_macro_imported(dummy, _Line) -> ok; +ensure_macro_imported(Macro, Line) -> + Macros = (get(cdata))#cdata.imported_macros, + case lists:member(Macro, Macros) of + true -> ok; + false -> + snmpc_lib:print_error("Macro ~p not imported.", [Macro], + Line) + end. + +test_table(NameOfTable, Taccess, Kind, _Tindex, Tline) -> + if + Taccess =/= 'not-accessible' -> + snmpc_lib:print_error( + "Table ~w must have STATUS not-accessible", + [NameOfTable],Tline), + error; + Kind =/= {variable,[]} -> + snmpc_lib:print_error( + "Bad table definition (~w).", + [NameOfTable],Tline), + error; + true -> + ok + end. + +save(Filename, MibName, Options) -> + R = filename:rootname(Filename), + File1 = filename:basename(R), + File3 = snmpc_misc:to_upper(File1), + case snmpc_misc:to_upper(atom_to_list(MibName)) of + File3 -> + {value, OutDirr} = snmpc_misc:assq(outdir, Options), + OutDir = snmpc_misc:ensure_trailing_dir_delimiter(OutDirr), + File2 = (OutDir ++ File1) ++ ".bin", + {ok, MIB} = snmpc_lib:get_final_mib(File1, Options), + case get(errors) of + undefined -> + case file:write_file(File2, term_to_binary(MIB)) of + ok -> + {ok, File2}; + _Err -> + snmpc_lib:error( + "Couldn't write file \"~s\".",[File2]) + end; + E -> + ?vlog("save failed: " + "~n ~p", [E]), + {'EXIT',error} + end; + MibNameL -> + snmpc_lib:error("Mibname (~s) differs from filename (~s).", + [MibNameL, File1]) + end. + +%% parse takes a text file as a input and the output is a list of tokens. +%% Input: FileName (file of mibs) +%% Output: {ok, Mib} where MIB is a tuple of Tokens. +%% {error, {LineNbr, Mod, Msg} an error on line number LineNb. + + +parse(FileName) -> + case snmpc_tok:start_link(reserved_words(), + [{file, FileName ++ ".mib"}, + {forget_stringdata, true}]) of + {error,ReasonStr} -> + snmpc_lib:error(lists:flatten(ReasonStr),[]); + {ok, TokPid} -> + Toks = snmpc_tok:get_all_tokens(TokPid), + set_version(Toks), + %% io:format("parse -> lexical analysis: ~n~p~n", [Toks]), + %% t("parse -> lexical analysis: ~n~p", [Toks]), + CDataArg = + case lists:keysearch(module, 1, get(options)) of + {value, {module, M}} -> {module, M}; + _ -> {file, FileName ++ ".funcs"} + end, + put(cdata,snmpc_lib:make_cdata(CDataArg)), + snmpc_tok:stop(TokPid), + Res = if + is_list(Toks) -> + snmpc_mib_gram:parse(Toks); + true -> + Toks + end, + %% t("parse -> parsed: ~n~p", [Res]), + case Res of + {ok, PData} -> + {ok, PData}; + {error, {LineNbr, Mod, Msg}} -> + case catch format_yecc_error(LineNbr, Msg) of + {Line, Format, Data} -> + snmpc_lib:error(Format,Data,Line); + _Q -> % sorry, have to use ugly yecc printouts + Str = apply(Mod, format_error, [Msg]), + snmpc_lib:error("~s",[Str],LineNbr) + end + end + end. + +set_version(Toks) when is_list(Toks) -> +%% MODULE-IDENTITY _must_ be invoked in SNMPv2 according to RFC1908 + case lists:keymember('MODULE-IDENTITY',1,Toks) of + true -> + put(snmp_version,2); + false -> + put(snmp_version,1) + end; +set_version(_) -> + put(snmp_version,1). + + +%% YeccGeneratedFile:format_error/1 is bad. +format_yecc_error(Line, [ErrMsg, [${,Category, $,, _LineStr,$,, Value, $}]]) -> + {Line, "~s \"~s\" (~s).", [ErrMsg, Value, Category]}. + +%% The same as the (quoted) Terminals in the snmpc_mib_gram.yrl +reserved_words() -> + [ + 'ACCESS', + 'BEGIN', + 'BIT', + 'CONTACT-INFO', + 'Counter', + 'DEFINITIONS', + 'DEFVAL', + 'DESCRIPTION', + 'DISPLAY-HINT', + 'END', + 'ENTERPRISE', + 'FROM', + 'Gauge', + 'IDENTIFIER', + 'IDENTIFIER', + 'IMPORTS', + 'INDEX', + 'INTEGER', + 'IpAddress', + 'LAST-UPDATED', + 'NetworkAddress', + 'OBJECT', + 'OBJECT', + 'OBJECT-TYPE', + 'OCTET', + 'OF', + 'Opaque', + 'REFERENCE', + 'SEQUENCE', + 'SIZE', + 'STATUS', + 'STRING', + 'SYNTAX', + 'TRAP-TYPE', + 'TimeTicks', + 'VARIABLES', + + %% v2 + 'LAST-UPDATED', + 'ORGANIZATION', + 'CONTACT-INFO', + 'MODULE-IDENTITY', + 'NOTIFICATION-TYPE', + 'MODULE-COMPLIANCE', + 'OBJECT-GROUP', + 'NOTIFICATION-GROUP', + 'REVISION', + 'OBJECT-IDENTITY', + 'MAX-ACCESS', + 'UNITS', + 'AUGMENTS', + 'IMPLIED', + 'OBJECTS', + 'TEXTUAL-CONVENTION', + 'OBJECT-GROUP', + 'NOTIFICATION-GROUP', + 'NOTIFICATIONS', + 'MODULE-COMPLIANCE', + 'MODULE', + 'MANDATORY-GROUPS', + 'GROUP', + 'WRITE-SYNTAX', + 'MIN-ACCESS', + 'BITS' + ] +. |