1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
16
%% $Id: mnesia_snmp_hook.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
18
-module(mnesia_snmp_hook).
20
%% Hooks (called from mnesia)
21
-export([check_ustruct/1, create_table/3, delete_table/2,
22
key_to_oid/3, update/1, start/2,
23
get_row/2, get_next_index/2, get_mnesia_key/2]).
25
%% sys callback functions
26
-export([system_continue/3,
35
true; %% default value, not SNMP'ified
36
check_ustruct([{key, Types}]) ->
37
is_snmp_type(to_list(Types));
38
check_ustruct(_) -> false.
40
to_list(Tuple) when tuple(Tuple) -> tuple_to_list(Tuple);
43
is_snmp_type([integer | T]) -> is_snmp_type(T);
44
is_snmp_type([string | T]) -> is_snmp_type(T);
45
is_snmp_type([fix_string | T]) -> is_snmp_type(T);
46
is_snmp_type([]) -> true;
47
is_snmp_type(_) -> false.
49
create_table([], MnesiaTab, _Storage) ->
50
mnesia:abort({badarg, MnesiaTab, {snmp, empty_snmpstruct}});
52
create_table([{key, Us}], MnesiaTab, Storage) ->
53
Tree = b_new(MnesiaTab, Us),
54
mnesia_lib:db_fixtable(Storage, MnesiaTab, true),
55
First = mnesia_lib:db_first(Storage, MnesiaTab),
56
build_table(First, MnesiaTab, Tree, Us, Storage),
57
mnesia_lib:db_fixtable(Storage, MnesiaTab, false),
60
build_table(MnesiaKey, MnesiaTab, Tree, Us, Storage)
61
when MnesiaKey /= '$end_of_table' ->
62
%% SnmpKey = key_to_oid(MnesiaTab, MnesiaKey, Us),
63
%% update(write, Tree, MnesiaKey, SnmpKey),
64
update(write, Tree, MnesiaKey, MnesiaKey),
65
Next = mnesia_lib:db_next_key(Storage, MnesiaTab, MnesiaKey),
66
build_table(Next, MnesiaTab, Tree, Us, Storage);
67
build_table('$end_of_table', _MnesiaTab, _Tree, _Us, _Storage) ->
70
delete_table(_MnesiaTab, Tree) ->
74
%%-----------------------------------------------------------------
75
%% update({Op, MnesiaTab, MnesiaKey, SnmpKey})
76
%%-----------------------------------------------------------------
78
update({clear_table, MnesiaTab}) ->
79
Tree = mnesia_lib:val({MnesiaTab, {index, snmp}}),
82
update({Op, MnesiaTab, MnesiaKey, SnmpKey}) ->
83
Tree = mnesia_lib:val({MnesiaTab, {index, snmp}}),
84
update(Op, Tree, MnesiaKey, SnmpKey).
86
update(Op, Tree, MnesiaKey, _) ->
89
b_insert(Tree, MnesiaKey, MnesiaKey);
93
b_delete(Tree, MnesiaKey);
95
b_delete(Tree, MnesiaKey)
99
%%-----------------------------------------------------------------
100
%% Func: key_to_oid(Tab, Key, Ustruct)
101
%% Args: Key ::= key()
102
%% key() ::= int() | string() | {int() | string()}
103
%% Type ::= {fix_string | term()}
104
%% Make an OBJECT IDENTIFIER out of it.
105
%% Variable length objects are prepended by their length.
106
%% Ex. Key = {"pelle", 42} AND Type = {string, integer} =>
107
%% OID [5, $p, $e, $l, $l, $e, 42]
108
%% Key = {"pelle", 42} AND Type = {fix_string, integer} =>
109
%% OID [$p, $e, $l, $l, $e, 42]
110
%%-----------------------------------------------------------------
111
key_to_oid(Tab, Key, [{key, Types}]) ->
112
MnesiaOid = {Tab, Key},
114
tuple(Key), tuple(Types) ->
115
case {size(Key), size(Types)} of
117
keys_to_oid(MnesiaOid, Size, Key, [], Types);
119
exit({bad_snmp_key, MnesiaOid})
122
key_to_oid_i(MnesiaOid, Key, Types)
125
key_to_oid_i(_MnesiaOid, Key, integer) when integer(Key) -> [Key];
126
key_to_oid_i(_MnesiaOid, Key, fix_string) when list(Key) -> Key;
127
key_to_oid_i(_MnesiaOid, Key, string) when list(Key) -> [length(Key) | Key];
128
key_to_oid_i(MnesiaOid, Key, Type) ->
129
exit({bad_snmp_key, [MnesiaOid, Key, Type]}).
131
keys_to_oid(_MnesiaOid, 0, _Key, Oid, _Types) -> Oid;
132
keys_to_oid(MnesiaOid, N, Key, Oid, Types) ->
133
Type = element(N, Types),
134
KeyPart = element(N, Key),
135
Oid2 = key_to_oid_i(MnesiaOid, KeyPart, Type) ++ Oid,
136
keys_to_oid(MnesiaOid, N-1, Key, Oid2, Types).
138
%%-----------------------------------------------------------------
140
%% Args: Name is the name of the table (atom)
141
%% RowIndex is an Oid
142
%% Returns: {ok, Row} | undefined
143
%% Note that the Row returned might contain columns that
144
%% are not visible via SNMP. e.g. the first column may be
145
%% ifIndex, and the last MFA ({ifIndex, col1, col2, MFA}).
146
%% where ifIndex is used only as index (not as a real col),
147
%% and MFA as extra info, used by the application.
148
%%-----------------------------------------------------------------
149
get_row(Name, RowIndex) ->
150
Tree = mnesia_lib:val({Name, {index, snmp}}),
151
case b_lookup(Tree, RowIndex) of
152
{ok, {_RowIndex, Key}} ->
153
[Row] = mnesia:dirty_read({Name, Key}),
159
%%-----------------------------------------------------------------
160
%% Func: get_next_index/2
161
%% Args: Name is the name of the table (atom)
162
%% RowIndex is an Oid
163
%% Returns: {ok, NextIndex} | endOfTable
164
%%-----------------------------------------------------------------
165
get_next_index(Name, RowIndex) ->
166
Tree = mnesia_lib:val({Name, {index, snmp}}),
167
case b_lookup_next(Tree, RowIndex) of
168
{ok, {NextIndex, _Key}} ->
174
%%-----------------------------------------------------------------
175
%% Func: get_mnesia_key/2
176
%% Purpose: Get the mnesia key corresponding to the RowIndex.
177
%% Args: Name is the name of the table (atom)
178
%% RowIndex is an Oid
179
%% Returns: {ok, Key} | undefiend
180
%%-----------------------------------------------------------------
181
get_mnesia_key(Name, RowIndex) ->
182
Tree = mnesia_lib:val({Name, {index, snmp}}),
183
case b_lookup(Tree, RowIndex) of
184
{ok, {_RowIndex, Key}} ->
190
%%-----------------------------------------------------------------
191
%% Encapsulate a bplus_tree in a process.
192
%%-----------------------------------------------------------------
194
b_new(MnesiaTab, Us) ->
195
case supervisor:start_child(mnesia_snmp_sup, [MnesiaTab, Us]) of
199
exit({badsnmp, MnesiaTab, Reason})
202
start(MnesiaTab, Us) ->
203
Name = {mnesia_snmp, MnesiaTab},
204
mnesia_monitor:start_proc(Name, ?MODULE, b_init, [self(), Us]).
206
b_insert(Tree, Key, Val) -> Tree ! {insert, Key, Val}.
207
b_delete(Tree, Key) -> Tree ! {delete, Key}.
208
b_lookup(Tree, Key) ->
209
Tree ! {lookup, self(), Key},
214
b_lookup_next(Tree, Key) ->
215
Tree ! {lookup_next, self(), Key},
225
b_init(Parent, Us) ->
227
Tree = snmp_index:new(Us),
228
proc_lib:init_ack(Parent, {ok, self()}),
229
b_loop(Parent, Tree, Us).
231
b_loop(Parent, Tree, Us) ->
233
{insert, Key, Val} ->
234
NTree = snmp_index:insert(Tree, Key, Val),
235
b_loop(Parent, NTree, Us);
237
NTree = snmp_index:delete(Tree, Key),
238
b_loop(Parent, NTree, Us);
239
{lookup, From, Key} ->
240
Res = snmp_index:get(Tree, Key),
241
From ! {bplus_res, Res},
242
b_loop(Parent, Tree, Us);
243
{lookup_next, From, Key} ->
244
Res = snmp_index:get_next(Tree, Key),
245
From ! {bplus_res, Res},
246
b_loop(Parent, Tree, Us);
248
catch snmp_index:delete(Tree), %% Catch because delete/1 is not
249
NewTree = snmp_index:new(Us), %% available in old snmp (before R5)
250
b_loop(Parent, NewTree, Us);
252
{'EXIT', Parent, Reason} ->
255
{system, From, Msg} ->
256
mnesia_lib:dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
257
sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], {Tree, Us})
261
%%%%%%%%%%%%%%%%%%%%%%%%%%%
264
system_continue(Parent, _Debug, {Tree, Us}) ->
265
b_loop(Parent, Tree, Us).
267
system_terminate(Reason, _Parent, _Debug, _Tree) ->
270
system_code_change(State, _Module, _OldVsn, _Extra) ->