Skip to content
Browse files

mv externel libs to src/lib, add snmp_mgr libs from R9C, add pgsql li…

…bs from jungerl

SVN Revision: 600
  • Loading branch information...
1 parent ff9ee37 commit 91156726007e413b0b9784fc3e65bb5fc5799546 @nniclausse nniclausse committed Dec 4, 2005
Showing with 2,523 additions and 7 deletions.
  1. +20 −7 Makefile.in
  2. 0 src/{tsung → lib}/gregexp.erl
  3. +606 −0 src/lib/pgsql_proto.erl
  4. +240 −0 src/lib/pgsql_util.erl
  5. +944 −0 src/lib/snmp_mgr.erl
  6. +713 −0 src/lib/snmp_mgr_misc.erl
View
27 Makefile.in
@@ -79,6 +79,7 @@ TARGETDIR = $(ERLANG_LIB_DIR)/$(APPLICATION)-$(VERSION)
TEMPLATES = $(wildcard $(ESRC)/templates/*.thtml)
TMP = $(wildcard *~) $(wildcard src/*~) $(wildcard inc/*~)
INC_FILES = $(wildcard $(INC)/*.hrl)
+LIBSRC = $(wildcard $(ESRC)/lib/*.erl)
SRC = $(wildcard $(ESRC)/$(APPLICATION)/*.erl)
CONTROLLER_SRC = $(wildcard $(ESRC)/$(CONTROLLER_APPLICATION)/*.erl)
RECORDER_SRC = $(wildcard $(ESRC)/$(RECORDER_APPLICATION)/*.erl)
@@ -92,6 +93,8 @@ PERL_SCRIPTS = $(basename $(PERL_SCRIPTS_SRC))
TARGET = $(addsuffix .beam, $(basename \
$(addprefix $(EBIN)/, $(notdir $(SRC)))))
+LIB_TARGET = $(addsuffix .beam, $(basename \
+ $(addprefix $(EBIN)/, $(notdir $(LIBSRC)))))
CONTROLLER_TARGET = $(addsuffix .beam, $(basename \
$(addprefix $(EBIN)/, $(notdir $(CONTROLLER_SRC)))))
RECORDER_TARGET = $(addsuffix .beam, $(basename \
@@ -126,7 +129,7 @@ DIST_COMMON=Makefile.in $(CONFFILE_SRC) $(PERL_SCRIPTS_SRC) tsung.sh.in tsung.sp
.PHONY: doc
-tsung: Makefile config.status $(PERL_SCRIPTS) tsung.sh tsung.spec $(TARGET) $(RECORDER_TARGET) $(CONTROLLER_TARGET)
+tsung: Makefile config.status $(PERL_SCRIPTS) tsung.sh tsung.spec $(TARGET) $(RECORDER_TARGET) $(CONTROLLER_TARGET) $(LIB_TARGET)
all: clean tsung
@@ -166,6 +169,7 @@ install: doc boot install_recorder install_controller $(CONFFILE)
cp $(INC_FILES) $(TARGETDIR)/include
cp $(TARGET) $(TARGETDIR)/ebin
+ cp $(LIB_TARGET) $(TARGETDIR)/ebin
cp builder.beam $(TARGETDIR)/ebin
cp $(TGT_APPFILES_E) $(TARGETDIR)/ebin
@@ -205,7 +209,7 @@ install_recorder:
cp $(RECORDER_TGT_APPFILES_P) $(RECORDER_TARGETDIR)/priv
cp $(RECORDER_SRC) $(RECORDER_SRC_APPFILES) $(RECORDER_TARGETDIR)/src
- echo $(BUILD_OPTIONS_DOT) > $(RECORDER_TARGETDIR)/BUILD_OPTIONS
+ @echo $(BUILD_OPTIONS_DOT) > $(RECORDER_TARGETDIR)/BUILD_OPTIONS
install_controller:
install -d $(CONTROLLER_TARGETDIR)/priv
@@ -219,7 +223,7 @@ install_controller:
cp $(CONTROLLER_TGT_APPFILES_P) $(CONTROLLER_TARGETDIR)/priv
cp $(CONTROLLER_SRC) $(CONTROLLER_SRC_APPFILES) $(CONTROLLER_TARGETDIR)/src
- echo $(BUILD_OPTIONS_DOT) > $(CONTROLLER_TARGETDIR)/BUILD_OPTIONS
+ @echo $(BUILD_OPTIONS_DOT) > $(CONTROLLER_TARGETDIR)/BUILD_OPTIONS
uninstall:
rm -rf $(TARGETDIR) $(SCRIPT)
@@ -236,11 +240,13 @@ priv/tsung.boot: builder.beam $(SRC_APPFILES)
@ln -sf $(PWD)/priv temp/lib/$(APPLICATION)-$(VERSION)/priv
@ln -sf $(PWD)/builder.beam temp/lib/$(APPLICATION)-$(VERSION)/
@ln -sf $(PWD) temp/lib/$(APPLICATION)-$(VERSION)
+ @echo -n "build main app boot script ... "
@(cd temp/lib/$(APPLICATION)-$(VERSION) \
&& echo $(BUILD_OPTIONS) > $(BUILD_OPTIONS_FILE) \
- && $(ERL) -s builder go -s init stop \
+ && $(ERL) -s builder go -s init stop > /dev/null 2>&1 \
)
@rm -rf temp
+ @echo "done"
priv/tsung_controller.boot: builder.beam $(CONTROLLER_SRC_APPFILES)
# use builder to make boot file
@@ -251,11 +257,13 @@ priv/tsung_controller.boot: builder.beam $(CONTROLLER_SRC_APPFILES)
@ln -sf $(PWD)/include temp/lib/$(CONTROLLER_APPLICATION)-$(VERSION)/include
@ln -sf $(PWD)/priv temp/lib/$(CONTROLLER_APPLICATION)-$(VERSION)/priv
@ln -sf $(PWD)/builder.beam temp/lib/$(CONTROLLER_APPLICATION)-$(VERSION)/
+ @echo -n "build controller boot script ... "
@(cd temp/lib/$(CONTROLLER_APPLICATION)-$(VERSION) \
&& echo $(BUILD_OPTIONS) > $(BUILD_OPTIONS_FILE) \
- && $(ERL) -s builder go -s init stop \
+ && $(ERL) -s builder go -s init stop > /dev/null 2>&1 \
)
@rm -rf temp
+ @echo "done"
priv/tsung_recorder.boot: builder.beam $(RECORDER_SRC_APPFILES)
# use builder to make boot file
@@ -266,11 +274,13 @@ priv/tsung_recorder.boot: builder.beam $(RECORDER_SRC_APPFILES)
@ln -sf $(PWD)/include temp/lib/$(RECORDER_APPLICATION)-$(VERSION)/include
@ln -sf $(PWD)/priv temp/lib/$(RECORDER_APPLICATION)-$(VERSION)/priv
@ln -sf $(PWD)/builder.beam temp/lib/$(RECORDER_APPLICATION)-$(VERSION)/
+ @echo -n "build recorder boot script ... "
@(cd temp/lib/$(RECORDER_APPLICATION)-$(VERSION) \
&& echo $(BUILD_OPTIONS) > $(BUILD_OPTIONS_FILE) \
- && $(ERL) -noshell -s builder go -s init stop \
+ && $(ERL) -noshell -s builder go -s init stop > /dev/null 2>&1 \
)
@rm -rf temp
+ @echo "done"
Makefile: Makefile.in config.status
@@ -321,7 +331,10 @@ snapshot:
make TYPE=snapshot release
builder.beam: priv/builder.erl
- $(CC) $(OPT) -I $(INC) $<
+ $(CC) -W0 $(OPT) -I $(INC) $<
+
+ebin/%.beam: src/lib/%.erl $(INC_FILES)
+ $(CC) $(OPT) -I $(INC) -I $(ERLANG_XMERL_DIR) -o ebin $<
ebin/%.beam: src/$(APPLICATION)/%.erl $(INC_FILES)
$(CC) $(OPT) -I $(INC) -I $(ERLANG_XMERL_DIR) -o ebin $<
View
0 src/tsung/gregexp.erl → src/lib/gregexp.erl
File renamed without changes.
View
606 src/lib/pgsql_proto.erl
@@ -0,0 +1,606 @@
+%%% File : pgsql_proto.erl
+%%% Author : Christian Sunesson <chrisu@kth.se>
+%%% Description : PostgreSQL protocol driver
+%%% Created : 9 May 2005
+%%%
+%%% Nicolas Niclausse 8/11/2005: export encode and encode_message
+
+%%% This is the protocol handling part of the PostgreSQL driver, it turns packages into
+%%% erlang term messages and back.
+
+-module(pgsql_proto).
+
+%% TODO:
+%% When factorizing make clear distinction between message and packet.
+%% Packet == binary on-wire representation
+%% Message = parsed Packet as erlang terms.
+
+%%% Version 3.0 of the protocol.
+%%% Supported in postgres from version 7.4
+-define(PROTOCOL_MAJOR, 3).
+-define(PROTOCOL_MINOR, 0).
+
+%%% PostgreSQL protocol message codes
+-define(PG_BACKEND_KEY_DATA, $K).
+-define(PG_PARAMETER_STATUS, $S).
+-define(PG_ERROR_MESSAGE, $E).
+-define(PG_NOTICE_RESPONSE, $N).
+-define(PG_EMPTY_RESPONSE, $I).
+-define(PG_ROW_DESCRIPTION, $T).
+-define(PG_DATA_ROW, $D).
+-define(PG_READY_FOR_QUERY, $Z).
+-define(PG_AUTHENTICATE, $R).
+-define(PG_BIND, $B).
+-define(PG_PARSE, $P).
+-define(PG_COMMAND_COMPLETE, $C).
+-define(PG_PARSE_COMPLETE, $1).
+-define(PG_BIND_COMPLETE, $2).
+-define(PG_CLOSE_COMPLETE, $3).
+-define(PG_PORTAL_SUSPENDED, $s).
+-define(PG_NO_DATA, $n).
+
+-export([init/2, idle/2]).
+-export([run/1]).
+
+%% For protocol unwrapping, pgsql_tcp for example.
+-export([decode_packet/2]).
+-export([encode_message/2]).
+-export([encode/2]).
+
+-import(pgsql_util, [option/2]).
+-import(pgsql_util, [socket/1]).
+-import(pgsql_util, [send/2, send_int/2, send_msg/3]).
+-import(pgsql_util, [recv_msg/2, recv_msg/1, recv_byte/2, recv_byte/1]).
+-import(pgsql_util, [string/1, make_pair/2, split_pair/1]).
+-import(pgsql_util, [count_string/1, to_string/1]).
+-import(pgsql_util, [coldescs/2, datacoldescs/3]).
+
+deliver(Message) ->
+ DriverPid = get(driver),
+ DriverPid ! Message.
+
+run(Options) ->
+ Db = spawn_link(pgsql_proto, init,
+ [self(), Options]),
+ {ok, Db}.
+
+init(DriverPid, Options) ->
+ put(options, Options), % connection setup options
+ put(driver, DriverPid), % driver's process id
+
+ %%io:format("Init~n", []),
+ Host = option(host, "localhost"),
+ Port = option(port, 5432),
+
+ case socket({tcp, Host, Port}) of
+ {ok, Sock } ->
+ connect(Sock);
+ Error ->
+ Reason = {init, Error},
+ DriverPid ! {pgsql_error, Reason},
+ exit(Reason)
+ end.
+
+connect(Sock) ->
+ %%io:format("Connect~n", []),
+ %% Connection settings for database-login.
+ UserName = option(user, "cos"),
+ DatabaseName = option(database, "template1"),
+
+ %% Make protocol startup packet.
+ Version = <<?PROTOCOL_MAJOR:16/integer, ?PROTOCOL_MINOR:16/integer>>,
+ User = make_pair(user, UserName),
+ Database = make_pair(database, DatabaseName),
+ StartupPacket = <<Version/binary,
+ User/binary,
+ Database/binary,
+ 0>>,
+
+ %% Backend will continue with authentication after the startup packet
+ PacketSize = 4 + size(StartupPacket),
+ ok = gen_tcp:send(Sock, <<PacketSize:32/integer, StartupPacket/binary>>),
+ authenticate(Sock).
+
+
+authenticate(Sock) ->
+ %% Await authentication request from backend.
+ {ok, Code, Packet} = recv_msg(Sock, 5000),
+ {ok, Value} = decode_packet(Code, Packet),
+ case Value of
+ %% Error response
+ {error_message, Message} ->
+ exit({authentication, Message});
+ {authenticate, AuthMethod} ->
+ case AuthMethod of
+ 0 -> % Auth ok
+ setup(Sock, []);
+ 1 -> % Kerberos 4
+ exit({nyi, auth_kerberos4});
+ 2 -> % Kerberos 5
+ exit({nyi, auth_kerberos5});
+ 3 -> % Password
+ PassString = option(password, ""),
+ PassWordLen = length(PassString) + 5,
+ Password = <<PassWordLen:32/integer,
+ PassString, 0:32/integer>>,
+ ok = send(Sock, Password),
+ authenticate(Sock);
+ 4 -> % Hashed password
+ exit({nyi, auth_crypt});
+ 5 -> % MD5 password
+ exit({nyi, auth_md5});
+ _ ->
+ exit({authentication, {unknown, AuthMethod}})
+ end;
+ %% Unknown message received
+ Any ->
+ exit({protocol_error, Any})
+ end.
+
+setup(Sock, Params) ->
+ %% Receive startup messages until ReadyForQuery
+ {ok, Code, Package} = recv_msg(Sock, 5000),
+ {ok, Pair} = decode_packet(Code, Package),
+ case Pair of
+ %% BackendKeyData, necessary for issuing cancel requests
+ {backend_key_data, {Pid, Secret}} ->
+ Params1 = [{secret, {Pid, Secret}}|Params],
+ setup(Sock, Params1);
+ %% ParameterStatus, a key-value pair.
+ {parameter_status, {Key, Value}} ->
+ Params1 = [{{parameter, Key}, Value}|Params],
+ setup(Sock, Params1);
+ %% Error message, with a sequence of <<Code:8/integer, String, 0>>
+ %% of error descriptions. Code==0 terminates the Reason.
+ {error_message, Message} ->
+ gen_tcp:close(Sock),
+ exit({error_response, Message});
+ %% Notice Response, with a sequence of <<Code:8/integer, String,0>>
+ %% identified fields. Code==0 terminates the Notice.
+ {notice_response, Notice} ->
+ deliver({pgsql_notice, Notice}),
+ setup(Sock, Params);
+ %% Ready for Query, backend is ready for a new query cycle
+ {ready_for_query, Status} ->
+ deliver({pgsql_params, Params}),
+ deliver(pgsql_connected),
+ put(params, Params),
+ connected(Sock);
+ Any ->
+ deliver({unknown_setup, Any}),
+ connected(Sock)
+
+ end.
+
+%% Connected state. Can now start to push messages
+%% between frontend and backend. But first some setup.
+connected(Sock) ->
+ DriverPid = get(driver),
+
+ %% Protocol unwrapping process. Factored out to make future
+ %% SSL and unix domain support easier. Store process under
+ %% 'socket' in the process dictionary.
+ begin
+ Unwrapper = spawn_link(pgsql_tcp, loop0, [Sock, self()]),
+ ok = gen_tcp:controlling_process(Sock, Unwrapper),
+ put(socket, Unwrapper)
+ end,
+
+ %% Lookup oid to type names and store them in a dictionary under
+ %% 'oidmap' in the process dictionary.
+ begin
+ Packet = encode_message(squery, "SELECT oid, typname FROM pg_type"),
+ ok = send(Sock, Packet),
+ {ok, [{"SELECT", _ColDesc, Rows}]} = process_squery([]),
+ Rows1 = lists:map(fun ([CodeS, NameS]) ->
+ Code = list_to_integer(CodeS),
+ Name = list_to_atom(NameS),
+ {Code, Name}
+ end,
+ Rows),
+ OidMap = dict:from_list(Rows1),
+ put(oidmap, OidMap)
+ end,
+
+ %% Ready to start marshalling between frontend and backend.
+ idle(Sock, DriverPid).
+
+%% In the idle state we should only be receiving requests from the
+%% frontend. Async backend messages should be forwarded to responsible
+%% process.
+idle(Sock, Pid) ->
+ receive
+ %% Unexpected idle messages. No request should continue to the
+ %% idle state before a ready-for-query has been received.
+ {message, Message} ->
+ io:format("Unexpected message when idle: ~p~n", [Message]),
+ idle(Sock, Pid);
+
+ %% Socket closed or socket error messages.
+ {socket, Sock, Condition} ->
+ exit({socket, Condition});
+
+ %% Close connection
+ {terminate, Ref, Pid} ->
+ Packet = encode_message(terminate, []),
+ ok = send(Sock, Packet),
+ gen_tcp:close(Sock),
+ Pid ! {pgsql, Ref, terminated},
+ unlink(Pid),
+ exit(terminating);
+ %% Simple query
+ {squery, Ref, Pid, Query} ->
+ Packet = encode_message(squery, Query),
+ ok = send(Sock, Packet),
+ {ok, Result} = process_squery([]),
+ Pid ! {pgsql, Ref, Result},
+ idle(Sock, Pid);
+ %% Extended query
+ %% simplistic version using the unnammed prepared statement and portal.
+ {equery, Ref, Pid, {Query, Params}} ->
+ ParseP = encode_message(parse, {"", Query, []}),
+ BindP = encode_message(bind, {"", "", Params, [binary]}),
+ DescribeP = encode_message(describe, {portal, ""}),
+ ExecuteP = encode_message(execute, {"", 0}),
+ SyncP = encode_message(sync, []),
+ ok = send(Sock, [ParseP, BindP, DescribeP, ExecuteP, SyncP]),
+
+ {ok, Command, Desc, Status, Logs} = process_equery([]),
+
+ OidMap = get(oidmap),
+ NameTypes = lists:map(fun({Name, _Format, _ColNo, Oid, _, _, _}) ->
+ {Name, dict:fetch(Oid, OidMap)}
+ end,
+ Desc),
+ Pid ! {pgsql, Ref, {Command, Status, NameTypes, Logs}},
+ idle(Sock, Pid);
+ %% Prepare a statement, so it can be used for queries later on.
+ {prepare, Ref, Pid, {Name, Query}} ->
+ send_message(Sock, parse, {Name, Query, []}),
+ send_message(Sock, describe, {prepared_statement, Name}),
+ send_message(Sock, sync, []),
+ {ok, State, ParamDesc, ResultDesc} = process_prepare({[], []}),
+ OidMap = get(oidmap),
+ ParamTypes =
+ lists:map(fun (Oid) -> dict:fetch(Oid, OidMap) end, ParamDesc),
+ ResultNameTypes = lists:map(fun ({ColName, _Format, _ColNo, Oid, _, _, _}) ->
+ {ColName, dict:fetch(Oid, OidMap)}
+ end,
+ ResultDesc),
+ Pid ! {pgsql, Ref, {prepared, State, ParamTypes, ResultNameTypes}},
+ idle(Sock, Pid);
+ %% Close a prepared statement.
+ {unprepare, Ref, Pid, Name} ->
+ send_message(Sock, close, {prepared_statement, Name}),
+ send_message(Sock, sync, []),
+ {ok, _Status} = process_unprepare(),
+ Pid ! {pgsql, Ref, unprepared},
+ idle(Sock, Pid);
+ %% Execute a prepared statement
+ {execute, Ref, Pid, {Name, Params}} ->
+ %%io:format("execute: ~p ~p ~n", [Name, Params]),
+ begin % Issue first requests for the prepared statement.
+ BindP = encode_message(bind, {"", Name, Params, [binary]}),
+ DescribeP = encode_message(describe, {portal, ""}),
+ ExecuteP = encode_message(execute, {"", 0}),
+ FlushP = encode_message(flush, []),
+ ok = send(Sock, [BindP, DescribeP, ExecuteP, FlushP])
+ end,
+ receive
+ {pgsql, {bind_complete, _}} -> % Bind reply first.
+ %% Collect response to describe message,
+ %% which gives a hint of the rest of the messages.
+ {ok, Command, Result} = process_execute(Sock, Ref, Pid),
+
+ begin % Close portal and end extended query.
+ CloseP = encode_message(close, {portal, ""}),
+ SyncP = encode_message(sync, []),
+ ok = send(Sock, [CloseP, SyncP])
+ end,
+ receive
+ %% Collect response to close message.
+ {pgsql, {close_complete, _}} ->
+ receive
+ %% Collect response to sync message.
+ {pgsql, {ready_for_query, Status}} ->
+ %%io:format("execute: ~p ~p ~p~n",
+ %% [Status, Command, Result]),
+ Pid ! {pgsql, Ref, {Command, Result}},
+ idle(Sock, Pid);
+ {pgsql, Unknown} ->
+ exit(Unknown)
+ end;
+ {pgsql, Unknown} ->
+ exit(Unknown)
+ end;
+ {pgsql, Unknown} ->
+ exit(Unknown)
+ end;
+
+ %% More requests to come.
+ %% .
+ %% .
+ %% .
+
+ Any ->
+ exit({unknown_request, Any})
+
+ end.
+
+
+%% In the process_squery state we collect responses until the backend is
+%% done processing.
+process_squery(Log) ->
+ receive
+ {pgsql, {row_description, Cols}} ->
+ {ok, Command, Rows} = process_squery_cols([]),
+ process_squery([{Command, Cols, Rows}|Log]);
+ {pgsql, {command_complete, Command}} ->
+ process_squery([Command|Log]);
+ {pgsql, {ready_for_query, Status}} ->
+ {ok, lists:reverse(Log)};
+ {pgsql, Any} ->
+ process_squery(Log)
+ end.
+process_squery_cols(Log) ->
+ receive
+ {pgsql, {data_row, Row}} ->
+ process_squery_cols([lists:map(fun binary_to_list/1, Row)|Log]);
+ {pgsql, {command_complete, Command}} ->
+ {ok, Command, lists:reverse(Log)}
+ end.
+
+process_equery(Log) ->
+ receive
+ %% Consume parse and bind complete messages when waiting for the first
+ %% first row_description message. What happens if the equery doesnt
+ %% return a result set?
+ {pgsql, {parse_complete, _}} ->
+ process_equery(Log);
+ {pgsql, {bind_complete, _}} ->
+ process_equery(Log);
+ {pgsql, {row_description, Descs}} ->
+ {ok, Descs1} = pgsql_util:decode_descs(Descs),
+ process_equery_datarow(Descs1, Log, {undefined, Descs, undefined});
+ {pgsql, Any} ->
+ process_equery([Any|Log])
+ end.
+
+process_equery_datarow(Types, Log, Info={Command, Desc, Status}) ->
+ receive
+ %%
+ {pgsql, {command_complete, Command1}} ->
+ process_equery_datarow(Types, Log, {Command1, Desc, Status});
+ {pgsql, {ready_for_query, Status1}} ->
+ {ok, Command, Desc, Status1, lists:reverse(Log)};
+ {pgsql, {data_row, Row}} ->
+ {ok, DecodedRow} = pgsql_util:decode_row(Types, Row),
+ process_equery_datarow(Types, [DecodedRow|Log], Info);
+ {pgsql, Any} ->
+ process_equery_datarow(Types, [Any|Log], Info)
+ end.
+
+process_prepare(Info={ParamDesc, ResultDesc}) ->
+ receive
+ {pgsql, {no_data, _}} ->
+ process_prepare({ParamDesc, []});
+ {pgsql, {parse_complete, _}} ->
+ process_prepare(Info);
+ {pgsql, {parameter_description, Oids}} ->
+ process_prepare({Oids, ResultDesc});
+ {pgsql, {row_description, Desc}} ->
+ process_prepare({ParamDesc, Desc});
+ {pgsql, {ready_for_query, Status}} ->
+ {ok, Status, ParamDesc, ResultDesc};
+ {pgsql, Any} ->
+ io:format("process_prepare: ~p~n", [Any]),
+ process_prepare(Info)
+ end.
+
+process_unprepare() ->
+ receive
+ {pgsql, {ready_for_query, Status}} ->
+ {ok, Status};
+ {pgsql, {close_complate, []}} ->
+ process_unprepare();
+ {pgsql, Any} ->
+ io:format("process_unprepare: ~p~n", [Any]),
+ process_unprepare()
+ end.
+
+process_execute(Sock, Ref, Pid) ->
+ %% Either the response begins with a no_data or a row_description
+ %% Needs to return {ok, Status, Result}
+ %% where Result = {Command, ...}
+ receive
+ {pgsql, {no_data, _}} ->
+ {ok, Command, Result} = process_execute_nodata();
+ {pgsql, {row_description, Descs}} ->
+ {ok, Types} = pgsql_util:decode_descs(Descs),
+ {ok, Command, Result} =
+ process_execute_resultset(Sock, Ref, Pid, Types, []);
+
+ {pgsql, Unknown} ->
+ exit(Unknown)
+ end.
+
+process_execute_nodata() ->
+ receive
+ {pgsql, {command_complete, Command}} ->
+ case Command of
+ "INSERT "++Rest ->
+ {ok, [{integer, _, _Table},
+ {integer, _, NRows}], _} = erl_scan:string(Rest),
+ {ok, 'INSERT', NRows};
+ "SELECT" ->
+ {ok, 'SELECT', should_not_happen};
+ "DELETE "++Rest ->
+ {ok, [{integer, _, NRows}], _} =
+ erl_scan:string(Rest),
+ {ok, 'DELETE', NRows};
+ Any ->
+ {ok, nyi, Any}
+ end;
+
+ {pgsql, Unknown} ->
+ exit(Unknown)
+ end.
+process_execute_resultset(Sock, Ref, Pid, Types, Log) ->
+ receive
+ {pgsql, {command_complete, Command}} ->
+ {ok, list_to_atom(Command), lists:reverse(Log)};
+ {pgsql, {data_row, Row}} ->
+ {ok, DecodedRow} = pgsql_util:decode_row(Types, Row),
+ process_execute_resultset(Sock, Ref, Pid, Types, [DecodedRow|Log]);
+ {pgsql, {portal_suspended, _}} ->
+ throw(portal_suspended);
+ {pgsql, Any} ->
+ %%process_execute_resultset(Types, [Any|Log])
+ exit(Any)
+ end.
+
+%% With a message type Code and the payload Packet apropriate
+%% decoding procedure can proceed.
+decode_packet(Code, Packet) ->
+ Ret = fun(CodeName, Values) -> {ok, {CodeName, Values}} end,
+ case Code of
+ ?PG_ERROR_MESSAGE ->
+ Message = pgsql_util:errordesc(Packet),
+ Ret(error_message, Message);
+ ?PG_EMPTY_RESPONSE ->
+ Ret(empty_response, []);
+ ?PG_ROW_DESCRIPTION ->
+ <<Columns:16/integer, ColDescs/binary>> = Packet,
+ Descs = coldescs(ColDescs, []),
+ Ret(row_description, Descs);
+ ?PG_READY_FOR_QUERY ->
+ <<State:8/integer>> = Packet,
+ case State of
+ $I ->
+ Ret(ready_for_query, idle);
+ $T ->
+ Ret(ready_for_query, transaction);
+ $E ->
+ Ret(ready_for_query, failed_transaction)
+ end;
+ ?PG_COMMAND_COMPLETE ->
+ {Task, _} = to_string(Packet),
+ Ret(command_complete, Task);
+ ?PG_DATA_ROW ->
+ <<NumberCol:16/integer, RowData/binary>> = Packet,
+ ColData = datacoldescs(NumberCol, RowData, []),
+ Ret(data_row, ColData);
+ ?PG_BACKEND_KEY_DATA ->
+ <<Pid:32/integer, Secret:32/integer>> = Packet,
+ Ret(backend_key_data, {Pid, Secret});
+ ?PG_PARAMETER_STATUS ->
+ {Key, Value} = split_pair(Packet),
+ Ret(parameter_status, {Key, Value});
+ ?PG_NOTICE_RESPONSE ->
+ Ret(notice_response, []);
+ ?PG_AUTHENTICATE ->
+ <<AuthMethod:32/integer, _Rest/binary>> = Packet,
+ Ret(authenticate, AuthMethod);
+ ?PG_PARSE_COMPLETE ->
+ Ret(parse_complete, []);
+ ?PG_BIND_COMPLETE ->
+ Ret(bind_complete, []);
+ ?PG_PORTAL_SUSPENDED ->
+ Ret(portal_suspended, []);
+ ?PG_CLOSE_COMPLETE ->
+ Ret(close_complete, []);
+ $t ->
+ <<NParams:16/integer, OidsP/binary>> = Packet,
+ Oids = pgsql_util:oids(OidsP, []),
+ Ret(parameter_description, Oids);
+ ?PG_NO_DATA ->
+ Ret(no_data, []);
+
+ Any ->
+ Ret(unknown, [Code])
+ end.
+
+send_message(Sock, Type, Values) ->
+ %%io:format("send_message:~p~n", [{Type, Values}]),
+ Packet = encode_message(Type, Values),
+ ok = send(Sock, Packet).
+
+%% Add header to a message.
+encode(Code, Packet) ->
+ Len = size(Packet) + 4,
+ <<Code:8/integer, Len:4/integer-unit:8, Packet/binary>>.
+
+%% Encode a message of a given type.
+encode_message(terminate, _) ->
+ encode($X, <<>>);
+encode_message(squery, Query) -> % squery as in simple query.
+ encode($Q, string(Query));
+encode_message(close, {Object, Name}) ->
+ Type = case Object of prepared_statement -> $S; portal -> $P end,
+ String = string(Name),
+ encode($C, <<Type/integer, String/binary>>);
+encode_message(describe, {Object, Name}) ->
+ ObjectP = case Object of prepared_statement -> $S; portal -> $P end,
+ NameP = string(Name),
+ encode($D, <<ObjectP:8/integer, NameP/binary>>);
+encode_message(flush, _) ->
+ encode($H, <<>>);
+encode_message(parse, {Name, Query, _Oids}) ->
+ StringName = string(Name),
+ StringQuery = string(Query),
+ encode($P, <<StringName/binary, StringQuery/binary, 0:16/integer>>);
+encode_message(bind, Bind={NamePortal, NamePrepared,
+ Parameters, ResultFormats}) ->
+ %%io:format("encode bind: ~p~n", [Bind]),
+ PortalP = string(NamePortal),
+ PreparedP = string(NamePrepared),
+
+ ParamFormatsList = lists:map(
+ fun (Bin) when is_binary(Bin) -> <<1:16/integer>>;
+ (Text) -> <<0:16/integer>> end,
+ Parameters),
+ ParamFormatsP = erlang:concat_binary(ParamFormatsList),
+
+ NParameters = length(Parameters),
+ ParametersList = lists:map(
+ fun (null) ->
+ Minus = -1,
+ <<Minus:32/integer>>;
+ (Bin) when is_binary(Bin) ->
+ Size = size(Bin),
+ <<Size:32/integer, Bin/binary>>;
+ (Integer) when is_integer(Integer) ->
+ List = integer_to_list(Integer),
+ Bin = list_to_binary(List),
+ Size = size(Bin),
+ <<Size:32/integer, Bin/binary>>;
+ (Text) ->
+ Bin = list_to_binary(Text),
+ Size = size(Bin),
+ <<Size:32/integer, Bin/binary>>
+ end,
+ Parameters),
+ ParametersP = erlang:concat_binary(ParametersList),
+
+ NResultFormats = length(ResultFormats),
+ ResultFormatsList = lists:map(
+ fun (binary) -> <<1:16/integer>>;
+ (text) -> <<0:16/integer>> end,
+ ResultFormats),
+ ResultFormatsP = erlang:concat_binary(ResultFormatsList),
+
+ %%io:format("encode bind: ~p~n", [{PortalP, PreparedP,
+ %% NParameters, ParamFormatsP,
+ %% NParameters, ParametersP,
+ %% NResultFormats, ResultFormatsP}]),
+ encode($B, <<PortalP/binary, PreparedP/binary,
+ NParameters:16/integer, ParamFormatsP/binary,
+ NParameters:16/integer, ParametersP/binary,
+ NResultFormats:16/integer, ResultFormatsP/binary>>);
+encode_message(execute, {Portal, Limit}) ->
+ String = string(Portal),
+ encode($E, <<String/binary, Limit:32/integer>>);
+encode_message(sync, _) ->
+ encode($S, <<>>).
+
+
View
240 src/lib/pgsql_util.erl
@@ -0,0 +1,240 @@
+%%% File : pgsql_util.erl
+%%% Author : Christian Sunesson
+%%% Description : utility functions used in implementation of
+%%% postgresql driver.
+%%% Created : 11 May 2005 by Blah <cos@local>
+
+%%% Nicolas Niclausse, 8/11/2005 : add split_pair_rec/1 fun
+
+-module(pgsql_util).
+
+%% Key-Value handling
+-export([option/2]).
+
+%% Networking
+-export([socket/1]).
+-export([send/2, send_int/2, send_msg/3]).
+-export([recv_msg/2, recv_msg/1, recv_byte/2, recv_byte/1]).
+
+%% Protocol packing
+-export([string/1, make_pair/2, split_pair/1]).
+-export([split_pair_rec/1]).
+-export([count_string/1, to_string/1]).
+-export([oids/2, coldescs/2, datacoldescs/3]).
+-export([decode_row/2, decode_descs/1]).
+-export([errordesc/1]).
+
+-export([zip/2]).
+
+%% Lookup key in a plist stored in process dictionary under 'options'.
+%% Default is returned if there is no value for Key in the plist.
+option(Key, Default) ->
+ Plist = get(options),
+ case proplists:get_value(Key, Plist, Default) of
+ Default ->
+ Default;
+ Value ->
+ Value
+ end.
+
+
+%% Open a TCP connection
+socket({tcp, Host, Port}) ->
+ gen_tcp:connect(Host, Port, [{active, false}, binary, {packet, raw}], 5000).
+
+send(Sock, Packet) ->
+ gen_tcp:send(Sock, Packet).
+send_int(Sock, Int) ->
+ Packet = <<Int:32/integer>>,
+ gen_tcp:send(Sock, Packet).
+
+send_msg(Sock, Code, Packet) when binary(Packet) ->
+ Len = size(Packet) + 4,
+ Msg = <<Code:8/integer, Len:4/integer-unit:8, Packet/binary>>,
+ gen_tcp:send(Sock, Msg).
+
+recv_msg(Sock, Timeout) ->
+ {ok, Head} = gen_tcp:recv(Sock, 5, Timeout),
+ <<Code:8/integer, Size:4/integer-unit:8>> = Head,
+ %%io:format("Code: ~p, Size: ~p~n", [Code, Size]),
+ if
+ Size > 4 ->
+ {ok, Packet} = gen_tcp:recv(Sock, Size-4, Timeout),
+ {ok, Code, Packet};
+ true ->
+ {ok, Code, <<>>}
+ end.
+recv_msg(Sock) ->
+ recv_msg(Sock, infinity).
+
+
+recv_byte(Sock) ->
+ recv_byte(Sock, infinity).
+recv_byte(Sock, Timeout) ->
+ case gen_tcp:recv(Sock, 1, Timeout) of
+ {ok, <<Byte:1/integer-unit:8>>} ->
+ {ok, Byte};
+ E={error, Reason} ->
+ throw(E)
+ end.
+
+string(String) ->
+ Bin = list_to_binary(String),
+ <<Bin/binary, 0/integer>>.
+
+%%% Two zero terminated strings.
+make_pair(Key, Value) when atom(Key) ->
+ make_pair(atom_to_list(Key), Value);
+make_pair(Key, Value) when atom(Value) ->
+ make_pair(Key, atom_to_list(Value));
+make_pair(Key, Value) ->
+ BinKey = list_to_binary(Key),
+ BinValue = list_to_binary(Value),
+ <<BinKey/binary, 0/integer,
+ BinValue/binary, 0/integer>>.
+
+split_pair(Bin) when binary(Bin) ->
+ split_pair(binary_to_list(Bin));
+split_pair(Str) ->
+ split_pair_rec(Str, norec).
+
+split_pair_rec(Bin) when binary(Bin) ->
+ split_pair_rec(binary_to_list(Bin));
+split_pair_rec(Arg) ->
+ split_pair_rec(Arg,[]).
+
+split_pair_rec([], Acc) ->
+ lists:reverse(Acc);
+split_pair_rec([0], Acc) ->
+ lists:reverse(Acc);
+split_pair_rec(S, Acc) ->
+ Fun = fun(C) -> C /= 0 end,
+ {Key, [0|S1]} = lists:splitwith(Fun, S),
+ {Value, [0|Tail]} = lists:splitwith(Fun, S1),
+ case Acc of
+ norec -> {Key, Value};
+ _ ->
+ split_pair_rec(Tail, [{Key, Value}| Acc])
+ end.
+
+
+count_string(Bin) when binary(Bin) ->
+ count_string(Bin, 0).
+
+count_string(<<>>, N) ->
+ {N, <<>>};
+count_string(<<0/integer, Rest/binary>>, N) ->
+ {N, Rest};
+count_string(<<C/integer, Rest/binary>>, N) ->
+ count_string(Rest, N+1).
+
+to_string(Bin) when binary(Bin) ->
+ {Count, _} = count_string(Bin, 0),
+ <<String:Count/binary, _/binary>> = Bin,
+ {binary_to_list(String), Count}.
+
+oids(<<>>, Oids) ->
+ lists:reverse(Oids);
+oids(<<Oid:32/integer, Rest/binary>>, Oids) ->
+ oids(Rest, [Oid|Oids]).
+
+coldescs(<<>>, Descs) ->
+ lists:reverse(Descs);
+coldescs(Bin, Descs) ->
+ {Name, Count} = to_string(Bin),
+ <<_:Count/binary, 0/integer,
+ TableOID:32/integer,
+ ColumnNumber:16/integer,
+ TypeId:32/integer,
+ TypeSize:16/integer-signed,
+ TypeMod:32/integer-signed,
+ FormatCode:16/integer,
+ Rest/binary>> = Bin,
+ Format = case FormatCode of
+ 0 -> text;
+ 1 -> binary
+ end,
+ Desc = {Name, Format, ColumnNumber,
+ TypeId, TypeSize, TypeMod,
+ TableOID},
+ coldescs(Rest, [Desc|Descs]).
+
+datacoldescs(N,
+ <<Len:32/integer, Data:Len/binary, Rest/binary>>,
+ Descs) when N >= 0 ->
+ datacoldescs(N-1, Rest, [Data|Descs]);
+datacoldescs(N, _, Descs) ->
+ lists:reverse(Descs).
+
+decode_descs(Cols) ->
+ decode_descs(Cols, []).
+decode_descs([], Descs) ->
+ {ok, lists:reverse(Descs)};
+decode_descs([Col|ColTail], Descs) ->
+ OidMap = get(oidmap),
+ {Name, Format, ColNumber, Oid, _, _, _} = Col,
+ OidName = dict:fetch(Oid, OidMap),
+ decode_descs(ColTail, [{Name, Format, ColNumber, OidName, [], [], []}|Descs]).
+
+decode_row(Types, Values) ->
+ decode_row(Types, Values, []).
+decode_row([], [], Out) ->
+ {ok, lists:reverse(Out)};
+decode_row([Type|TypeTail], [Value|ValueTail], Out0) ->
+ Out1 = decode_col(Type, Value),
+ decode_row(TypeTail, ValueTail, [Out1|Out0]).
+
+decode_col({_, text, _, _, _, _, _}, Value) ->
+ binary_to_list(Value);
+decode_col({_Name, _Format, _ColNumber, varchar, _Size, _Modifier, _TableOID}, Value) ->
+ binary_to_list(Value);
+decode_col({_Name, _Format, _ColNumber, int4, _Size, _Modifier, _TableOID}, Value) ->
+ <<Int4:32/integer>> = Value,
+ Int4;
+decode_col({_Name, _Format, _ColNumber, Oid, _Size, _Modifier, _TableOID}, Value) ->
+ {Oid, Value}.
+
+errordesc(Bin) ->
+ errordesc(Bin, []).
+
+errordesc(<<0/integer, Rest/binary>>, Lines) ->
+ lists:reverse(Lines);
+errordesc(<<Code/integer, Rest/binary>>, Lines) ->
+ {String, Count} = to_string(Rest),
+ <<_:Count/binary, 0, Rest1/binary>> = Rest,
+ Msg = case Code of
+ $S ->
+ {severity, list_to_atom(String)};
+ $C ->
+ {code, String};
+ $M ->
+ {message, String};
+ $D ->
+ {detail, String};
+ $H ->
+ {hint, String};
+ $P ->
+ {position, list_to_integer(String)};
+ $p ->
+ {internal_position, list_to_integer(String)};
+ $W ->
+ {where, String};
+ $F ->
+ {file, String};
+ $L ->
+ {line, list_to_integer(String)};
+ $R ->
+ {routine, String};
+ Unknown ->
+ {Unknown, String}
+ end,
+ errordesc(Rest1, [Msg|Lines]).
+
+%%% Zip two lists together
+zip(List1, List2) ->
+ zip(List1, List2, []).
+zip(List1, List2, Result) when List1 =:= [];
+ List2 =:= [] ->
+ lists:reverse(Result);
+zip([H1|List1], [H2|List2], Result) ->
+ zip(List1, List2, [{H1, H2}|Result]).
View
944 src/lib/snmp_mgr.erl
@@ -0,0 +1,944 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(snmp_mgr).
+
+%%----------------------------------------------------------------------
+%% This module implements a simple SNMP manager for Erlang.
+%%----------------------------------------------------------------------
+
+%% c(snmp_mgr).
+%% snmp_mgr:start().
+%% snmp_mgr:g([[sysContact,0]]).
+
+%% snmp_mgr:start([{engine_id, "mbjk's engine"}, v3, {agent, "clip"}, {mibs, ["../mibs/SNMPv2-MIB"]}]).
+
+%% snmp_mgr:start([{engine_id, "agentEngine"}, {user, "iwl_test"}, {dir, "mgr_conf"}, {sec_level, authPriv}, v3, {agent, "clip"}]).
+
+%% User interface
+-export([start_link/1, start/1, stop/0,
+ d/0, g/1, s/1, gn/1, gn/0, r/0, gb/3, rpl/1,
+ send_bytes/1,
+ expect/2,expect/3,expect/4,expect/6,get_response/2,
+ receive_response/0,
+ oid_to_name/1, name_to_oid/1]).
+
+%% Internal exports
+-export([get_oid_from_varbind/1,
+ var_and_value_to_varbind/2, flatten_oid/2, make_vb/1]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
+
+-include_lib("snmp/include/snmp_types.hrl").
+-include_lib("snmp/src/misc/snmp_debug.hrl").
+-include_lib("snmp/include/STANDARD-MIB.hrl").
+
+-record(state,{dbg = true,
+ quiet,
+ parent,
+ timeout = 3500,
+ print_traps = true,
+ mini_mib,
+ packet_server,
+ last_sent_pdu,
+ last_received_pdu}).
+
+start_link(Options) ->
+ gen_server:start_link({local, snmp_mgr}, snmp_mgr, {Options, self()}, []).
+
+start(Options) ->
+ gen_server:start({local, snmp_mgr}, snmp_mgr, {Options, self()}, []).
+
+stop() ->
+ gen_server:call(snmp_mgr, stop, infinity).
+
+d() ->
+ gen_server:call(snmp_mgr,discovery,infinity).
+
+g(Oids) ->
+ snmp_mgr ! {get, Oids}, ok.
+
+%% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value}
+s(VarsAndValues) ->
+ snmp_mgr ! {set, VarsAndValues}, ok.
+
+gn(Oids) when list(Oids) ->
+ snmp_mgr ! {get_next, Oids}, ok;
+gn(N) when integer(N) ->
+ snmp_mgr ! {iter_get_next, N}, ok.
+gn() ->
+ snmp_mgr ! iter_get_next, ok.
+
+r() ->
+ snmp_mgr ! resend_pdu, ok.
+
+gb(NonRepeaters, MaxRepetitions, Oids) ->
+ snmp_mgr ! {bulk, {NonRepeaters, MaxRepetitions, Oids}}, ok.
+
+rpl(RespPdu) ->
+ snmp_mgr ! {response, RespPdu}.
+
+send_bytes(Bytes) ->
+ snmp_mgr ! {send_bytes, Bytes}, ok.
+
+oid_to_name(Oid) ->
+ gen_server:call(snmp_mgr, {oid_to_name, Oid}, infinity).
+
+name_to_oid(Name) ->
+ gen_server:call(snmp_mgr, {name_to_oid, Name}, infinity).
+
+
+%%----------------------------------------------------------------------
+%% Purpose: For writing test sequences
+%% Args: Y=any (varbinds) | trap | timeout | VarBinds | ErrStatus
+%% Returns: ok|{error, Id, Reason}
+%%----------------------------------------------------------------------
+expect(Id,Y) -> echo_errors(expect_impl(Id,Y)).
+expect(Id,v2trap,VBs) -> echo_errors(expect_impl(Id,v2trap,VBs));
+expect(Id,report,VBs) -> echo_errors(expect_impl(Id,report,VBs));
+expect(Id,{inform, Reply},VBs) ->
+ echo_errors(expect_impl(Id,{inform,Reply},VBs)).
+expect(Id,Err,Idx,VBs) -> echo_errors(expect_impl(Id,Err,Idx,VBs)).
+expect(Id,trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
+ echo_errors(expect_impl(Id,trap,Enterp,Generic,
+ Specific,ExpectedVarbinds)).
+
+%%-----------------------------------------------------------------
+%% Purpose: For writing test sequences
+%%-----------------------------------------------------------------
+get_response(Id, Vars) -> echo_errors(get_response_impl(Id, Vars)).
+
+%%----------------------------------------------------------------------
+%% Receives a response from the agent.
+%% Returns: a PDU or {error, Reason}.
+%% It doesn't receive traps though.
+%%----------------------------------------------------------------------
+receive_response() ->
+ receive_response(get_timeout()).
+
+receive_response(Timeout) ->
+ d("await reponse within ~w ms",[Timeout]),
+ receive
+ {snmp_pdu, PDU} when record(PDU, pdu) ->
+ d("received PDU: ~n\t~p",[PDU]),
+ PDU
+ after Timeout ->
+ d("response timeout",[]),
+ {error, timeout}
+ end.
+
+
+get_timeout() ->
+ get_timeout(os:type()).
+
+get_timeout(vxworks) -> 7000;
+get_timeout(_) -> 3500.
+
+%%----------------------------------------------------------------------
+%% Receives a trap from the agent.
+%% Returns: TrapPdu|{error, Reason}
+%%----------------------------------------------------------------------
+receive_trap(Timeout) ->
+ d("await trap within ~w ms",[Timeout]),
+ receive
+ {snmp_pdu, PDU} when record(PDU, trappdu) ->
+ d("received trap-PDU: ~n\t~p",[PDU]),
+ PDU
+ after Timeout ->
+ d("trap timeout",[]),
+ {error, timeout}
+ end.
+
+%%----------------------------------------------------------------------
+%% Options: List of
+%% {agent_udp, UDPPort}, {agent, Agent}
+%% Optional:
+%% {community, String ("public" is default}, quiet,
+%% {mibs, List of Filenames}, {trap_udp, UDPPort (default 5000)},
+%%----------------------------------------------------------------------
+init({Options, CallerPid}) ->
+ {A1,A2,A3} = erlang:now(),
+ random:seed(A1,A2,A3),
+ case (catch is_options_ok(Options)) of
+ true ->
+ put(debug,get_value(debug,Options,false)),
+ d("init -> (~p) extract options",[self()]),
+ PacksDbg = get_value(packet_server_debug,Options,false),
+ RecBufSz = get_value(recbuf,Options,1024),
+ Mibs = get_value(mibs, Options, []),
+ Udp = get_value(agent_udp, Options, 4000),
+ User = get_value(user, Options, "initial"),
+ EngineId = get_value(engine_id, Options, "agentEngine"),
+ CtxEngineId = get_value(context_engine_id, Options, EngineId),
+ TrapUdp = get_value(trap_udp, Options, 5000),
+ Dir = get_value(dir, Options, "."),
+ SecLevel = get_value(sec_level, Options, noAuthNoPriv),
+ MiniMIB = snmp_misc:make_mini_mib(Mibs),
+ Version = case lists:member(v2,Options) of
+ true -> 'version-2';
+ false ->
+ case lists:member(v3,Options) of
+ true -> 'version-3';
+ false -> 'version-1'
+ end
+ end,
+ Com = case Version of
+ 'version-3' ->
+ get_value(context, Options, "");
+ _ ->
+ get_value(community, Options, "public")
+ end,
+ VsnHdrD =
+ {Com, User, EngineId, CtxEngineId, mk_seclevel(SecLevel)},
+ AgIp = case snmp_misc:assq(agent, Options) of
+ {value, Tuple4} when tuple(Tuple4),size(Tuple4)==4 ->
+ Tuple4;
+ {value, Host} when list(Host) ->
+ {ok, Ip} = snmp_misc:ip(Host),
+ Ip
+ end,
+ Quiet = lists:member(quiet, Options),
+ PackServ = start_packet_server(Quiet, Options, CallerPid,
+ AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz,
+ PacksDbg),
+ d("init -> packet server: ~p",[PackServ]),
+ State = #state{parent = CallerPid,
+ quiet = Quiet,
+ mini_mib = MiniMIB,
+ packet_server = PackServ},
+ d("init -> done",[]),
+ {ok, State};
+
+ {error, Reason} ->
+ {stop,Reason}
+ end.
+
+start_packet_server(false, _Options, _CallerPid, AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz, PacksDbg) ->
+ d("start_packet_server -> entry", []),
+ snmp_mgr_misc:start_link_packet({msg, self()},
+ AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz,
+ PacksDbg);
+start_packet_server(true, Options, CallerPid, AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz, PacksDbg) ->
+ Type = get_value(receive_type, Options, pdu),
+ d("start_packet_server -> entry with"
+ "~n CallerPid: ~p"
+ "~n when"
+ "~n Type: ~p",[CallerPid, Type]),
+ snmp_mgr_misc:start_link_packet({Type, CallerPid},
+ AgIp, Udp, TrapUdp,
+ VsnHdrD, Version, Dir, RecBufSz,
+ PacksDbg).
+
+is_options_ok([{mibs,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([quiet|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{agent,_}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{agent_udp,Int}|Opts]) when integer(Int) ->
+ is_options_ok(Opts);
+is_options_ok([{trap_udp,Int}|Opts]) when integer(Int) ->
+ is_options_ok(Opts);
+is_options_ok([{community,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{dir,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{sec_level,noAuthNoPriv}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{sec_level,authNoPriv}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{sec_level,authPriv}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{context,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{user,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{engine_id,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([{context_engine_id,List}|Opts]) when list(List) ->
+ is_options_ok(Opts);
+is_options_ok([v1|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([v2|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([v3|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{debug,Bool}|Opts]) ->
+ case is_bool(Bool) of
+ ok ->
+ is_options_ok(Opts);
+ error ->
+ {error, {bad_option, debug, Bool}}
+ end;
+is_options_ok([{packet_server_debug,Bool}|Opts]) ->
+ case is_bool(Bool) of
+ ok ->
+ is_options_ok(Opts);
+ error ->
+ {error, {bad_option, packet_server_debug, Bool}}
+ end;
+is_options_ok([{recbuf,Sz}|Opts]) when 0 < Sz, Sz =< 65535 ->
+ is_options_ok(Opts);
+is_options_ok([{receive_type, msg}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([{receive_type, pdu}|Opts]) ->
+ is_options_ok(Opts);
+is_options_ok([InvOpt|_]) ->
+ {error,{invalid_option,InvOpt}};
+is_options_ok([]) -> true.
+
+is_bool(true) -> ok;
+is_bool(false) -> ok;
+is_bool(_) -> error.
+
+mk_seclevel(noAuthNoPriv) -> 0;
+mk_seclevel(authNoPriv) -> 1;
+mk_seclevel(authPriv) -> 3.
+
+
+handle_info({get, Oids}, State) ->
+ d("handle_info -> get request for ~p",[Oids]),
+ {noreply, execute_request(get, Oids, State)};
+
+handle_info({set, VariablesAndValues}, State) ->
+ d("handle_info -> set request for ~p",[VariablesAndValues]),
+ {noreply, execute_request(set, VariablesAndValues, State)};
+
+handle_info({bulk, Args}, State) ->
+ d("handle_info -> bulk request for ~p",[Args]),
+ {noreply, execute_request(bulk, Args, State)};
+
+handle_info({response, RespPdu}, State) ->
+ d("handle_info -> response request with ~p",[RespPdu]),
+ snmp_mgr_misc:send_pdu(RespPdu, State#state.packet_server),
+ {noreply, State};
+
+handle_info({snmp_msg, Msg, Ip, Udp}, State) ->
+ io:format("* Got PDU: ~s", [snmp_mgr_misc:format_hdr(Msg)]),
+ PDU = snmp_mgr_misc:get_pdu(Msg),
+ echo_pdu(PDU, State#state.mini_mib),
+ case PDU#pdu.type of
+ 'inform-request' ->
+ %% Generate a response...
+ RespPDU = PDU#pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0},
+ RespMsg = snmp_mgr_misc:set_pdu(Msg, RespPDU),
+ snmp_mgr_misc:send_msg(RespMsg, State#state.packet_server, Ip, Udp);
+ _Else ->
+ ok
+ end,
+ {noreply, State#state{last_received_pdu = PDU}};
+
+handle_info({get_next, Oids}, State) ->
+ d("handle_info -> get-next request for ~p",[Oids]),
+ {noreply, execute_request(get_next, Oids, State)};
+
+handle_info(resend_pdu, State) ->
+ PDU = State#state.last_sent_pdu,
+ d("handle_info -> resend_pdu request when last sent pdu: ~n\t~p",[PDU]),
+ send_pdu(PDU#pdu{request_id = make_request_id()},
+ State#state.mini_mib,
+ State#state.packet_server),
+ {noreply, State};
+
+handle_info(iter_get_next, State)
+ when record(State#state.last_received_pdu, pdu) ->
+ d("handle_info -> iter_get_next request",[]),
+ PrevPDU = State#state.last_received_pdu,
+ Oids = snmp_misc:map({snmp_mgr, get_oid_from_varbind}, [],
+ PrevPDU#pdu.varbinds),
+ {noreply, execute_request(get_next, Oids, State)};
+
+handle_info(iter_get_next, State) ->
+ snmp_mgr_misc:error("[Iterated get-next] No Response PDU to "
+ "start iterating from.", []),
+ {noreply, State};
+
+handle_info({iter_get_next, N}, State) ->
+ d("handle_info -> iter_get_next(~p) request",[N]),
+ if
+ record(State#state.last_received_pdu, pdu) ->
+ PDU = get_next_iter_impl(N, State#state.last_received_pdu,
+ State#state.mini_mib,
+ State#state.packet_server),
+ {noreply, State#state{last_received_pdu = PDU}};
+ true ->
+ snmp_mgr_misc:error("[Iterated get-next] No Response PDU to "
+ "start iterating from.", []),
+ {noreply, State}
+ end;
+
+handle_info({send_bytes, Bytes}, State) ->
+ d("handle_info -> send-bytes request for ~p bytes",
+ [sizeOf(Bytes)]),
+ snmp_mgr_misc:send_bytes(Bytes, State#state.packet_server),
+ {noreply, State}.
+
+
+handle_call({find_pure_oid, XOid}, _From, State) ->
+ d("handle_call -> find_pure_oid for ~p",[XOid]),
+ {reply, catch flatten_oid(XOid, State#state.mini_mib), State};
+
+handle_call({oid_to_name, Oid}, _From, State) ->
+ d("handle_call -> oid_to_name for Oid: ~p",[Oid]),
+ Reply =
+ case lists:keysearch(Oid, 1, State#state.mini_mib) of
+ {value, {_Oid, Name, _Type}} ->
+ {ok, Name};
+ false ->
+ {error, {no_such_oid, Oid}}
+ end,
+ {reply, Reply, State};
+
+handle_call({name_to_oid, Name}, _From, State) ->
+ d("handle_call -> name_to_oid for Name: ~p",[Name]),
+ Reply =
+ case lists:keysearch(Name, 2, State#state.mini_mib) of
+ {value, {Oid, _Name, _Type}} ->
+ {ok, Oid};
+ false ->
+ {error, {no_such_name, Name}}
+ end,
+ {reply, Reply, State};
+
+handle_call(stop, _From, State) ->
+ d("handle_call -> stop request",[]),
+ {stop, normal, ok, State};
+
+handle_call(discovery, _From, State) ->
+ d("handle_call -> discovery",[]),
+ {Reply, NewState} = execute_discovery(State),
+ {reply, Reply, NewState}.
+
+handle_cast(Msg, State) ->
+ d("handle_cast -> unknown message: ~n\t~p",[Msg]),
+ {noreply, State}.
+
+terminate(Reason, State) ->
+ d("terminate -> with Reason: ~n\t~p",[Reason]),
+ snmp_mgr_misc:stop(State#state.packet_server).
+
+
+%%----------------------------------------------------------------------
+%% Returns: A new State
+%%----------------------------------------------------------------------
+execute_discovery(State) ->
+ Pdu = make_discovery_pdu(),
+ Reply = snmp_mgr_misc:send_discovery_pdu(Pdu,State#state.packet_server),
+ {Reply,State#state{last_sent_pdu = Pdu}}.
+
+
+execute_request(Operation, Data, State) ->
+ case (catch make_pdu(Operation, Data, State#state.mini_mib)) of
+ {error, {Format, Data2}} ->
+ report_error(State, Format, Data2),
+ State;
+ {error, _Reason} ->
+ State;
+ PDU when record(PDU, pdu) ->
+ send_pdu(PDU, State#state.mini_mib, State#state.packet_server),
+ State#state{last_sent_pdu = PDU}
+ end.
+
+report_error(#state{quiet = true, parent = Pid}, Format, Args) ->
+ Reason = lists:flatten(io_lib:format(Format, Args)),
+ Pid ! {oid_error, Reason};
+report_error(_, Format, Args) ->
+ snmp_mgr_misc:error(Format, Args).
+
+
+get_oid_from_varbind(#varbind{oid = Oid}) -> Oid.
+
+send_pdu(PDU, _MiniMIB, PackServ) ->
+ snmp_mgr_misc:send_pdu(PDU, PackServ).
+
+%%----------------------------------------------------------------------
+%% Purpose: Unnesting of oids like [myTable, 3, 4, "hej", 45] to
+%% [1,2,3,3,4,104,101,106,45]
+%%----------------------------------------------------------------------
+flatten_oid(XOid, DB) ->
+ Oid2 = case XOid of
+ [A|T] when atom(A) -> [remove_atom(A, DB)|T];
+ L when list(L) -> XOid;
+ Shit ->
+ throw({error,
+ {"Invalid oid, not a list of integers: ~w", [Shit]}})
+ end,
+ check_is_pure_oid(lists:flatten(Oid2)).
+
+remove_atom(AliasName, DB) when atom(AliasName) ->
+ case snmp_misc:oid(DB, AliasName) of
+ false ->
+ throw({error, {"Unknown aliasname in oid: ~w", [AliasName]}});
+ Oid -> Oid
+ end;
+remove_atom(X, _DB) -> X.
+
+%%----------------------------------------------------------------------
+%% Throws if not a list of integers
+%%----------------------------------------------------------------------
+check_is_pure_oid([]) -> [];
+check_is_pure_oid([X | T]) when integer(X), X >= 0 ->
+ [X | check_is_pure_oid(T)];
+check_is_pure_oid([X | _T]) ->
+ throw({error, {"Invalid oid, it contains a non-integer: ~w", [X]}}).
+
+get_next_iter_impl(0, PrevPDU, _MiniMIB, _PackServ) -> PrevPDU;
+get_next_iter_impl(N, PrevPDU, MiniMIB, PackServ) ->
+ Oids = snmp_misc:map({snmp_mgr, get_oid_from_varbind}, [],
+ PrevPDU#pdu.varbinds),
+ PDU = make_pdu(get_next, Oids, MiniMIB),
+ send_pdu(PDU, MiniMIB, PackServ),
+ case receive_response() of
+ {error, timeout} ->
+ io:format("(timeout)~n"),
+ get_next_iter_impl(N, PrevPDU, MiniMIB, PackServ);
+ {error, _Reason} ->
+ PrevPDU;
+ RPDU when record(RPDU, pdu) ->
+ io:format("(~w)", [N]),
+ echo_pdu(RPDU, MiniMIB),
+ get_next_iter_impl(N-1, RPDU, MiniMIB, PackServ)
+ end.
+
+%%--------------------------------------------------
+%% Used to resend a PDU. Takes the old PDU and
+%% generates a fresh one (with a new requestID).
+%%--------------------------------------------------
+
+make_pdu(set, VarsAndValues, MiniMIB) ->
+ VBs = snmp_misc:map({snmp_mgr, var_and_value_to_varbind}, [MiniMIB],
+ VarsAndValues),
+ make_pdu_impl(set, VBs);
+make_pdu(bulk, {NonRepeaters, MaxRepetitions, Oids}, MiniMIB) ->
+ Foids = snmp_misc:map({snmp_mgr, flatten_oid}, [MiniMIB], Oids),
+ #pdu{type = 'get-bulk-request',request_id = make_request_id(),
+ error_status = NonRepeaters, error_index = MaxRepetitions,
+ varbinds = snmp_misc:map({snmp_mgr, make_vb}, [], Foids)};
+make_pdu(Operation, Oids, MiniMIB) ->
+ make_pdu_impl(Operation,
+ snmp_misc:map({snmp_mgr, flatten_oid}, [MiniMIB], Oids)).
+
+make_pdu_impl(get, Oids) ->
+ #pdu{type = 'get-request',request_id = make_request_id(),
+ error_status = noError, error_index = 0,
+ varbinds = snmp_misc:map({snmp_mgr, make_vb}, [], Oids)};
+
+make_pdu_impl(get_next, Oids) ->
+ #pdu{type = 'get-next-request', request_id = make_request_id(),
+ error_status = noError, error_index = 0,
+ varbinds = snmp_misc:map({snmp_mgr, make_vb}, [], Oids)};
+
+make_pdu_impl(set, Varbinds) ->
+ #pdu{type = 'set-request', request_id = make_request_id(),
+ error_status = noError, error_index = 0, varbinds = Varbinds}.
+
+make_discovery_pdu() ->
+ #pdu{type = 'get-request',request_id = make_request_id(),
+ error_status = noError, error_index = 0,
+ varbinds = snmp_misc:map({snmp_mgr, make_vb}, [],
+ [?sysDescr_instance])}.
+
+var_and_value_to_varbind({Oid, Type, Value}, MiniMIB) ->
+ Oid2 = flatten_oid(Oid, MiniMIB),
+ #varbind{oid = Oid2, variabletype = char_to_type(Type), value = Value};
+var_and_value_to_varbind({XOid, Value}, MiniMIB) ->
+ Oid = flatten_oid(XOid, MiniMIB),
+ #varbind{oid = Oid, variabletype = snmp_misc:type(MiniMIB, Oid),
+ value = Value}.
+
+char_to_type(o) ->
+ 'OBJECT IDENTIFIER';
+char_to_type(i) ->
+ 'INTEGER';
+char_to_type(u) ->
+ 'Unsigned32';
+char_to_type(g) -> % Gauge, Gauge32
+ 'Unsigned32';
+char_to_type(s) ->
+ 'OCTET STRING'.
+
+make_vb(Oid) ->
+ #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
+
+make_request_id() ->
+ random:uniform(16#FFFFFFF-1).
+
+echo_pdu(PDU,MiniMIB) ->
+ io:format("~s",[snmp_misc:format_pdu(PDU,MiniMIB)]).
+
+%%----------------------------------------------------------------------
+%% Test Sequence
+%%----------------------------------------------------------------------
+echo_errors({error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}})->
+ io:format("* Unexpected Behaviour * Id: ~w.~n"
+ " Expected: " ++ ExpectedFormat ++ "~n"
+ " Got: " ++ Format ++ "~n",
+ [Id] ++ ExpectedData ++ Data),
+ {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}};
+echo_errors(ok) -> ok;
+echo_errors({ok, Val}) -> {ok, Val}.
+
+get_response_impl(Id, Vars) ->
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ match_vars(Id, find_pure_oids2(Vars), VBs, []);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['get-response', noError, 0, ReqId]},
+ {"Type: ~w ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+
+
+%%----------------------------------------------------------------------
+%% Returns: ok | {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}}
+%%----------------------------------------------------------------------
+expect_impl(Id, any) ->
+ case receive_response() of
+ PDU when record(PDU, pdu) -> ok;
+ {error, Reason} -> format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, return) ->
+ case receive_response() of
+ PDU when record(PDU, pdu) -> {ok, PDU};
+ {error, Reason} -> format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, trap) ->
+ case receive_trap(3500) of
+ PDU when record(PDU, trappdu) -> ok;
+ {error, Reason} -> format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, timeout) ->
+ receive
+ X -> {error, Id, {"Timeout", []}, {"Message ~w", [X]}}
+ after 3500 ->
+ ok
+ end;
+
+expect_impl(Id, Err) when atom(Err) ->
+ case receive_response() of
+ #pdu{error_status = Err} ->
+ ok;
+
+ #pdu{request_id = ReqId,
+ error_status = OtherErr} ->
+ {error, Id, {"ErrorStatus: ~w, RequestId: ~w", [Err,ReqId]},
+ {"ErrorStatus: ~w", [OtherErr]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, ExpectedVarbinds) when list(ExpectedVarbinds) ->
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['get-response', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+expect_impl(Id, v2trap, ExpectedVarbinds) when list(ExpectedVarbinds) ->
+ case receive_response() of
+ #pdu{type = 'snmpv2-trap',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['snmpv2-trap', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, report, ExpectedVarbinds) when list(ExpectedVarbinds) ->
+ case receive_response() of
+ #pdu{type = 'report',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ [report, noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, {inform, Reply}, ExpectedVarbinds) when
+ list(ExpectedVarbinds) ->
+ Resp = receive_response(),
+ case Resp of
+ #pdu{type = 'inform-request',
+ error_status = noError,
+ error_index = 0,
+ varbinds = VBs} ->
+ case check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs) of
+ ok when Reply == true ->
+ RespPDU = Resp#pdu{type = 'get-response',
+ error_status = noError,
+ error_index = 0},
+ snmp_mgr:rpl(RespPDU),
+ ok;
+ ok when element(1, Reply) == error ->
+ {error, Status, Index} = Reply,
+ RespPDU = Resp#pdu{type = 'get-response',
+ error_status = Status,
+ error_index = Index},
+ snmp_mgr:rpl(RespPDU),
+ ok;
+ ok when Reply == false ->
+ ok;
+ Else ->
+ Else
+ end;
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['inform-request', noError, 0, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+expect_impl(Id, Err, Index, any) ->
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = Err,
+ error_index = Index} ->
+ ok;
+
+ #pdu{type = 'get-response', error_status = Err} when Index == any ->
+ ok;
+
+ #pdu{type = 'get-response',
+ request_id = ReqId,
+ error_status = Err,
+ error_index = Idx} when list(Index) ->
+ case lists:member(Idx, Index) of
+ true ->
+ ok;
+ false ->
+ {error, Id, {"ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ [Err, Index, ReqId]},
+ {"ErrStat: ~w, Idx: ~w", [Err, Idx]}}
+ end;
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2} ->
+ {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
+ ['get-response', Err, Index, ReqId]},
+ {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end;
+
+expect_impl(Id, Err, Index, ExpectedVarbinds) ->
+ PureVBs = find_pure_oids(ExpectedVarbinds),
+ case receive_response() of
+ #pdu{type = 'get-response',
+ error_status = Err,
+ error_index = Index,
+ varbinds = VBs} ->
+ check_vars(Id, PureVBs, VBs);
+
+ #pdu{type = 'get-response',
+ error_status = Err,
+ varbinds = VBs} when Index == any ->
+ check_vars(Id, PureVBs, VBs);
+
+ #pdu{type = 'get-response',
+ request_id = ReqId,
+ error_status = Err,
+ error_index = Idx,
+ varbinds = VBs} when list(Index) ->
+ case lists:member(Idx, Index) of
+ true ->
+ check_vars(Id, PureVBs, VBs);
+ false ->
+ {error,Id,
+ {"ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
+ [Err,Index,PureVBs,ReqId]},
+ {"ErrStat: ~w, Idx: ~w, Varbinds: ~w",
+ [Err,Idx,VBs]}}
+ end;
+
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2,
+ error_index = Index2,
+ varbinds = VBs} ->
+ {error,Id,
+ {"Type: ~w, ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
+ ['get-response',Err,Index,PureVBs,ReqId]},
+ {"Type: ~w, ErrStat: ~w Idx: ~w Varbinds: ~w",
+ [Type2,Err2,Index2,VBs]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
+ PureE = find_pure_oid(Enterp),
+ case receive_trap(3500) of
+ #trappdu{enterprise = PureE,
+ generic_trap = Generic,
+ specific_trap = Specific,
+ varbinds = VBs} ->
+ check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+
+ #trappdu{enterprise = Ent2,
+ generic_trap = G2,
+ specific_trap = Spec2,
+ varbinds = VBs} ->
+ {error, Id,
+ {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
+ [PureE, Generic, Specific, ExpectedVarbinds]},
+ {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
+ [Ent2, G2, Spec2, VBs]}};
+
+ {error, Reason} ->
+ format_reason(Id, Reason)
+ end.
+
+format_reason(Id, Reason) ->
+ {error, Id, {"?", []}, {"~w", [Reason]}}.
+
+%%----------------------------------------------------------------------
+%% Args: Id, ExpectedVarbinds, GotVarbinds
+%% Returns: ok
+%% Fails: if not ok
+%%----------------------------------------------------------------------
+check_vars(_Id,[], []) ->
+ ok;
+check_vars(Id,Vars, []) ->
+ {error, Id, {"More Varbinds (~w)", [Vars]}, {"Too few", []}};
+check_vars(Id,[], Varbinds) ->
+ {error,Id, {"Fewer Varbinds", []}, {"Too many (~w)", [Varbinds]}};
+check_vars(Id,[{_XOid, any} | Vars], [#varbind{oid = _Oid} |Vbs]) ->
+ check_vars(Id,Vars, Vbs);
+check_vars(Id,[{Oid, Val} | Vars], [#varbind{oid = Oid, value = Val} |Vbs]) ->
+ check_vars(Id,Vars, Vbs);
+check_vars(Id,[{Oid, Val} | _], [#varbind{oid = Oid, value = Val2} |_]) ->
+ {error, Id, {" Varbind: ~w = ~w", [Oid, Val]}, {"Value: ~w", [Val2]}};
+check_vars(Id,[{Oid, _Val} | _], [#varbind{oid = Oid2, value = _Val2} |_]) ->
+ {error, Id, {"Oid: ~w", [Oid]}, {"Oid: ~w", [Oid2]}}.
+
+match_vars(Id, [Oid|T], [#varbind{oid = Oid, value = Value} | Vbs], Res) ->
+ match_vars(Id, T, Vbs, [Value | Res]);
+match_vars(_Id, [], [], Res) ->
+ {ok, lists:reverse(Res)};
+match_vars(Id, [Oid | _], [#varbind{oid = Oid2}], _Res) ->
+ {error, Id, {" Oid: ~w", [Oid]}, {"Oid2: ~w", [Oid2]}};
+match_vars(Id, Vars, [], _Res) ->
+ {error, Id, {"More Varbinds (~w)", [Vars]}, {"Too few", []}};
+match_vars(Id, [], Varbinds, _Res) ->
+ {error,Id, {"Fewer Varbinds", []}, {"Too many (~w)", [Varbinds]}}.
+
+
+
+find_pure_oids([]) -> [];
+find_pure_oids([{XOid, Q}|T]) ->
+ [{find_pure_oid(XOid), Q} | find_pure_oids(T)].
+
+find_pure_oids2([]) -> [];
+find_pure_oids2([XOid|T]) ->
+ [find_pure_oid(XOid) | find_pure_oids2(T)].
+
+%%----------------------------------------------------------------------
+%% Returns: Oid
+%% Fails: malformed oids
+%%----------------------------------------------------------------------
+find_pure_oid(XOid) ->
+ case gen_server:call(snmp_mgr, {find_pure_oid, XOid}, infinity) of
+ {error, {Format, Data}} ->
+ ok = io:format(Format, Data),
+ exit(malformed_oid);
+ Oid when list(Oid) -> Oid
+ end.
+
+get_value(Opt, Opts, Default) ->
+ case snmp_misc:assq(Opt,Opts) of
+ {value, C} -> C;
+ false -> Default
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Debug
+%%----------------------------------------------------------------------
+
+sizeOf(L) when list(L) ->
+ length(lists:flatten(L));
+sizeOf(B) when binary(B) ->
+ size(B).
+
+d(F,A) -> d(get(debug),F,A).
+
+d(true,F,A) ->
+ io:format("MGR_DBG:" ++ F ++ "~n",A);
+d(_,_F,_A) ->
+ ok.
View
713 src/lib/snmp_mgr_misc.erl
@@ -0,0 +1,713 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(snmp_mgr_misc).
+%% c(snmp_mgr_misc).
+
+%% API
+-export([start_link_packet/8, start_link_packet/9,
+ stop/1,
+ send_discovery_pdu/2, send_pdu/2, send_msg/4, send_bytes/2,
+ error/2,
+ get_pdu/1, set_pdu/2, format_hdr/1]).
+
+%% internal exports
+-export([init_packet/10]).
+
+-define(SNMP_USE_V3, true).
+-include_lib("snmp/include/snmp_types.hrl").
+-include_lib("snmp/src/misc/snmp_debug.hrl").
+
+-record(mini_mib_elem,{aliasname, oid, type}).
+
+%%----------------------------------------------------------------------
+%% The InHandler process will receive messages on the form {snmp_pdu, Pdu}.
+%%----------------------------------------------------------------------
+start_link_packet(InHandler,
+ AgentIp, UdpPort, TrapUdp,
+ VsnHdr, Version, Dir, BufSz) ->
+ start_link_packet(InHandler,
+ AgentIp, UdpPort, TrapUdp,
+ VsnHdr, Version, Dir, BufSz,
+ false).
+
+start_link_packet(InHandler,
+ AgentIp, UdpPort, TrapUdp,
+ VsnHdr, Version, Dir, BufSz,
+ Dbg) when integer(UdpPort) ->
+ Args = [self(), InHandler,
+ AgentIp, UdpPort, TrapUdp,
+ VsnHdr, Version, Dir, BufSz,
+ Dbg],
+ proc_lib:start_link(snmp_mgr_misc, init_packet, Args).
+
+stop(Pid) ->
+ Pid ! {stop, self()},
+ receive
+ {Pid, stopped} -> ok
+ end.
+
+
+send_discovery_pdu(Pdu, PacketPid) when record(Pdu, pdu) ->
+ PacketPid ! {send_discovery_pdu, self(), Pdu},
+ await_discovery_response_pdu().
+
+await_discovery_response_pdu() ->
+ receive
+ {discovery_response,Reply} ->
+ Reply;
+ _ ->
+ await_discovery_response_pdu()
+ end.
+
+
+send_pdu(Pdu, PacketPid) when record(Pdu, pdu) ->
+ PacketPid ! {send_pdu, Pdu}.
+
+send_msg(Msg, PacketPid, Ip, Udp) when record(Msg, message) ->
+ PacketPid ! {send_msg, Msg, Ip, Udp}.
+
+send_bytes(Bytes, PacketPid) ->
+ PacketPid ! {send_bytes, Bytes}.
+
+%%--------------------------------------------------
+%% The SNMP encode/decode process
+%%--------------------------------------------------
+init_packet(Parent, SnmpMgr,
+ AgentIp, UdpPort, TrapUdp,
+ VsnHdr, Version, Dir, BufSz, DbgOptions) ->
+ put(sname,mgr_misc),
+ init_debug(DbgOptions),
+ {ok, UdpId} = gen_udp:open(TrapUdp, [{recbuf,BufSz},{reuseaddr, true}]),
+ put(msg_id, 1),
+ proc_lib:init_ack(Parent, self()),
+ init_usm(Version, Dir),
+ packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []).
+
+init_debug(Dbg) when atom(Dbg) ->
+ put(debug,Dbg),
+ put(verbosity,silence);
+init_debug(DbgOptions) when list(DbgOptions) ->
+ case lists:keysearch(debug, 1, DbgOptions) of
+ {value, {_, Dbg}} when atom(Dbg) ->
+ put(debug,Dbg);
+ _ ->
+ put(debug, false)
+ end,
+ case lists:keysearch(verbosity, 1, DbgOptions) of
+ {value, {_, Ver}} when atom(ver) ->
+ put(verbosity,Ver);
+ _ ->
+ put(verbosity, silence)
+ end,
+ ok.
+
+
+packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) ->
+ receive
+ {send_discovery_pdu, From, Pdu} ->
+ d("packet_loop -> received send_discovery_pdu with"
+ "~n From: ~p"
+ "~n Pdu: ~p", [From, Pdu]),
+ case mk_discovery_msg(Version, Pdu, VsnHdr, "") of
+ error ->
+ ok;
+ {M, B} when list(B) ->
+ put(discovery,{M,From}),
+ display_outgoing_message(M),
+ udp_send(UdpId, AgentIp, UdpPort, B)
+ end,
+ packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
+ {send_pdu, Pdu} ->
+ d("packet_loop -> received send_pdu with"
+ "~n Pdu: ~p", [Pdu]),
+ case mk_msg(Version, Pdu, VsnHdr, MsgData) of
+ error ->
+ ok;
+ B when list(B) ->
+ udp_send(UdpId, AgentIp, UdpPort, B)
+ end,
+ packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
+ {send_msg, Msg, Ip, Udp} ->
+ d("packet_loop -> received send_msg with"
+ "~n Msg: ~p"
+ "~n Ip: ~p"
+ "~n Udp: ~p", [Msg,Ip,Udp]),
+ case catch snmp_pdus:enc_message(Msg) of
+ {'EXIT', Reason} ->
+ error("Encoding error. Msg: ~w. Reason: ~w",[Msg,Reason]);
+ B when list(B) ->
+ udp_send(UdpId, Ip, Udp, B)
+ end,
+ packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
+ {udp, UdpId, Ip, UdpPort, Bytes} ->
+ d("packet_loop -> received udp with"
+ "~n UdpId: ~p"
+ "~n Ip: ~p"
+ "~n UdpPort: ~p"
+ "~n sz(Bytes): ~p", [UdpId, Ip, UdpPort, sz(Bytes)]),
+ MsgData3 = handle_udp_packet(Version,erase(discovery),
+ UdpId, Ip, UdpPort, Bytes,
+ SnmpMgr, AgentIp),
+ packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,
+ MsgData3);
+ {send_bytes, B} ->
+ d("packet_loop -> received send_bytes with"
+ "~n sz(B): ~p", [sz(B)]),
+ udp_send(UdpId, AgentIp, UdpPort, B),
+ packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]);
+ {stop, Pid} ->
+ d("packet_loop -> received stop from ~p", [Pid]),
+ gen_udp:close(UdpId),
+ Pid ! {self(), stopped},
+ exit(normal);
+ Other ->
+ d("packet_loop -> received unknown"
+ "~n ~p", [Other]),
+ exit({snmp_packet_got_other, Other})
+ end.
+
+
+handle_udp_packet(_V,undefined,UdpId,Ip,UdpPort,Bytes,SnmpMgr,AgentIp) ->
+ M = (catch snmp_pdus:dec_message_only(Bytes)),
+ MsgData3 =
+ case M of
+ Message when Message#message.version == 'version-3' ->
+ d("handle_udp_packet -> version 3"),
+ case catch handle_v3_msg(Bytes, Message) of
+ {ok, NewData, MsgData2} ->
+ Msg = Message#message{data = NewData},
+ case SnmpMgr of
+ {pdu, Pid} ->
+ Pid ! {snmp_pdu, get_pdu(Msg)};
+ {msg, Pid} ->
+ Pid ! {snmp_msg, Msg, Ip, UdpPort}
+ end,
+ MsgData2;
+ {error, Reason, B} ->
+ udp_send(UdpId, AgentIp, UdpPort, B),
+ error("Decoding error. Auto-sending Report.\n"
+ "Reason: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Reason, UdpPort, Ip]),
+ [];
+ {error, Reason} ->
+ error("Decoding error. "
+ "Bytes: ~w ~n Reason: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Bytes, Reason, UdpPort, Ip]),
+ []
+ end;
+ Message when record(Message, message) ->
+ %% v1 or v2c
+ d("handle_udp_packet -> version 1 or v2c"),
+ case catch snmp_pdus:dec_pdu(Message#message.data) of
+ Pdu when record(Pdu, pdu) ->
+ case SnmpMgr of
+ {pdu, Pid} ->
+ Pid ! {snmp_pdu, Pdu};
+ {msg, Pid} ->
+ Msg = Message#message{data = Pdu},
+ Pid ! {snmp_msg, Msg, Ip, UdpPort}
+ end;
+ Pdu when record(Pdu, trappdu) ->
+ case SnmpMgr of
+ {pdu, Pid} ->
+ Pid ! {snmp_pdu, Pdu};
+ {msg, Pid} ->
+ Msg = Message#message{data = Pdu},
+ Pid ! {snmp_msg, Msg, Ip, UdpPort}
+ end;
+ Reason ->
+ error("Decoding error. "
+ "Bytes: ~w ~n Reason: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Bytes, Reason, UdpPort, Ip])
+ end,
+ [];
+ Reason ->
+ error("Decoding error. Bytes: ~w ~n Reason: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Bytes, Reason, UdpPort, Ip]),
+ []
+ end,
+ MsgData3;
+handle_udp_packet(V,{DiscoReqMsg,From},_UdpId,_Ip,_UdpPort,Bytes,_,_AgentIp) ->
+ DiscoRspMsg = (catch snmp_pdus:dec_message(Bytes)),
+ display_incomming_message(DiscoRspMsg),
+ Reply = (catch check_discovery_result(V,DiscoReqMsg,DiscoRspMsg)),
+ From ! {discovery_response,Reply},
+ [].
+
+%% This function assumes that the agent and the manager (thats us)
+%% has the same version.
+check_discovery_result('version-3',DiscoReqMsg,DiscoRspMsg) ->
+ ReqMsgID = getMsgID(DiscoReqMsg),
+ RspMsgID = getMsgID(DiscoRspMsg),
+ check_msgID(ReqMsgID,RspMsgID),
+ ReqRequestId = getRequestId('version-3',DiscoReqMsg),
+ RspRequestId = getRequestId('version-3',DiscoRspMsg),
+ check_requestId(ReqRequestId,RspRequestId),
+ {ok,getMsgAuthEngineID(DiscoRspMsg)};
+check_discovery_result(Version,DiscoReqMsg,DiscoRspMsg) ->
+ ReqRequestId = getRequestId(Version,DiscoReqMsg),
+ RspRequestId = getRequestId(Version,DiscoRspMsg),
+ check_requestId(ReqRequestId,RspRequestId),
+ {ok,getSysDescr(DiscoRspMsg)}.
+
+check_msgID(ID,ID) ->
+ ok;
+check_msgID(ReqMsgID,RspMsgID) ->
+ throw({error,{invalid_msgID,ReqMsgID,RspMsgID}}).
+
+check_requestId(Id,Id) ->
+ ok;
+check_requestId(ReqRequestId,RspRequestId) ->
+ throw({error,{invalid_requestId,ReqRequestId,RspRequestId}}).
+
+getMsgID(M) when record(M,message) ->
+ (M#message.vsn_hdr)#v3_hdr.msgID.
+
+getRequestId('version-3',M) when record(M,message) ->
+ ((M#message.data)#scopedPdu.data)#pdu.request_id;
+getRequestId(_Version,M) when record(M,message) ->
+ (M#message.data)#pdu.request_id;
+getRequestId(Version,M) ->
+ io:format("************* ERROR ****************"
+ "~n Version: ~w"
+ "~n M: ~w~n", [Version,M]),
+ throw({error, {unknown_request_id, Version, M}}).
+
+getMsgAuthEngineID(M) when record(M,message) ->
+ SecParams1 = (M#message.vsn_hdr)#v3_hdr.msgSecurityParameters,
+ SecParams2 = snmp_pdus:dec_usm_security_parameters(SecParams1),
+ SecParams2#usmSecurityParameters.msgAuthoritativeEngineID.
+
+getSysDescr(M) when record(M,message) ->
+ getSysDescr((M#message.data)#pdu.varbinds);
+getSysDescr([]) ->
+ not_found;
+getSysDescr([#varbind{oid = [1,3,6,1,2,1,1,1], value = Value}|_]) ->
+ Value;
+getSysDescr([#varbind{oid = [1,3,6,1,2,1,1,1,0], value = Value}|_]) ->
+ Value;
+getSysDescr([_|T]) ->
+ getSysDescr(T).
+
+handle_v3_msg(Packet, #message{vsn_hdr = V3Hdr, data = Data}) ->
+ %% Code copied from snmp_mpd.erl
+ #v3_hdr{msgID = MsgId, msgFlags = MsgFlags,
+ msgSecurityModel = MsgSecurityModel,
+ msgSecurityParameters = SecParams} = V3Hdr,
+