1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(snmpa_svbl).
21
22-include("snmp_types.hrl").
23
24-define(VMODULE,"SVBL").
25-include("snmp_verbosity.hrl").
26
27-export([sort_varbindlist/2, sort_varbinds_rows/1, sa_split/1,
28	 delete_org_index/1, col_to_orgindex/2]).
29
30%%-----------------------------------------------------------------
31%% Func: sort_varbindlist/2
32%% Args: Varbinds is a list of #varbind
33%% Purpose: Group all variablebindings that corresponds to logically
34%%          the same entity, i.e. group all plain variables, all
35%%          table operations for each table, all varbinds to each
36%%          subagent.
37%% Returns: {VarbindsForThisAgent
38%%           VarbindsForSubAgents}   where
39%%             VarbindsForThisAgent  = List of {TableOid, List of #ivarbinds} |
40%%                                              #ivarbinds
41%%             VarbindsForSubAgents = List of {SubAgentPid,
42%%                                             List of {SAOid, #varbinds}}
43%%-----------------------------------------------------------------
44sort_varbindlist(Mib, Varbinds) ->
45    {Vars, Tabs, Subagents} = partition(Mib, Varbinds),
46    {lists:append(Tabs, Vars), Subagents}.
47
48partition(Mib, Vbs) ->
49    partition(Mib, Vbs, [], [], []).
50partition(Mib, [Varbind | Vbs], Vars, Tabs, Subs) ->
51    #varbind{oid = Oid} = Varbind,
52    ?vtrace("partition -> Oid: ~p", [Oid]),
53    case snmpa_mib:lookup(Mib, Oid) of
54	{table_column, MibEntry, TableOid} ->
55	    IVarbind = #ivarbind{varbind = fix_bits(Varbind, MibEntry),
56				 mibentry = MibEntry},
57	    NewTabs = insert_key(TableOid, IVarbind, Tabs),
58	    partition(Mib, Vbs, Vars, NewTabs, Subs);
59	{subagent, SubagentPid, SAOid} ->
60	    NewSubs = insert_key(SubagentPid, {SAOid, Varbind}, Subs),
61	    partition(Mib, Vbs, Vars, Tabs, NewSubs);
62	{variable, MibEntry} ->
63	    IVarbind = #ivarbind{varbind = fix_bits(Varbind, MibEntry),
64				 mibentry = MibEntry},
65	    partition(Mib, Vbs, [IVarbind | Vars], Tabs, Subs);
66	{false, ErrorCode} -> % ErrorCode = noSuchObject | noSuchInstance
67	    IVarbind = #ivarbind{status = ErrorCode, varbind = Varbind},
68	    partition(Mib, Vbs, [IVarbind | Vars], Tabs, Subs)
69    end;
70partition(_Mib, [], Vars, Subs, Tabs) ->
71    {Vars, Subs, Tabs}.
72
73% fix_bits(#varbind{bertype      = 'BITS',
74% 		  variabletype = 'OCTET STRING',
75% 		  value        = V} = VarBind, #me{asn1_type = A}) ->
76%     VarBind#varbind{variabletype = 'BITS',
77% 		    value = snmp_pdus:octet_str_to_bits(V)};
78fix_bits(VarBind, #me{asn1_type=A})
79  when ((A#asn1_type.bertype =:= 'BITS') andalso
80	(VarBind#varbind.variabletype =:= 'OCTET STRING') andalso
81	is_list(VarBind#varbind.value)) ->
82    VarBind#varbind{variabletype = 'BITS',
83 		    value = snmp_pdus:octet_str_to_bits(VarBind#varbind.value)};
84fix_bits(Vb,_me) -> Vb.
85
86insert_key(Key, Value, [{Key, Values} | Rest]) ->
87    [{Key, [Value | Values]} | Rest];
88insert_key(Key, Value, [{KeyX, Values} | Rest]) ->
89    [{KeyX, Values} | insert_key(Key, Value, Rest)];
90insert_key(Key, Value, []) ->
91    [{Key, [Value]}].
92
93%%-----------------------------------------------------------------
94%% Tranforms a list of {Oid, Vb} to a 2-tuple with all
95%% Oids and all Vbs. These lists will be reversed.
96%%-----------------------------------------------------------------
97sa_split(Vbs) -> sa_split(Vbs, [], []).
98sa_split([{SAOid, Vb} | T], Oids, Vbs) ->
99    sa_split(T, [SAOid | Oids], [Vb | Vbs]);
100sa_split([], Oids, Vbs) ->
101    {Oids, Vbs}.
102
103%%-----------------------------------------------------------------
104%% Func: sort_varbinds_rows/1
105%% Args: Varbinds is a list of {Oid, Value}.
106%% Pre: Varbinds is for one table.
107%% Purpose: Sorts all varbinds in Oid order, and in row order.
108%% Returns: list of Row where
109%%          Row = {Indexes, List of Col} and
110%%          Col = {ColNo, Value, OrgIndex} and
111%%          OrgIndex is index in original varbind list.
112%%-----------------------------------------------------------------
113sort_varbinds_rows(Varbinds) ->
114    P = pack(Varbinds),
115    S = lists:keysort(1, P),
116    unpack(S).
117
118%% In: list of {Oid, Value}
119%% Out: list of {{Indexes_for_row, Col}, Val, Index}
120pack(V) -> pack(1, V).
121pack(Index, [{[Col | Rest], Val} | T]) ->
122    [{{Rest, Col}, Val, Index} | pack(Index+1, T)];
123pack(_, []) -> [].
124
125unpack([{{Rest, Col}, Val, Index} | T]) ->
126    unpack(Rest, [[{Col, Val, Index}]], T);
127unpack([]) -> [].
128
129unpack(Rest, [Row | Rows], [{{Rest, Col}, Val, Index} | T]) ->
130    unpack(Rest, [[{Col, Val, Index} | Row] | Rows], T);
131unpack(Rest, [Row | Rows], [{{Rest2, Col}, Val, Index} | T]) ->
132    unpack(Rest2, [[{Col, Val, Index}],
133		   {Rest, lists:reverse(Row)} | Rows], T);
134unpack(Rest, [Row | Rows], []) ->
135    NewRow = {Rest, lists:reverse(Row)},
136    lists:reverse([NewRow | Rows]).
137
138%% OrgIndex should not be present when we call the is_set_ok/set/undo
139%% table functions. They just see the list of cols, and if an error
140%% occurs, they return the column nunber.
141%% Also, delete duplicate columns.  If a SET is performed with duplicate
142%% columns, it is undefined which column to use.  We just pick one.
143delete_org_index([{RowIndex, Cols} | Rows]) ->
144    [{RowIndex, doi(Cols)} | delete_org_index(Rows)];
145delete_org_index([]) -> [].
146
147doi([{Col, Val, OrgIndex}, {Col, _Val, _OrgIndex} | T]) ->
148    doi([{Col, Val, OrgIndex} | T]);
149doi([{Col, Val, _OrgIndex} | T]) ->
150    [{Col, Val} | doi(T)];
151doi([]) -> [].
152
153%% Maps the column number to OrgIndex.
154col_to_orgindex(0, _) -> 0;
155col_to_orgindex(Col, [{Col, _Val, OrgIndex}|_]) ->
156    OrgIndex;
157col_to_orgindex(Col, [_|Cols]) ->
158    col_to_orgindex(Col, Cols);
159col_to_orgindex(BadCol, _) ->
160    {false, BadCol}.
161