Date: Tue, 6 Sep 2011 11:10:43 +0200
Subject: Allow an infinite timeout to shutdown worker processes
Now, in child specification, the shutdown value can also be set to infinity
for worker children. This restriction was removed because this is not always
possible to predict the shutdown time for a worker. This is highly
application-dependent.
---
lib/stdlib/doc/src/supervisor.xml | 3 ++-
lib/stdlib/src/supervisor.erl | 2 +-
lib/stdlib/test/supervisor_SUITE.erl | 26 +++++++++++++-------------
3 files changed, 16 insertions(+), 15 deletions(-)
(limited to 'lib')
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index ec607d6e4c..b4baf2f0a0 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -169,7 +169,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
exit(Child,kill).
If the child process is another supervisor, Shutdown
should be set to infinity to give the subtree ample
- time to shutdown.
+ time to shutdown. It is also allowed to set it to infinity,
+ if the child process is a worker.
Important note on simple-one-for-one supervisors:
The dynamically created child processes of a
simple-one-for-one supervisor are not explicitly killed,
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 9da0d52f8c..051dca462b 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1053,7 +1053,7 @@ validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}).
validShutdown(Shutdown, _)
when is_integer(Shutdown), Shutdown > 0 -> true;
-validShutdown(infinity, supervisor) -> true;
+validShutdown(infinity, _) -> true;
validShutdown(brutal_kill, _) -> true;
validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}).
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 2aa3131aeb..9d47233422 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -209,8 +209,8 @@ sup_start_fail(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
sup_stop_infinity(doc) ->
- ["See sup_stop/1 when Shutdown = infinity, this walue is only allowed "
- "for children of type supervisor"];
+ ["See sup_stop/1 when Shutdown = infinity, this walue is allowed "
+ "for children of type supervisor _AND_ worker"];
sup_stop_infinity(suite) -> [];
sup_stop_infinity(Config) when is_list(Config) ->
@@ -221,12 +221,13 @@ sup_stop_infinity(Config) when is_list(Config) ->
Child2 = {child2, {supervisor_1, start_child, []}, permanent,
infinity, worker, []},
{ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ {ok, CPid2} = supervisor:start_child(sup_test, Child2),
link(CPid1),
- {error, {invalid_shutdown,infinity}} =
- supervisor:start_child(sup_test, Child2),
+ link(CPid2),
terminate(Pid, shutdown),
- check_exit_reason(CPid1, shutdown).
+ check_exit_reason(CPid1, shutdown),
+ check_exit_reason(CPid2, shutdown).
%%-------------------------------------------------------------------------
@@ -458,9 +459,8 @@ child_specs(Config) when is_list(Config) ->
B2 = {child, {m,f,[a]}, prmanent, 1000, worker, []},
B3 = {child, {m,f,[a]}, permanent, -10, worker, []},
B4 = {child, {m,f,[a]}, permanent, 10, wrker, []},
- B5 = {child, {m,f,[a]}, permanent, infinity, worker, []},
- B6 = {child, {m,f,[a]}, permanent, 1000, worker, dy},
- B7 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]},
+ B5 = {child, {m,f,[a]}, permanent, 1000, worker, dy},
+ B6 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]},
%% Correct child specs!
%% (last parameter in a child spec) can be [] as we do
@@ -469,6 +469,7 @@ child_specs(Config) when is_list(Config) ->
C2 = {child, {m,f,[a]}, permanent, 1000, supervisor, []},
C3 = {child, {m,f,[a]}, temporary, 1000, worker, dynamic},
C4 = {child, {m,f,[a]}, transient, 1000, worker, [m]},
+ C5 = {child, {m,f,[a]}, permanent, infinity, worker, [m]},
{error, {invalid_mfa,mfa}} = supervisor:start_child(sup_test, B1),
{error, {invalid_restart_type, prmanent}} =
@@ -477,9 +478,8 @@ child_specs(Config) when is_list(Config) ->
= supervisor:start_child(sup_test, B3),
{error, {invalid_child_type,wrker}}
= supervisor:start_child(sup_test, B4),
- {error, _} = supervisor:start_child(sup_test, B5),
{error, {invalid_modules,dy}}
- = supervisor:start_child(sup_test, B6),
+ = supervisor:start_child(sup_test, B5),
{error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]),
{error, {invalid_restart_type,prmanent}} =
@@ -487,15 +487,15 @@ child_specs(Config) when is_list(Config) ->
{error, {invalid_shutdown,-10}} = supervisor:check_childspecs([B3]),
{error, {invalid_child_type,wrker}}
= supervisor:check_childspecs([B4]),
- {error, _} = supervisor:check_childspecs([B5]),
- {error, {invalid_modules,dy}} = supervisor:check_childspecs([B6]),
+ {error, {invalid_modules,dy}} = supervisor:check_childspecs([B5]),
{error, {invalid_module, 1}} =
- supervisor:check_childspecs([B7]),
+ supervisor:check_childspecs([B6]),
ok = supervisor:check_childspecs([C1]),
ok = supervisor:check_childspecs([C2]),
ok = supervisor:check_childspecs([C3]),
ok = supervisor:check_childspecs([C4]),
+ ok = supervisor:check_childspecs([C5]),
ok.
%%-------------------------------------------------------------------------
--
cgit v1.2.3
From 9679510bb27b569fd47394b6cb319916c3282de9 Mon Sep 17 00:00:00 2001
From: Christopher Faulet
Date: Wed, 14 Sep 2011 14:30:47 +0200
Subject: Add a warning to docs about workers' shutdown strategy
---
lib/stdlib/doc/src/supervisor.xml | 7 +++++++
1 file changed, 7 insertions(+)
(limited to 'lib')
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index b4baf2f0a0..54e7cab884 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -171,6 +171,13 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
should be set to infinity to give the subtree ample
time to shutdown. It is also allowed to set it to infinity,
if the child process is a worker.
+
+ Be careful by setting the Shutdown strategy to
+ infinity when the child process is a worker. Because, in this
+ situation, the termination of the supervision tree depends on the
+ child process, it must be implemented in a safe way and its cleanup
+ procedure must always return.
+
Important note on simple-one-for-one supervisors:
The dynamically created child processes of a
simple-one-for-one supervisor are not explicitly killed,
--
cgit v1.2.3
From f354e22115c8b92753fae861698945ff64970294 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin
Date: Tue, 11 Oct 2011 09:36:56 +0200
Subject: Put back ssl:peercert/1
I accidentally removed a little too much, only peercert/2 was deprecated.
---
lib/ssl/src/ssl.erl | 20 +++++++++++++++-----
lib/ssl/test/ssl_basic_SUITE.erl | 40 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 54 insertions(+), 6 deletions(-)
(limited to 'lib')
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 5819553bd4..35f9410562 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -27,16 +27,13 @@
transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3,
cipher_suites/0, cipher_suites/1, close/1, shutdown/2,
connect/3, connect/2, connect/4, connection_info/1,
- controlling_process/2, listen/2, pid/1, peername/1, recv/2,
- recv/3, send/2, getopts/2, setopts/2, sockname/1,
+ controlling_process/2, listen/2, pid/1, peername/1, peercert/1,
+ recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1,
versions/0, session_info/1, format_error/1,
renegotiate/1]).
-%% Should be deprecated as soon as old ssl is removed
-deprecated({pid, 1, next_major_release}).
-%-deprecated({peercert, 2, next_major_release}).
-%%-include("ssl_int.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
@@ -287,6 +284,19 @@ connection_info(#sslsocket{pid = Pid}) ->
peername(#sslsocket{pid = Pid}) ->
ssl_connection:peername(Pid).
+%%--------------------------------------------------------------------
+-spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}.
+%%
+%% Description: Returns the peercert.
+%%--------------------------------------------------------------------
+peercert(#sslsocket{pid = Pid}) ->
+ case ssl_connection:peer_certificate(Pid) of
+ {ok, undefined} ->
+ {error, no_peercert};
+ Result ->
+ Result
+ end.
+
%%--------------------------------------------------------------------
-spec cipher_suites() -> [erl_cipher_suite()].
-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()].
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index d5cd3b3b4b..a9109c5a6e 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -207,7 +207,7 @@ all() ->
[app, alerts, connection_info, protocol_versions,
empty_protocol_versions, controlling_process,
controller_dies, client_closes_socket,
- connect_dist, peername, sockname, socket_options,
+ connect_dist, peername, peercert, sockname, socket_options,
invalid_inet_get_option, invalid_inet_get_option_not_list,
invalid_inet_get_option_improper_list,
invalid_inet_set_option, invalid_inet_set_option_not_list,
@@ -662,6 +662,44 @@ peername(Config) when is_list(Config) ->
peername_result(S) ->
ssl:peername(S).
+%%--------------------------------------------------------------------
+peercert(doc) ->
+ [""];
+peercert(suite) ->
+ [];
+peercert(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ClientOpts}]),
+
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile),
+
+ ServerMsg = {error, no_peercert},
+ ClientMsg = {ok, BinCert},
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+peercert_result(Socket) ->
+ ssl:peercert(Socket).
+
%%--------------------------------------------------------------------
sockname(doc) ->
["Test API function sockname/1"];
--
cgit v1.2.3
From 1c52cfa3b8cbc6e8650444ff10bab827965aa4ec Mon Sep 17 00:00:00 2001
From: Lars Thorsen
Date: Wed, 12 Oct 2011 10:27:12 +0200
Subject: corba applications: Fix broken 'make clean'
The IDL-GENERATED files must be removed too; otherwise the target
files will not be made the next time 'make' is invoked.
---
lib/cosEvent/src/Makefile | 2 +-
lib/cosEventDomain/src/Makefile | 2 +-
lib/cosFileTransfer/src/Makefile | 2 +-
lib/cosNotification/src/Makefile | 2 +-
lib/cosProperty/src/Makefile | 2 +-
lib/cosTime/src/Makefile | 2 +-
lib/cosTransactions/src/Makefile | 2 +-
lib/ic/examples/pre_post_condition/Makefile | 2 +-
lib/orber/COSS/CosNaming/Makefile | 2 +-
lib/orber/examples/Stack/Makefile | 2 +-
lib/orber/src/Makefile | 2 +-
11 files changed, 11 insertions(+), 11 deletions(-)
(limited to 'lib')
diff --git a/lib/cosEvent/src/Makefile b/lib/cosEvent/src/Makefile
index c774d18380..736b95538a 100644
--- a/lib/cosEvent/src/Makefile
+++ b/lib/cosEvent/src/Makefile
@@ -164,7 +164,7 @@ debug:
@${MAKE} TYPE=debug opt
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/cosEventDomain/src/Makefile b/lib/cosEventDomain/src/Makefile
index 91bef4e7e6..5af790c760 100644
--- a/lib/cosEventDomain/src/Makefile
+++ b/lib/cosEventDomain/src/Makefile
@@ -137,7 +137,7 @@ debug:
@${MAKE} TYPE=debug opt
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/cosFileTransfer/src/Makefile b/lib/cosFileTransfer/src/Makefile
index 17e82f9bc2..b811ef1106 100644
--- a/lib/cosFileTransfer/src/Makefile
+++ b/lib/cosFileTransfer/src/Makefile
@@ -147,7 +147,7 @@ cleanb:
rm -f errs core *~
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile
index b976ab94f3..be52d1a06f 100644
--- a/lib/cosNotification/src/Makefile
+++ b/lib/cosNotification/src/Makefile
@@ -328,7 +328,7 @@ cleanb:
rm -f errs core *~
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/cosProperty/src/Makefile b/lib/cosProperty/src/Makefile
index d12554b18d..b72019f37d 100644
--- a/lib/cosProperty/src/Makefile
+++ b/lib/cosProperty/src/Makefile
@@ -147,7 +147,7 @@ cleanb:
rm -f errs core *~
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/cosTime/src/Makefile b/lib/cosTime/src/Makefile
index 1793822fb6..fa456249bd 100644
--- a/lib/cosTime/src/Makefile
+++ b/lib/cosTime/src/Makefile
@@ -162,7 +162,7 @@ cleanb:
rm -f errs core *~
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile
index 4b77251c3c..e7d4b0b080 100644
--- a/lib/cosTransactions/src/Makefile
+++ b/lib/cosTransactions/src/Makefile
@@ -141,7 +141,7 @@ debug:
@${MAKE} TYPE=debug
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/ic/examples/pre_post_condition/Makefile b/lib/ic/examples/pre_post_condition/Makefile
index 85cbbdb9ff..d57133c964 100644
--- a/lib/ic/examples/pre_post_condition/Makefile
+++ b/lib/ic/examples/pre_post_condition/Makefile
@@ -100,7 +100,7 @@ YRL_FLAGS =
debug opt: $(TARGET_FILES)
clean:
- rm -f $(TARGET_FILES) $(GEN_ERL_MODULES:%=%.erl) $(GEN_HRL_FILES) $(CLASS_FILES)
+ rm -f $(TARGET_FILES) $(GEN_ERL_MODULES:%=%.erl) $(GEN_HRL_FILES) $(CLASS_FILES) IDL-GENERATED
rm -f errs core *~
docs:
diff --git a/lib/orber/COSS/CosNaming/Makefile b/lib/orber/COSS/CosNaming/Makefile
index 28b4d9cacc..d4b2079036 100644
--- a/lib/orber/COSS/CosNaming/Makefile
+++ b/lib/orber/COSS/CosNaming/Makefile
@@ -113,7 +113,7 @@ debug:
@${MAKE} TYPE=debug
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
diff --git a/lib/orber/examples/Stack/Makefile b/lib/orber/examples/Stack/Makefile
index ccb65038a3..215e57fcbe 100644
--- a/lib/orber/examples/Stack/Makefile
+++ b/lib/orber/examples/Stack/Makefile
@@ -96,7 +96,7 @@ ERL_COMPILE_FLAGS += \
debug opt: $(TARGET_FILES)
clean:
- rm -f $(TARGET_FILES) $(GEN_ERL_MODULES:%=%.erl) $(GEN_HRL_FILES) $(CLASS_FILES)
+ rm -f $(TARGET_FILES) $(GEN_ERL_MODULES:%=%.erl) $(GEN_HRL_FILES) $(CLASS_FILES) IDL-GENERATED
rm -f errs core *~
docs:
diff --git a/lib/orber/src/Makefile b/lib/orber/src/Makefile
index ed62c94b98..e812e22b46 100644
--- a/lib/orber/src/Makefile
+++ b/lib/orber/src/Makefile
@@ -212,7 +212,7 @@ debug:
opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) IDL-GENERATED
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
--
cgit v1.2.3
From 36fa068807ef85fdcc85240ca077239ed7d34f5b Mon Sep 17 00:00:00 2001
From: Lars Thorsen
Date: Wed, 12 Oct 2011 15:11:20 +0200
Subject: Fix match bug
Incorrect use of ets:match changed to ets:match_object.
---
lib/ic/doc/src/notes.xml | 16 ++++++++++++++++
lib/ic/src/ic_pragma.erl | 6 +++---
lib/ic/vsn.mk | 2 +-
3 files changed, 20 insertions(+), 4 deletions(-)
(limited to 'lib')
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index de519d5f84..ff289bd76c 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -30,6 +30,22 @@
notes.xml
+
+ IC 4.2.28
+
+
+ Fixed Bugs and Malfunctions
+
+ -
+
+ Incorrect use of ets:match changed to ets:match_object.
+
+ Own Id: OTP-9630
+
+
+
+
+
IC 4.2.27
diff --git a/lib/ic/src/ic_pragma.erl b/lib/ic/src/ic_pragma.erl
index 7f2216b9dc..beaa2852ab 100644
--- a/lib/ic/src/ic_pragma.erl
+++ b/lib/ic/src/ic_pragma.erl
@@ -1601,7 +1601,7 @@ remove_inheriters(S,RS,InheriterList) ->
ReducedInhList;
_Other ->
CleanList =
- ets:match(S, {inherits,'_','_'}),
+ ets:match_object(S, {inherits,'_','_'}),
% CodeOptList =
% [X || X <- EtsList, element(1,X) == codeopt],
NoInheriters =remove_inheriters2(S,ReducedInhList,CleanList),
@@ -1648,7 +1648,7 @@ remove_inh([X],[Y],List,EtsList) ->
%%%----------------------------------------------
remove_inherited(S,InheriterList) ->
CleanList =
- ets:match(S, {inherits, '_', '_'}),
+ ets:match_object(S, {inherits, '_', '_'}),
remove_inherited(S,InheriterList,CleanList).
@@ -1766,7 +1766,7 @@ inherits2(_X,Y,Z,EtsList) ->
%% false otherwise
%%
is_inherited_by(Interface1,Interface2,PragmaTab) ->
- InheritsList = ets:match(PragmaTab, {inherits, '_', '_'}),
+ InheritsList = ets:match_object(PragmaTab, {inherits, '_', '_'}),
inherits(Interface2,Interface1,InheritsList).
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index 6561ccd2a7..703c8d29eb 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.2.27
+IC_VSN = 4.2.28
--
cgit v1.2.3
From 5847f72e816107678be7dae0dc4a61e9e2b84e2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Wed, 12 Oct 2011 10:40:21 +0200
Subject: asn1: Fix typo that caused failure of 'make release' on Windows
---
lib/asn1/c_src/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'lib')
diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile
index f7213b9651..8c06be56f8 100644
--- a/lib/asn1/c_src/Makefile
+++ b/lib/asn1/c_src/Makefile
@@ -62,7 +62,7 @@ NIF_OBJ_FILES = $(OBJDIR)/asn1_erl_nif.o
ifeq ($(TARGET),win32)
-NIF_SHARED_OBJ_FILES = $(LIBDIR)/asn1_erl_nif.dll
+NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.dll
CLIB_FLAGS =
LN=cp
else
--
cgit v1.2.3
From 34db76765561487e526fe66d3d19ecf3b3fb9dc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Tue, 30 Aug 2011 11:51:11 +0200
Subject: Allow noncharacter code points in unicode encoding and decoding
The two noncharacter code points 16#FFFE and 16#FFFF were not
allowed to be encoded or decoded using the unicode module or
bit syntax. That causes an inconsistency, since the noncharacters
16#FDD0 to 16#FDEF could be encoded/decoded.
There is two ways to fix that inconsistency.
We have chosen to allow 16#FFFE and 16#FFFF to be encoded and
decoded, because the noncharacters could be useful internally
within an application and it will make encoding and decoding
slightly faster.
Reported-by: Alisdair Sullivan
---
lib/compiler/test/bs_utf_SUITE.erl | 21 ---------------------
.../com/ericsson/otp/erlang/OtpErlangString.java | 5 ++---
lib/stdlib/doc/src/unicode.xml | 3 +--
lib/stdlib/test/unicode_SUITE.erl | 4 +---
4 files changed, 4 insertions(+), 29 deletions(-)
(limited to 'lib')
diff --git a/lib/compiler/test/bs_utf_SUITE.erl b/lib/compiler/test/bs_utf_SUITE.erl
index f30a4d3fef..94549ad0d3 100644
--- a/lib/compiler/test/bs_utf_SUITE.erl
+++ b/lib/compiler/test/bs_utf_SUITE.erl
@@ -264,18 +264,10 @@ literals(Config) when is_list(Config) ->
?line {'EXIT',{badarg,_}} = (catch <<(-1)/utf32,I/utf8>>),
?line {'EXIT',{badarg,_}} = (catch <<(-1)/little-utf32,I/utf8>>),
?line {'EXIT',{badarg,_}} = (catch <<16#D800/utf8,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFE/utf8,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFF/utf8,I/utf8>>),
?line {'EXIT',{badarg,_}} = (catch <<16#D800/utf16,I/utf8>>),
?line {'EXIT',{badarg,_}} = (catch <<16#D800/little-utf16,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFE/utf16,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFE/little-utf16,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFF/utf16,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFF/little-utf16,I/utf8>>),
?line {'EXIT',{badarg,_}} = (catch <<16#D800/utf32,I/utf8>>),
?line {'EXIT',{badarg,_}} = (catch <<16#D800/little-utf32,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFE/utf32,I/utf8>>),
- ?line {'EXIT',{badarg,_}} = (catch <<16#FFFF/little-utf32,I/utf8>>),
B = 16#10FFFF+1,
?line {'EXIT',{badarg,_}} = (catch <>),
@@ -286,20 +278,11 @@ literals(Config) when is_list(Config) ->
%% Matching of bad literals.
?line error = bad_literal_match(<<237,160,128>>), %16#D800 in UTF-8
- ?line error = bad_literal_match(<<239,191,190>>), %16#FFFE in UTF-8
- ?line error = bad_literal_match(<<239,191,191>>), %16#FFFF in UTF-8
?line error = bad_literal_match(<<244,144,128,128>>), %16#110000 in UTF-8
- ?line error = bad_literal_match(<<255,254>>), %16#FFFE in UTF-16
- ?line error = bad_literal_match(<<255,255>>), %16#FFFF in UTF-16
-
?line error = bad_literal_match(<<16#D800:32>>),
- ?line error = bad_literal_match(<<16#FFFE:32>>),
- ?line error = bad_literal_match(<<16#FFFF:32>>),
?line error = bad_literal_match(<<16#110000:32>>),
?line error = bad_literal_match(<<16#D800:32/little>>),
- ?line error = bad_literal_match(<<16#FFFE:32/little>>),
- ?line error = bad_literal_match(<<16#FFFF:32/little>>),
?line error = bad_literal_match(<<16#110000:32/little>>),
ok.
@@ -314,11 +297,7 @@ match_literal(<<"bj\366rn"/big-utf16>>) -> bjorn_utf16be;
match_literal(<<"bj\366rn"/little-utf16>>) -> bjorn_utf16le.
bad_literal_match(<<16#D800/utf8>>) -> ok;
-bad_literal_match(<<16#FFFE/utf8>>) -> ok;
-bad_literal_match(<<16#FFFF/utf8>>) -> ok;
bad_literal_match(<<16#110000/utf8>>) -> ok;
-bad_literal_match(<<16#FFFE/utf16>>) -> ok;
-bad_literal_match(<<16#FFFF/utf16>>) -> ok;
bad_literal_match(<<16#D800/utf32>>) -> ok;
bad_literal_match(<<16#110000/utf32>>) -> ok;
bad_literal_match(<<16#D800/little-utf32>>) -> ok;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
index 19ee92e0d0..23734bf83b 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
@@ -166,7 +166,7 @@ public class OtpErlangString extends OtpErlangObject implements Serializable,
/**
* Validate a code point according to Erlang definition; Unicode 3.0.
* That is; valid in the range U+0..U+10FFFF, but not in the range
- * U+D800..U+DFFF (surrogat pairs), nor U+FFFE..U+FFFF (non-characters).
+ * U+D800..U+DFFF (surrogat pairs).
*
* @param cp
* the code point value to validate
@@ -179,8 +179,7 @@ public class OtpErlangString extends OtpErlangObject implements Serializable,
// Erlang definition of valid Unicode code points;
// Unicode 3.0, XML, et.al.
return (cp>>>16) <= 0x10 // in 0..10FFFF; Unicode range
- && (cp & ~0x7FF) != 0xD800 // not in D800..DFFF; surrogate range
- && (cp & ~1) != 0xFFFE; // not in FFFE..FFFF; non-characters
+ && (cp & ~0x7FF) != 0xD800; // not in D800..DFFF; surrogate range
}
/**
diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml
index d02763f75c..1001ebbae4 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -203,8 +203,7 @@
- greater than 16#10FFFF
(the maximum unicode character),
- in the range 16#D800 to 16#DFFF
- (invalid unicode range)
- - or equal to 16#FFFE or 16#FFFF (non characters)
+ (invalid range reserved for UTF-16 surrogate pairs)
is found.
diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl
index 9aa800209d..4055af2741 100644
--- a/lib/stdlib/test/unicode_SUITE.erl
+++ b/lib/stdlib/test/unicode_SUITE.erl
@@ -322,7 +322,7 @@ roundtrips(Config) when is_list(Config) ->
ex_roundtrips(Config) when is_list(Config) ->
?line L1 = ranges(0, 16#D800 - 1,
erlang:system_info(context_reductions) * 11),
- ?line L2 = ranges(16#DFFF + 1, 16#FFFE - 1,
+ ?line L2 = ranges(16#DFFF + 1, 16#10000 - 1,
erlang:system_info(context_reductions) * 11),
%?line L3 = ranges(16#FFFF + 1, 16#10FFFF,
% erlang:system_info(context_reductions) * 11),
@@ -569,7 +569,6 @@ utf16_illegal_sequences_bif(Config) when is_list(Config) ->
ex_utf16_illegal_sequences_bif(Config) when is_list(Config) ->
?line utf16_fail_range_bif_simple(16#10FFFF+1, 16#10FFFF+512), %Too large.
?line utf16_fail_range_bif(16#D800, 16#DFFF), %Reserved for UTF-16.
- ?line utf16_fail_range_bif(16#FFFE, 16#FFFF), %Non-characters.
?line lonely_hi_surrogate_bif(16#D800, 16#DBFF,incomplete),
?line lonely_hi_surrogate_bif(16#DC00, 16#DFFF,error),
@@ -644,7 +643,6 @@ utf8_illegal_sequences_bif(Config) when is_list(Config) ->
ex_utf8_illegal_sequences_bif(Config) when is_list(Config) ->
?line fail_range_bif(16#10FFFF+1, 16#10FFFF+512), %Too large.
?line fail_range_bif(16#D800, 16#DFFF), %Reserved for UTF-16.
- ?line fail_range_bif(16#FFFE, 16#FFFF), %Reserved (BOM).
%% Illegal first character.
?line [fail_bif(<>,unicode) || I <- lists:seq(16#80, 16#BF)],
--
cgit v1.2.3
From a4ce63afbc88e7e089b2898d1c08578ceb5ee207 Mon Sep 17 00:00:00 2001
From: Hans Bolinder
Date: Thu, 6 Oct 2011 14:42:28 +0200
Subject: Fix bugs in the generation of manpages
The indentation of the tag has been corrected, as has the
corresponding tag used for Erlang specs.
The contents of the was not properly processed.
---
lib/erl_docgen/priv/xsl/db_man.xsl | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
(limited to 'lib')
diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl
index 1df96caa36..0aca74bc97 100644
--- a/lib/erl_docgen/priv/xsl/db_man.xsl
+++ b/lib/erl_docgen/priv/xsl/db_man.xsl
@@ -137,8 +137,9 @@
(there is no spec with more than one clause) -->
.RS
-
.TP 3
+
.LP
Types:
+
.RS 3
@@ -164,6 +165,8 @@
+
.RE
+
.RE
@@ -257,8 +260,8 @@
-
-
.br
+
.RS 2
+
.RE
@@ -757,24 +760,26 @@
.RS
-
.TP 3
+
.LP
Types:
+
.RS 3
.RE
+
.RE
-
+
.br
-
-
.br
+
.RS 2
+
.RE
--
cgit v1.2.3
From 6a37976806f9a0998d0970c17f0e176b6e19bdad Mon Sep 17 00:00:00 2001
From: Hans Bolinder
Date: Thu, 6 Oct 2011 15:08:09 +0200
Subject: Correct the docs
---
lib/stdlib/doc/src/lists.xml | 26 +++++++++++++-------------
lib/stdlib/src/lists.erl | 29 +++++++++++++++++------------
2 files changed, 30 insertions(+), 25 deletions(-)
(limited to 'lib')
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 6f3ed7af98..7042c84437 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -240,7 +240,7 @@ flatmap(Fun, List1) ->
Delete an element from a list of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns a copy of TupleList1 where the first
occurrence of a tuple whose Nth element compares equal to
@@ -266,7 +266,7 @@ flatmap(Fun, List1) ->
Map a function over a list of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns a list of tuples where, for each tuple in
TupleList1, the Nth element Term1 of the tuple
@@ -298,7 +298,7 @@ flatmap(Fun, List1) ->
Merge two key-sorted lists of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns the sorted list formed by merging TupleList1
and TupleList2. The merge is performed on
@@ -312,7 +312,7 @@ flatmap(Fun, List1) ->
Replace an element in a list of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns a copy of TupleList1 where the first
occurrence of a T tuple whose Nth element
@@ -342,7 +342,7 @@ flatmap(Fun, List1) ->
Sort a list of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns a list containing the sorted elements of the list
TupleList1. Sorting is performed on the Nth
@@ -352,7 +352,7 @@ flatmap(Fun, List1) ->
Store an element in a list of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns a copy of TupleList1 where the first
occurrence of a tuple T whose Nth element
@@ -366,7 +366,7 @@ flatmap(Fun, List1) ->
Extract an element from a list of tuples
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Searches the list of tuples TupleList1 for a tuple
whose Nth element compares equal to Key.
@@ -500,7 +500,7 @@ flatmap(Fun, List1) ->
Return the Nth element of a list
- 1..length(List)
+ 1..length(List)
Returns the Nth element of List. For example:
@@ -511,7 +511,7 @@ c
Return the Nth tail of a list
- 0..length(List)
+ 0..length(List)
Returns the Nth tail of List, that is, the sublist of
List starting at N+1 and continuing up to
@@ -630,7 +630,7 @@ length(lists:seq(From, To, Incr)) == (To-From+Incr) div Incr
Split a list into two lists
- 0..length(List1)
+ 0..length(List1)
Splits List1 into List2 and List3.
List2 contains the first N elements and
@@ -670,7 +670,7 @@ splitwith(Pred, List) ->
Return a sub-list starting at a given position and with a given number of elements
- 1..(length(List1)+1)
+ 1..(length(List1)+1)
Returns the sub-list of List1 starting at Start
and with (max) Len elements. It is not an error for
@@ -732,7 +732,7 @@ splitwith(Pred, List) ->
Merge two key-sorted lists of tuples, removing duplicates
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns the sorted list formed by merging TupleList1
and TupleList2. The merge is performed on the
@@ -746,7 +746,7 @@ splitwith(Pred, List) ->
Sort a list of tuples, removing duplicates
- 1..tuple_size(Tuple)
+ 1..tuple_size(Tuple)
Returns a list containing the sorted elements of the list
TupleList1 where all but the first tuple of the
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index bba46e4cb6..e73c087753 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -628,9 +628,10 @@ keydelete3(_, _, []) -> [].
-spec keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 when
Key :: term(),
N :: pos_integer(),
- TupleList1 :: [tuple()],
- TupleList2 :: [tuple()],
- NewTuple :: tuple().
+ TupleList1 :: [Tuple],
+ TupleList2 :: [Tuple],
+ NewTuple :: Tuple,
+ Tuple :: tuple().
keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
keyreplace3(K, N, L, New).
@@ -660,9 +661,10 @@ keytake(_K, _N, [], _L) -> false.
-spec keystore(Key, N, TupleList1, NewTuple) -> TupleList2 when
Key :: term(),
N :: pos_integer(),
- TupleList1 :: [tuple()],
- TupleList2 :: [tuple(), ...],
- NewTuple :: tuple().
+ TupleList1 :: [Tuple],
+ TupleList2 :: [Tuple, ...],
+ NewTuple :: Tuple,
+ Tuple :: tuple().
keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
keystore2(K, N, L, New).
@@ -740,8 +742,9 @@ keysort_1(_I, X, _EX, [], R) ->
TupleList1 :: [T1],
TupleList2 :: [T2],
TupleList3 :: [(T1 | T2)],
- T1 :: tuple(),
- T2 :: tuple().
+ T1 :: Tuple,
+ T2 :: Tuple,
+ Tuple :: tuple().
keymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -842,8 +845,9 @@ ukeysort_1(_I, X, _EX, []) ->
TupleList1 :: [T1],
TupleList2 :: [T2],
TupleList3 :: [(T1 | T2)],
- T1 :: tuple(),
- T2 :: tuple().
+ T1 :: Tuple,
+ T2 :: Tuple,
+ Tuple :: tuple().
ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 ->
case L1 of
@@ -873,8 +877,9 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
-spec keymap(Fun, N, TupleList1) -> TupleList2 when
Fun :: fun((Term1 :: term()) -> Term2 :: term()),
N :: pos_integer(),
- TupleList1 :: [tuple()],
- TupleList2 :: [tuple()].
+ TupleList1 :: [Tuple],
+ TupleList2 :: [Tuple],
+ Tuple :: tuple().
keymap(Fun, Index, [Tup|Tail]) ->
[setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)];
--
cgit v1.2.3
From 45951664c2214ee9e4342e5909a8fa8445f03b92 Mon Sep 17 00:00:00 2001
From: Hans Bolinder
Date: Thu, 6 Oct 2011 09:50:38 +0200
Subject: Fix a bug in Dets concerning repair of almost full tables
A Dets table with sufficiently large buckets could not always be repaired.
(Reported by Gordon Guthrie.)
The format of Dets files has been modified. When downgrading tables
created with the new system will be repaired. Otherwise the
modification should not be noticeable.
---
lib/stdlib/src/dets.erl | 57 +++++++++-----------
lib/stdlib/src/dets.hrl | 3 +-
lib/stdlib/src/dets_v8.erl | 16 +++---
lib/stdlib/src/dets_v9.erl | 118 +++++++++++++++++++++++------------------
lib/stdlib/test/dets_SUITE.erl | 93 ++++++++++++++++++++++++++++++--
5 files changed, 189 insertions(+), 98 deletions(-)
(limited to 'lib')
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 7ed9c4b695..c0f9ce34b0 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1754,17 +1754,6 @@ system_code_change(State, _Module, _OldVsn, _Extra) ->
%%% Internal functions
%%%----------------------------------------------------------------------
-constants(FH, FileName) ->
- Version = FH#fileheader.version,
- if
- Version =< 8 ->
- dets_v8:constants();
- Version =:= 9 ->
- dets_v9:constants();
- true ->
- throw({error, {not_a_dets_file, FileName}})
- end.
-
%% -> {ok, Fd, fileheader()} | throw(Error)
read_file_header(FileName, Access, RamFile) ->
BF = if
@@ -1842,7 +1831,11 @@ do_bchunk_init(Head, Tab) ->
{H2, {error, old_version}};
Parms ->
L = dets_utils:all_allocated(H2),
- C0 = #dets_cont{no_objs = default, bin = <<>>, alloc = L},
+ Bin = if
+ L =:= <<>> -> eof;
+ true -> <<>>
+ end,
+ C0 = #dets_cont{no_objs = default, bin = Bin, alloc = L},
BinParms = term_to_binary(Parms),
{H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk},
[BinParms]}}
@@ -2478,9 +2471,7 @@ fopen2(Fname, Tab) ->
Do = case Mod:check_file_header(FH, Fd) of
{ok, Head1, ExtraInfo} ->
Head2 = Head1#head{filename = Fname},
- try Mod:init_freelist(Head2, ExtraInfo) of
- Ftab ->
- {ok, Head1#head{freelists = Ftab}}
+ try {ok, Mod:init_freelist(Head2, ExtraInfo)}
catch
throw:_ ->
{repair, " has bad free lists, repairing ..."}
@@ -2574,9 +2565,7 @@ fopen_existing_file(Tab, OpenArgs) ->
Do = case Wh of
{Tag, Hd, Extra} when Tag =:= final; Tag =:= compact ->
Hd1 = Hd#head{filename = Fname},
- try Mod:init_freelist(Hd1, Extra) of
- Ftab ->
- {Tag, Hd#head{freelists = Ftab}}
+ try {Tag, Mod:init_freelist(Hd1, Extra)}
catch
throw:_ ->
{repair, " has bad free lists, repairing ..."}
@@ -3164,8 +3153,12 @@ init_scan(Head, NoObjs) ->
check_safe_fixtable(Head),
FreeLists = dets_utils:get_freelists(Head),
Base = Head#head.base,
- {From, To} = dets_utils:find_next_allocated(FreeLists, Base, Base),
- #dets_cont{no_objs = NoObjs, bin = <<>>, alloc = {From, To, <<>>}}.
+ case dets_utils:find_next_allocated(FreeLists, Base, Base) of
+ {From, To} ->
+ #dets_cont{no_objs = NoObjs, bin = <<>>, alloc = {From,To,<<>>}};
+ none ->
+ #dets_cont{no_objs = NoObjs, bin = eof, alloc = <<>>}
+ end.
check_safe_fixtable(Head) ->
case (Head#head.fixed =:= false) andalso
@@ -3266,18 +3259,18 @@ view(FileName) ->
case catch read_file_header(FileName, read, false) of
{ok, Fd, FH} ->
Mod = FH#fileheader.mod,
- try
- case Mod:check_file_header(FH, Fd) of
- {ok, H0, ExtraInfo} ->
- Ftab = Mod:init_freelist(H0, ExtraInfo),
- {_Bump, Base} = constants(FH, FileName),
- H = H0#head{freelists=Ftab, base = Base},
- v_free_list(H),
- Mod:v_segments(H),
- ok;
- X ->
- X
- end
+ try Mod:check_file_header(FH, Fd) of
+ {ok, H0, ExtraInfo} ->
+ Mod = FH#fileheader.mod,
+ case Mod:check_file_header(FH, Fd) of
+ {ok, H0, ExtraInfo} ->
+ H = Mod:init_freelist(H0, ExtraInfo),
+ v_free_list(H),
+ Mod:v_segments(H),
+ ok;
+ X ->
+ X
+ end
after file:close(Fd)
end;
X ->
diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl
index fbffc9d008..a3f99357a2 100644
--- a/lib/stdlib/src/dets.hrl
+++ b/lib/stdlib/src/dets.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. 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
@@ -92,6 +92,7 @@
%% Info extracted from the file header.
-record(fileheader, {
freelist,
+ fl_base,
cookie,
closed_properly,
type,
diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl
index cdd38d5604..3e962a1c8b 100644
--- a/lib/stdlib/src/dets_v8.erl
+++ b/lib/stdlib/src/dets_v8.erl
@@ -21,7 +21,7 @@
%% Dets files, implementation part. This module handles versions up to
%% and including 8(c). To be called from dets.erl only.
--export([constants/0, mark_dirty/1, read_file_header/2,
+-export([mark_dirty/1, read_file_header/2,
check_file_header/2, do_perform_save/1, initiate_file/11,
init_freelist/2, fsck_input/4,
bulk_input/3, output_objs/4, write_cache/1, may_grow/3,
@@ -196,10 +196,6 @@
%%-define(DEBUGF(X,Y), io:format(X, Y)).
-define(DEBUGF(X,Y), void).
-%% {Bump}
-constants() ->
- {?BUMP, ?BASE}.
-
%% -> ok | throw({NewHead,Error})
mark_dirty(Head) ->
Dirty = [{?CLOSED_PROPERLY_POS, <>}],
@@ -308,8 +304,9 @@ init_freelist(Head, {convert_freelist,_Version}) ->
Pos = Head#head.freelists_p,
case catch prterm(Head, Pos, ?OHDSZ) of
{0, _Sz, Term} ->
- FreeList = lists:reverse(Term),
- dets_utils:init_slots_from_old_file(FreeList, Ftab);
+ FreeList1 = lists:reverse(Term),
+ FreeList = dets_utils:init_slots_from_old_file(FreeList1, Ftab),
+ Head#head{freelists = FreeList, base = ?BASE};
_ ->
throw({error, {bad_freelists, Head#head.filename}})
end;
@@ -318,7 +315,7 @@ init_freelist(Head, _) ->
Pos = Head#head.freelists_p,
case catch prterm(Head, Pos, ?OHDSZ) of
{0, _Sz, Term} ->
- Term;
+ Head#head{freelists = Term, base = ?BASE};
_ ->
throw({error, {bad_freelists, Head#head.filename}})
end.
@@ -331,6 +328,7 @@ read_file_header(Fd, FileName) ->
{ok, EOF} = dets_utils:position_close(Fd, FileName, eof),
{ok, <>} = dets_utils:pread_close(Fd, FileName, EOF-4, 4),
FH = #fileheader{freelist = Freelist,
+ fl_base = ?BASE,
cookie = Cookie,
closed_properly = CP,
type = dets_utils:code_to_type(Type2),
@@ -413,7 +411,7 @@ check_file_header(FH, Fd) ->
version = ?FILE_FORMAT_VERSION,
mod = ?MODULE,
bump = ?BUMP,
- base = ?BASE},
+ base = FH#fileheader.fl_base},
{ok, H, ExtraInfo};
Error ->
Error
diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl
index 132af01f79..f577b4410f 100644
--- a/lib/stdlib/src/dets_v9.erl
+++ b/lib/stdlib/src/dets_v9.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. 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
@@ -21,7 +21,7 @@
%% Dets files, implementation part. This module handles version 9.
%% To be called from dets.erl only.
--export([constants/0, mark_dirty/1, read_file_header/2,
+-export([mark_dirty/1, read_file_header/2,
check_file_header/2, do_perform_save/1, initiate_file/11,
prep_table_copy/9, init_freelist/2, fsck_input/4,
bulk_input/3, output_objs/4, bchunk_init/2,
@@ -70,6 +70,17 @@
%% 16 MD5-sum for the 44 plus 112 bytes before the MD5-sum.
%% (FreelistsPointer, Cookie and ClosedProperly are not digested.)
%% 128 Reserved for future versions. Initially zeros.
+%% Version 9(d), introduced in R15A, has instead:
+%% 112 28 counters for the buddy system sizes (as for 9(b)).
+%% 16 MD5-sum for the 44 plus 112 bytes before the MD5-sum.
+%% (FreelistsPointer, Cookie and ClosedProperly are not digested.)
+%% 4 Base of the buddy system.
+%% 0 (zero) if the base is equal to ?BASE. Compatible with R14B.
+%% File size at the end of the file is RealFileSize - Base.
+%% The reason for modifying file size is that when a file created
+%% by R15 is read by R14 a repair takes place immediately, which
+%% is acceptable when downgrading.
+%% 124 Reserved for future versions. Initially zeros.
%% ---
%% ------------------ end of file header
%% 4*256 SegmentArray Pointers.
@@ -86,7 +97,7 @@
%% -----------------------------
%% ??? Free lists
%% -----------------------------
-%% 4 File size, in bytes.
+%% 4 File size, in bytes. See 9(d) obove.
%% Before we can find an object we must find the slot where the
%% object resides. Each slot is a (possibly empty) list (or chain) of
@@ -177,14 +188,14 @@
%%% File header
%%%
--define(RESERVED, 128). % Reserved for future use.
+-define(RESERVED, 124). % Reserved for future use.
-define(COLL_CNTRS, (28*4)). % Counters for the buddy system.
-define(MD5SZ, 16).
+-define(FL_BASE, 4).
--define(HEADSZ,
- 56+?COLL_CNTRS+?MD5SZ). % The size of the file header, in bytes,
- % not including the reserved part.
+-define(HEADSZ, 56+?COLL_CNTRS % The size of the file header, in bytes,
+ +?MD5SZ+?FL_BASE). % not including the reserved part.
-define(HEADEND, (?HEADSZ+?RESERVED)).
% End of header and reserved area.
-define(SEGSZ, 512). % Size of a segment, in words. SZOBJP*SEGSZP.
@@ -270,10 +281,6 @@
%%-define(DEBUGF(X,Y), io:format(X, Y)).
-define(DEBUGF(X,Y), void).
-%% {Bump}
-constants() ->
- {?BUMP, ?BASE}.
-
%% -> ok | throw({NewHead,Error})
mark_dirty(Head) ->
Dirty = [{?CLOSED_PROPERLY_POS, <>}],
@@ -356,7 +363,7 @@ init_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, Ram, CacheSz,
cache = dets_utils:new_cache(CacheSz),
version = ?FILE_FORMAT_VERSION,
bump = ?BUMP,
- base = ?BASE,
+ base = ?BASE, % to be overwritten
mod = ?MODULE
},
@@ -378,13 +385,20 @@ init_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, Ram, CacheSz,
{Head1, Ws1} = init_parts(Head0, 0, no_parts(Next), Zero, []),
NoSegs = no_segs(Next),
- {Head, WsI, WsP} = init_segments(Head1, 0, NoSegs, Zero, [], []),
+ {Head2, WsI, WsP} = init_segments(Head1, 0, NoSegs, Zero, [], []),
Ws2 = if
DoInitSegments -> WsP ++ WsI;
true -> WsP
end,
dets_utils:pwrite(Fd, Fname, [W0 | lists:append(Ws1) ++ Ws2]),
- true = hash_invars(Head),
+ true = hash_invars(Head2),
+ %% The allocations that have been made so far (parts, segments)
+ %% are permanent; the table will never shrink. Therefore the base
+ %% of the Buddy system can be set to the first free object.
+ %% This is used in allocate_all(), see below.
+ {_, Where, _} = dets_utils:alloc(Head2, ?BUMP),
+ NewFtab = dets_utils:init_alloc(Where),
+ Head = Head2#head{freelists = NewFtab, base = Where},
{ok, Head}.
%% Returns a power of two not less than 256.
@@ -451,8 +465,9 @@ read_file_header(Fd, FileName) ->
Version:32, M:32, Next:32, Kp:32,
NoObjects:32, NoKeys:32, MinNoSlots:32, MaxNoSlots:32,
HashMethod:32, N:32, NoCollsB:?COLL_CNTRS/binary,
- MD5:?MD5SZ/binary>> = Bin,
- <<_:12/binary,MD5DigestedPart:(?HEADSZ-?MD5SZ-12)/binary,_/binary>> = Bin,
+ MD5:?MD5SZ/binary, FlBase:32>> = Bin,
+ <<_:12/binary,MD5DigestedPart:(?HEADSZ-?MD5SZ-?FL_BASE-12)/binary,
+ _/binary>> = Bin,
{ok, EOF} = dets_utils:position_close(Fd, FileName, eof),
{ok, <>} = dets_utils:pread_close(Fd, FileName, EOF-4, 4),
{CL, <<>>} = lists:foldl(fun(LSz, {Acc,<>}) ->
@@ -468,8 +483,12 @@ read_file_header(Fd, FileName) ->
true ->
lists:reverse(CL)
end,
-
+ Base = case FlBase of
+ 0 -> ?BASE;
+ _ -> FlBase
+ end,
FH = #fileheader{freelist = FreeList,
+ fl_base = Base,
cookie = Cookie,
closed_properly = CP,
type = dets_utils:code_to_type(Type2),
@@ -486,7 +505,7 @@ read_file_header(Fd, FileName) ->
read_md5 = MD5,
has_md5 = <<0:?MD5SZ/unit:8>> =/= MD5,
md5 = erlang:md5(MD5DigestedPart),
- trailer = FileSize,
+ trailer = FileSize + FlBase,
eof = EOF,
n = N,
mod = ?MODULE},
@@ -544,7 +563,7 @@ check_file_header(FH, Fd) ->
version = ?FILE_FORMAT_VERSION,
mod = ?MODULE,
bump = ?BUMP,
- base = ?BASE},
+ base = FH#fileheader.fl_base},
{ok, H, ExtraInfo};
Error ->
Error
@@ -1185,41 +1204,25 @@ write_loop(Head, BytesToWrite, Bin) ->
write_loop(Head, BytesToWrite, SmallBin).
%% By allocating bigger objects before smaller ones, holes in the
-%% buddy system memory map are avoided. Unfortunately, the segments
-%% are always allocated first, so if there are objects bigger than a
-%% segment, there is a hole to handle. (Haven't considered placing the
-%% segments among other objects of the same size.)
+%% buddy system memory map are avoided.
allocate_all_objects(Head, SizeT) ->
DTL = lists:reverse(lists:keysort(1, ets:tab2list(SizeT))),
MaxSz = element(1, hd(DTL)),
- SegSize = ?ACTUAL_SEG_SIZE,
- {Head1, HSz, HN, HA} = alloc_hole(MaxSz, Head, SegSize),
- {Head2, NL} = allocate_all(Head1, DTL, []),
+ {Head1, NL} = allocate_all(Head, DTL, []),
%% Find the position that will be the end of the file by allocating
%% a minimal object.
- {_Head, EndOfFile, _} = dets_utils:alloc(Head2, ?BUMP),
- Head3 = free_hole(Head2, HSz, HN, HA),
- NewHead = Head3#head{maxobjsize = max_objsize(Head3#head.no_collections)},
+ {_Head, EndOfFile, _} = dets_utils:alloc(Head1, ?BUMP),
+ NewHead = Head1#head{maxobjsize = max_objsize(Head1#head.no_collections)},
{NewHead, NL, MaxSz, EndOfFile}.
-alloc_hole(LSize, Head, SegSz) when ?POW(LSize-1) > SegSz ->
- Size = ?POW(LSize-1),
- {_, SegAddr, _} = dets_utils:alloc(Head, adjsz(SegSz)),
- {_, Addr, _} = dets_utils:alloc(Head, adjsz(Size)),
- N = (Addr - SegAddr) div SegSz,
- Head1 = dets_utils:alloc_many(Head, SegSz, N, SegAddr),
- {Head1, SegSz, N, SegAddr};
-alloc_hole(_MaxSz, Head, _SegSz) ->
- {Head, 0, 0, 0}.
-
-free_hole(Head, _Size, 0, _Addr) ->
- Head;
-free_hole(Head, Size, N, Addr) ->
- {Head1, _} = dets_utils:free(Head, Addr, adjsz(Size)),
- free_hole(Head1, Size, N-1, Addr+Size).
-
%% One (temporary) file for each buddy size, write all objects of that
%% size to the file.
+%%
+%% Before R15 a "hole" was needed before the first bucket if the size
+%% of the biggest bucket was greater than the size of a segment. The
+%% hole proved to be a problem with almost full tables with huge
+%% buckets. Since R15 the hole is no longer needed due to the fact
+%% that the base of the Buddy system is flexible.
allocate_all(Head, [{?FSCK_SEGMENT,_,Data,_}], L) ->
%% And one file for the segments...
%% Note that space for the array parts and the segments has
@@ -1593,23 +1596,28 @@ do_perform_save(H) ->
H1 = H#head{freelists_p = FreeListsPointer},
{FLW, FLSize} = free_lists_to_file(H1),
FileSize = FreeListsPointer + FLSize + 4,
- ok = dets_utils:write(H1, [FLW | <>]),
+ AdjustedFileSize = case H#head.base of
+ ?BASE -> FileSize;
+ Base -> FileSize - Base
+ end,
+ ok = dets_utils:write(H1, [FLW | <>]),
FileHeader = file_header(H1, FreeListsPointer, ?CLOSED_PROPERLY),
case dets_utils:debug_mode() of
true ->
- TmpHead = H1#head{freelists = init_freelist(H1, true),
- fixed = false},
+ TmpHead0 = init_freelist(H1#head{fixed = false}, true),
+ TmpHead = TmpHead0#head{base = H1#head.base},
case
catch dets_utils:all_allocated_as_list(TmpHead)
=:= dets_utils:all_allocated_as_list(H1)
- of
+ of
true ->
dets_utils:pwrite(H1, [{0, FileHeader}]);
_ ->
+ throw(
dets_utils:corrupt_reason(H1, {failed_to_save_free_lists,
FreeListsPointer,
TmpHead#head.freelists,
- H1#head.freelists})
+ H1#head.freelists}))
end;
false ->
dets_utils:pwrite(H1, [{0, FileHeader}])
@@ -1648,7 +1656,11 @@ file_header(Head, FreeListsPointer, ClosedProperly, NoColls) ->
true -> erlang:md5(DigH);
false -> <<0:?MD5SZ/unit:8>>
end,
- [H1, DigH, MD5 | <<0:?RESERVED/unit:8>>].
+ Base = case Head#head.base of
+ ?BASE -> <<0:32>>;
+ FlBase -> <>
+ end,
+ [H1, DigH, MD5, Base | <<0:?RESERVED/unit:8>>].
%% Going through some trouble to avoid creating one single binary for
%% the free lists. If the free lists are huge, binary_to_term and
@@ -1695,8 +1707,8 @@ free_lists_from_file(H, Pos) ->
case catch bin_to_tree([], H, start, FL, -1, []) of
{'EXIT', _} ->
throw({error, {bad_freelists, H#head.filename}});
- Reply ->
- Reply
+ Ftab ->
+ H#head{freelists = Ftab, base = ?BASE}
end.
bin_to_tree(Bin, H, LastPos, Ftab, A0, L) ->
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index b68d83b12d..63767aeda6 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -34,6 +34,8 @@
-define(datadir(Conf), ?config(data_dir, Conf)).
-endif.
+-compile(r13). % OTP-9607
+
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
not_run/1, newly_started/1, basic_v8/1, basic_v9/1,
@@ -53,7 +55,7 @@
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
- otp_8923/1, otp_9282/1]).
+ otp_8923/1, otp_9282/1, otp_9607/1]).
-export([dets_dirty_loop/0]).
@@ -112,7 +114,7 @@ all() ->
many_clients, otp_4906, otp_5402, simultaneous_open,
insert_new, repair_continuation, otp_5487, otp_6206,
otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282]
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_9607]
end.
groups() ->
@@ -554,7 +556,11 @@ dets_dirty_loop() ->
{From, [write, Name, Value]} ->
Ret = dets:insert(Name, Value),
From ! {self(), Ret},
- dets_dirty_loop()
+ dets_dirty_loop();
+ {From, [close, Name]} ->
+ Ret = dets:close(Name),
+ From ! {self(), Ret},
+ dets_dirty_loop()
end.
@@ -3882,10 +3888,91 @@ some_calls(Tab, Config) ->
?line ok = dets:close(T),
file:delete(File).
+otp_9607(doc) ->
+ ["OTP-9607. Test downgrading the slightly changed format."];
+otp_9607(suite) ->
+ [];
+otp_9607(Config) when is_list(Config) ->
+ %% Note: the bug is about almost full tables. The fix of that
+ %% problem is *not* tested here.
+ Version = r13b,
+ case ?t:is_release_available(atom_to_list(Version)) of
+ true ->
+ T = otp_9607,
+ File = filename(T, Config),
+ Key = a,
+ Value = 1,
+ Args = [{file,File}],
+ ?line {ok, T} = dets:open_file(T, Args),
+ ?line ok = dets:insert(T, {Key, Value}),
+ ?line ok = dets:close(T),
+
+ ?line Call = fun(P, A) ->
+ P ! {self(), A},
+ receive
+ {P, Ans} ->
+ Ans
+ after 5000 ->
+ exit(other_process_dead)
+ end
+ end,
+ %% Create a file on the modified format, read the file
+ %% with an emulator that doesn't know about the modified
+ %% format.
+ ?line {ok, Node} = start_node_rel(Version, Version, slave),
+ ?line Pid = rpc:call(Node, erlang, spawn,
+ [?MODULE, dets_dirty_loop, []]),
+ ?line {error,{needs_repair, File}} =
+ Call(Pid, [open, T, Args++[{repair,false}]]),
+ io:format("Expect repair:~n"),
+ ?line {ok, T} = Call(Pid, [open, T, Args]),
+ ?line [{Key,Value}] = Call(Pid, [read, T, Key]),
+ ?line ok = Call(Pid, [close, T]),
+ file:delete(File),
+
+ %% Create a file on the unmodified format. Modify the file
+ %% using an emulator that must not turn the file into the
+ %% modified format. Read the file and make sure it is not
+ %% repaired.
+ ?line {ok, T} = Call(Pid, [open, T, Args]),
+ ?line ok = Call(Pid, [write, T, {Key,Value}]),
+ ?line [{Key,Value}] = Call(Pid, [read, T, Key]),
+ ?line ok = Call(Pid, [close, T]),
+
+ Key2 = b,
+ Value2 = 2,
+
+ ?line {ok, T} = dets:open_file(T, Args),
+ ?line [{Key,Value}] = dets:lookup(T, Key),
+ ?line ok = dets:insert(T, {Key2,Value2}),
+ ?line ok = dets:close(T),
+
+ ?line {ok, T} = Call(Pid, [open, T, Args++[{repair,false}]]),
+ ?line [{Key2,Value2}] = Call(Pid, [read, T, Key2]),
+ ?line ok = Call(Pid, [close, T]),
+
+ ?t:stop_node(Node),
+ file:delete(File),
+ ok;
+ false ->
+ {skipped, "No support for old node"}
+ end.
+
+
+
%%
%% Parts common to several test cases
%%
+start_node_rel(Name, Rel, How) ->
+ Release = [{release, atom_to_list(Rel)}],
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line test_server:start_node(Name, How,
+ [{args,
+ " -kernel net_setuptime 100 "
+ " -pa " ++ Pa},
+ {erl, Release}]).
+
crash(File, Where) ->
crash(File, Where, 10).
--
cgit v1.2.3
From c03ac3db1d90e2b86ac4acfd6f0d09a324be4a42 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 01:44:58 +0200
Subject: @result_code -> @define in dictionary files
The section simply results in generated macros and has nothing
specifically to do with result codes. It's still not documented,
and neither are the macros generated from @enum, since the generated
names are typically so long as to be impractical/unreadable in source.
Better to use numeric values with a comment or define your own
shorter macros as the need arises.
---
lib/diameter/src/app/diameter_gen_base_rfc3588.dia | 2 +-
lib/diameter/src/compiler/diameter_codegen.erl | 4 ++--
lib/diameter/src/compiler/diameter_spec_util.erl | 8 +++++---
3 files changed, 8 insertions(+), 6 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/app/diameter_gen_base_rfc3588.dia b/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
index 4a12e21acd..4c14ee7575 100644
--- a/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
+++ b/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
@@ -340,7 +340,7 @@
GRANT_AND_STORE 2
GRANT_AND_LOSE 3
-@result_code Result-Code
+@define Result-Code
;; 7.1.1. Informational
DIAMETER_MULTI_ROUND_AUTH 1001
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index a33b07a3d3..0fd4a0b301 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.erl
@@ -707,9 +707,9 @@ gen_hrl(Path, Mod, Spec) ->
write("ENUM Macros",
Fd,
m_enums(PREFIX, false, get_value(enums, Spec))),
- write("RESULT CODE Macros",
+ write("DEFINE Macros",
Fd,
- m_enums(PREFIX, false, get_value(result_codes, Spec))),
+ m_enums(PREFIX, false, get_value(defines, Spec))),
lists:foreach(fun({M,Es}) ->
write("ENUM Macros from " ++ atom_to_list(M),
diff --git a/lib/diameter/src/compiler/diameter_spec_util.erl b/lib/diameter/src/compiler/diameter_spec_util.erl
index b60886b678..e9221ed00a 100644
--- a/lib/diameter/src/compiler/diameter_spec_util.erl
+++ b/lib/diameter/src/compiler/diameter_spec_util.erl
@@ -204,9 +204,11 @@ chunk({avp_vendor_id = T, [{number, I}], Body}, Dict) ->
chunk({enum, [N], Str}, Dict) ->
append(enums, {atomize(N), parse_enums(Str)}, Dict);
-%% result_codes -> [{ResultName, [{Value, Name}, ...]}, ...]
-chunk({result_code, [N], Str}, Dict) ->
- append(result_codes, {atomize(N), parse_enums(Str)}, Dict);
+%% defines -> [{DefineName, [{Value, Name}, ...]}, ...]
+chunk({define, [N], Str}, Dict) ->
+ append(defines, {atomize(N), parse_enums(Str)}, Dict);
+chunk({result_code, [_] = N, Str}, Dict) -> %% backwards compatibility
+ chunk({define, N, Str}, Dict);
%% commands -> [{Name, Abbrev}, ...]
chunk({commands = T, [], Body}, Dict) ->
--
cgit v1.2.3
From 945d09cdea615404454b5951cb49472adab29eeb Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 01:55:55 +0200
Subject: Whitespace fixes
---
lib/diameter/bin/diameterc | 2 +-
.../src/app/diameter_gen_base_accounting.dia | 80 ++--
lib/diameter/src/app/diameter_gen_base_rfc3588.dia | 486 ++++++++++-----------
3 files changed, 284 insertions(+), 284 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc
index f5cf3ebc10..e232cdb120 100755
--- a/lib/diameter/bin/diameterc
+++ b/lib/diameter/bin/diameterc
@@ -120,7 +120,7 @@ arg(["-H" | Args], #argv{output = Output} = A) ->
arg(["-d" | Args], #argv{options = Opts, output = Output} = A) ->
arg(Args, A#argv{options = [debug | Opts],
- output = [spec | Output]});
+ output = [spec | Output]});
arg([[$- = M, C, H | T] | Args], A) %% clustered options
when C /= $i, C /= $o ->
diff --git a/lib/diameter/src/app/diameter_gen_base_accounting.dia b/lib/diameter/src/app/diameter_gen_base_accounting.dia
index 64e95dddb5..b76e9f3473 100644
--- a/lib/diameter/src/app/diameter_gen_base_accounting.dia
+++ b/lib/diameter/src/app/diameter_gen_base_accounting.dia
@@ -25,44 +25,44 @@
@messages
- ACR ::= < Diameter Header: 271, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
+ ACR ::= < Diameter Header: 271, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
- ACA ::= < Diameter Header: 271, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Error-Reporting-Host ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ AVP ]
+ ACA ::= < Diameter Header: 271, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Reporting-Host ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
diff --git a/lib/diameter/src/app/diameter_gen_base_rfc3588.dia b/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
index 4c14ee7575..7ec4c613d4 100644
--- a/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
+++ b/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
@@ -76,274 +76,274 @@
@messages
- CER ::= < Diameter Header: 257, REQ >
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Inband-Security-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
- CEA ::= < Diameter Header: 257 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- [ Error-Message ]
- * [ Failed-AVP ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Inband-Security-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
- DPR ::= < Diameter Header: 282, REQ >
- { Origin-Host }
- { Origin-Realm }
- { Disconnect-Cause }
-
- DPA ::= < Diameter Header: 282 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- * [ Failed-AVP ]
-
- DWR ::= < Diameter Header: 280, REQ >
- { Origin-Host }
- { Origin-Realm }
- [ Origin-State-Id ]
-
- DWA ::= < Diameter Header: 280 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- * [ Failed-AVP ]
- [ Origin-State-Id ]
-
- answer-message ::= < Diameter Header: code, ERR [PXY] >
- 0*1< Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Result-Code }
- [ Origin-State-Id ]
- [ Error-Reporting-Host ]
- [ Proxy-Info ]
- * [ AVP ]
-
- RAR ::= < Diameter Header: 258, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Destination-Host }
- { Auth-Application-Id }
- { Re-Auth-Request-Type }
- [ User-Name ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- RAA ::= < Diameter Header: 258, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- * [ Failed-AVP ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
- STR ::= < Diameter Header: 275, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Auth-Application-Id }
- { Termination-Cause }
- [ User-Name ]
- [ Destination-Host ]
- * [ Class ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- STA ::= < Diameter Header: 275, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- * [ Class ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- * [ Failed-AVP ]
- [ Origin-State-Id ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
- ASR ::= < Diameter Header: 274, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Destination-Host }
- { Auth-Application-Id }
- [ User-Name ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- ASA ::= < Diameter Header: 274, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- * [ Failed-AVP ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
- ACR ::= < Diameter Header: 271, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- ACA ::= < Diameter Header: 271, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Error-Reporting-Host ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ AVP ]
+ CER ::= < Diameter Header: 257, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+ CEA ::= < Diameter Header: 257 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ * [ Failed-AVP ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+ DPR ::= < Diameter Header: 282, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ { Disconnect-Cause }
+
+ DPA ::= < Diameter Header: 282 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ * [ Failed-AVP ]
+
+ DWR ::= < Diameter Header: 280, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ [ Origin-State-Id ]
+
+ DWA ::= < Diameter Header: 280 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ * [ Failed-AVP ]
+ [ Origin-State-Id ]
+
+ answer-message ::= < Diameter Header: code, ERR [PXY] >
+ 0*1 < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Result-Code }
+ [ Origin-State-Id ]
+ [ Error-Reporting-Host ]
+ [ Proxy-Info ]
+ * [ AVP ]
+
+ RAR ::= < Diameter Header: 258, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ { Re-Auth-Request-Type }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ RAA ::= < Diameter Header: 258, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ * [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ STR ::= < Diameter Header: 275, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Auth-Application-Id }
+ { Termination-Cause }
+ [ User-Name ]
+ [ Destination-Host ]
+ * [ Class ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ STA ::= < Diameter Header: 275, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ * [ Class ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ * [ Failed-AVP ]
+ [ Origin-State-Id ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ ASR ::= < Diameter Header: 274, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ASA ::= < Diameter Header: 274, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ * [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ ACR ::= < Diameter Header: 271, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ACA ::= < Diameter Header: 271, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Reporting-Host ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
@enum Disconnect-Cause
- REBOOTING 0
- BUSY 1
- DO_NOT_WANT_TO_TALK_TO_YOU 2
+ REBOOTING 0
+ BUSY 1
+ DO_NOT_WANT_TO_TALK_TO_YOU 2
@enum Redirect-Host-Usage
- DONT_CACHE 0
- ALL_SESSION 1
- ALL_REALM 2
- REALM_AND_APPLICATION 3
- ALL_APPLICATION 4
- ALL_HOST 5
- ALL_USER 6
+ DONT_CACHE 0
+ ALL_SESSION 1
+ ALL_REALM 2
+ REALM_AND_APPLICATION 3
+ ALL_APPLICATION 4
+ ALL_HOST 5
+ ALL_USER 6
@enum Auth-Request-Type
- AUTHENTICATE_ONLY 1
- AUTHORIZE_ONLY 2
- AUTHORIZE_AUTHENTICATE 3
+ AUTHENTICATE_ONLY 1
+ AUTHORIZE_ONLY 2
+ AUTHORIZE_AUTHENTICATE 3
@enum Auth-Session-State
- STATE_MAINTAINED 0
- NO_STATE_MAINTAINED 1
+ STATE_MAINTAINED 0
+ NO_STATE_MAINTAINED 1
@enum Re-Auth-Request-Type
- AUTHORIZE_ONLY 0
- AUTHORIZE_AUTHENTICATE 1
+ AUTHORIZE_ONLY 0
+ AUTHORIZE_AUTHENTICATE 1
@enum Termination-Cause
- DIAMETER_LOGOUT 1
- DIAMETER_SERVICE_NOT_PROVIDED 2
- DIAMETER_BAD_ANSWER 3
- DIAMETER_ADMINISTRATIVE 4
- DIAMETER_LINK_BROKEN 5
- DIAMETER_AUTH_EXPIRED 6
- DIAMETER_USER_MOVED 7
- DIAMETER_SESSION_TIMEOUT 8
+ DIAMETER_LOGOUT 1
+ DIAMETER_SERVICE_NOT_PROVIDED 2
+ DIAMETER_BAD_ANSWER 3
+ DIAMETER_ADMINISTRATIVE 4
+ DIAMETER_LINK_BROKEN 5
+ DIAMETER_AUTH_EXPIRED 6
+ DIAMETER_USER_MOVED 7
+ DIAMETER_SESSION_TIMEOUT 8
@enum Session-Server-Failover
- REFUSE_SERVICE 0
- TRY_AGAIN 1
- ALLOW_SERVICE 2
- TRY_AGAIN_ALLOW_SERVICE 3
+ REFUSE_SERVICE 0
+ TRY_AGAIN 1
+ ALLOW_SERVICE 2
+ TRY_AGAIN_ALLOW_SERVICE 3
@enum Accounting-Record-Type
- EVENT_RECORD 1
- START_RECORD 2
- INTERIM_RECORD 3
- STOP_RECORD 4
+ EVENT_RECORD 1
+ START_RECORD 2
+ INTERIM_RECORD 3
+ STOP_RECORD 4
@enum Accounting-Realtime-Required
- DELIVER_AND_GRANT 1
- GRANT_AND_STORE 2
- GRANT_AND_LOSE 3
+ DELIVER_AND_GRANT 1
+ GRANT_AND_STORE 2
+ GRANT_AND_LOSE 3
@define Result-Code
;; 7.1.1. Informational
- DIAMETER_MULTI_ROUND_AUTH 1001
+ DIAMETER_MULTI_ROUND_AUTH 1001
;; 7.1.2. Success
DIAMETER_SUCCESS 2001
@@ -393,16 +393,16 @@
* [ AVP ]
Failed-AVP ::= < AVP Header: 279 >
- 1* {AVP}
+ 1* {AVP}
Experimental-Result ::= < AVP Header: 297 >
- { Vendor-Id }
- { Experimental-Result-Code }
+ { Vendor-Id }
+ { Experimental-Result-Code }
Vendor-Specific-Application-Id ::= < AVP Header: 260 >
- 1* { Vendor-Id }
- [ Auth-Application-Id ]
- [ Acct-Application-Id ]
+ 1* { Vendor-Id }
+ [ Auth-Application-Id ]
+ [ Acct-Application-Id ]
;; The E2E-Sequence AVP is defined in RFC 3588 as Grouped, but
;; there is no definition of the group - only an informal text stating
--
cgit v1.2.3
From 1bc7cbe24f2ae45f6573e4dfd002d6cfc9d4887a Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 02:41:46 +0200
Subject: Move dictionaries into own directory and rename
Generated files are unchanged but the separation will be
especially pleasant when more dictionary files are added
as examples. It is still only the rfc3588 and relay
dictionaries that are known to the diameter implementation.
---
lib/diameter/src/app/Makefile | 59 ++-
.../src/app/diameter_gen_base_accounting.dia | 68 ----
lib/diameter/src/app/diameter_gen_base_rfc3588.dia | 413 --------------------
lib/diameter/src/app/diameter_gen_relay.dia | 24 --
lib/diameter/src/app/modules.mk | 8 +-
lib/diameter/src/dict/base_accounting.dia | 69 ++++
lib/diameter/src/dict/base_rfc3588.dia | 414 +++++++++++++++++++++
lib/diameter/src/dict/relay.dia | 25 ++
8 files changed, 540 insertions(+), 540 deletions(-)
delete mode 100644 lib/diameter/src/app/diameter_gen_base_accounting.dia
delete mode 100644 lib/diameter/src/app/diameter_gen_base_rfc3588.dia
delete mode 100644 lib/diameter/src/app/diameter_gen_relay.dia
create mode 100644 lib/diameter/src/dict/base_accounting.dia
create mode 100644 lib/diameter/src/dict/base_rfc3588.dia
create mode 100644 lib/diameter/src/dict/relay.dia
(limited to 'lib')
diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile
index 96b7736a90..9042bff407 100644
--- a/lib/diameter/src/app/Makefile
+++ b/lib/diameter/src/app/Makefile
@@ -28,8 +28,6 @@ EBIN = ../../ebin
include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
endif
-
-
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
@@ -52,19 +50,10 @@ INCDIR = ../../include
include modules.mk
-diameter_gen_base_accounting.erl: \
- $(EBIN)/diameter_gen_base_rfc3588.beam
-diameter_gen_relay.erl: \
- $(EBIN)/diameter_gen_base_rfc3588.beam
-
-SPEC_MODULES = \
- $(SPEC_FILES:%.dia=%)
-
-SPEC_ERL_FILES = \
- $(SPEC_FILES:%.dia=%.erl)
-
-SPEC_HRL_FILES = \
- $(SPEC_FILES:%.dia=%.hrl)
+DICT_FILES = $(DICTIONARIES:%=../dict/%.dia)
+DICT_MODULES = $(DICTIONARIES:%=diameter_gen_%)
+DICT_ERL_FILES = $(DICT_MODULES:%=%.erl)
+DICT_HRL_FILES = $(DICT_MODULES:%=%.hrl)
MODULES = \
$(RUNTIME_MODULES) \
@@ -72,7 +61,7 @@ MODULES = \
APP_MODULES = \
$(RUNTIME_MODULES) \
- $(SPEC_MODULES)
+ $(DICT_MODULES)
TARGET_MODULES = \
$(APP_MODULES) \
@@ -118,7 +107,7 @@ debug:
opt: $(TARGET_FILES)
clean:
- rm -f $(TARGET_FILES) $(SPEC_ERL_FILES) $(SPEC_HRL_FILES)
+ rm -f $(TARGET_FILES) $(DICT_ERL_FILES) $(DICT_HRL_FILES)
rm -f $(APP_TARGET) $(APPUP_TARGET)
rm -f errs core *~ diameter_gen_*.forms diameter_gen_*.spec
rm -f depend.mk
@@ -127,8 +116,8 @@ docs:
info:
@echo ""
- @echo "SPEC_FILES = $(FILES)"
- @echo "MODULES = $(MODULES)"
+ @echo "DICTIONARIES = $(DICTIONARIES)"
+ @echo "MODULES = $(MODULES)"
@echo ""
@echo "EXTERNAL_HRL_FILES = $(EXTERNAL_HRL_FILES)"
@echo "INTERNAL_HRL_FILES = $(INTERNAL_HRL_FILES)"
@@ -140,6 +129,10 @@ info:
# Special Build Targets
# ----------------------------------------------------
+# erl/hrl from application spec
+diameter_gen_%.erl diameter_gen_%.hrl: ../dict/%.dia
+ ../../bin/diameterc -i $(EBIN) $<
+
# Generate the app file and then modules into in. This shouldn't know
# about ../transport but good enough for now.
$(APP_TARGET): $(APP_SRC) \
@@ -158,13 +151,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk
compiler:
$(MAKE) -C ../$@
-app: $(APP_TARGET) $(APPUP_TARGET)
-
-# erl/hrl from application spec
-diameter_gen_%.erl diameter_gen_%.hrl: diameter_gen_%.dia
- ../../bin/diameterc -i $(EBIN) -o $(@D) $<
+app: $(APP_TARGET) $(APPUP_TARGET)
+dict: $(DICT_ERL_FILES)
-$(SPEC_MODULES:%=$(EBIN)/%.$(EMULATOR)): $(EBIN)/diameter_exprecs.$(EMULATOR)
+$(DICT_MODULES:%=$(EBIN)/%.$(EMULATOR)): $(EBIN)/diameter_exprecs.$(EMULATOR)
# ----------------------------------------------------
# Release Target
@@ -180,14 +170,15 @@ release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/bin
$(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DIR) $(RELSYSDIR)/src/app
+ $(INSTALL_DIR) $(RELSYSDIR)/src/dict
$(INSTALL_DIR) $(RELSYSDIR)/include
$(INSTALL_DIR) $(RELSYSDIR)/examples
$(INSTALL_SCRIPT) $(ESCRIPT_FILES) $(RELSYSDIR)/bin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(MODULES:%=%.erl) $(SPEC_ERL_FILES) $(RELSYSDIR)/src/app
- $(INSTALL_DATA) $(SPEC_FILES) $(RELSYSDIR)/src/app
+ $(INSTALL_DATA) $(MODULES:%=%.erl) $(DICT_ERL_FILES) $(RELSYSDIR)/src/app
+ $(INSTALL_DATA) $(DICT_FILES) $(RELSYSDIR)/src/dict
$(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/app
- $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(SPEC_HRL_FILES) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(DICT_HRL_FILES) $(RELSYSDIR)/include
$(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
release_docs_spec:
@@ -196,7 +187,12 @@ release_docs_spec:
# Dependencies
# ----------------------------------------------------
-$(SPEC_FILES:%.dia=$(EBIN)/%.$(EMULATOR)): \
+diameter_gen_base_accounting.erl diameter_gen_relay.erl: \
+ $(EBIN)/diameter_gen_base_rfc3588.beam
+
+$(DICT_ERL_FILES) $(DICT_HRL_FILES): compiler
+
+$(DICT_ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR)): \
$(DIAMETER_TOP)/include/diameter.hrl \
$(DIAMETER_TOP)/include/diameter_gen.hrl
@@ -214,5 +210,6 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
-include depend.mk
-.PRECIOUS: $(SPEC_ERL_FILES) $(SPEC_HRL_FILES)
-.PHONY: app clean debug depend info opt compiler release_spec release_docs_spec
+.PRECIOUS: $(DICT_ERL_FILES) $(DICT_HRL_FILES)
+.PHONY: app clean debug depend info opt compiler dict
+.PHONY: release_spec release_docs_spec
diff --git a/lib/diameter/src/app/diameter_gen_base_accounting.dia b/lib/diameter/src/app/diameter_gen_base_accounting.dia
deleted file mode 100644
index b76e9f3473..0000000000
--- a/lib/diameter/src/app/diameter_gen_base_accounting.dia
+++ /dev/null
@@ -1,68 +0,0 @@
-;;
-;; %CopyrightBegin%
-;;
-;; Copyright Ericsson AB 2010-2011. 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%
-;;
-
-@id 3
-@prefix diameter_base_accounting
-@vendor 0 IETF
-
-@inherits diameter_gen_base_rfc3588
-
-@messages
-
- ACR ::= < Diameter Header: 271, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- ACA ::= < Diameter Header: 271, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Error-Reporting-Host ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ AVP ]
diff --git a/lib/diameter/src/app/diameter_gen_base_rfc3588.dia b/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
deleted file mode 100644
index 7ec4c613d4..0000000000
--- a/lib/diameter/src/app/diameter_gen_base_rfc3588.dia
+++ /dev/null
@@ -1,413 +0,0 @@
-;;
-;; %CopyrightBegin%
-;;
-;; Copyright Ericsson AB 2010-2011. 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%
-;;
-
-@id 0
-@prefix diameter_base
-@vendor 0 IETF
-
-@avp_types
-
- Acct-Interim-Interval 85 Unsigned32 M
- Accounting-Realtime-Required 483 Enumerated M
- Acct-Multi-Session-Id 50 UTF8String M
- Accounting-Record-Number 485 Unsigned32 M
- Accounting-Record-Type 480 Enumerated M
- Acct-Session-Id 44 OctetString M
- Accounting-Sub-Session-Id 287 Unsigned64 M
- Acct-Application-Id 259 Unsigned32 M
- Auth-Application-Id 258 Unsigned32 M
- Auth-Request-Type 274 Enumerated M
- Authorization-Lifetime 291 Unsigned32 M
- Auth-Grace-Period 276 Unsigned32 M
- Auth-Session-State 277 Enumerated M
- Re-Auth-Request-Type 285 Enumerated M
- Class 25 OctetString M
- Destination-Host 293 DiamIdent M
- Destination-Realm 283 DiamIdent M
- Disconnect-Cause 273 Enumerated M
- E2E-Sequence 300 Grouped M
- Error-Message 281 UTF8String -
- Error-Reporting-Host 294 DiamIdent -
- Event-Timestamp 55 Time M
- Experimental-Result 297 Grouped M
- Experimental-Result-Code 298 Unsigned32 M
- Failed-AVP 279 Grouped M
- Firmware-Revision 267 Unsigned32 -
- Host-IP-Address 257 Address M
- Inband-Security-Id 299 Unsigned32 M
- Multi-Round-Time-Out 272 Unsigned32 M
- Origin-Host 264 DiamIdent M
- Origin-Realm 296 DiamIdent M
- Origin-State-Id 278 Unsigned32 M
- Product-Name 269 UTF8String -
- Proxy-Host 280 DiamIdent M
- Proxy-Info 284 Grouped M
- Proxy-State 33 OctetString M
- Redirect-Host 292 DiamURI M
- Redirect-Host-Usage 261 Enumerated M
- Redirect-Max-Cache-Time 262 Unsigned32 M
- Result-Code 268 Unsigned32 M
- Route-Record 282 DiamIdent M
- Session-Id 263 UTF8String M
- Session-Timeout 27 Unsigned32 M
- Session-Binding 270 Unsigned32 M
- Session-Server-Failover 271 Enumerated M
- Supported-Vendor-Id 265 Unsigned32 M
- Termination-Cause 295 Enumerated M
- User-Name 1 UTF8String M
- Vendor-Id 266 Unsigned32 M
- Vendor-Specific-Application-Id 260 Grouped M
-
-@messages
-
- CER ::= < Diameter Header: 257, REQ >
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Inband-Security-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
- CEA ::= < Diameter Header: 257 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- [ Error-Message ]
- * [ Failed-AVP ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Inband-Security-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
- DPR ::= < Diameter Header: 282, REQ >
- { Origin-Host }
- { Origin-Realm }
- { Disconnect-Cause }
-
- DPA ::= < Diameter Header: 282 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- * [ Failed-AVP ]
-
- DWR ::= < Diameter Header: 280, REQ >
- { Origin-Host }
- { Origin-Realm }
- [ Origin-State-Id ]
-
- DWA ::= < Diameter Header: 280 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- * [ Failed-AVP ]
- [ Origin-State-Id ]
-
- answer-message ::= < Diameter Header: code, ERR [PXY] >
- 0*1 < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Result-Code }
- [ Origin-State-Id ]
- [ Error-Reporting-Host ]
- [ Proxy-Info ]
- * [ AVP ]
-
- RAR ::= < Diameter Header: 258, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Destination-Host }
- { Auth-Application-Id }
- { Re-Auth-Request-Type }
- [ User-Name ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- RAA ::= < Diameter Header: 258, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- * [ Failed-AVP ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
- STR ::= < Diameter Header: 275, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Auth-Application-Id }
- { Termination-Cause }
- [ User-Name ]
- [ Destination-Host ]
- * [ Class ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- STA ::= < Diameter Header: 275, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- * [ Class ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- * [ Failed-AVP ]
- [ Origin-State-Id ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
- ASR ::= < Diameter Header: 274, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Destination-Host }
- { Auth-Application-Id }
- [ User-Name ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- ASA ::= < Diameter Header: 274, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- * [ Failed-AVP ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
- ACR ::= < Diameter Header: 271, REQ, PXY >
- < Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
- ACA ::= < Diameter Header: 271, PXY >
- < Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Error-Reporting-Host ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ AVP ]
-
-@enum Disconnect-Cause
-
- REBOOTING 0
- BUSY 1
- DO_NOT_WANT_TO_TALK_TO_YOU 2
-
-@enum Redirect-Host-Usage
-
- DONT_CACHE 0
- ALL_SESSION 1
- ALL_REALM 2
- REALM_AND_APPLICATION 3
- ALL_APPLICATION 4
- ALL_HOST 5
- ALL_USER 6
-
-@enum Auth-Request-Type
-
- AUTHENTICATE_ONLY 1
- AUTHORIZE_ONLY 2
- AUTHORIZE_AUTHENTICATE 3
-
-@enum Auth-Session-State
-
- STATE_MAINTAINED 0
- NO_STATE_MAINTAINED 1
-
-@enum Re-Auth-Request-Type
-
- AUTHORIZE_ONLY 0
- AUTHORIZE_AUTHENTICATE 1
-
-@enum Termination-Cause
-
- DIAMETER_LOGOUT 1
- DIAMETER_SERVICE_NOT_PROVIDED 2
- DIAMETER_BAD_ANSWER 3
- DIAMETER_ADMINISTRATIVE 4
- DIAMETER_LINK_BROKEN 5
- DIAMETER_AUTH_EXPIRED 6
- DIAMETER_USER_MOVED 7
- DIAMETER_SESSION_TIMEOUT 8
-
-@enum Session-Server-Failover
-
- REFUSE_SERVICE 0
- TRY_AGAIN 1
- ALLOW_SERVICE 2
- TRY_AGAIN_ALLOW_SERVICE 3
-
-@enum Accounting-Record-Type
-
- EVENT_RECORD 1
- START_RECORD 2
- INTERIM_RECORD 3
- STOP_RECORD 4
-
-@enum Accounting-Realtime-Required
-
- DELIVER_AND_GRANT 1
- GRANT_AND_STORE 2
- GRANT_AND_LOSE 3
-
-@define Result-Code
-
-;; 7.1.1. Informational
- DIAMETER_MULTI_ROUND_AUTH 1001
-
-;; 7.1.2. Success
- DIAMETER_SUCCESS 2001
- DIAMETER_LIMITED_SUCCESS 2002
-
-;; 7.1.3. Protocol Errors
- DIAMETER_COMMAND_UNSUPPORTED 3001
- DIAMETER_UNABLE_TO_DELIVER 3002
- DIAMETER_REALM_NOT_SERVED 3003
- DIAMETER_TOO_BUSY 3004
- DIAMETER_LOOP_DETECTED 3005
- DIAMETER_REDIRECT_INDICATION 3006
- DIAMETER_APPLICATION_UNSUPPORTED 3007
- DIAMETER_INVALID_HDR_BITS 3008
- DIAMETER_INVALID_AVP_BITS 3009
- DIAMETER_UNKNOWN_PEER 3010
-
-;; 7.1.4. Transient Failures
- DIAMETER_AUTHENTICATION_REJECTED 4001
- DIAMETER_OUT_OF_SPACE 4002
- ELECTION_LOST 4003
-
-;; 7.1.5. Permanent Failures
- DIAMETER_AVP_UNSUPPORTED 5001
- DIAMETER_UNKNOWN_SESSION_ID 5002
- DIAMETER_AUTHORIZATION_REJECTED 5003
- DIAMETER_INVALID_AVP_VALUE 5004
- DIAMETER_MISSING_AVP 5005
- DIAMETER_RESOURCES_EXCEEDED 5006
- DIAMETER_CONTRADICTING_AVPS 5007
- DIAMETER_AVP_NOT_ALLOWED 5008
- DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
- DIAMETER_NO_COMMON_APPLICATION 5010
- DIAMETER_UNSUPPORTED_VERSION 5011
- DIAMETER_UNABLE_TO_COMPLY 5012
- DIAMETER_INVALID_BIT_IN_HEADER 5013
- DIAMETER_INVALID_AVP_LENGTH 5014
- DIAMETER_INVALID_MESSAGE_LENGTH 5015
- DIAMETER_INVALID_AVP_BIT_COMBO 5016
- DIAMETER_NO_COMMON_SECURITY 5017
-
-@grouped
-
- Proxy-Info ::= < AVP Header: 284 >
- { Proxy-Host }
- { Proxy-State }
- * [ AVP ]
-
- Failed-AVP ::= < AVP Header: 279 >
- 1* {AVP}
-
- Experimental-Result ::= < AVP Header: 297 >
- { Vendor-Id }
- { Experimental-Result-Code }
-
- Vendor-Specific-Application-Id ::= < AVP Header: 260 >
- 1* { Vendor-Id }
- [ Auth-Application-Id ]
- [ Acct-Application-Id ]
-
-;; The E2E-Sequence AVP is defined in RFC 3588 as Grouped, but
-;; there is no definition of the group - only an informal text stating
-;; that there should be a nonce (an OctetString) and a counter
-;; (integer)
-;;
- E2E-Sequence ::=
- 2* { AVP }
diff --git a/lib/diameter/src/app/diameter_gen_relay.dia b/lib/diameter/src/app/diameter_gen_relay.dia
deleted file mode 100644
index d86446e368..0000000000
--- a/lib/diameter/src/app/diameter_gen_relay.dia
+++ /dev/null
@@ -1,24 +0,0 @@
-;;
-;; %CopyrightBegin%
-;;
-;; Copyright Ericsson AB 2010-2011. 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%
-;;
-
-@id 0xFFFFFFFF
-@prefix diameter_relay
-@vendor 0 IETF
-
-@inherits diameter_gen_base_rfc3588
diff --git a/lib/diameter/src/app/modules.mk b/lib/diameter/src/app/modules.mk
index c133e6f64e..ea4c58bfd7 100644
--- a/lib/diameter/src/app/modules.mk
+++ b/lib/diameter/src/app/modules.mk
@@ -17,10 +17,10 @@
#
# %CopyrightEnd%
-SPEC_FILES = \
- diameter_gen_base_rfc3588.dia \
- diameter_gen_base_accounting.dia \
- diameter_gen_relay.dia
+DICTIONARIES = \
+ base_rfc3588 \
+ base_accounting \
+ relay
RUNTIME_MODULES = \
diameter \
diff --git a/lib/diameter/src/dict/base_accounting.dia b/lib/diameter/src/dict/base_accounting.dia
new file mode 100644
index 0000000000..ced324078c
--- /dev/null
+++ b/lib/diameter/src/dict/base_accounting.dia
@@ -0,0 +1,69 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2010-2011. 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%
+;;
+
+@id 3
+@name diameter_gen_base_accounting
+@prefix diameter_base_accounting
+@vendor 0 IETF
+
+@inherits diameter_gen_base_rfc3588
+
+@messages
+
+ ACR ::= < Diameter Header: 271, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ACA ::= < Diameter Header: 271, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Reporting-Host ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
diff --git a/lib/diameter/src/dict/base_rfc3588.dia b/lib/diameter/src/dict/base_rfc3588.dia
new file mode 100644
index 0000000000..f7a0b717cd
--- /dev/null
+++ b/lib/diameter/src/dict/base_rfc3588.dia
@@ -0,0 +1,414 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2010-2011. 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%
+;;
+
+@id 0
+@name diameter_gen_base_rfc3588
+@prefix diameter_base
+@vendor 0 IETF
+
+@avp_types
+
+ Acct-Interim-Interval 85 Unsigned32 M
+ Accounting-Realtime-Required 483 Enumerated M
+ Acct-Multi-Session-Id 50 UTF8String M
+ Accounting-Record-Number 485 Unsigned32 M
+ Accounting-Record-Type 480 Enumerated M
+ Acct-Session-Id 44 OctetString M
+ Accounting-Sub-Session-Id 287 Unsigned64 M
+ Acct-Application-Id 259 Unsigned32 M
+ Auth-Application-Id 258 Unsigned32 M
+ Auth-Request-Type 274 Enumerated M
+ Authorization-Lifetime 291 Unsigned32 M
+ Auth-Grace-Period 276 Unsigned32 M
+ Auth-Session-State 277 Enumerated M
+ Re-Auth-Request-Type 285 Enumerated M
+ Class 25 OctetString M
+ Destination-Host 293 DiamIdent M
+ Destination-Realm 283 DiamIdent M
+ Disconnect-Cause 273 Enumerated M
+ E2E-Sequence 300 Grouped M
+ Error-Message 281 UTF8String -
+ Error-Reporting-Host 294 DiamIdent -
+ Event-Timestamp 55 Time M
+ Experimental-Result 297 Grouped M
+ Experimental-Result-Code 298 Unsigned32 M
+ Failed-AVP 279 Grouped M
+ Firmware-Revision 267 Unsigned32 -
+ Host-IP-Address 257 Address M
+ Inband-Security-Id 299 Unsigned32 M
+ Multi-Round-Time-Out 272 Unsigned32 M
+ Origin-Host 264 DiamIdent M
+ Origin-Realm 296 DiamIdent M
+ Origin-State-Id 278 Unsigned32 M
+ Product-Name 269 UTF8String -
+ Proxy-Host 280 DiamIdent M
+ Proxy-Info 284 Grouped M
+ Proxy-State 33 OctetString M
+ Redirect-Host 292 DiamURI M
+ Redirect-Host-Usage 261 Enumerated M
+ Redirect-Max-Cache-Time 262 Unsigned32 M
+ Result-Code 268 Unsigned32 M
+ Route-Record 282 DiamIdent M
+ Session-Id 263 UTF8String M
+ Session-Timeout 27 Unsigned32 M
+ Session-Binding 270 Unsigned32 M
+ Session-Server-Failover 271 Enumerated M
+ Supported-Vendor-Id 265 Unsigned32 M
+ Termination-Cause 295 Enumerated M
+ User-Name 1 UTF8String M
+ Vendor-Id 266 Unsigned32 M
+ Vendor-Specific-Application-Id 260 Grouped M
+
+@messages
+
+ CER ::= < Diameter Header: 257, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+ CEA ::= < Diameter Header: 257 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ * [ Failed-AVP ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+ DPR ::= < Diameter Header: 282, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ { Disconnect-Cause }
+
+ DPA ::= < Diameter Header: 282 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ * [ Failed-AVP ]
+
+ DWR ::= < Diameter Header: 280, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ [ Origin-State-Id ]
+
+ DWA ::= < Diameter Header: 280 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ * [ Failed-AVP ]
+ [ Origin-State-Id ]
+
+ answer-message ::= < Diameter Header: code, ERR [PXY] >
+ 0*1 < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Result-Code }
+ [ Origin-State-Id ]
+ [ Error-Reporting-Host ]
+ [ Proxy-Info ]
+ * [ AVP ]
+
+ RAR ::= < Diameter Header: 258, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ { Re-Auth-Request-Type }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ RAA ::= < Diameter Header: 258, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ * [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ STR ::= < Diameter Header: 275, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Auth-Application-Id }
+ { Termination-Cause }
+ [ User-Name ]
+ [ Destination-Host ]
+ * [ Class ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ STA ::= < Diameter Header: 275, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ * [ Class ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ * [ Failed-AVP ]
+ [ Origin-State-Id ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ ASR ::= < Diameter Header: 274, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ASA ::= < Diameter Header: 274, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ * [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ ACR ::= < Diameter Header: 271, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ACA ::= < Diameter Header: 271, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Reporting-Host ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+@enum Disconnect-Cause
+
+ REBOOTING 0
+ BUSY 1
+ DO_NOT_WANT_TO_TALK_TO_YOU 2
+
+@enum Redirect-Host-Usage
+
+ DONT_CACHE 0
+ ALL_SESSION 1
+ ALL_REALM 2
+ REALM_AND_APPLICATION 3
+ ALL_APPLICATION 4
+ ALL_HOST 5
+ ALL_USER 6
+
+@enum Auth-Request-Type
+
+ AUTHENTICATE_ONLY 1
+ AUTHORIZE_ONLY 2
+ AUTHORIZE_AUTHENTICATE 3
+
+@enum Auth-Session-State
+
+ STATE_MAINTAINED 0
+ NO_STATE_MAINTAINED 1
+
+@enum Re-Auth-Request-Type
+
+ AUTHORIZE_ONLY 0
+ AUTHORIZE_AUTHENTICATE 1
+
+@enum Termination-Cause
+
+ DIAMETER_LOGOUT 1
+ DIAMETER_SERVICE_NOT_PROVIDED 2
+ DIAMETER_BAD_ANSWER 3
+ DIAMETER_ADMINISTRATIVE 4
+ DIAMETER_LINK_BROKEN 5
+ DIAMETER_AUTH_EXPIRED 6
+ DIAMETER_USER_MOVED 7
+ DIAMETER_SESSION_TIMEOUT 8
+
+@enum Session-Server-Failover
+
+ REFUSE_SERVICE 0
+ TRY_AGAIN 1
+ ALLOW_SERVICE 2
+ TRY_AGAIN_ALLOW_SERVICE 3
+
+@enum Accounting-Record-Type
+
+ EVENT_RECORD 1
+ START_RECORD 2
+ INTERIM_RECORD 3
+ STOP_RECORD 4
+
+@enum Accounting-Realtime-Required
+
+ DELIVER_AND_GRANT 1
+ GRANT_AND_STORE 2
+ GRANT_AND_LOSE 3
+
+@define Result-Code
+
+;; 7.1.1. Informational
+ DIAMETER_MULTI_ROUND_AUTH 1001
+
+;; 7.1.2. Success
+ DIAMETER_SUCCESS 2001
+ DIAMETER_LIMITED_SUCCESS 2002
+
+;; 7.1.3. Protocol Errors
+ DIAMETER_COMMAND_UNSUPPORTED 3001
+ DIAMETER_UNABLE_TO_DELIVER 3002
+ DIAMETER_REALM_NOT_SERVED 3003
+ DIAMETER_TOO_BUSY 3004
+ DIAMETER_LOOP_DETECTED 3005
+ DIAMETER_REDIRECT_INDICATION 3006
+ DIAMETER_APPLICATION_UNSUPPORTED 3007
+ DIAMETER_INVALID_HDR_BITS 3008
+ DIAMETER_INVALID_AVP_BITS 3009
+ DIAMETER_UNKNOWN_PEER 3010
+
+;; 7.1.4. Transient Failures
+ DIAMETER_AUTHENTICATION_REJECTED 4001
+ DIAMETER_OUT_OF_SPACE 4002
+ ELECTION_LOST 4003
+
+;; 7.1.5. Permanent Failures
+ DIAMETER_AVP_UNSUPPORTED 5001
+ DIAMETER_UNKNOWN_SESSION_ID 5002
+ DIAMETER_AUTHORIZATION_REJECTED 5003
+ DIAMETER_INVALID_AVP_VALUE 5004
+ DIAMETER_MISSING_AVP 5005
+ DIAMETER_RESOURCES_EXCEEDED 5006
+ DIAMETER_CONTRADICTING_AVPS 5007
+ DIAMETER_AVP_NOT_ALLOWED 5008
+ DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
+ DIAMETER_NO_COMMON_APPLICATION 5010
+ DIAMETER_UNSUPPORTED_VERSION 5011
+ DIAMETER_UNABLE_TO_COMPLY 5012
+ DIAMETER_INVALID_BIT_IN_HEADER 5013
+ DIAMETER_INVALID_AVP_LENGTH 5014
+ DIAMETER_INVALID_MESSAGE_LENGTH 5015
+ DIAMETER_INVALID_AVP_BIT_COMBO 5016
+ DIAMETER_NO_COMMON_SECURITY 5017
+
+@grouped
+
+ Proxy-Info ::= < AVP Header: 284 >
+ { Proxy-Host }
+ { Proxy-State }
+ * [ AVP ]
+
+ Failed-AVP ::= < AVP Header: 279 >
+ 1* {AVP}
+
+ Experimental-Result ::= < AVP Header: 297 >
+ { Vendor-Id }
+ { Experimental-Result-Code }
+
+ Vendor-Specific-Application-Id ::= < AVP Header: 260 >
+ 1* { Vendor-Id }
+ [ Auth-Application-Id ]
+ [ Acct-Application-Id ]
+
+;; The E2E-Sequence AVP is defined in RFC 3588 as Grouped, but
+;; there is no definition of the group - only an informal text stating
+;; that there should be a nonce (an OctetString) and a counter
+;; (integer)
+;;
+ E2E-Sequence ::=
+ 2* { AVP }
diff --git a/lib/diameter/src/dict/relay.dia b/lib/diameter/src/dict/relay.dia
new file mode 100644
index 0000000000..c22293209b
--- /dev/null
+++ b/lib/diameter/src/dict/relay.dia
@@ -0,0 +1,25 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2010-2011. 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%
+;;
+
+@id 0xFFFFFFFF
+@name diameter_gen_relay
+@prefix diameter_relay
+@vendor 0 IETF
+
+@inherits diameter_gen_base_rfc3588
--
cgit v1.2.3
From d8ec8f4f35707581fa55f4e97fc8264b7adf0cab Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 12:19:14 +0200
Subject: Dependency fix
Has to follow the release_targets include for make not to
think that a misspelled dictionary is up to date just because
the exprecs dependency is.
---
lib/diameter/src/app/Makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile
index 9042bff407..b2efc42a69 100644
--- a/lib/diameter/src/app/Makefile
+++ b/lib/diameter/src/app/Makefile
@@ -154,8 +154,6 @@ compiler:
app: $(APP_TARGET) $(APPUP_TARGET)
dict: $(DICT_ERL_FILES)
-$(DICT_MODULES:%=$(EBIN)/%.$(EMULATOR)): $(EBIN)/diameter_exprecs.$(EMULATOR)
-
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -187,6 +185,8 @@ release_docs_spec:
# Dependencies
# ----------------------------------------------------
+$(DICT_MODULES:%=$(EBIN)/%.$(EMULATOR)): $(EBIN)/diameter_exprecs.$(EMULATOR)
+
diameter_gen_base_accounting.erl diameter_gen_relay.erl: \
$(EBIN)/diameter_gen_base_rfc3588.beam
--
cgit v1.2.3
From 1327c7c7d098a7593e5458bc5c06fd9459e73de3 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 10:01:22 +0200
Subject: Allow @name/@prefix to be set with diameterc
---
lib/diameter/bin/diameterc | 28 ++++++++++++++++--------
lib/diameter/src/compiler/diameter_spec_util.erl | 19 +++++++++++-----
2 files changed, 33 insertions(+), 14 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc
index e232cdb120..c4dbd0060f 100755
--- a/lib/diameter/bin/diameterc
+++ b/lib/diameter/bin/diameterc
@@ -33,19 +33,23 @@
usage() ->
io:format(
- "~w [options] file~n"
+ "~w [options] dict~n"
"~n"
" Compile a diameter dictionary file (.dia) to Erlang source (.erl)~n"
" and/or header (.hrl) files.~n"
"~n"
" options:~n"
- " -h = print this message~n"
- " -v = verbose output~n"
- " -o dir = set the output directory (default .)~n"
- " -i dir = set an include directory for inherited beams~n"
- " -E = no .erl output~n"
- " -H = no .hrl output~n"
- " -d = write intermediate files (.spec and .forms)~n",
+ "~n"
+ " --name name = set @name~n"
+ " --prefix prefix = set @prefix~n"
+ "~n"
+ " -h = print this message~n"
+ " -v = verbose output~n"
+ " -o dir = set the output directory (default .)~n"
+ " -i dir = set an include directory for inherited beams~n"
+ " -E = no .erl output~n"
+ " -H = no .hrl output~n"
+ " -d = write intermediate files (.spec and .forms)~n",
[?MODULE]).
main(Args) ->
@@ -112,6 +116,12 @@ arg(["-i", Dir | Args], #argv{options = Opts} = A) ->
true = dir_exists(Dir),
arg(Args, A#argv{options = Opts ++ [{include, Dir}]});
+arg(["--name", Name | Args], #argv{options = Opts} = A) ->
+ arg(Args, A#argv{options = [{name, Name} | Opts]});
+
+arg(["--prefix", Name | Args], #argv{options = Opts} = A) ->
+ arg(Args, A#argv{options = [{prefix, Name} | Opts]});
+
arg(["-E" | Args], #argv{output = Output} = A) ->
arg(Args, A#argv{output = lists:delete(erl, Output)});
@@ -123,7 +133,7 @@ arg(["-d" | Args], #argv{options = Opts, output = Output} = A) ->
output = [spec | Output]});
arg([[$- = M, C, H | T] | Args], A) %% clustered options
- when C /= $i, C /= $o ->
+ when C /= $i, C /= $o, C /= $- ->
arg([[M,C], [M,H|T] | Args], A);
arg([File], A) ->
diff --git a/lib/diameter/src/compiler/diameter_spec_util.erl b/lib/diameter/src/compiler/diameter_spec_util.erl
index e9221ed00a..e62ba05427 100644
--- a/lib/diameter/src/compiler/diameter_spec_util.erl
+++ b/lib/diameter/src/compiler/diameter_spec_util.erl
@@ -34,19 +34,28 @@
%%
%% Output: orddict()
-parse(Path, Options) ->
- put({?MODULE, verbose}, lists:member(verbose, Options)),
+parse(Path, Opts) ->
+ put({?MODULE, verbose}, lists:member(verbose, Opts)),
{ok, B} = file:read_file(Path),
Chunks = chunk(B),
- Spec = make_spec(Chunks),
+ Spec = reset(make_spec(Chunks), Opts, [name, prefix]),
true = groups_defined(Spec), %% sanity checks
true = customs_defined(Spec), %%
- Full = import_enums(import_groups(import_avps(insert_codes(Spec),
- Options))),
+ Full = import_enums(import_groups(import_avps(insert_codes(Spec), Opts))),
true = enums_defined(Full), %% sanity checks
true = v_flags_set(Spec),
Full.
+reset(Spec, Opts, Keys) ->
+ lists:foldl(fun(K,S) -> reset(lists:keyfind(K, 1, Opts), S) end,
+ Spec,
+ Keys).
+
+reset({Key, Str}, Spec) ->
+ orddict:store(Key, ?ATOM(Str), Spec);
+reset(_, Spec) ->
+ Spec.
+
%% Optional reports when running verbosely.
report(What, Data) ->
report(get({?MODULE, verbose}), What, Data).
--
cgit v1.2.3
From 7352ec2c7d3f52a44b8bc18642dadfdc160209a5 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 14:59:27 +0200
Subject: Allow @inherits to be set/cleared with diameterc
This is to enable dictionaries compiled with --name/--prefix
to be inherited using --inherits.
---
lib/diameter/bin/diameterc | 8 ++++++--
lib/diameter/src/compiler/diameter_spec_util.erl | 18 ++++++++++++++----
2 files changed, 20 insertions(+), 6 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc
index c4dbd0060f..8e0eda10dd 100755
--- a/lib/diameter/bin/diameterc
+++ b/lib/diameter/bin/diameterc
@@ -40,8 +40,9 @@ usage() ->
"~n"
" options:~n"
"~n"
- " --name name = set @name~n"
- " --prefix prefix = set @prefix~n"
+ " --name name = set @name~n"
+ " --prefix prefix = set @prefix~n"
+ " --inherits dict|- = set/clear @inherits~n"
"~n"
" -h = print this message~n"
" -v = verbose output~n"
@@ -122,6 +123,9 @@ arg(["--name", Name | Args], #argv{options = Opts} = A) ->
arg(["--prefix", Name | Args], #argv{options = Opts} = A) ->
arg(Args, A#argv{options = [{prefix, Name} | Opts]});
+arg(["--inherits", Dict | Args], #argv{options = Opts} = A) ->
+ arg(Args, A#argv{options = Opts ++ [{inherits, Dict}]});
+
arg(["-E" | Args], #argv{output = Output} = A) ->
arg(Args, A#argv{output = lists:delete(erl, Output)});
diff --git a/lib/diameter/src/compiler/diameter_spec_util.erl b/lib/diameter/src/compiler/diameter_spec_util.erl
index e62ba05427..62536bf06d 100644
--- a/lib/diameter/src/compiler/diameter_spec_util.erl
+++ b/lib/diameter/src/compiler/diameter_spec_util.erl
@@ -38,7 +38,7 @@ parse(Path, Opts) ->
put({?MODULE, verbose}, lists:member(verbose, Opts)),
{ok, B} = file:read_file(Path),
Chunks = chunk(B),
- Spec = reset(make_spec(Chunks), Opts, [name, prefix]),
+ Spec = reset(make_spec(Chunks), Opts, [name, prefix, inherits]),
true = groups_defined(Spec), %% sanity checks
true = customs_defined(Spec), %%
Full = import_enums(import_groups(import_avps(insert_codes(Spec), Opts))),
@@ -47,12 +47,22 @@ parse(Path, Opts) ->
Full.
reset(Spec, Opts, Keys) ->
- lists:foldl(fun(K,S) -> reset(lists:keyfind(K, 1, Opts), S) end,
+ lists:foldl(fun(K,S) ->
+ reset([{A,?ATOM(V)} || {A,V} <- Opts, A == K], S)
+ end,
Spec,
Keys).
-reset({Key, Str}, Spec) ->
- orddict:store(Key, ?ATOM(Str), Spec);
+reset(L, Spec)
+ when is_list(L) ->
+ lists:foldl(fun reset/2, Spec, L);
+
+reset({inherits = Key, '-'}, Spec) ->
+ orddict:erase(Key, Spec);
+reset({inherits = Key, Dict}, Spec) ->
+ orddict:append(Key, Dict, Spec);
+reset({Key, Atom}, Spec) ->
+ orddict:store(Key, Atom, Spec);
reset(_, Spec) ->
Spec.
--
cgit v1.2.3
From dcfb32c0a47a8e87cb475d7304bc5d7b407e5f0e Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 15:23:34 +0200
Subject: Don't require -i directory to exist
---
lib/diameter/bin/diameterc | 1 -
1 file changed, 1 deletion(-)
(limited to 'lib')
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc
index 8e0eda10dd..c0e83ea1a4 100755
--- a/lib/diameter/bin/diameterc
+++ b/lib/diameter/bin/diameterc
@@ -114,7 +114,6 @@ arg(["-o", Dir | Args], #argv{options = Opts} = A) ->
arg(Args, A#argv{options = [{outdir, Dir} | Opts]});
arg(["-i", Dir | Args], #argv{options = Opts} = A) ->
- true = dir_exists(Dir),
arg(Args, A#argv{options = Opts ++ [{include, Dir}]});
arg(["--name", Name | Args], #argv{options = Opts} = A) ->
--
cgit v1.2.3
From fa5503c2faa47cfdeb006fe6a17b4aea6acef771 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 15:48:04 +0200
Subject: Update documentation
---
lib/diameter/doc/src/diameter_compile.xml | 32 ++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/doc/src/diameter_compile.xml b/lib/diameter/doc/src/diameter_compile.xml
index 72bac30709..60e08d41da 100644
--- a/lib/diameter/doc/src/diameter_compile.xml
+++ b/lib/diameter/doc/src/diameter_compile.xml
@@ -64,9 +64,10 @@ Defaults to the current working directory.
-
Specifies a directory to add to the code path.
-Typically used to point at beam files corresponding to dictionaries
-included by the one being compiled (using the @includes directive):
-inclusion is a beam dependency, not an erl/hrl dependency.
+Use to point at beam files corresponding to dictionaries
+inherited by the one being compiled using @inherits or
+--inherits.
+Inheritance is a beam dependency, not an erl/hrl dependency.
Multiple -i options can be specified.
@@ -84,6 +85,31 @@ Supresses erl generation.
Supresses hrl generation.
+]]>
+-
+
+Set @name in the dictionary file.
+Overrides any setting in the file itself.
+
+
+]]>
+-
+
+Set @prefix in the dictionary file.
+Overrides any setting in the file itself.
+
+
+]]>
+-
+
+Append an @inherits to the dictionary file.
+Specifying '-' as the dictionary has the effect of clearing
+any previous inherits, effectively ignoring previous inherits.
+
+
+Multiple --inherits options can be specified.
+
+
--
cgit v1.2.3
From a0fb3d0302a18420cb622c9e172ca71ffdaf6a73 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 17:04:38 +0200
Subject: Add diameter_make as compilation interface
As a module-based alternative to the escript diameterc.
---
lib/diameter/src/compiler/diameter_make.erl | 122 +++++++++-------------------
lib/diameter/src/compiler/modules.mk | 3 +-
lib/diameter/test/diameter_app_SUITE.erl | 1 +
3 files changed, 43 insertions(+), 83 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl
index 4431b88c4d..5380ee56ca 100644
--- a/lib/diameter/src/compiler/diameter_make.erl
+++ b/lib/diameter/src/compiler/diameter_make.erl
@@ -18,103 +18,61 @@
%%
%%
-%% Driver for the encoder generator utility.
+%% Module alternative to diameterc for dictionary compilation.
+%%
+%% Eg. 1> diameter_make:dict("mydict.dia").
+%%
+%% $ erl -noshell \
+%% -boot start_clean \
+%% -s diameter_make dict mydict.dia \
+%% -s init stop
%%
-module(diameter_make).
--export([spec/0,
- hrl/0,
- erl/0]).
+-export([dict/1,
+ dict/2,
+ spec/1,
+ spec/2]).
--spec spec() -> no_return().
--spec hrl() -> no_return().
--spec erl() -> no_return().
+-type opt() :: {outdir|include|name|prefix|inherits, string()}
+ | verbose
+ | debug.
-spec() ->
- make(spec).
+%% dict/1-2
-hrl() ->
- make(hrl).
+-spec dict(string(), [opt()])
+ -> ok.
-erl() ->
- make(erl).
+dict(File, Opts) ->
+ make(File,
+ Opts,
+ spec(File, Opts),
+ [spec || _ <- [1], lists:member(debug, Opts)] ++ [erl, hrl]).
-%% make/1
+dict(File) ->
+ dict(File, []).
-make(Mode) ->
- Args = init:get_plain_arguments(),
- Opts = try options(Args) catch throw: help -> help(Mode) end,
- Files = proplists:get_value(files, Opts, []),
- lists:foreach(fun(F) -> from_file(F, Mode, Opts) end, Files),
- halt(0).
+%% spec/2
-%% from_file/3
-
-from_file(F, Mode, Opts) ->
- try to_spec(F, Mode, Opts) of
- Spec ->
- from_spec(F, Spec, Mode, Opts)
- catch
- error: Reason ->
- io:format("==> ~p parse failure:~n~p~n",
- [F, {Reason, erlang:get_stacktrace()}]),
- halt(1)
- end.
+-spec spec(string(), [opt()])
+ -> orddict:orddict().
-%% to_spec/2
+spec(File, Opts) ->
+ diameter_spec_util:parse(File, Opts).
-%% Try to read the input as an already parsed file or else parse it.
-to_spec(F, spec, Opts) ->
- diameter_spec_util:parse(F, Opts);
-to_spec(F, _, _) ->
- {ok, [Spec]} = file:consult(F),
- Spec.
+spec(File) ->
+ spec(File, []).
-%% from_spec/4
+%% ===========================================================================
-from_spec(File, Spec, Mode, Opts) ->
- try
- diameter_codegen:from_spec(File, Spec, Opts, Mode)
+make(_, _, _, []) ->
+ ok;
+make(File, Opts, Spec, [Mode | Rest]) ->
+ try diameter_codegen:from_spec(File, Spec, Opts, Mode) of
+ ok ->
+ make(File, Opts, Spec, Rest)
catch
error: Reason ->
- io:format("==> ~p codegen failure:~n~p~n~p~n",
- [Mode, File, {Reason, erlang:get_stacktrace()}]),
- halt(1)
+ {error, {Reason, Mode, erlang:get_stacktrace()}}
end.
-
-%% options/1
-
-options(["-v" | Rest]) ->
- [verbose | options(Rest)];
-
-options(["-o", Outdir | Rest]) ->
- [{outdir, Outdir} | options(Rest)];
-
-options(["-i", Incdir | Rest]) ->
- [{include, Incdir} | options(Rest)];
-
-options(["-h" | _]) ->
- throw(help);
-
-options(["--" | Fs]) ->
- [{files, Fs}];
-
-options(["-" ++ _ = Opt | _]) ->
- io:fwrite("==> unknown option: ~s~n", [Opt]),
- throw(help);
-
-options(Fs) -> %% trailing arguments
- options(["--" | Fs]).
-
-%% help/1
-
-help(M) ->
- io:fwrite("Usage: ~p ~p [Options] [--] File ...~n"
- "Options:~n"
- " -v verbose output~n"
- " -h shows this help message~n"
- " -o OutDir where to put the output files~n"
- " -i IncludeDir where to search for beams to import~n",
- [?MODULE, M]),
- halt(1).
diff --git a/lib/diameter/src/compiler/modules.mk b/lib/diameter/src/compiler/modules.mk
index 17a311dacf..4b40e99a3e 100644
--- a/lib/diameter/src/compiler/modules.mk
+++ b/lib/diameter/src/compiler/modules.mk
@@ -20,7 +20,8 @@
MODULES = \
diameter_codegen \
diameter_spec_scan \
- diameter_spec_util
+ diameter_spec_util \
+ diameter_make
HRL_FILES = \
diameter_forms.hrl
diff --git a/lib/diameter/test/diameter_app_SUITE.erl b/lib/diameter/test/diameter_app_SUITE.erl
index 15a98d4441..d5ecdea291 100644
--- a/lib/diameter/test/diameter_app_SUITE.erl
+++ b/lib/diameter/test/diameter_app_SUITE.erl
@@ -98,6 +98,7 @@ modules(Config) ->
diameter_dbg,
diameter_exprecs,
diameter_info,
+ diameter_make,
diameter_spec_scan,
diameter_spec_util],
{[], Help} = {Mods -- Installed, lists:sort(Installed -- Mods)}.
--
cgit v1.2.3
From 270dd088e9e29ca8880ae7f0fd02bfe2b7a27d92 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 17:25:45 +0200
Subject: Move diameter_exprecs to compiler directory
---
lib/diameter/src/app/Makefile | 2 -
lib/diameter/src/app/diameter_exprecs.erl | 301 -------------------------
lib/diameter/src/app/modules.mk | 1 -
lib/diameter/src/compiler/diameter_exprecs.erl | 301 +++++++++++++++++++++++++
lib/diameter/src/compiler/modules.mk | 1 +
5 files changed, 302 insertions(+), 304 deletions(-)
delete mode 100644 lib/diameter/src/app/diameter_exprecs.erl
create mode 100644 lib/diameter/src/compiler/diameter_exprecs.erl
(limited to 'lib')
diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile
index b2efc42a69..38af3cbe37 100644
--- a/lib/diameter/src/app/Makefile
+++ b/lib/diameter/src/app/Makefile
@@ -185,8 +185,6 @@ release_docs_spec:
# Dependencies
# ----------------------------------------------------
-$(DICT_MODULES:%=$(EBIN)/%.$(EMULATOR)): $(EBIN)/diameter_exprecs.$(EMULATOR)
-
diameter_gen_base_accounting.erl diameter_gen_relay.erl: \
$(EBIN)/diameter_gen_base_rfc3588.beam
diff --git a/lib/diameter/src/app/diameter_exprecs.erl b/lib/diameter/src/app/diameter_exprecs.erl
deleted file mode 100644
index 5e120d6f44..0000000000
--- a/lib/diameter/src/app/diameter_exprecs.erl
+++ /dev/null
@@ -1,301 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% Parse transform for generating record access functions
-%%
-%% This parse transform can be used to reduce compile-time
-%% dependencies in large systems.
-%%
-%% In the old days, before records, Erlang programmers often wrote
-%% access functions for tuple data. This was tedious and error-prone.
-%% The record syntax made this easier, but since records were implemented
-%% fully in the pre-processor, a nasty compile-time dependency was
-%% introduced.
-%%
-%% This module automates the generation of access functions for
-%% records. While this method cannot fully replace the utility of
-%% pattern matching, it does allow a fair bit of functionality on
-%% records without the need for compile-time dependencies.
-%%
-%% Whenever record definitions need to be exported from a module,
-%% inserting a compiler attribute,
-%%
-%% export_records([RecName, ...])
-%%
-%% causes this transform to lay out access functions for the exported
-%% records:
-%%
-%% -module(foo)
-%% -compile({parse_transform, diameter_exprecs}).
-%%
-%% -record(r, {a, b, c}).
-%% -export_records([a]).
-%%
-%% -export(['#info-'/1, '#info-'/2,
-%% '#new-'/1, '#new-'/2,
-%% '#get-'/2, '#set-'/2,
-%% '#new-a'/0, '#new-a'/1,
-%% '#get-a'/2, '#set-a'/2,
-%% '#info-a'/1]).
-%%
-%% '#info-'(RecName) ->
-%% '#info-'(RecName, fields).
-%%
-%% '#info-'(r, Info) ->
-%% '#info-r'(Info).
-%%
-%% '#new-'(r) -> #r{}.
-%% '#new-'(r, Vals) -> '#new-r'(Vals)
-%%
-%% '#new-r'() -> #r{}.
-%% '#new-r'(Vals) -> '#set-r'(Vals, #r{}).
-%%
-%% '#get-'(Attrs, #r{} = Rec) ->
-%% '#get-r'(Attrs, Rec).
-%%
-%% '#get-r'(Attrs, Rec) when is_list(Attrs) ->
-%% ['#get-r'(A, Rec) || A <- Attrs];
-%% '#get-r'(a, Rec) -> Rec#r.a;
-%% '#get-r'(b, Rec) -> Rec#r.b;
-%% '#get-r'(c, Rec) -> Rec#r.c.
-%%
-%% '#set-'(Vals, #r{} = Rec) ->
-%% '#set-r'(Vals, Rec).
-%%
-%% '#set-r'(Vals, Rec) when is_list(Vals) ->
-%% lists:foldl(fun '#set-r'/2, Rec, Vals);
-%% '#set-r'({a,V}, Rec) -> Rec#r{a = V};
-%% '#set-r'({b,V}, Rec) -> Rec#r{b = V};
-%% '#set-r'({c,V}, Rec) -> Rec#r{c = V}.
-%%
-%% '#info-r'(fields) -> record_info(fields, r);
-%% '#info-r'(size) -> record_info(size, r);
-%% '#info-r'({index, a}) -> 1;
-%% '#info-r'({index, b}) -> 2;
-%% '#info-r'({index, c}) -> 3;
-%%
-
--module(diameter_exprecs).
-
--export([parse_transform/2]).
-
-%% Form tag with line number.
--define(F(T), T, ?LINE).
-%% Yes, that's right. The replacement is to the first unmatched ')'.
-
--define(attribute, ?F(attribute)).
--define(clause, ?F(clause)).
--define(function, ?F(function)).
--define(call, ?F(call)).
--define('fun', ?F('fun')).
--define(generate, ?F(generate)).
--define(lc, ?F(lc)).
--define(match, ?F(match)).
--define(remote, ?F(remote)).
--define(record, ?F(record)).
--define(record_field, ?F(record_field)).
--define(record_index, ?F(record_index)).
--define(tuple, ?F(tuple)).
-
--define(ATOM(T), {atom, ?LINE, T}).
--define(VAR(V), {var, ?LINE, V}).
-
--define(CALL(F,A), {?call, ?ATOM(F), A}).
--define(APPLY(M,F,A), {?call, {?remote, ?ATOM(M), ?ATOM(F)}, A}).
-
-%% parse_transform/2
-
-parse_transform(Forms, _Options) ->
- Rs = [R || {attribute, _, record, R} <- Forms],
- case lists:append([E || {attribute, _, export_records, E} <- Forms]) of
- [] ->
- Forms;
- Es ->
- {H,T} = lists:splitwith(fun is_head/1, Forms),
- H ++ [a_export(Es) | f_accessors(Es, Rs)] ++ T
- end.
-
-is_head(T) ->
- not lists:member(element(1,T), [function, eof]).
-
-%% a_export/1
-
-a_export(Exports) ->
- {?attribute, export, [{fname(info), 1},
- {fname(info), 2},
- {fname(new), 1},
- {fname(new), 2},
- {fname(get), 2},
- {fname(set), 2}
- | lists:flatmap(fun export/1, Exports)]}.
-
-export(Rname) ->
- New = fname(new, Rname),
- [{New, 0},
- {New, 1},
- {fname(get, Rname), 2},
- {fname(set, Rname), 2},
- {fname(info, Rname), 1}].
-
-%% f_accessors/2
-
-f_accessors(Es, Rs) ->
- ['#info-/1'(),
- '#info-/2'(Es),
- '#new-/1'(Es),
- '#new-/2'(Es),
- '#get-/2'(Es),
- '#set-/2'(Es)
- | lists:flatmap(fun(N) -> accessors(N, fields(N, Rs)) end, Es)].
-
-accessors(Rname, Fields) ->
- ['#new-X/0'(Rname),
- '#new-X/1'(Rname),
- '#get-X/2'(Rname, Fields),
- '#set-X/2'(Rname, Fields),
- '#info-X/1'(Rname, Fields)].
-
-fields(Rname, Recs) ->
- {Rname, Fields} = lists:keyfind(Rname, 1, Recs),
- lists:map(fun({record_field, _, {atom, _, N}}) -> N;
- ({record_field, _, {atom, _, N}, _}) -> N
- end,
- Fields).
-
-fname_prefix(Op) ->
- "#" ++ atom_to_list(Op) ++ "-".
-
-fname(Op) ->
- list_to_atom(fname_prefix(Op)).
-
-fname(Op, Rname) ->
- Prefix = fname_prefix(Op),
- list_to_atom(Prefix ++ atom_to_list(Rname)).
-
-%% Generated functions.
-
-'#info-/1'() ->
- Fname = fname(info),
- {?function, Fname, 1,
- [{?clause, [?VAR('RecName')],
- [],
- [?CALL(Fname, [?VAR('RecName'), ?ATOM(fields)])]}]}.
-
-'#info-/2'(Exports) ->
- {?function, fname(info), 2,
- lists:map(fun 'info-'/1, Exports)}.
-
-'info-'(R) ->
- {?clause, [?ATOM(R), ?VAR('Info')],
- [],
- [?CALL(fname(info, R), [?VAR('Info')])]}.
-
-'#new-/1'(Exports) ->
- {?function, fname(new), 1,
- lists:map(fun 'new-'/1, Exports)}.
-
-'new-'(R) ->
- {?clause, [?ATOM(R)],
- [],
- [{?record, R, []}]}.
-
-'#new-/2'(Exports) ->
- {?function, fname(new), 2,
- lists:map(fun 'new--'/1, Exports)}.
-
-'new--'(R) ->
- {?clause, [?ATOM(R), ?VAR('Vals')],
- [],
- [?CALL(fname(new, R), [?VAR('Vals')])]}.
-
-'#get-/2'(Exports) ->
- {?function, fname(get), 2,
- lists:map(fun 'get-'/1, Exports)}.
-
-'get-'(R) ->
- {?clause, [?VAR('Attrs'),
- {?match, {?record, R, []}, ?VAR('Rec')}],
- [],
- [?CALL(fname(get, R), [?VAR('Attrs'), ?VAR('Rec')])]}.
-
-'#set-/2'(Exports) ->
- {?function, fname(set), 2,
- lists:map(fun 'set-'/1, Exports)}.
-
-'set-'(R) ->
- {?clause, [?VAR('Vals'), {?match, {?record, R, []}, ?VAR('Rec')}],
- [],
- [?CALL(fname(set, R), [?VAR('Vals'), ?VAR('Rec')])]}.
-
-'#new-X/0'(Rname) ->
- {?function, fname(new, Rname), 0,
- [{?clause, [],
- [],
- [{?record, Rname, []}]}]}.
-
-'#new-X/1'(Rname) ->
- {?function, fname(new, Rname), 1,
- [{?clause, [?VAR('Vals')],
- [],
- [?CALL(fname(set, Rname), [?VAR('Vals'), {?record, Rname, []}])]}]}.
-
-'#set-X/2'(Rname, Fields) ->
- {?function, fname(set, Rname), 2,
- [{?clause, [?VAR('Vals'), ?VAR('Rec')],
- [[?CALL(is_list, [?VAR('Vals')])]],
- [?APPLY(lists, foldl, [{?'fun', {function, fname(set, Rname), 2}},
- ?VAR('Rec'),
- ?VAR('Vals')])]}
- | lists:map(fun(A) -> 'set-X'(Rname, A) end, Fields)]}.
-
-'set-X'(Rname, Attr) ->
- {?clause, [{?tuple, [?ATOM(Attr), ?VAR('V')]}, ?VAR('Rec')],
- [],
- [{?record, ?VAR('Rec'), Rname,
- [{?record_field, ?ATOM(Attr), ?VAR('V')}]}]}.
-
-'#get-X/2'(Rname, Fields) ->
- FName = fname(get, Rname),
- {?function, FName, 2,
- [{?clause, [?VAR('Attrs'), ?VAR('Rec')],
- [[?CALL(is_list, [?VAR('Attrs')])]],
- [{?lc, ?CALL(FName, [?VAR('A'), ?VAR('Rec')]),
- [{?generate, ?VAR('A'), ?VAR('Attrs')}]}]}
- | lists:map(fun(A) -> 'get-X'(Rname, A) end, Fields)]}.
-
-'get-X'(Rname, Attr) ->
- {?clause, [?ATOM(Attr), ?VAR('Rec')],
- [],
- [{?record_field, ?VAR('Rec'), Rname, ?ATOM(Attr)}]}.
-
-'#info-X/1'(Rname, Fields) ->
- {?function, fname(info, Rname), 1,
- [{?clause, [?ATOM(fields)],
- [],
- [?CALL(record_info, [?ATOM(fields), ?ATOM(Rname)])]},
- {?clause, [?ATOM(size)],
- [],
- [?CALL(record_info, [?ATOM(size), ?ATOM(Rname)])]}
- | lists:map(fun(A) -> 'info-X'(Rname, A) end, Fields)]}.
-
-'info-X'(Rname, Attr) ->
- {?clause, [{?tuple, [?ATOM(index), ?ATOM(Attr)]}],
- [],
- [{?record_index, Rname, ?ATOM(Attr)}]}.
diff --git a/lib/diameter/src/app/modules.mk b/lib/diameter/src/app/modules.mk
index ea4c58bfd7..2b602cd713 100644
--- a/lib/diameter/src/app/modules.mk
+++ b/lib/diameter/src/app/modules.mk
@@ -47,7 +47,6 @@ RUNTIME_MODULES = \
HELP_MODULES = \
diameter_callback \
- diameter_exprecs \
diameter_dbg \
diameter_info
diff --git a/lib/diameter/src/compiler/diameter_exprecs.erl b/lib/diameter/src/compiler/diameter_exprecs.erl
new file mode 100644
index 0000000000..5e120d6f44
--- /dev/null
+++ b/lib/diameter/src/compiler/diameter_exprecs.erl
@@ -0,0 +1,301 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% Parse transform for generating record access functions
+%%
+%% This parse transform can be used to reduce compile-time
+%% dependencies in large systems.
+%%
+%% In the old days, before records, Erlang programmers often wrote
+%% access functions for tuple data. This was tedious and error-prone.
+%% The record syntax made this easier, but since records were implemented
+%% fully in the pre-processor, a nasty compile-time dependency was
+%% introduced.
+%%
+%% This module automates the generation of access functions for
+%% records. While this method cannot fully replace the utility of
+%% pattern matching, it does allow a fair bit of functionality on
+%% records without the need for compile-time dependencies.
+%%
+%% Whenever record definitions need to be exported from a module,
+%% inserting a compiler attribute,
+%%
+%% export_records([RecName, ...])
+%%
+%% causes this transform to lay out access functions for the exported
+%% records:
+%%
+%% -module(foo)
+%% -compile({parse_transform, diameter_exprecs}).
+%%
+%% -record(r, {a, b, c}).
+%% -export_records([a]).
+%%
+%% -export(['#info-'/1, '#info-'/2,
+%% '#new-'/1, '#new-'/2,
+%% '#get-'/2, '#set-'/2,
+%% '#new-a'/0, '#new-a'/1,
+%% '#get-a'/2, '#set-a'/2,
+%% '#info-a'/1]).
+%%
+%% '#info-'(RecName) ->
+%% '#info-'(RecName, fields).
+%%
+%% '#info-'(r, Info) ->
+%% '#info-r'(Info).
+%%
+%% '#new-'(r) -> #r{}.
+%% '#new-'(r, Vals) -> '#new-r'(Vals)
+%%
+%% '#new-r'() -> #r{}.
+%% '#new-r'(Vals) -> '#set-r'(Vals, #r{}).
+%%
+%% '#get-'(Attrs, #r{} = Rec) ->
+%% '#get-r'(Attrs, Rec).
+%%
+%% '#get-r'(Attrs, Rec) when is_list(Attrs) ->
+%% ['#get-r'(A, Rec) || A <- Attrs];
+%% '#get-r'(a, Rec) -> Rec#r.a;
+%% '#get-r'(b, Rec) -> Rec#r.b;
+%% '#get-r'(c, Rec) -> Rec#r.c.
+%%
+%% '#set-'(Vals, #r{} = Rec) ->
+%% '#set-r'(Vals, Rec).
+%%
+%% '#set-r'(Vals, Rec) when is_list(Vals) ->
+%% lists:foldl(fun '#set-r'/2, Rec, Vals);
+%% '#set-r'({a,V}, Rec) -> Rec#r{a = V};
+%% '#set-r'({b,V}, Rec) -> Rec#r{b = V};
+%% '#set-r'({c,V}, Rec) -> Rec#r{c = V}.
+%%
+%% '#info-r'(fields) -> record_info(fields, r);
+%% '#info-r'(size) -> record_info(size, r);
+%% '#info-r'({index, a}) -> 1;
+%% '#info-r'({index, b}) -> 2;
+%% '#info-r'({index, c}) -> 3;
+%%
+
+-module(diameter_exprecs).
+
+-export([parse_transform/2]).
+
+%% Form tag with line number.
+-define(F(T), T, ?LINE).
+%% Yes, that's right. The replacement is to the first unmatched ')'.
+
+-define(attribute, ?F(attribute)).
+-define(clause, ?F(clause)).
+-define(function, ?F(function)).
+-define(call, ?F(call)).
+-define('fun', ?F('fun')).
+-define(generate, ?F(generate)).
+-define(lc, ?F(lc)).
+-define(match, ?F(match)).
+-define(remote, ?F(remote)).
+-define(record, ?F(record)).
+-define(record_field, ?F(record_field)).
+-define(record_index, ?F(record_index)).
+-define(tuple, ?F(tuple)).
+
+-define(ATOM(T), {atom, ?LINE, T}).
+-define(VAR(V), {var, ?LINE, V}).
+
+-define(CALL(F,A), {?call, ?ATOM(F), A}).
+-define(APPLY(M,F,A), {?call, {?remote, ?ATOM(M), ?ATOM(F)}, A}).
+
+%% parse_transform/2
+
+parse_transform(Forms, _Options) ->
+ Rs = [R || {attribute, _, record, R} <- Forms],
+ case lists:append([E || {attribute, _, export_records, E} <- Forms]) of
+ [] ->
+ Forms;
+ Es ->
+ {H,T} = lists:splitwith(fun is_head/1, Forms),
+ H ++ [a_export(Es) | f_accessors(Es, Rs)] ++ T
+ end.
+
+is_head(T) ->
+ not lists:member(element(1,T), [function, eof]).
+
+%% a_export/1
+
+a_export(Exports) ->
+ {?attribute, export, [{fname(info), 1},
+ {fname(info), 2},
+ {fname(new), 1},
+ {fname(new), 2},
+ {fname(get), 2},
+ {fname(set), 2}
+ | lists:flatmap(fun export/1, Exports)]}.
+
+export(Rname) ->
+ New = fname(new, Rname),
+ [{New, 0},
+ {New, 1},
+ {fname(get, Rname), 2},
+ {fname(set, Rname), 2},
+ {fname(info, Rname), 1}].
+
+%% f_accessors/2
+
+f_accessors(Es, Rs) ->
+ ['#info-/1'(),
+ '#info-/2'(Es),
+ '#new-/1'(Es),
+ '#new-/2'(Es),
+ '#get-/2'(Es),
+ '#set-/2'(Es)
+ | lists:flatmap(fun(N) -> accessors(N, fields(N, Rs)) end, Es)].
+
+accessors(Rname, Fields) ->
+ ['#new-X/0'(Rname),
+ '#new-X/1'(Rname),
+ '#get-X/2'(Rname, Fields),
+ '#set-X/2'(Rname, Fields),
+ '#info-X/1'(Rname, Fields)].
+
+fields(Rname, Recs) ->
+ {Rname, Fields} = lists:keyfind(Rname, 1, Recs),
+ lists:map(fun({record_field, _, {atom, _, N}}) -> N;
+ ({record_field, _, {atom, _, N}, _}) -> N
+ end,
+ Fields).
+
+fname_prefix(Op) ->
+ "#" ++ atom_to_list(Op) ++ "-".
+
+fname(Op) ->
+ list_to_atom(fname_prefix(Op)).
+
+fname(Op, Rname) ->
+ Prefix = fname_prefix(Op),
+ list_to_atom(Prefix ++ atom_to_list(Rname)).
+
+%% Generated functions.
+
+'#info-/1'() ->
+ Fname = fname(info),
+ {?function, Fname, 1,
+ [{?clause, [?VAR('RecName')],
+ [],
+ [?CALL(Fname, [?VAR('RecName'), ?ATOM(fields)])]}]}.
+
+'#info-/2'(Exports) ->
+ {?function, fname(info), 2,
+ lists:map(fun 'info-'/1, Exports)}.
+
+'info-'(R) ->
+ {?clause, [?ATOM(R), ?VAR('Info')],
+ [],
+ [?CALL(fname(info, R), [?VAR('Info')])]}.
+
+'#new-/1'(Exports) ->
+ {?function, fname(new), 1,
+ lists:map(fun 'new-'/1, Exports)}.
+
+'new-'(R) ->
+ {?clause, [?ATOM(R)],
+ [],
+ [{?record, R, []}]}.
+
+'#new-/2'(Exports) ->
+ {?function, fname(new), 2,
+ lists:map(fun 'new--'/1, Exports)}.
+
+'new--'(R) ->
+ {?clause, [?ATOM(R), ?VAR('Vals')],
+ [],
+ [?CALL(fname(new, R), [?VAR('Vals')])]}.
+
+'#get-/2'(Exports) ->
+ {?function, fname(get), 2,
+ lists:map(fun 'get-'/1, Exports)}.
+
+'get-'(R) ->
+ {?clause, [?VAR('Attrs'),
+ {?match, {?record, R, []}, ?VAR('Rec')}],
+ [],
+ [?CALL(fname(get, R), [?VAR('Attrs'), ?VAR('Rec')])]}.
+
+'#set-/2'(Exports) ->
+ {?function, fname(set), 2,
+ lists:map(fun 'set-'/1, Exports)}.
+
+'set-'(R) ->
+ {?clause, [?VAR('Vals'), {?match, {?record, R, []}, ?VAR('Rec')}],
+ [],
+ [?CALL(fname(set, R), [?VAR('Vals'), ?VAR('Rec')])]}.
+
+'#new-X/0'(Rname) ->
+ {?function, fname(new, Rname), 0,
+ [{?clause, [],
+ [],
+ [{?record, Rname, []}]}]}.
+
+'#new-X/1'(Rname) ->
+ {?function, fname(new, Rname), 1,
+ [{?clause, [?VAR('Vals')],
+ [],
+ [?CALL(fname(set, Rname), [?VAR('Vals'), {?record, Rname, []}])]}]}.
+
+'#set-X/2'(Rname, Fields) ->
+ {?function, fname(set, Rname), 2,
+ [{?clause, [?VAR('Vals'), ?VAR('Rec')],
+ [[?CALL(is_list, [?VAR('Vals')])]],
+ [?APPLY(lists, foldl, [{?'fun', {function, fname(set, Rname), 2}},
+ ?VAR('Rec'),
+ ?VAR('Vals')])]}
+ | lists:map(fun(A) -> 'set-X'(Rname, A) end, Fields)]}.
+
+'set-X'(Rname, Attr) ->
+ {?clause, [{?tuple, [?ATOM(Attr), ?VAR('V')]}, ?VAR('Rec')],
+ [],
+ [{?record, ?VAR('Rec'), Rname,
+ [{?record_field, ?ATOM(Attr), ?VAR('V')}]}]}.
+
+'#get-X/2'(Rname, Fields) ->
+ FName = fname(get, Rname),
+ {?function, FName, 2,
+ [{?clause, [?VAR('Attrs'), ?VAR('Rec')],
+ [[?CALL(is_list, [?VAR('Attrs')])]],
+ [{?lc, ?CALL(FName, [?VAR('A'), ?VAR('Rec')]),
+ [{?generate, ?VAR('A'), ?VAR('Attrs')}]}]}
+ | lists:map(fun(A) -> 'get-X'(Rname, A) end, Fields)]}.
+
+'get-X'(Rname, Attr) ->
+ {?clause, [?ATOM(Attr), ?VAR('Rec')],
+ [],
+ [{?record_field, ?VAR('Rec'), Rname, ?ATOM(Attr)}]}.
+
+'#info-X/1'(Rname, Fields) ->
+ {?function, fname(info, Rname), 1,
+ [{?clause, [?ATOM(fields)],
+ [],
+ [?CALL(record_info, [?ATOM(fields), ?ATOM(Rname)])]},
+ {?clause, [?ATOM(size)],
+ [],
+ [?CALL(record_info, [?ATOM(size), ?ATOM(Rname)])]}
+ | lists:map(fun(A) -> 'info-X'(Rname, A) end, Fields)]}.
+
+'info-X'(Rname, Attr) ->
+ {?clause, [{?tuple, [?ATOM(index), ?ATOM(Attr)]}],
+ [],
+ [{?record_index, Rname, ?ATOM(Attr)}]}.
diff --git a/lib/diameter/src/compiler/modules.mk b/lib/diameter/src/compiler/modules.mk
index 4b40e99a3e..6fbc8fa8d4 100644
--- a/lib/diameter/src/compiler/modules.mk
+++ b/lib/diameter/src/compiler/modules.mk
@@ -19,6 +19,7 @@
MODULES = \
diameter_codegen \
+ diameter_exprecs \
diameter_spec_scan \
diameter_spec_util \
diameter_make
--
cgit v1.2.3
From f4c38ecd803451280d0021bcfa7f2ad25b96cbcd Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 17:41:28 +0200
Subject: Remove app dependency on compiler to avoid forced recompilation
---
lib/diameter/src/app/Makefile | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile
index 38af3cbe37..7752bb1798 100644
--- a/lib/diameter/src/app/Makefile
+++ b/lib/diameter/src/app/Makefile
@@ -148,6 +148,10 @@ $(APP_TARGET): $(APP_SRC) \
$(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
+# This isn't a dependency for $(DICT_ERL_FILES) $(DICT_HRL_FILES)
+# since it would force recompilation: compiler must explicity be made
+# first, which ../Makefile does. Should share a makefile for proper
+# dependencies. (Aka, recursive make considered harmful.)
compiler:
$(MAKE) -C ../$@
@@ -188,8 +192,6 @@ release_docs_spec:
diameter_gen_base_accounting.erl diameter_gen_relay.erl: \
$(EBIN)/diameter_gen_base_rfc3588.beam
-$(DICT_ERL_FILES) $(DICT_HRL_FILES): compiler
-
$(DICT_ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR)): \
$(DIAMETER_TOP)/include/diameter.hrl \
$(DIAMETER_TOP)/include/diameter_gen.hrl
--
cgit v1.2.3
From 14bf1dc855bbd973bb15578f418e37ab2d4f17fe Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 14 Oct 2011 19:40:29 +0200
Subject: One makefile for src build instead of recursion
Simpler, no duplication of similar makefiles and makes for
better dependencies. (Aka, recursive make considered harmful.)
---
lib/diameter/configure.in | 1 -
lib/diameter/src/.gitignore | 2 +
lib/diameter/src/Makefile | 224 +-
lib/diameter/src/app/.gitignore | 6 -
lib/diameter/src/app/Makefile | 215 --
lib/diameter/src/app/depend.sed | 31 -
lib/diameter/src/app/diameter.app.src | 28 -
lib/diameter/src/app/diameter.appup.src | 47 -
lib/diameter/src/app/diameter.erl | 190 --
lib/diameter/src/app/diameter.mk.in | 47 -
lib/diameter/src/app/diameter_app.erl | 36 -
lib/diameter/src/app/diameter_callback.erl | 91 -
lib/diameter/src/app/diameter_capx.erl | 405 ----
lib/diameter/src/app/diameter_codec.erl | 561 -----
lib/diameter/src/app/diameter_config.erl | 676 ------
lib/diameter/src/app/diameter_dbg.erl | 516 ----
lib/diameter/src/app/diameter_dict.erl | 153 --
lib/diameter/src/app/diameter_info.erl | 869 -------
lib/diameter/src/app/diameter_internal.hrl | 80 -
lib/diameter/src/app/diameter_lib.erl | 272 ---
lib/diameter/src/app/diameter_misc_sup.erl | 58 -
lib/diameter/src/app/diameter_peer.erl | 225 --
lib/diameter/src/app/diameter_peer_fsm.erl | 777 ------
lib/diameter/src/app/diameter_peer_fsm_sup.erl | 63 -
lib/diameter/src/app/diameter_reg.erl | 327 ---
lib/diameter/src/app/diameter_service.erl | 2903 -----------------------
lib/diameter/src/app/diameter_service_sup.erl | 64 -
lib/diameter/src/app/diameter_session.erl | 172 --
lib/diameter/src/app/diameter_stats.erl | 342 ---
lib/diameter/src/app/diameter_sup.erl | 101 -
lib/diameter/src/app/diameter_sync.erl | 550 -----
lib/diameter/src/app/diameter_types.erl | 537 -----
lib/diameter/src/app/diameter_types.hrl | 139 --
lib/diameter/src/app/diameter_watchdog.erl | 571 -----
lib/diameter/src/app/diameter_watchdog_sup.erl | 60 -
lib/diameter/src/app/modules.mk | 69 -
lib/diameter/src/base/diameter.app.src | 28 +
lib/diameter/src/base/diameter.appup.src | 47 +
lib/diameter/src/base/diameter.erl | 190 ++
lib/diameter/src/base/diameter_app.erl | 36 +
lib/diameter/src/base/diameter_callback.erl | 91 +
lib/diameter/src/base/diameter_capx.erl | 405 ++++
lib/diameter/src/base/diameter_codec.erl | 561 +++++
lib/diameter/src/base/diameter_config.erl | 676 ++++++
lib/diameter/src/base/diameter_dbg.erl | 516 ++++
lib/diameter/src/base/diameter_dict.erl | 153 ++
lib/diameter/src/base/diameter_info.erl | 869 +++++++
lib/diameter/src/base/diameter_internal.hrl | 80 +
lib/diameter/src/base/diameter_lib.erl | 272 +++
lib/diameter/src/base/diameter_misc_sup.erl | 58 +
lib/diameter/src/base/diameter_peer.erl | 225 ++
lib/diameter/src/base/diameter_peer_fsm.erl | 777 ++++++
lib/diameter/src/base/diameter_peer_fsm_sup.erl | 63 +
lib/diameter/src/base/diameter_reg.erl | 327 +++
lib/diameter/src/base/diameter_service.erl | 2903 +++++++++++++++++++++++
lib/diameter/src/base/diameter_service_sup.erl | 64 +
lib/diameter/src/base/diameter_session.erl | 172 ++
lib/diameter/src/base/diameter_stats.erl | 342 +++
lib/diameter/src/base/diameter_sup.erl | 101 +
lib/diameter/src/base/diameter_sync.erl | 550 +++++
lib/diameter/src/base/diameter_types.erl | 537 +++++
lib/diameter/src/base/diameter_types.hrl | 139 ++
lib/diameter/src/base/diameter_watchdog.erl | 571 +++++
lib/diameter/src/base/diameter_watchdog_sup.erl | 60 +
lib/diameter/src/compiler/.gitignore | 3 -
lib/diameter/src/compiler/Makefile | 131 -
lib/diameter/src/compiler/modules.mk | 29 -
lib/diameter/src/depend.sed | 51 +
lib/diameter/src/gen/.gitignore | 2 +
lib/diameter/src/modules.mk | 93 +
lib/diameter/src/subdirs.mk | 21 -
lib/diameter/src/transport/.gitignore | 3 -
lib/diameter/src/transport/Makefile | 141 --
lib/diameter/src/transport/modules.mk | 29 -
lib/diameter/test/Makefile | 23 +-
75 files changed, 11185 insertions(+), 11562 deletions(-)
create mode 100644 lib/diameter/src/.gitignore
delete mode 100644 lib/diameter/src/app/.gitignore
delete mode 100644 lib/diameter/src/app/Makefile
delete mode 100644 lib/diameter/src/app/depend.sed
delete mode 100644 lib/diameter/src/app/diameter.app.src
delete mode 100644 lib/diameter/src/app/diameter.appup.src
delete mode 100644 lib/diameter/src/app/diameter.erl
delete mode 100644 lib/diameter/src/app/diameter.mk.in
delete mode 100644 lib/diameter/src/app/diameter_app.erl
delete mode 100644 lib/diameter/src/app/diameter_callback.erl
delete mode 100644 lib/diameter/src/app/diameter_capx.erl
delete mode 100644 lib/diameter/src/app/diameter_codec.erl
delete mode 100644 lib/diameter/src/app/diameter_config.erl
delete mode 100644 lib/diameter/src/app/diameter_dbg.erl
delete mode 100644 lib/diameter/src/app/diameter_dict.erl
delete mode 100644 lib/diameter/src/app/diameter_info.erl
delete mode 100644 lib/diameter/src/app/diameter_internal.hrl
delete mode 100644 lib/diameter/src/app/diameter_lib.erl
delete mode 100644 lib/diameter/src/app/diameter_misc_sup.erl
delete mode 100644 lib/diameter/src/app/diameter_peer.erl
delete mode 100644 lib/diameter/src/app/diameter_peer_fsm.erl
delete mode 100644 lib/diameter/src/app/diameter_peer_fsm_sup.erl
delete mode 100644 lib/diameter/src/app/diameter_reg.erl
delete mode 100644 lib/diameter/src/app/diameter_service.erl
delete mode 100644 lib/diameter/src/app/diameter_service_sup.erl
delete mode 100644 lib/diameter/src/app/diameter_session.erl
delete mode 100644 lib/diameter/src/app/diameter_stats.erl
delete mode 100644 lib/diameter/src/app/diameter_sup.erl
delete mode 100644 lib/diameter/src/app/diameter_sync.erl
delete mode 100644 lib/diameter/src/app/diameter_types.erl
delete mode 100644 lib/diameter/src/app/diameter_types.hrl
delete mode 100644 lib/diameter/src/app/diameter_watchdog.erl
delete mode 100644 lib/diameter/src/app/diameter_watchdog_sup.erl
delete mode 100644 lib/diameter/src/app/modules.mk
create mode 100644 lib/diameter/src/base/diameter.app.src
create mode 100644 lib/diameter/src/base/diameter.appup.src
create mode 100644 lib/diameter/src/base/diameter.erl
create mode 100644 lib/diameter/src/base/diameter_app.erl
create mode 100644 lib/diameter/src/base/diameter_callback.erl
create mode 100644 lib/diameter/src/base/diameter_capx.erl
create mode 100644 lib/diameter/src/base/diameter_codec.erl
create mode 100644 lib/diameter/src/base/diameter_config.erl
create mode 100644 lib/diameter/src/base/diameter_dbg.erl
create mode 100644 lib/diameter/src/base/diameter_dict.erl
create mode 100644 lib/diameter/src/base/diameter_info.erl
create mode 100644 lib/diameter/src/base/diameter_internal.hrl
create mode 100644 lib/diameter/src/base/diameter_lib.erl
create mode 100644 lib/diameter/src/base/diameter_misc_sup.erl
create mode 100644 lib/diameter/src/base/diameter_peer.erl
create mode 100644 lib/diameter/src/base/diameter_peer_fsm.erl
create mode 100644 lib/diameter/src/base/diameter_peer_fsm_sup.erl
create mode 100644 lib/diameter/src/base/diameter_reg.erl
create mode 100644 lib/diameter/src/base/diameter_service.erl
create mode 100644 lib/diameter/src/base/diameter_service_sup.erl
create mode 100644 lib/diameter/src/base/diameter_session.erl
create mode 100644 lib/diameter/src/base/diameter_stats.erl
create mode 100644 lib/diameter/src/base/diameter_sup.erl
create mode 100644 lib/diameter/src/base/diameter_sync.erl
create mode 100644 lib/diameter/src/base/diameter_types.erl
create mode 100644 lib/diameter/src/base/diameter_types.hrl
create mode 100644 lib/diameter/src/base/diameter_watchdog.erl
create mode 100644 lib/diameter/src/base/diameter_watchdog_sup.erl
delete mode 100644 lib/diameter/src/compiler/.gitignore
delete mode 100644 lib/diameter/src/compiler/Makefile
delete mode 100644 lib/diameter/src/compiler/modules.mk
create mode 100644 lib/diameter/src/depend.sed
create mode 100644 lib/diameter/src/gen/.gitignore
create mode 100644 lib/diameter/src/modules.mk
delete mode 100644 lib/diameter/src/subdirs.mk
delete mode 100644 lib/diameter/src/transport/.gitignore
delete mode 100644 lib/diameter/src/transport/Makefile
delete mode 100644 lib/diameter/src/transport/modules.mk
(limited to 'lib')
diff --git a/lib/diameter/configure.in b/lib/diameter/configure.in
index 9aca3859c5..8acfb28fed 100644
--- a/lib/diameter/configure.in
+++ b/lib/diameter/configure.in
@@ -132,7 +132,6 @@ dnl
AC_OUTPUT(
Makefile:Makefile.in
- src/app/diameter.mk:src/app/diameter.mk.in
make/$host/rules.mk:make/rules.mk.in
)
diff --git a/lib/diameter/src/.gitignore b/lib/diameter/src/.gitignore
new file mode 100644
index 0000000000..feeb378fd8
--- /dev/null
+++ b/lib/diameter/src/.gitignore
@@ -0,0 +1,2 @@
+
+/depend.mk
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 6935eb053e..a72bf4420b 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -16,28 +16,228 @@
#
# %CopyrightEnd%
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-else
+ifeq ($(ERL_TOP),)
include $(DIAMETER_TOP)/make/target.mk
include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
+else
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
endif
# ----------------------------------------------------
-# Common Macros
+# Application version
+# ----------------------------------------------------
+
+include ../vsn.mk
+
+VSN = $(DIAMETER_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+
+RELSYSDIR = $(RELEASE_PATH)/lib/diameter-$(VSN)
+
+# Where to put/find things.
+EBIN = ../ebin
+INCDIR = ../include
+
+# Where make should look for dependencies.
+VPATH = .:base:compiler:transport:gen
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+include modules.mk
+
+DICT_FILES = $(RT_DICTS:%=dict/%.dia)
+DICT_MODULES = $(RT_DICTS:%=gen/diameter_gen_%)
+DICT_ERL_FILES = $(DICT_MODULES:%=%.erl)
+DICT_HRL_FILES = $(DICT_MODULES:%=%.hrl)
+
+# Modules to build before compiling dictionaries.
+COMPILER_MODULES = $(filter compiler/%, $(CT_MODULES))
+
+# All handwritten modules.
+MODULES = \
+ $(RT_MODULES) \
+ $(CT_MODULES)
+
+# Modules whose names are inserted into the app file.
+APP_MODULES = \
+ $(RT_MODULES) \
+ $(DICT_MODULES)
+
+# Modules for which to build beams.
+TARGET_MODULES = \
+ $(APP_MODULES) \
+ $(CT_MODULES)
+
+# What to build for the 'opt' target.
+TARGET_FILES = \
+ $(patsubst %,$(EBIN)/%.$(EMULATOR),$(notdir $(TARGET_MODULES))) \
+ $(APP_TARGET) \
+ $(APPUP_TARGET)
+
+# Subdirectories of src to release modules into.
+TARGET_DIRS = $(sort $(dir $(TARGET_MODULES)))
+
+APP_FILE = diameter.app
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+APPUP_FILE = diameter.appup
+APPUP_SRC = $(APPUP_FILE).src
+APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+
+EXAMPLE_FILES = $(EXAMPLES:%=../examples/%)
+BIN_FILES = $(BINS:%=../bin/%)
+
+# ----------------------------------------------------
+# FLAGS
# ----------------------------------------------------
-include subdirs.mk
+ifeq ($(TYPE),debug)
+ERL_COMPILE_FLAGS += -Ddebug
+endif
-SPECIAL_TARGETS =
+ERL_COMPILE_FLAGS += \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ +warn_unused_vars \
+ -pa $(EBIN) \
+ -I $(INCDIR) \
+ -I gen
# ----------------------------------------------------
-# Default Subdir Targets
+# Targets
# ----------------------------------------------------
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/otp_subdir.mk
+
+debug:
+ @$(MAKE) TYPE=debug opt
+
+opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES) $(DICT_ERL_FILES) $(DICT_HRL_FILES)
+ rm -f $(APP_TARGET) $(APPUP_TARGET)
+ rm -f errs core *~ gen/diameter_gen_*.forms gen/diameter_gen_*.spec
+ rm -f depend.mk
+
+docs:
+
+list = echo $(1):; echo $($(1)) | tr ' ' '\n' | sort | sed 's@^@ @'
+
+info:
+ @echo ========================================
+ @$(call list,RT_DICTS)
+ @echo
+ @$(call list,RT_MODULES)
+ @echo
+ @$(call list,CT_MODULES)
+ @echo
+ @$(call list,TARGET_MODULES)
+ @echo
+ @$(call list,TARGET_DIRS)
+ @echo
+ @$(call list,EXTERNAL_HRL_FILES)
+ @echo
+ @$(call list,INTERNAL_HRL_FILES)
+ @echo
+ @$(call list,EXAMPLE_FILES)
+ @echo
+ @$(call list,BIN_FILES)
+ @echo ========================================
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+# erl/hrl from dictionary file.
+gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
+ ../bin/diameterc -o gen -i $(EBIN) $<
+
+# Generate the app file.
+$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
+ M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ sed -e 's;%VSN%;$(VSN);' \
+ -e "s;%MODULES%;$$M;" \
+ $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+app: $(APP_TARGET) $(APPUP_TARGET)
+dict: $(DICT_ERL_FILES)
+
+# Make the modules from a subdirectory.
+$(TARGET_DIRS:%/=%):
+ $(MAKE) \
+ $(patsubst $@/%,$(EBIN)/%.$(EMULATOR),$(filter $@/%,$(TARGET_MODULES)))
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+
+ifeq ($(ERL_TOP),)
+include $(DIAMETER_TOP)/make/release_targets.mk
else
-include $(DIAMETER_TOP)/make/subdir.mk
+include $(ERL_TOP)/make/otp_release_targets.mk
endif
-#include ../make/subdir.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/bin \
+ $(RELSYSDIR)/ebin \
+ $(RELSYSDIR)/src/dict \
+ $(TARGET_DIRS:%=$(RELSYSDIR)/src/%) \
+ $(RELSYSDIR)/include \
+ $(RELSYSDIR)/examples
+ $(INSTALL_SCRIPT) $(BIN_FILES) $(RELSYSDIR)/bin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(DICT_FILES) $(RELSYSDIR)/src/dict
+ $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(DICT_HRL_FILES) \
+ $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
+ for dir in $(TARGET_DIRS); do \
+ $(MAKE) release_subdir SRCDIR=$$dir; \
+ done
+
+release_subdir:
+ [ -d "$(SRCDIR)" ]
+ $(INSTALL_DATA) $(filter $(SRCDIR)%,$(TARGET_MODULES:%=%.erl) \
+ $(INTERNAL_HRL_FILES)) \
+ $(RELSYSDIR)/src/$(SRCDIR)
+
+release_docs_spec:
+
+# ----------------------------------------------------
+# Dependencies
+# ----------------------------------------------------
+
+gen/diameter_gen_base_accounting.erl gen/diameter_gen_relay.erl \
+gen/diameter_gen_base_accounting.hrl gen/diameter_gen_relay.hrl: \
+ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR)
+
+gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl: \
+ $(COMPILER_MODULES:compiler/%=$(EBIN)/%.$(EMULATOR))
+
+$(DICT_MODULES:%=$(EBIN)/%.$(EMULATOR)): \
+ $(INCDIR)/diameter.hrl \
+ $(INCDIR)/diameter_gen.hrl
+
+depend: depend.mk
+
+# Generate dependencies makefile.
+depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
+ (for f in $(MODULES); do \
+ (echo $$f; cat $$f.erl) | sed -f $<; \
+ done) \
+ > $@
+
+-include depend.mk
+
+.PRECIOUS: $(DICT_ERL_FILES) $(DICT_HRL_FILES)
+.PHONY: app clean depend dict info release_subdir
+.PHONY: debug opt release_docs_spec release_spec
+.PHONY: $(TARGET_DIRS:%/=%)
diff --git a/lib/diameter/src/app/.gitignore b/lib/diameter/src/app/.gitignore
deleted file mode 100644
index d388e61877..0000000000
--- a/lib/diameter/src/app/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-
-/diameter_gen_*.erl
-/diameter_gen_*.hrl
-/depend.mk
-/diameter.mk
-
diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile
deleted file mode 100644
index 7752bb1798..0000000000
--- a/lib/diameter/src/app/Makefile
+++ /dev/null
@@ -1,215 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-#
-#
-
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-else
-include $(DIAMETER_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-endif
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-
-include ../../vsn.mk
-
-VSN=$(DIAMETER_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-
-RELSYSDIR = $(RELEASE_PATH)/lib/diameter-$(VSN)
-
-INCDIR = ../../include
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-
-include modules.mk
-
-DICT_FILES = $(DICTIONARIES:%=../dict/%.dia)
-DICT_MODULES = $(DICTIONARIES:%=diameter_gen_%)
-DICT_ERL_FILES = $(DICT_MODULES:%=%.erl)
-DICT_HRL_FILES = $(DICT_MODULES:%=%.hrl)
-
-MODULES = \
- $(RUNTIME_MODULES) \
- $(HELP_MODULES)
-
-APP_MODULES = \
- $(RUNTIME_MODULES) \
- $(DICT_MODULES)
-
-TARGET_MODULES = \
- $(APP_MODULES) \
- $(HELP_MODULES)
-
-TARGET_FILES = \
- $(TARGET_MODULES:%=$(EBIN)/%.$(EMULATOR)) \
- $(APP_TARGET) \
- $(APPUP_TARGET)
-
-ESCRIPT_FILES = \
- ../../bin/diameterc
-
-APP_FILE = diameter.app
-APP_SRC = $(APP_FILE).src
-APP_TARGET = $(EBIN)/$(APP_FILE)
-
-APPUP_FILE = diameter.appup
-APPUP_SRC = $(APPUP_FILE).src
-APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-
-ifeq ($(TYPE),debug)
-ERL_COMPILE_FLAGS += -Ddebug
-endif
-
-include diameter.mk
-
-ERL_COMPILE_FLAGS += \
- $(DIAMETER_ERL_COMPILE_FLAGS) \
- -I$(INCDIR)
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug:
- @$(MAKE) TYPE=debug opt
-
-opt: $(TARGET_FILES)
-
-clean:
- rm -f $(TARGET_FILES) $(DICT_ERL_FILES) $(DICT_HRL_FILES)
- rm -f $(APP_TARGET) $(APPUP_TARGET)
- rm -f errs core *~ diameter_gen_*.forms diameter_gen_*.spec
- rm -f depend.mk
-
-docs:
-
-info:
- @echo ""
- @echo "DICTIONARIES = $(DICTIONARIES)"
- @echo "MODULES = $(MODULES)"
- @echo ""
- @echo "EXTERNAL_HRL_FILES = $(EXTERNAL_HRL_FILES)"
- @echo "INTERNAL_HRL_FILES = $(INTERNAL_HRL_FILES)"
- @echo ""
- @echo "EXAMPLE_FILES = $(EXAMPLE_FILES)"
- @echo ""
-
-# ----------------------------------------------------
-# Special Build Targets
-# ----------------------------------------------------
-
-# erl/hrl from application spec
-diameter_gen_%.erl diameter_gen_%.hrl: ../dict/%.dia
- ../../bin/diameterc -i $(EBIN) $<
-
-# Generate the app file and then modules into in. This shouldn't know
-# about ../transport but good enough for now.
-$(APP_TARGET): $(APP_SRC) \
- ../../vsn.mk \
- modules.mk \
- ../transport/modules.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
- M=`echo $(APP_MODULES) | sed -e 's/^ *//' -e 's/ *$$//' -e 'y/ /,/'`; \
- echo "/%APP_MODULES%/s//$$M/;w;q" | tr ';' '\n' \
- | ed -s $@
- $(MAKE) -C ../transport $(APP_TARGET) APP_TARGET=$(APP_TARGET)
-
-$(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-# This isn't a dependency for $(DICT_ERL_FILES) $(DICT_HRL_FILES)
-# since it would force recompilation: compiler must explicity be made
-# first, which ../Makefile does. Should share a makefile for proper
-# dependencies. (Aka, recursive make considered harmful.)
-compiler:
- $(MAKE) -C ../$@
-
-app: $(APP_TARGET) $(APPUP_TARGET)
-dict: $(DICT_ERL_FILES)
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/otp_release_targets.mk
-else
-include $(DIAMETER_TOP)/make/release_targets.mk
-endif
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/bin
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DIR) $(RELSYSDIR)/src/app
- $(INSTALL_DIR) $(RELSYSDIR)/src/dict
- $(INSTALL_DIR) $(RELSYSDIR)/include
- $(INSTALL_DIR) $(RELSYSDIR)/examples
- $(INSTALL_SCRIPT) $(ESCRIPT_FILES) $(RELSYSDIR)/bin
- $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(MODULES:%=%.erl) $(DICT_ERL_FILES) $(RELSYSDIR)/src/app
- $(INSTALL_DATA) $(DICT_FILES) $(RELSYSDIR)/src/dict
- $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/app
- $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(DICT_HRL_FILES) $(RELSYSDIR)/include
- $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
-
-release_docs_spec:
-
-# ----------------------------------------------------
-# Dependencies
-# ----------------------------------------------------
-
-diameter_gen_base_accounting.erl diameter_gen_relay.erl: \
- $(EBIN)/diameter_gen_base_rfc3588.beam
-
-$(DICT_ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR)): \
- $(DIAMETER_TOP)/include/diameter.hrl \
- $(DIAMETER_TOP)/include/diameter_gen.hrl
-
-depend: depend.mk
-
-# Generate dependencies makefile. It's assumed that the compile target
-# has already been made since it's currently not smart enough to not
-# force a rebuild of those beams dependent on generated hrls, and this
-# is a no-no at make release.
-depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
- (for f in $(MODULES); do \
- sed -f $< $$f.erl | sed "s@/@/$$f@"; \
- done) \
- > $@
-
--include depend.mk
-
-.PRECIOUS: $(DICT_ERL_FILES) $(DICT_HRL_FILES)
-.PHONY: app clean debug depend info opt compiler dict
-.PHONY: release_spec release_docs_spec
diff --git a/lib/diameter/src/app/depend.sed b/lib/diameter/src/app/depend.sed
deleted file mode 100644
index 9df0133960..0000000000
--- a/lib/diameter/src/app/depend.sed
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-#
-
-#
-# Extract include dependencies from .erl files. The output is massaged
-# further in Makefile.
-#
-
-/^-include/!d
-/"diameter/!d
-
-s@^-include_lib("[^/]*@$(DIAMETER_TOP)@
-s@^-include("@@
-s@".*@@
-s@^@$(EBIN)/.$(EMULATOR): @
diff --git a/lib/diameter/src/app/diameter.app.src b/lib/diameter/src/app/diameter.app.src
deleted file mode 100644
index a806b5c78a..0000000000
--- a/lib/diameter/src/app/diameter.app.src
+++ /dev/null
@@ -1,28 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-{application, diameter,
- [{description, "Diameter protocol"},
- {vsn, "%VSN%"},
- {modules, [%APP_MODULES%,%TRANSPORT_MODULES%]},
- {registered, []},
- {applications, [stdlib, kernel]},
- {env, []},
- {mod, {diameter_app, []}}
- ]}.
diff --git a/lib/diameter/src/app/diameter.appup.src b/lib/diameter/src/app/diameter.appup.src
deleted file mode 100644
index 6d8ceadb92..0000000000
--- a/lib/diameter/src/app/diameter.appup.src
+++ /dev/null
@@ -1,47 +0,0 @@
-%% This is an -*- erlang -*- file.
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-{"%VSN%",
- [
- {"0.9",
- [
- {load_module, diameter, soft_purge, soft_purge, []},
- {load_module, diameter_capx, soft_purge, soft_purge, []},
- {load_module, diameter_codec, soft_purge, soft_purge, [diameter_lib]},
- {load_module, diameter_lib, soft_purge, soft_purge, []},
- {load_module, diameter_types, soft_purge, soft_purge, []},
- {load_module, diameter_gen_base_accounting, soft_purge, soft_purge, []},
- {load_module, diameter_gen_base_rfc3588, soft_purge, soft_purge, []},
- {load_module, diameter_gen_relay, soft_purge, soft_purge, []},
- {update, diameter_service, soft, soft_purge, soft_purge, [diameter_lib]},
- {update, diameter_config, soft, soft_purge, soft_purge, []},
- {update, diameter_peer, soft, soft_purge, soft_purge, []},
- {update, diameter_peer_fsm, soft, soft_purge, soft_purge, [diameter_lib]},
- {update, diameter_reg, soft, soft_purge, soft_purge, []},
- {update, diameter_sctp, soft, soft_purge, soft_purge, []},
- {update, diameter_stats, soft, soft_purge, soft_purge, []},
- {update, diameter_sync, soft, soft_purge, soft_purge, []},
- {update, diameter_watchdog, soft, soft_purge, soft_purge, [diameter_lib]}
- ]
- }
- ],
- [
- ]
-}.
diff --git a/lib/diameter/src/app/diameter.erl b/lib/diameter/src/app/diameter.erl
deleted file mode 100644
index 2f721421d8..0000000000
--- a/lib/diameter/src/app/diameter.erl
+++ /dev/null
@@ -1,190 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter).
-
-%% Configuration.
--export([start_service/2,
- stop_service/1,
- add_transport/2,
- remove_transport/2,
- subscribe/1,
- unsubscribe/1]).
-
-%% Traffic.
--export([session_id/1,
- origin_state_id/0,
- call/3,
- call/4]).
-
-%% Information.
--export([services/0,
- service_info/2]).
-
-%% Start/stop the application. In a "real" application this should
-%% typically be a consequence of specifying diameter in a release file
-%% rather than by calling start/stop explicitly.
--export([start/0,
- stop/0]).
-
--include("diameter_internal.hrl").
--include("diameter_types.hrl").
-
-%%% --------------------------------------------------------------------------
-%%% start/0
-%%% --------------------------------------------------------------------------
-
--spec start()
- -> ok
- | {error, term()}.
-
-start() ->
- application:start(?APPLICATION).
-
-%%% --------------------------------------------------------------------------
-%%% stop/0
-%%% --------------------------------------------------------------------------
-
--spec stop()
- -> ok
- | {error, term()}.
-
-stop() ->
- application:stop(?APPLICATION).
-
-%%% --------------------------------------------------------------------------
-%%% start_service/2
-%%% --------------------------------------------------------------------------
-
--spec start_service(service_name(), [service_opt()])
- -> ok
- | {error, term()}.
-
-start_service(SvcName, Opts)
- when is_list(Opts) ->
- diameter_config:start_service(SvcName, Opts).
-
-%%% --------------------------------------------------------------------------
-%%% stop_service/1
-%%% --------------------------------------------------------------------------
-
--spec stop_service(service_name())
- -> ok
- | {error, term()}.
-
-stop_service(SvcName) ->
- diameter_config:stop_service(SvcName).
-
-%%% --------------------------------------------------------------------------
-%%% services/0
-%%% --------------------------------------------------------------------------
-
--spec services()
- -> [service_name()].
-
-services() ->
- [Name || {Name, _} <- diameter_service:services()].
-
-%%% --------------------------------------------------------------------------
-%%% service_info/2
-%%% --------------------------------------------------------------------------
-
--spec service_info(service_name(), atom() | [atom()])
- -> any().
-
-service_info(SvcName, Option) ->
- diameter_service:info(SvcName, Option).
-
-%%% --------------------------------------------------------------------------
-%%% add_transport/3
-%%% --------------------------------------------------------------------------
-
--spec add_transport(service_name(), {listen|connect, [transport_opt()]})
- -> {ok, transport_ref()}
- | {error, term()}.
-
-add_transport(SvcName, {T, Opts} = Cfg)
- when is_list(Opts), (T == connect orelse T == listen) ->
- diameter_config:add_transport(SvcName, Cfg).
-
-%%% --------------------------------------------------------------------------
-%%% remove_transport/2
-%%% --------------------------------------------------------------------------
-
--spec remove_transport(service_name(), transport_pred())
- -> ok | {error, term()}.
-
-remove_transport(SvcName, Pred) ->
- diameter_config:remove_transport(SvcName, Pred).
-
-%%% --------------------------------------------------------------------------
-%%% # subscribe(SvcName)
-%%%
-%%% Description: Subscribe to #diameter_event{} messages for the specified
-%%% service.
-%%% --------------------------------------------------------------------------
-
--spec subscribe(service_name())
- -> true.
-
-subscribe(SvcName) ->
- diameter_service:subscribe(SvcName).
-
-%%% --------------------------------------------------------------------------
-%%% # unsubscribe(SvcName)
-%%% --------------------------------------------------------------------------
-
--spec unsubscribe(service_name())
- -> true.
-
-unsubscribe(SvcName) ->
- diameter_service:unsubscribe(SvcName).
-
-%%% ----------------------------------------------------------
-%%% # session_id/1
-%%% ----------------------------------------------------------
-
--spec session_id('DiameterIdentity'())
- -> 'OctetString'().
-
-session_id(Ident) ->
- diameter_session:session_id(Ident).
-
-%%% ----------------------------------------------------------
-%%% # origin_state_id/0
-%%% ----------------------------------------------------------
-
--spec origin_state_id()
- -> 'Unsigned32'().
-
-origin_state_id() ->
- diameter_session:origin_state_id().
-
-%%% --------------------------------------------------------------------------
-%%% # call/[34]
-%%% --------------------------------------------------------------------------
-
--spec call(service_name(), app_alias(), any(), [call_opt()])
- -> any().
-
-call(SvcName, App, Message, Options) ->
- diameter_service:call(SvcName, {alias, App}, Message, Options).
-
-call(SvcName, App, Message) ->
- call(SvcName, App, Message, []).
diff --git a/lib/diameter/src/app/diameter.mk.in b/lib/diameter/src/app/diameter.mk.in
deleted file mode 100644
index c161064303..0000000000
--- a/lib/diameter/src/app/diameter.mk.in
+++ /dev/null
@@ -1,47 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-
-DIAMETER_TOP = @DIAMETER_TOP@
-
-# ifneq ($(PREFIX),)
-# ifeq ($(TESTROOT),)
-# TESTROOT = $(PREFIX)
-# endif
-# endif
-
-ifeq ($(USE_DIAMETER_TEST_CODE), true)
-ERL_COMPILE_FLAGS += -DDIAMETER_TEST_CODE=mona_lisa_spelar_doom
-endif
-
-ifeq ($(USE_DIAMETER_HIPE), true)
-ERL_COMPILE_FLAGS += +native
-endif
-
-ifeq ($(WARN_UNUSED_WARS), true)
-ERL_COMPILE_FLAGS += +warn_unused_vars
-endif
-
-DIAMETER_APP_VSN_COMPILE_FLAGS = \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
-
-DIAMETER_ERL_COMPILE_FLAGS += \
- -pa $(DIAMETER_TOP)/ebin \
- $(DIAMETER_APP_VSN_COMPILE_FLAGS)
-
diff --git a/lib/diameter/src/app/diameter_app.erl b/lib/diameter/src/app/diameter_app.erl
deleted file mode 100644
index 600f7ff04d..0000000000
--- a/lib/diameter/src/app/diameter_app.erl
+++ /dev/null
@@ -1,36 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_app).
-
--behaviour(application).
-
-%% application callbacks
--export([start/2,
- stop/1]).
-
-%% start/2
-
-start(_Type, _Args) ->
- diameter_sup:start_link().
-
-%% stop/1
-
-stop(_) ->
- ok.
diff --git a/lib/diameter/src/app/diameter_callback.erl b/lib/diameter/src/app/diameter_callback.erl
deleted file mode 100644
index 6d5c8cdca1..0000000000
--- a/lib/diameter/src/app/diameter_callback.erl
+++ /dev/null
@@ -1,91 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% A minimal application callback module.
-%%
-
--module(diameter_callback).
-
--export([peer_up/3,
- peer_down/3,
- pick_peer/4,
- prepare_request/3,
- prepare_retransmit/3,
- handle_request/3,
- handle_answer/4,
- handle_error/4]).
-
--include_lib("diameter/include/diameter.hrl").
-
-%%% ----------------------------------------------------------
-%%% # peer_up/3
-%%% ----------------------------------------------------------
-
-peer_up(_Svc, _Peer, State) ->
- State.
-
-%%% ----------------------------------------------------------
-%%% # peer_down/3
-%%% ----------------------------------------------------------
-
-peer_down(_SvcName, _Peer, State) ->
- State.
-
-%%% ----------------------------------------------------------
-%%% # pick_peer/4
-%%% ----------------------------------------------------------
-
-pick_peer([Peer|_], _, _SvcName, _State) ->
- {ok, Peer}.
-
-%%% ----------------------------------------------------------
-%%% # prepare_request/3
-%%% ----------------------------------------------------------
-
-prepare_request(Pkt, _SvcName, _Peer) ->
- {send, Pkt}.
-
-%%% ----------------------------------------------------------
-%%% # prepare_retransmit/3
-%%% ----------------------------------------------------------
-
-prepare_retransmit(Pkt, _SvcName, _Peer) ->
- {send, Pkt}.
-
-%%% ----------------------------------------------------------
-%%% # handle_request/3
-%%% ----------------------------------------------------------
-
-handle_request(_Pkt, _SvcName, _Peer) ->
- {protocol_error, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED
-
-%%% ----------------------------------------------------------
-%%% # handle_answer/4
-%%% ----------------------------------------------------------
-
-handle_answer(#diameter_packet{msg = Ans}, _Req, _SvcName, _Peer) ->
- Ans.
-
-%%% ---------------------------------------------------------------------------
-%%% # handle_error/4
-%%% ---------------------------------------------------------------------------
-
-handle_error(Reason, _Req, _SvcName, _Peer) ->
- {error, Reason}.
diff --git a/lib/diameter/src/app/diameter_capx.erl b/lib/diameter/src/app/diameter_capx.erl
deleted file mode 100644
index 138e76411e..0000000000
--- a/lib/diameter/src/app/diameter_capx.erl
+++ /dev/null
@@ -1,405 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% This module builds CER and CEA records for use during capabilities
-%% exchange. All of a CER/CEA is built from AVP values configured on
-%% the service in question but values for Supported-Vendor-Id,
-%% Vendor-Specific-Application-Id, Auth-Application-Id and
-%% Acct-Application-id are also obtained using an older method that
-%% remains only for backwards compatibility. With this method, each
-%% dictionary module was required to export a cer/0 that returned a
-%% diameter_base_CER record (or corresponding list, although the list
-%% is also a later addition). Each returned CER contributes its member
-%% values for the aforementioned four AVPs to the resulting CER, with
-%% remaining AVP's either unspecified or identical to those configured
-%% on the service. Auth-Application-Id and Acct-Application-id were
-%% originally treated a little differently, each callback being
-%% required to return either no value of the same value as the other
-%% callbacks, but this coupled the callback modules unnecessarily. (A
-%% union is backwards compatible to boot.)
-%%
-%% Values obtained from the service and callbacks are all included
-%% when building a CER. Older code with only callback can continue to
-%% use them, newer code should probably stick to service configuration
-%% (since this is more explicit) or mix at their own peril.
-%%
-%% The cer/0 callback is now undocumented (despite never being fully
-%% documented to begin with) and should be considered deprecated even
-%% by those poor souls still using it.
-%%
-
--module(diameter_capx).
-
--export([build_CER/1,
- recv_CER/2,
- recv_CEA/2,
- make_caps/2]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
--include("diameter_types.hrl").
--include("diameter_gen_base_rfc3588.hrl").
-
--define(SUCCESS, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
--define(NOAPP, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_NO_COMMON_APPLICATION').
--define(NOSECURITY, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_NO_COMMON_SECURITY').
-
--define(NO_INBAND_SECURITY, 0).
--define(TLS, 1).
-
-%% ===========================================================================
-
--type tried(T) :: {ok, T} | {error, {term(), list()}}.
-
--spec build_CER(#diameter_caps{})
- -> tried(#diameter_base_CER{}).
-
-build_CER(Caps) ->
- try_it([fun bCER/1, Caps]).
-
--spec recv_CER(#diameter_base_CER{}, #diameter_service{})
- -> tried({['Unsigned32'()], #diameter_caps{}, #diameter_base_CEA{}}).
-
-recv_CER(CER, Svc) ->
- try_it([fun rCER/2, CER, Svc]).
-
--spec recv_CEA(#diameter_base_CEA{}, #diameter_service{})
- -> tried({['Unsigned32'()], ['Unsigned32'()], #diameter_caps{}}).
-
-recv_CEA(CEA, Svc) ->
- try_it([fun rCEA/2, CEA, Svc]).
-
-make_caps(Caps, Opts) ->
- try_it([fun mk_caps/2, Caps, Opts]).
-
-%% ===========================================================================
-%% ===========================================================================
-
-try_it([Fun | Args]) ->
- try apply(Fun, Args) of
- T -> {ok, T}
- catch
- throw: ?FAILURE(Reason) -> {error, {Reason, Args}}
- end.
-
-%% mk_caps/2
-
-mk_caps(Caps0, Opts) ->
- {Caps, _} = lists:foldl(fun set_cap/2,
- {Caps0, #diameter_caps{_ = false}},
- Opts),
- Caps.
-
--define(SC(K,F),
- set_cap({K, Val}, {Caps, #diameter_caps{F = false} = C}) ->
- {Caps#diameter_caps{F = cap(K, Val)}, C#diameter_caps{F = true}}).
-
-?SC('Origin-Host', origin_host);
-?SC('Origin-Realm', origin_realm);
-?SC('Host-IP-Address', host_ip_address);
-?SC('Vendor-Id', vendor_id);
-?SC('Product-Name', product_name);
-?SC('Origin-State-Id', origin_state_id);
-?SC('Supported-Vendor-Id', supported_vendor_id);
-?SC('Auth-Application-Id', auth_application_id);
-?SC('Inband-Security-Id', inband_security_id);
-?SC('Acct-Application-Id', acct_application_id);
-?SC('Vendor-Specific-Application-Id', vendor_specific_application_id);
-?SC('Firmware-Revision', firmware_revision);
-
-set_cap({Key, _}, _) ->
- ?THROW({duplicate, Key}).
-
-cap(K, V)
- when K == 'Origin-Host';
- K == 'Origin-Realm';
- K == 'Vendor-Id';
- K == 'Product-Name' ->
- V;
-
-cap('Host-IP-Address', Vs)
- when is_list(Vs) ->
- lists:map(fun ipaddr/1, Vs);
-
-cap('Firmware-Revision', V) ->
- [V];
-
-cap(_, Vs)
- when is_list(Vs) ->
- Vs;
-
-cap(K, V) ->
- ?THROW({invalid, K, V}).
-
-ipaddr(A) ->
- try
- diameter_lib:ipaddr(A)
- catch
- error: {invalid_address, _} = T ->
- ?THROW(T)
- end.
-
-%% bCER/1
-%%
-%% Build a CER record to send to a remote peer.
-
-%% Use the fact that diameter_caps has the same field names as CER.
-bCER(#diameter_caps{} = Rec) ->
- #diameter_base_CER{}
- = list_to_tuple([diameter_base_CER | tl(tuple_to_list(Rec))]).
-
-%% rCER/2
-%%
-%% Build a CEA record to send to a remote peer in response to an
-%% incoming CER. RFC 3588 gives no guidance on what should be sent
-%% here: should we advertise applications that the peer hasn't sent in
-%% its CER (aside from the relay application) or not? If we send
-%% applications that the peer hasn't advertised then the peer may have
-%% to be aware of the possibility. If we don't then we just look like
-%% a server that supports a subset (possibly) of what the client
-%% advertised, so this feels like the path of least incompatibility.
-%% However, the current draft standard (draft-ietf-dime-rfc3588bis-26,
-%% expires 24 July 2011) says this in section 5.3, Capabilities
-%% Exchange:
-%%
-%% The receiver of the Capabilities-Exchange-Request (CER) MUST
-%% determine common applications by computing the intersection of its
-%% own set of supported Application Id against all of the application
-%% identifier AVPs (Auth-Application-Id, Acct-Application-Id and Vendor-
-%% Specific-Application-Id) present in the CER. The value of the
-%% Vendor-Id AVP in the Vendor-Specific-Application-Id MUST NOT be used
-%% during computation. The sender of the Capabilities-Exchange-Answer
-%% (CEA) SHOULD include all of its supported applications as a hint to
-%% the receiver regarding all of its application capabilities.
-%%
-%% Both RFC and the draft also say this:
-%%
-%% The receiver only issues commands to its peers that have advertised
-%% support for the Diameter application that defines the command. A
-%% Diameter node MUST cache the supported applications in order to
-%% ensure that unrecognized commands and/or AVPs are not unnecessarily
-%% sent to a peer.
-%%
-%% That is, each side sends all of its capabilities and is responsible for
-%% not sending commands that the peer doesn't support.
-
-%% 6.10. Inband-Security-Id AVP
-%%
-%% NO_INBAND_SECURITY 0
-%% This peer does not support TLS. This is the default value, if the
-%% AVP is omitted.
-%%
-%% TLS 1
-%% This node supports TLS security, as defined by [TLS].
-
-rCER(CER, #diameter_service{capabilities = LCaps} = Svc) ->
- #diameter_base_CEA{}
- = CEA
- = cea_from_cer(bCER(LCaps)),
-
- RCaps = capx_to_caps(CER),
- SApps = common_applications(LCaps, RCaps, Svc),
-
- {SApps,
- RCaps,
- build_CEA(SApps,
- LCaps,
- RCaps,
- CEA#diameter_base_CEA{'Result-Code' = ?SUCCESS})}.
-
-%% TODO: 5.3 of RFC 3588 says we MUST return DIAMETER_NO_COMMON_APPLICATION
-%% in the CEA and SHOULD disconnect the transport. However, we have
-%% no way to guarantee the send before disconnecting.
-
-build_CEA([], _, _, CEA) ->
- CEA#diameter_base_CEA{'Result-Code' = ?NOAPP};
-
-build_CEA(_, LCaps, RCaps, CEA) ->
- case common_security(LCaps, RCaps) of
- [] ->
- CEA#diameter_base_CEA{'Result-Code' = ?NOSECURITY};
- [_] = IS ->
- CEA#diameter_base_CEA{'Inband-Security-Id' = IS}
- end.
-
-%% common_security/2
-
-common_security(#diameter_caps{inband_security_id = LS},
- #diameter_caps{inband_security_id = RS}) ->
- cs(LS, RS).
-
-%% Unspecified is equivalent to NO_INBAND_SECURITY.
-cs([], RS) ->
- cs([?NO_INBAND_SECURITY], RS);
-cs(LS, []) ->
- cs(LS, [?NO_INBAND_SECURITY]);
-
-%% Agree on TLS if both parties support it. When sending CEA, this is
-%% to ensure the peer is clear that we will be expecting a TLS
-%% handshake since there is no ssl:maybe_accept that would allow the
-%% peer to choose between TLS or not upon reception of our CEA. When
-%% receiving CEA it deals with a server that isn't explicit about its choice.
-%% TODO: Make the choice configurable.
-cs(LS, RS) ->
- Is = ordsets:to_list(ordsets:intersection(ordsets:from_list(LS),
- ordsets:from_list(RS))),
- case lists:member(?TLS, Is) of
- true ->
- [?TLS];
- false when [] == Is ->
- Is;
- false ->
- [hd(Is)] %% probably NO_INBAND_SECURITY
- end.
-%% The only two values defined by RFC 3588 are NO_INBAND_SECURITY and
-%% TLS but don't enforce this. In theory this allows some other
-%% security mechanism we don't have to know about, although in
-%% practice something there may be a need for more synchronization
-%% than notification by way of an event subscription offers.
-
-%% cea_from_cer/1
-
-%% CER is a subset of CEA, the latter adding Result-Code and a few
-%% more AVP's.
-cea_from_cer(#diameter_base_CER{} = CER) ->
- lists:foldl(fun(F,A) -> to_cea(CER, F, A) end,
- #diameter_base_CEA{},
- record_info(fields, diameter_base_CER)).
-
-to_cea(CER, Field, CEA) ->
- try ?BASE:'#get-'(Field, CER) of
- V -> ?BASE:'#set-'({Field, V}, CEA)
- catch
- error: _ -> CEA
- end.
-
-%% rCEA/2
-
-rCEA(#diameter_base_CEA{'Result-Code' = RC}
- = CEA,
- #diameter_service{capabilities = LCaps}
- = Svc) ->
- RC == ?SUCCESS orelse ?THROW({'Result-Code', RC}),
-
- RCaps = capx_to_caps(CEA),
- SApps = common_applications(LCaps, RCaps, Svc),
-
- [] == SApps andalso ?THROW(no_common_applications),
-
- IS = common_security(LCaps, RCaps),
-
- [] == IS andalso ?THROW(no_common_security),
-
- {SApps, IS, RCaps};
-
-rCEA(CEA, _Svc) ->
- ?THROW({invalid, CEA}).
-
-%% capx_to_caps/1
-
-capx_to_caps(#diameter_base_CEA{'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'Host-IP-Address' = IP,
- 'Vendor-Id' = VId,
- 'Product-Name' = PN,
- 'Origin-State-Id' = OSI,
- 'Supported-Vendor-Id' = SV,
- 'Auth-Application-Id' = Auth,
- 'Inband-Security-Id' = IS,
- 'Acct-Application-Id' = Acct,
- 'Vendor-Specific-Application-Id' = VSA,
- 'Firmware-Revision' = FR,
- 'AVP' = X}) ->
- #diameter_caps{origin_host = OH,
- origin_realm = OR,
- vendor_id = VId,
- product_name = PN,
- origin_state_id = OSI,
- host_ip_address = IP,
- supported_vendor_id = SV,
- auth_application_id = Auth,
- inband_security_id = IS,
- acct_application_id = Acct,
- vendor_specific_application_id = VSA,
- firmware_revision = FR,
- avp = X};
-
-capx_to_caps(#diameter_base_CER{} = CER) ->
- capx_to_caps(cea_from_cer(CER)).
-
-%% ---------------------------------------------------------------------------
-%% ---------------------------------------------------------------------------
-
-%% common_applications/3
-%%
-%% Identify the (local) applications to be supported on the connection
-%% in question.
-
-common_applications(LCaps, RCaps, #diameter_service{applications = Apps}) ->
- LA = app_union(LCaps),
- RA = app_union(RCaps),
-
- lists:foldl(fun(I,A) -> ca(I, Apps, RA, A) end, [], LA).
-
-ca(Id, Apps, RA, Acc) ->
- Relay = lists:member(?APP_ID_RELAY, RA),
- #diameter_app{alias = Alias} = find_app(Id, Apps),
- tcons(Relay %% peer is a relay
- orelse ?APP_ID_RELAY == Id %% we're a relay
- orelse lists:member(Id, RA), %% app is supported by the peer
- Id,
- Alias,
- Acc).
-%% 5.3 of the RFC states that a peer advertising itself as a relay must
-%% be interpreted as having common applications.
-
-%% Extract the list of all application identifiers from Auth-Application-Id,
-%% Acct-Application-Id and Vendor-Specific-Application-Id.
-app_union(#diameter_caps{auth_application_id = U,
- acct_application_id = C,
- vendor_specific_application_id = V}) ->
- set_list(U ++ C ++ lists:flatmap(fun vsa_apps/1, V)).
-
-vsa_apps(#'diameter_base_Vendor-Specific-Application-Id'
- {'Auth-Application-Id' = U,
- 'Acct-Application-Id' = C}) ->
- U ++ C;
-vsa_apps(L) ->
- Rec = ?BASE:'#new-'('diameter_base_Vendor-Specific-Application-Id', L),
- vsa_apps(Rec).
-
-%% It's a configuration error for a locally advertised application not
-%% to be represented in Apps. Don't just match on lists:keyfind/3 in
-%% order to generate a more helpful error.
-find_app(Id, Apps) ->
- case lists:keyfind(Id, #diameter_app.id, Apps) of
- #diameter_app{} = A ->
- A;
- false ->
- ?THROW({app_not_configured, Id})
- end.
-
-set_list(L) ->
- sets:to_list(sets:from_list(L)).
-
-tcons(true, K, V, Acc) ->
- [{K,V} | Acc];
-tcons(false, _, _, Acc) ->
- Acc.
diff --git a/lib/diameter/src/app/diameter_codec.erl b/lib/diameter/src/app/diameter_codec.erl
deleted file mode 100644
index d88f42fb7c..0000000000
--- a/lib/diameter/src/app/diameter_codec.erl
+++ /dev/null
@@ -1,561 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_codec).
-
--export([encode/2,
- decode/2,
- decode/3,
- collect_avps/1,
- decode_header/1,
- sequence_numbers/1,
- hop_by_hop_id/2,
- msg_name/1,
- msg_id/1]).
-
-%% Towards generated encoders (from diameter_gen.hrl).
--export([pack_avp/1,
- pack_avp/2]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
--define(MASK(N,I), ((I) band (1 bsl (N)))).
-
-%% 0 1 2 3
-%% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | Version | Message Length |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | command flags | Command-Code |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | Application-ID |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | Hop-by-Hop Identifier |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | End-to-End Identifier |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | AVPs ...
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-
-
-%%% ---------------------------------------------------------------------------
-%%% # encode/[2-4]
-%%% ---------------------------------------------------------------------------
-
-encode(Mod, #diameter_packet{} = Pkt) ->
- try
- e(Mod, Pkt)
- catch
- error: Reason ->
- %% Be verbose rather than letting the emulator truncate the
- %% error report.
- X = {Reason, ?STACK},
- diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}),
- exit(X)
- end;
-
-encode(Mod, Msg) ->
- Seq = diameter_session:sequence(),
- Hdr = #diameter_header{version = ?DIAMETER_VERSION,
- end_to_end_id = Seq,
- hop_by_hop_id = Seq},
- encode(Mod, #diameter_packet{header = Hdr,
- msg = Msg}).
-
-e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
- Avps = encode_avps(As),
- Length = size(Avps) + 20,
-
- #diameter_header{version = Vsn,
- cmd_code = Code,
- application_id = Aid,
- hop_by_hop_id = Hid,
- end_to_end_id = Eid}
- = Hdr,
-
- Flags = make_flags(0, Hdr),
-
- Pkt#diameter_packet{bin = <>};
-
-e(Mod0, #diameter_packet{header = Hdr, msg = Msg} = Pkt) ->
- #diameter_header{version = Vsn,
- hop_by_hop_id = Hid,
- end_to_end_id = Eid}
- = Hdr,
-
- {Mod, MsgName} = rec2msg(Mod0, Msg),
- {Code, Flags0, Aid} = msg_header(Mod, MsgName, Hdr),
- Flags = make_flags(Flags0, Hdr),
-
- Avps = encode_avps(Mod, MsgName, values(Msg)),
- Length = size(Avps) + 20,
-
- Pkt#diameter_packet{header = Hdr#diameter_header
- {length = Length,
- cmd_code = Code,
- application_id = Aid,
- is_request = 0 /= ?MASK(7, Flags),
- is_proxiable = 0 /= ?MASK(6, Flags),
- is_error = 0 /= ?MASK(5, Flags),
- is_retransmitted = 0 /= ?MASK(4, Flags)},
- bin = <>}.
-
-%% make_flags/2
-
-make_flags(Flags0, #diameter_header{is_request = R,
- is_proxiable = P,
- is_error = E,
- is_retransmitted = T}) ->
- {Flags, 3} = lists:foldl(fun(B,{F,N}) -> {mf(B,F,N), N-1} end,
- {Flags0, 7},
- [R,P,E,T]),
- Flags.
-
-mf(undefined, F, _) ->
- F;
-mf(B, F, N) -> %% reset the affected bit
- (F bxor (F band (1 bsl N))) bor bit(B, N).
-
-bit(true, N) -> 1 bsl N;
-bit(false, _) -> 0.
-
-%% values/1
-
-values([H|T])
- when is_atom(H) ->
- T;
-values(Avps) ->
- Avps.
-
-%% encode_avps/3
-
-%% Specifying values as a #diameter_avp list bypasses arity and other
-%% checks: the values are expected to be already encoded and the AVP's
-%% presented are simply sent. This is needed for relay agents, since
-%% these have to be able to resend whatever comes.
-
-%% Message as a list of #diameter_avp{} ...
-encode_avps(_, _, [#diameter_avp{} | _] = Avps) ->
- encode_avps(reorder(Avps, [], Avps));
-
-%% ... or as a tuple list or record.
-encode_avps(Mod, MsgName, Values) ->
- Mod:encode_avps(MsgName, Values).
-
-%% reorder/1
-
-reorder([#diameter_avp{index = 0} | _] = Avps, Acc, _) ->
- Avps ++ Acc;
-
-reorder([#diameter_avp{index = N} = A | Avps], Acc, _)
- when is_integer(N) ->
- lists:reverse(Avps, [A | Acc]);
-
-reorder([H | T], Acc, Avps) ->
- reorder(T, [H | Acc], Avps);
-
-reorder([], Acc, _) ->
- Acc.
-
-%% encode_avps/1
-
-encode_avps(Avps) ->
- list_to_binary(lists:map(fun pack_avp/1, Avps)).
-
-%% msg_header/3
-
-msg_header(Mod, MsgName, Header) ->
- {Code, Flags, ApplId} = h(Mod, MsgName, Header),
- {Code, p(Flags, Header), ApplId}.
-
-%% 6.2 of 3588 requires the same 'P' bit on an answer as on the
-%% request.
-
-p(Flags, #diameter_header{is_request = true,
- is_proxiable = P}) ->
- Flags band (2#10110000 bor choose(P, 2#01000000, 0));
-p(Flags, _) ->
- Flags.
-
-h(Mod, 'answer-message' = MsgName, Header) ->
- ?BASE = Mod,
- #diameter_header{cmd_code = Code} = Header,
- {_, Flags, ApplId} = ?BASE:msg_header(MsgName),
- {Code, Flags, ApplId};
-
-h(Mod, MsgName, _) ->
- Mod:msg_header(MsgName).
-
-%% rec2msg/2
-
-rec2msg(_, ['answer-message' = M | _]) ->
- {?BASE, M};
-
-rec2msg(Mod, [MsgName|_])
- when is_atom(MsgName) ->
- {Mod, MsgName};
-
-rec2msg(Mod, Rec) ->
- R = element(1, Rec),
- A = 'answer-message',
- case ?BASE:msg2rec(A) of
- R ->
- {?BASE, A};
- _ ->
- {Mod, Mod:rec2msg(R)}
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # decode/2
-%%% ---------------------------------------------------------------------------
-
-%% Unsuccessfully decoded AVPs will be placed in #diameter_packet.errors.
-
-decode(Mod, Pkt) ->
- decode(Mod:id(), Mod, Pkt).
-
-%% If we're a relay application then just extract the avp's without
-%% any decoding of their data since we don't know the application in
-%% question.
-decode(?APP_ID_RELAY, _, #diameter_packet{} = Pkt) ->
- case collect_avps(Pkt) of
- {Bs, As} ->
- Pkt#diameter_packet{avps = As,
- errors = [Bs]};
- As ->
- Pkt#diameter_packet{avps = As}
- end;
-
-%% Otherwise decode using the dictionary.
-decode(_, Mod, #diameter_packet{header = Hdr} = Pkt)
- when is_atom(Mod) ->
- #diameter_header{cmd_code = CmdCode,
- is_request = IsRequest,
- is_error = IsError}
- = Hdr,
-
- {M, MsgName} = if IsError andalso not IsRequest ->
- {?BASE, 'answer-message'};
- true ->
- {Mod, Mod:msg_name(CmdCode, IsRequest)}
- end,
-
- decode_avps(MsgName, M, Pkt, collect_avps(Pkt));
-
-decode(Id, Mod, Bin)
- when is_bitstring(Bin) ->
- decode(Id, Mod, #diameter_packet{header = decode_header(Bin), bin = Bin}).
-
-decode_avps(MsgName, Mod, Pkt, {Bs, Avps}) -> %% invalid avp bits ...
- ?LOG(invalid, Pkt#diameter_packet.bin),
- #diameter_packet{errors = Failed}
- = P
- = decode_avps(MsgName, Mod, Pkt, Avps),
- P#diameter_packet{errors = [Bs | Failed]};
-
-decode_avps('', Mod, Pkt, Avps) -> %% unknown message ...
- ?LOG(unknown, {Mod, Pkt#diameter_packet.header}),
- Pkt#diameter_packet{avps = lists:reverse(Avps),
- errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED
-%% msg = undefined identifies this case.
-
-decode_avps(MsgName, Mod, Pkt, Avps) -> %% ... or not
- {Rec, As, Failed} = Mod:decode_avps(MsgName, Avps),
- ?LOGC([] /= Failed, failed, {Mod, Failed}),
- Pkt#diameter_packet{msg = Rec,
- errors = Failed,
- avps = As}.
-
-%%% ---------------------------------------------------------------------------
-%%% # decode_header/1
-%%% ---------------------------------------------------------------------------
-
-decode_header(<>) ->
- <>
- = CmdFlags,
- %% 3588 (ch 3) says that reserved bits MUST be set to 0 and ignored
- %% by the receiver.
-
- %% The RFC is quite unclear about the order of the bits in this
- %% case. It writes
- %%
- %% 0 1 2 3 4 5 6 7
- %% +-+-+-+-+-+-+-+-+
- %% |R P E T r r r r|
- %% +-+-+-+-+-+-+-+-+
- %%
- %% in defining these but the scale refers to the (big endian)
- %% transmission order, first to last, not the bit order. That is,
- %% R is the high order bit. It's odd that a standard reserves
- %% low-order bit rather than high-order ones.
-
- #diameter_header{version = Version,
- length = MsgLength,
- cmd_code = CmdCode,
- application_id = ApplicationId,
- hop_by_hop_id = HopByHopId,
- end_to_end_id = EndToEndId,
- is_request = 1 == R,
- is_proxiable = 1 == P,
- is_error = 1 == E,
- is_retransmitted = 1 == T};
-
-decode_header(_) ->
- false.
-
-%%% ---------------------------------------------------------------------------
-%%% # sequence_numbers/1
-%%% ---------------------------------------------------------------------------
-
-%% The End-To-End identifier must be unique for at least 4 minutes. We
-%% maintain a 24-bit wraparound counter, and add an 8-bit persistent
-%% wraparound counter. The 8-bit counter is incremented each time the
-%% system is restarted.
-
-sequence_numbers(#diameter_packet{bin = Bin})
- when is_binary(Bin) ->
- sequence_numbers(Bin);
-
-sequence_numbers(#diameter_packet{header = #diameter_header{} = H}) ->
- sequence_numbers(H);
-
-sequence_numbers(#diameter_header{hop_by_hop_id = H,
- end_to_end_id = E}) ->
- {H,E};
-
-sequence_numbers(<<_:12/binary, H:32, E:32, _/binary>>) ->
- {H,E}.
-
-%%% ---------------------------------------------------------------------------
-%%% # hop_by_hop_id/2
-%%% ---------------------------------------------------------------------------
-
-hop_by_hop_id(Id, <>) ->
- <>.
-
-%%% ---------------------------------------------------------------------------
-%%% # msg_name/1
-%%% ---------------------------------------------------------------------------
-
-msg_name(#diameter_header{application_id = ?APP_ID_COMMON,
- cmd_code = C,
- is_request = R}) ->
- ?BASE:msg_name(C,R);
-
-msg_name(Hdr) ->
- msg_id(Hdr).
-
-%% Note that messages in different applications could have the same
-%% name.
-
-%%% ---------------------------------------------------------------------------
-%%% # msg_id/1
-%%% ---------------------------------------------------------------------------
-
-msg_id(#diameter_packet{msg = [#diameter_header{} = Hdr | _]}) ->
- msg_id(Hdr);
-
-msg_id(#diameter_packet{header = #diameter_header{} = Hdr}) ->
- msg_id(Hdr);
-
-msg_id(#diameter_header{application_id = A,
- cmd_code = C,
- is_request = R}) ->
- {A, C, if R -> 1; true -> 0 end};
-
-msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/bitstring>>) ->
- {ApplId, CmdCode, Rbit}.
-
-%%% ---------------------------------------------------------------------------
-%%% # collect_avps/1
-%%% ---------------------------------------------------------------------------
-
-%% Note that the returned list of AVP's is reversed relative to their
-%% order in the binary. Note also that grouped avp's aren't unraveled,
-%% only those at the top level.
-
-collect_avps(#diameter_packet{bin = Bin}) ->
- <<_:20/binary, Avps/bitstring>> = Bin,
- collect_avps(Avps);
-
-collect_avps(Bin) ->
- collect_avps(Bin, 0, []).
-
-collect_avps(<<>>, _, Acc) ->
- Acc;
-collect_avps(Bin, N, Acc) ->
- try split_avp(Bin) of
- {Rest, AVP} ->
- collect_avps(Rest, N+1, [AVP#diameter_avp{index = N} | Acc])
- catch
- ?FAILURE(_) ->
- {Bin, Acc}
- end.
-
-%% 0 1 2 3
-%% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | AVP Code |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% |V M P r r r r r| AVP Length |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | Vendor-ID (opt) |
-%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-%% | Data ...
-%% +-+-+-+-+-+-+-+-+
-
-%% split_avp/1
-
-split_avp(Bin) ->
- 8 =< size(Bin) orelse ?THROW(truncated_header),
-
- <>
- = Bin,
-
- DataSize = Length - 8, % size(Code+Flags+Length) = 8 octets
- PadSize = (4 - (DataSize rem 4)) rem 4,
-
- DataSize + PadSize =< size(Rest)
- orelse ?THROW(truncated_data),
-
- <>
- = Rest,
- <>
- = Flags,
-
- 0 == Vbit orelse 4 =< size(Data)
- orelse ?THROW(truncated_vendor_id),
-
- {Vid, D} = vid(Vbit, Data),
- {R, #diameter_avp{code = Code,
- vendor_id = Vid,
- is_mandatory = 1 == Mbit,
- need_encryption = 1 == Pbit,
- data = D}}.
-
-%% The RFC is a little misleading when stating that OctetString is
-%% padded to a 32-bit boundary while other types align naturally. All
-%% other types are already multiples of 32 bits so there's no need to
-%% distinguish between types here. Any invalid lengths will result in
-%% decode error in diameter_types.
-
-vid(1, <>) ->
- {Vid, Data};
-vid(0, Data) ->
- {undefined, Data}.
-
-%%% ---------------------------------------------------------------------------
-%%% # pack_avp/1
-%%% ---------------------------------------------------------------------------
-
-%% The normal case here is data as an #diameter_avp{} list or an
-%% iolist, which are the cases that generated codec modules use. The
-%% other case is as a convenience in the relay case in which the
-%% dictionary doesn't know about specific AVP's.
-
-%% Grouped AVP whose components need packing ...
-pack_avp(#diameter_avp{data = [#diameter_avp{} | _] = Avps} = A) ->
- pack_avp(A#diameter_avp{data = encode_avps(Avps)});
-
-%% ... data as a type/value tuple, possibly with header data, ...
-pack_avp(#diameter_avp{data = {Type, Value}} = A)
- when is_atom(Type) ->
- pack_avp(A#diameter_avp{data = diameter_types:Type(encode, Value)});
-pack_avp(#diameter_avp{data = {{_,_,_} = T, {Type, Value}}}) ->
- pack_avp(T, iolist_to_binary(diameter_types:Type(encode, Value)));
-pack_avp(#diameter_avp{data = {{_,_,_} = T, Bin}})
- when is_binary(Bin) ->
- pack_avp(T, Bin);
-pack_avp(#diameter_avp{data = {Dict, Name, Value}} = A) ->
- {Code, _Flags, Vid} = Hdr = Dict:avp_header(Name),
- {Name, Type} = Dict:avp_name(Code, Vid),
- pack_avp(A#diameter_avp{data = {Hdr, {Type, Value}}});
-
-%% ... or as an iolist.
-pack_avp(#diameter_avp{code = Code,
- vendor_id = V,
- is_mandatory = M,
- need_encryption = P,
- data = Data}) ->
- Flags = lists:foldl(fun flag_avp/2, 0, [{V /= undefined, 2#10000000},
- {M, 2#01000000},
- {P, 2#00100000}]),
- pack_avp({Code, Flags, V}, iolist_to_binary(Data)).
-
-flag_avp({true, B}, F) ->
- F bor B;
-flag_avp({false, _}, F) ->
- F.
-
-%%% ---------------------------------------------------------------------------
-%%% # pack_avp/2
-%%% ---------------------------------------------------------------------------
-
-pack_avp({Code, Flags, VendorId}, Bin)
- when is_binary(Bin) ->
- Sz = size(Bin),
- pack_avp(Code, Flags, VendorId, Sz, pad(Sz rem 4, Bin)).
-
-pad(0, Bin) ->
- Bin;
-pad(N, Bin) ->
- P = 8*(4-N),
- <>.
-%% Note that padding is not included in the length field as mandated by
-%% the RFC.
-
-%% pack_avp/5
-%%
-%% Prepend the vendor id as required.
-
-pack_avp(Code, Flags, Vid, Sz, Bin)
- when 0 == Flags band 2#10000000 ->
- undefined = Vid, %% sanity check
- pack_avp(Code, Flags, Sz, Bin);
-
-pack_avp(Code, Flags, Vid, Sz, Bin) ->
- pack_avp(Code, Flags, Sz+4, <>).
-
-%% pack_avp/4
-
-pack_avp(Code, Flags, Sz, Bin) ->
- Length = Sz + 8,
- <>.
-
-%% ===========================================================================
-
-choose(true, X, _) -> X;
-choose(false, _, X) -> X.
diff --git a/lib/diameter/src/app/diameter_config.erl b/lib/diameter/src/app/diameter_config.erl
deleted file mode 100644
index a6b48fe65b..0000000000
--- a/lib/diameter/src/app/diameter_config.erl
+++ /dev/null
@@ -1,676 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% This module writes service/transport configuration to the table
-%% diameter_config, so that the config will survive service process
-%% death, and then turns it into calls towards diameter_service. It
-%% also restarts services upon their death.
-%%
-%% The table diameter_config is only written here while
-%% diameter_service reads. This is all somewhat after the fact. Once
-%% upon a time the config was only stored in the service process,
-%% causing much grief if these processes died (which they did with
-%% some regularity) and one was forced to reconfigure. This module was
-%% then inserted into the service start in order to keep a more
-%% permanent record of the config. That said, service processes are
-%% now much more robust than they once were and crashing is a thing of
-%% the past.
-%%
-
--module(diameter_config).
--compile({no_auto_import, [monitor/2]}).
-
--behaviour(gen_server).
-
--export([start_service/2,
- stop_service/1,
- add_transport/2,
- remove_transport/2,
- have_transport/2,
- lookup/1]).
-
-%% child server start
--export([start_link/0]).
-
-%% gen_server callbacks
--export([init/1,
- terminate/2,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- code_change/3]).
-
-%% diameter_sync requests.
--export([sync/1]).
-
-%% debug
--export([state/0,
- uptime/0]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
-%% Server state.
--record(state, {id = now()}).
-
-%% Registered name of the server.
--define(SERVER, ?MODULE).
-
-%% Table config is written to.
--define(TABLE, ?MODULE).
-
-%% Workaround for dialyzer's lack of understanding of match specs.
--type match(T)
- :: T | '_' | '$1' | '$2' | '$3' | '$4'.
-
-%% Configuration records in ?TABLE.
-
--record(service,
- {name,
- rec :: match(#diameter_service{}),
- options :: match(list())}).
-
--record(transport,
- {service, %% name
- ref = make_ref() :: match(reference()),
- type :: match(connect | listen),
- options :: match(list())}).
-
-%% Monitor entry in ?TABLE.
--record(monitor, {mref = make_ref() :: reference(),
- service}). %% name
-
-%% Time to lay low before restarting a dead service.
--define(RESTART_SLEEP, 2000).
-
-%% A minimal diameter_caps for checking for valid capabilities values.
--define(EXAMPLE_CAPS,
- #diameter_caps{origin_host = "TheHost",
- origin_realm = "TheRealm",
- host_ip_address = [{127,0,0,1}],
- vendor_id = 42,
- product_name = "TheProduct"}).
-
--define(VALUES(Rec), tl(tuple_to_list(Rec))).
-
-%%% The return values below assume the server diameter_config is started.
-%%% The functions will exit if it isn't.
-
-%% --------------------------------------------------------------------------
-%% # start_service(SvcName, Opts)
-%%
-%% Output: ok | {error, Reason}
-%% --------------------------------------------------------------------------
-
-start_service(SvcName, Opts)
- when is_list(Opts) ->
- start_rc(sync(SvcName, {start_service, SvcName, Opts})).
-
-start_rc({ok = T, _Pid}) ->
- T;
-start_rc({error, _} = No) ->
- No;
-start_rc(timeout) ->
- {error, application_not_started}.
-
-%% --------------------------------------------------------------------------
-%% # stop_service(SvcName)
-%%
-%% Output: ok
-%% --------------------------------------------------------------------------
-
-stop_service(SvcName) ->
- sync(SvcName, {stop_service, SvcName}).
-
-%% --------------------------------------------------------------------------
-%% # add_transport(SvcName, {Type, Opts})
-%%
-%% Input: Type = connect | listen
-%%
-%% Output: {ok, Ref} | {error, Reason}
-%% --------------------------------------------------------------------------
-
-add_transport(SvcName, {T, Opts})
- when is_list(Opts), (T == connect orelse T == listen) ->
- sync(SvcName, {add, SvcName, T, Opts}).
-
-%% --------------------------------------------------------------------------
-%% # remove_transport(SvcName, Pred)
-%%
-%% Input: Pred = arity 3 fun on transport ref, connect|listen and Opts,
-%% returning true if the transport is to be removed, false if
-%% not
-%% | arity 2 fun on Ref and Opts only
-%% | arity 1 fun on Opts only
-%% | Opts matching all transports that have all of the specified
-%% options
-%% | Ref matching only the transport with this reference.
-%% | {M,F,A} applied to Ref, connect|listen and Opts
-%% | boolean()
-%%
-%% Output: ok | {error, Reason}
-%% --------------------------------------------------------------------------
-
-remove_transport(SvcName, Pred) ->
- try
- sync(SvcName, {remove, SvcName, pred(Pred)})
- catch
- ?FAILURE(Reason) ->
- {error, Reason}
- end.
-
-pred(Pred)
- when is_function(Pred, 3) ->
- Pred;
-pred(Pred)
- when is_function(Pred, 2) ->
- fun(R,_,O) -> Pred(R,O) end;
-pred(Pred)
- when is_function(Pred, 1) ->
- fun(_,_,O) -> Pred(O) end;
-pred(Opts)
- when is_list(Opts) ->
- fun(_,_,O) -> [] == Opts -- O end;
-pred(Ref)
- when is_reference(Ref) ->
- fun(R,_,_) -> R == Ref end;
-pred({M,F,A})
- when is_atom(M), is_atom(F), is_list(A) ->
- fun(R,T,O) -> apply(M,F,[R,T,O|A]) end;
-pred({Type, Pred}) -> %% backwards compatibility
- P = pred(Pred),
- fun(R,T,O) -> T == Type andalso P(R,T,O) end;
-pred(B)
- when is_boolean(B) ->
- fun(_,_,_) -> B end;
-pred(_) ->
- ?THROW(pred).
-
-%% --------------------------------------------------------------------------
-%% # have_transport/2
-%%
-%% Output: true | false
-%% --------------------------------------------------------------------------
-
-have_transport(SvcName, Ref) ->
- member([{#transport{service = '$1',
- ref = '$2',
- _ = '_'},
- [{'andalso', {'=:=', '$1', {const, SvcName}},
- {'=:=', '$2', {const, Ref}}}],
- [true]}]).
-
-%% --------------------------------------------------------------------------
-%% # lookup/1
-%% --------------------------------------------------------------------------
-
-lookup(SvcName) ->
- select([{#service{name = '$1', rec = '$2', options = '$3'},
- [{'=:=', '$1', {const, SvcName}}],
- [{{'$1', '$2', '$3'}}]},
- {#transport{service = '$1',
- ref = '$2',
- type = '$3',
- options = '$4'},
- [{'=:=', '$1', {const, SvcName}}],
- [{{'$2', '$3', '$4'}}]}]).
-
-%% ---------------------------------------------------------
-%% EXPORTED INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
-
-start_link() ->
- ServerName = {local, ?SERVER},
- Module = ?MODULE,
- Args = [],
- Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
- gen_server:start_link(ServerName, Module, Args, Options).
-
-state() ->
- call(state).
-
-uptime() ->
- call(uptime).
-
-%%% ----------------------------------------------------------
-%%% # init/1
-%%% ----------------------------------------------------------
-
-init([]) ->
- {ok, #state{}}.
-
-%%% ----------------------------------------------------------
-%%% # handle_call/2
-%%% ----------------------------------------------------------
-
-handle_call(state, _, State) ->
- {reply, State, State};
-
-handle_call(uptime, _, #state{id = Time} = State) ->
- {reply, diameter_lib:now_diff(Time), State};
-
-handle_call(Req, From, State) ->
- ?UNEXPECTED([Req, From]),
- Reply = {error, {bad_request, Req}},
- {reply, Reply, State}.
-
-%%% ----------------------------------------------------------
-%%% # handle_cast/2
-%%% ----------------------------------------------------------
-
-handle_cast(Msg, State) ->
- ?UNEXPECTED([Msg]),
- {noreply, State}.
-
-%%% ----------------------------------------------------------
-%%% # handle_info/2
-%%% ----------------------------------------------------------
-
-%% A service process has died. This is most likely a consequence of
-%% stop_service, in which case the restart will find no config for the
-%% service and do nothing. The entry keyed on the monitor ref is only
-%% removed as a result of the 'DOWN' notification however.
-handle_info({'DOWN', MRef, process, _, Reason}, State) ->
- [#monitor{service = SvcName} = T] = select([{#monitor{mref = MRef,
- _ = '_'},
- [],
- ['$_']}]),
- queue_restart(Reason, SvcName),
- delete_object(T),
- {noreply, State};
-
-handle_info({monitor, SvcName, Pid}, State) ->
- monitor(Pid, SvcName),
- {noreply, State};
-
-handle_info({restart, SvcName}, State) ->
- restart(SvcName),
- {noreply, State};
-
-handle_info(restart, State) ->
- restart(),
- {noreply, State};
-
-handle_info(Info, State) ->
- ?UNEXPECTED([Info]),
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% # terminate/2
-%%--------------------------------------------------------------------
-
-terminate(_Reason, _State) ->
- ok.
-
-%%% ----------------------------------------------------------
-%%% # code_change/3
-%%% ----------------------------------------------------------
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%% ---------------------------------------------------------
-%% INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
-
-insert(T) ->
- ets:insert(?TABLE, T).
-
-%% ?TABLE is a bag: check only for a service entry.
-have_service(SvcName) ->
- member([{#service{name = '$1', _ = '_'},
- [{'=:=', '$1', {const, SvcName}}],
- [true]}]).
-
-member(MatchSpec) ->
- '$end_of_table' =/= ets:select(?TABLE, MatchSpec, 1).
-
-delete_object(T) ->
- ets:delete_object(?TABLE, T).
-
-delete(Key) ->
- ets:delete(?TABLE, Key).
-
-select(MatchSpec) ->
- ets:select(?TABLE, MatchSpec).
-
-select_delete(MatchSpec) ->
- ets:select_delete(?TABLE, MatchSpec).
-
-%% sync/2
-%%
-%% Interface functions used to be implemented as calls to ?SERVER but
-%% now serialize things per service instead since stopping a service
-%% can take time if the server doesn't answer DPR. A caller who wants
-%% to stop multiple services can then improve performance by spawning
-%% processes to stop them concurrently.
-
-sync(SvcName, T) ->
- diameter_sync:call({?MODULE, SvcName},
- {?MODULE, sync, [T]},
- infinity,
- infinity).
-
-%% sync/1
-
-sync({restart, SvcName}) ->
- have_service(SvcName) andalso start(SvcName);
-
-sync({start_service, SvcName, Opts}) ->
- try
- start(have_service(SvcName), SvcName, Opts)
- catch
- ?FAILURE(Reason) -> {error, Reason}
- end;
-
-sync({stop_service, SvcName}) ->
- stop(SvcName);
-
-sync({add, SvcName, Type, Opts}) ->
- try
- add(SvcName, Type, Opts)
- catch
- ?FAILURE(Reason) -> {error, Reason}
- end;
-
-sync({remove, SvcName, Pred}) ->
- remove(select([{#transport{service = '$1', _ = '_'},
- [{'=:=', '$1', {const, SvcName}}],
- ['$_']}]),
- SvcName,
- Pred).
-
-%% start/3
-
-start(true, _, _) ->
- {error, already_started};
-start(false, SvcName, Opts) ->
- insert(make_config(SvcName, Opts)),
- start(SvcName).
-
-%% start/1
-
-start(SvcName) ->
- RC = diameter_service:start(SvcName),
- startmon(SvcName, RC),
- RC.
-
-startmon(SvcName, {ok, Pid}) ->
- ?SERVER ! {monitor, SvcName, Pid};
-startmon(_, {error, _}) ->
- ok.
-
-monitor(Pid, SvcName) ->
- MRef = erlang:monitor(process, Pid),
- insert(#monitor{mref = MRef, service = SvcName}).
-
-%% queue_restart/2
-
-%% Service has gone down on monitor death. Note that all service-related
-%% config is deleted.
-queue_restart({shutdown, {monitor, _}}, SvcName) ->
- delete(SvcName);
-
-%% Application shutdown: ignore.
-queue_restart(shutdown, _) ->
- ok;
-
-%% Or not.
-queue_restart(_, SvcName) ->
- erlang:send_after(?RESTART_SLEEP, self(), {restart, SvcName}).
-
-%% restart/1
-
-restart(SvcName) ->
- sync(SvcName, {restart, SvcName}).
-
-%% restart/0
-%%
-%% Start anything configured as required. Bang 'restart' to the server
-%% to kick things into gear manually. (Not that it should be required
-%% but it's been useful for test.)
-
-restart() ->
- MatchSpec = [{#service{name = '$1', _ = '_'},
- [],
- ['$1']}],
- lists:foreach(fun restart/1, select(MatchSpec)).
-
-%% stop/1
-
-stop(SvcName) ->
- %% If the call to the service returns error for any reason other
- %% than the process not being alive then deleting the config from
- %% under it will surely bring it down.
- diameter_service:stop(SvcName),
- %% Delete only the service entry, not everything keyed on the name,
- select_delete([{#service{name = '$1', _ = '_'},
- [{'=:=', '$1', {const, SvcName}}],
- [true]}]),
- ok.
-%% Note that a transport has to be removed for its statistics to be
-%% deleted.
-
-%% add/3
-
-add(SvcName, Type, Opts) ->
- %% Ensure usable capabilities. diameter_service:merge_service/2
- %% depends on this.
- lists:foreach(fun(Os) ->
- is_list(Os) orelse ?THROW({capabilities, Os}),
- ok = encode_CER(Os)
- end,
- [Os || {capabilities, Os} <- Opts, is_list(Os)]),
-
- Ref = make_ref(),
- T = {Ref, Type, Opts},
- %% The call to the service returns error if the service isn't
- %% started yet, which is harmless. The transport will be started
- %% when the service is in that case.
- case start_transport(SvcName, T) of
- ok ->
- insert(#transport{service = SvcName,
- ref = Ref,
- type = Type,
- options = Opts}),
- {ok, Ref};
- {error, _} = No ->
- No
- end.
-
-start_transport(SvcName, T) ->
- case diameter_service:start_transport(SvcName, T) of
- {ok, _Pid} ->
- ok;
- {error, no_service} ->
- ok;
- {error, _} = No ->
- No
- end.
-
-%% remove/3
-
-remove(L, SvcName, Pred) ->
- rm(SvcName, lists:filter(fun(#transport{ref = R, type = T, options = O}) ->
- Pred(R,T,O)
- end,
- L)).
-
-rm(_, []) ->
- ok;
-rm(SvcName, L) ->
- Refs = lists:map(fun(#transport{ref = R}) -> R end, L),
- case stop_transport(SvcName, Refs) of
- ok ->
- lists:foreach(fun delete_object/1, L);
- {error, _} = No ->
- No
- end.
-
-stop_transport(SvcName, Refs) ->
- case diameter_service:stop_transport(SvcName, Refs) of
- ok ->
- ok;
- {error, no_service} ->
- ok;
- {error, _} = No ->
- No
- end.
-
-%% make_config/2
-
-make_config(SvcName, Opts) ->
- Apps = init_apps(Opts),
- [] == Apps andalso ?THROW(no_apps),
-
- %% Use the fact that diameter_caps has the same field names as CER.
- Fields = diameter_gen_base_rfc3588:'#info-'(diameter_base_CER) -- ['AVP'],
-
- COpts = [T || {K,_} = T <- Opts, lists:member(K, Fields)],
- Caps = make_caps(#diameter_caps{}, COpts),
-
- ok = encode_CER(COpts),
-
- Os = split(Opts, [{[fun erlang:is_boolean/1], false, share_peers},
- {[fun erlang:is_boolean/1], false, use_shared_peers},
- {[fun erlang:is_pid/1, false], false, monitor}]),
- %% share_peers and use_shared_peers are currently undocumented.
-
- #service{name = SvcName,
- rec = #diameter_service{applications = Apps,
- capabilities = Caps},
- options = Os}.
-
-make_caps(Caps, Opts) ->
- case diameter_capx:make_caps(Caps, Opts) of
- {ok, T} ->
- T;
- {error, {Reason, _}} ->
- ?THROW(Reason)
- end.
-
-%% Validate types by encoding a CER.
-encode_CER(Opts) ->
- {ok, CER} = diameter_capx:build_CER(make_caps(?EXAMPLE_CAPS, Opts)),
-
- Hdr = #diameter_header{version = ?DIAMETER_VERSION,
- end_to_end_id = 0,
- hop_by_hop_id = 0},
-
- try
- diameter_codec:encode(?BASE, #diameter_packet{header = Hdr,
- msg = CER}),
- ok
- catch
- exit: Reason ->
- ?THROW(Reason)
- end.
-
-init_apps(Opts) ->
- lists:foldl(fun app_acc/2, [], lists:reverse(Opts)).
-
-app_acc({application, Opts}, Acc) ->
- is_list(Opts) orelse ?THROW({application, Opts}),
-
- [Dict, Mod] = get_opt([dictionary, module], Opts),
- Alias = get_opt(alias, Opts, Dict),
- ModS = get_opt(state, Opts, Alias),
- M = get_opt(call_mutates_state, Opts, false),
- A = get_opt(answer_errors, Opts, report),
- [#diameter_app{alias = Alias,
- dictionary = Dict,
- id = cb(Dict, id),
- module = init_mod(Mod),
- init_state = ModS,
- mutable = init_mutable(M),
- answer_errors = init_answers(A)}
- | Acc];
-app_acc(_, Acc) ->
- Acc.
-
-init_mod(M)
- when is_atom(M) ->
- [M];
-init_mod([M|_] = L)
- when is_atom(M) ->
- L;
-init_mod(M) ->
- ?THROW({module, M}).
-
-init_mutable(M)
- when M == true;
- M == false ->
- M;
-init_mutable(M) ->
- ?THROW({call_mutates_state, M}).
-
-init_answers(A)
- when callback == A;
- report == A;
- discard == A ->
- A;
-init_answers(A) ->
- ?THROW({answer_errors, A}).
-
-%% Get a single value at the specified key.
-get_opt(Keys, List)
- when is_list(Keys) ->
- [get_opt(K, List) || K <- Keys];
-get_opt(Key, List) ->
- case [V || {K,V} <- List, K == Key] of
- [V] -> V;
- _ -> ?THROW({arity, Key})
- end.
-
-%% Get an optional value at the specified key.
-get_opt(Key, List, Def) ->
- case [V || {K,V} <- List, K == Key] of
- [] -> Def;
- [V] -> V;
- _ -> ?THROW({arity, Key})
- end.
-
-split(Opts, Defs) ->
- [{K, value(D, Opts)} || {_,_,K} = D <- Defs].
-
-value({Preds, Def, Key}, Opts) ->
- V = get_opt(Key, Opts, Def),
- lists:any(fun(P) -> pred(P,V) end, Preds)
- orelse ?THROW({value, Key}),
- V.
-
-pred(F, V)
- when is_function(F) ->
- F(V);
-pred(T, V) ->
- T == V.
-
-cb(M,F) ->
- try M:F() of
- V -> V
- catch
- E: Reason ->
- ?THROW({callback, E, Reason, ?STACK})
- end.
-
-%% call/1
-
-call(Request) ->
- gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/app/diameter_dbg.erl b/lib/diameter/src/app/diameter_dbg.erl
deleted file mode 100644
index 5b0ac3a3b6..0000000000
--- a/lib/diameter/src/app/diameter_dbg.erl
+++ /dev/null
@@ -1,516 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_dbg).
-
--export([table/1,
- tables/0,
- fields/1,
- help/0,
- modules/0,
- versions/0,
- version_info/0,
- compiled/0,
- procs/0,
- latest/0,
- nl/0,
- log/4]).
-
--export([diameter_config/0,
- diameter_peer/0,
- diameter_reg/0,
- diameter_request/0,
- diameter_sequence/0,
- diameter_service/0,
- diameter_stats/0]).
-
--export([pp/1,
- subscriptions/0,
- children/0]).
-
-%% Trace help.
--export([tracer/0, tracer/1,
- p/0, p/1,
- stop/0,
- tpl/1,
- tp/1]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
-
--define(INFO, diameter_info).
--define(SEP(), ?INFO:sep()).
-
--define(LOCAL, [diameter_config,
- diameter_peer,
- diameter_reg,
- diameter_request,
- diameter_sequence,
- diameter_service,
- diameter_stats]).
-
--define(VALUES(Rec), tl(tuple_to_list(Rec))).
-
-log(_Slogan, _Mod, _Line, _Details) ->
- ok.
-
-%%% ----------------------------------------------------------
-%%% # help()
-%%% ----------------------------------------------------------
-
-help() ->
- not_yet_implemented.
-
-%%% ----------------------------------------------------------
-%%% # table(TableName)
-%%%
-%%% Input: TableName = diameter table containing record entries.
-%%%
-%%% Output: Count | undefined
-%%% ----------------------------------------------------------
-
-table(T)
- when (T == diameter_peer) orelse (T == diameter_reg) ->
- ?INFO:format(collect(T), fields(T), fun ?INFO:split/2);
-
-table(Table)
- when is_atom(Table) ->
- case fields(Table) of
- undefined = No ->
- No;
- Fields ->
- ?INFO:format(Table, Fields, fun split/2)
- end.
-
-split([started, name | Fs], [S, N | Vs]) ->
- {name, [started | Fs], N, [S | Vs]};
-split([[F|FT]|Fs], [Rec|Vs]) ->
- [_, V | VT] = tuple_to_list(Rec),
- {F, FT ++ Fs, V, VT ++ Vs};
-split([F|Fs], [V|Vs]) ->
- {F, Fs, V, Vs}.
-
-%%% ----------------------------------------------------------
-%%% # TableName()
-%%% ----------------------------------------------------------
-
--define(TABLE(Name), Name() -> table(Name)).
-
-?TABLE(diameter_config).
-?TABLE(diameter_peer).
-?TABLE(diameter_reg).
-?TABLE(diameter_request).
-?TABLE(diameter_sequence).
-?TABLE(diameter_service).
-?TABLE(diameter_stats).
-
-%%% ----------------------------------------------------------
-%%% # tables()
-%%%
-%%% Output: Number of records output.
-%%%
-%%% Description: Pretty-print records in diameter tables from all nodes.
-%%% ----------------------------------------------------------
-
-tables() ->
- ?INFO:format(field(?LOCAL), fun split/3, fun collect/1).
-
-field(Tables) ->
- lists:map(fun(T) -> {T, fields(T)} end, lists:sort(Tables)).
-
-split(_, Fs, Vs) ->
- split(Fs, Vs).
-
-%%% ----------------------------------------------------------
-%%% # modules()
-%%% ----------------------------------------------------------
-
-modules() ->
- Path = filename:join([appdir(), atom_to_list(?APPLICATION) ++ ".app"]),
- {ok, [{application, ?APPLICATION, Attrs}]} = file:consult(Path),
- {modules, Mods} = lists:keyfind(modules, 1, Attrs),
- Mods.
-
-appdir() ->
- [_|_] = code:lib_dir(?APPLICATION, ebin).
-
-%%% ----------------------------------------------------------
-%%% # versions()
-%%% ----------------------------------------------------------
-
-versions() ->
- ?INFO:versions(modules()).
-
-%%% ----------------------------------------------------------
-%%% # versions()
-%%% ----------------------------------------------------------
-
-version_info() ->
- ?INFO:version_info(modules()).
-
-%%% ----------------------------------------------------------
-%%% # compiled()
-%%% ----------------------------------------------------------
-
-compiled() ->
- ?INFO:compiled(modules()).
-
-%%% ----------------------------------------------------------
-%%% procs()
-%%% ----------------------------------------------------------
-
-procs() ->
- ?INFO:procs(?APPLICATION).
-
-%%% ----------------------------------------------------------
-%%% # latest()
-%%% ----------------------------------------------------------
-
-latest() ->
- ?INFO:latest(modules()).
-
-%%% ----------------------------------------------------------
-%%% # nl()
-%%% ----------------------------------------------------------
-
-nl() ->
- lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()).
-
-%%% ----------------------------------------------------------
-%%% # pp(Bin)
-%%%
-%%% Description: Pretty-print a message binary.
-%%% ----------------------------------------------------------
-
-%% Network byte order = big endian.
-
-pp(<>) ->
- ?SEP(),
- ppp(["Version",
- "Message length",
- "[Actual length]",
- "R(equest)",
- "P(roxiable)",
- "E(rror)",
- "T(Potential retrans)",
- "Reserved bits",
- "Command code",
- "Application id",
- "Hop by hop id",
- "End to end id"],
- [Version, MsgLength, size(AVPs) + 20,
- Rbit, Pbit, Ebit, Tbit, Reserved,
- CmdCode,
- ApplId,
- HbHid,
- E2Eid]),
- N = avp_loop({AVPs, MsgLength - 20}, 0),
- ?SEP(),
- N;
-
-pp(<<_Version:8, MsgLength:24, _/binary>> = Bin) ->
- {bad_message_length, MsgLength, size(Bin)};
-
-pp(Bin)
- when is_binary(Bin) ->
- {truncated_binary, size(Bin)};
-
-pp(_) ->
- not_binary.
-
-%% avp_loop/2
-
-avp_loop({Bin, Size}, N) ->
- avp_loop(avp(Bin, Size), N+1);
-avp_loop(ok, N) ->
- N;
-avp_loop([_E, _Rest] = L, N) ->
- io:format("! ~s: ~p~n", L),
- N;
-avp_loop([E, Rest, Fmt | Values], N)
- when is_binary(Rest) ->
- io:format("! ~s (" ++ Fmt ++ "): ~p~n", [E|Values] ++ [Rest]),
- N.
-
-%% avp/2
-
-avp(<<>>, 0) ->
- ok;
-avp(<>,
- Size) ->
- avp(Code, Flags, Length, Rest, Size);
-avp(Bin, _) ->
- ["truncated AVP header", Bin].
-
-%% avp/5
-
-avp(Code, Flags, Length, Rest, Size) ->
- <>
- = Flags,
- b(),
- ppp(["AVP Code",
- "V(endor)",
- "M(andatory)",
- "P(Security)",
- "R(eserved)",
- "Length"],
- [Code, V, M, P, Res, Length]),
- avp(V, Rest, Length - 8, Size - 8).
-
-%% avp/4
-
-avp(1, <>, Length, Size) ->
- ppp({"Vendor-ID", V}),
- data(Data, Length - 4, Size - 4);
-avp(1, Bin, _, _) ->
- ["truncated Vendor-ID", Bin];
-avp(0, Data, Length, Size) ->
- data(Data, Length, Size).
-
-data(Bin, Length, Size)
- when size(Bin) >= Length ->
- <> = Bin,
- ppp({"Data", AVP}),
- unpad(Rest, Size - Length, Length rem 4);
-
-data(Bin, _, _) ->
- ["truncated AVP data", Bin].
-
-%% Remove padding bytes up to the next word boundary.
-unpad(Bin, Size, 0) ->
- {Bin, Size};
-unpad(Bin, Size, N) ->
- un(Bin, Size, 4 - N).
-
-un(Bin, Size, N)
- when size(Bin) >= N ->
- ppp({"Padding bytes", N}),
- <> = Bin,
- Bits = N*8,
- case Pad of
- <<0:Bits>> ->
- {Rest, Size - N};
- _ ->
- ["non-zero padding", Bin, "~p", N]
- end;
-
-un(Bin, _, _) ->
- ["truncated padding", Bin].
-
-b() ->
- io:format("#~n").
-
-ppp(Fields, Values) ->
- lists:foreach(fun ppp/1, lists:zip(Fields, Values)).
-
-ppp({Field, Value}) ->
- io:format(": ~-22s : ~p~n", [Field, Value]).
-
-%%% ----------------------------------------------------------
-%%% # subscriptions()
-%%%
-%%% Output: list of {SvcName, Pid}
-%%% ----------------------------------------------------------
-
-subscriptions() ->
- diameter_service:subscriptions().
-
-%%% ----------------------------------------------------------
-%%% # children()
-%%% ----------------------------------------------------------
-
-children() ->
- diameter_sup:tree().
-
-%%% ----------------------------------------------------------
-
-%% tracer/[12]
-
-tracer(Port)
- when is_integer(Port) ->
- dbg:tracer(port, dbg:trace_port(ip, Port));
-
-tracer(Path)
- when is_list(Path) ->
- dbg:tracer(port, dbg:trace_port(file, Path)).
-
-tracer() ->
- dbg:tracer(process, {fun p/2, ok}).
-
-p(T,_) ->
- io:format("+ ~p~n", [T]).
-
-%% p/[01]
-
-p() ->
- p([c,timestamp]).
-
-p(T) ->
- dbg:p(all,T).
-
-%% stop/0
-
-stop() ->
- dbg:ctp(),
- dbg:stop_clear().
-
-%% tpl/1
-%% tp/1
-
-tpl(T) ->
- dbg(tpl, T).
-
-tp(T) ->
- dbg(tp, T).
-
-%% dbg/2
-
-dbg(F, L)
- when is_list(L) ->
- [dbg(F, X) || X <- L];
-
-dbg(F, M)
- when is_atom(M) ->
- apply(dbg, F, [M, x]);
-
-dbg(F, T)
- when is_tuple(T) ->
- apply(dbg, F, tuple_to_list(T)).
-
-%% ===========================================================================
-%% ===========================================================================
-
-%% collect/1
-
-collect(diameter_peer) ->
- lists:flatmap(fun peers/1, diameter:services());
-
-collect(diameter_reg) ->
- diameter_reg:terms();
-
-collect(Name) ->
- c(ets:info(Name), Name).
-
-c(undefined, _) ->
- [];
-c(_, Name) ->
- ets:tab2list(Name).
-
-%% peers/1
-
-peers(Name) ->
- peers(Name, diameter:service_info(Name, transport)).
-
-peers(_, undefined) ->
- [];
-peers(Name, Ts) ->
- lists:flatmap(fun(T) -> mk_peers(Name, T) end, Ts).
-
-mk_peers(Name, [_, {type, connect} | _] = Ts) ->
- [[Name | mk_peer(Ts)]];
-mk_peers(Name, [R, {type, listen}, O, {accept = A, As}]) ->
- [[Name | mk_peer([R, {type, A}, O | Ts])] || Ts <- As].
-%% This is a bit lame: service_info works to build this list and out
-%% of something like what we want here and then we take it apart.
-
-mk_peer(Vs) ->
- [Type, Ref, State, Opts, WPid, TPid, SApps, Caps]
- = get_values(Vs, [type,ref,state,options,watchdog,peer,apps,caps]),
- [Ref, State, [{type, Type} | Opts], s(WPid), s(TPid), SApps, Caps].
-
-get_values(Vs, Ks) ->
- [proplists:get_value(K, Vs) || K <- Ks].
-
-s(undefined = T) ->
- T;
-s({Pid, _Started, _State}) ->
- state(Pid);
-s({Pid, _Started}) ->
- state(Pid).
-
-%% Collect states from watchdog/transport pids.
-state(Pid) ->
- MRef = erlang:monitor(process, Pid),
- Pid ! {state, self()},
- receive
- {'DOWN', MRef, process, _, _} ->
- Pid;
- {Pid, _} = T ->
- erlang:demonitor(MRef, [flush]),
- T
- end.
-
-%% fields/1
-
--define(FIELDS(Table), fields(Table) -> record_info(fields, Table)).
-
-fields(diameter_config) ->
- [];
-
-fields(T)
- when T == diameter_request;
- T == diameter_sequence ->
- fun kv/1;
-
-fields(diameter_stats) ->
- fun({Ctr, N}) when not is_pid(Ctr) ->
- {[counter, value], [Ctr, N]};
- (_) ->
- []
- end;
-
-fields(diameter_service) ->
- [started,
- name,
- record_info(fields, diameter_service),
- peerT,
- connT,
- share_peers,
- use_shared_peers,
- shared_peers,
- local_peers,
- monitor];
-
-?FIELDS(diameter_event);
-?FIELDS(diameter_uri);
-?FIELDS(diameter_avp);
-?FIELDS(diameter_header);
-?FIELDS(diameter_packet);
-?FIELDS(diameter_app);
-?FIELDS(diameter_caps);
-
-fields(diameter_peer) ->
- [service, ref, state, options, watchdog, peer, applications, capabilities];
-
-fields(diameter_reg) ->
- [property, pids];
-
-fields(_) ->
- undefined.
-
-kv({_,_}) ->
- [key, value];
-kv(_) ->
- [].
diff --git a/lib/diameter/src/app/diameter_dict.erl b/lib/diameter/src/app/diameter_dict.erl
deleted file mode 100644
index 3b9ba00a3f..0000000000
--- a/lib/diameter/src/app/diameter_dict.erl
+++ /dev/null
@@ -1,153 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% This module provide OTP's dict interface built on top of ets.
-%%
-%% Note that while the interface is the same as dict the semantics
-%% aren't quite. A Dict here is just a table identifier (although
-%% this fact can't be used if you want dict/ets-based implementations
-%% to be interchangeable) so changes made to the Dict modify the
-%% underlying table. For merge/3, the first argument table is modified.
-%%
-%% The underlying ets table implementing a dict is deleted when the
-%% process from which new() was invoked exits and the dict is only
-%% writable from this process.
-%%
-%% The reason for this is to be able to swap dict/ets-based
-%% implementations: the former is easier to debug, the latter is
-%% faster for larger tables. It's also just a nice interface even
-%% when there's no need for swapability.
-%%
-
--module(diameter_dict).
-
--export([append/3,
- append_list/3,
- erase/2,
- fetch/2,
- fetch_keys/1,
- filter/2,
- find/2,
- fold/3,
- from_list/1,
- is_key/2,
- map/2,
- merge/3,
- new/0,
- store/3,
- to_list/1,
- update/3,
- update/4,
- update_counter/3]).
-
-%%% ----------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ----------------------------------------------------------
-
-append(Key, Value, Dict) ->
- append_list(Key, [Value], Dict).
-
-append_list(Key, ValueList, Dict)
- when is_list(ValueList) ->
- update(Key, fun(V) -> V ++ ValueList end, ValueList, Dict).
-
-erase(Key, Dict) ->
- ets:delete(Dict, Key),
- Dict.
-
-fetch(Key, Dict) ->
- {ok, V} = find(Key, Dict),
- V.
-
-fetch_keys(Dict) ->
- ets:foldl(fun({K,_}, Acc) -> [K | Acc] end, [], Dict).
-
-filter(Pred, Dict) ->
- lists:foreach(fun({K,V}) -> filter(Pred(K,V), K, Dict) end, to_list(Dict)),
- Dict.
-
-find(Key, Dict) ->
- case ets:lookup(Dict, Key) of
- [{Key, V}] ->
- {ok, V};
- [] ->
- error
- end.
-
-fold(Fun, Acc0, Dict) ->
- ets:foldl(fun({K,V}, Acc) -> Fun(K, V, Acc) end, Acc0, Dict).
-
-from_list(List) ->
- lists:foldl(fun store/2, new(), List).
-
-is_key(Key, Dict) ->
- ets:member(Dict, Key).
-
-map(Fun, Dict) ->
- lists:foreach(fun({K,V}) -> store(K, Fun(K,V), Dict) end, to_list(Dict)),
- Dict.
-
-merge(Fun, Dict1, Dict2) ->
- fold(fun(K2,V2,_) ->
- update(K2, fun(V1) -> Fun(K2, V1, V2) end, V2, Dict1)
- end,
- Dict1,
- Dict2).
-
-new() ->
- ets:new(?MODULE, [set]).
-
-store(Key, Value, Dict) ->
- store({Key, Value}, Dict).
-
-to_list(Dict) ->
- ets:tab2list(Dict).
-
-update(Key, Fun, Dict) ->
- store(Key, Fun(fetch(Key, Dict)), Dict).
-
-update(Key, Fun, Initial, Dict) ->
- store(Key, map(Key, Fun, Dict, Initial), Dict).
-
-update_counter(Key, Increment, Dict)
- when is_integer(Increment) ->
- update(Key, fun(V) -> V + Increment end, Increment, Dict).
-
-%%% ---------------------------------------------------------
-%%% INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-store({_,_} = T, Dict) ->
- ets:insert(Dict, T),
- Dict.
-
-filter(true, _, _) ->
- ok;
-filter(false, K, Dict) ->
- erase(K, Dict).
-
-map(Key, Fun, Dict, Error) ->
- case find(Key, Dict) of
- {ok, V} ->
- Fun(V);
- error ->
- Error
- end.
-
diff --git a/lib/diameter/src/app/diameter_info.erl b/lib/diameter/src/app/diameter_info.erl
deleted file mode 100644
index 39d32d07cd..0000000000
--- a/lib/diameter/src/app/diameter_info.erl
+++ /dev/null
@@ -1,869 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_info).
-
--export([usage/1,
- format/1,
- format/2,
- format/3,
- format/4,
- table/2,
- tables/1,
- tables/2,
- split/2,
- split/3,
- tab2list/1,
- modules/1,
- versions/1,
- version_info/1,
- attrs/2,
- compiled/1,
- procs/1,
- latest/1,
- list/1]).
-
-%% Support for rolling your own.
--export([sep/0,
- sep/1,
- widest/1,
- p/1,
- p/3]).
-
--compile({no_auto_import,[max/2]}).
-
--export([collect/2]).
-
--define(LONG_TIMEOUT, 30000).
--define(VALUES(Rec), tl(tuple_to_list(Rec))).
-
-%%% ----------------------------------------------------------
-%%% # usage(String)
-%%% ----------------------------------------------------------
-
-usage(Usage) ->
- sep($+),
- io:format("+ ~p~n", [?MODULE]),
- io:format("~n~s~n~n", [compact(Usage)]),
- sep($+).
-
-%%%
-%%% The function format/3, for pretty-printing tables, comes in
-%%% several flavours.
-%%%
-
-%%% ----------------------------------------------------------
-%%% # format(TableName, Fields, SplitFun)
-%%%
-%%% Input: TableName = atom() name of table.
-%%%
-%%% Fields = List of field names for the records maintained
-%%% in the specified table. Can be empty, in which
-%%% case entries are listed unadorned of field names
-%%% and SplitFun is unused.
-%%% | Integer, equivalent to a list with this many '' atoms.
-%%% | Arity 1 fun mapping a table entry to a Fields list
-%%% or a tuple {Fields, Values} of lists of the same
-%%% length.
-%%%
-%%% If Fields is a list then its length must be the same
-%%% as or one less than the size of the tuples contained
-%%% in the table. (The values printed then being those
-%%% in the tuple or record in question.)
-%%%
-%%% SplitFun = Arity 3 fun applied as
-%%%
-%%% SplitFun(TableName, Fields, Values)
-%%%
-%%% in order to obtain a tuple
-%%%
-%%% {Field, RestFields, Value, RestValues}
-%%%
-%%% for which Field/Value will be formatted on
-%%% STDOUT. (This is to allow a value to be
-%%% transformed before being output by returning a
-%%% new value and/or replacing the remainder of
-%%% the list.) The returned lists must have the
-%%% same length and Field here is an atom, '' causing
-%%% a value to be listed unadorned of the field name.
-%%%
-%%% Field can also be list of field names, in
-%%% which case Value must be a record of the
-%%% corresponding type.
-%%%
-%%% | Arity 2 fun applied as SplitFun(Fields, Values).
-%%%
-%%% Output: Count | undefined
-%%%
-%%% Count = Number of entries output.
-%%%
-%%% Description: Pretty-print records in a named table.
-%%% ----------------------------------------------------------
-
-format(Table, Fields, SFun)
- when is_atom(Table), is_function(SFun, 2) ->
- ft(ets:info(Table), Table, SFun, Fields);
-
-format(Table, Fields, SFun)
- when is_atom(Table), is_function(SFun, 3) ->
- format(Table, Fields, fun(Fs,Vs) -> SFun(Table, Fs, Vs) end);
-
-%%% ----------------------------------------------------------
-%%% # format(Recs, Fields, SplitFun)
-%%%
-%%% Input: Recs = list of records/tuples
-%%% Fields = As for format(Table, Fields, SplitFun), a table
-%%% entry there being a member of Recs.
-%%% SplitFun = Arity 3 fun applied as above but with the TableName
-%%% replaced by the first element of the records in
-%%% question.
-%%% | Arity 2 fun as for format/3.
-%%%
-%%% Output: length(Recs)
-%%%
-%%% Description: Pretty print records/tuples.
-%%% ----------------------------------------------------------
-
-format(Recs, Fields, SFun)
- when is_list(Recs), is_function(SFun, 3) ->
- lists:foldl(fun(R,A) -> f(recsplit(SFun, R), 0, Fields, R, A) end,
- 0,
- Recs);
-
-format(Recs, Fields, SFun)
- when is_list(Recs), is_function(SFun, 2) ->
- lists:foldl(fun(R,A) -> f(SFun, 0, Fields, R, A) end,
- 0,
- Recs);
-
-%%% ----------------------------------------------------------
-%%% # format(Tables, SplitFun, CollectFun)
-%%%
-%%% Input: Tables = list of {TableName, Fields}.
-%%% SplitFun = As for format(Table, Fields, SplitFun).
-%%% CollectFun = arity 1 fun mapping a table name to a list
-%%% of elements. A non-list can be returned to indicate
-%%% that the table in question doesn't exist.
-%%%
-%%% Output: Number of entries output.
-%%%
-%%% Description: Pretty-print records in a named tables as collected
-%%% from known nodes. Each table listing is preceeded by
-%%% a banner.
-%%% ----------------------------------------------------------
-
-format(Tables, SFun, CFun)
- when is_list(Tables), is_function(CFun, 1) ->
- format_remote(Tables,
- SFun,
- rpc:multicall(nodes(known),
- ?MODULE,
- collect,
- [CFun, lists:map(fun({T,_}) -> T end, Tables)],
- ?LONG_TIMEOUT));
-
-%%% ----------------------------------------------------------
-%%% # format(LocalTables, RemoteTables, SplitFun, CollectFun)
-%%% # format(LocalTables, RemoteTables, SplitFun)
-%%%
-%%% Input: LocalTables = list of {TableName, Fields}.
-%%% | list of {TableName, Recs, Fields}
-%%% RemoteTable = list of {TableName, Fields}.
-%%% SplitFun, CollectFun = As for format(Table, CollectFun, SplitFun).
-%%%
-%%% Output: Number of entries output.
-%%%
-%%% Description: Pretty-print records in a named tables as collected
-%%% from local and remote nodes. Each table listing is
-%%% preceeded by a banner.
-%%% ----------------------------------------------------------
-
-format(Local, Remote, SFun) ->
- format(Local, Remote, SFun, fun tab2list/1).
-
-format(Local, Remote, SFun, CFun)
- when is_list(Local), is_list(Remote), is_function(CFun, 1) ->
- format_local(Local, SFun) + format(Remote, SFun, CFun).
-
-%%% ----------------------------------------------------------
-%%% # format(Tables, SplitFun)
-%%% ----------------------------------------------------------
-
-format(Tables, SFun)
- when is_list(Tables), (is_function(SFun, 2) or is_function(SFun, 3)) ->
- format(Tables, SFun, fun tab2list/1);
-
-format(Tables, CFun)
- when is_list(Tables), is_function(CFun, 1) ->
- format(Tables, fun split/2, CFun).
-
-%%% ----------------------------------------------------------
-%%% # format(Table|Tables)
-%%% ----------------------------------------------------------
-
-format(Table)
- when is_atom(Table) ->
- format(Table, [], fun split/2);
-
-format(Tables)
- when is_list(Tables) ->
- format(Tables, fun split/2, fun tab2list/1).
-
-%%% ----------------------------------------------------------
-%%% # split(TableName, Fields, Values)
-%%%
-%%% Description: format/3 SplitFun that does nothing special.
-%%% ----------------------------------------------------------
-
-split([F|FT], [V|VT]) ->
- {F, FT, V, VT}.
-
-split(_, Fs, Vs) ->
- split(Fs, Vs).
-
-%%% ----------------------------------------------------------
-%%% # tab2list(TableName)
-%%%
-%%% Description: format/4 CollectFun that extracts records from an
-%%% existing ets table.
-%%% ----------------------------------------------------------
-
-tab2list(Table) ->
- case ets:info(Table) of
- undefined = No ->
- No;
- _ ->
- ets:tab2list(Table)
- end.
-
-list(Table) ->
- l(tab2list(Table)).
-
-l(undefined = No) ->
- No;
-l(List)
- when is_list(List) ->
- io:format("~p~n", [List]),
- length(List).
-
-%%% ----------------------------------------------------------
-%%% # table(TableName, Fields)
-%%% ----------------------------------------------------------
-
-table(Table, Fields) ->
- format(Table, Fields, fun split/2).
-
-%%% ----------------------------------------------------------
-%%% # tables(LocalTables, RemoteTables)
-%%% ----------------------------------------------------------
-
-tables(Local, Remote) ->
- format(Local, Remote, fun split/2).
-
-%%% ----------------------------------------------------------
-%%% # tables(Tables)
-%%% ----------------------------------------------------------
-
-tables(Tables) ->
- format(Tables, fun split/2).
-
-%%% ----------------------------------------------------------
-%%% # modules(Prefix|Prefixes)
-%%%
-%%% Input: Prefix = atom()
-%%%
-%%% Description: Return the list of all loaded modules with the
-%%% specified prefix.
-%%% ----------------------------------------------------------
-
-modules(Prefix)
- when is_atom(Prefix) ->
- lists:sort(mods(Prefix));
-
-modules(Prefixes)
- when is_list(Prefixes) ->
- lists:sort(lists:flatmap(fun modules/1, Prefixes)).
-
-mods(Prefix) ->
- P = atom_to_list(Prefix),
- lists:filter(fun(M) ->
- lists:prefix(P, atom_to_list(M))
- end,
- erlang:loaded()).
-
-%%% ----------------------------------------------------------
-%%% # versions(Modules|Prefix)
-%%%
-%%% Output: Number of modules listed.
-%%%
-%%% Description: List the versions of the specified modules.
-%%% ----------------------------------------------------------
-
-versions(Modules) ->
- {SysInfo, OsInfo, ModInfo} = version_info(Modules),
- sep(),
- print_sys_info(SysInfo),
- sep(),
- print_os_info(OsInfo),
- sep(),
- print_mod_info(ModInfo),
- sep().
-
-%%% ----------------------------------------------------------
-%%% # attrs(Modules|Prefix, Attr|FormatFun)
-%%%
-%%% Output: Number of modules listed.
-%%%
-%%% Description: List an attribute from module_info.
-%%% ----------------------------------------------------------
-
-attrs(Modules, Attr)
- when is_atom(Attr) ->
- attrs(Modules, fun(W,M) -> attr(W, M, Attr, fun attr/1) end);
-
-attrs(Modules, Fun)
- when is_list(Modules) ->
- sep(),
- W = 2 + widest(Modules),
- N = lists:foldl(fun(M,A) -> Fun(W,M), A+1 end, 0, Modules),
- sep(),
- N;
-
-attrs(Prefix, Fun) ->
- attrs(modules(Prefix), Fun).
-
-%% attr/1
-
-attr(T) when is_atom(T) ->
- atom_to_list(T);
-attr(N) when is_integer(N) ->
- integer_to_list(N);
-attr(V) ->
- case is_list(V) andalso lists:all(fun is_char/1, V) of
- true -> %% string
- V;
- false ->
- io_lib:format("~p", [V])
- end.
-
-is_char(C) ->
- 0 =< C andalso C < 256.
-
-%% attr/4
-
-attr(Width, Mod, Attr, VFun) ->
- io:format(": ~*s~s~n", [-Width, Mod, attr(Mod, Attr, VFun)]).
-
-attr(Mod, Attr, VFun) ->
- Key = key(Attr),
- try
- VFun(val(Attr, keyfetch(Attr, Mod:module_info(Key))))
- catch
- _:_ ->
- "-"
- end.
-
-attr(Mod, Attr) ->
- attr(Mod, Attr, fun attr/1).
-
-key(time) -> compile;
-key(_) -> attributes.
-
-val(time, {_,_,_,_,_,_} = T) ->
- lists:flatten(io_lib:format("~p-~2..0B-~2..0B ~2..0B:~2..0B:~2..0B",
- tuple_to_list(T)));
-val(_, [V]) ->
- V.
-
-%%% ----------------------------------------------------------
-%%% # compiled(Modules|Prefix)
-%%%
-%%% Output: Number of modules listed.
-%%%
-%%% Description: List the compile times of the specified modules.
-%%% ----------------------------------------------------------
-
-compiled(Modules)
- when is_list(Modules) ->
- attrs(Modules, fun compiled/2);
-
-compiled(Prefix) ->
- compiled(modules(Prefix)).
-
-compiled(Width, Mod) ->
- io:format(": ~*s~19s ~s~n", [-Width,
- Mod,
- attr(Mod, time),
- opt(attr(Mod, date))]).
-
-opt("-") ->
- "";
-opt(D) ->
- "(" ++ D ++ ")".
-
-%%% ----------------------------------------------------------
-%%% # procs(Pred|Prefix|Prefixes|Pid|Pids)
-%%%
-%%% Input: Pred = arity 2 fun returning true|false when applied to a
-%%% pid and its process info.
-%%%
-%%% Output: Number of processes listed.
-%%%
-%%% Description: List process info for all local processes that test
-%%% true with the specified predicate. With the prefix
-%%% form, those processes that are either currently
-%%% executing in, started executing in, or have a
-%%% registered name with a specified prefix are listed.
-%%% With the pid forms, only those process that are local
-%%% are listed and those that are dead list only the pid
-%%% itself.
-%%% ----------------------------------------------------------
-
-procs(Pred)
- when is_function(Pred, 2) ->
- procs(Pred, erlang:processes());
-
-procs([]) ->
- 0;
-
-procs(Prefix)
- when is_atom(Prefix) ->
- procs(fun(_,I) -> info(fun pre1/2, I, atom_to_list(Prefix)) end);
-
-procs(Prefixes)
- when is_atom(hd(Prefixes)) ->
- procs(fun(_,I) -> info(fun pre/2, I, Prefixes) end);
-
-procs(Pid)
- when is_pid(Pid) ->
- procs(fun true2/2, [Pid]);
-
-procs(Pids)
- when is_list(Pids) ->
- procs(fun true2/2, Pids).
-
-true2(_,_) ->
- true.
-
-%% procs/2
-
-procs(Pred, Pids) ->
- Procs = lists:foldl(fun(P,A) ->
- procs_acc(Pred, P, catch process_info(P), A)
- end,
- [],
- Pids),
- sep(0 < length(Procs)),
- lists:foldl(fun(T,N) -> p(T), sep(), N+1 end, 0, Procs).
-
-procs_acc(_, Pid, undefined, Acc) -> %% dead
- [[{pid, Pid}] | Acc];
-procs_acc(Pred, Pid, Info, Acc)
- when is_list(Info) ->
- p_acc(Pred(Pid, Info), Pid, Info, Acc);
-procs_acc(_, _, _, Acc) ->
- Acc.
-
-p_acc(true, Pid, Info, Acc) ->
- [[{pid, Pid} | Info] | Acc];
-p_acc(false, _, _, Acc) ->
- Acc.
-
-%% info/3
-
-info(Pred, Info, T) ->
- lists:any(fun(I) -> i(Pred, I, T) end, Info).
-
-i(Pred, {K, {M,_,_}}, T)
- when K == current_function;
- K == initial_call ->
- Pred(M,T);
-i(Pred, {registered_name, N}, T) ->
- Pred(N,T);
-i(_,_,_) ->
- false.
-
-pre1(A, Pre) ->
- lists:prefix(Pre, atom_to_list(A)).
-
-pre(A, Prefixes) ->
- lists:any(fun(P) -> pre1(A, atom_to_list(P)) end, Prefixes).
-
-%%% ----------------------------------------------------------
-%%% # latest(Modules|Prefix)
-%%%
-%%% Output: {Mod, {Y,M,D,HH,MM,SS}, Version}
-%%%
-%%% Description: Return the compile time of the most recently compiled
-%%% module from the specified non-empty list. The modules
-%%% are assumed to exist.
-%%% ----------------------------------------------------------
-
-latest(Prefix)
- when is_atom(Prefix) ->
- latest(modules(Prefix));
-
-latest([_|_] = Modules) ->
- {Mod, T}
- = hd(lists:sort(fun latest/2, lists:map(fun compile_time/1, Modules))),
- {Mod, T, app_vsn(Mod)}.
-
-app_vsn(Mod) ->
- keyfetch(app_vsn, Mod:module_info(attributes)).
-
-compile_time(Mod) ->
- T = keyfetch(time, Mod:module_info(compile)),
- {Mod, T}.
-
-latest({_,T1},{_,T2}) ->
- T1 > T2.
-
-%%% ----------------------------------------------------------
-%%% version_info(Modules|Prefix)
-%%%
-%%% Output: {SysInfo, OSInfo, [ModInfo]}
-%%%
-%%% SysInfo = {Arch, Vers}
-%%% OSInfo = {Vers, {Fam, Name}}
-%%% ModInfo = {Vsn, AppVsn, Time, CompilerVsn}
-%%% ----------------------------------------------------------
-
-version_info(Prefix)
- when is_atom(Prefix) ->
- version_info(modules(Prefix));
-
-version_info(Mods)
- when is_list(Mods) ->
- {sys_info(), os_info(), [{M, mod_version_info(M)} || M <- Mods]}.
-
-mod_version_info(Mod) ->
- try
- Info = Mod:module_info(),
- [[Vsn], AppVsn] = get_values(attributes, [vsn, app_vsn], Info),
- [Ver, Time] = get_values(compile, [version, time], Info),
- [Vsn, AppVsn, Ver, Time]
- catch
- _:_ ->
- []
- end.
-
-get_values(Attr, Keys, Info) ->
- As = proplists:get_value(Attr, Info),
- [proplists:get_value(K, As, "?") || K <- Keys].
-
-sys_info() ->
- [A,V] = [chomp(erlang:system_info(K)) || K <- [system_architecture,
- system_version]],
- {A,V}.
-
-os_info() ->
- {os:version(), case os:type() of
- {_Fam, _Name} = T ->
- T;
- Fam ->
- {Fam, ""}
- end}.
-
-chomp(S) ->
- string:strip(S, right, $\n).
-
-print_sys_info({Arch, Ver}) ->
- io:format("System info:~n"
- " architecture : ~s~n"
- " version : ~s~n",
- [Arch, Ver]).
-
-print_os_info({Vsn, {Fam, Name}}) ->
- io:format("OS info:~n"
- " family : ~s ~s~n"
- " version : ~s~n",
- [str(Fam), bkt(str(Name)), vsn(Vsn)]).
-
-print_mod_info(Mods) ->
- io:format("Module info:~n", []),
- lists:foreach(fun print_mod/1, Mods).
-
-print_mod({Mod, []}) ->
- io:format(" ~w:~n", [Mod]);
-print_mod({Mod, [Vsn, AppVsn, Ver, {Year, Month, Day, Hour, Min, Sec}]}) ->
- Time = io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
- [Year, Month, Day, Hour, Min, Sec]),
- io:format(" ~w:~n"
- " vsn : ~s~n"
- " app_vsn : ~s~n"
- " compiled : ~s~n"
- " compiler : ~s~n",
- [Mod, str(Vsn), str(AppVsn), Time, Ver]).
-
-str(A)
- when is_atom(A) ->
- atom_to_list(A);
-str(S)
- when is_list(S) ->
- S;
-str(T) ->
- io_lib:format("~p", [T]).
-
-bkt("" = S) ->
- S;
-bkt(S) ->
- [$[, S, $]].
-
-vsn(T) when is_tuple(T) ->
- case [[$., integer_to_list(N)] || N <- tuple_to_list(T)] of
- [[$.,S] | Rest] ->
- [S | Rest];
- [] = S ->
- S
- end;
-vsn(T) ->
- str(T).
-
-%%% ----------------------------------------------------------
-%%% ----------------------------------------------------------
-
-%% p/1
-
-p(Info) ->
- W = 2 + widest([K || {K,_} <- Info]),
- lists:foreach(fun({K,V}) -> p(W,K,V) end, Info).
-
-p(Width, Key, Value) ->
- io:format(": ~*s: ~p~n", [-Width, Key, Value]).
-
-%% sep/[01]
-
-sep() ->
- sep($#).
-
-sep(true) ->
- sep();
-sep(false) ->
- ok;
-
-sep(Ch) ->
- io:format("~c~65c~n", [Ch, $-]).
-
-%% widest/1
-
-widest(List) ->
- lists:foldl(fun widest/2, 0, List).
-
-widest(T, Max)
- when is_atom(T) ->
- widest(atom_to_list(T), Max);
-
-widest(T, Max)
- when is_integer(T) ->
- widest(integer_to_list(T), Max);
-
-widest(T, Max)
- when is_list(T) -> %% string
- max(length(T), Max).
-
-pt(T) ->
- io:format(": ~p~n", [T]).
-
-recsplit(SFun, Rec) ->
- fun(Fs,Vs) -> SFun(element(1, Rec), Fs, Vs) end.
-
-max(A, B) ->
- if A > B -> A; true -> B end.
-
-keyfetch(Key, List) ->
- {Key,V} = lists:keyfind(Key, 1, List),
- V.
-
-%% ft/4
-
-ft(undefined = No, _, _, _) ->
- No;
-
-ft(_, Table, SFun, Fields) ->
- ets:foldl(fun(R,A) ->
- f(SFun, 0, Fields, R, A)
- end,
- 0,
- Table).
-
-%% f/5
-
-f(SFun, Width, Fields, Rec, Count) ->
- ff(SFun, Width, fields(Fields, Rec), Rec, Count).
-
-ff(SFun, Width, Fields, Rec, Count) ->
- sep(0 == Count),
- f(SFun, Width, Fields, Rec),
- sep(),
- Count+1.
-
-fields(N, _)
- when is_integer(N), N >= 0 ->
- lists:duplicate(N, ''); %% list values unadorned
-fields(Fields, R)
- when is_function(Fields, 1) ->
- fields(Fields(R), R);
-fields({Fields, Values} = T, _)
- when length(Fields) == length(Values) ->
- T;
-fields(Fields, _)
- when is_list(Fields) ->
- Fields. %% list field/value pairs, or tuples if []
-
-%% f/4
-
-%% Empty fields list: just print the entry.
-f(_, _, [], Rec)
- when is_tuple(Rec) ->
- pt(Rec);
-
-%% Otherwise list field names/values.
-f(SFun, Width, {Fields, Values}, _) ->
- f(SFun, Width, Fields, Values);
-
-f(SFun, Width, Fields, Rec)
- when is_tuple(Rec) ->
- f(SFun, Width, Fields, values(Fields, Rec));
-
-f(_, _, [], []) ->
- ok;
-
-f(SFun, Width, [HF | _] = Fields, Values) ->
- {F, FT, V, VT} = SFun(Fields, Values),
- if is_list(F) -> %% V is a record
- break($>, HF),
- f(SFun, Width, F, values(F,V)),
- break($<, HF),
- f(SFun, Width, FT, VT);
- F == '' -> %% no field name: just list value
- pt(V),
- f(SFun, Width, FT, VT);
- true -> %% list field/value.
- W = max(Width, 1 + widest(Fields)),
- p(W, F, V),
- f(SFun, W, FT, VT)
- end.
-
-values(Fields, Rec)
- when length(Fields) == size(Rec) - 1 ->
- ?VALUES(Rec);
-values(Fields, T)
- when length(Fields) == size(T) ->
- tuple_to_list(T).
-
-%% format_local/2
-
-format_local(Tables, SFun) ->
- lists:foldl(fun(T,A) -> fl(SFun, T, A) end, 0, Tables).
-
-fl(SFun, {Table, Recs, Fields}, Count) ->
- sep(),
- io:format("# ~p~n", [Table]),
- N = fmt(Recs, Fields, SFun),
- sep(0 == N),
- Count + N;
-
-fl(SFun, {Table, Fields}, Count) ->
- fl(SFun, {Table, Table, Fields}, Count).
-
-%% fmt/3
-
-fmt(T, Fields, SFun) ->
- case format(T, Fields, SFun) of
- undefined ->
- 0;
- N ->
- N
- end.
-
-%% break/2
-
-break(C, T) ->
- io:format("~c ~p~n", [C, T]).
-
-%% collect/2
-%%
-%% Output: {[{TableName, Recs}, ...], node()}
-
-collect(CFun, TableNames) ->
- {lists:foldl(fun(N,A) -> c(CFun, N, A) end, [], TableNames), node()}.
-
-c(CFun, TableName, Acc) ->
- case CFun(TableName) of
- Recs when is_list(Recs) ->
- [{TableName, Recs} | Acc];
- _ ->
- Acc
- end.
-
-%% format_remote/3
-
-format_remote(Tables, SFun, {Replies, BadNodes}) ->
- N = lists:foldl(fun(T,A) -> fr(Tables, SFun, T, A) end,
- 0,
- Replies),
- sep(0 == N andalso [] /= BadNodes),
- lists:foreach(fun(Node) -> io:format("# no reply from ~p~n", [Node]) end,
- BadNodes),
- sep([] /= BadNodes),
- N.
-
-fr(Tables, SFun, {List, Node}, Count)
- when is_list(List) -> %% guard against {badrpc, Reason}
- lists:foldl(fun({T,Recs}, C) -> fr(Tables, SFun, Node, T, Recs,C) end,
- Count,
- List);
-fr(_, _, _, Count) ->
- Count.
-
-fr(Tables, SFun, Node, Table, Recs, Count) ->
- Fields = keyfetch(Table, Tables),
- sep(),
- io:format("# ~p@~p~n", [Table, Node]),
- N = format(Recs, Fields, tblsplit(SFun, Table)),
- sep(0 == N),
- Count + N.
-
-tblsplit(SFun, Table)
- when is_function(SFun, 3) ->
- fun(Fs,Vs) -> SFun(Table, Fs, Vs) end;
-tblsplit(SFun, _)
- when is_function(SFun, 2) ->
- SFun.
-
-%% compact/1
-%%
-%% Strip whitespace from both ends of a string.
-
-compact(Str) ->
- compact(Str, true).
-
-compact([Ch|Rest], B)
- when Ch == $\n;
- Ch == $ ;
- Ch == $\t;
- Ch == $\v;
- Ch == $\r ->
- compact(Rest, B);
-
-compact(Str, false) ->
- Str;
-
-compact(Str, true) ->
- lists:reverse(compact(lists:reverse(Str), false)).
diff --git a/lib/diameter/src/app/diameter_internal.hrl b/lib/diameter/src/app/diameter_internal.hrl
deleted file mode 100644
index 63b35550a8..0000000000
--- a/lib/diameter/src/app/diameter_internal.hrl
+++ /dev/null
@@ -1,80 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%% Our Erlang application.
--define(APPLICATION, diameter).
-
-%% The one and only.
--define(DIAMETER_VERSION, 1).
-
-%% Exception for use within a module with decent protection against
-%% catching something we haven't thrown. Not foolproof but close
-%% enough. ?MODULE is rudmentary protection against catching across
-%% module boundaries, a root of much evil: always catch ?FAILURE(X),
-%% never X.
--define(FAILURE(Reason), {{?MODULE}, {Reason}}).
--define(THROW(Reason), throw(?FAILURE(Reason))).
-
-%% A corresponding error when failure is the best option.
--define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
-
-%% Failure reports always get a stack trace.
--define(STACK, erlang:get_stacktrace()).
-
-%% Warning report for unexpected messages in various processes.
--define(UNEXPECTED(F,A),
- diameter_lib:warning_report(unexpected, {?MODULE, F, A})).
--define(UNEXPECTED(A), ?UNEXPECTED(?FUNC, A)).
-
-%% Something to trace on.
--define(LOG(Slogan, Details),
- diameter_lib:log(Slogan, ?MODULE, ?LINE, Details)).
--define(LOGC(Bool, Slogan, Details), ((Bool) andalso ?LOG(Slogan, Details))).
-
-%% Compensate for no builtin ?FUNC for use in log reports.
--define(FUNC, element(2, element(2, process_info(self(), current_function)))).
-
-%% Disjunctive match spec condition. 'false' is to ensure that there's at
-%% least one condition.
--define(ORCOND(List), list_to_tuple(['orelse', false | List])).
-
-%% 3588, 2.4:
--define(APP_ID_COMMON, 0).
--define(APP_ID_RELAY, 16#FFFFFFFF).
-
--define(BASE, diameter_gen_base_rfc3588).
-
-%%% ---------------------------------------------------------
-
-%%% RFC 3588, ch 2.6 Peer table
--record(diameter_peer,
- {host_id,
- statusT,
- is_dynamic,
- expiration,
- tls_enabled}).
-
-%%% RFC 3588, ch 2.7 Realm-based routing table
--record(diameter_realm,
- {name,
- app_id,
- local_action, % LOCAL | RELAY | PROXY | REDIRECT
- server_id,
- is_dynamic,
- expiration}).
diff --git a/lib/diameter/src/app/diameter_lib.erl b/lib/diameter/src/app/diameter_lib.erl
deleted file mode 100644
index 362d593b24..0000000000
--- a/lib/diameter/src/app/diameter_lib.erl
+++ /dev/null
@@ -1,272 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_lib).
-
--export([report/2, info_report/2,
- error_report/2,
- warning_report/2,
- now_diff/1,
- time/1,
- eval/1,
- ip4address/1,
- ip6address/1,
- ipaddr/1,
- spawn_opts/2,
- wait/1,
- fold_tuple/3,
- log/4]).
-
--include("diameter_internal.hrl").
-
-%% ---------------------------------------------------------------------------
-%% # info_report(Reason, MFA)
-%%
-%% Input: Reason = Arbitrary term indicating the reason for the report.
-%% MFA = {Module, Function, Args} to report.
-%%
-%% Output: true
-%% ---------------------------------------------------------------------------
-
-report(Reason, MFA) ->
- info_report(Reason, MFA).
-
-info_report(Reason, MFA) ->
- report(fun error_logger:info_report/1, Reason, MFA),
- true.
-
-%%% ---------------------------------------------------------------------------
-%%% # error_report(Reason, MFA)
-%%% # warning_report(Reason, MFA)
-%%%
-%%% Output: false
-%%% ---------------------------------------------------------------------------
-
-error_report(Reason, MFA) ->
- report(fun error_logger:error_report/1, Reason, MFA).
-
-warning_report(Reason, MFA) ->
- report(fun error_logger:warning_report/1, Reason, MFA).
-
-report(Fun, Reason, MFA) ->
- Fun([{why, Reason}, {who, self()}, {what, MFA}]),
- false.
-
-%%% ---------------------------------------------------------------------------
-%%% # now_diff(Time)
-%%%
-%%% Description: Return timer:now_diff(now(), Time) as an {H, M, S, MicroS}
-%%% tuple instead of as integer microseconds.
-%%% ---------------------------------------------------------------------------
-
-now_diff({_,_,_} = Time) ->
- time(timer:now_diff(erlang:now(), Time)).
-
-%%% ---------------------------------------------------------------------------
-%%% # time(Time)
-%%%
-%%% Input: Time = {MegaSec, Sec, MicroSec}
-%%% | MicroSec
-%%%
-%%% Output: {H, M, S, MicroS}
-%%% ---------------------------------------------------------------------------
-
-time({_,_,_} = Time) -> %% time of day
- %% 24 hours = 24*60*60*1000000 = 86400000000 microsec
- time(timer:now_diff(Time, {0,0,0}) rem 86400000000);
-
-time(Micro) -> %% elapsed time
- Seconds = Micro div 1000000,
- H = Seconds div 3600,
- M = (Seconds rem 3600) div 60,
- S = Seconds rem 60,
- {H, M, S, Micro rem 1000000}.
-
-%%% ---------------------------------------------------------------------------
-%%% # eval(Func)
-%%% ---------------------------------------------------------------------------
-
-eval({M,F,A}) ->
- apply(M,F,A);
-
-eval([{M,F,A} | X]) ->
- apply(M, F, X ++ A);
-
-eval([[F|A] | X]) ->
- eval([F | X ++ A]);
-
-eval([F|A]) ->
- apply(F,A);
-
-eval({F}) ->
- eval(F);
-
-eval(F) ->
- F().
-
-%%% ---------------------------------------------------------------------------
-%%% # ip4address(Addr)
-%%%
-%%% Input: string() (eg. "10.0.0.1")
-%%% | list of integer()
-%%% | tuple of integer()
-%%%
-%%% Output: {_,_,_,_} of integer
-%%%
-%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
-%%% ---------------------------------------------------------------------------
-
-ip4address([_,_,_,_] = Addr) -> %% Length 4 string can't be an address.
- ipaddr(list_to_tuple(Addr));
-
-%% Be brutal.
-ip4address(Addr) ->
- try
- {_,_,_,_} = ipaddr(Addr)
- catch
- error: _ ->
- erlang:error({invalid_address, Addr, ?STACK})
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # ip6address(Addr)
-%%%
-%%% Input: string() (eg. "1080::8:800:200C:417A")
-%%% | list of integer()
-%%% | tuple of integer()
-%%%
-%%% Output: {_,_,_,_,_,_,_,_} of integer
-%%%
-%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
-%%% ---------------------------------------------------------------------------
-
-ip6address([_,_,_,_,_,_,_,_] = Addr) -> %% Length 8 string can't be an address.
- ipaddr(list_to_tuple(Addr));
-
-%% Be brutal.
-ip6address(Addr) ->
- try
- {_,_,_,_,_,_,_,_} = ipaddr(Addr)
- catch
- error: _ ->
- erlang:error({invalid_address, Addr, ?STACK})
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # ipaddr(Addr)
-%%%
-%%% Input: string() | tuple of integer()
-%%%
-%%% Output: {_,_,_,_} | {_,_,_,_,_,_,_,_}
-%%%
-%%% Exceptions: error: {invalid_address, erlang:get_stacktrace()}
-%%% ---------------------------------------------------------------------------
-
--spec ipaddr(string() | tuple())
- -> inet:ip_address().
-
-%% Don't convert lists of integers since a length 8 list like
-%% [$1,$0,$.,$0,$.,$0,$.,$1] is ambiguous: is it "10.0.0.1" or
-%% "49:48:46:48:46:48:46:49"?
-%%
-%% RFC 2373 defines the format parsed for v6 addresses.
-
-%% Be brutal.
-ipaddr(Addr) ->
- try
- ip(Addr)
- catch
- error: _ ->
- erlang:error({invalid_address, ?STACK})
- end.
-
-%% Already a tuple: ensure non-negative integers of the right size.
-ip(T)
- when size(T) == 4;
- size(T) == 8 ->
- Bs = 2*size(T),
- [] = lists:filter(fun(N) when 0 =< N -> 0 < N bsr Bs end,
- tuple_to_list(T)),
- T;
-
-%% Or not: convert from '.'/':'-separated decimal/hex.
-ip(Addr) ->
- {ok, A} = inet_parse:address(Addr), %% documented in inet(3)
- A.
-
-%%% ---------------------------------------------------------------------------
-%%% # spawn_opts(Type, Opts)
-%%% ---------------------------------------------------------------------------
-
-%% TODO: config variables.
-
-spawn_opts(server, Opts) ->
- opts(75000, Opts);
-spawn_opts(worker, Opts) ->
- opts(5000, Opts).
-
-opts(HeapSize, Opts) ->
- [{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)].
-
-%%% ---------------------------------------------------------------------------
-%%% # wait(MRefs)
-%%% ---------------------------------------------------------------------------
-
-wait(L) ->
- w([erlang:monitor(process, P) || P <- L]).
-
-w([]) ->
- ok;
-w(L) ->
- receive
- {'DOWN', MRef, process, _, _} ->
- w(lists:delete(MRef, L))
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # fold_tuple(N, T0, T)
-%%% ---------------------------------------------------------------------------
-
-%% Replace fields in T0 by those of T starting at index N, unless the
-%% new value is 'undefined'.
-%%
-%% eg. fold_tuple(2, Hdr, #diameter_header{end_to_end_id = 42})
-
-fold_tuple(_, T, undefined) ->
- T;
-
-fold_tuple(N, T0, T1) ->
- {_, T} = lists:foldl(fun(V, {I,_} = IT) -> {I+1, ft(V, IT)} end,
- {N, T0},
- lists:nthtail(N-1, tuple_to_list(T1))),
- T.
-
-ft(undefined, {_, T}) ->
- T;
-ft(Value, {Idx, T}) ->
- setelement(Idx, T, Value).
-
-%%% ----------------------------------------------------------
-%%% # log(Slogan, Mod, Line, Details)
-%%%
-%%% Called to have something to trace on for happenings of interest.
-%%% ----------------------------------------------------------
-
-log(_, _, _, _) ->
- ok.
diff --git a/lib/diameter/src/app/diameter_misc_sup.erl b/lib/diameter/src/app/diameter_misc_sup.erl
deleted file mode 100644
index 4e40476f14..0000000000
--- a/lib/diameter/src/app/diameter_misc_sup.erl
+++ /dev/null
@@ -1,58 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% The supervisor of the static server processes.
-%%
-
--module(diameter_misc_sup).
-
--behaviour(supervisor).
-
--export([start_link/0]). %% supervisor start
-
-%% supervisor callback
--export([init/1]).
-
--define(CHILDREN, [diameter_sync, %% serialization
- diameter_stats, %% statistics counter management
- diameter_reg, %% service/property publishing
- diameter_peer, %% remote peer manager
- diameter_config]). %% configuration/restart
-
-%% start_link/0
-
-start_link() ->
- SupName = {local, ?MODULE},
- supervisor:start_link(SupName, ?MODULE, []).
-
-%% init/1
-
-init([]) ->
- Flags = {one_for_one, 1, 5},
- Workers = lists:map(fun spec/1, ?CHILDREN),
- {ok, {Flags, Workers}}.
-
-spec(Mod) ->
- {Mod,
- {Mod, start_link, []},
- permanent,
- 1000,
- worker,
- [Mod]}.
diff --git a/lib/diameter/src/app/diameter_peer.erl b/lib/diameter/src/app/diameter_peer.erl
deleted file mode 100644
index 3e78c4caef..0000000000
--- a/lib/diameter/src/app/diameter_peer.erl
+++ /dev/null
@@ -1,225 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_peer).
-
--behaviour(gen_server).
-
-%% Interface towards transport modules ...
--export([recv/2,
- up/1,
- up/2]).
-
-%% ... and the stack.
--export([start/3,
- send/2,
- close/1,
- abort/1,
- notify/2]).
-
-%% Server start.
--export([start_link/0]).
-
-%% gen_server callbacks
--export([init/1,
- terminate/2,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- code_change/3]).
-
-%% debug
--export([state/0,
- uptime/0]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
-%% Registered name of the server.
--define(SERVER, ?MODULE).
-
-%% Server state.
--record(state, {id = now()}).
-
-%%% ---------------------------------------------------------------------------
-%%% # notify/2
-%%% ---------------------------------------------------------------------------
-
-notify(SvcName, T) ->
- rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}).
-
-%%% ---------------------------------------------------------------------------
-%%% # start/3
-%%% ---------------------------------------------------------------------------
-
-start(T, Opts, #diameter_service{} = Svc) ->
- {Mod, Cfg} = split_transport(Opts),
- apply(Mod, start, [T, Svc, Cfg]).
-
-%%% ---------------------------------------------------------------------------
-%%% # up/[12]
-%%% ---------------------------------------------------------------------------
-
-up(Pid) -> %% accepting transport
- ifc_send(Pid, {self(), connected}).
-
-up(Pid, Remote) -> %% connecting transport
- ifc_send(Pid, {self(), connected, Remote}).
-
-%%% ---------------------------------------------------------------------------
-%%% # recv/2
-%%% ---------------------------------------------------------------------------
-
-recv(Pid, Pkt) ->
- ifc_send(Pid, {recv, Pkt}).
-
-%%% ---------------------------------------------------------------------------
-%%% # send/2
-%%% ---------------------------------------------------------------------------
-
-send(Pid, #diameter_packet{transport_data = undefined,
- bin = Bin}) ->
- send(Pid, Bin);
-
-send(Pid, Pkt) ->
- ifc_send(Pid, {send, Pkt}).
-
-%%% ---------------------------------------------------------------------------
-%%% # close/1
-%%% ---------------------------------------------------------------------------
-
-close(Pid) ->
- ifc_send(Pid, {close, self()}).
-
-%%% ---------------------------------------------------------------------------
-%%% # abort/1
-%%% ---------------------------------------------------------------------------
-
-abort(Pid) ->
- exit(Pid, shutdown).
-
-%% ---------------------------------------------------------------------------
-%% ---------------------------------------------------------------------------
-
-start_link() ->
- ServerName = {local, ?SERVER},
- Module = ?MODULE,
- Args = [],
- Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
- gen_server:start_link(ServerName, Module, Args, Options).
-
-state() ->
- call(state).
-
-uptime() ->
- call(uptime).
-
-%%% ----------------------------------------------------------
-%%% # init(Role)
-%%% ----------------------------------------------------------
-
-init([]) ->
- {ok, #state{}}.
-
-%%% ----------------------------------------------------------
-%%% # handle_call(Request, From, State)
-%%% ----------------------------------------------------------
-
-handle_call(state, _, State) ->
- {reply, State, State};
-
-handle_call(uptime, _, #state{id = Time} = State) ->
- {reply, diameter_lib:now_diff(Time), State};
-
-handle_call(Req, From, State) ->
- ?UNEXPECTED([Req, From]),
- {reply, nok, State}.
-
-%%% ----------------------------------------------------------
-%%% # handle_cast(Request, State)
-%%% ----------------------------------------------------------
-
-handle_cast(Msg, State) ->
- ?UNEXPECTED([Msg]),
- {noreply, State}.
-
-%%% ----------------------------------------------------------
-%%% # handle_info(Request, State)
-%%% ----------------------------------------------------------
-
-%% Remote service is distributing a message.
-handle_info({notify, SvcName, T}, S) ->
- bang(diameter_service:whois(SvcName), T),
- {noreply, S};
-
-handle_info(Info, State) ->
- ?UNEXPECTED([Info]),
- {noreply, State}.
-
-%% ----------------------------------------------------------
-%% terminate(Reason, State)
-%% ----------------------------------------------------------
-
-terminate(_Reason, _State) ->
- ok.
-
-%% ----------------------------------------------------------
-%% code_change(OldVsn, State, Extra)
-%% ----------------------------------------------------------
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%% ---------------------------------------------------------
-%% INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
-
-%% ifc_send/2
-%%
-%% Send something over the transport interface.
-
-ifc_send(Pid, T) ->
- Pid ! {diameter, T}.
-
-%% bang/2
-
-bang(undefined = No, _) ->
- No;
-bang(Pid, T) ->
- Pid ! T.
-
-%% split_transport/1
-%%
-%% Split options into transport module, transport config and
-%% remaining options.
-
-split_transport(Opts) ->
- {[M,C], _} = proplists:split(Opts, [transport_module,
- transport_config]),
- {value(M, diameter_tcp), value(C, [])}.
-
-value([{_,V}], _) ->
- V;
-value([], V) ->
- V.
-
-%% call/1
-
-call(Request) ->
- gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/app/diameter_peer_fsm.erl b/lib/diameter/src/app/diameter_peer_fsm.erl
deleted file mode 100644
index 282fa2742f..0000000000
--- a/lib/diameter/src/app/diameter_peer_fsm.erl
+++ /dev/null
@@ -1,777 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% This module implements (as a process) the RFC 3588 Peer State
-%% Machine modulo the necessity of adapting the peer election to the
-%% fact that we don't know the identity of a peer until we've
-%% received a CER/CEA from it.
-%%
-
--module(diameter_peer_fsm).
--behaviour(gen_server).
-
-%% Interface towards diameter_watchdog.
--export([start/3]).
-
-%% gen_server callbacks
--export([init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3]).
-
-%% diameter_peer_fsm_sup callback
--export([start_link/1]).
-
-%% internal callbacks
--export([match/1]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
--include("diameter_types.hrl").
--include("diameter_gen_base_rfc3588.hrl").
-
--define(GOAWAY, ?'DIAMETER_BASE_DISCONNECT-CAUSE_DO_NOT_WANT_TO_TALK_TO_YOU').
--define(REBOOT, ?'DIAMETER_BASE_DISCONNECT-CAUSE_REBOOTING').
-
--define(NO_INBAND_SECURITY, 0).
--define(TLS, 1).
-
--define(LOOP_TIMEOUT, 2000).
-
-%% RFC 3588:
-%%
-%% Timeout An application-defined timer has expired while waiting
-%% for some event.
-%%
--define(EVENT_TIMEOUT, 10000).
-
-%% How long to wait for a DPA in response to DPR before simply
-%% aborting. Used to distinguish between shutdown and not but there's
-%% not really any need. Stopping a service will require a timeout if
-%% the peer doesn't answer DPR so the value should be short-ish.
--define(DPA_TIMEOUT, 1000).
-
--record(state,
- {state = 'Wait-Conn-Ack' %% state of RFC 3588 Peer State Machine
- :: 'Wait-Conn-Ack' | recv_CER | 'Wait-CEA' | 'Open',
- mode :: accept | connect | {connect, reference()},
- parent :: pid(),
- transport :: pid(),
- service :: #diameter_service{},
- dpr = false :: false | {'Unsigned32'(), 'Unsigned32'()}}).
- %% | hop by hop and end to end identifiers
-
-%% There are non-3588 states possible as a consequence of 5.6.1 of the
-%% standard and the corresponding problem for incoming CEA's: we don't
-%% know who we're talking to until either a CER or CEA has been
-%% received. The CEA problem in particular makes it impossible to
-%% follow the state machine exactly as documented in 3588: there can
-%% be no election until the CEA arrives and we have an Origin-Host to
-%% elect.
-
-%%
-%% Once upon a time start/2 started a process akin to that started by
-%% start/3 below, which in turn started a watchdog/transport process
-%% with the result that the watchdog could send DWR/DWA regardless of
-%% whether or not the corresponding Peer State Machine was in its open
-%% state; that is, before capabilities exchange had taken place. This
-%% is not what RFC's 3588 and 3539 say (albeit not very clearly).
-%% Watchdog messages are only exchanged on *open* connections, so the
-%% 3539 state machine is more naturally placed on top of the 3588 Peer
-%% State Machine rather than closer to the transport. This is what we
-%% now do below: connect/accept call diameter_watchdog and return the
-%% pid of the watchdog process, and the watchdog in turn calls start/3
-%% below to start the process implementing the Peer State Machine. The
-%% former is a "peer" in diameter_service while the latter is a
-%% "conn". In a sense, diameter_service sees the watchdog as
-%% implementing the Peer State Machine and the process implemented
-%% here as being the transport, not being aware of the watchdog at
-%% all.
-%%
-
-%%% ---------------------------------------------------------------------------
-%%% # start({connect|accept, Ref}, Opts, Service)
-%%%
-%%% Output: Pid
-%%% ---------------------------------------------------------------------------
-
-%% diameter_config requires a non-empty list of applications on the
-%% service but diameter_service then constrains the list to any
-%% specified on the transport in question. Check here that the list is
-%% still non-empty.
-
-start({_, Ref} = Type, Opts, #diameter_service{applications = Apps} = Svc) ->
- [] /= Apps orelse ?ERROR({no_apps, Type, Opts}),
- T = {self(), Type, Opts, Svc},
- {ok, Pid} = diameter_peer_fsm_sup:start_child(T),
- diameter_stats:reg(Pid, Ref),
- Pid.
-
-start_link(T) ->
- {ok, _} = proc_lib:start_link(?MODULE,
- init,
- [T],
- infinity,
- diameter_lib:spawn_opts(server, [])).
-
-%%% ---------------------------------------------------------------------------
-%%% ---------------------------------------------------------------------------
-
-%% init/1
-
-init(T) ->
- proc_lib:init_ack({ok, self()}),
- gen_server:enter_loop(?MODULE, [], i(T)).
-
-i({WPid, {M, _} = T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
- putr(dwa, dwa(Caps)),
- {ok, TPid, Svc} = start_transport(T, Opts, Svc0),
- erlang:monitor(process, TPid),
- erlang:monitor(process, WPid),
- #state{parent = WPid,
- transport = TPid,
- mode = M,
- service = Svc}.
-%% The transport returns its local ip addresses so that different
-%% transports on the same service can use different local addresses.
-%% The local addresses are put into Host-IP-Address avps here when
-%% sending capabilities exchange messages.
-%%
-%% Invalid transport config may cause us to crash but note that the
-%% watchdog start (start/2) succeeds regardless so as not to crash the
-%% service.
-
-start_transport(T, Opts, Svc) ->
- case diameter_peer:start(T, Opts, Svc) of
- {ok, TPid} ->
- {ok, TPid, Svc};
- {ok, TPid, [_|_] = Addrs} ->
- #diameter_service{capabilities = Caps0} = Svc,
- Caps = Caps0#diameter_caps{host_ip_address = Addrs},
- {ok, TPid, Svc#diameter_service{capabilities = Caps}};
- No ->
- exit({shutdown, No})
- end.
-
-%% handle_call/3
-
-handle_call(_, _, State) ->
- {reply, nok, State}.
-
-%% handle_cast/2
-
-handle_cast(_, State) ->
- {noreply, State}.
-
-%% handle_info/1
-
-handle_info(T, #state{} = State) ->
- try transition(T, State) of
- ok ->
- {noreply, State};
- #state{state = X} = S ->
- ?LOGC(X =/= State#state.state, transition, X),
- {noreply, S};
- {stop, Reason} ->
- ?LOG(stop, Reason),
- x(Reason, State);
- stop ->
- ?LOG(stop, T),
- x(T, State)
- catch
- throw: {?MODULE, Tag, Reason} ->
- ?LOG(Tag, {Reason, T}),
- {stop, {shutdown, Reason}, State}
- end.
-
-x(Reason, #state{} = S) ->
- close_wd(Reason, S),
- {stop, {shutdown, Reason}, S}.
-
-%% terminate/2
-
-terminate(_, _) ->
- ok.
-
-%% code_change/3
-
-code_change(_, State, _) ->
- {ok, State}.
-
-%%% ---------------------------------------------------------------------------
-%%% ---------------------------------------------------------------------------
-
-putr(Key, Val) ->
- put({?MODULE, Key}, Val).
-
-getr(Key) ->
- get({?MODULE, Key}).
-
-%% transition/2
-
-%% Connection to peer.
-transition({diameter, {TPid, connected, Remote}},
- #state{state = PS,
- mode = M}
- = S) ->
- 'Wait-Conn-Ack' = PS, %% assert
- connect = M, %%
- send_CER(S#state{mode = {M, Remote},
- transport = TPid});
-
-%% Connection from peer.
-transition({diameter, {TPid, connected}},
- #state{state = PS,
- mode = M,
- parent = Pid}
- = S) ->
- 'Wait-Conn-Ack' = PS, %% assert
- accept = M, %%
- Pid ! {accepted, self()},
- start_timer(S#state{state = recv_CER,
- transport = TPid});
-
-%% Incoming message from the transport.
-transition({diameter, {recv, Pkt}}, S) ->
- recv(Pkt, S);
-
-%% Timeout when still in the same state ...
-transition({timeout, PS}, #state{state = PS}) ->
- stop;
-
-%% ... or not.
-transition({timeout, _}, _) ->
- ok;
-
-%% Outgoing message.
-transition({send, Msg}, #state{transport = TPid}) ->
- send(TPid, Msg),
- ok;
-
-%% Request for graceful shutdown.
-transition({shutdown, Pid}, #state{parent = Pid, dpr = false} = S) ->
- dpr(?GOAWAY, S);
-transition({shutdown, Pid}, #state{parent = Pid}) ->
- ok;
-
-%% Application shutdown.
-transition(shutdown, #state{dpr = false} = S) ->
- dpr(?REBOOT, S);
-transition(shutdown, _) -> %% DPR already send: ensure expected timeout
- dpa_timer(),
- ok;
-
-%% Request to close the transport connection.
-transition({close = T, Pid}, #state{parent = Pid,
- transport = TPid}) ->
- diameter_peer:close(TPid),
- {stop, T};
-
-%% DPA reception has timed out.
-transition(dpa_timeout, _) ->
- stop;
-
-%% Someone wants to know a resolved port: forward to the transport process.
-transition({resolve_port, _Pid} = T, #state{transport = TPid}) ->
- TPid ! T,
- ok;
-
-%% Parent or transport has died.
-transition({'DOWN', _, process, P, _},
- #state{parent = Pid,
- transport = TPid})
- when P == Pid;
- P == TPid ->
- stop;
-
-%% State query.
-transition({state, Pid}, #state{state = S, transport = TPid}) ->
- Pid ! {self(), [S, TPid]},
- ok.
-
-%% Crash on anything unexpected.
-
-%% send_CER/1
-
-send_CER(#state{mode = {connect, Remote},
- service = #diameter_service{capabilities = Caps},
- transport = TPid}
- = S) ->
- req_send_CER(Caps#diameter_caps.origin_host, Remote)
- orelse
- close(connected, S),
- CER = build_CER(S),
- ?LOG(send, 'CER'),
- send(TPid, encode(CER)),
- start_timer(S#state{state = 'Wait-CEA'}).
-
-%% Register ourselves as connecting to the remote endpoint in
-%% question. This isn't strictly necessary since a peer implementing
-%% the 3588 Peer State Machine should reject duplicate connection's
-%% from the same peer but there's little point in us setting up a
-%% duplicate connection in the first place. This could also include
-%% the transport protocol being used but since we're blind to
-%% transport just avoid duplicate connections to the same host/port.
-req_send_CER(OriginHost, Remote) ->
- register_everywhere({?MODULE, connection, OriginHost, {remote, Remote}}).
-
-%% start_timer/1
-
-start_timer(#state{state = PS} = S) ->
- erlang:send_after(?EVENT_TIMEOUT, self(), {timeout, PS}),
- S.
-
-%% build_CER/1
-
-build_CER(#state{service = #diameter_service{capabilities = Caps}}) ->
- {ok, CER} = diameter_capx:build_CER(Caps),
- CER.
-
-%% encode/1
-
-encode(Rec) ->
- #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Rec),
- Bin.
-
-%% recv/2
-
-%% RFC 3588 has result code 5015 for an invalid length but if a
-%% transport is detecting message boundaries using the length header
-%% then a length error will likely lead to further errors.
-
-recv(#diameter_packet{header = #diameter_header{length = Len}
- = Hdr,
- bin = Bin},
- S)
- when Len < 20;
- (0 /= Len rem 4 orelse bit_size(Bin) /= 8*Len) ->
- discard(invalid_message_length, recv, [size(Bin),
- bit_size(Bin) rem 8,
- Hdr,
- S]);
-
-recv(#diameter_packet{header = #diameter_header{} = Hdr}
- = Pkt,
- #state{parent = Pid}
- = S) ->
- Name = diameter_codec:msg_name(Hdr),
- Pid ! {recv, self(), Name, Pkt},
- diameter_stats:incr({msg_id(Name, Hdr), recv}), %% count received
- rcv(Name, Pkt, S);
-
-recv(#diameter_packet{header = undefined,
- bin = Bin}
- = Pkt,
- S) ->
- recv(Pkt#diameter_packet{header = diameter_codec:decode_header(Bin)}, S);
-
-recv(Bin, S)
- when is_binary(Bin) ->
- recv(#diameter_packet{bin = Bin}, S);
-
-recv(#diameter_packet{header = false} = Pkt, S) ->
- discard(truncated_header, recv, [Pkt, S]).
-
-msg_id({_,_,_} = T, _) ->
- T;
-msg_id(_, Hdr) ->
- diameter_codec:msg_id(Hdr).
-
-%% Treat invalid length as a transport error and die. Especially in
-%% the TCP case, in which there's no telling where the next message
-%% begins in the incoming byte stream, keeping a crippled connection
-%% alive may just make things worse.
-
-discard(Reason, F, A) ->
- diameter_stats:incr(Reason),
- diameter_lib:warning_report(Reason, {?MODULE, F, A}),
- throw({?MODULE, abort, Reason}).
-
-%% rcv/3
-
-%% Incoming CEA.
-rcv('CEA', Pkt, #state{state = 'Wait-CEA'} = S) ->
- handle_CEA(Pkt, S);
-
-%% Incoming CER
-rcv('CER' = N, Pkt, #state{state = recv_CER} = S) ->
- handle_request(N, Pkt, S);
-
-%% Anything but CER/CEA in a non-Open state is an error, as is
-%% CER/CEA in anything but recv_CER/Wait-CEA.
-rcv(Name, _, #state{state = PS})
- when PS /= 'Open';
- Name == 'CER';
- Name == 'CEA' ->
- {stop, {Name, PS}};
-
-rcv(N, Pkt, S)
- when N == 'DWR';
- N == 'DPR' ->
- handle_request(N, Pkt, S);
-
-%% DPA even though we haven't sent DPR: ignore.
-rcv('DPA', _Pkt, #state{dpr = false}) ->
- ok;
-
-%% DPA in response to DPR. We could check the sequence numbers but
-%% don't bother, just close.
-rcv('DPA' = N, _Pkt, #state{transport = TPid}) ->
- diameter_peer:close(TPid),
- {stop, N};
-
-rcv(_, _, _) ->
- ok.
-
-%% send/2
-
-%% Msg here could be a #diameter_packet or a binary depending on who's
-%% sending. In particular, the watchdog will send DWR as a binary
-%% while messages coming from clients will be in a #diameter_packet.
-send(Pid, Msg) ->
- diameter_stats:incr({diameter_codec:msg_id(Msg), send}),
- diameter_peer:send(Pid, Msg).
-
-%% handle_request/3
-
-handle_request(Type, #diameter_packet{} = Pkt, S) ->
- ?LOG(recv, Type),
- send_answer(Type, diameter_codec:decode(?BASE, Pkt), S).
-
-%% send_answer/3
-
-send_answer(Type, ReqPkt, #state{transport = TPid} = S) ->
- #diameter_packet{header = #diameter_header{version = V,
- end_to_end_id = Eid,
- hop_by_hop_id = Hid,
- is_proxiable = P},
- transport_data = TD}
- = ReqPkt,
-
- {Answer, PostF} = build_answer(Type, V, ReqPkt, S),
-
- Pkt = #diameter_packet{header = #diameter_header{version = V,
- end_to_end_id = Eid,
- hop_by_hop_id = Hid,
- is_proxiable = P},
- msg = Answer,
- transport_data = TD},
-
- send(TPid, diameter_codec:encode(?BASE, Pkt)),
- eval(PostF, S).
-
-eval([F|A], S) ->
- apply(F, A ++ [S]);
-eval(ok, S) ->
- S.
-
-%% build_answer/4
-
-build_answer('CER',
- ?DIAMETER_VERSION,
- #diameter_packet{msg = CER,
- header = #diameter_header{is_error = false},
- errors = []}
- = Pkt,
- #state{service = Svc}
- = S) ->
- #diameter_service{capabilities = #diameter_caps{origin_host = OH}}
- = Svc,
-
- {SupportedApps,
- #diameter_caps{origin_host = DH} = RCaps,
- #diameter_base_CEA{'Result-Code' = RC}
- = CEA}
- = recv_CER(CER, S),
-
- try
- 2001 == RC %% DIAMETER_SUCCESS
- orelse ?THROW({sent_CEA, RC}),
- register_everywhere({?MODULE, connection, OH, DH})
- orelse ?THROW({election_lost, 4003}),
- #diameter_base_CEA{'Inband-Security-Id' = [IS]}
- = CEA,
- {CEA, [fun open/5, Pkt, SupportedApps, RCaps, {accept, IS}]}
- catch
- ?FAILURE({Reason, RC}) ->
- {answer('CER', S) ++ [{'Result-Code', RC}],
- [fun close/2, {'CER', Reason, DH}]}
- end;
-
-%% The error checks below are similar to those in diameter_service for
-%% other messages. Should factor out the commonality.
-
-build_answer(Type, V, #diameter_packet{header = H, errors = Es} = Pkt, S) ->
- FailedAvp = failed_avp([A || {_,A} <- Es]),
- Ans = answer(answer(Type, S), V, H, Es),
- {set(Ans, FailedAvp), if 'CER' == Type ->
- [fun close/2, {Type, V, Pkt}];
- true ->
- ok
- end}.
-
-failed_avp([] = No) ->
- No;
-failed_avp(Avps) ->
- [{'Failed-AVP', [[{'AVP', Avps}]]}].
-
-set(Ans, []) ->
- Ans;
-set(['answer-message' | _] = Ans, FailedAvp) ->
- Ans ++ [{'AVP', [FailedAvp]}];
-set([_|_] = Ans, FailedAvp) ->
- Ans ++ FailedAvp.
-
-answer([_, OH, OR | _], _, #diameter_header{is_error = true}, _) ->
- ['answer-message', OH, OR, {'Result-Code', 3008}];
-
-answer([_, OH, OR | _], _, _, [Bs|_])
- when is_bitstring(Bs) ->
- ['answer-message', OH, OR, {'Result-Code', 3009}];
-
-answer(Ans, ?DIAMETER_VERSION, _, Es) ->
- Ans ++ [{'Result-Code', rc(Es)}];
-
-answer(Ans, _, _, _) ->
- Ans ++ [{'Result-Code', 5011}]. %% DIAMETER_UNSUPPORTED_VERSION
-
-rc([]) ->
- 2001; %% DIAMETER_SUCCESS
-rc([{RC,_}|_]) ->
- RC;
-rc([RC|_]) ->
- RC.
-
-%% DIAMETER_INVALID_HDR_BITS 3008
-%% A request was received whose bits in the Diameter header were
-%% either set to an invalid combination, or to a value that is
-%% inconsistent with the command code's definition.
-
-%% DIAMETER_INVALID_AVP_BITS 3009
-%% A request was received that included an AVP whose flag bits are
-%% set to an unrecognized value, or that is inconsistent with the
-%% AVP's definition.
-
-%% ELECTION_LOST 4003
-%% The peer has determined that it has lost the election process and
-%% has therefore disconnected the transport connection.
-
-%% DIAMETER_NO_COMMON_APPLICATION 5010
-%% This error is returned when a CER message is received, and there
-%% are no common applications supported between the peers.
-
-%% DIAMETER_UNSUPPORTED_VERSION 5011
-%% This error is returned when a request was received, whose version
-%% number is unsupported.
-
-%% answer/2
-
-answer('DWR', _) ->
- getr(dwa);
-
-answer(Name, #state{service = #diameter_service{capabilities = Caps}}) ->
- a(Name, Caps).
-
-a('CER', #diameter_caps{vendor_id = Vid,
- origin_host = Host,
- origin_realm = Realm,
- host_ip_address = Addrs,
- product_name = Name}) ->
- ['CEA', {'Origin-Host', Host},
- {'Origin-Realm', Realm},
- {'Host-IP-Address', Addrs},
- {'Vendor-Id', Vid},
- {'Product-Name', Name}];
-
-a('DPR', #diameter_caps{origin_host = Host,
- origin_realm = Realm}) ->
- ['DPA', {'Origin-Host', Host},
- {'Origin-Realm', Realm}].
-
-%% recv_CER/2
-
-recv_CER(CER, #state{service = Svc}) ->
- {ok, T} = diameter_capx:recv_CER(CER, Svc),
- T.
-
-%% handle_CEA/1
-
-handle_CEA(#diameter_packet{header = #diameter_header{version = V},
- bin = Bin}
- = Pkt,
- #state{service = #diameter_service{capabilities = LCaps}}
- = S)
- when is_binary(Bin) ->
- ?LOG(recv, 'CEA'),
-
- ?DIAMETER_VERSION == V orelse close({version, V}, S),
-
- #diameter_packet{msg = CEA, errors = Errors}
- = DPkt
- = diameter_codec:decode(?BASE, Pkt),
-
- [] == Errors orelse close({errors, Errors}, S),
-
- {SApps, [IS], #diameter_caps{origin_host = DH} = RCaps}
- = recv_CEA(CEA, S),
-
- #diameter_caps{origin_host = OH}
- = LCaps,
-
- %% Ensure that we don't already have a connection to the peer in
- %% question. This isn't the peer election of 3588 except in the
- %% sense that, since we don't know who we're talking to until we
- %% receive a CER/CEA, the first that arrives wins the right to a
- %% connection with the peer.
-
- register_everywhere({?MODULE, connection, OH, DH})
- orelse close({'CEA', DH}, S),
-
- open(DPkt, SApps, RCaps, {connect, IS}, S).
-
-%% recv_CEA/2
-
-recv_CEA(CEA, #state{service = Svc} = S) ->
- case diameter_capx:recv_CEA(CEA, Svc) of
- {ok, {_,_}} -> %% return from old code
- close({'CEA', update}, S);
- {ok, {[], _, _}} ->
- close({'CEA', no_common_application}, S);
- {ok, {_, [], _}} ->
- close({'CEA', no_common_security}, S);
- {ok, {_,_,_} = T} ->
- T;
- {error, Reason} ->
- close({'CEA', Reason}, S)
- end.
-
-%% open/5
-
-open(Pkt, SupportedApps, RCaps, {Type, IS}, #state{parent = Pid,
- service = Svc}
- = S) ->
- #diameter_service{capabilities = #diameter_caps{origin_host = OH,
- inband_security_id = LS}
- = LCaps}
- = Svc,
- #diameter_caps{origin_host = DH}
- = RCaps,
-
- tls_ack(lists:member(?TLS, LS), Type, IS, S),
- Pid ! {open, self(), {OH,DH}, {capz(LCaps, RCaps), SupportedApps, Pkt}},
-
- S#state{state = 'Open'}.
-
-%% We've advertised TLS support: tell the transport the result
-%% and expect a reply when the handshake is complete.
-tls_ack(true, Type, IS, #state{transport = TPid} = S) ->
- Ref = make_ref(),
- MRef = erlang:monitor(process, TPid),
- TPid ! {diameter, {tls, Ref, Type, IS == ?TLS}},
- receive
- {diameter, {tls, Ref}} ->
- erlang:demonitor(MRef, [flush]);
- {'DOWN', MRef, process, _, _} = T ->
- close({tls_ack, T}, S)
- end;
-
-%% Or not. Don't send anything to the transport so that transports
-%% not supporting TLS work as before without modification.
-tls_ack(false, _, _, _) ->
- ok.
-
-capz(#diameter_caps{} = L, #diameter_caps{} = R) ->
- #diameter_caps{}
- = list_to_tuple([diameter_caps | lists:zip(tl(tuple_to_list(L)),
- tl(tuple_to_list(R)))]).
-
-%% close/2
-
-%% Tell the watchdog that our death isn't due to transport failure.
-close(Reason, #state{parent = Pid}) ->
- close_wd(Reason, Pid),
- throw({?MODULE, close, Reason}).
-
-%% close_wd/2
-
-%% Ensure the watchdog dies if DPR has been sent ...
-close_wd(_, #state{dpr = false}) ->
- ok;
-close_wd(Reason, #state{parent = Pid}) ->
- close_wd(Reason, Pid);
-
-%% ... or otherwise
-close_wd(Reason, Pid) ->
- Pid ! {close, self(), Reason}.
-
-%% dwa/1
-
-dwa(#diameter_caps{origin_host = OH,
- origin_realm = OR,
- origin_state_id = OSI}) ->
- ['DWA', {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Origin-State-Id', OSI}].
-
-%% dpr/2
-
-dpr(Cause, #state{transport = TPid,
- service = #diameter_service{capabilities = Caps}}
- = S) ->
- #diameter_caps{origin_host = OH,
- origin_realm = OR}
- = Caps,
-
- Bin = encode(['DPR', {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Disconnect-Cause', Cause}]),
- send(TPid, Bin),
- dpa_timer(),
- ?LOG(send, 'DPR'),
- S#state{dpr = diameter_codec:sequence_numbers(Bin)}.
-
-dpa_timer() ->
- erlang:send_after(?DPA_TIMEOUT, self(), dpa_timeout).
-
-%% register_everywhere/1
-%%
-%% Register a term and ensure it's not registered elsewhere. Note that
-%% two process that simultaneously register the same term may well
-%% both fail to do so this isn't foolproof.
-
-register_everywhere(T) ->
- diameter_reg:add_new(T)
- andalso unregistered(T).
-
-unregistered(T) ->
- {ResL, _} = rpc:multicall(?MODULE, match, [{node(), T}]),
- lists:all(fun(L) -> [] == L end, ResL).
-
-match({Node, _})
- when Node == node() ->
- [];
-match({_, T}) ->
- try
- diameter_reg:match(T)
- catch
- _:_ -> []
- end.
diff --git a/lib/diameter/src/app/diameter_peer_fsm_sup.erl b/lib/diameter/src/app/diameter_peer_fsm_sup.erl
deleted file mode 100644
index 995eaf74d0..0000000000
--- a/lib/diameter/src/app/diameter_peer_fsm_sup.erl
+++ /dev/null
@@ -1,63 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% The supervisor of peer_fsm processes.
-%%
-
--module(diameter_peer_fsm_sup).
-
--behaviour(supervisor).
-
--define(NAME, ?MODULE). %% supervisor name
-
-%%-----------------------------------------------------------------
-%% External exports
-%%-----------------------------------------------------------------
-
--export([start_link/0, %% supervisor start
- start_child/1]). %% peer fsm start
-
--export([init/1]).
-
-%% start_link/0
-
-start_link() ->
- SupName = {local, ?NAME},
- supervisor:start_link(SupName, ?MODULE, []).
-
-%% start_child/1
-%%
-%% Start a peer_fsm process.
-
-start_child(T) ->
- supervisor:start_child(?NAME, [T]).
-
-%% init/1
-
-init([]) ->
- Mod = diameter_peer_fsm,
- Flags = {simple_one_for_one, 0, 1},
- ChildSpec = {Mod,
- {Mod, start_link, []},
- temporary,
- 1000,
- worker,
- [Mod]},
- {ok, {Flags, [ChildSpec]}}.
diff --git a/lib/diameter/src/app/diameter_reg.erl b/lib/diameter/src/app/diameter_reg.erl
deleted file mode 100644
index 882b9da238..0000000000
--- a/lib/diameter/src/app/diameter_reg.erl
+++ /dev/null
@@ -1,327 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% The module implements a simple term -> pid registry.
-%%
-
--module(diameter_reg).
--compile({no_auto_import, [monitor/2]}).
-
--behaviour(gen_server).
-
--export([add/1,
- add_new/1,
- del/1,
- repl/2,
- match/1]).
-
--export([start_link/0]).
-
-%% gen_server callbacks
--export([init/1,
- terminate/2,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- code_change/3]).
-
-%% test
--export([pids/0,
- terms/0]).
-
-%% debug
--export([state/0,
- uptime/0]).
-
--include("diameter_internal.hrl").
-
--define(SERVER, ?MODULE).
--define(TABLE, ?MODULE).
-
-%% Table entry used to keep from starting more than one monitor on the
-%% same process. This isn't a problem but there's no point in starting
-%% multiple monitors if we can avoid it. Note that we can't have a 2-tuple
-%% keyed on Pid since a registered term can be anything. Want the entry
-%% keyed on Pid so that lookup is fast.
--define(MONITOR(Pid, MRef), {Pid, monitor, MRef}).
-
-%% Table entry containing the Term -> Pid mapping.
--define(MAPPING(Term, Pid), {Term, Pid}).
-
--record(state, {id = now()}).
-
-%%% ----------------------------------------------------------
-%%% # add(T)
-%%%
-%%% Input: Term = term()
-%%%
-%%% Output: true
-%%%
-%%% Description: Associate the specified term with self(). The list of pids
-%%% having this or other assocations can be retrieved using
-%%% match/1.
-%%%
-%%% An association is removed when the calling process dies
-%%% or as a result of calling del/1. Adding the same term
-%%% more than once is equivalent to adding it exactly once.
-%%%
-%%% Note that since match/1 takes a pattern as argument,
-%%% specifying a term that contains match variables is
-%%% probably not a good idea
-%%% ----------------------------------------------------------
-
--spec add(any())
- -> true.
-
-add(T) ->
- call({add, fun ets:insert/2, T, self()}).
-
-%%% ----------------------------------------------------------
-%%% # add_new(T)
-%%%
-%%% Input: T = term()
-%%%
-%%% Output: true | false
-%%%
-%%% Description: Like add/1 but only one process is allowed to have the
-%%% the association, false being returned if an association
-%%% already exists.
-%%% ----------------------------------------------------------
-
--spec add_new(any())
- -> boolean().
-
-add_new(T) ->
- call({add, fun insert_new/2, T, self()}).
-
-%%% ----------------------------------------------------------
-%%% # repl(T, NewT)
-%%%
-%%% Input: T, NewT = term()
-%%%
-%%% Output: true | false
-%%%
-%%% Description: Like add/1 but only replace an existing association on T,
-%%% false being returned if it doesn't exist.
-%%% ----------------------------------------------------------
-
--spec repl(any(), any())
- -> boolean().
-
-repl(T, U) ->
- call({repl, T, U, self()}).
-
-%%% ----------------------------------------------------------
-%%% # del(Term)
-%%%
-%%% Input: Term = term()
-%%%
-%%% Output: true
-%%%
-%%% Description: Remove any existing association of Term with self().
-%%% ----------------------------------------------------------
-
--spec del(any())
- -> true.
-
-del(T) ->
- call({del, T, self()}).
-
-%%% ----------------------------------------------------------
-%%% # match(Pat)
-%%%
-%%% Input: Pat = pattern in the sense of ets:match_object/2.
-%%%
-%%% Output: list of {Term, Pid}
-%%%
-%%% Description: Return the list of associations whose Term, as specified
-%%% to add/1 or add_new/1, matches the specified pattern.
-%%%
-%%% Note that there's no guarantee that the returned processes
-%%% are still alive. (Although one that isn't will soon have
-%%% its associations removed.)
-%%% ----------------------------------------------------------
-
--spec match(tuple())
- -> [{term(), pid()}].
-
-match(Pat) ->
- ets:match_object(?TABLE, ?MAPPING(Pat, '_')).
-
-%% ---------------------------------------------------------
-%% EXPORTED INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
-
-start_link() ->
- ServerName = {local, ?SERVER},
- Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
- gen_server:start_link(ServerName, ?MODULE, [], Options).
-
-state() ->
- call(state).
-
-uptime() ->
- call(uptime).
-
-%% pids/0
-%%
-%% Output: list of {Pid, [Term, ...]}
-
-pids() ->
- to_list(fun swap/1).
-
-to_list(Fun) ->
- ets:foldl(fun(T,A) -> acc(Fun, T, A) end, orddict:new(), ?TABLE).
-
-acc(Fun, ?MAPPING(Term, Pid), Dict) ->
- append(Fun({Term, Pid}), Dict);
-acc(_, _, Dict) ->
- Dict.
-
-append({K,V}, Dict) ->
- orddict:append(K, V, Dict).
-
-id(T) -> T.
-
-%% terms/0
-%%
-%% Output: list of {Term, [Pid, ...]}
-
-terms() ->
- to_list(fun id/1).
-
-swap({X,Y}) -> {Y,X}.
-
-%%% ----------------------------------------------------------
-%%% # init(Role)
-%%%
-%%% Output: {ok, State}
-%%% ----------------------------------------------------------
-
-init(_) ->
- ets:new(?TABLE, [bag, named_table]),
- {ok, #state{}}.
-
-%%% ----------------------------------------------------------
-%%% # handle_call(Request, From, State)
-%%% ----------------------------------------------------------
-
-handle_call({add, Fun, Key, Pid}, _, State) ->
- B = Fun(?TABLE, {Key, Pid}),
- monitor(B andalso no_monitor(Pid), Pid),
- {reply, B, State};
-
-handle_call({del, Key, Pid}, _, State) ->
- {reply, ets:delete_object(?TABLE, ?MAPPING(Key, Pid)), State};
-
-handle_call({repl, T, U, Pid}, _, State) ->
- MatchSpec = [{?MAPPING('$1', Pid),
- [{'=:=', '$1', {const, T}}],
- ['$_']}],
- {reply, repl(ets:select(?TABLE, MatchSpec), U, Pid), State};
-
-handle_call(state, _, State) ->
- {reply, State, State};
-
-handle_call(uptime, _, #state{id = Time} = State) ->
- {reply, diameter_lib:now_diff(Time), State};
-
-handle_call(Req, From, State) ->
- ?UNEXPECTED([Req, From]),
- {reply, nok, State}.
-
-%%% ----------------------------------------------------------
-%%% # handle_cast(Request, State)
-%%% ----------------------------------------------------------
-
-handle_cast(Msg, State)->
- ?UNEXPECTED([Msg]),
- {noreply, State}.
-
-%%% ----------------------------------------------------------
-%%% # handle_info(Request, State)
-%%% ----------------------------------------------------------
-
-handle_info({'DOWN', MRef, process, Pid, _}, State) ->
- ets:delete_object(?TABLE, ?MONITOR(Pid, MRef)),
- ets:match_delete(?TABLE, ?MAPPING('_', Pid)),
- {noreply, State};
-
-handle_info(Info, State) ->
- ?UNEXPECTED([Info]),
- {noreply, State}.
-
-%%% ----------------------------------------------------------
-%%% # terminate(Reason, State)
-%%% ----------------------------------------------------------
-
-terminate(_Reason, _State)->
- ok.
-
-%%% ----------------------------------------------------------
-%%% # code_change(OldVsn, State, Extra)
-%%% ----------------------------------------------------------
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%% ---------------------------------------------------------
-%% INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
-
-monitor(true, Pid) ->
- ets:insert(?TABLE, ?MONITOR(Pid, erlang:monitor(process, Pid)));
-monitor(false, _) ->
- ok.
-
-%% Do we need a monitor for the specified Pid?
-no_monitor(Pid) ->
- [] == ets:match_object(?TABLE, ?MONITOR(Pid, '_')).
-
-%% insert_new/2
-
-insert_new(?TABLE, {Key, _} = T) ->
- flush(ets:lookup(?TABLE, Key)),
- ets:insert_new(?TABLE, T).
-
-%% Remove any processes that are dead but for which we may not have
-%% received 'DOWN' yet. This is to ensure that add_new can be used
-%% to register a unique name each time a process restarts.
-flush(List) ->
- lists:foreach(fun({_,P} = T) ->
- del(erlang:is_process_alive(P), T)
- end,
- List).
-
-del(Alive, T) ->
- Alive orelse ets:delete_object(?TABLE, T).
-
-%% repl/3
-
-repl([?MAPPING(_, Pid) = M], Key, Pid) ->
- ets:delete_object(?TABLE, M),
- true = ets:insert(?TABLE, ?MAPPING(Key, Pid));
-repl([], _, _) ->
- false.
-
-%% call/1
-
-call(Request) ->
- gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/app/diameter_service.erl b/lib/diameter/src/app/diameter_service.erl
deleted file mode 100644
index 421e36ccf5..0000000000
--- a/lib/diameter/src/app/diameter_service.erl
+++ /dev/null
@@ -1,2903 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% Implements the process that represents a service.
-%%
-
--module(diameter_service).
--behaviour(gen_server).
-
--export([start/1,
- stop/1,
- start_transport/2,
- stop_transport/2,
- info/2,
- call/4]).
-
-%% towards diameter_watchdog
--export([receive_message/3]).
-
-%% service supervisor
--export([start_link/1]).
-
--export([subscribe/1,
- unsubscribe/1,
- subscriptions/1,
- subscriptions/0,
- services/0,
- services/1,
- whois/1,
- flush_stats/1]).
-
-%% test/debug
--export([call_module/3,
- state/1,
- uptime/1]).
-
-%%% gen_server callbacks
--export([init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3]).
-
-%% Other callbacks.
--export([send/1]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
--include("diameter_types.hrl").
-
--define(STATE_UP, up).
--define(STATE_DOWN, down).
-
--define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1
--define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
--define(RESTART_TC, 1000). %% if restart was this recent
-
-%% Used to be able to swap this with anything else dict-like but now
-%% rely on the fact that a service's #state{} record does not change
-%% in storing in it ?STATE table and not always going through the
-%% service process. In particular, rely on the fact that operations on
-%% a ?Dict don't change the handle to it.
--define(Dict, diameter_dict).
-
-%% Table containing outgoing requests for which a reply has yet to be
-%% received.
--define(REQUEST_TABLE, diameter_request).
-
-%% Maintains state in a table. In contrast to previously, a service's
-%% stat is not constant and is accessed outside of the service
-%% process.
--define(STATE_TABLE, ?MODULE).
-
-%% Workaround for dialyzer's lack of understanding of match specs.
--type match(T)
- :: T | '_' | '$1' | '$2' | '$3' | '$4'.
-
-%% State of service gen_server.
--record(state,
- {id = now(),
- service_name, %% as passed to start_service/2, key in ?STATE_TABLE
- service :: #diameter_service{},
- peerT = ets_new(peers) :: ets:tid(), %% #peer{} at start_fsm
- connT = ets_new(conns) :: ets:tid(), %% #conn{} at connection_up
- share_peers = false :: boolean(), %% broadcast peers to remote nodes?
- use_shared_peers = false :: boolean(), %% use broadcasted peers?
- shared_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
- local_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
- monitor = false :: false | pid()}). %% process to die with
-%% shared_peers reflects the peers broadcast from remote nodes. Note
-%% that the state term itself doesn't change, which is relevant for
-%% the stateless application callbacks since the state is retrieved
-%% from ?STATE_TABLE from outside the service process. The pid in the
-%% service record is used to determine whether or not we need to call
-%% the process for a pick_peer callback.
-
-%% Record representing a watchdog process.
--record(peer,
- {pid :: match(pid()),
- type :: match(connect | accept),
- ref :: match(reference()), %% key into diameter_config
- options :: match([transport_opt()]), %% as passed to start_transport
- op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP),
- started = now(), %% at process start
- conn = false :: match(boolean() | pid())}).
- %% true at accept, pid() at connection_up (connT key)
-
-%% Record representing a peer_fsm process.
--record(conn,
- {pid :: pid(),
- apps :: [{0..16#FFFFFFFF, app_alias()}], %% {Id, Alias}
- caps :: #diameter_caps{},
- started = now(), %% at process start
- peer :: pid()}). %% key into peerT
-
-%% Record stored in diameter_request for each outgoing request.
--record(request,
- {from, %% arg 2 of handle_call/3
- handler :: match(pid()), %% request process
- transport :: match(pid()), %% peer process
- caps :: match(#diameter_caps{}),
- app :: match(app_alias()), %% #diameter_app.alias
- dictionary :: match(module()), %% #diameter_app.dictionary
- module :: match(nonempty_improper_list(module(), list())),
- %% #diameter_app.module
- filter :: match(peer_filter()),
- packet :: match(#diameter_packet{})}).
-
-%% Record call/4 options are parsed into.
--record(options,
- {filter = none :: peer_filter(),
- extra = [] :: list(),
- timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
- detach = false :: boolean()}).
-
-%% Since RFC 3588 requires that a Diameter agent not modify End-to-End
-%% Identifiers, the possibility of explicitly setting an End-to-End
-%% Identifier would be needed to be able to implement an agent in
-%% which one side of the communication is not implemented on top of
-%% diameter. For example, Diameter being sent or received encapsulated
-%% in some other protocol, or even another Diameter stack in a
-%% non-Erlang environment. (Not that this is likely to be a normal
-%% case.)
-%%
-%% The implemented solution is not an option but to respect any header
-%% values set in a diameter_header record returned from a
-%% prepare_request callback. A call to diameter:call/4 can communicate
-%% values to the callback using the 'extra' option if so desired.
-
-%%% ---------------------------------------------------------------------------
-%%% # start(SvcName)
-%%% ---------------------------------------------------------------------------
-
-start(SvcName) ->
- diameter_service_sup:start_child(SvcName).
-
-start_link(SvcName) ->
- Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
- gen_server:start_link(?MODULE, [SvcName], Options).
-%% Put the arbitrary term SvcName in a list in case we ever want to
-%% send more than this and need to distinguish old from new.
-
-%%% ---------------------------------------------------------------------------
-%%% # stop(SvcName)
-%%% ---------------------------------------------------------------------------
-
-stop(SvcName) ->
- case whois(SvcName) of
- undefined ->
- {error, not_started};
- Pid ->
- stop(call_service(Pid, stop), Pid)
- end.
-
-stop(ok, Pid) ->
- MRef = erlang:monitor(process, Pid),
- receive {'DOWN', MRef, process, _, _} -> ok end;
-stop(No, _) ->
- No.
-
-%%% ---------------------------------------------------------------------------
-%%% # start_transport(SvcName, {Ref, Type, Opts})
-%%% ---------------------------------------------------------------------------
-
-start_transport(SvcName, {_,_,_} = T) ->
- call_service_by_name(SvcName, {start, T}).
-
-%%% ---------------------------------------------------------------------------
-%%% # stop_transport(SvcName, Refs)
-%%% ---------------------------------------------------------------------------
-
-stop_transport(_, []) ->
- ok;
-stop_transport(SvcName, [_|_] = Refs) ->
- call_service_by_name(SvcName, {stop, Refs}).
-
-%%% ---------------------------------------------------------------------------
-%%% # info(SvcName, Item)
-%%% ---------------------------------------------------------------------------
-
-info(SvcName, Item) ->
- info_rc(call_service_by_name(SvcName, {info, Item})).
-
-info_rc({error, _}) ->
- undefined;
-info_rc(Info) ->
- Info.
-
-%%% ---------------------------------------------------------------------------
-%%% # receive_message(TPid, Pkt, MessageData)
-%%% ---------------------------------------------------------------------------
-
-%% Handle an incoming message in the watchdog process. This used to
-%% come through the service process but this avoids that becoming a
-%% bottleneck.
-
-receive_message(TPid, Pkt, T)
- when is_pid(TPid) ->
- #diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
- recv(R, (not R) andalso lookup_request(Pkt, TPid), TPid, Pkt, T).
-
-%% Incoming request ...
-recv(true, false, TPid, Pkt, T) ->
- try
- spawn(fun() -> recv_request(TPid, Pkt, T) end)
- catch
- error: system_limit = E -> %% discard
- ?LOG({error, E}, now())
- end;
-
-%% ... answer to known request ...
-recv(false, #request{from = {_, Ref}, handler = Pid} = Req, _, Pkt, _) ->
- Pid ! {answer, Ref, Req, Pkt};
-%% Note that failover could have happened prior to this message being
-%% received and triggering failback. That is, both a failover message
-%% and answer may be on their way to the handler process. In the worst
-%% case the request process gets notification of the failover and
-%% sends to the alternate peer before an answer arrives, so it's
-%% always the case that we can receive more than one answer after
-%% failover. The first answer received by the request process wins,
-%% any others are discarded.
-
-%% ... or not.
-recv(false, false, _, _, _) ->
- ok.
-
-%%% ---------------------------------------------------------------------------
-%%% # call(SvcName, App, Msg, Options)
-%%% ---------------------------------------------------------------------------
-
-call(SvcName, App, Msg, Options)
- when is_list(Options) ->
- Rec = make_options(Options),
- Ref = make_ref(),
- Caller = {self(), Ref},
- Fun = fun() -> exit({Ref, call(SvcName, App, Msg, Rec, Caller)}) end,
- try spawn_monitor(Fun) of
- {_, MRef} ->
- recv(MRef, Ref, Rec#options.detach, false)
- catch
- error: system_limit = E ->
- {error, E}
- end.
-
-%% Don't rely on gen_server:call/3 for the timeout handling since it
-%% makes no guarantees about not leaving a reply message in the
-%% mailbox if we catch its exit at timeout. It currently *can* do so,
-%% which is also undocumented.
-
-recv(MRef, _, true, true) ->
- erlang:demonitor(MRef, [flush]),
- ok;
-
-recv(MRef, Ref, Detach, Sent) ->
- receive
- Ref -> %% send has been attempted
- recv(MRef, Ref, Detach, true);
- {'DOWN', MRef, process, _, Reason} ->
- call_rc(Reason, Ref, Sent)
- end.
-
-%% call/5 has returned ...
-call_rc({Ref, Ans}, Ref, _) ->
- Ans;
-
-%% ... or not. In this case failure/encode are documented.
-call_rc(_, _, Sent) ->
- {error, choose(Sent, failure, encode)}.
-
-%% call/5
-%%
-%% In the process spawned for the outgoing request.
-
-call(SvcName, App, Msg, Opts, Caller) ->
- c(ets:lookup(?STATE_TABLE, SvcName), App, Msg, Opts, Caller).
-
-c([#state{service_name = SvcName} = S], App, Msg, Opts, Caller) ->
- case find_transport(App, Msg, Opts, S) of
- {_,_,_} = T ->
- send_request(T, Msg, Opts, Caller, SvcName);
- false ->
- {error, no_connection};
- {error, _} = No ->
- No
- end;
-
-c([], _, _, _, _) ->
- {error, no_service}.
-
-%% make_options/1
-
-make_options(Options) ->
- lists:foldl(fun mo/2, #options{}, Options).
-
-mo({timeout, T}, Rec)
- when is_integer(T), 0 =< T ->
- Rec#options{timeout = T};
-
-mo({filter, F}, #options{filter = none} = Rec) ->
- Rec#options{filter = F};
-mo({filter, F}, #options{filter = {all, Fs}} = Rec) ->
- Rec#options{filter = {all, [F | Fs]}};
-mo({filter, F}, #options{filter = F0} = Rec) ->
- Rec#options{filter = {all, [F0, F]}};
-
-mo({extra, L}, #options{extra = X} = Rec)
- when is_list(L) ->
- Rec#options{extra = X ++ L};
-
-mo(detach, Rec) ->
- Rec#options{detach = true};
-
-mo(T, _) ->
- ?ERROR({invalid_option, T}).
-
-%%% ---------------------------------------------------------------------------
-%%% # subscribe(SvcName)
-%%% # unsubscribe(SvcName)
-%%% ---------------------------------------------------------------------------
-
-subscribe(SvcName) ->
- diameter_reg:add({?MODULE, subscriber, SvcName}).
-
-unsubscribe(SvcName) ->
- diameter_reg:del({?MODULE, subscriber, SvcName}).
-
-subscriptions(Pat) ->
- pmap(diameter_reg:match({?MODULE, subscriber, Pat})).
-
-subscriptions() ->
- subscriptions('_').
-
-pmap(Props) ->
- lists:map(fun({{?MODULE, _, Name}, Pid}) -> {Name, Pid} end, Props).
-
-%%% ---------------------------------------------------------------------------
-%%% # services(Pattern)
-%%% ---------------------------------------------------------------------------
-
-services(Pat) ->
- pmap(diameter_reg:match({?MODULE, service, Pat})).
-
-services() ->
- services('_').
-
-whois(SvcName) ->
- case diameter_reg:match({?MODULE, service, SvcName}) of
- [{_, Pid}] ->
- Pid;
- [] ->
- undefined
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # flush_stats/1
-%%%
-%%% Output: list of {{SvcName, Alias, Counter}, Value}
-%%% ---------------------------------------------------------------------------
-
-flush_stats(TPid) ->
- diameter_stats:flush(TPid).
-
-%% ===========================================================================
-%% ===========================================================================
-
-state(Svc) ->
- call_service(Svc, state).
-
-uptime(Svc) ->
- call_service(Svc, uptime).
-
-%% call_module/3
-
-call_module(Service, AppMod, Request) ->
- call_service(Service, {call_module, AppMod, Request}).
-
-%%% ---------------------------------------------------------------------------
-%%% # init([SvcName])
-%%% ---------------------------------------------------------------------------
-
-init([SvcName]) ->
- process_flag(trap_exit, true), %% ensure terminate(shutdown, _)
- i(SvcName, diameter_reg:add_new({?MODULE, service, SvcName})).
-
-i(SvcName, true) ->
- {ok, i(SvcName)};
-i(_, false) ->
- {stop, {shutdown, already_started}}.
-
-%%% ---------------------------------------------------------------------------
-%%% # handle_call(Req, From, State)
-%%% ---------------------------------------------------------------------------
-
-handle_call(state, _, S) ->
- {reply, S, S};
-
-handle_call(uptime, _, #state{id = T} = S) ->
- {reply, diameter_lib:now_diff(T), S};
-
-%% Start a transport.
-handle_call({start, {Ref, Type, Opts}}, _From, S) ->
- {reply, start(Ref, {Type, Opts}, S), S};
-
-%% Stop transports.
-handle_call({stop, Refs}, _From, S) ->
- shutdown(Refs, S),
- {reply, ok, S};
-
-%% pick_peer with mutable state
-handle_call({pick_peer, Local, Remote, App}, _From, S) ->
- #diameter_app{mutable = true} = App, %% assert
- {reply, pick_peer(Local, Remote, self(), S#state.service_name, App), S};
-
-handle_call({call_module, AppMod, Req}, From, S) ->
- call_module(AppMod, Req, From, S);
-
-handle_call({info, Item}, _From, S) ->
- {reply, service_info(Item, S), S};
-
-handle_call(stop, _From, S) ->
- shutdown(S),
- {stop, normal, ok, S};
-%% The server currently isn't guaranteed to be dead when the caller
-%% gets the reply. We deal with this in the call to the server,
-%% stating a monitor that waits for DOWN before returning.
-
-handle_call(Req, From, S) ->
- unexpected(handle_call, [Req, From], S),
- {reply, nok, S}.
-
-%%% ---------------------------------------------------------------------------
-%%% # handle_cast(Req, State)
-%%% ---------------------------------------------------------------------------
-
-handle_cast(Req, S) ->
- unexpected(handle_cast, [Req], S),
- {noreply, S}.
-
-%%% ---------------------------------------------------------------------------
-%%% # handle_info(Req, State)
-%%% ---------------------------------------------------------------------------
-
-handle_info(T,S) ->
- case transition(T,S) of
- ok ->
- {noreply, S};
- #state{} = NS ->
- {noreply, NS};
- {stop, Reason} ->
- {stop, {shutdown, Reason}, S}
- end.
-
-%% transition/2
-
-%% Peer process is telling us to start a new accept process.
-transition({accepted, Pid, TPid}, S) ->
- accepted(Pid, TPid, S),
- ok;
-
-%% Peer process has a new open connection.
-transition({connection_up, Pid, T}, S) ->
- connection_up(Pid, T, S);
-
-%% Peer process has left state open.
-transition({connection_down, Pid}, S) ->
- connection_down(Pid, S);
-
-%% Peer process has returned to state open.
-transition({connection_up, Pid}, S) ->
- connection_up(Pid, S);
-
-%% Accepting transport has lost connectivity.
-transition({close, Pid}, S) ->
- close(Pid, S),
- ok;
-
-%% Connecting transport is being restarted by watchdog.
-transition({reconnect, Pid}, S) ->
- reconnect(Pid, S),
- ok;
-
-%% Monitor process has died. Just die with a reason that tells
-%% diameter_config about the happening. If a cleaner shutdown is
-%% required then someone should stop us.
-transition({'DOWN', MRef, process, _, Reason}, #state{monitor = MRef}) ->
- {stop, {monitor, Reason}};
-
-%% Local peer process has died.
-transition({'DOWN', _, process, Pid, Reason}, S)
- when node(Pid) == node() ->
- peer_down(Pid, Reason, S);
-
-%% Remote service wants to know about shared transports.
-transition({service, Pid}, S) ->
- share_peers(Pid, S),
- ok;
-
-%% Remote service is communicating a shared peer.
-transition({peer, TPid, Aliases, Caps}, S) ->
- remote_peer_up(TPid, Aliases, Caps, S);
-
-%% Remote peer process has died.
-transition({'DOWN', _, process, TPid, _}, S) ->
- remote_peer_down(TPid, S);
-
-%% Restart after tc expiry.
-transition({tc_timeout, T}, S) ->
- tc_timeout(T, S),
- ok;
-
-%% Request process is telling us it may have missed a failover message
-%% after a transport went down and the service process looked up
-%% outstanding requests.
-transition({failover, TRef, Seqs}, S) ->
- failover(TRef, Seqs, S),
- ok;
-
-transition(Req, S) ->
- unexpected(handle_info, [Req], S),
- ok.
-
-%%% ---------------------------------------------------------------------------
-%%% # terminate(Reason, State)
-%%% ---------------------------------------------------------------------------
-
-terminate(Reason, #state{service_name = Name} = S) ->
- ets:delete(?STATE_TABLE, Name),
- shutdown == Reason %% application shutdown
- andalso shutdown(S).
-
-%%% ---------------------------------------------------------------------------
-%%% # code_change(FromVsn, State, Extra)
-%%% ---------------------------------------------------------------------------
-
-code_change(FromVsn,
- #state{service_name = SvcName,
- service = #diameter_service{applications = Apps}}
- = S,
- Extra) ->
- lists:foreach(fun(A) ->
- code_change(FromVsn, SvcName, Extra, A)
- end,
- Apps),
- {ok, S}.
-
-code_change(FromVsn, SvcName, Extra, #diameter_app{alias = Alias} = A) ->
- {ok, S} = cb(A, code_change, [FromVsn,
- mod_state(Alias),
- Extra,
- SvcName]),
- mod_state(Alias, S).
-
-%% ===========================================================================
-%% ===========================================================================
-
-unexpected(F, A, #state{service_name = Name}) ->
- ?UNEXPECTED(F, A ++ [Name]).
-
-cb([_|_] = M, F, A) ->
- eval(M, F, A);
-cb(Rec, F, A) ->
- {_, M} = app(Rec),
- eval(M, F, A).
-
-app(#request{app = A, module = M}) ->
- {A,M};
-app(#diameter_app{alias = A, module = M}) ->
- {A,M}.
-
-eval([M|X], F, A) ->
- apply(M, F, A ++ X).
-
-%% Callback with state.
-
-state_cb(#diameter_app{mutable = false, init_state = S}, {ModX, F, A}) ->
- eval(ModX, F, A ++ [S]);
-
-state_cb(#diameter_app{mutable = true, alias = Alias}, {_,_,_} = MFA) ->
- state_cb(MFA, Alias);
-
-state_cb({ModX,F,A}, Alias)
- when is_list(ModX) ->
- eval(ModX, F, A ++ [mod_state(Alias)]).
-
-choose(true, X, _) -> X;
-choose(false, _, X) -> X.
-
-ets_new(Tbl) ->
- ets:new(Tbl, [{keypos, 2}]).
-
-insert(Tbl, Rec) ->
- ets:insert(Tbl, Rec),
- Rec.
-
-monitor(Pid) ->
- erlang:monitor(process, Pid),
- Pid.
-
-%% Using the process dictionary for the callback state was initially
-%% just a way to make what was horrendous trace (big state record and
-%% much else everywhere) somewhat more readable. There's not as much
-%% need for it now but it's no worse (except possibly that we don't
-%% see the table identifier being passed around) than an ets table so
-%% keep it.
-
-mod_state(Alias) ->
- get({?MODULE, mod_state, Alias}).
-
-mod_state(Alias, ModS) ->
- put({?MODULE, mod_state, Alias}, ModS).
-
-%% have_transport/2
-
-have_transport(SvcName, Ref) ->
- [] /= diameter_config:have_transport(SvcName, Ref).
-
-%%% ---------------------------------------------------------------------------
-%%% # shutdown/2
-%%% ---------------------------------------------------------------------------
-
-shutdown(Refs, #state{peerT = PeerT}) ->
- ets:foldl(fun(P,ok) -> s(P, Refs), ok end, ok, PeerT).
-
-s(#peer{ref = Ref, pid = Pid}, Refs) ->
- s(lists:member(Ref, Refs), Pid);
-
-s(true, Pid) ->
- Pid ! {shutdown, self()}; %% 'DOWN' will cleanup as usual
-s(false, _) ->
- ok.
-
-%%% ---------------------------------------------------------------------------
-%%% # shutdown/1
-%%% ---------------------------------------------------------------------------
-
-shutdown(#state{peerT = PeerT}) ->
- %% A transport might not be alive to receive the shutdown request
- %% but give those that are a chance to shutdown gracefully.
- wait(fun st/2, PeerT),
- %% Kill the watchdogs explicitly in case there was no transport.
- wait(fun sw/2, PeerT).
-
-wait(Fun, T) ->
- diameter_lib:wait(ets:foldl(Fun, [], T)).
-
-st(#peer{conn = B}, Acc)
- when is_boolean(B) ->
- Acc;
-st(#peer{conn = Pid}, Acc) ->
- Pid ! shutdown,
- [Pid | Acc].
-
-sw(#peer{pid = Pid}, Acc) ->
- exit(Pid, shutdown),
- [Pid | Acc].
-
-%%% ---------------------------------------------------------------------------
-%%% # call_service/2
-%%% ---------------------------------------------------------------------------
-
-call_service(Pid, Req)
- when is_pid(Pid) ->
- cs(Pid, Req);
-call_service(SvcName, Req) ->
- call_service_by_name(SvcName, Req).
-
-call_service_by_name(SvcName, Req) ->
- cs(whois(SvcName), Req).
-
-cs(Pid, Req)
- when is_pid(Pid) ->
- try
- gen_server:call(Pid, Req, infinity)
- catch
- E: Reason when E == exit ->
- {error, {E, Reason}}
- end;
-
-cs(undefined, _) ->
- {error, no_service}.
-
-%%% ---------------------------------------------------------------------------
-%%% # i/1
-%%%
-%%% Output: #state{}
-%%% ---------------------------------------------------------------------------
-
-%% Intialize the state of a service gen_server.
-
-i(SvcName) ->
- %% Split the config into a server state and a list of transports.
- {#state{} = S, CL} = lists:foldl(fun cfg_acc/2,
- {false, []},
- diameter_config:lookup(SvcName)),
-
- %% Publish the state in order to be able to access it outside of
- %% the service process. Originally table identifiers were only
- %% known to the service process but we now want to provide the
- %% option of application callbacks being 'stateless' in order to
- %% avoid having to go through a common process. (Eg. An agent that
- %% sends a request for every incoming request.)
- true = ets:insert_new(?STATE_TABLE, S),
-
- %% Start fsms for each transport.
- lists:foreach(fun(T) -> start_fsm(T,S) end, CL),
-
- init_shared(S),
- S.
-
-cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
- {false, Acc}) ->
- lists:foreach(fun init_mod/1, Apps),
- S = #state{service_name = SvcName,
- service = Rec#diameter_service{pid = self()},
- share_peers = get_value(share_peers, Opts),
- use_shared_peers = get_value(use_shared_peers, Opts),
- monitor = mref(get_value(monitor, Opts))},
- {S, Acc};
-
-cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
- when Type == connect;
- Type == listen ->
- {S, [T | Acc]}.
-
-mref(false = No) ->
- No;
-mref(P) ->
- erlang:monitor(process, P).
-
-init_shared(#state{use_shared_peers = true,
- service_name = Svc}) ->
- diameter_peer:notify(Svc, {service, self()});
-init_shared(#state{use_shared_peers = false}) ->
- ok.
-
-init_mod(#diameter_app{alias = Alias,
- init_state = S}) ->
- mod_state(Alias, S).
-
-start_fsm({Ref, Type, Opts}, S) ->
- start(Ref, {Type, Opts}, S).
-
-get_value(Key, Vs) ->
- {_, V} = lists:keyfind(Key, 1, Vs),
- V.
-
-%%% ---------------------------------------------------------------------------
-%%% # start/3
-%%% ---------------------------------------------------------------------------
-
-%% If the initial start/3 at service/transport start succeeds then
-%% subsequent calls to start/4 on the same service will also succeed
-%% since they involve the same call to merge_service/2. We merge here
-%% rather than earlier since the service may not yet be configured
-%% when the transport is configured.
-
-start(Ref, {T, Opts}, S)
- when T == connect;
- T == listen ->
- try
- {ok, start(Ref, type(T), Opts, S)}
- catch
- ?FAILURE(Reason) ->
- {error, Reason}
- end.
-%% TODO: don't actually raise any errors yet
-
-%% There used to be a difference here between the handling of
-%% configured listening and connecting transports but now we simply
-%% tell the transport_module to start an accepting or connecting
-%% process respectively, the transport implementation initiating
-%% listening on a port as required.
-type(listen) -> accept;
-type(accept) -> listen;
-type(connect = T) -> T.
-
-%% start/4
-
-start(Ref, Type, Opts, #state{peerT = PeerT,
- connT = ConnT,
- service_name = SvcName,
- service = Svc})
- when Type == connect;
- Type == accept ->
- Pid = monitor(s(Type, Ref, {ConnT,
- Opts,
- SvcName,
- merge_service(Opts, Svc)})),
- insert(PeerT, #peer{pid = Pid,
- type = Type,
- ref = Ref,
- options = Opts}),
- Pid.
-
-%% Note that the service record passed into the watchdog is the merged
-%% record so that each watchdog (and peer_fsm) may get a different
-%% record. This record is what is passed back into application
-%% callbacks.
-
-s(Type, Ref, T) ->
- diameter_watchdog:start({Type, Ref}, T).
-
-%% merge_service/2
-
-merge_service(Opts, Svc) ->
- lists:foldl(fun ms/2, Svc, Opts).
-
-%% Limit the applications known to the fsm to those in the 'apps'
-%% option. That this might be empty is checked by the fsm. It's not
-%% checked at config-time since there's no requirement that the
-%% service be configured first. (Which could be considered a bit odd.)
-ms({applications, As}, #diameter_service{applications = Apps} = S)
- when is_list(As) ->
- S#diameter_service{applications
- = [A || A <- Apps,
- lists:member(A#diameter_app.alias, As)]};
-
-%% The fact that all capabilities can be configured on the transports
-%% means that the service doesn't necessarily represent a single
-%% locally implemented Diameter peer as identified by Origin-Host: a
-%% transport can configure its own Origin-Host. This means that the
-%% service little more than a placeholder for default capabilities
-%% plus a list of applications that individual transports can choose
-%% to support (or not).
-ms({capabilities, Opts}, #diameter_service{capabilities = Caps0} = Svc)
- when is_list(Opts) ->
- %% make_caps has already succeeded in diameter_config so it will succeed
- %% again here.
- {ok, Caps} = diameter_capx:make_caps(Caps0, Opts),
- Svc#diameter_service{capabilities = Caps};
-
-ms(_, Svc) ->
- Svc.
-
-%%% ---------------------------------------------------------------------------
-%%% # accepted/3
-%%% ---------------------------------------------------------------------------
-
-accepted(Pid, _TPid, #state{peerT = PeerT} = S) ->
- #peer{ref = Ref, type = accept = T, conn = false, options = Opts}
- = P
- = fetch(PeerT, Pid),
- insert(PeerT, P#peer{conn = true}), %% mark replacement transport started
- start(Ref, T, Opts, S). %% start new peer
-
-fetch(Tid, Key) ->
- [T] = ets:lookup(Tid, Key),
- T.
-
-%%% ---------------------------------------------------------------------------
-%%% # connection_up/3
-%%%
-%%% Output: #state{}
-%%% ---------------------------------------------------------------------------
-
-%% Peer process has reached the open state.
-
-connection_up(Pid, {TPid, {Caps, SApps, Pkt}}, #state{peerT = PeerT,
- connT = ConnT}
- = S) ->
- P = fetch(PeerT, Pid),
- C = #conn{pid = TPid,
- apps = SApps,
- caps = Caps,
- peer = Pid},
-
- insert(ConnT, C),
- connection_up([Pkt], P#peer{conn = TPid}, C, S).
-
-%%% ---------------------------------------------------------------------------
-%%% # connection_up/2
-%%%
-%%% Output: #state{}
-%%% ---------------------------------------------------------------------------
-
-%% Peer process has transitioned back into the open state. Note that there
-%% has been no new capabilties exchange in this case.
-
-connection_up(Pid, #state{peerT = PeerT,
- connT = ConnT}
- = S) ->
- #peer{conn = TPid} = P = fetch(PeerT, Pid),
- C = fetch(ConnT, TPid),
- connection_up([], P, C, S).
-
-%% connection_up/4
-
-connection_up(T, P, C, #state{peerT = PeerT,
- local_peers = LDict,
- service_name = SvcName,
- service
- = #diameter_service{applications = Apps}}
- = S) ->
- #peer{conn = TPid, op_state = ?STATE_DOWN}
- = P,
- #conn{apps = SApps, caps = Caps}
- = C,
-
- insert(PeerT, P#peer{op_state = ?STATE_UP}),
-
- request_peer_up(TPid),
- report_status(up, P, C, S, T),
- S#state{local_peers = insert_local_peer(SApps,
- {{TPid, Caps}, {SvcName, Apps}},
- LDict)}.
-
-insert_local_peer(SApps, T, LDict) ->
- lists:foldl(fun(A,D) -> ilp(A, T, D) end, LDict, SApps).
-
-ilp({Id, Alias}, {TC, SA}, LDict) ->
- init_conn(Id, Alias, TC, SA),
- ?Dict:append(Alias, TC, LDict).
-
-init_conn(Id, Alias, TC, {SvcName, Apps}) ->
- #diameter_app{module = ModX,
- id = Id} %% assert
- = find_app(Alias, Apps),
-
- peer_cb({ModX, peer_up, [SvcName, TC]}, Alias).
-
-find_app(Alias, Apps) ->
- lists:keyfind(Alias, #diameter_app.alias, Apps).
-
-%% A failing peer callback brings down the service. In the case of
-%% peer_up we could just kill the transport and emit an error but for
-%% peer_down we have no way to cleanup any state change that peer_up
-%% may have introduced.
-peer_cb(MFA, Alias) ->
- try state_cb(MFA, Alias) of
- ModS ->
- mod_state(Alias, ModS)
- catch
- E: Reason ->
- ?ERROR({E, Reason, MFA, ?STACK})
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # connection_down/2
-%%%
-%%% Output: #state{}
-%%% ---------------------------------------------------------------------------
-
-%% Peer process has transitioned out of the open state.
-
-connection_down(Pid, #state{peerT = PeerT,
- connT = ConnT}
- = S) ->
- #peer{conn = TPid}
- = P
- = fetch(PeerT, Pid),
-
- C = fetch(ConnT, TPid),
- insert(PeerT, P#peer{op_state = ?STATE_DOWN}),
- connection_down(P,C,S).
-
-%% connection_down/3
-
-connection_down(#peer{conn = TPid,
- op_state = ?STATE_UP}
- = P,
- #conn{caps = Caps,
- apps = SApps}
- = C,
- #state{service_name = SvcName,
- service = #diameter_service{applications = Apps},
- local_peers = LDict}
- = S) ->
- report_status(down, P, C, S, []),
- NewS = S#state{local_peers
- = remove_local_peer(SApps,
- {{TPid, Caps}, {SvcName, Apps}},
- LDict)},
- request_peer_down(TPid, NewS),
- NewS.
-
-remove_local_peer(SApps, T, LDict) ->
- lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
-
-rlp({Id, Alias}, {TC, SA}, LDict) ->
- L = ?Dict:fetch(Alias, LDict),
- down_conn(Id, Alias, TC, SA),
- ?Dict:store(Alias, lists:delete(TC, L), LDict).
-
-down_conn(Id, Alias, TC, {SvcName, Apps}) ->
- #diameter_app{module = ModX,
- id = Id} %% assert
- = find_app(Alias, Apps),
-
- peer_cb({ModX, peer_down, [SvcName, TC]}, Alias).
-
-%%% ---------------------------------------------------------------------------
-%%% # peer_down/3
-%%%
-%%% Output: #state{}
-%%% ---------------------------------------------------------------------------
-
-%% Peer process has died.
-
-peer_down(Pid, _Reason, #state{peerT = PeerT} = S) ->
- P = fetch(PeerT, Pid),
- ets:delete_object(PeerT, P),
- restart(P,S),
- peer_down(P,S).
-
-%% peer_down/2
-
-%% The peer has never come up ...
-peer_down(#peer{conn = B}, S)
- when is_boolean(B) ->
- S;
-
-%% ... or it has.
-peer_down(#peer{ref = Ref,
- conn = TPid,
- type = Type,
- options = Opts}
- = P,
- #state{service_name = SvcName,
- connT = ConnT}
- = S) ->
- #conn{caps = Caps}
- = C
- = fetch(ConnT, TPid),
- ets:delete_object(ConnT, C),
- try
- pd(P,C,S)
- after
- send_event(SvcName, {closed, Ref, {TPid, Caps}, {type(Type), Opts}})
- end.
-
-pd(#peer{op_state = ?STATE_DOWN}, _, S) ->
- S;
-pd(#peer{op_state = ?STATE_UP} = P, C, S) ->
- connection_down(P,C,S).
-
-%% restart/2
-
-restart(P,S) ->
- q_restart(restart(P), S).
-
-%% restart/1
-
-%% Always try to reconnect.
-restart(#peer{ref = Ref,
- type = connect = T,
- options = Opts,
- started = Time}) ->
- {Time, {Ref, T, Opts}};
-
-%% Transport connection hasn't yet been accepted ...
-restart(#peer{ref = Ref,
- type = accept = T,
- options = Opts,
- conn = false,
- started = Time}) ->
- {Time, {Ref, T, Opts}};
-
-%% ... or it has: a replacement transport has already been spawned.
-restart(#peer{type = accept}) ->
- false.
-
-%% q_restart/2
-
-%% Start the reconnect timer.
-q_restart({Time, {_Ref, Type, Opts} = T}, S) ->
- start_tc(tc(Time, default_tc(Type, Opts)), T, S);
-q_restart(false, _) ->
- ok.
-
-%% RFC 3588, 2.1:
-%%
-%% When no transport connection exists with a peer, an attempt to
-%% connect SHOULD be periodically made. This behavior is handled via
-%% the Tc timer, whose recommended value is 30 seconds. There are
-%% certain exceptions to this rule, such as when a peer has terminated
-%% the transport connection stating that it does not wish to
-%% communicate.
-
-default_tc(connect, Opts) ->
- proplists:get_value(reconnect_timer, Opts, ?DEFAULT_TC);
-default_tc(accept, _) ->
- 0.
-
-%% Bound tc below if the peer was restarted recently to avoid
-%% continuous in case of faulty config or other problems.
-tc(Time, Tc) ->
- choose(Tc > ?RESTART_TC
- orelse timer:now_diff(now(), Time) > 1000*?RESTART_TC,
- Tc,
- ?RESTART_TC).
-
-start_tc(0, T, S) ->
- tc_timeout(T, S);
-start_tc(Tc, T, _) ->
- erlang:send_after(Tc, self(), {tc_timeout, T}).
-
-%% tc_timeout/2
-
-tc_timeout({Ref, _Type, _Opts} = T, #state{service_name = SvcName} = S) ->
- tc(have_transport(SvcName, Ref), T, S).
-
-tc(true, {Ref, Type, Opts}, #state{service_name = SvcName}
- = S) ->
- send_event(SvcName, {reconnect, Ref, Opts}),
- start(Ref, Type, Opts, S);
-tc(false = No, _, _) -> %% removed
- No.
-
-%%% ---------------------------------------------------------------------------
-%%% # close/2
-%%% ---------------------------------------------------------------------------
-
-%% The watchdog doesn't start a new fsm in the accept case, it
-%% simply stays alive until someone tells it to die in order for
-%% another watchdog to be able to detect that it should transition
-%% from initial into reopen rather than okay. That someone is either
-%% the accepting watchdog upon reception of a CER from the previously
-%% connected peer, or us after reconnect_timer timeout.
-
-close(Pid, #state{service_name = SvcName,
- peerT = PeerT}) ->
- #peer{pid = Pid,
- type = accept,
- ref = Ref,
- options = Opts}
- = fetch(PeerT, Pid),
-
- c(Pid, have_transport(SvcName, Ref), Opts).
-
-%% Tell watchdog to (maybe) die later ...
-c(Pid, true, Opts) ->
- Tc = proplists:get_value(reconnect_timer, Opts, 2*?DEFAULT_TC),
- erlang:send_after(Tc, Pid, close);
-
-%% ... or now.
-c(Pid, false, _Opts) ->
- Pid ! close.
-
-%% The RFC's only document the behaviour of Tc, our reconnect_timer,
-%% for the establishment of connections but we also give
-%% reconnect_timer semantics for a listener, being the time within
-%% which a new connection attempt is expected of a connecting peer.
-%% The value should be greater than the peer's Tc + jitter.
-
-%%% ---------------------------------------------------------------------------
-%%% # reconnect/2
-%%% ---------------------------------------------------------------------------
-
-reconnect(Pid, #state{service_name = SvcName,
- peerT = PeerT}) ->
- #peer{ref = Ref,
- type = connect,
- options = Opts}
- = fetch(PeerT, Pid),
- send_event(SvcName, {reconnect, Ref, Opts}).
-
-%%% ---------------------------------------------------------------------------
-%%% # call_module/4
-%%% ---------------------------------------------------------------------------
-
-%% Backwards compatibility and never documented/advertised. May be
-%% removed.
-
-call_module(Mod, Req, From, #state{service
- = #diameter_service{applications = Apps},
- service_name = Svc}
- = S) ->
- case cm([A || A <- Apps, Mod == hd(A#diameter_app.module)],
- Req,
- From,
- Svc)
- of
- {reply = T, RC} ->
- {T, RC, S};
- noreply = T ->
- {T, S};
- Reason ->
- {reply, {error, Reason}, S}
- end.
-
-cm([#diameter_app{module = ModX, alias = Alias}], Req, From, Svc) ->
- MFA = {ModX, handle_call, [Req, From, Svc]},
-
- try state_cb(MFA, Alias) of
- {noreply = T, ModS} ->
- mod_state(Alias, ModS),
- T;
- {reply = T, RC, ModS} ->
- mod_state(Alias, ModS),
- {T, RC};
- T ->
- diameter_lib:error_report({invalid, T}, MFA),
- invalid
- catch
- E: Reason ->
- diameter_lib:error_report({failure, {E, Reason, ?STACK}}, MFA),
- failure
- end;
-
-cm([], _, _, _) ->
- unknown;
-
-cm([_,_|_], _, _, _) ->
- multiple.
-
-%%% ---------------------------------------------------------------------------
-%%% # send_request/5
-%%% ---------------------------------------------------------------------------
-
-%% Send an outgoing request in its dedicated process.
-%%
-%% Note that both encode of the outgoing request and of the received
-%% answer happens in this process. It's also this process that replies
-%% to the caller. The service process only handles the state-retaining
-%% callbacks.
-%%
-%% The mod field of the #diameter_app{} here includes any extra
-%% arguments passed to diameter:call/2.
-
-send_request({TPid, Caps, App}, Msg, Opts, Caller, SvcName) ->
- #diameter_app{module = ModX}
- = App,
-
- Pkt = make_packet(Msg),
-
- case cb(ModX, prepare_request, [Pkt, SvcName, {TPid, Caps}]) of
- {send, P} ->
- send_request(make_packet(P, Pkt),
- TPid,
- Caps,
- App,
- Opts,
- Caller,
- SvcName);
- {discard, Reason} ->
- {error, Reason};
- discard ->
- {error, discarded};
- T ->
- ?ERROR({invalid_return, prepare_request, App, T})
- end.
-
-%% make_packet/1
-%%
-%% Turn an outgoing request as passed to call/4 into a diameter_packet
-%% record in preparation for a prepare_request callback.
-
-make_packet(Bin)
- when is_binary(Bin) ->
- #diameter_packet{header = diameter_codec:decode_header(Bin),
- bin = Bin};
-
-make_packet(#diameter_packet{msg = [#diameter_header{} = Hdr | Avps]} = Pkt) ->
- Pkt#diameter_packet{msg = [make_header(Hdr) | Avps]};
-
-make_packet(#diameter_packet{header = Hdr} = Pkt) ->
- Pkt#diameter_packet{header = make_header(Hdr)};
-
-make_packet(Msg) ->
- make_packet(#diameter_packet{msg = Msg}).
-
-%% make_header/1
-
-make_header(undefined) ->
- Seq = diameter_session:sequence(),
- make_header(#diameter_header{end_to_end_id = Seq,
- hop_by_hop_id = Seq});
-
-make_header(#diameter_header{version = undefined} = Hdr) ->
- make_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
-
-make_header(#diameter_header{end_to_end_id = undefined} = H) ->
- Seq = diameter_session:sequence(),
- make_header(H#diameter_header{end_to_end_id = Seq});
-
-make_header(#diameter_header{hop_by_hop_id = undefined} = H) ->
- Seq = diameter_session:sequence(),
- make_header(H#diameter_header{hop_by_hop_id = Seq});
-
-make_header(#diameter_header{} = Hdr) ->
- Hdr;
-
-make_header(T) ->
- ?ERROR({invalid_header, T}).
-
-%% make_packet/2
-%%
-%% Reconstruct a diameter_packet from the return value of
-%% prepare_request or prepare_retransmit callback.
-
-make_packet(Bin, _)
- when is_binary(Bin) ->
- make_packet(Bin);
-
-make_packet(#diameter_packet{msg = [#diameter_header{} | _]} = Pkt, _) ->
- Pkt;
-
-%% Returning a diameter_packet with no header from a prepare_request
-%% or prepare_retransmit callback retains the header passed into it.
-%% This is primarily so that the end to end and hop by hop identifiers
-%% are retained.
-make_packet(#diameter_packet{header = Hdr} = Pkt,
- #diameter_packet{header = Hdr0}) ->
- Pkt#diameter_packet{header = fold_record(Hdr0, Hdr)};
-
-make_packet(Msg, Pkt) ->
- Pkt#diameter_packet{msg = Msg}.
-
-%% fold_record/2
-
-fold_record(undefined, R) ->
- R;
-fold_record(Rec, R) ->
- diameter_lib:fold_tuple(2, Rec, R).
-
-%% send_request/7
-
-send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) ->
- #diameter_app{alias = Alias,
- dictionary = Dict,
- module = ModX,
- answer_errors = AE}
- = App,
-
- EPkt = encode(Dict, Pkt),
-
- #options{filter = Filter,
- timeout = Timeout}
- = Opts,
-
- Req = #request{packet = Pkt,
- from = Caller,
- handler = self(),
- transport = TPid,
- caps = Caps,
- app = Alias,
- filter = Filter,
- dictionary = Dict,
- module = ModX},
-
- try
- TRef = send_request(TPid, EPkt, Req, Timeout),
- ack(Caller),
- handle_answer(SvcName, AE, recv_answer(Timeout, SvcName, {TRef, Req}))
- after
- erase_request(EPkt)
- end.
-
-%% Tell caller a send has been attempted.
-ack({Pid, Ref}) ->
- Pid ! Ref.
-
-%% recv_answer/3
-
-recv_answer(Timeout,
- SvcName,
- {TRef, #request{from = {_, Ref}, packet = RPkt} = Req}
- = T) ->
-
- %% Matching on TRef below ensures we ignore messages that pertain
- %% to a previous transport prior to failover. The answer message
- %% includes the #request{} since it's not necessarily Req; that
- %% is, from the last peer to which we've transmitted.
-
- receive
- {answer = A, Ref, Rq, Pkt} -> %% Answer from peer
- {A, Rq, Pkt};
- {timeout = Reason, TRef, _} -> %% No timely reply
- {error, Req, Reason};
- {failover = Reason, TRef, false} -> %% No alternate peer
- {error, Req, Reason};
- {failover, TRef, Transport} -> %% Resend to alternate peer
- try_retransmit(Timeout, SvcName, Req, Transport);
- {failover, TRef} -> %% May have missed failover notification
- Seqs = diameter_codec:sequence_numbers(RPkt),
- Pid = whois(SvcName),
- is_pid(Pid) andalso (Pid ! {failover, TRef, Seqs}),
- recv_answer(Timeout, SvcName, T)
- end.
-%% Note that failover starts a new timer and that expiry of an old
-%% timer value is ignored. This means that an answer could be accepted
-%% from a peer after timeout in the case of failover.
-
-try_retransmit(Timeout, SvcName, Req, Transport) ->
- try retransmit(Transport, Req, SvcName, Timeout) of
- T -> recv_answer(Timeout, SvcName, T)
- catch
- ?FAILURE(Reason) -> {error, Req, Reason}
- end.
-
-%% handle_error/3
-
-handle_error(Req, Reason, SvcName) ->
- #request{module = ModX,
- packet = Pkt,
- transport = TPid,
- caps = Caps}
- = Req,
- cb(ModX, handle_error, [Reason, msg(Pkt), SvcName, {TPid, Caps}]).
-
-msg(#diameter_packet{msg = undefined, bin = Bin}) ->
- Bin;
-msg(#diameter_packet{msg = Msg}) ->
- Msg.
-
-%% encode/2
-
-%% Note that prepare_request can return a diameter_packet containing
-%% header or transport_data. Even allow the returned record to contain
-%% an encoded binary. This isn't the usual case but could some in
-%% handy, for test at least. (For example, to send garbage.)
-
-%% The normal case: encode the returned message.
-encode(Dict, #diameter_packet{msg = Msg, bin = undefined} = Pkt) ->
- D = pick_dictionary([Dict, ?BASE], Msg),
- diameter_codec:encode(D, Pkt);
-
-%% Callback has returned an encoded binary: just send.
-encode(_, #diameter_packet{} = Pkt) ->
- Pkt.
-
-%% pick_dictionary/2
-
-%% Pick the first dictionary that declares the application id in the
-%% specified header.
-pick_dictionary(Ds, [#diameter_header{application_id = Id} | _]) ->
- pd(Ds, fun(D) -> Id = D:id() end);
-
-%% Pick the first dictionary that knows the specified message name.
-pick_dictionary(Ds, [MsgName|_]) ->
- pd(Ds, fun(D) -> D:msg2rec(MsgName) end);
-
-%% Pick the first dictionary that knows the name of the specified
-%% message record.
-pick_dictionary(Ds, Rec) ->
- Name = element(1, Rec),
- pd(Ds, fun(D) -> D:rec2msg(Name) end).
-
-pd([D|Ds], F) ->
- try
- F(D),
- D
- catch
- error:_ ->
- pd(Ds, F)
- end;
-
-pd([], _) ->
- ?ERROR(no_dictionary).
-
-%% send_request/4
-
-send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, Timeout)
- when node() == node(TPid) ->
- %% Store the outgoing request before sending to avoid a race with
- %% reply reception.
- TRef = store_request(TPid, Bin, Req, Timeout),
- send(TPid, Pkt),
- TRef;
-
-%% Send using a remote transport: spawn a process on the remote node
-%% to relay the answer.
-send_request(TPid, #diameter_packet{} = Pkt, Req, Timeout) ->
- TRef = erlang:start_timer(Timeout, self(), timeout),
- T = {TPid, Pkt, Req, Timeout, TRef},
- spawn(node(TPid), ?MODULE, send, [T]),
- TRef.
-
-%% send/1
-
-send({TPid, Pkt, #request{handler = Pid} = Req, Timeout, TRef}) ->
- Ref = send_request(TPid, Pkt, Req#request{handler = self()}, Timeout),
- Pid ! reref(receive T -> T end, Ref, TRef).
-
-reref({T, Ref, R}, Ref, TRef) ->
- {T, TRef, R};
-reref(T, _, _) ->
- T.
-
-%% send/2
-
-send(Pid, Pkt) ->
- Pid ! {send, Pkt}.
-
-%% retransmit/4
-
-retransmit({TPid, Caps, #diameter_app{alias = Alias} = App},
- #request{app = Alias,
- packet = Pkt}
- = Req,
- SvcName,
- Timeout) ->
- have_request(Pkt, TPid) %% Don't failover to a peer we've
- andalso ?THROW(timeout), %% already sent to.
-
- case cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]) of
- {send, P} ->
- retransmit(make_packet(P, Pkt), TPid, Caps, Req, Timeout);
- {discard, Reason} ->
- ?THROW(Reason);
- discard ->
- ?THROW(discarded);
- T ->
- ?ERROR({invalid_return, prepare_retransmit, App, T})
- end.
-
-%% retransmit/5
-
-retransmit(Pkt, TPid, Caps, #request{dictionary = Dict} = Req, Timeout) ->
- EPkt = encode(Dict, Pkt),
-
- NewReq = Req#request{transport = TPid,
- packet = Pkt,
- caps = Caps},
-
- ?LOG(retransmission, NewReq),
- TRef = send_request(TPid, EPkt, NewReq, Timeout),
- {TRef, NewReq}.
-
-%% store_request/4
-
-store_request(TPid, Bin, Req, Timeout) ->
- Seqs = diameter_codec:sequence_numbers(Bin),
- TRef = erlang:start_timer(Timeout, self(), timeout),
- ets:insert(?REQUEST_TABLE, {Seqs, Req, TRef}),
- ets:member(?REQUEST_TABLE, TPid)
- orelse (self() ! {failover, TRef}), %% possibly missed failover
- TRef.
-
-%% lookup_request/2
-
-lookup_request(Msg, TPid)
- when is_pid(TPid) ->
- lookup(Msg, TPid, '_');
-
-lookup_request(Msg, TRef)
- when is_reference(TRef) ->
- lookup(Msg, '_', TRef).
-
-lookup(Msg, TPid, TRef) ->
- Seqs = diameter_codec:sequence_numbers(Msg),
- Spec = [{{Seqs, #request{transport = TPid, _ = '_'}, TRef},
- [],
- ['$_']}],
- case ets:select(?REQUEST_TABLE, Spec) of
- [{_, Req, _}] ->
- Req;
- [] ->
- false
- end.
-
-%% erase_request/1
-
-erase_request(Pkt) ->
- ets:delete(?REQUEST_TABLE, diameter_codec:sequence_numbers(Pkt)).
-
-%% match_requests/1
-
-match_requests(TPid) ->
- Pat = {'_', #request{transport = TPid, _ = '_'}, '_'},
- ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]).
-
-%% have_request/2
-
-have_request(Pkt, TPid) ->
- Seqs = diameter_codec:sequence_numbers(Pkt),
- Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'},
- '$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1).
-
-%% request_peer_up/1
-
-request_peer_up(TPid) ->
- ets:insert(?REQUEST_TABLE, {TPid}).
-
-%% request_peer_down/2
-
-request_peer_down(TPid, S) ->
- ets:delete(?REQUEST_TABLE, TPid),
- lists:foreach(fun(T) -> failover(T,S) end, match_requests(TPid)).
-%% Note that a request process can store its request after failover
-%% notifications are sent here: store_request/4 sends the notification
-%% in that case. Note also that we'll send as many notifications to a
-%% given handler as there are peers its sent to. All but one of these
-%% will be ignored.
-
-%%% ---------------------------------------------------------------------------
-%%% recv_request/3
-%%% ---------------------------------------------------------------------------
-
-recv_request(TPid, Pkt, {ConnT, SvcName, Apps}) ->
- try ets:lookup(ConnT, TPid) of
- [C] ->
- recv_request(C, TPid, Pkt, SvcName, Apps);
- [] -> %% transport has gone down
- ok
- catch
- error: badarg -> %% service has gone down (and taken table with it)
- ok
- end.
-
-%% recv_request/5
-
-recv_request(#conn{apps = SApps, caps = Caps}, TPid, Pkt, SvcName, Apps) ->
- #diameter_caps{origin_host = {OH,_},
- origin_realm = {OR,_}}
- = Caps,
-
- #diameter_packet{header = #diameter_header{application_id = Id}}
- = Pkt,
-
- recv_request(find_recv_app(Id, SApps),
- {SvcName, OH, OR},
- TPid,
- Apps,
- Caps,
- Pkt).
-
-%% find_recv_app/2
-
-%% No one should be sending the relay identifier.
-find_recv_app(?APP_ID_RELAY, _) ->
- false;
-
-%% With any other id we either support it locally or as a relay.
-find_recv_app(Id, SApps) ->
- keyfind([Id, ?APP_ID_RELAY], 1, SApps).
-
-%% keyfind/3
-
-keyfind([], _, _) ->
- false;
-keyfind([Key | Rest], Pos, L) ->
- case lists:keyfind(Key, Pos, L) of
- false ->
- keyfind(Rest, Pos, L);
- T ->
- T
- end.
-
-%% recv_request/6
-
-recv_request({Id, Alias}, T, TPid, Apps, Caps, Pkt) ->
- #diameter_app{dictionary = Dict}
- = A
- = find_app(Alias, Apps),
- recv_request(T, {TPid, Caps}, A, diameter_codec:decode(Id, Dict, Pkt));
-%% Note that the decode is different depending on whether or not Id is
-%% ?APP_ID_RELAY.
-
-%% DIAMETER_APPLICATION_UNSUPPORTED 3007
-%% A request was sent for an application that is not supported.
-
-recv_request(false, T, TPid, _, _, Pkt) ->
- As = collect_avps(Pkt),
- protocol_error(3007, T, TPid, Pkt#diameter_packet{avps = As}).
-
-collect_avps(Pkt) ->
- case diameter_codec:collect_avps(Pkt) of
- {_Bs, As} ->
- As;
- As ->
- As
- end.
-
-%% recv_request/4
-
-%% Wrong number of bits somewhere in the message: reply.
-%%
-%% DIAMETER_INVALID_AVP_BITS 3009
-%% A request was received that included an AVP whose flag bits are
-%% set to an unrecognized value, or that is inconsistent with the
-%% AVP's definition.
-%%
-recv_request(T, {TPid, _}, _, #diameter_packet{errors = [Bs | _]} = Pkt)
- when is_bitstring(Bs) ->
- protocol_error(3009, T, TPid, Pkt);
-
-%% Either we support this application but don't recognize the command
-%% or we're a relay and the command isn't proxiable.
-%%
-%% DIAMETER_COMMAND_UNSUPPORTED 3001
-%% The Request contained a Command-Code that the receiver did not
-%% recognize or support. This MUST be used when a Diameter node
-%% receives an experimental command that it does not understand.
-%%
-recv_request(T,
- {TPid, _},
- #diameter_app{id = Id},
- #diameter_packet{header = #diameter_header{is_proxiable = P},
- msg = M}
- = Pkt)
- when ?APP_ID_RELAY /= Id, undefined == M;
- ?APP_ID_RELAY == Id, not P ->
- protocol_error(3001, T, TPid, Pkt);
-
-%% Error bit was set on a request.
-%%
-%% DIAMETER_INVALID_HDR_BITS 3008
-%% A request was received whose bits in the Diameter header were
-%% either set to an invalid combination, or to a value that is
-%% inconsistent with the command code's definition.
-%%
-recv_request(T,
- {TPid, _},
- _,
- #diameter_packet{header = #diameter_header{is_error = true}}
- = Pkt) ->
- protocol_error(3008, T, TPid, Pkt);
-
-%% A message in a locally supported application or a proxiable message
-%% in the relay application. Don't distinguish between the two since
-%% each application has its own callback config. That is, the user can
-%% easily distinguish between the two cases.
-recv_request(T, TC, App, Pkt) ->
- request_cb(T, TC, App, examine(Pkt)).
-
-%% Note that there may still be errors but these aren't protocol
-%% (3xxx) errors that lead to an answer-message.
-
-request_cb({SvcName, _OH, _OR} = T, TC, App, Pkt) ->
- request_cb(cb(App, handle_request, [Pkt, SvcName, TC]), App, T, TC, Pkt).
-
-%% examine/1
-%%
-%% Look for errors in a decoded message. Length errors result in
-%% decode failure in diameter_codec.
-
-examine(#diameter_packet{header = #diameter_header{version
- = ?DIAMETER_VERSION}}
- = Pkt) ->
- Pkt;
-
-%% DIAMETER_UNSUPPORTED_VERSION 5011
-%% This error is returned when a request was received, whose version
-%% number is unsupported.
-
-examine(#diameter_packet{errors = Es} = Pkt) ->
- Pkt#diameter_packet{errors = [5011 | Es]}.
-%% It's odd/unfortunate that this isn't a protocol error.
-
-%% request_cb/5
-
-%% A reply may be an answer-message, constructed either here or by
-%% the handle_request callback. The header from the incoming request
-%% is passed into the encode so that it can retrieve the relevant
-%% command code in this case. It will also then ignore Dict and use
-%% the base encoder.
-request_cb({reply, Ans},
- #diameter_app{dictionary = Dict},
- _,
- {TPid, _},
- Pkt) ->
- reply(Ans, Dict, TPid, Pkt);
-
-%% An 3xxx result code, for which the E-bit is set in the header.
-request_cb({protocol_error, RC}, _, T, {TPid, _}, Pkt)
- when 3000 =< RC, RC < 4000 ->
- protocol_error(RC, T, TPid, Pkt);
-
-%% RFC 3588 says we must reply 3001 to anything unrecognized or
-%% unsupported. 'noreply' is undocumented (and inappropriately named)
-%% backwards compatibility for this, protocol_error the documented
-%% alternative.
-request_cb(noreply, _, T, {TPid, _}, Pkt) ->
- protocol_error(3001, T, TPid, Pkt);
-
-%% Relay a request to another peer. This is equivalent to doing an
-%% explicit call/4 with the message in question except that (1) a loop
-%% will be detected by examining Route-Record AVP's, (3) a
-%% Route-Record AVP will be added to the outgoing request and (3) the
-%% End-to-End Identifier will default to that in the
-%% #diameter_header{} without the need for an end_to_end_identifier
-%% option.
-%%
-%% relay and proxy are similar in that they require the same handling
-%% with respect to Route-Record and End-to-End identifier. The
-%% difference is that a proxy advertises specific applications, while
-%% a relay advertises the relay application. If a callback doesn't
-%% want to distinguish between the cases in the callback return value
-%% then 'resend' is a neutral alternative.
-%%
-request_cb({A, Opts},
- #diameter_app{id = Id}
- = App,
- T,
- TC,
- Pkt)
- when A == relay, Id == ?APP_ID_RELAY;
- A == proxy, Id /= ?APP_ID_RELAY;
- A == resend ->
- resend(Opts, App, T, TC, Pkt);
-
-request_cb(discard, _, _, _, _) ->
- ok;
-
-request_cb({eval, RC, F}, App, T, TC, Pkt) ->
- request_cb(RC, App, T, TC, Pkt),
- diameter_lib:eval(F).
-
-%% protocol_error/4
-
-protocol_error(RC, {_, OH, OR}, TPid, #diameter_packet{avps = Avps} = Pkt) ->
- ?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Pkt).
-
-%% resend/5
-%%
-%% Resend a message as a relay or proxy agent.
-
-resend(Opts,
- #diameter_app{} = App,
- {_SvcName, OH, _OR} = T,
- {_TPid, _Caps} = TC,
- #diameter_packet{avps = Avps} = Pkt) ->
- {Code, _Flags, Vid} = ?BASE:avp_header('Route-Record'),
- resend(is_loop(Code, Vid, OH, Avps), Opts, App, T, TC, Pkt).
-
-%% DIAMETER_LOOP_DETECTED 3005
-%% An agent detected a loop while trying to get the message to the
-%% intended recipient. The message MAY be sent to an alternate peer,
-%% if one is available, but the peer reporting the error has
-%% identified a configuration problem.
-
-resend(true, _, _, T, {TPid, _}, Pkt) -> %% Route-Record loop
- protocol_error(3005, T, TPid, Pkt);
-
-%% 6.1.8. Relaying and Proxying Requests
-%%
-%% A relay or proxy agent MUST append a Route-Record AVP to all requests
-%% forwarded. The AVP contains the identity of the peer the request was
-%% received from.
-
-resend(false,
- Opts,
- App,
- {SvcName, _, _} = T,
- {TPid, #diameter_caps{origin_host = {_, OH}}},
- #diameter_packet{header = Hdr0,
- avps = Avps}
- = Pkt) ->
- Route = #diameter_avp{data = {?BASE, 'Route-Record', OH}},
- Seq = diameter_session:sequence(),
- Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
- Msg = [Hdr, Route | Avps],
- resend(call(SvcName, App, Msg, Opts), T, TPid, Pkt).
-%% The incoming request is relayed with the addition of a
-%% Route-Record. Note the requirement on the return from call/4 below,
-%% which places a requirement on the value returned by the
-%% handle_answer callback of the application module in question.
-%%
-%% Note that there's nothing stopping the request from being relayed
-%% back to the sender. A pick_peer callback may want to avoid this but
-%% a smart peer might recognize the potential loop and choose another
-%% route. A less smart one will probably just relay the request back
-%% again and force us to detect the loop. A pick_peer that wants to
-%% avoid this can specify filter to avoid the possibility.
-%% Eg. {neg, {host, OH} where #diameter_caps{origin_host = {OH, _}}.
-%%
-%% RFC 6.3 says that a relay agent does not modify Origin-Host but
-%% says nothing about a proxy. Assume it should behave the same way.
-
-%% resend/4
-%%
-%% Relay a reply to a relayed request.
-
-%% Answer from the peer: reset the hop by hop identifier and send.
-resend(#diameter_packet{bin = B}
- = Pkt,
- _,
- TPid,
- #diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
- transport_data = TD}) ->
- send(TPid, Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
- transport_data = TD});
-%% TODO: counters
-
-%% Or not: DIAMETER_UNABLE_TO_DELIVER.
-resend(_, T, TPid, Pkt) ->
- protocol_error(3002, T, TPid, Pkt).
-
-%% is_loop/4
-%%
-%% Is there a Route-Record AVP with our Origin-Host?
-
-is_loop(Code,
- Vid,
- Bin,
- [#diameter_avp{code = Code, vendor_id = Vid, data = Bin} | _]) ->
- true;
-
-is_loop(_, _, _, []) ->
- false;
-
-is_loop(Code, Vid, OH, [_ | Avps])
- when is_binary(OH) ->
- is_loop(Code, Vid, OH, Avps);
-
-is_loop(Code, Vid, OH, Avps) ->
- is_loop(Code, Vid, ?BASE:avp(encode, OH, 'Route-Record'), Avps).
-
-%% reply/4
-%%
-%% Send a locally originating reply.
-
-%% No errors or a diameter_header/avp list.
-reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
- transport_data = TD}
- = ReqPkt)
- when [] == Es;
- is_record(hd(Msg), diameter_header) ->
- Pkt = diameter_codec:encode(Dict, make_reply_packet(Msg, ReqPkt)),
- incr(send, Pkt, Dict, TPid), %% count result codes in sent answers
- send(TPid, Pkt#diameter_packet{transport_data = TD});
-
-%% Or not: set Result-Code and Failed-AVP AVP's.
-reply(Msg, Dict, TPid, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
- reply(rc(Msg, rc(H), [A || {_,A} <- Es], Dict),
- Dict,
- TPid,
- Pkt#diameter_packet{errors = []}).
-
-%% make_reply_packet/2
-
-%% Binaries and header/avp lists are sent as-is.
-make_reply_packet(Bin, _)
- when is_binary(Bin) ->
- #diameter_packet{bin = Bin};
-make_reply_packet([#diameter_header{} | _] = Msg, _) ->
- #diameter_packet{msg = Msg};
-
-%% Otherwise a reply message clears the R and T flags and retains the
-%% P flag. The E flag will be set at encode.
-make_reply_packet(Msg, #diameter_packet{header = ReqHdr}) ->
- Hdr = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
- is_request = false,
- is_error = undefined,
- is_retransmitted = false},
- #diameter_packet{header = Hdr,
- msg = Msg}.
-
-%% rc/1
-
-rc({RC, _}) ->
- RC;
-rc(RC) ->
- RC.
-
-%% rc/4
-
-rc(Rec, RC, Failed, Dict)
- when is_integer(RC) ->
- set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict).
-
-%% Reply as name and tuple list ...
-set([_|_] = Ans, Avps, _) ->
- Ans ++ Avps; %% Values nearer tail take precedence.
-
-%% ... or record.
-set(Rec, Avps, Dict) ->
- Dict:'#set-'(Avps, Rec).
-
-%% failed_avp/3
-
-failed_avp(_, [] = No, _) ->
- No;
-
-failed_avp(Rec, Failed, Dict) ->
- [fa(Rec, [{'AVP', Failed}], Dict)].
-
-%% Reply as name and tuple list ...
-fa([MsgName | Values], FailedAvp, Dict) ->
- R = Dict:msg2rec(MsgName),
- try
- Dict:'#info-'(R, {index, 'Failed-AVP'}),
- {'Failed-AVP', [FailedAvp]}
- catch
- error: _ ->
- Avps = proplists:get_value('AVP', Values, []),
- A = #diameter_avp{name = 'Failed-AVP',
- value = FailedAvp},
- {'AVP', [A|Avps]}
- end;
-
-%% ... or record.
-fa(Rec, FailedAvp, Dict) ->
- try
- {'Failed-AVP', [FailedAvp]}
- catch
- error: _ ->
- Avps = Dict:'get-'('AVP', Rec),
- A = #diameter_avp{name = 'Failed-AVP',
- value = FailedAvp},
- {'AVP', [A|Avps]}
- end.
-
-%% 3. Diameter Header
-%%
-%% E(rror) - If set, the message contains a protocol error,
-%% and the message will not conform to the ABNF
-%% described for this command. Messages with the 'E'
-%% bit set are commonly referred to as error
-%% messages. This bit MUST NOT be set in request
-%% messages. See Section 7.2.
-
-%% 3.2. Command Code ABNF specification
-%%
-%% e-bit = ", ERR"
-%% ; If present, the 'E' bit in the Command
-%% ; Flags is set, indicating that the answer
-%% ; message contains a Result-Code AVP in
-%% ; the "protocol error" class.
-
-%% 7.1.3. Protocol Errors
-%%
-%% Errors that fall within the Protocol Error category SHOULD be treated
-%% on a per-hop basis, and Diameter proxies MAY attempt to correct the
-%% error, if it is possible. Note that these and only these errors MUST
-%% only be used in answer messages whose 'E' bit is set.
-
-%% Thus, only construct answers to protocol errors. Other errors
-%% require an message-specific answer and must be handled by the
-%% application.
-
-%% 6.2. Diameter Answer Processing
-%%
-%% When a request is locally processed, the following procedures MUST be
-%% applied to create the associated answer, in addition to any
-%% additional procedures that MAY be discussed in the Diameter
-%% application defining the command:
-%%
-%% - The same Hop-by-Hop identifier in the request is used in the
-%% answer.
-%%
-%% - The local host's identity is encoded in the Origin-Host AVP.
-%%
-%% - The Destination-Host and Destination-Realm AVPs MUST NOT be
-%% present in the answer message.
-%%
-%% - The Result-Code AVP is added with its value indicating success or
-%% failure.
-%%
-%% - If the Session-Id is present in the request, it MUST be included
-%% in the answer.
-%%
-%% - Any Proxy-Info AVPs in the request MUST be added to the answer
-%% message, in the same order they were present in the request.
-%%
-%% - The 'P' bit is set to the same value as the one in the request.
-%%
-%% - The same End-to-End identifier in the request is used in the
-%% answer.
-%%
-%% Note that the error messages (see Section 7.3) are also subjected to
-%% the above processing rules.
-
-%% 7.3. Error-Message AVP
-%%
-%% The Error-Message AVP (AVP Code 281) is of type UTF8String. It MAY
-%% accompany a Result-Code AVP as a human readable error message. The
-%% Error-Message AVP is not intended to be useful in real-time, and
-%% SHOULD NOT be expected to be parsed by network entities.
-
-%% answer_message/2
-
-answer_message({OH, OR, RC}, Avps) ->
- {Code, _, Vid} = ?BASE:avp_header('Session-Id'),
- ['answer-message', {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Result-Code', RC}
- | session_id(Code, Vid, Avps)].
-
-session_id(Code, Vid, Avps)
- when is_list(Avps) ->
- try
- {value, #diameter_avp{data = D}} = find_avp(Code, Vid, Avps),
- [{'Session-Id', [?BASE:avp(decode, D, 'Session-Id')]}]
- catch
- error: _ ->
- []
- end.
-
-%% find_avp/3
-
-find_avp(Code, Vid, Avps)
- when is_integer(Code), (undefined == Vid orelse is_integer(Vid)) ->
- find(fun(A) -> is_avp(Code, Vid, A) end, Avps).
-
-%% The final argument here could be a list of AVP's, depending on the case,
-%% but we're only searching at the top level.
-is_avp(Code, Vid, #diameter_avp{code = Code, vendor_id = Vid}) ->
- true;
-is_avp(_, _, _) ->
- false.
-
-find(_, []) ->
- false;
-find(Pred, [H|T]) ->
- case Pred(H) of
- true ->
- {value, H};
- false ->
- find(Pred, T)
- end.
-
-%% 7. Error Handling
-%%
-%% There are certain Result-Code AVP application errors that require
-%% additional AVPs to be present in the answer. In these cases, the
-%% Diameter node that sets the Result-Code AVP to indicate the error
-%% MUST add the AVPs. Examples are:
-%%
-%% - An unrecognized AVP is received with the 'M' bit (Mandatory bit)
-%% set, causes an answer to be sent with the Result-Code AVP set to
-%% DIAMETER_AVP_UNSUPPORTED, and the Failed-AVP AVP containing the
-%% offending AVP.
-%%
-%% - An AVP that is received with an unrecognized value causes an
-%% answer to be returned with the Result-Code AVP set to
-%% DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
-%% AVP causing the error.
-%%
-%% - A command is received with an AVP that is omitted, yet is
-%% mandatory according to the command's ABNF. The receiver issues an
-%% answer with the Result-Code set to DIAMETER_MISSING_AVP, and
-%% creates an AVP with the AVP Code and other fields set as expected
-%% in the missing AVP. The created AVP is then added to the Failed-
-%% AVP AVP.
-%%
-%% The Result-Code AVP describes the error that the Diameter node
-%% encountered in its processing. In case there are multiple errors,
-%% the Diameter node MUST report only the first error it encountered
-%% (detected possibly in some implementation dependent order). The
-%% specific errors that can be described by this AVP are described in
-%% the following section.
-
-%% 7.5. Failed-AVP AVP
-%%
-%% The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
-%% debugging information in cases where a request is rejected or not
-%% fully processed due to erroneous information in a specific AVP. The
-%% value of the Result-Code AVP will provide information on the reason
-%% for the Failed-AVP AVP.
-%%
-%% The possible reasons for this AVP are the presence of an improperly
-%% constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
-%% value, the omission of a required AVP, the presence of an explicitly
-%% excluded AVP (see tables in Section 10), or the presence of two or
-%% more occurrences of an AVP which is restricted to 0, 1, or 0-1
-%% occurrences.
-%%
-%% A Diameter message MAY contain one Failed-AVP AVP, containing the
-%% entire AVP that could not be processed successfully. If the failure
-%% reason is omission of a required AVP, an AVP with the missing AVP
-%% code, the missing vendor id, and a zero filled payload of the minimum
-%% required length for the omitted AVP will be added.
-
-%%% ---------------------------------------------------------------------------
-%%% # handle_answer/3
-%%% ---------------------------------------------------------------------------
-
-%% Process an answer message in call-specific process.
-
-handle_answer(SvcName, _, {error, Req, Reason}) ->
- handle_error(Req, Reason, SvcName);
-
-handle_answer(SvcName,
- AnswerErrors,
- {answer, #request{dictionary = Dict} = Req, Pkt}) ->
- a(examine(diameter_codec:decode(Dict, Pkt)),
- SvcName,
- AnswerErrors,
- Req).
-
-%% We don't really need to do a full decode if we're a relay and will
-%% just resend with a new hop by hop identifier, but might a proxy
-%% want to examine the answer?
-
-a(#diameter_packet{errors = []}
- = Pkt,
- SvcName,
- AE,
- #request{transport = TPid,
- dictionary = Dict,
- caps = Caps,
- packet = P}
- = Req) ->
- try
- incr(in, Pkt, Dict, TPid)
- of
- _ ->
- cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}])
- catch
- exit: {invalid_error_bit, _} = E ->
- e(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
- end;
-
-a(#diameter_packet{} = Pkt, SvcName, AE, Req) ->
- e(Pkt, SvcName, AE, Req).
-
-e(Pkt, SvcName, callback, #request{transport = TPid,
- caps = Caps,
- packet = Pkt}
- = Req) ->
- cb(Req, handle_answer, [Pkt, msg(Pkt), SvcName, {TPid, Caps}]);
-e(Pkt, SvcName, report, Req) ->
- x(errors, handle_answer, [SvcName, Req, Pkt]);
-e(Pkt, SvcName, discard, Req) ->
- x({errors, handle_answer, [SvcName, Req, Pkt]}).
-
-%% Note that we don't check that the application id in the answer's
-%% header is what we expect. (TODO: Does the rfc says anything about
-%% this?)
-
-%% incr/4
-%%
-%% Increment a stats counter for an incoming or outgoing message.
-
-%% TODO: fix
-incr(_, #diameter_packet{msg = undefined}, _, _) ->
- ok;
-
-incr(Dir, Pkt, Dict, TPid)
- when is_pid(TPid) ->
- #diameter_packet{header = #diameter_header{is_error = E}
- = Hdr,
- msg = Rec}
- = Pkt,
-
- D = choose(E, ?BASE, Dict),
- RC = int(get_avp_value(D, 'Result-Code', Rec)),
- PE = is_protocol_error(RC),
-
- %% Check that the E bit is set only for 3xxx result codes.
- (not (E orelse PE))
- orelse (E andalso PE)
- orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
-
- Ctr = rc_counter(D, Rec, RC),
- is_tuple(Ctr)
- andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
-
-%% incr/2
-
-incr(TPid, Counter) ->
- diameter_stats:incr(Counter, TPid, 1).
-
-%% RFC 3588, 7.6:
-%%
-%% All Diameter answer messages defined in vendor-specific
-%% applications MUST include either one Result-Code AVP or one
-%% Experimental-Result AVP.
-%%
-%% Maintain statistics assuming one or the other, not both, which is
-%% surely the intent of the RFC.
-
-rc_counter(_, _, RC)
- when is_integer(RC) ->
- {'Result-Code', RC};
-rc_counter(D, Rec, _) ->
- rcc(get_avp_value(D, 'Experimental-Result', Rec)).
-
-%% Outgoing answers may be in any of the forms messages can be sent
-%% in. Incoming messages will be records. We're assuming here that the
-%% arity of the result code AVP's is 0 or 1.
-
-rcc([{_,_,RC} = T])
- when is_integer(RC) ->
- T;
-rcc({_,_,RC} = T)
- when is_integer(RC) ->
- T;
-rcc(_) ->
- undefined.
-
-int([N])
- when is_integer(N) ->
- N;
-int(N)
- when is_integer(N) ->
- N;
-int(_) ->
- undefined.
-
-is_protocol_error(RC) ->
- 3000 =< RC andalso RC < 4000.
-
--spec x(any(), atom(), list()) -> no_return().
-
-%% Warn and exit request process on errors in an incoming answer.
-x(Reason, F, A) ->
- diameter_lib:warning_report(Reason, {?MODULE, F, A}),
- x(Reason).
-
-x(T) ->
- exit(T).
-
-%%% ---------------------------------------------------------------------------
-%%% # failover/[23]
-%%% ---------------------------------------------------------------------------
-
-%% Failover as a consequence of request_peer_down/2.
-failover({_, #request{handler = Pid} = Req, TRef}, S) ->
- Pid ! {failover, TRef, rt(Req, S)}.
-
-%% Failover as a consequence of store_request/4.
-failover(TRef, Seqs, S)
- when is_reference(TRef) ->
- case lookup_request(Seqs, TRef) of
- #request{} = Req ->
- failover({Seqs, Req, TRef}, S);
- false ->
- ok
- end.
-
-%% prepare_request returned a binary ...
-rt(#request{packet = #diameter_packet{msg = undefined}}, _) ->
- false; %% TODO: Not what we should do.
-
-%% ... or not.
-rt(#request{packet = #diameter_packet{msg = Msg}, dictionary = D} = Req, S) ->
- find_transport(get_destination(Msg, D), Req, S).
-
-%%% ---------------------------------------------------------------------------
-%%% # report_status/5
-%%% ---------------------------------------------------------------------------
-
-report_status(Status,
- #peer{ref = Ref,
- conn = TPid,
- type = Type,
- options = Opts},
- #conn{apps = [_|_] = As,
- caps = Caps},
- #state{service_name = SvcName}
- = S,
- Extra) ->
- share_peer(Status, Caps, As, TPid, S),
- Info = [Status, Ref, {TPid, Caps}, {type(Type), Opts} | Extra],
- send_event(SvcName, list_to_tuple(Info)).
-
-%% send_event/2
-
-send_event(SvcName, Info) ->
- send_event(#diameter_event{service = SvcName,
- info = Info}).
-
-send_event(#diameter_event{service = SvcName} = E) ->
- lists:foreach(fun({_, Pid}) -> Pid ! E end, subscriptions(SvcName)).
-
-%%% ---------------------------------------------------------------------------
-%%% # share_peer/5
-%%% ---------------------------------------------------------------------------
-
-share_peer(up, Caps, Aliases, TPid, #state{share_peers = true,
- service_name = Svc}) ->
- diameter_peer:notify(Svc, {peer, TPid, Aliases, Caps});
-
-share_peer(_, _, _, _, _) ->
- ok.
-
-%%% ---------------------------------------------------------------------------
-%%% # share_peers/2
-%%% ---------------------------------------------------------------------------
-
-share_peers(Pid, #state{share_peers = true,
- local_peers = PDict}) ->
- ?Dict:fold(fun(A,Ps,ok) -> sp(Pid, A, Ps), ok end, ok, PDict);
-
-share_peers(_, #state{share_peers = false}) ->
- ok.
-
-sp(Pid, Alias, Peers) ->
- lists:foreach(fun({P,C}) -> Pid ! {peer, P, [Alias], C} end, Peers).
-
-%%% ---------------------------------------------------------------------------
-%%% # remote_peer_up/4
-%%% ---------------------------------------------------------------------------
-
-remote_peer_up(Pid, Aliases, Caps, #state{use_shared_peers = true,
- service = Svc,
- shared_peers = PDict}
- = S) ->
- #diameter_service{applications = Apps} = Svc,
- Update = lists:filter(fun(A) ->
- lists:keymember(A, #diameter_app.alias, Apps)
- end,
- Aliases),
- S#state{shared_peers = rpu(Pid, Caps, PDict, Update)};
-
-remote_peer_up(_, _, _, #state{use_shared_peers = false} = S) ->
- S.
-
-rpu(_, _, PDict, []) ->
- PDict;
-rpu(Pid, Caps, PDict, Aliases) ->
- erlang:monitor(process, Pid),
- T = {Pid, Caps},
- lists:foldl(fun(A,D) -> ?Dict:append(A, T, D) end,
- PDict,
- Aliases).
-
-%%% ---------------------------------------------------------------------------
-%%% # remote_peer_down/2
-%%% ---------------------------------------------------------------------------
-
-remote_peer_down(Pid, #state{use_shared_peers = true,
- shared_peers = PDict}
- = S) ->
- S#state{shared_peers = lists:foldl(fun(A,D) -> rpd(Pid, A, D) end,
- PDict,
- ?Dict:fetch_keys(PDict))}.
-
-rpd(Pid, Alias, PDict) ->
- ?Dict:update(Alias, fun(Ps) -> lists:keydelete(Pid, 1, Ps) end, PDict).
-
-%%% ---------------------------------------------------------------------------
-%%% find_transport/[34]
-%%%
-%%% Output: {TransportPid, #diameter_caps{}, #diameter_app{}}
-%%% | false
-%%% | {error, Reason}
-%%% ---------------------------------------------------------------------------
-
-%% Initial call, from an arbitrary process.
-find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) ->
- #diameter_service{applications = Apps} = Svc,
- ft(find_send_app(Alias, Apps), Msg, Opts, S);
-
-%% Relay or proxy send.
-find_transport(#diameter_app{} = App, Msg, Opts, S) ->
- ft(App, Msg, Opts, S).
-
-ft(#diameter_app{module = Mod, dictionary = D} = App, Msg, Opts, S) ->
- #options{filter = Filter,
- extra = Xtra}
- = Opts,
- pick_peer(App#diameter_app{module = Mod ++ Xtra},
- get_destination(Msg, D),
- Filter,
- S);
-ft(false = No, _, _, _) ->
- No.
-
-%% This can't be used if we're a relay and sending a message
-%% in an application not known locally. (TODO)
-find_send_app(Alias, Apps) ->
- case lists:keyfind(Alias, #diameter_app.alias, Apps) of
- #diameter_app{id = ?APP_ID_RELAY} ->
- false;
- T ->
- T
- end.
-
-%% Retransmission, in the service process.
-find_transport([_,_] = RH,
- Req,
- #state{service = #diameter_service{pid = Pid,
- applications = Apps}}
- = S)
- when self() == Pid ->
- #request{app = Alias,
- filter = Filter,
- module = ModX}
- = Req,
- #diameter_app{}
- = App
- = lists:keyfind(Alias, #diameter_app.alias, Apps),
-
- pick_peer(App#diameter_app{module = ModX},
- RH,
- Filter,
- S).
-
-%% get_destination/2
-
-get_destination(Msg, Dict) ->
- [str(get_avp_value(Dict, 'Destination-Realm', Msg)),
- str(get_avp_value(Dict, 'Destination-Host', Msg))].
-
-%% This is not entirely correct. The avp could have an arity 1, in
-%% which case an empty list is a DiameterIdentity of length 0 rather
-%% than the list of no values we treat it as by mapping to undefined.
-%% This behaviour is documented.
-str([]) ->
- undefined;
-str(T) ->
- T.
-
-%% get_avp_value/3
-%%
-%% Find an AVP in a message of one of three forms:
-%%
-%% - a message record (as generated from a .dia spec) or
-%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
-%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
-%%
-%% In the first two forms a dictionary module is used at encode to
-%% identify the type of the AVP and its arity in the message in
-%% question. The third form allows messages to be sent as is, without
-%% a dictionary, which is needed in the case of relay agents, for one.
-
-get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
- try
- {Code, _, VId} = Dict:avp_header(Name),
- [A|_] = lists:dropwhile(fun(#diameter_avp{code = C, vendor_id = V}) ->
- C /= Code orelse V /= VId
- end,
- Avps),
- avp_decode(Dict, Name, A)
- catch
- error: _ ->
- undefined
- end;
-
-get_avp_value(_, Name, [_MsgName | Avps]) ->
- case lists:keyfind(Name, 1, Avps) of
- {_, V} ->
- V;
- _ ->
- undefined
- end;
-
-%% Message is typically a record but not necessarily: diameter:call/4
-%% can be passed an arbitrary term.
-get_avp_value(Dict, Name, Rec) ->
- try
- Dict:'#get-'(Name, Rec)
- catch
- error:_ ->
- undefined
- end.
-
-avp_decode(Dict, Name, #diameter_avp{value = undefined,
- data = Bin}) ->
- Dict:avp(decode, Bin, Name);
-avp_decode(_, _, #diameter_avp{value = V}) ->
- V.
-
-%%% ---------------------------------------------------------------------------
-%%% # pick_peer(App, [DestRealm, DestHost], Filter, #state{})
-%%%
-%%% Output: {TransportPid, #diameter_caps{}, App}
-%%% | false
-%%% | {error, Reason}
-%%% ---------------------------------------------------------------------------
-
-%% Find transports to a given realm/host.
-
-pick_peer(#diameter_app{alias = Alias}
- = App,
- [_,_] = RH,
- Filter,
- #state{local_peers = L,
- shared_peers = S,
- service_name = SvcName,
- service = #diameter_service{pid = Pid}}) ->
- pick_peer(peers(Alias, RH, Filter, L),
- peers(Alias, RH, Filter, S),
- Pid,
- SvcName,
- App).
-
-%% pick_peer/5
-
-pick_peer([], [], _, _, _) ->
- false;
-
-%% App state is mutable but we're not in the service process: go there.
-pick_peer(Local, Remote, Pid, _SvcName, #diameter_app{mutable = true} = App)
- when self() /= Pid ->
- call_service(Pid, {pick_peer, Local, Remote, App});
-
-%% App state isn't mutable or it is and we're in the service process:
-%% do the deed.
-pick_peer(Local,
- Remote,
- _Pid,
- SvcName,
- #diameter_app{module = ModX,
- alias = Alias,
- init_state = S,
- mutable = M}
- = App) ->
- MFA = {ModX, pick_peer, [Local, Remote, SvcName]},
-
- try state_cb(App, MFA) of
- {ok, {TPid, #diameter_caps{} = Caps}} when is_pid(TPid) ->
- {TPid, Caps, App};
- {{TPid, #diameter_caps{} = Caps}, ModS} when is_pid(TPid), M ->
- mod_state(Alias, ModS),
- {TPid, Caps, App};
- {false = No, ModS} when M ->
- mod_state(Alias, ModS),
- No;
- {ok, false = No} ->
- No;
- false = No ->
- No;
- {{TPid, #diameter_caps{} = Caps}, S} when is_pid(TPid) ->
- {TPid, Caps, App}; %% Accept returned state in the immutable
- {false = No, S} -> %% case as long it isn't changed.
- No;
- T ->
- diameter_lib:error_report({invalid, T, App}, MFA)
- catch
- E: Reason ->
- diameter_lib:error_report({failure, {E, Reason, ?STACK}}, MFA)
- end.
-
-%% peers/4
-
-peers(Alias, RH, Filter, Peers) ->
- case ?Dict:find(Alias, Peers) of
- {ok, L} ->
- ps(L, RH, Filter, {[],[]});
- error ->
- []
- end.
-
-%% Place a peer whose Destination-Host/Realm matches those of the
-%% request at the front of the result list. Could add some sort of
-%% 'sort' option to allow more control.
-
-ps([], _, _, {Ys, Ns}) ->
- lists:reverse(Ys, Ns);
-ps([{_TPid, #diameter_caps{} = Caps} = TC | Rest], RH, Filter, Acc) ->
- ps(Rest, RH, Filter, pacc(caps_filter(Caps, RH, Filter),
- caps_filter(Caps, RH, {all, [host, realm]}),
- TC,
- Acc)).
-
-pacc(true, true, Peer, {Ts, Fs}) ->
- {[Peer|Ts], Fs};
-pacc(true, false, Peer, {Ts, Fs}) ->
- {Ts, [Peer|Fs]};
-pacc(_, _, _, Acc) ->
- Acc.
-
-%% caps_filter/3
-
-caps_filter(C, RH, {neg, F}) ->
- not caps_filter(C, RH, F);
-
-caps_filter(C, RH, {all, L})
- when is_list(L) ->
- lists:all(fun(F) -> caps_filter(C, RH, F) end, L);
-
-caps_filter(C, RH, {any, L})
- when is_list(L) ->
- lists:any(fun(F) -> caps_filter(C, RH, F) end, L);
-
-caps_filter(#diameter_caps{origin_host = {_,OH}}, [_,DH], host) ->
- eq(undefined, DH, OH);
-
-caps_filter(#diameter_caps{origin_realm = {_,OR}}, [DR,_], realm) ->
- eq(undefined, DR, OR);
-
-caps_filter(C, _, Filter) ->
- caps_filter(C, Filter).
-
-%% caps_filter/2
-
-caps_filter(_, none) ->
- true;
-
-caps_filter(#diameter_caps{origin_host = {_,OH}}, {host, H}) ->
- eq(any, H, OH);
-
-caps_filter(#diameter_caps{origin_realm = {_,OR}}, {realm, R}) ->
- eq(any, R, OR);
-
-%% Anything else is expected to be an eval filter. Filter failure is
-%% documented as being equivalent to a non-matching filter.
-
-caps_filter(C, T) ->
- try
- {eval, F} = T,
- diameter_lib:eval([F,C])
- catch
- _:_ -> false
- end.
-
-eq(Any, Id, PeerId) ->
- Any == Id orelse try
- iolist_to_binary(Id) == iolist_to_binary(PeerId)
- catch
- _:_ -> false
- end.
-%% OctetString() can be specified as an iolist() so test for string
-%% rather then term equality.
-
-%% transports/1
-
-transports(#state{peerT = PeerT}) ->
- ets:select(PeerT, [{#peer{conn = '$1', _ = '_'},
- [{'is_pid', '$1'}],
- ['$1']}]).
-
-%%% ---------------------------------------------------------------------------
-%%% # service_info/2
-%%% ---------------------------------------------------------------------------
-
-%% The config passed to diameter:start_service/2.
--define(CAP_INFO, ['Origin-Host',
- 'Origin-Realm',
- 'Vendor-Id',
- 'Product-Name',
- 'Origin-State-Id',
- 'Host-IP-Address',
- 'Supported-Vendor-Id',
- 'Auth-Application-Id',
- 'Inband-Security-Id',
- 'Acct-Application-Id',
- 'Vendor-Specific-Application-Id',
- 'Firmware-Revision']).
-
--define(ALL_INFO, [capabilities,
- applications,
- transport,
- pending,
- statistics]).
-
-service_info(Items, S)
- when is_list(Items) ->
- [{complete(I), service_info(I,S)} || I <- Items];
-service_info(Item, S)
- when is_atom(Item) ->
- service_info(Item, S, true).
-
-service_info(Item, #state{service = Svc} = S, Complete) ->
- case Item of
- name ->
- S#state.service_name;
- 'Origin-Host' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.origin_host;
- 'Origin-Realm' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.origin_realm;
- 'Vendor-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.vendor_id;
- 'Product-Name' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.product_name;
- 'Origin-State-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.origin_state_id;
- 'Host-IP-Address' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.host_ip_address;
- 'Supported-Vendor-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.supported_vendor_id;
- 'Auth-Application-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.auth_application_id;
- 'Inband-Security-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.inband_security_id;
- 'Acct-Application-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.acct_application_id;
- 'Vendor-Specific-Application-Id' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.vendor_specific_application_id;
- 'Firmware-Revision' ->
- (Svc#diameter_service.capabilities)
- #diameter_caps.firmware_revision;
- capabilities -> service_info(?CAP_INFO, S);
- applications -> info_apps(S);
- transport -> info_transport(S);
- pending -> info_pending(S);
- statistics -> info_stats(S);
- keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test
- all -> service_info(?ALL_INFO, S);
- _ when Complete -> service_info(complete(Item), S, false);
- _ -> undefined
- end.
-
-complete(Pre) ->
- P = atom_to_list(Pre),
- case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO,
- lists:prefix(P, atom_to_list(I))]
- of
- [I] -> I;
- _ -> Pre
- end.
-
-info_stats(#state{peerT = PeerT}) ->
- Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'},
- [{'is_pid', '$2'}],
- [['$1', '$2']]}]),
- diameter_stats:read(lists:append(Peers)).
-%% TODO: include peer identities in return value
-
-info_transport(#state{peerT = PeerT, connT = ConnT}) ->
- dict:fold(fun it/3,
- [],
- ets:foldl(fun(T,A) -> it_acc(ConnT, A, T) end,
- dict:new(),
- PeerT)).
-
-it(Ref, [[{type, connect} | _] = L], Acc) ->
- [[{ref, Ref} | L] | Acc];
-it(Ref, [[{type, accept}, {options, Opts} | _] | _] = L, Acc) ->
- [[{ref, Ref},
- {type, listen},
- {options, Opts},
- {accept, [lists:nthtail(2,A) || A <- L]}]
- | Acc].
-%% Each entry has the same Opts. (TODO)
-
-it_acc(ConnT, Acc, #peer{pid = Pid,
- type = Type,
- ref = Ref,
- options = Opts,
- op_state = OS,
- started = T,
- conn = TPid}) ->
- dict:append(Ref,
- [{type, Type},
- {options, Opts},
- {watchdog, {Pid, T, OS}}
- | info_conn(ConnT, TPid)],
- Acc).
-
-info_conn(ConnT, TPid) ->
- info_conn(ets:lookup(ConnT, TPid)).
-
-info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) ->
- [{peer, {Pid, T}},
- {apps, SApps},
- {caps, info_caps(Caps)}];
-info_conn([] = No) ->
- No.
-
-info_caps(#diameter_caps{} = C) ->
- lists:zip(record_info(fields, diameter_caps), tl(tuple_to_list(C))).
-
-info_apps(#state{service = #diameter_service{applications = Apps}}) ->
- lists:map(fun mk_app/1, Apps).
-
-mk_app(#diameter_app{alias = Alias,
- dictionary = Dict,
- module = ModX,
- id = Id}) ->
- [{alias, Alias},
- {dictionary, Dict},
- {module, ModX},
- {id, Id}].
-
-info_pending(#state{} = S) ->
- MatchSpec = [{{'$1',
- #request{transport = '$2',
- from = '$3',
- app = '$4',
- _ = '_'},
- '_'},
- [?ORCOND([{'==', T, '$2'} || T <- transports(S)])],
- [{{'$1', [{{app, '$4'}},
- {{transport, '$2'}},
- {{from, '$3'}}]}}]}],
-
- ets:select(?REQUEST_TABLE, MatchSpec).
diff --git a/lib/diameter/src/app/diameter_service_sup.erl b/lib/diameter/src/app/diameter_service_sup.erl
deleted file mode 100644
index 153fff902f..0000000000
--- a/lib/diameter/src/app/diameter_service_sup.erl
+++ /dev/null
@@ -1,64 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% The supervisor of service processes.
-%%
-
--module(diameter_service_sup).
-
--behaviour(supervisor).
-
--export([start_link/0, %% supervisor start
- start_child/1]). %% service start
-
-%% supervisor callback
--export([init/1]).
-
--define(NAME, ?MODULE). %% supervisor name
-
-%% start_link/0
-
-start_link() ->
- SupName = {local, ?NAME},
- supervisor:start_link(SupName, ?MODULE, []).
-
-%% start_child/1
-%%
-%% A service and its related processes (transport, peer_fwm and
-%% watchdog) are all temporary since they're all restarted in
-%% application code. A Transport and peer_fsm is restarted by a
-%% watchdog as required by the RFC 3539 state machine, a watchdog is
-%% restarted by service, and services are restarted by diameter_config.
-
-start_child(ServiceName) ->
- supervisor:start_child(?NAME, [ServiceName]).
-
-%% init/1
-
-init([]) ->
- Mod = diameter_service,
- Flags = {simple_one_for_one, 0, 1},
- ChildSpec = {Mod,
- {Mod, start_link, []},
- temporary,
- 1000,
- worker,
- [Mod]},
- {ok, {Flags, [ChildSpec]}}.
diff --git a/lib/diameter/src/app/diameter_session.erl b/lib/diameter/src/app/diameter_session.erl
deleted file mode 100644
index bb91e97f39..0000000000
--- a/lib/diameter/src/app/diameter_session.erl
+++ /dev/null
@@ -1,172 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_session).
-
--export([sequence/0,
- session_id/1,
- origin_state_id/0]).
-
-%% towards diameter_sup
--export([init/0]).
-
--include("diameter_types.hrl").
-
--define(INT64, 16#FFFFFFFFFFFFFFFF).
--define(INT32, 16#FFFFFFFF).
-
-%% ---------------------------------------------------------------------------
-%% # sequence/0
-%%
-%% Output: 32-bit
-%% ---------------------------------------------------------------------------
-
-%% 3588, 3:
-%%
-%% Hop-by-Hop Identifier
-%% The Hop-by-Hop Identifier is an unsigned 32-bit integer field (in
-%% network byte order) and aids in matching requests and replies.
-%% The sender MUST ensure that the Hop-by-Hop identifier in a request
-%% is unique on a given connection at any given time, and MAY attempt
-%% to ensure that the number is unique across reboots. The sender of
-%% an Answer message MUST ensure that the Hop-by-Hop Identifier field
-%% contains the same value that was found in the corresponding
-%% request. The Hop-by-Hop identifier is normally a monotonically
-%% increasing number, whose start value was randomly generated. An
-%% answer message that is received with an unknown Hop-by-Hop
-%% Identifier MUST be discarded.
-%%
-%% End-to-End Identifier
-%% The End-to-End Identifier is an unsigned 32-bit integer field (in
-%% network byte order) and is used to detect duplicate messages.
-%% Upon reboot implementations MAY set the high order 12 bits to
-%% contain the low order 12 bits of current time, and the low order
-%% 20 bits to a random value. Senders of request messages MUST
-%% insert a unique identifier on each message. The identifier MUST
-%% remain locally unique for a period of at least 4 minutes, even
-%% across reboots. The originator of an Answer message MUST ensure
-%% that the End-to-End Identifier field contains the same value that
-%% was found in the corresponding request. The End-to-End Identifier
-%% MUST NOT be modified by Diameter agents of any kind. The
-%% combination of the Origin-Host (see Section 6.3) and this field is
-%% used to detect duplicates. Duplicate requests SHOULD cause the
-%% same answer to be transmitted (modulo the hop-by-hop Identifier
-%% field and any routing AVPs that may be present), and MUST NOT
-%% affect any state that was set when the original request was
-%% processed. Duplicate answer messages that are to be locally
-%% consumed (see Section 6.2) SHOULD be silently discarded.
-
--spec sequence()
- -> 'Unsigned32'().
-
-sequence() ->
- Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT32, _SetVal = 0},
- ets:update_counter(diameter_sequence, sequence, Instr).
-
-%% ---------------------------------------------------------------------------
-%% # origin_state_id/0
-%% ---------------------------------------------------------------------------
-
-%% 3588, 8.16:
-%%
-%% The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
-%% monotonically increasing value that is advanced whenever a Diameter
-%% entity restarts with loss of previous state, for example upon reboot.
-%% Origin-State-Id MAY be included in any Diameter message, including
-%% CER.
-%%
-%% A Diameter entity issuing this AVP MUST create a higher value for
-%% this AVP each time its state is reset. A Diameter entity MAY set
-%% Origin-State-Id to the time of startup, or it MAY use an incrementing
-%% counter retained in non-volatile memory across restarts.
-
--spec origin_state_id()
- -> 'Unsigned32'().
-
-origin_state_id() ->
- ets:lookup_element(diameter_sequence, origin_state_id, 2).
-
-%% ---------------------------------------------------------------------------
-%% # session_id/1
-%% ---------------------------------------------------------------------------
-
-%% 3588, 8.8:
-%%
-%% The Session-Id MUST begin with the sender's identity encoded in the
-%% DiameterIdentity type (see Section 4.4). The remainder of the
-%% Session-Id is delimited by a ";" character, and MAY be any sequence
-%% that the client can guarantee to be eternally unique; however, the
-%% following format is recommended, (square brackets [] indicate an
-%% optional element):
-%%
-%% ;;[;]
-%%
-%% and are decimal representations of the
-%% high and low 32 bits of a monotonically increasing 64-bit value. The
-%% 64-bit value is rendered in two part to simplify formatting by 32-bit
-%% processors. At startup, the high 32 bits of the 64-bit value MAY be
-%% initialized to the time, and the low 32 bits MAY be initialized to
-%% zero. This will for practical purposes eliminate the possibility of
-%% overlapping Session-Ids after a reboot, assuming the reboot process
-%% takes longer than a second. Alternatively, an implementation MAY
-%% keep track of the increasing value in non-volatile memory.
-%%
-%% is implementation specific but may include a modem's
-%% device Id, a layer 2 address, timestamp, etc.
-
--spec session_id('DiameterIdentity'())
- -> 'OctetString'().
-%% Note that Session-Id has type UTF8String and that any OctetString
-%% is a UTF8String.
-
-session_id(Host) ->
- Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT64, _Set = 0},
- N = ets:update_counter(diameter_sequence, session_base, Instr),
- Hi = N bsr 32,
- Lo = N band ?INT32,
- [Host, ";", integer_to_list(Hi),
- ";", integer_to_list(Lo),
- ";", atom_to_list(node())].
-
-%% ---------------------------------------------------------------------------
-%% # init/0
-%% ---------------------------------------------------------------------------
-
-init() ->
- Now = now(),
- random:seed(Now),
- Time = time32(Now),
- Seq = (?INT32 band (Time bsl 20)) bor (random:uniform(1 bsl 20) - 1),
- ets:insert(diameter_sequence, [{origin_state_id, Time},
- {session_base, Time bsl 32},
- {sequence, Seq}]),
- Time.
-
-%% ---------------------------------------------------------
-%% INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
-
-%% The minimum value represented by a Time value. (See diameter_types.)
-%% 32 bits extends to 2104.
--define(TIME0, 62105714048). %% {{1968,1,20},{3,14,8}}
-
-time32(Now) ->
- Time = calendar:now_to_universal_time(Now),
- Diff = calendar:datetime_to_gregorian_seconds(Time) - ?TIME0,
- Diff band ?INT32.
diff --git a/lib/diameter/src/app/diameter_stats.erl b/lib/diameter/src/app/diameter_stats.erl
deleted file mode 100644
index 71479afa95..0000000000
--- a/lib/diameter/src/app/diameter_stats.erl
+++ /dev/null
@@ -1,342 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% Statistics collector.
-%%
-
--module(diameter_stats).
--compile({no_auto_import, [monitor/2]}).
-
--behaviour(gen_server).
-
--export([reg/1, reg/2,
- incr/1, incr/2, incr/3,
- read/1,
- flush/0, flush/1]).
-
-%% supervisor callback
--export([start_link/0]).
-
-%% gen_server callbacks
--export([init/1,
- terminate/2,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- code_change/3]).
-
-%% debug
--export([state/0,
- uptime/0]).
-
--include("diameter_internal.hrl").
-
-%% ets table containing stats. reg(Pid, Ref) inserts a {Pid, Ref},
-%% incr(Counter, X, N) updates the counter keyed at {Counter, X}, and
-%% Pid death causes counters keyed on {Counter, Pid} to be deleted and
-%% added to those keyed on {Counter, Ref}.
--define(TABLE, ?MODULE).
-
-%% Name of registered server.
--define(SERVER, ?MODULE).
-
-%% Entries in the table.
--define(REC(Key, Value), {Key, Value}).
-
-%% Server state.
--record(state, {id = now()}).
-
--type counter() :: any().
--type contrib() :: any().
-
-%%% ---------------------------------------------------------------------------
-%%% # reg(Pid, Contrib)
-%%%
-%%% Description: Register a process as a contributor of statistics
-%%% associated with a specified term. Statistics can be
-%%% contributed by specifying either Pid or Contrib as
-%%% the second argument to incr/3. Statistics contributed
-%%% by Pid are folded into the corresponding entry for
-%%% Contrib when the process dies.
-%%%
-%%% Contrib can be any term but should not be a pid
-%%% passed as the first argument to reg/2. Subsequent
-%%% registrations for the same Pid overwrite the association
-%%% ---------------------------------------------------------------------------
-
--spec reg(pid(), contrib())
- -> true.
-
-reg(Pid, Contrib)
- when is_pid(Pid) ->
- call({reg, Pid, Contrib}).
-
--spec reg(contrib())
- -> true.
-
-reg(Ref) ->
- reg(self(), Ref).
-
-%%% ---------------------------------------------------------------------------
-%%% # incr(Counter, Contrib, N)
-%%%
-%%% Description: Increment a counter for the specified contributor.
-%%%
-%%% Contrib will typically be an argument passed to reg/2
-%%% but there's nothing that requires this. In particular,
-%%% if Contrib is a pid that hasn't been registered then
-%%% counters are unaffected by the death of the process.
-%%% ---------------------------------------------------------------------------
-
--spec incr(counter(), contrib(), integer())
- -> integer().
-
-incr(Ctr, Contrib, N) ->
- update_counter({Ctr, Contrib}, N).
-
-incr(Ctr, N)
- when is_integer(N) ->
- incr(Ctr, self(), N);
-
-incr(Ctr, Contrib) ->
- incr(Ctr, Contrib, 1).
-
-incr(Ctr) ->
- incr(Ctr, self(), 1).
-
-%%% ---------------------------------------------------------------------------
-%%% # read(Contribs)
-%%%
-%%% Description: Retrieve counters for the specified contributors.
-%%% ---------------------------------------------------------------------------
-
--spec read([contrib()])
- -> [{contrib(), [{counter(), integer()}]}].
-
-read(Contribs) ->
- lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end,
- orddict:new(),
- ets:select(?TABLE, [{?REC({'_', '$1'}, '_'),
- [?ORCOND([{'=:=', '$1', {const, C}}
- || C <- Contribs])],
- ['$_']}])).
-
-%%% ---------------------------------------------------------------------------
-%%% # flush(Contrib)
-%%%
-%%% Description: Retrieve and delete statistics for the specified
-%%% contributor.
-%%%
-%%% If Contrib is a pid registered with reg/2 then statistics
-%%% for both and its associated contributor are retrieved.
-%%% ---------------------------------------------------------------------------
-
--spec flush(contrib())
- -> [{counter(), integer()}].
-
-flush(Contrib) ->
- try
- call({flush, Contrib})
- catch
- exit: _ ->
- []
- end.
-
-flush() ->
- flush(self()).
-
-%%% ---------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-start_link() ->
- ServerName = {local, ?SERVER},
- Module = ?MODULE,
- Args = [],
- Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
- gen_server:start_link(ServerName, Module, Args, Options).
-
-state() ->
- call(state).
-
-uptime() ->
- call(uptime).
-
-%%% ----------------------------------------------------------
-%%% # init(_)
-%%%
-%%% Output: {ok, State}
-%%% ----------------------------------------------------------
-
-init([]) ->
- ets:new(?TABLE, [named_table, ordered_set, public]),
- {ok, #state{}}.
-
-%% ----------------------------------------------------------
-%% handle_call(Request, From, State)
-%% ----------------------------------------------------------
-
-handle_call(state, _, State) ->
- {reply, State, State};
-
-handle_call(uptime, _, #state{id = Time} = State) ->
- {reply, diameter_lib:now_diff(Time), State};
-
-handle_call({reg, Pid, Contrib}, _From, State) ->
- monitor(not ets:member(?TABLE, Pid), Pid),
- {reply, insert(?REC(Pid, Contrib)), State};
-
-handle_call({flush, Contrib}, _From, State) ->
- {reply, fetch(Contrib), State};
-
-handle_call(Req, From, State) ->
- ?UNEXPECTED([Req, From]),
- {reply, nok, State}.
-
-%% ----------------------------------------------------------
-%% handle_cast(Request, State)
-%% ----------------------------------------------------------
-
-handle_cast({incr, Rec}, State) ->
- update_counter(Rec),
- {noreply, State};
-
-handle_cast(Msg, State) ->
- ?UNEXPECTED([Msg]),
- {noreply, State}.
-
-%% ----------------------------------------------------------
-%% handle_info(Request, State)
-%% ----------------------------------------------------------
-
-handle_info({'DOWN', _MRef, process, Pid, _}, State) ->
- down(Pid),
- {noreply, State};
-
-handle_info(Info, State) ->
- ?UNEXPECTED([Info]),
- {noreply, State}.
-
-%% ----------------------------------------------------------
-%% terminate(Reason, State)
-%% ----------------------------------------------------------
-
-terminate(_Reason, _State) ->
- ok.
-
-%% ----------------------------------------------------------
-%% code_change(OldVsn, State, Extra)
-%% ----------------------------------------------------------
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%% ---------------------------------------------------------
-%%% INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-%% monitor/2
-
-monitor(true, Pid) ->
- erlang:monitor(process, Pid);
-monitor(false = No, _) ->
- No.
-
-%% down/1
-
-down(Pid) ->
- L = ets:match_object(?TABLE, ?REC({'_', Pid}, '_')),
- [?REC(_, Ref) = T] = lookup(Pid),
- fold(Ref, L),
- delete_object(T),
- delete(L).
-
-%% Fold Pid-based entries into Ref-based ones.
-fold(Ref, L) ->
- lists:foreach(fun(?REC({K, _}, V)) -> update_counter({{K, Ref}, V}) end,
- L).
-
-delete(Objs) ->
- lists:foreach(fun delete_object/1, Objs).
-
-%% fetch/1
-
-fetch(X) ->
- MatchSpec = [{?REC({'_', '$1'}, '_'),
- [?ORCOND([{'==', '$1', {const, T}} || T <- [X | ref(X)]])],
- ['$_']}],
- L = ets:select(?TABLE, MatchSpec),
- delete(L),
- D = lists:foldl(fun sum/2, dict:new(), L),
- dict:to_list(D).
-
-sum({{Ctr, _}, N}, Dict) ->
- dict:update(Ctr, fun(V) -> V+N end, N, Dict).
-
-ref(Pid)
- when is_pid(Pid) ->
- ets:select(?TABLE, [{?REC(Pid, '$1'), [], ['$1']}]);
-ref(_) ->
- [].
-
-%% update_counter/2
-%%
-%% From an arbitrary request process. Cast to the server process to
-%% insert a new element if the counter doesn't exists so that two
-%% processes don't do so simultaneously.
-
-update_counter(Key, N) ->
- try
- ets:update_counter(?TABLE, Key, N)
- catch
- error: badarg ->
- cast({incr, ?REC(Key, N)})
- end.
-
-%% update_counter/1
-%%
-%% From the server process.
-
-update_counter(?REC(Key, N) = T) ->
- try
- ets:update_counter(?TABLE, Key, N)
- catch
- error: badarg ->
- insert(T)
- end.
-
-insert(T) ->
- ets:insert(?TABLE, T).
-
-lookup(Key) ->
- ets:lookup(?TABLE, Key).
-
-delete_object(T) ->
- ets:delete_object(?TABLE, T).
-
-%% cast/1
-
-cast(Msg) ->
- gen_server:cast(?SERVER, Msg).
-
-%% call/1
-
-call(Request) ->
- gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/app/diameter_sup.erl b/lib/diameter/src/app/diameter_sup.erl
deleted file mode 100644
index e5afd23dcd..0000000000
--- a/lib/diameter/src/app/diameter_sup.erl
+++ /dev/null
@@ -1,101 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% The top supervisor for the diameter application.
-%%
-
--module(diameter_sup).
-
--behaviour(supervisor).
-
-%% interface
--export([start_link/0, %% supervisor start
- tree/0]). %% supervision tree
-
-%% supervisor callback
--export([init/1]).
-
--define(CHILDREN, [diameter_misc_sup,
- diameter_watchdog_sup,
- diameter_peer_fsm_sup,
- diameter_transport_sup,
- diameter_service_sup]).
-
--define(TABLES, [{diameter_sequence, [set]},
- {diameter_service, [set, {keypos, 3}]},
- {diameter_request, [bag]},
- {diameter_config, [bag, {keypos, 2}]}]).
-
-%% start_link/0
-
-start_link() ->
- SupName = {local, ?MODULE},
- supervisor:start_link(SupName, ?MODULE, []).
-
-%% init/1
-
-init([]) ->
- ets_new(?TABLES),
- diameter_session:init(),
- Flags = {one_for_one, 1, 5},
- ChildSpecs = lists:map(fun spec/1, ?CHILDREN),
- {ok, {Flags, ChildSpecs}}.
-
-%% spec/1
-
-spec(Mod) ->
- {Mod,
- {Mod, start_link, []},
- permanent,
- 1000,
- supervisor,
- [Mod]}.
-
-%% ets_new/1
-
-ets_new(List)
- when is_list(List) ->
- lists:foreach(fun ets_new/1, List);
-
-ets_new({Table, Opts}) ->
- ets:new(Table, [named_table, public | Opts]).
-
-%% tree/0
-
-tree() ->
- [{?MODULE, whereis(?MODULE), tree(?MODULE)}].
-
-tree(Sup) ->
- lists:map(fun t/1, supervisor:which_children(Sup)).
-
-t({Name, Pid, supervisor, _}) ->
- t(Name, Pid, tree(Pid));
-t({Name, Pid, worker, _}) ->
- t(Name, Pid).
-
-t(undefined, Pid, Children) ->
- {Pid, Children};
-t(Name, Pid, Children) ->
- {Name, Pid, Children}.
-
-t(undefined, Pid) ->
- Pid;
-t(Name, Pid) ->
- {Name, Pid}.
diff --git a/lib/diameter/src/app/diameter_sync.erl b/lib/diameter/src/app/diameter_sync.erl
deleted file mode 100644
index ce2db4b3a2..0000000000
--- a/lib/diameter/src/app/diameter_sync.erl
+++ /dev/null
@@ -1,550 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% This module implements a server that serializes requests in named
-%% queues. A request is an MFA or fun and a name can be any term. A
-%% request is applied in a dedicated process that terminates when
-%% the request function returns.
-%%
-
--module(diameter_sync).
--behaviour(gen_server).
-
--export([call/4, call/5,
- cast/4, cast/5,
- carp/1, carp/2]).
-
-%% supervisor callback
--export([start_link/0]).
-
-%% gen_server interface
--export([init/1,
- terminate/2,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- code_change/3]).
-
-%% test/debug
--export([state/0,
- uptime/0,
- flush/1,
- pending/0,
- pending/1,
- queues/0,
- pids/1]).
-
--include("diameter_internal.hrl").
-
-%% Locally registered server name.
--define(SERVER, ?MODULE).
-
-%% Message to the server to queue a request ...
--define(REQUEST(CallOrCast, Name, Req, Max, Timeout),
- {request, CallOrCast, Name, Req, Max, Timeout}).
-
-%% ... and to retrieve the pid of the prevailing request process.
--define(CARP(Name),
- {carp, Name}).
-
-%% Forever ...
--define(TIMEOUT, 30000).
-
-%% Server state.
--record(state,
- {time = now(),
- pending = 0 :: non_neg_integer(), %% outstanding requests
- monitor = new() :: ets:tid(), %% MonitorRef -> {Name, From}
- queue = new() :: ets:tid()}). %% Name -> queue of {Pid, Ref}
-
-%% ----------------------------------------------------------
-%% # call(Node, Name, Req, Max, Timeout)
-%% # call(Name, Req, Max, Timeout)
-%%
-%% Input: Name = term() identifying the queue in which the request is
-%% to be evaluated.
-%% Req = {M,F,A}
-%% | {Fun, Arg}
-%% | [Fun | Args]
-%% | Fun
-%% Max = Upper bound for the number of outstanding requests
-%% in the named queue for Req to be queued.
-%% If more than this number are in the queue then
-%% 'rejected' is returned to the caller. Can be any
-%% term but integer() | infinity is sufficient.
-%% Timeout = 32 bit integer() number of milliseconds after which
-%% request is cancelled (if not already started), causing
-%% 'timeout' to be returned to the caller.
-%% | infinity
-%%
-%% Output: Req() | rejected | timeout
-%%
-%% Description: Serialize a request in a named queue. Note that if
-%% 'timeout' is returned and the request itself does not
-%% return this atom then request has not been evaluated.
-%% ----------------------------------------------------------
-
-call(Name, Req, Max, Timeout) ->
- call(node(), Name, Req, Max, Timeout).
-
-call(Node, Name, Req, Max, Timeout) ->
- gen_call({?SERVER, Node}, ?REQUEST(call, Name, Req, Max, Timeout)).
-
-%%% ----------------------------------------------------------
-%%% # cast(Node, Name, Req, Max, Timeout)
-%%% # cast(Name, Req, Max, Timeout)
-%%%
-%%% Output: ok | rejected | timeout
-%%%
-%%% Description: Serialize a request without returning the result to the
-%%% caller. Returns after the task is queued.
-%%% ----------------------------------------------------------
-
-cast(Name, Req, Max, Timeout) ->
- cast(node(), Name, Req, Max, Timeout).
-
-cast(Node, Name, Req, Max, Timeout) ->
- gen_call({?SERVER, Node}, ?REQUEST(cast, Name, Req, Max, Timeout)).
-
-%% 'timeout' is only return if the server process that processes
-%% requests isn't alive. Ditto for call/carp.
-
-%%% ----------------------------------------------------------
-%%% # carp(Node, Name)
-%%% # carp(Name)
-%%%
-%%% Output: {value, Pid} | false | timeout
-%%%
-%%% Description: Return the pid of the process processing the task
-%%% at the head of the named queue. Note that the value
-%%% returned by subsequent calls changes as tasks are
-%%% completed, each task executing in a dedicated
-%%% process. The exit value of this process will be
-%%% {value, Req()} if the task returns.
-%%% ----------------------------------------------------------
-
-%% The intention of this is to let a process enqueue a task that waits
-%% for a message before completing, the target pid being retrieved
-%% with carp/[12].
-
-carp(Name) ->
- carp(node(), Name).
-
-carp(Node, Name) ->
- gen_call({?SERVER, Node}, ?CARP(Name)).
-
-%%% ---------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-state() ->
- call(state).
-
-uptime() ->
- call(uptime).
-
-flush(Name) ->
- call({flush, Name}).
-
-pending() ->
- call(pending).
-
-pending(Name) ->
- call({pending, Name}).
-
-queues() ->
- call(queues).
-
-pids(Name) ->
- call({pids, Name}).
-
-%%% ----------------------------------------------------------
-%%% # start_link()
-%%% ----------------------------------------------------------
-
-start_link() ->
- ServerName = {local, ?SERVER},
- Module = ?MODULE,
- Args = [],
- Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
- gen_server:start_link(ServerName, Module, Args, Options).
-
-%%% ----------------------------------------------------------
-%%% # init(_)
-%%% ----------------------------------------------------------
-
-init(_) ->
- {ok, #state{}}.
-
-%%% ----------------------------------------------------------
-%%% # handle_call(Request, From, State)
-%%% ----------------------------------------------------------
-
-%% Enqueue a new request.
-handle_call(?REQUEST(Type, Name, Req, Max, Timeout),
- From,
- #state{queue = QD} = State) ->
- T = find(Name, QD),
- nq(queued(T) =< Max, T, {Type, From}, Name, Req, Timeout, State);
-
-handle_call(Request, From, State) ->
- {reply, call(Request, From, State), State}.
-
-%% call/3
-
-call(?CARP(Name), _, #state{queue = QD}) ->
- pcar(find(Name, QD));
-
-call(state, _, State) ->
- State;
-
-call(uptime, _, #state{time = T}) ->
- diameter_lib:now_diff(T);
-
-call({flush, Name}, _, #state{queue = QD}) ->
- cancel(find(Name, QD));
-
-call(pending, _, #state{pending = N}) ->
- N;
-
-call({pending, Name}, _, #state{queue = QD}) ->
- queued(find(Name, QD));
-
-call(queues, _, #state{queue = QD}) ->
- fetch_keys(QD);
-
-call({pids, Name}, _, #state{queue = QD}) ->
- plist(find(Name, QD));
-
-call(Req, From, _State) -> %% ignore
- ?UNEXPECTED(handle_call, [Req, From]),
- nok.
-
-%%% ----------------------------------------------------------
-%%% handle_cast(Request, State)
-%%% ----------------------------------------------------------
-
-handle_cast(Msg, State) ->
- ?UNEXPECTED([Msg]),
- {noreply, State}.
-
-%%% ----------------------------------------------------------
-%%% handle_info(Request, State)
-%%% ----------------------------------------------------------
-
-handle_info(Request, State) ->
- {noreply, info(Request, State)}.
-
-%% info/2
-
-%% A request has completed execution or timed out.
-info({'DOWN', MRef, process, Pid, Info},
- #state{pending = N,
- monitor = MD,
- queue = QD}
- = State) ->
- {Name, From} = fetch(MRef, MD),
- reply(From, rc(Info)),
- State#state{pending = N-1,
- monitor = erase(MRef, MD),
- queue = dq(fetch(Name, QD), Pid, Info, Name, QD)};
-
-info(Info, State) ->
- ?UNEXPECTED(handle_info, [Info]),
- State.
-
-reply({call, From}, T) ->
- gen_server:reply(From, T);
-reply(cast, _) ->
- ok.
-
-rc({value, T}) ->
- T;
-rc(_) ->
- timeout.
-
-%%% ----------------------------------------------------------
-%%% code_change(OldVsn, State, Extra)
-%%% ----------------------------------------------------------
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%% ----------------------------------------------------------
-%%% terminate(Reason, State)
-%%% ----------------------------------------------------------
-
-terminate(_Reason, _State)->
- ok.
-
-%%% ---------------------------------------------------------
-%%% INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-%% queued/1
-
-queued({ok, {N,_}}) ->
- N;
-queued(error) ->
- 0.
-
-%% nq/7
-
-%% Maximum number of pending requests exceeded ...
-nq(false, _, _, _Name, _Req, _Timeout, State) ->
- {reply, rejected, State};
-
-%% ... or not.
-nq(true, T, From, Name, Req, Timeout, #state{pending = N,
- monitor = MD,
- queue = QD}
- = State) ->
- Ref = make_ref(),
- Pid = init(Ref, Req, timeout(Timeout, T)),
- MRef = erlang:monitor(process, Pid),
- {noreply, State#state{pending = N+1,
- monitor = store(MRef, {Name, from(From)}, MD),
- queue = store(Name, nq(T, {Pid, Ref}), QD)}}.
-
-from({call, _} = T) ->
- T;
-from({cast = T, From}) ->
- gen_server:reply(From, ok),
- T.
-
-%% nq/2
-
-%% Other requests in the queue: append.
-nq({ok, {N,Q}}, T) ->
- {N+1, queue:in(T,Q)};
-
-%% Queue is empty: start execution.
-nq(error, T) ->
- go(T),
- {1, queue:from_list([T])}.
-
-%% Don't timeout if the request is evaluated immediately so as to
-%% avoid a race between getting a 'go' and a 'timeout'. Queueing a
-%% request in an empty queue always results in execution.
-timeout(_, error) ->
- infinity;
-timeout(Timeout, _) ->
- Timeout.
-
-%% dq/5
-%%
-%% A request process has terminated.
-
-dq({N,Q}, Pid, _Info, Name, QD) ->
- {{value, T}, TQ} = queue:out(Q),
- dq(N-1, Pid, T, TQ, Name, QD).
-
-%% dq/6
-
-%% Request was at the head of the queue: start another.
-dq(N, Pid, {Pid, _}, TQ, Name, QD) ->
- dq(N, TQ, Name, QD);
-
-%% Or not: remove the offender from the queue.
-dq(N, Pid, T, TQ, Name, QD) ->
- store(Name, {N, req(Pid, queue:from_list([T]), TQ)}, QD).
-
-%% dq/4
-
-%% Queue is empty: erase.
-dq(0, TQ, Name, QD) ->
- true = queue:is_empty(TQ), %% assert
- erase(Name, QD);
-
-%% Start the next request.
-dq(N, TQ, Name, QD) ->
- go(queue:head(TQ)),
- store(Name, {N, TQ}, QD).
-
-%% req/3
-%%
-%% Find and remove the queue element for the specified pid.
-
-req(Pid, HQ, Q) ->
- {{value, T}, TQ} = queue:out(Q),
- req(Pid, T, HQ, TQ).
-
-req(Pid, {Pid, _}, HQ, TQ) ->
- queue:join(HQ, TQ);
-req(Pid, T, HQ, TQ) ->
- req(Pid, queue:in(T,HQ), TQ).
-
-%% go/1
-
-go({Pid, Ref}) ->
- Pid ! {Ref, ok}.
-
-%% init/4
-%%
-%% Start the dedicated process for handling a request. The exit value
-%% is as promised by carp/1.
-
-init(Ref, Req, Timeout) ->
- spawn(fun() -> exit(i(Ref, Req, Timeout)) end).
-
-i(Ref, Req, Timeout) ->
- Timer = send_timeout(Ref, Timeout),
- MRef = erlang:monitor(process, ?SERVER),
- receive
- {Ref, ok} -> %% Do the deed.
- %% Ensure we don't leave messages in the mailbox since the
- %% request itself might receive. Alternatively, could have
- %% done the eval in a new process but then we'd have to
- %% relay messages arriving at this one.
- cancel_timer(Timer),
- erlang:demonitor(MRef, [flush]),
- %% Ref is to ensure that we don't extract any message that
- %% a client may have sent after retrieving self() with
- %% carp/1, there being no guarantee that the message
- %% banged by go/1 is received before the pid becomes
- %% accessible.
- {value, eval(Req)};
- {Ref, timeout = T} ->
- T;
- {'DOWN', MRef, process, _Pid, _Info} = D -> %% server death
- D
- end.
-
-send_timeout(_Ref, infinity = No) ->
- No;
-send_timeout(Ref, Ms) ->
- Msg = {Ref, timeout},
- TRef = erlang:send_after(Ms, self(), Msg),
- {TRef, Msg}.
-
-cancel_timer(infinity = No) ->
- No;
-cancel_timer({TRef, Msg}) ->
- flush(Msg, erlang:cancel_timer(TRef)).
-
-flush(Msg, false) -> %% Message has already been sent ...
- %% 'error' should never happen but crash if it does so as not to
- %% hang the process.
- ok = receive Msg -> ok after ?TIMEOUT -> error end;
-flush(_, _) -> %% ... or not.
- ok.
-
-eval({M,F,A}) ->
- apply(M,F,A);
-eval([Fun | Args]) ->
- apply(Fun, Args);
-eval({Fun, A}) ->
- Fun(A);
-eval(Fun) ->
- Fun().
-
-%% pcar/1
-
-pcar({ok, {_,Q}}) ->
- {Pid, _Ref} = queue:head(Q),
- {value, Pid};
-pcar(error) ->
- false.
-
-%% plist/1
-
-plist({ok, {_,Q}}) ->
- lists:map(fun({Pid, _Ref}) -> Pid end, queue:to_list(Q));
-plist(error) ->
- [].
-
-%% cancel/1
-%%
-%% Cancel all but the active request from the named queue. Return the
-%% number of requests cancelled.
-
-%% Just send timeout messages to each request to make them die. Note
-%% that these are guaranteed to arrive before a go message after the
-%% current request completes since both messages are sent from the
-%% server process.
-cancel({ok, {N,Q}}) ->
- {_,TQ} = queue:split(1,Q),
- foreach(fun({Pid, Ref}) -> Pid ! {Ref, timeout} end, N-1, TQ),
- N-1;
-cancel(error) ->
- 0.
-
-%% foreach/3
-
-foreach(_, 0, _) ->
- ok;
-foreach(Fun, N, Q) ->
- Fun(queue:head(Q)),
- foreach(Fun, N-1, queue:tail(Q)).
-
-%% call/1
-
-%% gen_server:call/3 will exit if the target process dies.
-call(Request) ->
- try
- gen_server:call(?SERVER, Request, ?TIMEOUT)
- catch
- exit: Reason ->
- {error, Reason}
- end.
-
-%% dict-like table manipulation.
-
-erase(Key, Dict) ->
- ets:delete(Dict, Key),
- Dict.
-
-fetch(Key, Dict) ->
- {ok, V} = find(Key, Dict),
- V.
-
-fetch_keys(Dict) ->
- ets:foldl(fun({K,_}, Acc) -> [K | Acc] end, [], Dict).
-
-find(Key, Dict) ->
- case ets:lookup(Dict, Key) of
- [{Key, V}] ->
- {ok, V};
- [] ->
- error
- end.
-
-new() ->
- ets:new(?MODULE, [set]).
-
-store(Key, Value, Dict) ->
- store({Key, Value}, Dict).
-
-store({_,_} = T, Dict) ->
- ets:insert(Dict, T),
- Dict.
-
-%% gen_call/1
-
-gen_call(Server, Req) ->
- gen_call(Server, Req, infinity).
-
-gen_call(Server, Req, Timeout) ->
- try
- gen_server:call(Server, Req, Timeout)
- catch
- exit: _ ->
- timeout
- end.
diff --git a/lib/diameter/src/app/diameter_types.erl b/lib/diameter/src/app/diameter_types.erl
deleted file mode 100644
index 6b1b1b8d39..0000000000
--- a/lib/diameter/src/app/diameter_types.erl
+++ /dev/null
@@ -1,537 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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(diameter_types).
-
-%%
-%% Encode/decode of RFC 3588 Data Formats, Basic (section 4.2) and
-%% Derived (section 4.3).
-%%
-
-%% Basic types.
--export(['OctetString'/2,
- 'Integer32'/2,
- 'Integer64'/2,
- 'Unsigned32'/2,
- 'Unsigned64'/2,
- 'Float32'/2,
- 'Float64'/2]).
-
-%% Derived types.
--export(['Address'/2,
- 'Time'/2,
- 'UTF8String'/2,
- 'DiameterIdentity'/2,
- 'DiameterURI'/2,
- 'IPFilterRule'/2,
- 'QoSFilterRule'/2]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
--define(UINT(N,X), ((0 =< X) andalso (X < 1 bsl N))).
--define(SINT(N,X), ((-1*(1 bsl (N-1)) < X) andalso (X < 1 bsl (N-1)))).
-
-%% The Grouped and Enumerated types are dealt with directly in
-%% generated decode modules by way of diameter_gen.hrl and
-%% diameter_codec.erl. Padding and the setting of Length and other
-%% fields are also dealt with there.
-
-%% 3588:
-%%
-%% DIAMETER_INVALID_AVP_LENGTH 5014
-%% The request contained an AVP with an invalid length. A Diameter
-%% message indicating this error MUST include the offending AVPs
-%% within a Failed-AVP AVP.
-%%
--define(INVALID_LENGTH(Bin), erlang:error({'DIAMETER', 5014, Bin})).
-
-%% -------------------------------------------------------------------------
-%% 3588, 4.2. Basic AVP Data Formats
-%%
-%% The Data field is zero or more octets and contains information
-%% specific to the Attribute. The format and length of the Data field
-%% is determined by the AVP Code and AVP Length fields. The format of
-%% the Data field MUST be one of the following base data types or a data
-%% type derived from the base data types. In the event that a new Basic
-%% AVP Data Format is needed, a new version of this RFC must be created.
-%% --------------------
-
-'OctetString'(decode, Bin)
- when is_binary(Bin) ->
- binary_to_list(Bin);
-
-'OctetString'(encode = M, zero) ->
- 'OctetString'(M, []);
-
-'OctetString'(encode, Str) ->
- iolist_to_binary(Str).
-
-%% --------------------
-
-'Integer32'(decode, <>) ->
- X;
-
-'Integer32'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Integer32'(encode = M, zero) ->
- 'Integer32'(M, 0);
-
-'Integer32'(encode, I)
- when ?SINT(32,I) ->
- <>.
-
-%% --------------------
-
-'Integer64'(decode, <>) ->
- X;
-
-'Integer64'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Integer64'(encode = M, zero) ->
- 'Integer64'(M, 0);
-
-'Integer64'(encode, I)
- when ?SINT(64,I) ->
- <>.
-
-%% --------------------
-
-'Unsigned32'(decode, <>) ->
- X;
-
-'Unsigned32'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Unsigned32'(encode = M, zero) ->
- 'Unsigned32'(M, 0);
-
-'Unsigned32'(encode, I)
- when ?UINT(32,I) ->
- <>.
-
-%% --------------------
-
-'Unsigned64'(decode, <>) ->
- X;
-
-'Unsigned64'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Unsigned64'(encode = M, zero) ->
- 'Unsigned64'(M, 0);
-
-'Unsigned64'(encode, I)
- when ?UINT(64,I) ->
- <>.
-
-%% --------------------
-
-%% Decent summaries of the IEEE floating point formats can be
-%% found at http://en.wikipedia.org/wiki/IEEE_754-1985 and
-%% http://www.psc.edu/general/software/packages/ieee/ieee.php.
-%%
-%% That the bit syntax uses these formats isn't well documented but
-%% this does indeed appear to be the case. However, the bit syntax
-%% only encodes numeric values, not the standard's (signed) infinity
-%% or NaN. It also encodes any large value as 'infinity', never 'NaN'.
-%% Treat these equivalently on decode for this reason.
-%%
-%% An alternative would be to decode infinity/NaN to the largest
-%% possible float but could likely lead to misleading results if
-%% arithmetic is performed on the decoded value. Better to be explicit
-%% that precision has been lost.
-
-'Float32'(decode, <>) ->
- choose(S, infinity, '-infinity');
-
-'Float32'(decode, <>) ->
- X;
-
-'Float32'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Float32'(encode = M, zero) ->
- 'Float32'(M, 0.0);
-
-'Float32'(encode, infinity) ->
- <<0:1, 255:8, 0:23>>;
-
-'Float32'(encode, '-infinity') ->
- <<1:1, 255:8, 0:23>>;
-
-'Float32'(encode, X)
- when is_float(X) ->
- <>.
-%% Note that this could also encode infinity/-infinity for large
-%% (signed) numeric values. Note also that precision is lost just in
-%% using the floating point syntax. For example:
-%%
-%% 1> B = <<3.14159:32/float>>.
-%% <<64,73,15,208>>
-%% 2> <> = B.
-%% <<64,73,15,208>>
-%% 3> F.
-%% 3.141590118408203
-%%
-%% (The 64 bit type does better.)
-
-%% --------------------
-
-%% The 64 bit format is entirely analogous to the 32 bit format.
-
-'Float64'(decode, <>) ->
- choose(S, infinity, '-infinity');
-
-'Float64'(decode, <>) ->
- X;
-
-'Float64'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Float64'(encode, infinity) ->
- <<0:1, 2047:11, 0:52>>;
-
-'Float64'(encode, '-infinity') ->
- <<1:1, 2047:11, 0:52>>;
-
-'Float64'(encode = M, zero) ->
- 'Float64'(M, 0.0);
-
-'Float64'(encode, X)
- when is_float(X) ->
- <>.
-
-%% -------------------------------------------------------------------------
-%% 3588, 4.3. Derived AVP Data Formats
-%%
-%% In addition to using the Basic AVP Data Formats, applications may
-%% define data formats derived from the Basic AVP Data Formats. An
-%% application that defines new AVP Derived Data Formats MUST include
-%% them in a section entitled "AVP Derived Data Formats", using the same
-%% format as the definitions below. Each new definition must be either
-%% defined or listed with a reference to the RFC that defines the
-%% format.
-%% --------------------
-
-'Address'(encode, zero) ->
- <<0:48>>;
-
-'Address'(decode, <<1:16, B/binary>>)
- when size(B) == 4 ->
- list_to_tuple(binary_to_list(B));
-
-'Address'(decode, <<2:16, B/binary>>)
- when size(B) == 16 ->
- list_to_tuple(v6dec(B, []));
-
-'Address'(decode, <> = B)
- when 1 == A;
- 2 == A ->
- ?INVALID_LENGTH(B);
-
-'Address'(encode, T) ->
- ipenc(diameter_lib:ipaddr(T)).
-
-ipenc(T)
- when is_tuple(T), size(T) == 4 ->
- B = list_to_binary(tuple_to_list(T)),
- <<1:16, B/binary>>;
-
-ipenc(T)
- when is_tuple(T), size(T) == 8 ->
- B = v6enc(lists:reverse(tuple_to_list(T)), <<>>),
- <<2:16, B/binary>>.
-
-v6dec(<>, Acc) ->
- v6dec(B, [N | Acc]);
-
-v6dec(<<>>, Acc) ->
- lists:reverse(Acc).
-
-v6enc([N | Rest], B)
- when ?UINT(16,N) ->
- v6enc(Rest, <>);
-
-v6enc([], B) ->
- B.
-
-%% --------------------
-
-%% A DiameterIdentity is a FQDN as definined in RFC 1035, which is at
-%% least one character.
-
-'DiameterIdentity'(encode = M, zero) ->
- 'OctetString'(M, [0]);
-
-'DiameterIdentity'(encode = M, X) ->
- <<_,_/binary>> = 'OctetString'(M, X);
-
-'DiameterIdentity'(decode = M, <<_,_/binary>> = X) ->
- 'OctetString'(M, X).
-
-%% --------------------
-
-'DiameterURI'(decode, Bin)
- when is_binary(Bin) ->
- scan_uri(Bin);
-
-%% The minimal DiameterURI is "aaa://x", 7 characters.
-'DiameterURI'(encode = M, zero) ->
- 'OctetString'(M, lists:duplicate(0,7));
-
-'DiameterURI'(encode, #diameter_uri{type = Type,
- fqdn = D,
- port = P,
- transport = T,
- protocol = Prot}
- = U) ->
- S = lists:append([atom_to_list(Type), "://", D,
- ":", integer_to_list(P),
- ";transport=", atom_to_list(T),
- ";protocol=", atom_to_list(Prot)]),
- U = scan_uri(S), %% assert
- list_to_binary(S);
-
-'DiameterURI'(encode, Str) ->
- Bin = iolist_to_binary(Str),
- #diameter_uri{} = scan_uri(Bin), %% type check
- Bin.
-
-%% --------------------
-
-%% This minimal rule is "deny in 0 from 0.0.0.0 to 0.0.0.0", 33 characters.
-'IPFilterRule'(encode = M, zero) ->
- 'OctetString'(M, lists:duplicate(0,33));
-
-%% TODO: parse grammar.
-'IPFilterRule'(M, X) ->
- 'OctetString'(M, X).
-
-%% --------------------
-
-%% This minimal rule is the same as for an IPFilterRule.
-'QoSFilterRule'(encode = M, zero = X) ->
- 'IPFilterRule'(M, X);
-
-%% TODO: parse grammar.
-'QoSFilterRule'(M, X) ->
- 'OctetString'(M, X).
-
-%% --------------------
-
-'UTF8String'(decode, Bin) ->
- udec(Bin, []);
-
-'UTF8String'(encode = M, zero) ->
- 'UTF8String'(M, []);
-
-'UTF8String'(encode, S) ->
- uenc(S, []).
-
-udec(<<>>, Acc) ->
- lists:reverse(Acc);
-
-udec(<>, Acc) ->
- udec(Rest, [C | Acc]).
-
-uenc(E, Acc)
- when E == [];
- E == <<>> ->
- list_to_binary(lists:reverse(Acc));
-
-uenc(<>, Acc) ->
- uenc(Rest, [<> | Acc]);
-
-uenc([[] | Rest], Acc) ->
- uenc(Rest, Acc);
-
-uenc([[H|T] | Rest], Acc) ->
- uenc([H, T | Rest], Acc);
-
-uenc([C | Rest], Acc) ->
- uenc(Rest, [<> | Acc]).
-
-%% --------------------
-
-%% RFC 3588, 4.3:
-%%
-%% Time
-%% The Time format is derived from the OctetString AVP Base Format.
-%% The string MUST contain four octets, in the same format as the
-%% first four bytes are in the NTP timestamp format. The NTP
-%% Timestamp format is defined in chapter 3 of [SNTP].
-%%
-%% This represents the number of seconds since 0h on 1 January 1900
-%% with respect to the Coordinated Universal Time (UTC).
-%%
-%% On 6h 28m 16s UTC, 7 February 2036 the time value will overflow.
-%% SNTP [SNTP] describes a procedure to extend the time to 2104.
-%% This procedure MUST be supported by all DIAMETER nodes.
-
-%% RFC 2030, 3:
-%%
-%% As the NTP timestamp format has been in use for the last 17 years,
-%% it remains a possibility that it will be in use 40 years from now
-%% when the seconds field overflows. As it is probably inappropriate
-%% to archive NTP timestamps before bit 0 was set in 1968, a
-%% convenient way to extend the useful life of NTP timestamps is the
-%% following convention: If bit 0 is set, the UTC time is in the
-%% range 1968-2036 and UTC time is reckoned from 0h 0m 0s UTC on 1
-%% January 1900. If bit 0 is not set, the time is in the range 2036-
-%% 2104 and UTC time is reckoned from 6h 28m 16s UTC on 7 February
-%% 2036. Note that when calculating the correspondence, 2000 is not a
-%% leap year. Note also that leap seconds are not counted in the
-%% reckoning.
-%%
-%% The statement regarding year 2000 is wrong: errata id 518 at
-%% http://www.rfc-editor.org/errata_search.php?rfc=2030 notes this.
-
--define(TIME_1900, 59958230400). %% {{1900,1,1},{0,0,0}}
--define(TIME_2036, 64253197696). %% {{2036,2,7},{6,28,16}}
-%% TIME_2036 = TIME_1900 + (1 bsl 32)
-
-%% Time maps [0, 1 bsl 31) onto [TIME_1900 + 1 bsl 31, TIME_2036 + 1 bsl 31)
-%% by taking integers with the high-order bit set relative to TIME_1900
-%% and those without relative to TIME_2036. This corresponds to the
-%% following dates.
--define(TIME_MIN, {{1968,1,20},{3,14,8}}). %% TIME_1900 + 1 bsl 31
--define(TIME_MAX, {{2104,2,26},{9,42,24}}). %% TIME_2036 + 1 bsl 31
-
-'Time'(decode, <>) ->
- Offset = msb(1 == Time bsr 31),
- calendar:gregorian_seconds_to_datetime(Time + Offset);
-
-'Time'(decode, B) ->
- ?INVALID_LENGTH(B);
-
-'Time'(encode, {{_Y,_M,_D},{_HH,_MM,_SS}} = Datetime)
- when ?TIME_MIN =< Datetime, Datetime < ?TIME_MAX ->
- S = calendar:datetime_to_gregorian_seconds(Datetime),
- T = S - msb(S < ?TIME_2036),
- 0 = T bsr 32, %% sanity check
- <>;
-
-'Time'(encode, zero) ->
- <<0:32>>.
-
-%% ===========================================================================
-%% ===========================================================================
-
-choose(0, X, _) -> X;
-choose(1, _, X) -> X.
-
-msb(true) -> ?TIME_1900;
-msb(false) -> ?TIME_2036.
-
-%% RFC 3588, 4.3:
-%%
-%% The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
-%% syntax [URI] rules specified below:
-%%
-%% "aaa://" FQDN [ port ] [ transport ] [ protocol ]
-%%
-%% ; No transport security
-%%
-%% "aaas://" FQDN [ port ] [ transport ] [ protocol ]
-%%
-%% ; Transport security used
-%%
-%% FQDN = Fully Qualified Host Name
-%%
-%% port = ":" 1*DIGIT
-%%
-%% ; One of the ports used to listen for
-%% ; incoming connections.
-%% ; If absent,
-%% ; the default Diameter port (3868) is
-%% ; assumed.
-%%
-%% transport = ";transport=" transport-protocol
-%%
-%% ; One of the transports used to listen
-%% ; for incoming connections. If absent,
-%% ; the default SCTP [SCTP] protocol is
-%% ; assumed. UDP MUST NOT be used when
-%% ; the aaa-protocol field is set to
-%% ; diameter.
-%%
-%% transport-protocol = ( "tcp" / "sctp" / "udp" )
-%%
-%% protocol = ";protocol=" aaa-protocol
-%%
-%% ; If absent, the default AAA protocol
-%% ; is diameter.
-%%
-%% aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
-
-scan_uri(Bin)
- when is_binary(Bin) ->
- scan_uri(binary_to_list(Bin));
-scan_uri("aaa://" ++ Rest) ->
- scan_fqdn(Rest, #diameter_uri{type = aaa});
-scan_uri("aaas://" ++ Rest) ->
- scan_fqdn(Rest, #diameter_uri{type = aaas}).
-
-scan_fqdn(S, U) ->
- {[_|_] = F, Rest} = lists:splitwith(fun is_fqdn/1, S),
- scan_opt_port(Rest, U#diameter_uri{fqdn = F}).
-
-scan_opt_port(":" ++ S, U) ->
- {[_|_] = P, Rest} = lists:splitwith(fun is_digit/1, S),
- scan_opt_transport(Rest, U#diameter_uri{port = list_to_integer(P)});
-scan_opt_port(S, U) ->
- scan_opt_transport(S, U).
-
-scan_opt_transport(";transport=" ++ S, U) ->
- {P, Rest} = transport(S),
- scan_opt_protocol(Rest, U#diameter_uri{transport = P});
-scan_opt_transport(S, U) ->
- scan_opt_protocol(S, U).
-
-scan_opt_protocol(";protocol=" ++ S, U) ->
- {P, ""} = protocol(S),
- U#diameter_uri{protocol = P};
-scan_opt_protocol("", U) ->
- U.
-
-transport("tcp" ++ S) ->
- {tcp, S};
-transport("sctp" ++ S) ->
- {sctp, S};
-transport("udp" ++ S) ->
- {udp, S}.
-
-protocol("diameter" ++ S) ->
- {diameter, S};
-protocol("radius" ++ S) ->
- {radius, S};
-protocol("tacacs+" ++ S) ->
- {'tacacs+', S}.
-
-is_fqdn(C) ->
- is_digit(C) orelse is_alpha(C) orelse C == $. orelse C == $-.
-
-is_alpha(C) ->
- ($a =< C andalso C =< $z) orelse ($A =< C andalso C =< $Z).
-
-is_digit(C) ->
- $0 =< C andalso C =< $9.
diff --git a/lib/diameter/src/app/diameter_types.hrl b/lib/diameter/src/app/diameter_types.hrl
deleted file mode 100644
index 02bf8a74dd..0000000000
--- a/lib/diameter/src/app/diameter_types.hrl
+++ /dev/null
@@ -1,139 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% Types for function specifications, primarily in diameter.erl. This
-%% has nothing specifically to do with diameter_types.erl.
-%%
-
--type evaluable()
- :: {module(), atom(), list()}
- | fun()
- | nonempty_improper_list(evaluable(), list()). %% [evaluable() | Args]
-
--type app_alias()
- :: any().
-
--type service_name()
- :: any().
-
-%% Diameter basic types
-
--type 'OctetString'() :: iolist().
--type 'Integer32'() :: -2147483647..2147483647.
--type 'Integer64'() :: -9223372036854775807..9223372036854775807.
--type 'Unsigned32'() :: 0..4294967295.
--type 'Unsigned64'() :: 0..18446744073709551615.
--type 'Float32'() :: '-infinity' | float() | infinity.
--type 'Float64'() :: '-infinity' | float() | infinity.
--type 'Grouped'() :: list() | tuple().
-
-%% Diameter derived types
-
--type 'Address'()
- :: inet:ip_address()
- | string().
-
--type 'Time'() :: {{integer(), 1..12, 1..31},
- {0..23, 0..59, 0..59}}.
--type 'UTF8String'() :: iolist().
--type 'DiameterIdentity'() :: 'OctetString'().
--type 'DiameterURI'() :: 'OctetString'().
--type 'Enumerated'() :: 'Integer32'().
--type 'IPFilterRule'() :: 'OctetString'().
--type 'QoSFilterRule'() :: 'OctetString'().
-
-%% Capabilities options/avps on start_service/2 and/or add_transport/2
-
--type capability()
- :: {'Origin-Host', 'DiameterIdentity'()}
- | {'Origin-Realm', 'DiameterIdentity'()}
- | {'Host-IP-Address', ['Address'()]}
- | {'Vendor-Id', 'Unsigned32'()}
- | {'Product-Name', 'UTF8String'()}
- | {'Supported-Vendor-Id', ['Unsigned32'()]}
- | {'Auth-Application-Id', ['Unsigned32'()]}
- | {'Vendor-Specific-Application-Id', ['Grouped'()]}
- | {'Firmware-Revision', 'Unsigned32'()}.
-
-%% Filters for call/4
-
--type peer_filter()
- :: none
- | host
- | realm
- | {host, any|'DiameterIdentity'()}
- | {realm, any|'DiameterIdentity'()}
- | {eval, evaluable()}
- | {neg, peer_filter()}
- | {all, [peer_filter()]}
- | {any, [peer_filter()]}.
-
-%% Options passed to start_service/2
-
--type service_opt()
- :: capability()
- | {application, [application_opt()]}.
-
--type application_opt()
- :: {alias, app_alias()}
- | {dictionary, module()}
- | {module, app_module()}
- | {state, any()}
- | {call_mutates_state, boolean()}
- | {answer_errors, callback|report|discard}.
-
--type app_module()
- :: module()
- | nonempty_improper_list(module(), list()). %% list with module() head
-
-%% Identifier returned by add_transport/2
-
--type transport_ref()
- :: reference().
-
-%% Options passed to add_transport/2
-
--type transport_opt()
- :: {transport_module, atom()}
- | {transport_config, any()}
- | {applications, [app_alias()]}
- | {capabilities, [capability()]}
- | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
- | {reconnect_timer, 'Unsigned32'()}
- | {private, any()}.
-
-%% Predicate passed to remove_transport/2
-
--type transport_pred()
- :: fun((reference(), connect|listen, list()) -> boolean())
- | fun((reference(), list()) -> boolean())
- | fun((list()) -> boolean())
- | reference()
- | list()
- | {connect|listen, transport_pred()}
- | {atom(), atom(), list()}.
-
-%% Options passed to call/4
-
--type call_opt()
- :: {extra, list()}
- | {filter, peer_filter()}
- | {timeout, 'Unsigned32'()}
- | detach.
diff --git a/lib/diameter/src/app/diameter_watchdog.erl b/lib/diameter/src/app/diameter_watchdog.erl
deleted file mode 100644
index b7c1491f4b..0000000000
--- a/lib/diameter/src/app/diameter_watchdog.erl
+++ /dev/null
@@ -1,571 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% This module implements (as a process) the state machine documented
-%% in Appendix A of RFC 3539.
-%%
-
--module(diameter_watchdog).
--behaviour(gen_server).
-
-%% towards diameter_service
--export([start/2]).
-
-%% gen_server callbacks
--export([init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3]).
-
-%% diameter_watchdog_sup callback
--export([start_link/1]).
-
--include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
--define(DEFAULT_TW_INIT, 30000). %% RFC 3539 ch 3.4.1
-
--record(watchdog,
- {%% PCB - Peer Control Block; see RFC 3539, Appendix A
- status = initial :: initial | okay | suspect | down | reopen,
- pending = false :: boolean(),
- tw :: 6000..16#FFFFFFFF | {module(), atom(), list()},
- %% {M,F,A} -> integer() >= 0
- num_dwa = 0 :: -1 | non_neg_integer(),
- %% number of DWAs received during reopen
- %% end PCB
- parent = self() :: pid(),
- transport :: pid(),
- tref :: reference(), %% reference for current watchdog timer
- message_data}). %% term passed into diameter_service with message
-
-%% start/2
-
-start({_,_} = Type, T) ->
- {ok, Pid} = diameter_watchdog_sup:start_child({Type, self(), T}),
- Pid.
-
-start_link(T) ->
- {ok, _} = proc_lib:start_link(?MODULE,
- init,
- [T],
- infinity,
- diameter_lib:spawn_opts(server, [])).
-
-%% ===========================================================================
-%% ===========================================================================
-
-%% init/1
-
-init(T) ->
- proc_lib:init_ack({ok, self()}),
- gen_server:enter_loop(?MODULE, [], i(T)).
-
-i({T, Pid, {ConnT, Opts, SvcName, #diameter_service{applications = Apps,
- capabilities = Caps}
- = Svc}}) ->
- {M,S,U} = now(),
- random:seed(M,S,U),
- putr(restart, {T, Opts, Svc}), %% save seeing it in trace
- putr(dwr, dwr(Caps)), %%
- #watchdog{parent = monitor(Pid),
- transport = monitor(diameter_peer_fsm:start(T, Opts, Svc)),
- tw = proplists:get_value(watchdog_timer,
- Opts,
- ?DEFAULT_TW_INIT),
- message_data = {ConnT, SvcName, Apps}}.
-
-%% handle_call/3
-
-handle_call(_, _, State) ->
- {reply, nok, State}.
-
-%% handle_cast/2
-
-handle_cast(_, State) ->
- {noreply, State}.
-
-%% handle_info/2
-
-handle_info(T, State) ->
- case transition(T, State) of
- ok ->
- {noreply, State};
- #watchdog{status = X} = S ->
- ?LOGC(X =/= State#watchdog.status, transition, X),
- {noreply, S};
- stop ->
- ?LOG(stop, T),
- {stop, {shutdown, T}, State}
- end.
-
-%% terminate/2
-
-terminate(_, _) ->
- ok.
-
-%% code_change/3
-
-code_change(_, State, _) ->
- {ok, State}.
-
-%% ===========================================================================
-%% ===========================================================================
-
-%% transition/2
-%%
-%% The state transitions documented here are extracted from RFC 3539,
-%% the commentary is ours.
-
-%% Service or watchdog is telling the watchdog of an accepting
-%% transport to die after reconnect_timer expiry or reestablished
-%% connection (in another transport process) respectively.
-transition(close, #watchdog{status = down}) ->
- {{accept, _}, _, _} = getr(restart), %% assert
- stop;
-transition(close, #watchdog{}) ->
- ok;
-
-%% Service is asking for the peer to be taken down gracefully.
-transition({shutdown, Pid}, #watchdog{parent = Pid,
- transport = undefined,
- status = S}) ->
- down = S, %% sanity check
- stop;
-transition({shutdown = T, Pid}, #watchdog{parent = Pid,
- transport = TPid}) ->
- TPid ! {T, self()},
- ok;
-
-%% Parent process has died,
-transition({'DOWN', _, process, Pid, _Reason},
- #watchdog{parent = Pid}) ->
- stop;
-
-%% Transport has accepted a connection.
-transition({accepted = T, TPid}, #watchdog{transport = TPid,
- parent = Pid}) ->
- Pid ! {T, self(), TPid},
- ok;
-
-%% Transport is telling us that its impending death isn't failure.
-transition({close, TPid, _Reason}, #watchdog{transport = TPid}) ->
- stop;
-
-%% STATE Event Actions New State
-%% ===== ------ ------- ----------
-%% INITIAL Connection up SetWatchdog() OKAY
-
-%% By construction, the watchdog timer isn't set until we move into
-%% state okay as the result of the Peer State Machine reaching the
-%% Open state.
-%%
-%% If we're an acceptor then we may be resuming a connection that went
-%% down in another acceptor process, in which case this is the
-%% transition below, from down into reopen. That is, it's not until
-%% we know the identity of the peer (ie. now) that we know that we're
-%% in state down rather than initial.
-
-transition({open, TPid, Hosts, T} = Open,
- #watchdog{transport = TPid,
- status = initial,
- parent = Pid}
- = S) ->
- case okay(getr(restart), Hosts) of
- okay ->
- open(Pid, {TPid, T}),
- set_watchdog(S#watchdog{status = okay});
- reopen ->
- transition(Open, S#watchdog{status = down})
- end;
-
-%% DOWN Connection up NumDWA = 0
-%% SendWatchdog()
-%% SetWatchdog()
-%% Pending = TRUE REOPEN
-
-transition({open = P, TPid, _Hosts, T},
- #watchdog{transport = TPid,
- status = down}
- = S) ->
- %% Store the info we need to notify the parent to reopen the
- %% connection after the requisite DWA's are received, at which
- %% time we eraser(open).
- putr(P, {TPid, T}),
- set_watchdog(send_watchdog(S#watchdog{status = reopen,
- num_dwa = 0}));
-
-%% OKAY Connection down CloseConnection()
-%% Failover()
-%% SetWatchdog() DOWN
-%% SUSPECT Connection down CloseConnection()
-%% SetWatchdog() DOWN
-%% REOPEN Connection down CloseConnection()
-%% SetWatchdog() DOWN
-
-transition({'DOWN', _, process, TPid, _},
- #watchdog{transport = TPid,
- status = initial}) ->
- stop;
-
-transition({'DOWN', _, process, Pid, _},
- #watchdog{transport = Pid}
- = S) ->
- failover(S),
- close(S),
- set_watchdog(S#watchdog{status = down,
- pending = false,
- transport = undefined});
-%% Any outstanding pending (or other messages from the transport) will
-%% have arrived before 'DOWN' since the message comes from the same
-%% process. Note that we could also get this message in the initial
-%% state.
-
-%% Incoming message.
-transition({recv, TPid, Name, Pkt}, #watchdog{transport = TPid} = S) ->
- recv(Name, Pkt, S);
-
-%% Current watchdog has timed out.
-transition({timeout, TRef, tw}, #watchdog{tref = TRef} = S) ->
- set_watchdog(timeout(S));
-
-%% Timer was canceled after message was already sent.
-transition({timeout, _, tw}, #watchdog{}) ->
- ok;
-
-%% State query.
-transition({state, Pid}, #watchdog{status = S}) ->
- Pid ! {self(), S},
- ok.
-
-%% ===========================================================================
-
-monitor(Pid) ->
- erlang:monitor(process, Pid),
- Pid.
-
-putr(Key, Val) ->
- put({?MODULE, Key}, Val).
-
-getr(Key) ->
- get({?MODULE, Key}).
-
-eraser(Key) ->
- erase({?MODULE, Key}).
-
-%% encode/1
-
-encode(Msg) ->
- #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Msg),
- Bin.
-
-%% okay/2
-
-okay({{accept, Ref}, _, _}, Hosts) ->
- T = {?MODULE, connection, Ref, Hosts},
- diameter_reg:add(T),
- okay(diameter_reg:match(T));
-%% Register before matching so that at least one of two registering
-%% processes will match the other. (Which can't happen as long as
-%% diameter_peer_fsm guarantees at most one open connection to the same
-%% peer.)
-
-okay({{connect, _}, _, _}, _) ->
- okay.
-
-%% The peer hasn't been connected recently ...
-okay([{_,P}]) ->
- P = self(), %% assert
- okay;
-
-%% ... or it has.
-okay(C) ->
- [_|_] = [P ! close || {_,P} <- C, self() /= P],
- reopen.
-
-%% set_watchdog/1
-
-set_watchdog(#watchdog{tw = TwInit,
- tref = TRef}
- = S) ->
- cancel(TRef),
- S#watchdog{tref = erlang:start_timer(tw(TwInit), self(), tw)}.
-
-cancel(undefined) ->
- ok;
-cancel(TRef) ->
- erlang:cancel_timer(TRef).
-
-tw(T)
- when is_integer(T), T >= 6000 ->
- T - 2000 + (random:uniform(4001) - 1); %% RFC3539 jitter of +/- 2 sec.
-tw({M,F,A}) ->
- apply(M,F,A).
-
-%% open/2
-
-open(Pid, {_,_} = T) ->
- Pid ! {connection_up, self(), T}.
-
-%% failover/1
-
-failover(#watchdog{status = okay,
- parent = Pid}) ->
- Pid ! {connection_down, self()};
-
-failover(_) ->
- ok.
-
-%% close/1
-
-close(#watchdog{status = down}) ->
- ok;
-
-close(#watchdog{parent = Pid}) ->
- {{T, _}, _, _} = getr(restart),
- T == accept andalso (Pid ! {close, self()}).
-
-%% send_watchdog/1
-
-send_watchdog(#watchdog{pending = false,
- transport = TPid}
- = S) ->
- TPid ! {send, encode(getr(dwr))},
- ?LOG(send, 'DWR'),
- S#watchdog{pending = true}.
-
-%% recv/3
-
-recv(Name, Pkt, S) ->
- try rcv(Name, S) of
- #watchdog{} = NS ->
- rcv(Name, Pkt, S),
- NS
- catch
- throw: {?MODULE, throwaway, #watchdog{} = NS} ->
- NS
- end.
-
-%% rcv/3
-
-rcv(N, _, _)
- when N == 'CER';
- N == 'CEA';
- N == 'DWR';
- N == 'DWA';
- N == 'DPR';
- N == 'DPA' ->
- false;
-
-rcv(_, Pkt, #watchdog{transport = TPid,
- message_data = T}) ->
- diameter_service:receive_message(TPid, Pkt, T).
-
-throwaway(S) ->
- throw({?MODULE, throwaway, S}).
-
-%% rcv/2
-
-%% INITIAL Receive DWA Pending = FALSE
-%% Throwaway() INITIAL
-%% INITIAL Receive non-DWA Throwaway() INITIAL
-
-rcv('DWA', #watchdog{status = initial} = S) ->
- throwaway(S#watchdog{pending = false});
-
-rcv(_, #watchdog{status = initial} = S) ->
- throwaway(S);
-
-%% DOWN Receive DWA Pending = FALSE
-%% Throwaway() DOWN
-%% DOWN Receive non-DWA Throwaway() DOWN
-
-rcv('DWA', #watchdog{status = down} = S) ->
- throwaway(S#watchdog{pending = false});
-
-rcv(_, #watchdog{status = down} = S) ->
- throwaway(S);
-
-%% OKAY Receive DWA Pending = FALSE
-%% SetWatchdog() OKAY
-%% OKAY Receive non-DWA SetWatchdog() OKAY
-
-rcv('DWA', #watchdog{status = okay} = S) ->
- set_watchdog(S#watchdog{pending = false});
-
-rcv(_, #watchdog{status = okay} = S) ->
- set_watchdog(S);
-
-%% SUSPECT Receive DWA Pending = FALSE
-%% Failback()
-%% SetWatchdog() OKAY
-%% SUSPECT Receive non-DWA Failback()
-%% SetWatchdog() OKAY
-
-rcv('DWA', #watchdog{status = suspect} = S) ->
- failback(S),
- set_watchdog(S#watchdog{status = okay,
- pending = false});
-
-rcv(_, #watchdog{status = suspect} = S) ->
- failback(S),
- set_watchdog(S#watchdog{status = okay});
-
-%% REOPEN Receive DWA & Pending = FALSE
-%% NumDWA == 2 NumDWA++
-%% Failback() OKAY
-
-rcv('DWA', #watchdog{status = reopen,
- num_dwa = 2 = N,
- parent = Pid}
- = S) ->
- open(Pid, eraser(open)),
- S#watchdog{status = okay,
- num_dwa = N+1,
- pending = false};
-
-%% REOPEN Receive DWA & Pending = FALSE
-%% NumDWA < 2 NumDWA++ REOPEN
-
-rcv('DWA', #watchdog{status = reopen,
- num_dwa = N}
- = S) ->
- S#watchdog{num_dwa = N+1,
- pending = false};
-
-%% REOPEN Receive non-DWA Throwaway() REOPEN
-
-rcv(_, #watchdog{status = reopen} = S) ->
- throwaway(S).
-
-%% failback/1
-
-failback(#watchdog{parent = Pid}) ->
- Pid ! {connection_up, self()}.
-
-%% timeout/1
-%%
-%% The caller sets the watchdog on the return value.
-
-%% OKAY Timer expires & SendWatchdog()
-%% !Pending SetWatchdog()
-%% Pending = TRUE OKAY
-%% REOPEN Timer expires & SendWatchdog()
-%% !Pending SetWatchdog()
-%% Pending = TRUE REOPEN
-
-timeout(#watchdog{status = T,
- pending = false}
- = S)
- when T == okay;
- T == reopen ->
- send_watchdog(S);
-
-%% OKAY Timer expires & Failover()
-%% Pending SetWatchdog() SUSPECT
-
-timeout(#watchdog{status = okay,
- pending = true}
- = S) ->
- failover(S),
- S#watchdog{status = suspect};
-
-%% SUSPECT Timer expires CloseConnection()
-%% SetWatchdog() DOWN
-%% REOPEN Timer expires & CloseConnection()
-%% Pending & SetWatchdog()
-%% NumDWA < 0 DOWN
-
-timeout(#watchdog{status = T,
- pending = P,
- num_dwa = N,
- transport = TPid}
- = S)
- when T == suspect;
- T == reopen, P, N < 0 ->
- exit(TPid, shutdown),
- close(S),
- S#watchdog{status = down};
-
-%% REOPEN Timer expires & NumDWA = -1
-%% Pending & SetWatchdog()
-%% NumDWA >= 0 REOPEN
-
-timeout(#watchdog{status = reopen,
- pending = true,
- num_dwa = N}
- = S)
- when 0 =< N ->
- S#watchdog{num_dwa = -1};
-
-%% DOWN Timer expires AttemptOpen()
-%% SetWatchdog() DOWN
-%% INITIAL Timer expires AttemptOpen()
-%% SetWatchdog() INITIAL
-
-%% RFC 3539, 3.4.1:
-%%
-%% [5] While the connection is in the closed state, the AAA client MUST
-%% NOT attempt to send further watchdog messages on the connection.
-%% However, after the connection is closed, the AAA client continues
-%% to periodically attempt to reopen the connection.
-%%
-%% The AAA client SHOULD wait for the transport layer to report
-%% connection failure before attempting again, but MAY choose to
-%% bound this wait time by the watchdog interval, Tw.
-
-%% Don't bound, restarting the peer process only when the previous
-%% process has died. We only need to handle state down since we start
-%% the first watchdog when transitioning out of initial.
-
-timeout(#watchdog{status = down} = S) ->
- restart(S).
-
-%% restart/1
-
-restart(#watchdog{transport = undefined} = S) ->
- restart(getr(restart), S);
-restart(S) ->
- S.
-
-%% Only restart the transport in the connecting case. For an accepting
-%% transport, we've registered the peer connection when leaving state
-%% initial and this is used by a new accepting process to realize that
-%% it's actually in state down rather then initial when receiving
-%% notification of an open connection.
-
-restart({{connect, _} = T, Opts, Svc}, #watchdog{parent = Pid} = S) ->
- Pid ! {reconnect, self()},
- S#watchdog{transport = monitor(diameter_peer_fsm:start(T, Opts, Svc))};
-restart({{accept, _}, _, _}, S) ->
- S.
-%% Don't currently use Opts/Svc in the accept case but having them in
-%% the process dictionary is helpful if the process dies unexpectedly.
-
-%% dwr/1
-
-dwr(#diameter_caps{origin_host = OH,
- origin_realm = OR,
- origin_state_id = OSI}) ->
- ['DWR', {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Origin-State-Id', OSI}].
diff --git a/lib/diameter/src/app/diameter_watchdog_sup.erl b/lib/diameter/src/app/diameter_watchdog_sup.erl
deleted file mode 100644
index fc837fe4ef..0000000000
--- a/lib/diameter/src/app/diameter_watchdog_sup.erl
+++ /dev/null
@@ -1,60 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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%
-%%
-
-%%
-%% Supervisor for all watchdog processes.
-%%
-
--module(diameter_watchdog_sup).
-
--behaviour(supervisor).
-
-%% interface
--export([start_link/0, %% supervisor start
- start_child/1]). %% watchdog start
-
--export([init/1]).
-
--define(NAME, ?MODULE). %% supervisor name
-
-%% start_link/0
-
-start_link() ->
- SupName = {local, ?NAME},
- supervisor:start_link(SupName, ?MODULE, []).
-
-%% start_child/1
-%%
-%% Start a watchdog process.
-
-start_child(T) ->
- supervisor:start_child(?NAME, [T]).
-
-%% init/1
-
-init([]) ->
- Mod = diameter_watchdog,
- Flags = {simple_one_for_one, 0, 1},
- ChildSpec = {Mod,
- {Mod, start_link, []},
- temporary,
- 1000,
- worker,
- [Mod]},
- {ok, {Flags, [ChildSpec]}}.
diff --git a/lib/diameter/src/app/modules.mk b/lib/diameter/src/app/modules.mk
deleted file mode 100644
index 2b602cd713..0000000000
--- a/lib/diameter/src/app/modules.mk
+++ /dev/null
@@ -1,69 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-
-DICTIONARIES = \
- base_rfc3588 \
- base_accounting \
- relay
-
-RUNTIME_MODULES = \
- diameter \
- diameter_app \
- diameter_capx \
- diameter_config \
- diameter_codec \
- diameter_dict \
- diameter_lib \
- diameter_misc_sup \
- diameter_peer \
- diameter_peer_fsm \
- diameter_peer_fsm_sup \
- diameter_reg \
- diameter_service \
- diameter_service_sup \
- diameter_session \
- diameter_stats \
- diameter_sup \
- diameter_sync \
- diameter_types \
- diameter_watchdog \
- diameter_watchdog_sup
-
-HELP_MODULES = \
- diameter_callback \
- diameter_dbg \
- diameter_info
-
-INTERNAL_HRL_FILES = \
- diameter_internal.hrl \
- diameter_types.hrl
-
-EXTERNAL_HRL_FILES = \
- ../../include/diameter.hrl \
- ../../include/diameter_gen.hrl
-
-EXAMPLE_FILES = \
- ../../examples/GNUmakefile \
- ../../examples/peer.erl \
- ../../examples/client.erl \
- ../../examples/client_cb.erl \
- ../../examples/server.erl \
- ../../examples/server_cb.erl \
- ../../examples/relay.erl \
- ../../examples/relay_cb.erl
diff --git a/lib/diameter/src/base/diameter.app.src b/lib/diameter/src/base/diameter.app.src
new file mode 100644
index 0000000000..c092fdb022
--- /dev/null
+++ b/lib/diameter/src/base/diameter.app.src
@@ -0,0 +1,28 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+{application, diameter,
+ [{description, "Diameter protocol"},
+ {vsn, "%VSN%"},
+ {modules, [%MODULES%]},
+ {registered, []},
+ {applications, [stdlib, kernel]},
+ {env, []},
+ {mod, {diameter_app, []}}
+ ]}.
diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src
new file mode 100644
index 0000000000..6d8ceadb92
--- /dev/null
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -0,0 +1,47 @@
+%% This is an -*- erlang -*- file.
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+{"%VSN%",
+ [
+ {"0.9",
+ [
+ {load_module, diameter, soft_purge, soft_purge, []},
+ {load_module, diameter_capx, soft_purge, soft_purge, []},
+ {load_module, diameter_codec, soft_purge, soft_purge, [diameter_lib]},
+ {load_module, diameter_lib, soft_purge, soft_purge, []},
+ {load_module, diameter_types, soft_purge, soft_purge, []},
+ {load_module, diameter_gen_base_accounting, soft_purge, soft_purge, []},
+ {load_module, diameter_gen_base_rfc3588, soft_purge, soft_purge, []},
+ {load_module, diameter_gen_relay, soft_purge, soft_purge, []},
+ {update, diameter_service, soft, soft_purge, soft_purge, [diameter_lib]},
+ {update, diameter_config, soft, soft_purge, soft_purge, []},
+ {update, diameter_peer, soft, soft_purge, soft_purge, []},
+ {update, diameter_peer_fsm, soft, soft_purge, soft_purge, [diameter_lib]},
+ {update, diameter_reg, soft, soft_purge, soft_purge, []},
+ {update, diameter_sctp, soft, soft_purge, soft_purge, []},
+ {update, diameter_stats, soft, soft_purge, soft_purge, []},
+ {update, diameter_sync, soft, soft_purge, soft_purge, []},
+ {update, diameter_watchdog, soft, soft_purge, soft_purge, [diameter_lib]}
+ ]
+ }
+ ],
+ [
+ ]
+}.
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
new file mode 100644
index 0000000000..2f721421d8
--- /dev/null
+++ b/lib/diameter/src/base/diameter.erl
@@ -0,0 +1,190 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter).
+
+%% Configuration.
+-export([start_service/2,
+ stop_service/1,
+ add_transport/2,
+ remove_transport/2,
+ subscribe/1,
+ unsubscribe/1]).
+
+%% Traffic.
+-export([session_id/1,
+ origin_state_id/0,
+ call/3,
+ call/4]).
+
+%% Information.
+-export([services/0,
+ service_info/2]).
+
+%% Start/stop the application. In a "real" application this should
+%% typically be a consequence of specifying diameter in a release file
+%% rather than by calling start/stop explicitly.
+-export([start/0,
+ stop/0]).
+
+-include("diameter_internal.hrl").
+-include("diameter_types.hrl").
+
+%%% --------------------------------------------------------------------------
+%%% start/0
+%%% --------------------------------------------------------------------------
+
+-spec start()
+ -> ok
+ | {error, term()}.
+
+start() ->
+ application:start(?APPLICATION).
+
+%%% --------------------------------------------------------------------------
+%%% stop/0
+%%% --------------------------------------------------------------------------
+
+-spec stop()
+ -> ok
+ | {error, term()}.
+
+stop() ->
+ application:stop(?APPLICATION).
+
+%%% --------------------------------------------------------------------------
+%%% start_service/2
+%%% --------------------------------------------------------------------------
+
+-spec start_service(service_name(), [service_opt()])
+ -> ok
+ | {error, term()}.
+
+start_service(SvcName, Opts)
+ when is_list(Opts) ->
+ diameter_config:start_service(SvcName, Opts).
+
+%%% --------------------------------------------------------------------------
+%%% stop_service/1
+%%% --------------------------------------------------------------------------
+
+-spec stop_service(service_name())
+ -> ok
+ | {error, term()}.
+
+stop_service(SvcName) ->
+ diameter_config:stop_service(SvcName).
+
+%%% --------------------------------------------------------------------------
+%%% services/0
+%%% --------------------------------------------------------------------------
+
+-spec services()
+ -> [service_name()].
+
+services() ->
+ [Name || {Name, _} <- diameter_service:services()].
+
+%%% --------------------------------------------------------------------------
+%%% service_info/2
+%%% --------------------------------------------------------------------------
+
+-spec service_info(service_name(), atom() | [atom()])
+ -> any().
+
+service_info(SvcName, Option) ->
+ diameter_service:info(SvcName, Option).
+
+%%% --------------------------------------------------------------------------
+%%% add_transport/3
+%%% --------------------------------------------------------------------------
+
+-spec add_transport(service_name(), {listen|connect, [transport_opt()]})
+ -> {ok, transport_ref()}
+ | {error, term()}.
+
+add_transport(SvcName, {T, Opts} = Cfg)
+ when is_list(Opts), (T == connect orelse T == listen) ->
+ diameter_config:add_transport(SvcName, Cfg).
+
+%%% --------------------------------------------------------------------------
+%%% remove_transport/2
+%%% --------------------------------------------------------------------------
+
+-spec remove_transport(service_name(), transport_pred())
+ -> ok | {error, term()}.
+
+remove_transport(SvcName, Pred) ->
+ diameter_config:remove_transport(SvcName, Pred).
+
+%%% --------------------------------------------------------------------------
+%%% # subscribe(SvcName)
+%%%
+%%% Description: Subscribe to #diameter_event{} messages for the specified
+%%% service.
+%%% --------------------------------------------------------------------------
+
+-spec subscribe(service_name())
+ -> true.
+
+subscribe(SvcName) ->
+ diameter_service:subscribe(SvcName).
+
+%%% --------------------------------------------------------------------------
+%%% # unsubscribe(SvcName)
+%%% --------------------------------------------------------------------------
+
+-spec unsubscribe(service_name())
+ -> true.
+
+unsubscribe(SvcName) ->
+ diameter_service:unsubscribe(SvcName).
+
+%%% ----------------------------------------------------------
+%%% # session_id/1
+%%% ----------------------------------------------------------
+
+-spec session_id('DiameterIdentity'())
+ -> 'OctetString'().
+
+session_id(Ident) ->
+ diameter_session:session_id(Ident).
+
+%%% ----------------------------------------------------------
+%%% # origin_state_id/0
+%%% ----------------------------------------------------------
+
+-spec origin_state_id()
+ -> 'Unsigned32'().
+
+origin_state_id() ->
+ diameter_session:origin_state_id().
+
+%%% --------------------------------------------------------------------------
+%%% # call/[34]
+%%% --------------------------------------------------------------------------
+
+-spec call(service_name(), app_alias(), any(), [call_opt()])
+ -> any().
+
+call(SvcName, App, Message, Options) ->
+ diameter_service:call(SvcName, {alias, App}, Message, Options).
+
+call(SvcName, App, Message) ->
+ call(SvcName, App, Message, []).
diff --git a/lib/diameter/src/base/diameter_app.erl b/lib/diameter/src/base/diameter_app.erl
new file mode 100644
index 0000000000..600f7ff04d
--- /dev/null
+++ b/lib/diameter/src/base/diameter_app.erl
@@ -0,0 +1,36 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_app).
+
+-behaviour(application).
+
+%% application callbacks
+-export([start/2,
+ stop/1]).
+
+%% start/2
+
+start(_Type, _Args) ->
+ diameter_sup:start_link().
+
+%% stop/1
+
+stop(_) ->
+ ok.
diff --git a/lib/diameter/src/base/diameter_callback.erl b/lib/diameter/src/base/diameter_callback.erl
new file mode 100644
index 0000000000..6d5c8cdca1
--- /dev/null
+++ b/lib/diameter/src/base/diameter_callback.erl
@@ -0,0 +1,91 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% A minimal application callback module.
+%%
+
+-module(diameter_callback).
+
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/4,
+ prepare_request/3,
+ prepare_retransmit/3,
+ handle_request/3,
+ handle_answer/4,
+ handle_error/4]).
+
+-include_lib("diameter/include/diameter.hrl").
+
+%%% ----------------------------------------------------------
+%%% # peer_up/3
+%%% ----------------------------------------------------------
+
+peer_up(_Svc, _Peer, State) ->
+ State.
+
+%%% ----------------------------------------------------------
+%%% # peer_down/3
+%%% ----------------------------------------------------------
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%%% ----------------------------------------------------------
+%%% # pick_peer/4
+%%% ----------------------------------------------------------
+
+pick_peer([Peer|_], _, _SvcName, _State) ->
+ {ok, Peer}.
+
+%%% ----------------------------------------------------------
+%%% # prepare_request/3
+%%% ----------------------------------------------------------
+
+prepare_request(Pkt, _SvcName, _Peer) ->
+ {send, Pkt}.
+
+%%% ----------------------------------------------------------
+%%% # prepare_retransmit/3
+%%% ----------------------------------------------------------
+
+prepare_retransmit(Pkt, _SvcName, _Peer) ->
+ {send, Pkt}.
+
+%%% ----------------------------------------------------------
+%%% # handle_request/3
+%%% ----------------------------------------------------------
+
+handle_request(_Pkt, _SvcName, _Peer) ->
+ {protocol_error, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED
+
+%%% ----------------------------------------------------------
+%%% # handle_answer/4
+%%% ----------------------------------------------------------
+
+handle_answer(#diameter_packet{msg = Ans}, _Req, _SvcName, _Peer) ->
+ Ans.
+
+%%% ---------------------------------------------------------------------------
+%%% # handle_error/4
+%%% ---------------------------------------------------------------------------
+
+handle_error(Reason, _Req, _SvcName, _Peer) ->
+ {error, Reason}.
diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl
new file mode 100644
index 0000000000..138e76411e
--- /dev/null
+++ b/lib/diameter/src/base/diameter_capx.erl
@@ -0,0 +1,405 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% This module builds CER and CEA records for use during capabilities
+%% exchange. All of a CER/CEA is built from AVP values configured on
+%% the service in question but values for Supported-Vendor-Id,
+%% Vendor-Specific-Application-Id, Auth-Application-Id and
+%% Acct-Application-id are also obtained using an older method that
+%% remains only for backwards compatibility. With this method, each
+%% dictionary module was required to export a cer/0 that returned a
+%% diameter_base_CER record (or corresponding list, although the list
+%% is also a later addition). Each returned CER contributes its member
+%% values for the aforementioned four AVPs to the resulting CER, with
+%% remaining AVP's either unspecified or identical to those configured
+%% on the service. Auth-Application-Id and Acct-Application-id were
+%% originally treated a little differently, each callback being
+%% required to return either no value of the same value as the other
+%% callbacks, but this coupled the callback modules unnecessarily. (A
+%% union is backwards compatible to boot.)
+%%
+%% Values obtained from the service and callbacks are all included
+%% when building a CER. Older code with only callback can continue to
+%% use them, newer code should probably stick to service configuration
+%% (since this is more explicit) or mix at their own peril.
+%%
+%% The cer/0 callback is now undocumented (despite never being fully
+%% documented to begin with) and should be considered deprecated even
+%% by those poor souls still using it.
+%%
+
+-module(diameter_capx).
+
+-export([build_CER/1,
+ recv_CER/2,
+ recv_CEA/2,
+ make_caps/2]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+-include("diameter_types.hrl").
+-include("diameter_gen_base_rfc3588.hrl").
+
+-define(SUCCESS, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
+-define(NOAPP, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_NO_COMMON_APPLICATION').
+-define(NOSECURITY, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_NO_COMMON_SECURITY').
+
+-define(NO_INBAND_SECURITY, 0).
+-define(TLS, 1).
+
+%% ===========================================================================
+
+-type tried(T) :: {ok, T} | {error, {term(), list()}}.
+
+-spec build_CER(#diameter_caps{})
+ -> tried(#diameter_base_CER{}).
+
+build_CER(Caps) ->
+ try_it([fun bCER/1, Caps]).
+
+-spec recv_CER(#diameter_base_CER{}, #diameter_service{})
+ -> tried({['Unsigned32'()], #diameter_caps{}, #diameter_base_CEA{}}).
+
+recv_CER(CER, Svc) ->
+ try_it([fun rCER/2, CER, Svc]).
+
+-spec recv_CEA(#diameter_base_CEA{}, #diameter_service{})
+ -> tried({['Unsigned32'()], ['Unsigned32'()], #diameter_caps{}}).
+
+recv_CEA(CEA, Svc) ->
+ try_it([fun rCEA/2, CEA, Svc]).
+
+make_caps(Caps, Opts) ->
+ try_it([fun mk_caps/2, Caps, Opts]).
+
+%% ===========================================================================
+%% ===========================================================================
+
+try_it([Fun | Args]) ->
+ try apply(Fun, Args) of
+ T -> {ok, T}
+ catch
+ throw: ?FAILURE(Reason) -> {error, {Reason, Args}}
+ end.
+
+%% mk_caps/2
+
+mk_caps(Caps0, Opts) ->
+ {Caps, _} = lists:foldl(fun set_cap/2,
+ {Caps0, #diameter_caps{_ = false}},
+ Opts),
+ Caps.
+
+-define(SC(K,F),
+ set_cap({K, Val}, {Caps, #diameter_caps{F = false} = C}) ->
+ {Caps#diameter_caps{F = cap(K, Val)}, C#diameter_caps{F = true}}).
+
+?SC('Origin-Host', origin_host);
+?SC('Origin-Realm', origin_realm);
+?SC('Host-IP-Address', host_ip_address);
+?SC('Vendor-Id', vendor_id);
+?SC('Product-Name', product_name);
+?SC('Origin-State-Id', origin_state_id);
+?SC('Supported-Vendor-Id', supported_vendor_id);
+?SC('Auth-Application-Id', auth_application_id);
+?SC('Inband-Security-Id', inband_security_id);
+?SC('Acct-Application-Id', acct_application_id);
+?SC('Vendor-Specific-Application-Id', vendor_specific_application_id);
+?SC('Firmware-Revision', firmware_revision);
+
+set_cap({Key, _}, _) ->
+ ?THROW({duplicate, Key}).
+
+cap(K, V)
+ when K == 'Origin-Host';
+ K == 'Origin-Realm';
+ K == 'Vendor-Id';
+ K == 'Product-Name' ->
+ V;
+
+cap('Host-IP-Address', Vs)
+ when is_list(Vs) ->
+ lists:map(fun ipaddr/1, Vs);
+
+cap('Firmware-Revision', V) ->
+ [V];
+
+cap(_, Vs)
+ when is_list(Vs) ->
+ Vs;
+
+cap(K, V) ->
+ ?THROW({invalid, K, V}).
+
+ipaddr(A) ->
+ try
+ diameter_lib:ipaddr(A)
+ catch
+ error: {invalid_address, _} = T ->
+ ?THROW(T)
+ end.
+
+%% bCER/1
+%%
+%% Build a CER record to send to a remote peer.
+
+%% Use the fact that diameter_caps has the same field names as CER.
+bCER(#diameter_caps{} = Rec) ->
+ #diameter_base_CER{}
+ = list_to_tuple([diameter_base_CER | tl(tuple_to_list(Rec))]).
+
+%% rCER/2
+%%
+%% Build a CEA record to send to a remote peer in response to an
+%% incoming CER. RFC 3588 gives no guidance on what should be sent
+%% here: should we advertise applications that the peer hasn't sent in
+%% its CER (aside from the relay application) or not? If we send
+%% applications that the peer hasn't advertised then the peer may have
+%% to be aware of the possibility. If we don't then we just look like
+%% a server that supports a subset (possibly) of what the client
+%% advertised, so this feels like the path of least incompatibility.
+%% However, the current draft standard (draft-ietf-dime-rfc3588bis-26,
+%% expires 24 July 2011) says this in section 5.3, Capabilities
+%% Exchange:
+%%
+%% The receiver of the Capabilities-Exchange-Request (CER) MUST
+%% determine common applications by computing the intersection of its
+%% own set of supported Application Id against all of the application
+%% identifier AVPs (Auth-Application-Id, Acct-Application-Id and Vendor-
+%% Specific-Application-Id) present in the CER. The value of the
+%% Vendor-Id AVP in the Vendor-Specific-Application-Id MUST NOT be used
+%% during computation. The sender of the Capabilities-Exchange-Answer
+%% (CEA) SHOULD include all of its supported applications as a hint to
+%% the receiver regarding all of its application capabilities.
+%%
+%% Both RFC and the draft also say this:
+%%
+%% The receiver only issues commands to its peers that have advertised
+%% support for the Diameter application that defines the command. A
+%% Diameter node MUST cache the supported applications in order to
+%% ensure that unrecognized commands and/or AVPs are not unnecessarily
+%% sent to a peer.
+%%
+%% That is, each side sends all of its capabilities and is responsible for
+%% not sending commands that the peer doesn't support.
+
+%% 6.10. Inband-Security-Id AVP
+%%
+%% NO_INBAND_SECURITY 0
+%% This peer does not support TLS. This is the default value, if the
+%% AVP is omitted.
+%%
+%% TLS 1
+%% This node supports TLS security, as defined by [TLS].
+
+rCER(CER, #diameter_service{capabilities = LCaps} = Svc) ->
+ #diameter_base_CEA{}
+ = CEA
+ = cea_from_cer(bCER(LCaps)),
+
+ RCaps = capx_to_caps(CER),
+ SApps = common_applications(LCaps, RCaps, Svc),
+
+ {SApps,
+ RCaps,
+ build_CEA(SApps,
+ LCaps,
+ RCaps,
+ CEA#diameter_base_CEA{'Result-Code' = ?SUCCESS})}.
+
+%% TODO: 5.3 of RFC 3588 says we MUST return DIAMETER_NO_COMMON_APPLICATION
+%% in the CEA and SHOULD disconnect the transport. However, we have
+%% no way to guarantee the send before disconnecting.
+
+build_CEA([], _, _, CEA) ->
+ CEA#diameter_base_CEA{'Result-Code' = ?NOAPP};
+
+build_CEA(_, LCaps, RCaps, CEA) ->
+ case common_security(LCaps, RCaps) of
+ [] ->
+ CEA#diameter_base_CEA{'Result-Code' = ?NOSECURITY};
+ [_] = IS ->
+ CEA#diameter_base_CEA{'Inband-Security-Id' = IS}
+ end.
+
+%% common_security/2
+
+common_security(#diameter_caps{inband_security_id = LS},
+ #diameter_caps{inband_security_id = RS}) ->
+ cs(LS, RS).
+
+%% Unspecified is equivalent to NO_INBAND_SECURITY.
+cs([], RS) ->
+ cs([?NO_INBAND_SECURITY], RS);
+cs(LS, []) ->
+ cs(LS, [?NO_INBAND_SECURITY]);
+
+%% Agree on TLS if both parties support it. When sending CEA, this is
+%% to ensure the peer is clear that we will be expecting a TLS
+%% handshake since there is no ssl:maybe_accept that would allow the
+%% peer to choose between TLS or not upon reception of our CEA. When
+%% receiving CEA it deals with a server that isn't explicit about its choice.
+%% TODO: Make the choice configurable.
+cs(LS, RS) ->
+ Is = ordsets:to_list(ordsets:intersection(ordsets:from_list(LS),
+ ordsets:from_list(RS))),
+ case lists:member(?TLS, Is) of
+ true ->
+ [?TLS];
+ false when [] == Is ->
+ Is;
+ false ->
+ [hd(Is)] %% probably NO_INBAND_SECURITY
+ end.
+%% The only two values defined by RFC 3588 are NO_INBAND_SECURITY and
+%% TLS but don't enforce this. In theory this allows some other
+%% security mechanism we don't have to know about, although in
+%% practice something there may be a need for more synchronization
+%% than notification by way of an event subscription offers.
+
+%% cea_from_cer/1
+
+%% CER is a subset of CEA, the latter adding Result-Code and a few
+%% more AVP's.
+cea_from_cer(#diameter_base_CER{} = CER) ->
+ lists:foldl(fun(F,A) -> to_cea(CER, F, A) end,
+ #diameter_base_CEA{},
+ record_info(fields, diameter_base_CER)).
+
+to_cea(CER, Field, CEA) ->
+ try ?BASE:'#get-'(Field, CER) of
+ V -> ?BASE:'#set-'({Field, V}, CEA)
+ catch
+ error: _ -> CEA
+ end.
+
+%% rCEA/2
+
+rCEA(#diameter_base_CEA{'Result-Code' = RC}
+ = CEA,
+ #diameter_service{capabilities = LCaps}
+ = Svc) ->
+ RC == ?SUCCESS orelse ?THROW({'Result-Code', RC}),
+
+ RCaps = capx_to_caps(CEA),
+ SApps = common_applications(LCaps, RCaps, Svc),
+
+ [] == SApps andalso ?THROW(no_common_applications),
+
+ IS = common_security(LCaps, RCaps),
+
+ [] == IS andalso ?THROW(no_common_security),
+
+ {SApps, IS, RCaps};
+
+rCEA(CEA, _Svc) ->
+ ?THROW({invalid, CEA}).
+
+%% capx_to_caps/1
+
+capx_to_caps(#diameter_base_CEA{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Host-IP-Address' = IP,
+ 'Vendor-Id' = VId,
+ 'Product-Name' = PN,
+ 'Origin-State-Id' = OSI,
+ 'Supported-Vendor-Id' = SV,
+ 'Auth-Application-Id' = Auth,
+ 'Inband-Security-Id' = IS,
+ 'Acct-Application-Id' = Acct,
+ 'Vendor-Specific-Application-Id' = VSA,
+ 'Firmware-Revision' = FR,
+ 'AVP' = X}) ->
+ #diameter_caps{origin_host = OH,
+ origin_realm = OR,
+ vendor_id = VId,
+ product_name = PN,
+ origin_state_id = OSI,
+ host_ip_address = IP,
+ supported_vendor_id = SV,
+ auth_application_id = Auth,
+ inband_security_id = IS,
+ acct_application_id = Acct,
+ vendor_specific_application_id = VSA,
+ firmware_revision = FR,
+ avp = X};
+
+capx_to_caps(#diameter_base_CER{} = CER) ->
+ capx_to_caps(cea_from_cer(CER)).
+
+%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+
+%% common_applications/3
+%%
+%% Identify the (local) applications to be supported on the connection
+%% in question.
+
+common_applications(LCaps, RCaps, #diameter_service{applications = Apps}) ->
+ LA = app_union(LCaps),
+ RA = app_union(RCaps),
+
+ lists:foldl(fun(I,A) -> ca(I, Apps, RA, A) end, [], LA).
+
+ca(Id, Apps, RA, Acc) ->
+ Relay = lists:member(?APP_ID_RELAY, RA),
+ #diameter_app{alias = Alias} = find_app(Id, Apps),
+ tcons(Relay %% peer is a relay
+ orelse ?APP_ID_RELAY == Id %% we're a relay
+ orelse lists:member(Id, RA), %% app is supported by the peer
+ Id,
+ Alias,
+ Acc).
+%% 5.3 of the RFC states that a peer advertising itself as a relay must
+%% be interpreted as having common applications.
+
+%% Extract the list of all application identifiers from Auth-Application-Id,
+%% Acct-Application-Id and Vendor-Specific-Application-Id.
+app_union(#diameter_caps{auth_application_id = U,
+ acct_application_id = C,
+ vendor_specific_application_id = V}) ->
+ set_list(U ++ C ++ lists:flatmap(fun vsa_apps/1, V)).
+
+vsa_apps(#'diameter_base_Vendor-Specific-Application-Id'
+ {'Auth-Application-Id' = U,
+ 'Acct-Application-Id' = C}) ->
+ U ++ C;
+vsa_apps(L) ->
+ Rec = ?BASE:'#new-'('diameter_base_Vendor-Specific-Application-Id', L),
+ vsa_apps(Rec).
+
+%% It's a configuration error for a locally advertised application not
+%% to be represented in Apps. Don't just match on lists:keyfind/3 in
+%% order to generate a more helpful error.
+find_app(Id, Apps) ->
+ case lists:keyfind(Id, #diameter_app.id, Apps) of
+ #diameter_app{} = A ->
+ A;
+ false ->
+ ?THROW({app_not_configured, Id})
+ end.
+
+set_list(L) ->
+ sets:to_list(sets:from_list(L)).
+
+tcons(true, K, V, Acc) ->
+ [{K,V} | Acc];
+tcons(false, _, _, Acc) ->
+ Acc.
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
new file mode 100644
index 0000000000..d88f42fb7c
--- /dev/null
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -0,0 +1,561 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_codec).
+
+-export([encode/2,
+ decode/2,
+ decode/3,
+ collect_avps/1,
+ decode_header/1,
+ sequence_numbers/1,
+ hop_by_hop_id/2,
+ msg_name/1,
+ msg_id/1]).
+
+%% Towards generated encoders (from diameter_gen.hrl).
+-export([pack_avp/1,
+ pack_avp/2]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+-define(MASK(N,I), ((I) band (1 bsl (N)))).
+
+%% 0 1 2 3
+%% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | Version | Message Length |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | command flags | Command-Code |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | Application-ID |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | Hop-by-Hop Identifier |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | End-to-End Identifier |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | AVPs ...
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-
+
+%%% ---------------------------------------------------------------------------
+%%% # encode/[2-4]
+%%% ---------------------------------------------------------------------------
+
+encode(Mod, #diameter_packet{} = Pkt) ->
+ try
+ e(Mod, Pkt)
+ catch
+ error: Reason ->
+ %% Be verbose rather than letting the emulator truncate the
+ %% error report.
+ X = {Reason, ?STACK},
+ diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}),
+ exit(X)
+ end;
+
+encode(Mod, Msg) ->
+ Seq = diameter_session:sequence(),
+ Hdr = #diameter_header{version = ?DIAMETER_VERSION,
+ end_to_end_id = Seq,
+ hop_by_hop_id = Seq},
+ encode(Mod, #diameter_packet{header = Hdr,
+ msg = Msg}).
+
+e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
+ Avps = encode_avps(As),
+ Length = size(Avps) + 20,
+
+ #diameter_header{version = Vsn,
+ cmd_code = Code,
+ application_id = Aid,
+ hop_by_hop_id = Hid,
+ end_to_end_id = Eid}
+ = Hdr,
+
+ Flags = make_flags(0, Hdr),
+
+ Pkt#diameter_packet{bin = <>};
+
+e(Mod0, #diameter_packet{header = Hdr, msg = Msg} = Pkt) ->
+ #diameter_header{version = Vsn,
+ hop_by_hop_id = Hid,
+ end_to_end_id = Eid}
+ = Hdr,
+
+ {Mod, MsgName} = rec2msg(Mod0, Msg),
+ {Code, Flags0, Aid} = msg_header(Mod, MsgName, Hdr),
+ Flags = make_flags(Flags0, Hdr),
+
+ Avps = encode_avps(Mod, MsgName, values(Msg)),
+ Length = size(Avps) + 20,
+
+ Pkt#diameter_packet{header = Hdr#diameter_header
+ {length = Length,
+ cmd_code = Code,
+ application_id = Aid,
+ is_request = 0 /= ?MASK(7, Flags),
+ is_proxiable = 0 /= ?MASK(6, Flags),
+ is_error = 0 /= ?MASK(5, Flags),
+ is_retransmitted = 0 /= ?MASK(4, Flags)},
+ bin = <>}.
+
+%% make_flags/2
+
+make_flags(Flags0, #diameter_header{is_request = R,
+ is_proxiable = P,
+ is_error = E,
+ is_retransmitted = T}) ->
+ {Flags, 3} = lists:foldl(fun(B,{F,N}) -> {mf(B,F,N), N-1} end,
+ {Flags0, 7},
+ [R,P,E,T]),
+ Flags.
+
+mf(undefined, F, _) ->
+ F;
+mf(B, F, N) -> %% reset the affected bit
+ (F bxor (F band (1 bsl N))) bor bit(B, N).
+
+bit(true, N) -> 1 bsl N;
+bit(false, _) -> 0.
+
+%% values/1
+
+values([H|T])
+ when is_atom(H) ->
+ T;
+values(Avps) ->
+ Avps.
+
+%% encode_avps/3
+
+%% Specifying values as a #diameter_avp list bypasses arity and other
+%% checks: the values are expected to be already encoded and the AVP's
+%% presented are simply sent. This is needed for relay agents, since
+%% these have to be able to resend whatever comes.
+
+%% Message as a list of #diameter_avp{} ...
+encode_avps(_, _, [#diameter_avp{} | _] = Avps) ->
+ encode_avps(reorder(Avps, [], Avps));
+
+%% ... or as a tuple list or record.
+encode_avps(Mod, MsgName, Values) ->
+ Mod:encode_avps(MsgName, Values).
+
+%% reorder/1
+
+reorder([#diameter_avp{index = 0} | _] = Avps, Acc, _) ->
+ Avps ++ Acc;
+
+reorder([#diameter_avp{index = N} = A | Avps], Acc, _)
+ when is_integer(N) ->
+ lists:reverse(Avps, [A | Acc]);
+
+reorder([H | T], Acc, Avps) ->
+ reorder(T, [H | Acc], Avps);
+
+reorder([], Acc, _) ->
+ Acc.
+
+%% encode_avps/1
+
+encode_avps(Avps) ->
+ list_to_binary(lists:map(fun pack_avp/1, Avps)).
+
+%% msg_header/3
+
+msg_header(Mod, MsgName, Header) ->
+ {Code, Flags, ApplId} = h(Mod, MsgName, Header),
+ {Code, p(Flags, Header), ApplId}.
+
+%% 6.2 of 3588 requires the same 'P' bit on an answer as on the
+%% request.
+
+p(Flags, #diameter_header{is_request = true,
+ is_proxiable = P}) ->
+ Flags band (2#10110000 bor choose(P, 2#01000000, 0));
+p(Flags, _) ->
+ Flags.
+
+h(Mod, 'answer-message' = MsgName, Header) ->
+ ?BASE = Mod,
+ #diameter_header{cmd_code = Code} = Header,
+ {_, Flags, ApplId} = ?BASE:msg_header(MsgName),
+ {Code, Flags, ApplId};
+
+h(Mod, MsgName, _) ->
+ Mod:msg_header(MsgName).
+
+%% rec2msg/2
+
+rec2msg(_, ['answer-message' = M | _]) ->
+ {?BASE, M};
+
+rec2msg(Mod, [MsgName|_])
+ when is_atom(MsgName) ->
+ {Mod, MsgName};
+
+rec2msg(Mod, Rec) ->
+ R = element(1, Rec),
+ A = 'answer-message',
+ case ?BASE:msg2rec(A) of
+ R ->
+ {?BASE, A};
+ _ ->
+ {Mod, Mod:rec2msg(R)}
+ end.
+
+%%% ---------------------------------------------------------------------------
+%%% # decode/2
+%%% ---------------------------------------------------------------------------
+
+%% Unsuccessfully decoded AVPs will be placed in #diameter_packet.errors.
+
+decode(Mod, Pkt) ->
+ decode(Mod:id(), Mod, Pkt).
+
+%% If we're a relay application then just extract the avp's without
+%% any decoding of their data since we don't know the application in
+%% question.
+decode(?APP_ID_RELAY, _, #diameter_packet{} = Pkt) ->
+ case collect_avps(Pkt) of
+ {Bs, As} ->
+ Pkt#diameter_packet{avps = As,
+ errors = [Bs]};
+ As ->
+ Pkt#diameter_packet{avps = As}
+ end;
+
+%% Otherwise decode using the dictionary.
+decode(_, Mod, #diameter_packet{header = Hdr} = Pkt)
+ when is_atom(Mod) ->
+ #diameter_header{cmd_code = CmdCode,
+ is_request = IsRequest,
+ is_error = IsError}
+ = Hdr,
+
+ {M, MsgName} = if IsError andalso not IsRequest ->
+ {?BASE, 'answer-message'};
+ true ->
+ {Mod, Mod:msg_name(CmdCode, IsRequest)}
+ end,
+
+ decode_avps(MsgName, M, Pkt, collect_avps(Pkt));
+
+decode(Id, Mod, Bin)
+ when is_bitstring(Bin) ->
+ decode(Id, Mod, #diameter_packet{header = decode_header(Bin), bin = Bin}).
+
+decode_avps(MsgName, Mod, Pkt, {Bs, Avps}) -> %% invalid avp bits ...
+ ?LOG(invalid, Pkt#diameter_packet.bin),
+ #diameter_packet{errors = Failed}
+ = P
+ = decode_avps(MsgName, Mod, Pkt, Avps),
+ P#diameter_packet{errors = [Bs | Failed]};
+
+decode_avps('', Mod, Pkt, Avps) -> %% unknown message ...
+ ?LOG(unknown, {Mod, Pkt#diameter_packet.header}),
+ Pkt#diameter_packet{avps = lists:reverse(Avps),
+ errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED
+%% msg = undefined identifies this case.
+
+decode_avps(MsgName, Mod, Pkt, Avps) -> %% ... or not
+ {Rec, As, Failed} = Mod:decode_avps(MsgName, Avps),
+ ?LOGC([] /= Failed, failed, {Mod, Failed}),
+ Pkt#diameter_packet{msg = Rec,
+ errors = Failed,
+ avps = As}.
+
+%%% ---------------------------------------------------------------------------
+%%% # decode_header/1
+%%% ---------------------------------------------------------------------------
+
+decode_header(<>) ->
+ <>
+ = CmdFlags,
+ %% 3588 (ch 3) says that reserved bits MUST be set to 0 and ignored
+ %% by the receiver.
+
+ %% The RFC is quite unclear about the order of the bits in this
+ %% case. It writes
+ %%
+ %% 0 1 2 3 4 5 6 7
+ %% +-+-+-+-+-+-+-+-+
+ %% |R P E T r r r r|
+ %% +-+-+-+-+-+-+-+-+
+ %%
+ %% in defining these but the scale refers to the (big endian)
+ %% transmission order, first to last, not the bit order. That is,
+ %% R is the high order bit. It's odd that a standard reserves
+ %% low-order bit rather than high-order ones.
+
+ #diameter_header{version = Version,
+ length = MsgLength,
+ cmd_code = CmdCode,
+ application_id = ApplicationId,
+ hop_by_hop_id = HopByHopId,
+ end_to_end_id = EndToEndId,
+ is_request = 1 == R,
+ is_proxiable = 1 == P,
+ is_error = 1 == E,
+ is_retransmitted = 1 == T};
+
+decode_header(_) ->
+ false.
+
+%%% ---------------------------------------------------------------------------
+%%% # sequence_numbers/1
+%%% ---------------------------------------------------------------------------
+
+%% The End-To-End identifier must be unique for at least 4 minutes. We
+%% maintain a 24-bit wraparound counter, and add an 8-bit persistent
+%% wraparound counter. The 8-bit counter is incremented each time the
+%% system is restarted.
+
+sequence_numbers(#diameter_packet{bin = Bin})
+ when is_binary(Bin) ->
+ sequence_numbers(Bin);
+
+sequence_numbers(#diameter_packet{header = #diameter_header{} = H}) ->
+ sequence_numbers(H);
+
+sequence_numbers(#diameter_header{hop_by_hop_id = H,
+ end_to_end_id = E}) ->
+ {H,E};
+
+sequence_numbers(<<_:12/binary, H:32, E:32, _/binary>>) ->
+ {H,E}.
+
+%%% ---------------------------------------------------------------------------
+%%% # hop_by_hop_id/2
+%%% ---------------------------------------------------------------------------
+
+hop_by_hop_id(Id, <>) ->
+ <>.
+
+%%% ---------------------------------------------------------------------------
+%%% # msg_name/1
+%%% ---------------------------------------------------------------------------
+
+msg_name(#diameter_header{application_id = ?APP_ID_COMMON,
+ cmd_code = C,
+ is_request = R}) ->
+ ?BASE:msg_name(C,R);
+
+msg_name(Hdr) ->
+ msg_id(Hdr).
+
+%% Note that messages in different applications could have the same
+%% name.
+
+%%% ---------------------------------------------------------------------------
+%%% # msg_id/1
+%%% ---------------------------------------------------------------------------
+
+msg_id(#diameter_packet{msg = [#diameter_header{} = Hdr | _]}) ->
+ msg_id(Hdr);
+
+msg_id(#diameter_packet{header = #diameter_header{} = Hdr}) ->
+ msg_id(Hdr);
+
+msg_id(#diameter_header{application_id = A,
+ cmd_code = C,
+ is_request = R}) ->
+ {A, C, if R -> 1; true -> 0 end};
+
+msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/bitstring>>) ->
+ {ApplId, CmdCode, Rbit}.
+
+%%% ---------------------------------------------------------------------------
+%%% # collect_avps/1
+%%% ---------------------------------------------------------------------------
+
+%% Note that the returned list of AVP's is reversed relative to their
+%% order in the binary. Note also that grouped avp's aren't unraveled,
+%% only those at the top level.
+
+collect_avps(#diameter_packet{bin = Bin}) ->
+ <<_:20/binary, Avps/bitstring>> = Bin,
+ collect_avps(Avps);
+
+collect_avps(Bin) ->
+ collect_avps(Bin, 0, []).
+
+collect_avps(<<>>, _, Acc) ->
+ Acc;
+collect_avps(Bin, N, Acc) ->
+ try split_avp(Bin) of
+ {Rest, AVP} ->
+ collect_avps(Rest, N+1, [AVP#diameter_avp{index = N} | Acc])
+ catch
+ ?FAILURE(_) ->
+ {Bin, Acc}
+ end.
+
+%% 0 1 2 3
+%% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | AVP Code |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% |V M P r r r r r| AVP Length |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | Vendor-ID (opt) |
+%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+%% | Data ...
+%% +-+-+-+-+-+-+-+-+
+
+%% split_avp/1
+
+split_avp(Bin) ->
+ 8 =< size(Bin) orelse ?THROW(truncated_header),
+
+ <>
+ = Bin,
+
+ DataSize = Length - 8, % size(Code+Flags+Length) = 8 octets
+ PadSize = (4 - (DataSize rem 4)) rem 4,
+
+ DataSize + PadSize =< size(Rest)
+ orelse ?THROW(truncated_data),
+
+ <>
+ = Rest,
+ <>
+ = Flags,
+
+ 0 == Vbit orelse 4 =< size(Data)
+ orelse ?THROW(truncated_vendor_id),
+
+ {Vid, D} = vid(Vbit, Data),
+ {R, #diameter_avp{code = Code,
+ vendor_id = Vid,
+ is_mandatory = 1 == Mbit,
+ need_encryption = 1 == Pbit,
+ data = D}}.
+
+%% The RFC is a little misleading when stating that OctetString is
+%% padded to a 32-bit boundary while other types align naturally. All
+%% other types are already multiples of 32 bits so there's no need to
+%% distinguish between types here. Any invalid lengths will result in
+%% decode error in diameter_types.
+
+vid(1, <>) ->
+ {Vid, Data};
+vid(0, Data) ->
+ {undefined, Data}.
+
+%%% ---------------------------------------------------------------------------
+%%% # pack_avp/1
+%%% ---------------------------------------------------------------------------
+
+%% The normal case here is data as an #diameter_avp{} list or an
+%% iolist, which are the cases that generated codec modules use. The
+%% other case is as a convenience in the relay case in which the
+%% dictionary doesn't know about specific AVP's.
+
+%% Grouped AVP whose components need packing ...
+pack_avp(#diameter_avp{data = [#diameter_avp{} | _] = Avps} = A) ->
+ pack_avp(A#diameter_avp{data = encode_avps(Avps)});
+
+%% ... data as a type/value tuple, possibly with header data, ...
+pack_avp(#diameter_avp{data = {Type, Value}} = A)
+ when is_atom(Type) ->
+ pack_avp(A#diameter_avp{data = diameter_types:Type(encode, Value)});
+pack_avp(#diameter_avp{data = {{_,_,_} = T, {Type, Value}}}) ->
+ pack_avp(T, iolist_to_binary(diameter_types:Type(encode, Value)));
+pack_avp(#diameter_avp{data = {{_,_,_} = T, Bin}})
+ when is_binary(Bin) ->
+ pack_avp(T, Bin);
+pack_avp(#diameter_avp{data = {Dict, Name, Value}} = A) ->
+ {Code, _Flags, Vid} = Hdr = Dict:avp_header(Name),
+ {Name, Type} = Dict:avp_name(Code, Vid),
+ pack_avp(A#diameter_avp{data = {Hdr, {Type, Value}}});
+
+%% ... or as an iolist.
+pack_avp(#diameter_avp{code = Code,
+ vendor_id = V,
+ is_mandatory = M,
+ need_encryption = P,
+ data = Data}) ->
+ Flags = lists:foldl(fun flag_avp/2, 0, [{V /= undefined, 2#10000000},
+ {M, 2#01000000},
+ {P, 2#00100000}]),
+ pack_avp({Code, Flags, V}, iolist_to_binary(Data)).
+
+flag_avp({true, B}, F) ->
+ F bor B;
+flag_avp({false, _}, F) ->
+ F.
+
+%%% ---------------------------------------------------------------------------
+%%% # pack_avp/2
+%%% ---------------------------------------------------------------------------
+
+pack_avp({Code, Flags, VendorId}, Bin)
+ when is_binary(Bin) ->
+ Sz = size(Bin),
+ pack_avp(Code, Flags, VendorId, Sz, pad(Sz rem 4, Bin)).
+
+pad(0, Bin) ->
+ Bin;
+pad(N, Bin) ->
+ P = 8*(4-N),
+ <>.
+%% Note that padding is not included in the length field as mandated by
+%% the RFC.
+
+%% pack_avp/5
+%%
+%% Prepend the vendor id as required.
+
+pack_avp(Code, Flags, Vid, Sz, Bin)
+ when 0 == Flags band 2#10000000 ->
+ undefined = Vid, %% sanity check
+ pack_avp(Code, Flags, Sz, Bin);
+
+pack_avp(Code, Flags, Vid, Sz, Bin) ->
+ pack_avp(Code, Flags, Sz+4, <>).
+
+%% pack_avp/4
+
+pack_avp(Code, Flags, Sz, Bin) ->
+ Length = Sz + 8,
+ <>.
+
+%% ===========================================================================
+
+choose(true, X, _) -> X;
+choose(false, _, X) -> X.
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
new file mode 100644
index 0000000000..a6b48fe65b
--- /dev/null
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -0,0 +1,676 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% This module writes service/transport configuration to the table
+%% diameter_config, so that the config will survive service process
+%% death, and then turns it into calls towards diameter_service. It
+%% also restarts services upon their death.
+%%
+%% The table diameter_config is only written here while
+%% diameter_service reads. This is all somewhat after the fact. Once
+%% upon a time the config was only stored in the service process,
+%% causing much grief if these processes died (which they did with
+%% some regularity) and one was forced to reconfigure. This module was
+%% then inserted into the service start in order to keep a more
+%% permanent record of the config. That said, service processes are
+%% now much more robust than they once were and crashing is a thing of
+%% the past.
+%%
+
+-module(diameter_config).
+-compile({no_auto_import, [monitor/2]}).
+
+-behaviour(gen_server).
+
+-export([start_service/2,
+ stop_service/1,
+ add_transport/2,
+ remove_transport/2,
+ have_transport/2,
+ lookup/1]).
+
+%% child server start
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+ terminate/2,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ code_change/3]).
+
+%% diameter_sync requests.
+-export([sync/1]).
+
+%% debug
+-export([state/0,
+ uptime/0]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+%% Server state.
+-record(state, {id = now()}).
+
+%% Registered name of the server.
+-define(SERVER, ?MODULE).
+
+%% Table config is written to.
+-define(TABLE, ?MODULE).
+
+%% Workaround for dialyzer's lack of understanding of match specs.
+-type match(T)
+ :: T | '_' | '$1' | '$2' | '$3' | '$4'.
+
+%% Configuration records in ?TABLE.
+
+-record(service,
+ {name,
+ rec :: match(#diameter_service{}),
+ options :: match(list())}).
+
+-record(transport,
+ {service, %% name
+ ref = make_ref() :: match(reference()),
+ type :: match(connect | listen),
+ options :: match(list())}).
+
+%% Monitor entry in ?TABLE.
+-record(monitor, {mref = make_ref() :: reference(),
+ service}). %% name
+
+%% Time to lay low before restarting a dead service.
+-define(RESTART_SLEEP, 2000).
+
+%% A minimal diameter_caps for checking for valid capabilities values.
+-define(EXAMPLE_CAPS,
+ #diameter_caps{origin_host = "TheHost",
+ origin_realm = "TheRealm",
+ host_ip_address = [{127,0,0,1}],
+ vendor_id = 42,
+ product_name = "TheProduct"}).
+
+-define(VALUES(Rec), tl(tuple_to_list(Rec))).
+
+%%% The return values below assume the server diameter_config is started.
+%%% The functions will exit if it isn't.
+
+%% --------------------------------------------------------------------------
+%% # start_service(SvcName, Opts)
+%%
+%% Output: ok | {error, Reason}
+%% --------------------------------------------------------------------------
+
+start_service(SvcName, Opts)
+ when is_list(Opts) ->
+ start_rc(sync(SvcName, {start_service, SvcName, Opts})).
+
+start_rc({ok = T, _Pid}) ->
+ T;
+start_rc({error, _} = No) ->
+ No;
+start_rc(timeout) ->
+ {error, application_not_started}.
+
+%% --------------------------------------------------------------------------
+%% # stop_service(SvcName)
+%%
+%% Output: ok
+%% --------------------------------------------------------------------------
+
+stop_service(SvcName) ->
+ sync(SvcName, {stop_service, SvcName}).
+
+%% --------------------------------------------------------------------------
+%% # add_transport(SvcName, {Type, Opts})
+%%
+%% Input: Type = connect | listen
+%%
+%% Output: {ok, Ref} | {error, Reason}
+%% --------------------------------------------------------------------------
+
+add_transport(SvcName, {T, Opts})
+ when is_list(Opts), (T == connect orelse T == listen) ->
+ sync(SvcName, {add, SvcName, T, Opts}).
+
+%% --------------------------------------------------------------------------
+%% # remove_transport(SvcName, Pred)
+%%
+%% Input: Pred = arity 3 fun on transport ref, connect|listen and Opts,
+%% returning true if the transport is to be removed, false if
+%% not
+%% | arity 2 fun on Ref and Opts only
+%% | arity 1 fun on Opts only
+%% | Opts matching all transports that have all of the specified
+%% options
+%% | Ref matching only the transport with this reference.
+%% | {M,F,A} applied to Ref, connect|listen and Opts
+%% | boolean()
+%%
+%% Output: ok | {error, Reason}
+%% --------------------------------------------------------------------------
+
+remove_transport(SvcName, Pred) ->
+ try
+ sync(SvcName, {remove, SvcName, pred(Pred)})
+ catch
+ ?FAILURE(Reason) ->
+ {error, Reason}
+ end.
+
+pred(Pred)
+ when is_function(Pred, 3) ->
+ Pred;
+pred(Pred)
+ when is_function(Pred, 2) ->
+ fun(R,_,O) -> Pred(R,O) end;
+pred(Pred)
+ when is_function(Pred, 1) ->
+ fun(_,_,O) -> Pred(O) end;
+pred(Opts)
+ when is_list(Opts) ->
+ fun(_,_,O) -> [] == Opts -- O end;
+pred(Ref)
+ when is_reference(Ref) ->
+ fun(R,_,_) -> R == Ref end;
+pred({M,F,A})
+ when is_atom(M), is_atom(F), is_list(A) ->
+ fun(R,T,O) -> apply(M,F,[R,T,O|A]) end;
+pred({Type, Pred}) -> %% backwards compatibility
+ P = pred(Pred),
+ fun(R,T,O) -> T == Type andalso P(R,T,O) end;
+pred(B)
+ when is_boolean(B) ->
+ fun(_,_,_) -> B end;
+pred(_) ->
+ ?THROW(pred).
+
+%% --------------------------------------------------------------------------
+%% # have_transport/2
+%%
+%% Output: true | false
+%% --------------------------------------------------------------------------
+
+have_transport(SvcName, Ref) ->
+ member([{#transport{service = '$1',
+ ref = '$2',
+ _ = '_'},
+ [{'andalso', {'=:=', '$1', {const, SvcName}},
+ {'=:=', '$2', {const, Ref}}}],
+ [true]}]).
+
+%% --------------------------------------------------------------------------
+%% # lookup/1
+%% --------------------------------------------------------------------------
+
+lookup(SvcName) ->
+ select([{#service{name = '$1', rec = '$2', options = '$3'},
+ [{'=:=', '$1', {const, SvcName}}],
+ [{{'$1', '$2', '$3'}}]},
+ {#transport{service = '$1',
+ ref = '$2',
+ type = '$3',
+ options = '$4'},
+ [{'=:=', '$1', {const, SvcName}}],
+ [{{'$2', '$3', '$4'}}]}]).
+
+%% ---------------------------------------------------------
+%% EXPORTED INTERNAL FUNCTIONS
+%% ---------------------------------------------------------
+
+start_link() ->
+ ServerName = {local, ?SERVER},
+ Module = ?MODULE,
+ Args = [],
+ Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
+ gen_server:start_link(ServerName, Module, Args, Options).
+
+state() ->
+ call(state).
+
+uptime() ->
+ call(uptime).
+
+%%% ----------------------------------------------------------
+%%% # init/1
+%%% ----------------------------------------------------------
+
+init([]) ->
+ {ok, #state{}}.
+
+%%% ----------------------------------------------------------
+%%% # handle_call/2
+%%% ----------------------------------------------------------
+
+handle_call(state, _, State) ->
+ {reply, State, State};
+
+handle_call(uptime, _, #state{id = Time} = State) ->
+ {reply, diameter_lib:now_diff(Time), State};
+
+handle_call(Req, From, State) ->
+ ?UNEXPECTED([Req, From]),
+ Reply = {error, {bad_request, Req}},
+ {reply, Reply, State}.
+
+%%% ----------------------------------------------------------
+%%% # handle_cast/2
+%%% ----------------------------------------------------------
+
+handle_cast(Msg, State) ->
+ ?UNEXPECTED([Msg]),
+ {noreply, State}.
+
+%%% ----------------------------------------------------------
+%%% # handle_info/2
+%%% ----------------------------------------------------------
+
+%% A service process has died. This is most likely a consequence of
+%% stop_service, in which case the restart will find no config for the
+%% service and do nothing. The entry keyed on the monitor ref is only
+%% removed as a result of the 'DOWN' notification however.
+handle_info({'DOWN', MRef, process, _, Reason}, State) ->
+ [#monitor{service = SvcName} = T] = select([{#monitor{mref = MRef,
+ _ = '_'},
+ [],
+ ['$_']}]),
+ queue_restart(Reason, SvcName),
+ delete_object(T),
+ {noreply, State};
+
+handle_info({monitor, SvcName, Pid}, State) ->
+ monitor(Pid, SvcName),
+ {noreply, State};
+
+handle_info({restart, SvcName}, State) ->
+ restart(SvcName),
+ {noreply, State};
+
+handle_info(restart, State) ->
+ restart(),
+ {noreply, State};
+
+handle_info(Info, State) ->
+ ?UNEXPECTED([Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% # terminate/2
+%%--------------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%% ----------------------------------------------------------
+%%% # code_change/3
+%%% ----------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%% ---------------------------------------------------------
+%% INTERNAL FUNCTIONS
+%% ---------------------------------------------------------
+
+insert(T) ->
+ ets:insert(?TABLE, T).
+
+%% ?TABLE is a bag: check only for a service entry.
+have_service(SvcName) ->
+ member([{#service{name = '$1', _ = '_'},
+ [{'=:=', '$1', {const, SvcName}}],
+ [true]}]).
+
+member(MatchSpec) ->
+ '$end_of_table' =/= ets:select(?TABLE, MatchSpec, 1).
+
+delete_object(T) ->
+ ets:delete_object(?TABLE, T).
+
+delete(Key) ->
+ ets:delete(?TABLE, Key).
+
+select(MatchSpec) ->
+ ets:select(?TABLE, MatchSpec).
+
+select_delete(MatchSpec) ->
+ ets:select_delete(?TABLE, MatchSpec).
+
+%% sync/2
+%%
+%% Interface functions used to be implemented as calls to ?SERVER but
+%% now serialize things per service instead since stopping a service
+%% can take time if the server doesn't answer DPR. A caller who wants
+%% to stop multiple services can then improve performance by spawning
+%% processes to stop them concurrently.
+
+sync(SvcName, T) ->
+ diameter_sync:call({?MODULE, SvcName},
+ {?MODULE, sync, [T]},
+ infinity,
+ infinity).
+
+%% sync/1
+
+sync({restart, SvcName}) ->
+ have_service(SvcName) andalso start(SvcName);
+
+sync({start_service, SvcName, Opts}) ->
+ try
+ start(have_service(SvcName), SvcName, Opts)
+ catch
+ ?FAILURE(Reason) -> {error, Reason}
+ end;
+
+sync({stop_service, SvcName}) ->
+ stop(SvcName);
+
+sync({add, SvcName, Type, Opts}) ->
+ try
+ add(SvcName, Type, Opts)
+ catch
+ ?FAILURE(Reason) -> {error, Reason}
+ end;
+
+sync({remove, SvcName, Pred}) ->
+ remove(select([{#transport{service = '$1', _ = '_'},
+ [{'=:=', '$1', {const, SvcName}}],
+ ['$_']}]),
+ SvcName,
+ Pred).
+
+%% start/3
+
+start(true, _, _) ->
+ {error, already_started};
+start(false, SvcName, Opts) ->
+ insert(make_config(SvcName, Opts)),
+ start(SvcName).
+
+%% start/1
+
+start(SvcName) ->
+ RC = diameter_service:start(SvcName),
+ startmon(SvcName, RC),
+ RC.
+
+startmon(SvcName, {ok, Pid}) ->
+ ?SERVER ! {monitor, SvcName, Pid};
+startmon(_, {error, _}) ->
+ ok.
+
+monitor(Pid, SvcName) ->
+ MRef = erlang:monitor(process, Pid),
+ insert(#monitor{mref = MRef, service = SvcName}).
+
+%% queue_restart/2
+
+%% Service has gone down on monitor death. Note that all service-related
+%% config is deleted.
+queue_restart({shutdown, {monitor, _}}, SvcName) ->
+ delete(SvcName);
+
+%% Application shutdown: ignore.
+queue_restart(shutdown, _) ->
+ ok;
+
+%% Or not.
+queue_restart(_, SvcName) ->
+ erlang:send_after(?RESTART_SLEEP, self(), {restart, SvcName}).
+
+%% restart/1
+
+restart(SvcName) ->
+ sync(SvcName, {restart, SvcName}).
+
+%% restart/0
+%%
+%% Start anything configured as required. Bang 'restart' to the server
+%% to kick things into gear manually. (Not that it should be required
+%% but it's been useful for test.)
+
+restart() ->
+ MatchSpec = [{#service{name = '$1', _ = '_'},
+ [],
+ ['$1']}],
+ lists:foreach(fun restart/1, select(MatchSpec)).
+
+%% stop/1
+
+stop(SvcName) ->
+ %% If the call to the service returns error for any reason other
+ %% than the process not being alive then deleting the config from
+ %% under it will surely bring it down.
+ diameter_service:stop(SvcName),
+ %% Delete only the service entry, not everything keyed on the name,
+ select_delete([{#service{name = '$1', _ = '_'},
+ [{'=:=', '$1', {const, SvcName}}],
+ [true]}]),
+ ok.
+%% Note that a transport has to be removed for its statistics to be
+%% deleted.
+
+%% add/3
+
+add(SvcName, Type, Opts) ->
+ %% Ensure usable capabilities. diameter_service:merge_service/2
+ %% depends on this.
+ lists:foreach(fun(Os) ->
+ is_list(Os) orelse ?THROW({capabilities, Os}),
+ ok = encode_CER(Os)
+ end,
+ [Os || {capabilities, Os} <- Opts, is_list(Os)]),
+
+ Ref = make_ref(),
+ T = {Ref, Type, Opts},
+ %% The call to the service returns error if the service isn't
+ %% started yet, which is harmless. The transport will be started
+ %% when the service is in that case.
+ case start_transport(SvcName, T) of
+ ok ->
+ insert(#transport{service = SvcName,
+ ref = Ref,
+ type = Type,
+ options = Opts}),
+ {ok, Ref};
+ {error, _} = No ->
+ No
+ end.
+
+start_transport(SvcName, T) ->
+ case diameter_service:start_transport(SvcName, T) of
+ {ok, _Pid} ->
+ ok;
+ {error, no_service} ->
+ ok;
+ {error, _} = No ->
+ No
+ end.
+
+%% remove/3
+
+remove(L, SvcName, Pred) ->
+ rm(SvcName, lists:filter(fun(#transport{ref = R, type = T, options = O}) ->
+ Pred(R,T,O)
+ end,
+ L)).
+
+rm(_, []) ->
+ ok;
+rm(SvcName, L) ->
+ Refs = lists:map(fun(#transport{ref = R}) -> R end, L),
+ case stop_transport(SvcName, Refs) of
+ ok ->
+ lists:foreach(fun delete_object/1, L);
+ {error, _} = No ->
+ No
+ end.
+
+stop_transport(SvcName, Refs) ->
+ case diameter_service:stop_transport(SvcName, Refs) of
+ ok ->
+ ok;
+ {error, no_service} ->
+ ok;
+ {error, _} = No ->
+ No
+ end.
+
+%% make_config/2
+
+make_config(SvcName, Opts) ->
+ Apps = init_apps(Opts),
+ [] == Apps andalso ?THROW(no_apps),
+
+ %% Use the fact that diameter_caps has the same field names as CER.
+ Fields = diameter_gen_base_rfc3588:'#info-'(diameter_base_CER) -- ['AVP'],
+
+ COpts = [T || {K,_} = T <- Opts, lists:member(K, Fields)],
+ Caps = make_caps(#diameter_caps{}, COpts),
+
+ ok = encode_CER(COpts),
+
+ Os = split(Opts, [{[fun erlang:is_boolean/1], false, share_peers},
+ {[fun erlang:is_boolean/1], false, use_shared_peers},
+ {[fun erlang:is_pid/1, false], false, monitor}]),
+ %% share_peers and use_shared_peers are currently undocumented.
+
+ #service{name = SvcName,
+ rec = #diameter_service{applications = Apps,
+ capabilities = Caps},
+ options = Os}.
+
+make_caps(Caps, Opts) ->
+ case diameter_capx:make_caps(Caps, Opts) of
+ {ok, T} ->
+ T;
+ {error, {Reason, _}} ->
+ ?THROW(Reason)
+ end.
+
+%% Validate types by encoding a CER.
+encode_CER(Opts) ->
+ {ok, CER} = diameter_capx:build_CER(make_caps(?EXAMPLE_CAPS, Opts)),
+
+ Hdr = #diameter_header{version = ?DIAMETER_VERSION,
+ end_to_end_id = 0,
+ hop_by_hop_id = 0},
+
+ try
+ diameter_codec:encode(?BASE, #diameter_packet{header = Hdr,
+ msg = CER}),
+ ok
+ catch
+ exit: Reason ->
+ ?THROW(Reason)
+ end.
+
+init_apps(Opts) ->
+ lists:foldl(fun app_acc/2, [], lists:reverse(Opts)).
+
+app_acc({application, Opts}, Acc) ->
+ is_list(Opts) orelse ?THROW({application, Opts}),
+
+ [Dict, Mod] = get_opt([dictionary, module], Opts),
+ Alias = get_opt(alias, Opts, Dict),
+ ModS = get_opt(state, Opts, Alias),
+ M = get_opt(call_mutates_state, Opts, false),
+ A = get_opt(answer_errors, Opts, report),
+ [#diameter_app{alias = Alias,
+ dictionary = Dict,
+ id = cb(Dict, id),
+ module = init_mod(Mod),
+ init_state = ModS,
+ mutable = init_mutable(M),
+ answer_errors = init_answers(A)}
+ | Acc];
+app_acc(_, Acc) ->
+ Acc.
+
+init_mod(M)
+ when is_atom(M) ->
+ [M];
+init_mod([M|_] = L)
+ when is_atom(M) ->
+ L;
+init_mod(M) ->
+ ?THROW({module, M}).
+
+init_mutable(M)
+ when M == true;
+ M == false ->
+ M;
+init_mutable(M) ->
+ ?THROW({call_mutates_state, M}).
+
+init_answers(A)
+ when callback == A;
+ report == A;
+ discard == A ->
+ A;
+init_answers(A) ->
+ ?THROW({answer_errors, A}).
+
+%% Get a single value at the specified key.
+get_opt(Keys, List)
+ when is_list(Keys) ->
+ [get_opt(K, List) || K <- Keys];
+get_opt(Key, List) ->
+ case [V || {K,V} <- List, K == Key] of
+ [V] -> V;
+ _ -> ?THROW({arity, Key})
+ end.
+
+%% Get an optional value at the specified key.
+get_opt(Key, List, Def) ->
+ case [V || {K,V} <- List, K == Key] of
+ [] -> Def;
+ [V] -> V;
+ _ -> ?THROW({arity, Key})
+ end.
+
+split(Opts, Defs) ->
+ [{K, value(D, Opts)} || {_,_,K} = D <- Defs].
+
+value({Preds, Def, Key}, Opts) ->
+ V = get_opt(Key, Opts, Def),
+ lists:any(fun(P) -> pred(P,V) end, Preds)
+ orelse ?THROW({value, Key}),
+ V.
+
+pred(F, V)
+ when is_function(F) ->
+ F(V);
+pred(T, V) ->
+ T == V.
+
+cb(M,F) ->
+ try M:F() of
+ V -> V
+ catch
+ E: Reason ->
+ ?THROW({callback, E, Reason, ?STACK})
+ end.
+
+%% call/1
+
+call(Request) ->
+ gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/base/diameter_dbg.erl b/lib/diameter/src/base/diameter_dbg.erl
new file mode 100644
index 0000000000..5b0ac3a3b6
--- /dev/null
+++ b/lib/diameter/src/base/diameter_dbg.erl
@@ -0,0 +1,516 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_dbg).
+
+-export([table/1,
+ tables/0,
+ fields/1,
+ help/0,
+ modules/0,
+ versions/0,
+ version_info/0,
+ compiled/0,
+ procs/0,
+ latest/0,
+ nl/0,
+ log/4]).
+
+-export([diameter_config/0,
+ diameter_peer/0,
+ diameter_reg/0,
+ diameter_request/0,
+ diameter_sequence/0,
+ diameter_service/0,
+ diameter_stats/0]).
+
+-export([pp/1,
+ subscriptions/0,
+ children/0]).
+
+%% Trace help.
+-export([tracer/0, tracer/1,
+ p/0, p/1,
+ stop/0,
+ tpl/1,
+ tp/1]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+
+-define(INFO, diameter_info).
+-define(SEP(), ?INFO:sep()).
+
+-define(LOCAL, [diameter_config,
+ diameter_peer,
+ diameter_reg,
+ diameter_request,
+ diameter_sequence,
+ diameter_service,
+ diameter_stats]).
+
+-define(VALUES(Rec), tl(tuple_to_list(Rec))).
+
+log(_Slogan, _Mod, _Line, _Details) ->
+ ok.
+
+%%% ----------------------------------------------------------
+%%% # help()
+%%% ----------------------------------------------------------
+
+help() ->
+ not_yet_implemented.
+
+%%% ----------------------------------------------------------
+%%% # table(TableName)
+%%%
+%%% Input: TableName = diameter table containing record entries.
+%%%
+%%% Output: Count | undefined
+%%% ----------------------------------------------------------
+
+table(T)
+ when (T == diameter_peer) orelse (T == diameter_reg) ->
+ ?INFO:format(collect(T), fields(T), fun ?INFO:split/2);
+
+table(Table)
+ when is_atom(Table) ->
+ case fields(Table) of
+ undefined = No ->
+ No;
+ Fields ->
+ ?INFO:format(Table, Fields, fun split/2)
+ end.
+
+split([started, name | Fs], [S, N | Vs]) ->
+ {name, [started | Fs], N, [S | Vs]};
+split([[F|FT]|Fs], [Rec|Vs]) ->
+ [_, V | VT] = tuple_to_list(Rec),
+ {F, FT ++ Fs, V, VT ++ Vs};
+split([F|Fs], [V|Vs]) ->
+ {F, Fs, V, Vs}.
+
+%%% ----------------------------------------------------------
+%%% # TableName()
+%%% ----------------------------------------------------------
+
+-define(TABLE(Name), Name() -> table(Name)).
+
+?TABLE(diameter_config).
+?TABLE(diameter_peer).
+?TABLE(diameter_reg).
+?TABLE(diameter_request).
+?TABLE(diameter_sequence).
+?TABLE(diameter_service).
+?TABLE(diameter_stats).
+
+%%% ----------------------------------------------------------
+%%% # tables()
+%%%
+%%% Output: Number of records output.
+%%%
+%%% Description: Pretty-print records in diameter tables from all nodes.
+%%% ----------------------------------------------------------
+
+tables() ->
+ ?INFO:format(field(?LOCAL), fun split/3, fun collect/1).
+
+field(Tables) ->
+ lists:map(fun(T) -> {T, fields(T)} end, lists:sort(Tables)).
+
+split(_, Fs, Vs) ->
+ split(Fs, Vs).
+
+%%% ----------------------------------------------------------
+%%% # modules()
+%%% ----------------------------------------------------------
+
+modules() ->
+ Path = filename:join([appdir(), atom_to_list(?APPLICATION) ++ ".app"]),
+ {ok, [{application, ?APPLICATION, Attrs}]} = file:consult(Path),
+ {modules, Mods} = lists:keyfind(modules, 1, Attrs),
+ Mods.
+
+appdir() ->
+ [_|_] = code:lib_dir(?APPLICATION, ebin).
+
+%%% ----------------------------------------------------------
+%%% # versions()
+%%% ----------------------------------------------------------
+
+versions() ->
+ ?INFO:versions(modules()).
+
+%%% ----------------------------------------------------------
+%%% # versions()
+%%% ----------------------------------------------------------
+
+version_info() ->
+ ?INFO:version_info(modules()).
+
+%%% ----------------------------------------------------------
+%%% # compiled()
+%%% ----------------------------------------------------------
+
+compiled() ->
+ ?INFO:compiled(modules()).
+
+%%% ----------------------------------------------------------
+%%% procs()
+%%% ----------------------------------------------------------
+
+procs() ->
+ ?INFO:procs(?APPLICATION).
+
+%%% ----------------------------------------------------------
+%%% # latest()
+%%% ----------------------------------------------------------
+
+latest() ->
+ ?INFO:latest(modules()).
+
+%%% ----------------------------------------------------------
+%%% # nl()
+%%% ----------------------------------------------------------
+
+nl() ->
+ lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()).
+
+%%% ----------------------------------------------------------
+%%% # pp(Bin)
+%%%
+%%% Description: Pretty-print a message binary.
+%%% ----------------------------------------------------------
+
+%% Network byte order = big endian.
+
+pp(<>) ->
+ ?SEP(),
+ ppp(["Version",
+ "Message length",
+ "[Actual length]",
+ "R(equest)",
+ "P(roxiable)",
+ "E(rror)",
+ "T(Potential retrans)",
+ "Reserved bits",
+ "Command code",
+ "Application id",
+ "Hop by hop id",
+ "End to end id"],
+ [Version, MsgLength, size(AVPs) + 20,
+ Rbit, Pbit, Ebit, Tbit, Reserved,
+ CmdCode,
+ ApplId,
+ HbHid,
+ E2Eid]),
+ N = avp_loop({AVPs, MsgLength - 20}, 0),
+ ?SEP(),
+ N;
+
+pp(<<_Version:8, MsgLength:24, _/binary>> = Bin) ->
+ {bad_message_length, MsgLength, size(Bin)};
+
+pp(Bin)
+ when is_binary(Bin) ->
+ {truncated_binary, size(Bin)};
+
+pp(_) ->
+ not_binary.
+
+%% avp_loop/2
+
+avp_loop({Bin, Size}, N) ->
+ avp_loop(avp(Bin, Size), N+1);
+avp_loop(ok, N) ->
+ N;
+avp_loop([_E, _Rest] = L, N) ->
+ io:format("! ~s: ~p~n", L),
+ N;
+avp_loop([E, Rest, Fmt | Values], N)
+ when is_binary(Rest) ->
+ io:format("! ~s (" ++ Fmt ++ "): ~p~n", [E|Values] ++ [Rest]),
+ N.
+
+%% avp/2
+
+avp(<<>>, 0) ->
+ ok;
+avp(<>,
+ Size) ->
+ avp(Code, Flags, Length, Rest, Size);
+avp(Bin, _) ->
+ ["truncated AVP header", Bin].
+
+%% avp/5
+
+avp(Code, Flags, Length, Rest, Size) ->
+ <>
+ = Flags,
+ b(),
+ ppp(["AVP Code",
+ "V(endor)",
+ "M(andatory)",
+ "P(Security)",
+ "R(eserved)",
+ "Length"],
+ [Code, V, M, P, Res, Length]),
+ avp(V, Rest, Length - 8, Size - 8).
+
+%% avp/4
+
+avp(1, <>, Length, Size) ->
+ ppp({"Vendor-ID", V}),
+ data(Data, Length - 4, Size - 4);
+avp(1, Bin, _, _) ->
+ ["truncated Vendor-ID", Bin];
+avp(0, Data, Length, Size) ->
+ data(Data, Length, Size).
+
+data(Bin, Length, Size)
+ when size(Bin) >= Length ->
+ <> = Bin,
+ ppp({"Data", AVP}),
+ unpad(Rest, Size - Length, Length rem 4);
+
+data(Bin, _, _) ->
+ ["truncated AVP data", Bin].
+
+%% Remove padding bytes up to the next word boundary.
+unpad(Bin, Size, 0) ->
+ {Bin, Size};
+unpad(Bin, Size, N) ->
+ un(Bin, Size, 4 - N).
+
+un(Bin, Size, N)
+ when size(Bin) >= N ->
+ ppp({"Padding bytes", N}),
+ <> = Bin,
+ Bits = N*8,
+ case Pad of
+ <<0:Bits>> ->
+ {Rest, Size - N};
+ _ ->
+ ["non-zero padding", Bin, "~p", N]
+ end;
+
+un(Bin, _, _) ->
+ ["truncated padding", Bin].
+
+b() ->
+ io:format("#~n").
+
+ppp(Fields, Values) ->
+ lists:foreach(fun ppp/1, lists:zip(Fields, Values)).
+
+ppp({Field, Value}) ->
+ io:format(": ~-22s : ~p~n", [Field, Value]).
+
+%%% ----------------------------------------------------------
+%%% # subscriptions()
+%%%
+%%% Output: list of {SvcName, Pid}
+%%% ----------------------------------------------------------
+
+subscriptions() ->
+ diameter_service:subscriptions().
+
+%%% ----------------------------------------------------------
+%%% # children()
+%%% ----------------------------------------------------------
+
+children() ->
+ diameter_sup:tree().
+
+%%% ----------------------------------------------------------
+
+%% tracer/[12]
+
+tracer(Port)
+ when is_integer(Port) ->
+ dbg:tracer(port, dbg:trace_port(ip, Port));
+
+tracer(Path)
+ when is_list(Path) ->
+ dbg:tracer(port, dbg:trace_port(file, Path)).
+
+tracer() ->
+ dbg:tracer(process, {fun p/2, ok}).
+
+p(T,_) ->
+ io:format("+ ~p~n", [T]).
+
+%% p/[01]
+
+p() ->
+ p([c,timestamp]).
+
+p(T) ->
+ dbg:p(all,T).
+
+%% stop/0
+
+stop() ->
+ dbg:ctp(),
+ dbg:stop_clear().
+
+%% tpl/1
+%% tp/1
+
+tpl(T) ->
+ dbg(tpl, T).
+
+tp(T) ->
+ dbg(tp, T).
+
+%% dbg/2
+
+dbg(F, L)
+ when is_list(L) ->
+ [dbg(F, X) || X <- L];
+
+dbg(F, M)
+ when is_atom(M) ->
+ apply(dbg, F, [M, x]);
+
+dbg(F, T)
+ when is_tuple(T) ->
+ apply(dbg, F, tuple_to_list(T)).
+
+%% ===========================================================================
+%% ===========================================================================
+
+%% collect/1
+
+collect(diameter_peer) ->
+ lists:flatmap(fun peers/1, diameter:services());
+
+collect(diameter_reg) ->
+ diameter_reg:terms();
+
+collect(Name) ->
+ c(ets:info(Name), Name).
+
+c(undefined, _) ->
+ [];
+c(_, Name) ->
+ ets:tab2list(Name).
+
+%% peers/1
+
+peers(Name) ->
+ peers(Name, diameter:service_info(Name, transport)).
+
+peers(_, undefined) ->
+ [];
+peers(Name, Ts) ->
+ lists:flatmap(fun(T) -> mk_peers(Name, T) end, Ts).
+
+mk_peers(Name, [_, {type, connect} | _] = Ts) ->
+ [[Name | mk_peer(Ts)]];
+mk_peers(Name, [R, {type, listen}, O, {accept = A, As}]) ->
+ [[Name | mk_peer([R, {type, A}, O | Ts])] || Ts <- As].
+%% This is a bit lame: service_info works to build this list and out
+%% of something like what we want here and then we take it apart.
+
+mk_peer(Vs) ->
+ [Type, Ref, State, Opts, WPid, TPid, SApps, Caps]
+ = get_values(Vs, [type,ref,state,options,watchdog,peer,apps,caps]),
+ [Ref, State, [{type, Type} | Opts], s(WPid), s(TPid), SApps, Caps].
+
+get_values(Vs, Ks) ->
+ [proplists:get_value(K, Vs) || K <- Ks].
+
+s(undefined = T) ->
+ T;
+s({Pid, _Started, _State}) ->
+ state(Pid);
+s({Pid, _Started}) ->
+ state(Pid).
+
+%% Collect states from watchdog/transport pids.
+state(Pid) ->
+ MRef = erlang:monitor(process, Pid),
+ Pid ! {state, self()},
+ receive
+ {'DOWN', MRef, process, _, _} ->
+ Pid;
+ {Pid, _} = T ->
+ erlang:demonitor(MRef, [flush]),
+ T
+ end.
+
+%% fields/1
+
+-define(FIELDS(Table), fields(Table) -> record_info(fields, Table)).
+
+fields(diameter_config) ->
+ [];
+
+fields(T)
+ when T == diameter_request;
+ T == diameter_sequence ->
+ fun kv/1;
+
+fields(diameter_stats) ->
+ fun({Ctr, N}) when not is_pid(Ctr) ->
+ {[counter, value], [Ctr, N]};
+ (_) ->
+ []
+ end;
+
+fields(diameter_service) ->
+ [started,
+ name,
+ record_info(fields, diameter_service),
+ peerT,
+ connT,
+ share_peers,
+ use_shared_peers,
+ shared_peers,
+ local_peers,
+ monitor];
+
+?FIELDS(diameter_event);
+?FIELDS(diameter_uri);
+?FIELDS(diameter_avp);
+?FIELDS(diameter_header);
+?FIELDS(diameter_packet);
+?FIELDS(diameter_app);
+?FIELDS(diameter_caps);
+
+fields(diameter_peer) ->
+ [service, ref, state, options, watchdog, peer, applications, capabilities];
+
+fields(diameter_reg) ->
+ [property, pids];
+
+fields(_) ->
+ undefined.
+
+kv({_,_}) ->
+ [key, value];
+kv(_) ->
+ [].
diff --git a/lib/diameter/src/base/diameter_dict.erl b/lib/diameter/src/base/diameter_dict.erl
new file mode 100644
index 0000000000..3b9ba00a3f
--- /dev/null
+++ b/lib/diameter/src/base/diameter_dict.erl
@@ -0,0 +1,153 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% This module provide OTP's dict interface built on top of ets.
+%%
+%% Note that while the interface is the same as dict the semantics
+%% aren't quite. A Dict here is just a table identifier (although
+%% this fact can't be used if you want dict/ets-based implementations
+%% to be interchangeable) so changes made to the Dict modify the
+%% underlying table. For merge/3, the first argument table is modified.
+%%
+%% The underlying ets table implementing a dict is deleted when the
+%% process from which new() was invoked exits and the dict is only
+%% writable from this process.
+%%
+%% The reason for this is to be able to swap dict/ets-based
+%% implementations: the former is easier to debug, the latter is
+%% faster for larger tables. It's also just a nice interface even
+%% when there's no need for swapability.
+%%
+
+-module(diameter_dict).
+
+-export([append/3,
+ append_list/3,
+ erase/2,
+ fetch/2,
+ fetch_keys/1,
+ filter/2,
+ find/2,
+ fold/3,
+ from_list/1,
+ is_key/2,
+ map/2,
+ merge/3,
+ new/0,
+ store/3,
+ to_list/1,
+ update/3,
+ update/4,
+ update_counter/3]).
+
+%%% ----------------------------------------------------------
+%%% EXPORTED INTERNAL FUNCTIONS
+%%% ----------------------------------------------------------
+
+append(Key, Value, Dict) ->
+ append_list(Key, [Value], Dict).
+
+append_list(Key, ValueList, Dict)
+ when is_list(ValueList) ->
+ update(Key, fun(V) -> V ++ ValueList end, ValueList, Dict).
+
+erase(Key, Dict) ->
+ ets:delete(Dict, Key),
+ Dict.
+
+fetch(Key, Dict) ->
+ {ok, V} = find(Key, Dict),
+ V.
+
+fetch_keys(Dict) ->
+ ets:foldl(fun({K,_}, Acc) -> [K | Acc] end, [], Dict).
+
+filter(Pred, Dict) ->
+ lists:foreach(fun({K,V}) -> filter(Pred(K,V), K, Dict) end, to_list(Dict)),
+ Dict.
+
+find(Key, Dict) ->
+ case ets:lookup(Dict, Key) of
+ [{Key, V}] ->
+ {ok, V};
+ [] ->
+ error
+ end.
+
+fold(Fun, Acc0, Dict) ->
+ ets:foldl(fun({K,V}, Acc) -> Fun(K, V, Acc) end, Acc0, Dict).
+
+from_list(List) ->
+ lists:foldl(fun store/2, new(), List).
+
+is_key(Key, Dict) ->
+ ets:member(Dict, Key).
+
+map(Fun, Dict) ->
+ lists:foreach(fun({K,V}) -> store(K, Fun(K,V), Dict) end, to_list(Dict)),
+ Dict.
+
+merge(Fun, Dict1, Dict2) ->
+ fold(fun(K2,V2,_) ->
+ update(K2, fun(V1) -> Fun(K2, V1, V2) end, V2, Dict1)
+ end,
+ Dict1,
+ Dict2).
+
+new() ->
+ ets:new(?MODULE, [set]).
+
+store(Key, Value, Dict) ->
+ store({Key, Value}, Dict).
+
+to_list(Dict) ->
+ ets:tab2list(Dict).
+
+update(Key, Fun, Dict) ->
+ store(Key, Fun(fetch(Key, Dict)), Dict).
+
+update(Key, Fun, Initial, Dict) ->
+ store(Key, map(Key, Fun, Dict, Initial), Dict).
+
+update_counter(Key, Increment, Dict)
+ when is_integer(Increment) ->
+ update(Key, fun(V) -> V + Increment end, Increment, Dict).
+
+%%% ---------------------------------------------------------
+%%% INTERNAL FUNCTIONS
+%%% ---------------------------------------------------------
+
+store({_,_} = T, Dict) ->
+ ets:insert(Dict, T),
+ Dict.
+
+filter(true, _, _) ->
+ ok;
+filter(false, K, Dict) ->
+ erase(K, Dict).
+
+map(Key, Fun, Dict, Error) ->
+ case find(Key, Dict) of
+ {ok, V} ->
+ Fun(V);
+ error ->
+ Error
+ end.
+
diff --git a/lib/diameter/src/base/diameter_info.erl b/lib/diameter/src/base/diameter_info.erl
new file mode 100644
index 0000000000..39d32d07cd
--- /dev/null
+++ b/lib/diameter/src/base/diameter_info.erl
@@ -0,0 +1,869 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_info).
+
+-export([usage/1,
+ format/1,
+ format/2,
+ format/3,
+ format/4,
+ table/2,
+ tables/1,
+ tables/2,
+ split/2,
+ split/3,
+ tab2list/1,
+ modules/1,
+ versions/1,
+ version_info/1,
+ attrs/2,
+ compiled/1,
+ procs/1,
+ latest/1,
+ list/1]).
+
+%% Support for rolling your own.
+-export([sep/0,
+ sep/1,
+ widest/1,
+ p/1,
+ p/3]).
+
+-compile({no_auto_import,[max/2]}).
+
+-export([collect/2]).
+
+-define(LONG_TIMEOUT, 30000).
+-define(VALUES(Rec), tl(tuple_to_list(Rec))).
+
+%%% ----------------------------------------------------------
+%%% # usage(String)
+%%% ----------------------------------------------------------
+
+usage(Usage) ->
+ sep($+),
+ io:format("+ ~p~n", [?MODULE]),
+ io:format("~n~s~n~n", [compact(Usage)]),
+ sep($+).
+
+%%%
+%%% The function format/3, for pretty-printing tables, comes in
+%%% several flavours.
+%%%
+
+%%% ----------------------------------------------------------
+%%% # format(TableName, Fields, SplitFun)
+%%%
+%%% Input: TableName = atom() name of table.
+%%%
+%%% Fields = List of field names for the records maintained
+%%% in the specified table. Can be empty, in which
+%%% case entries are listed unadorned of field names
+%%% and SplitFun is unused.
+%%% | Integer, equivalent to a list with this many '' atoms.
+%%% | Arity 1 fun mapping a table entry to a Fields list
+%%% or a tuple {Fields, Values} of lists of the same
+%%% length.
+%%%
+%%% If Fields is a list then its length must be the same
+%%% as or one less than the size of the tuples contained
+%%% in the table. (The values printed then being those
+%%% in the tuple or record in question.)
+%%%
+%%% SplitFun = Arity 3 fun applied as
+%%%
+%%% SplitFun(TableName, Fields, Values)
+%%%
+%%% in order to obtain a tuple
+%%%
+%%% {Field, RestFields, Value, RestValues}
+%%%
+%%% for which Field/Value will be formatted on
+%%% STDOUT. (This is to allow a value to be
+%%% transformed before being output by returning a
+%%% new value and/or replacing the remainder of
+%%% the list.) The returned lists must have the
+%%% same length and Field here is an atom, '' causing
+%%% a value to be listed unadorned of the field name.
+%%%
+%%% Field can also be list of field names, in
+%%% which case Value must be a record of the
+%%% corresponding type.
+%%%
+%%% | Arity 2 fun applied as SplitFun(Fields, Values).
+%%%
+%%% Output: Count | undefined
+%%%
+%%% Count = Number of entries output.
+%%%
+%%% Description: Pretty-print records in a named table.
+%%% ----------------------------------------------------------
+
+format(Table, Fields, SFun)
+ when is_atom(Table), is_function(SFun, 2) ->
+ ft(ets:info(Table), Table, SFun, Fields);
+
+format(Table, Fields, SFun)
+ when is_atom(Table), is_function(SFun, 3) ->
+ format(Table, Fields, fun(Fs,Vs) -> SFun(Table, Fs, Vs) end);
+
+%%% ----------------------------------------------------------
+%%% # format(Recs, Fields, SplitFun)
+%%%
+%%% Input: Recs = list of records/tuples
+%%% Fields = As for format(Table, Fields, SplitFun), a table
+%%% entry there being a member of Recs.
+%%% SplitFun = Arity 3 fun applied as above but with the TableName
+%%% replaced by the first element of the records in
+%%% question.
+%%% | Arity 2 fun as for format/3.
+%%%
+%%% Output: length(Recs)
+%%%
+%%% Description: Pretty print records/tuples.
+%%% ----------------------------------------------------------
+
+format(Recs, Fields, SFun)
+ when is_list(Recs), is_function(SFun, 3) ->
+ lists:foldl(fun(R,A) -> f(recsplit(SFun, R), 0, Fields, R, A) end,
+ 0,
+ Recs);
+
+format(Recs, Fields, SFun)
+ when is_list(Recs), is_function(SFun, 2) ->
+ lists:foldl(fun(R,A) -> f(SFun, 0, Fields, R, A) end,
+ 0,
+ Recs);
+
+%%% ----------------------------------------------------------
+%%% # format(Tables, SplitFun, CollectFun)
+%%%
+%%% Input: Tables = list of {TableName, Fields}.
+%%% SplitFun = As for format(Table, Fields, SplitFun).
+%%% CollectFun = arity 1 fun mapping a table name to a list
+%%% of elements. A non-list can be returned to indicate
+%%% that the table in question doesn't exist.
+%%%
+%%% Output: Number of entries output.
+%%%
+%%% Description: Pretty-print records in a named tables as collected
+%%% from known nodes. Each table listing is preceeded by
+%%% a banner.
+%%% ----------------------------------------------------------
+
+format(Tables, SFun, CFun)
+ when is_list(Tables), is_function(CFun, 1) ->
+ format_remote(Tables,
+ SFun,
+ rpc:multicall(nodes(known),
+ ?MODULE,
+ collect,
+ [CFun, lists:map(fun({T,_}) -> T end, Tables)],
+ ?LONG_TIMEOUT));
+
+%%% ----------------------------------------------------------
+%%% # format(LocalTables, RemoteTables, SplitFun, CollectFun)
+%%% # format(LocalTables, RemoteTables, SplitFun)
+%%%
+%%% Input: LocalTables = list of {TableName, Fields}.
+%%% | list of {TableName, Recs, Fields}
+%%% RemoteTable = list of {TableName, Fields}.
+%%% SplitFun, CollectFun = As for format(Table, CollectFun, SplitFun).
+%%%
+%%% Output: Number of entries output.
+%%%
+%%% Description: Pretty-print records in a named tables as collected
+%%% from local and remote nodes. Each table listing is
+%%% preceeded by a banner.
+%%% ----------------------------------------------------------
+
+format(Local, Remote, SFun) ->
+ format(Local, Remote, SFun, fun tab2list/1).
+
+format(Local, Remote, SFun, CFun)
+ when is_list(Local), is_list(Remote), is_function(CFun, 1) ->
+ format_local(Local, SFun) + format(Remote, SFun, CFun).
+
+%%% ----------------------------------------------------------
+%%% # format(Tables, SplitFun)
+%%% ----------------------------------------------------------
+
+format(Tables, SFun)
+ when is_list(Tables), (is_function(SFun, 2) or is_function(SFun, 3)) ->
+ format(Tables, SFun, fun tab2list/1);
+
+format(Tables, CFun)
+ when is_list(Tables), is_function(CFun, 1) ->
+ format(Tables, fun split/2, CFun).
+
+%%% ----------------------------------------------------------
+%%% # format(Table|Tables)
+%%% ----------------------------------------------------------
+
+format(Table)
+ when is_atom(Table) ->
+ format(Table, [], fun split/2);
+
+format(Tables)
+ when is_list(Tables) ->
+ format(Tables, fun split/2, fun tab2list/1).
+
+%%% ----------------------------------------------------------
+%%% # split(TableName, Fields, Values)
+%%%
+%%% Description: format/3 SplitFun that does nothing special.
+%%% ----------------------------------------------------------
+
+split([F|FT], [V|VT]) ->
+ {F, FT, V, VT}.
+
+split(_, Fs, Vs) ->
+ split(Fs, Vs).
+
+%%% ----------------------------------------------------------
+%%% # tab2list(TableName)
+%%%
+%%% Description: format/4 CollectFun that extracts records from an
+%%% existing ets table.
+%%% ----------------------------------------------------------
+
+tab2list(Table) ->
+ case ets:info(Table) of
+ undefined = No ->
+ No;
+ _ ->
+ ets:tab2list(Table)
+ end.
+
+list(Table) ->
+ l(tab2list(Table)).
+
+l(undefined = No) ->
+ No;
+l(List)
+ when is_list(List) ->
+ io:format("~p~n", [List]),
+ length(List).
+
+%%% ----------------------------------------------------------
+%%% # table(TableName, Fields)
+%%% ----------------------------------------------------------
+
+table(Table, Fields) ->
+ format(Table, Fields, fun split/2).
+
+%%% ----------------------------------------------------------
+%%% # tables(LocalTables, RemoteTables)
+%%% ----------------------------------------------------------
+
+tables(Local, Remote) ->
+ format(Local, Remote, fun split/2).
+
+%%% ----------------------------------------------------------
+%%% # tables(Tables)
+%%% ----------------------------------------------------------
+
+tables(Tables) ->
+ format(Tables, fun split/2).
+
+%%% ----------------------------------------------------------
+%%% # modules(Prefix|Prefixes)
+%%%
+%%% Input: Prefix = atom()
+%%%
+%%% Description: Return the list of all loaded modules with the
+%%% specified prefix.
+%%% ----------------------------------------------------------
+
+modules(Prefix)
+ when is_atom(Prefix) ->
+ lists:sort(mods(Prefix));
+
+modules(Prefixes)
+ when is_list(Prefixes) ->
+ lists:sort(lists:flatmap(fun modules/1, Prefixes)).
+
+mods(Prefix) ->
+ P = atom_to_list(Prefix),
+ lists:filter(fun(M) ->
+ lists:prefix(P, atom_to_list(M))
+ end,
+ erlang:loaded()).
+
+%%% ----------------------------------------------------------
+%%% # versions(Modules|Prefix)
+%%%
+%%% Output: Number of modules listed.
+%%%
+%%% Description: List the versions of the specified modules.
+%%% ----------------------------------------------------------
+
+versions(Modules) ->
+ {SysInfo, OsInfo, ModInfo} = version_info(Modules),
+ sep(),
+ print_sys_info(SysInfo),
+ sep(),
+ print_os_info(OsInfo),
+ sep(),
+ print_mod_info(ModInfo),
+ sep().
+
+%%% ----------------------------------------------------------
+%%% # attrs(Modules|Prefix, Attr|FormatFun)
+%%%
+%%% Output: Number of modules listed.
+%%%
+%%% Description: List an attribute from module_info.
+%%% ----------------------------------------------------------
+
+attrs(Modules, Attr)
+ when is_atom(Attr) ->
+ attrs(Modules, fun(W,M) -> attr(W, M, Attr, fun attr/1) end);
+
+attrs(Modules, Fun)
+ when is_list(Modules) ->
+ sep(),
+ W = 2 + widest(Modules),
+ N = lists:foldl(fun(M,A) -> Fun(W,M), A+1 end, 0, Modules),
+ sep(),
+ N;
+
+attrs(Prefix, Fun) ->
+ attrs(modules(Prefix), Fun).
+
+%% attr/1
+
+attr(T) when is_atom(T) ->
+ atom_to_list(T);
+attr(N) when is_integer(N) ->
+ integer_to_list(N);
+attr(V) ->
+ case is_list(V) andalso lists:all(fun is_char/1, V) of
+ true -> %% string
+ V;
+ false ->
+ io_lib:format("~p", [V])
+ end.
+
+is_char(C) ->
+ 0 =< C andalso C < 256.
+
+%% attr/4
+
+attr(Width, Mod, Attr, VFun) ->
+ io:format(": ~*s~s~n", [-Width, Mod, attr(Mod, Attr, VFun)]).
+
+attr(Mod, Attr, VFun) ->
+ Key = key(Attr),
+ try
+ VFun(val(Attr, keyfetch(Attr, Mod:module_info(Key))))
+ catch
+ _:_ ->
+ "-"
+ end.
+
+attr(Mod, Attr) ->
+ attr(Mod, Attr, fun attr/1).
+
+key(time) -> compile;
+key(_) -> attributes.
+
+val(time, {_,_,_,_,_,_} = T) ->
+ lists:flatten(io_lib:format("~p-~2..0B-~2..0B ~2..0B:~2..0B:~2..0B",
+ tuple_to_list(T)));
+val(_, [V]) ->
+ V.
+
+%%% ----------------------------------------------------------
+%%% # compiled(Modules|Prefix)
+%%%
+%%% Output: Number of modules listed.
+%%%
+%%% Description: List the compile times of the specified modules.
+%%% ----------------------------------------------------------
+
+compiled(Modules)
+ when is_list(Modules) ->
+ attrs(Modules, fun compiled/2);
+
+compiled(Prefix) ->
+ compiled(modules(Prefix)).
+
+compiled(Width, Mod) ->
+ io:format(": ~*s~19s ~s~n", [-Width,
+ Mod,
+ attr(Mod, time),
+ opt(attr(Mod, date))]).
+
+opt("-") ->
+ "";
+opt(D) ->
+ "(" ++ D ++ ")".
+
+%%% ----------------------------------------------------------
+%%% # procs(Pred|Prefix|Prefixes|Pid|Pids)
+%%%
+%%% Input: Pred = arity 2 fun returning true|false when applied to a
+%%% pid and its process info.
+%%%
+%%% Output: Number of processes listed.
+%%%
+%%% Description: List process info for all local processes that test
+%%% true with the specified predicate. With the prefix
+%%% form, those processes that are either currently
+%%% executing in, started executing in, or have a
+%%% registered name with a specified prefix are listed.
+%%% With the pid forms, only those process that are local
+%%% are listed and those that are dead list only the pid
+%%% itself.
+%%% ----------------------------------------------------------
+
+procs(Pred)
+ when is_function(Pred, 2) ->
+ procs(Pred, erlang:processes());
+
+procs([]) ->
+ 0;
+
+procs(Prefix)
+ when is_atom(Prefix) ->
+ procs(fun(_,I) -> info(fun pre1/2, I, atom_to_list(Prefix)) end);
+
+procs(Prefixes)
+ when is_atom(hd(Prefixes)) ->
+ procs(fun(_,I) -> info(fun pre/2, I, Prefixes) end);
+
+procs(Pid)
+ when is_pid(Pid) ->
+ procs(fun true2/2, [Pid]);
+
+procs(Pids)
+ when is_list(Pids) ->
+ procs(fun true2/2, Pids).
+
+true2(_,_) ->
+ true.
+
+%% procs/2
+
+procs(Pred, Pids) ->
+ Procs = lists:foldl(fun(P,A) ->
+ procs_acc(Pred, P, catch process_info(P), A)
+ end,
+ [],
+ Pids),
+ sep(0 < length(Procs)),
+ lists:foldl(fun(T,N) -> p(T), sep(), N+1 end, 0, Procs).
+
+procs_acc(_, Pid, undefined, Acc) -> %% dead
+ [[{pid, Pid}] | Acc];
+procs_acc(Pred, Pid, Info, Acc)
+ when is_list(Info) ->
+ p_acc(Pred(Pid, Info), Pid, Info, Acc);
+procs_acc(_, _, _, Acc) ->
+ Acc.
+
+p_acc(true, Pid, Info, Acc) ->
+ [[{pid, Pid} | Info] | Acc];
+p_acc(false, _, _, Acc) ->
+ Acc.
+
+%% info/3
+
+info(Pred, Info, T) ->
+ lists:any(fun(I) -> i(Pred, I, T) end, Info).
+
+i(Pred, {K, {M,_,_}}, T)
+ when K == current_function;
+ K == initial_call ->
+ Pred(M,T);
+i(Pred, {registered_name, N}, T) ->
+ Pred(N,T);
+i(_,_,_) ->
+ false.
+
+pre1(A, Pre) ->
+ lists:prefix(Pre, atom_to_list(A)).
+
+pre(A, Prefixes) ->
+ lists:any(fun(P) -> pre1(A, atom_to_list(P)) end, Prefixes).
+
+%%% ----------------------------------------------------------
+%%% # latest(Modules|Prefix)
+%%%
+%%% Output: {Mod, {Y,M,D,HH,MM,SS}, Version}
+%%%
+%%% Description: Return the compile time of the most recently compiled
+%%% module from the specified non-empty list. The modules
+%%% are assumed to exist.
+%%% ----------------------------------------------------------
+
+latest(Prefix)
+ when is_atom(Prefix) ->
+ latest(modules(Prefix));
+
+latest([_|_] = Modules) ->
+ {Mod, T}
+ = hd(lists:sort(fun latest/2, lists:map(fun compile_time/1, Modules))),
+ {Mod, T, app_vsn(Mod)}.
+
+app_vsn(Mod) ->
+ keyfetch(app_vsn, Mod:module_info(attributes)).
+
+compile_time(Mod) ->
+ T = keyfetch(time, Mod:module_info(compile)),
+ {Mod, T}.
+
+latest({_,T1},{_,T2}) ->
+ T1 > T2.
+
+%%% ----------------------------------------------------------
+%%% version_info(Modules|Prefix)
+%%%
+%%% Output: {SysInfo, OSInfo, [ModInfo]}
+%%%
+%%% SysInfo = {Arch, Vers}
+%%% OSInfo = {Vers, {Fam, Name}}
+%%% ModInfo = {Vsn, AppVsn, Time, CompilerVsn}
+%%% ----------------------------------------------------------
+
+version_info(Prefix)
+ when is_atom(Prefix) ->
+ version_info(modules(Prefix));
+
+version_info(Mods)
+ when is_list(Mods) ->
+ {sys_info(), os_info(), [{M, mod_version_info(M)} || M <- Mods]}.
+
+mod_version_info(Mod) ->
+ try
+ Info = Mod:module_info(),
+ [[Vsn], AppVsn] = get_values(attributes, [vsn, app_vsn], Info),
+ [Ver, Time] = get_values(compile, [version, time], Info),
+ [Vsn, AppVsn, Ver, Time]
+ catch
+ _:_ ->
+ []
+ end.
+
+get_values(Attr, Keys, Info) ->
+ As = proplists:get_value(Attr, Info),
+ [proplists:get_value(K, As, "?") || K <- Keys].
+
+sys_info() ->
+ [A,V] = [chomp(erlang:system_info(K)) || K <- [system_architecture,
+ system_version]],
+ {A,V}.
+
+os_info() ->
+ {os:version(), case os:type() of
+ {_Fam, _Name} = T ->
+ T;
+ Fam ->
+ {Fam, ""}
+ end}.
+
+chomp(S) ->
+ string:strip(S, right, $\n).
+
+print_sys_info({Arch, Ver}) ->
+ io:format("System info:~n"
+ " architecture : ~s~n"
+ " version : ~s~n",
+ [Arch, Ver]).
+
+print_os_info({Vsn, {Fam, Name}}) ->
+ io:format("OS info:~n"
+ " family : ~s ~s~n"
+ " version : ~s~n",
+ [str(Fam), bkt(str(Name)), vsn(Vsn)]).
+
+print_mod_info(Mods) ->
+ io:format("Module info:~n", []),
+ lists:foreach(fun print_mod/1, Mods).
+
+print_mod({Mod, []}) ->
+ io:format(" ~w:~n", [Mod]);
+print_mod({Mod, [Vsn, AppVsn, Ver, {Year, Month, Day, Hour, Min, Sec}]}) ->
+ Time = io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
+ [Year, Month, Day, Hour, Min, Sec]),
+ io:format(" ~w:~n"
+ " vsn : ~s~n"
+ " app_vsn : ~s~n"
+ " compiled : ~s~n"
+ " compiler : ~s~n",
+ [Mod, str(Vsn), str(AppVsn), Time, Ver]).
+
+str(A)
+ when is_atom(A) ->
+ atom_to_list(A);
+str(S)
+ when is_list(S) ->
+ S;
+str(T) ->
+ io_lib:format("~p", [T]).
+
+bkt("" = S) ->
+ S;
+bkt(S) ->
+ [$[, S, $]].
+
+vsn(T) when is_tuple(T) ->
+ case [[$., integer_to_list(N)] || N <- tuple_to_list(T)] of
+ [[$.,S] | Rest] ->
+ [S | Rest];
+ [] = S ->
+ S
+ end;
+vsn(T) ->
+ str(T).
+
+%%% ----------------------------------------------------------
+%%% ----------------------------------------------------------
+
+%% p/1
+
+p(Info) ->
+ W = 2 + widest([K || {K,_} <- Info]),
+ lists:foreach(fun({K,V}) -> p(W,K,V) end, Info).
+
+p(Width, Key, Value) ->
+ io:format(": ~*s: ~p~n", [-Width, Key, Value]).
+
+%% sep/[01]
+
+sep() ->
+ sep($#).
+
+sep(true) ->
+ sep();
+sep(false) ->
+ ok;
+
+sep(Ch) ->
+ io:format("~c~65c~n", [Ch, $-]).
+
+%% widest/1
+
+widest(List) ->
+ lists:foldl(fun widest/2, 0, List).
+
+widest(T, Max)
+ when is_atom(T) ->
+ widest(atom_to_list(T), Max);
+
+widest(T, Max)
+ when is_integer(T) ->
+ widest(integer_to_list(T), Max);
+
+widest(T, Max)
+ when is_list(T) -> %% string
+ max(length(T), Max).
+
+pt(T) ->
+ io:format(": ~p~n", [T]).
+
+recsplit(SFun, Rec) ->
+ fun(Fs,Vs) -> SFun(element(1, Rec), Fs, Vs) end.
+
+max(A, B) ->
+ if A > B -> A; true -> B end.
+
+keyfetch(Key, List) ->
+ {Key,V} = lists:keyfind(Key, 1, List),
+ V.
+
+%% ft/4
+
+ft(undefined = No, _, _, _) ->
+ No;
+
+ft(_, Table, SFun, Fields) ->
+ ets:foldl(fun(R,A) ->
+ f(SFun, 0, Fields, R, A)
+ end,
+ 0,
+ Table).
+
+%% f/5
+
+f(SFun, Width, Fields, Rec, Count) ->
+ ff(SFun, Width, fields(Fields, Rec), Rec, Count).
+
+ff(SFun, Width, Fields, Rec, Count) ->
+ sep(0 == Count),
+ f(SFun, Width, Fields, Rec),
+ sep(),
+ Count+1.
+
+fields(N, _)
+ when is_integer(N), N >= 0 ->
+ lists:duplicate(N, ''); %% list values unadorned
+fields(Fields, R)
+ when is_function(Fields, 1) ->
+ fields(Fields(R), R);
+fields({Fields, Values} = T, _)
+ when length(Fields) == length(Values) ->
+ T;
+fields(Fields, _)
+ when is_list(Fields) ->
+ Fields. %% list field/value pairs, or tuples if []
+
+%% f/4
+
+%% Empty fields list: just print the entry.
+f(_, _, [], Rec)
+ when is_tuple(Rec) ->
+ pt(Rec);
+
+%% Otherwise list field names/values.
+f(SFun, Width, {Fields, Values}, _) ->
+ f(SFun, Width, Fields, Values);
+
+f(SFun, Width, Fields, Rec)
+ when is_tuple(Rec) ->
+ f(SFun, Width, Fields, values(Fields, Rec));
+
+f(_, _, [], []) ->
+ ok;
+
+f(SFun, Width, [HF | _] = Fields, Values) ->
+ {F, FT, V, VT} = SFun(Fields, Values),
+ if is_list(F) -> %% V is a record
+ break($>, HF),
+ f(SFun, Width, F, values(F,V)),
+ break($<, HF),
+ f(SFun, Width, FT, VT);
+ F == '' -> %% no field name: just list value
+ pt(V),
+ f(SFun, Width, FT, VT);
+ true -> %% list field/value.
+ W = max(Width, 1 + widest(Fields)),
+ p(W, F, V),
+ f(SFun, W, FT, VT)
+ end.
+
+values(Fields, Rec)
+ when length(Fields) == size(Rec) - 1 ->
+ ?VALUES(Rec);
+values(Fields, T)
+ when length(Fields) == size(T) ->
+ tuple_to_list(T).
+
+%% format_local/2
+
+format_local(Tables, SFun) ->
+ lists:foldl(fun(T,A) -> fl(SFun, T, A) end, 0, Tables).
+
+fl(SFun, {Table, Recs, Fields}, Count) ->
+ sep(),
+ io:format("# ~p~n", [Table]),
+ N = fmt(Recs, Fields, SFun),
+ sep(0 == N),
+ Count + N;
+
+fl(SFun, {Table, Fields}, Count) ->
+ fl(SFun, {Table, Table, Fields}, Count).
+
+%% fmt/3
+
+fmt(T, Fields, SFun) ->
+ case format(T, Fields, SFun) of
+ undefined ->
+ 0;
+ N ->
+ N
+ end.
+
+%% break/2
+
+break(C, T) ->
+ io:format("~c ~p~n", [C, T]).
+
+%% collect/2
+%%
+%% Output: {[{TableName, Recs}, ...], node()}
+
+collect(CFun, TableNames) ->
+ {lists:foldl(fun(N,A) -> c(CFun, N, A) end, [], TableNames), node()}.
+
+c(CFun, TableName, Acc) ->
+ case CFun(TableName) of
+ Recs when is_list(Recs) ->
+ [{TableName, Recs} | Acc];
+ _ ->
+ Acc
+ end.
+
+%% format_remote/3
+
+format_remote(Tables, SFun, {Replies, BadNodes}) ->
+ N = lists:foldl(fun(T,A) -> fr(Tables, SFun, T, A) end,
+ 0,
+ Replies),
+ sep(0 == N andalso [] /= BadNodes),
+ lists:foreach(fun(Node) -> io:format("# no reply from ~p~n", [Node]) end,
+ BadNodes),
+ sep([] /= BadNodes),
+ N.
+
+fr(Tables, SFun, {List, Node}, Count)
+ when is_list(List) -> %% guard against {badrpc, Reason}
+ lists:foldl(fun({T,Recs}, C) -> fr(Tables, SFun, Node, T, Recs,C) end,
+ Count,
+ List);
+fr(_, _, _, Count) ->
+ Count.
+
+fr(Tables, SFun, Node, Table, Recs, Count) ->
+ Fields = keyfetch(Table, Tables),
+ sep(),
+ io:format("# ~p@~p~n", [Table, Node]),
+ N = format(Recs, Fields, tblsplit(SFun, Table)),
+ sep(0 == N),
+ Count + N.
+
+tblsplit(SFun, Table)
+ when is_function(SFun, 3) ->
+ fun(Fs,Vs) -> SFun(Table, Fs, Vs) end;
+tblsplit(SFun, _)
+ when is_function(SFun, 2) ->
+ SFun.
+
+%% compact/1
+%%
+%% Strip whitespace from both ends of a string.
+
+compact(Str) ->
+ compact(Str, true).
+
+compact([Ch|Rest], B)
+ when Ch == $\n;
+ Ch == $ ;
+ Ch == $\t;
+ Ch == $\v;
+ Ch == $\r ->
+ compact(Rest, B);
+
+compact(Str, false) ->
+ Str;
+
+compact(Str, true) ->
+ lists:reverse(compact(lists:reverse(Str), false)).
diff --git a/lib/diameter/src/base/diameter_internal.hrl b/lib/diameter/src/base/diameter_internal.hrl
new file mode 100644
index 0000000000..63b35550a8
--- /dev/null
+++ b/lib/diameter/src/base/diameter_internal.hrl
@@ -0,0 +1,80 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%% Our Erlang application.
+-define(APPLICATION, diameter).
+
+%% The one and only.
+-define(DIAMETER_VERSION, 1).
+
+%% Exception for use within a module with decent protection against
+%% catching something we haven't thrown. Not foolproof but close
+%% enough. ?MODULE is rudmentary protection against catching across
+%% module boundaries, a root of much evil: always catch ?FAILURE(X),
+%% never X.
+-define(FAILURE(Reason), {{?MODULE}, {Reason}}).
+-define(THROW(Reason), throw(?FAILURE(Reason))).
+
+%% A corresponding error when failure is the best option.
+-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
+
+%% Failure reports always get a stack trace.
+-define(STACK, erlang:get_stacktrace()).
+
+%% Warning report for unexpected messages in various processes.
+-define(UNEXPECTED(F,A),
+ diameter_lib:warning_report(unexpected, {?MODULE, F, A})).
+-define(UNEXPECTED(A), ?UNEXPECTED(?FUNC, A)).
+
+%% Something to trace on.
+-define(LOG(Slogan, Details),
+ diameter_lib:log(Slogan, ?MODULE, ?LINE, Details)).
+-define(LOGC(Bool, Slogan, Details), ((Bool) andalso ?LOG(Slogan, Details))).
+
+%% Compensate for no builtin ?FUNC for use in log reports.
+-define(FUNC, element(2, element(2, process_info(self(), current_function)))).
+
+%% Disjunctive match spec condition. 'false' is to ensure that there's at
+%% least one condition.
+-define(ORCOND(List), list_to_tuple(['orelse', false | List])).
+
+%% 3588, 2.4:
+-define(APP_ID_COMMON, 0).
+-define(APP_ID_RELAY, 16#FFFFFFFF).
+
+-define(BASE, diameter_gen_base_rfc3588).
+
+%%% ---------------------------------------------------------
+
+%%% RFC 3588, ch 2.6 Peer table
+-record(diameter_peer,
+ {host_id,
+ statusT,
+ is_dynamic,
+ expiration,
+ tls_enabled}).
+
+%%% RFC 3588, ch 2.7 Realm-based routing table
+-record(diameter_realm,
+ {name,
+ app_id,
+ local_action, % LOCAL | RELAY | PROXY | REDIRECT
+ server_id,
+ is_dynamic,
+ expiration}).
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
new file mode 100644
index 0000000000..362d593b24
--- /dev/null
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -0,0 +1,272 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_lib).
+
+-export([report/2, info_report/2,
+ error_report/2,
+ warning_report/2,
+ now_diff/1,
+ time/1,
+ eval/1,
+ ip4address/1,
+ ip6address/1,
+ ipaddr/1,
+ spawn_opts/2,
+ wait/1,
+ fold_tuple/3,
+ log/4]).
+
+-include("diameter_internal.hrl").
+
+%% ---------------------------------------------------------------------------
+%% # info_report(Reason, MFA)
+%%
+%% Input: Reason = Arbitrary term indicating the reason for the report.
+%% MFA = {Module, Function, Args} to report.
+%%
+%% Output: true
+%% ---------------------------------------------------------------------------
+
+report(Reason, MFA) ->
+ info_report(Reason, MFA).
+
+info_report(Reason, MFA) ->
+ report(fun error_logger:info_report/1, Reason, MFA),
+ true.
+
+%%% ---------------------------------------------------------------------------
+%%% # error_report(Reason, MFA)
+%%% # warning_report(Reason, MFA)
+%%%
+%%% Output: false
+%%% ---------------------------------------------------------------------------
+
+error_report(Reason, MFA) ->
+ report(fun error_logger:error_report/1, Reason, MFA).
+
+warning_report(Reason, MFA) ->
+ report(fun error_logger:warning_report/1, Reason, MFA).
+
+report(Fun, Reason, MFA) ->
+ Fun([{why, Reason}, {who, self()}, {what, MFA}]),
+ false.
+
+%%% ---------------------------------------------------------------------------
+%%% # now_diff(Time)
+%%%
+%%% Description: Return timer:now_diff(now(), Time) as an {H, M, S, MicroS}
+%%% tuple instead of as integer microseconds.
+%%% ---------------------------------------------------------------------------
+
+now_diff({_,_,_} = Time) ->
+ time(timer:now_diff(erlang:now(), Time)).
+
+%%% ---------------------------------------------------------------------------
+%%% # time(Time)
+%%%
+%%% Input: Time = {MegaSec, Sec, MicroSec}
+%%% | MicroSec
+%%%
+%%% Output: {H, M, S, MicroS}
+%%% ---------------------------------------------------------------------------
+
+time({_,_,_} = Time) -> %% time of day
+ %% 24 hours = 24*60*60*1000000 = 86400000000 microsec
+ time(timer:now_diff(Time, {0,0,0}) rem 86400000000);
+
+time(Micro) -> %% elapsed time
+ Seconds = Micro div 1000000,
+ H = Seconds div 3600,
+ M = (Seconds rem 3600) div 60,
+ S = Seconds rem 60,
+ {H, M, S, Micro rem 1000000}.
+
+%%% ---------------------------------------------------------------------------
+%%% # eval(Func)
+%%% ---------------------------------------------------------------------------
+
+eval({M,F,A}) ->
+ apply(M,F,A);
+
+eval([{M,F,A} | X]) ->
+ apply(M, F, X ++ A);
+
+eval([[F|A] | X]) ->
+ eval([F | X ++ A]);
+
+eval([F|A]) ->
+ apply(F,A);
+
+eval({F}) ->
+ eval(F);
+
+eval(F) ->
+ F().
+
+%%% ---------------------------------------------------------------------------
+%%% # ip4address(Addr)
+%%%
+%%% Input: string() (eg. "10.0.0.1")
+%%% | list of integer()
+%%% | tuple of integer()
+%%%
+%%% Output: {_,_,_,_} of integer
+%%%
+%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
+%%% ---------------------------------------------------------------------------
+
+ip4address([_,_,_,_] = Addr) -> %% Length 4 string can't be an address.
+ ipaddr(list_to_tuple(Addr));
+
+%% Be brutal.
+ip4address(Addr) ->
+ try
+ {_,_,_,_} = ipaddr(Addr)
+ catch
+ error: _ ->
+ erlang:error({invalid_address, Addr, ?STACK})
+ end.
+
+%%% ---------------------------------------------------------------------------
+%%% # ip6address(Addr)
+%%%
+%%% Input: string() (eg. "1080::8:800:200C:417A")
+%%% | list of integer()
+%%% | tuple of integer()
+%%%
+%%% Output: {_,_,_,_,_,_,_,_} of integer
+%%%
+%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
+%%% ---------------------------------------------------------------------------
+
+ip6address([_,_,_,_,_,_,_,_] = Addr) -> %% Length 8 string can't be an address.
+ ipaddr(list_to_tuple(Addr));
+
+%% Be brutal.
+ip6address(Addr) ->
+ try
+ {_,_,_,_,_,_,_,_} = ipaddr(Addr)
+ catch
+ error: _ ->
+ erlang:error({invalid_address, Addr, ?STACK})
+ end.
+
+%%% ---------------------------------------------------------------------------
+%%% # ipaddr(Addr)
+%%%
+%%% Input: string() | tuple of integer()
+%%%
+%%% Output: {_,_,_,_} | {_,_,_,_,_,_,_,_}
+%%%
+%%% Exceptions: error: {invalid_address, erlang:get_stacktrace()}
+%%% ---------------------------------------------------------------------------
+
+-spec ipaddr(string() | tuple())
+ -> inet:ip_address().
+
+%% Don't convert lists of integers since a length 8 list like
+%% [$1,$0,$.,$0,$.,$0,$.,$1] is ambiguous: is it "10.0.0.1" or
+%% "49:48:46:48:46:48:46:49"?
+%%
+%% RFC 2373 defines the format parsed for v6 addresses.
+
+%% Be brutal.
+ipaddr(Addr) ->
+ try
+ ip(Addr)
+ catch
+ error: _ ->
+ erlang:error({invalid_address, ?STACK})
+ end.
+
+%% Already a tuple: ensure non-negative integers of the right size.
+ip(T)
+ when size(T) == 4;
+ size(T) == 8 ->
+ Bs = 2*size(T),
+ [] = lists:filter(fun(N) when 0 =< N -> 0 < N bsr Bs end,
+ tuple_to_list(T)),
+ T;
+
+%% Or not: convert from '.'/':'-separated decimal/hex.
+ip(Addr) ->
+ {ok, A} = inet_parse:address(Addr), %% documented in inet(3)
+ A.
+
+%%% ---------------------------------------------------------------------------
+%%% # spawn_opts(Type, Opts)
+%%% ---------------------------------------------------------------------------
+
+%% TODO: config variables.
+
+spawn_opts(server, Opts) ->
+ opts(75000, Opts);
+spawn_opts(worker, Opts) ->
+ opts(5000, Opts).
+
+opts(HeapSize, Opts) ->
+ [{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)].
+
+%%% ---------------------------------------------------------------------------
+%%% # wait(MRefs)
+%%% ---------------------------------------------------------------------------
+
+wait(L) ->
+ w([erlang:monitor(process, P) || P <- L]).
+
+w([]) ->
+ ok;
+w(L) ->
+ receive
+ {'DOWN', MRef, process, _, _} ->
+ w(lists:delete(MRef, L))
+ end.
+
+%%% ---------------------------------------------------------------------------
+%%% # fold_tuple(N, T0, T)
+%%% ---------------------------------------------------------------------------
+
+%% Replace fields in T0 by those of T starting at index N, unless the
+%% new value is 'undefined'.
+%%
+%% eg. fold_tuple(2, Hdr, #diameter_header{end_to_end_id = 42})
+
+fold_tuple(_, T, undefined) ->
+ T;
+
+fold_tuple(N, T0, T1) ->
+ {_, T} = lists:foldl(fun(V, {I,_} = IT) -> {I+1, ft(V, IT)} end,
+ {N, T0},
+ lists:nthtail(N-1, tuple_to_list(T1))),
+ T.
+
+ft(undefined, {_, T}) ->
+ T;
+ft(Value, {Idx, T}) ->
+ setelement(Idx, T, Value).
+
+%%% ----------------------------------------------------------
+%%% # log(Slogan, Mod, Line, Details)
+%%%
+%%% Called to have something to trace on for happenings of interest.
+%%% ----------------------------------------------------------
+
+log(_, _, _, _) ->
+ ok.
diff --git a/lib/diameter/src/base/diameter_misc_sup.erl b/lib/diameter/src/base/diameter_misc_sup.erl
new file mode 100644
index 0000000000..4e40476f14
--- /dev/null
+++ b/lib/diameter/src/base/diameter_misc_sup.erl
@@ -0,0 +1,58 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% The supervisor of the static server processes.
+%%
+
+-module(diameter_misc_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0]). %% supervisor start
+
+%% supervisor callback
+-export([init/1]).
+
+-define(CHILDREN, [diameter_sync, %% serialization
+ diameter_stats, %% statistics counter management
+ diameter_reg, %% service/property publishing
+ diameter_peer, %% remote peer manager
+ diameter_config]). %% configuration/restart
+
+%% start_link/0
+
+start_link() ->
+ SupName = {local, ?MODULE},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+%% init/1
+
+init([]) ->
+ Flags = {one_for_one, 1, 5},
+ Workers = lists:map(fun spec/1, ?CHILDREN),
+ {ok, {Flags, Workers}}.
+
+spec(Mod) ->
+ {Mod,
+ {Mod, start_link, []},
+ permanent,
+ 1000,
+ worker,
+ [Mod]}.
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
new file mode 100644
index 0000000000..3e78c4caef
--- /dev/null
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -0,0 +1,225 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_peer).
+
+-behaviour(gen_server).
+
+%% Interface towards transport modules ...
+-export([recv/2,
+ up/1,
+ up/2]).
+
+%% ... and the stack.
+-export([start/3,
+ send/2,
+ close/1,
+ abort/1,
+ notify/2]).
+
+%% Server start.
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+ terminate/2,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ code_change/3]).
+
+%% debug
+-export([state/0,
+ uptime/0]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+%% Registered name of the server.
+-define(SERVER, ?MODULE).
+
+%% Server state.
+-record(state, {id = now()}).
+
+%%% ---------------------------------------------------------------------------
+%%% # notify/2
+%%% ---------------------------------------------------------------------------
+
+notify(SvcName, T) ->
+ rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}).
+
+%%% ---------------------------------------------------------------------------
+%%% # start/3
+%%% ---------------------------------------------------------------------------
+
+start(T, Opts, #diameter_service{} = Svc) ->
+ {Mod, Cfg} = split_transport(Opts),
+ apply(Mod, start, [T, Svc, Cfg]).
+
+%%% ---------------------------------------------------------------------------
+%%% # up/[12]
+%%% ---------------------------------------------------------------------------
+
+up(Pid) -> %% accepting transport
+ ifc_send(Pid, {self(), connected}).
+
+up(Pid, Remote) -> %% connecting transport
+ ifc_send(Pid, {self(), connected, Remote}).
+
+%%% ---------------------------------------------------------------------------
+%%% # recv/2
+%%% ---------------------------------------------------------------------------
+
+recv(Pid, Pkt) ->
+ ifc_send(Pid, {recv, Pkt}).
+
+%%% ---------------------------------------------------------------------------
+%%% # send/2
+%%% ---------------------------------------------------------------------------
+
+send(Pid, #diameter_packet{transport_data = undefined,
+ bin = Bin}) ->
+ send(Pid, Bin);
+
+send(Pid, Pkt) ->
+ ifc_send(Pid, {send, Pkt}).
+
+%%% ---------------------------------------------------------------------------
+%%% # close/1
+%%% ---------------------------------------------------------------------------
+
+close(Pid) ->
+ ifc_send(Pid, {close, self()}).
+
+%%% ---------------------------------------------------------------------------
+%%% # abort/1
+%%% ---------------------------------------------------------------------------
+
+abort(Pid) ->
+ exit(Pid, shutdown).
+
+%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+
+start_link() ->
+ ServerName = {local, ?SERVER},
+ Module = ?MODULE,
+ Args = [],
+ Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
+ gen_server:start_link(ServerName, Module, Args, Options).
+
+state() ->
+ call(state).
+
+uptime() ->
+ call(uptime).
+
+%%% ----------------------------------------------------------
+%%% # init(Role)
+%%% ----------------------------------------------------------
+
+init([]) ->
+ {ok, #state{}}.
+
+%%% ----------------------------------------------------------
+%%% # handle_call(Request, From, State)
+%%% ----------------------------------------------------------
+
+handle_call(state, _, State) ->
+ {reply, State, State};
+
+handle_call(uptime, _, #state{id = Time} = State) ->
+ {reply, diameter_lib:now_diff(Time), State};
+
+handle_call(Req, From, State) ->
+ ?UNEXPECTED([Req, From]),
+ {reply, nok, State}.
+
+%%% ----------------------------------------------------------
+%%% # handle_cast(Request, State)
+%%% ----------------------------------------------------------
+
+handle_cast(Msg, State) ->
+ ?UNEXPECTED([Msg]),
+ {noreply, State}.
+
+%%% ----------------------------------------------------------
+%%% # handle_info(Request, State)
+%%% ----------------------------------------------------------
+
+%% Remote service is distributing a message.
+handle_info({notify, SvcName, T}, S) ->
+ bang(diameter_service:whois(SvcName), T),
+ {noreply, S};
+
+handle_info(Info, State) ->
+ ?UNEXPECTED([Info]),
+ {noreply, State}.
+
+%% ----------------------------------------------------------
+%% terminate(Reason, State)
+%% ----------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%% ----------------------------------------------------------
+%% code_change(OldVsn, State, Extra)
+%% ----------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%% ---------------------------------------------------------
+%% INTERNAL FUNCTIONS
+%% ---------------------------------------------------------
+
+%% ifc_send/2
+%%
+%% Send something over the transport interface.
+
+ifc_send(Pid, T) ->
+ Pid ! {diameter, T}.
+
+%% bang/2
+
+bang(undefined = No, _) ->
+ No;
+bang(Pid, T) ->
+ Pid ! T.
+
+%% split_transport/1
+%%
+%% Split options into transport module, transport config and
+%% remaining options.
+
+split_transport(Opts) ->
+ {[M,C], _} = proplists:split(Opts, [transport_module,
+ transport_config]),
+ {value(M, diameter_tcp), value(C, [])}.
+
+value([{_,V}], _) ->
+ V;
+value([], V) ->
+ V.
+
+%% call/1
+
+call(Request) ->
+ gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
new file mode 100644
index 0000000000..282fa2742f
--- /dev/null
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -0,0 +1,777 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% This module implements (as a process) the RFC 3588 Peer State
+%% Machine modulo the necessity of adapting the peer election to the
+%% fact that we don't know the identity of a peer until we've
+%% received a CER/CEA from it.
+%%
+
+-module(diameter_peer_fsm).
+-behaviour(gen_server).
+
+%% Interface towards diameter_watchdog.
+-export([start/3]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+%% diameter_peer_fsm_sup callback
+-export([start_link/1]).
+
+%% internal callbacks
+-export([match/1]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+-include("diameter_types.hrl").
+-include("diameter_gen_base_rfc3588.hrl").
+
+-define(GOAWAY, ?'DIAMETER_BASE_DISCONNECT-CAUSE_DO_NOT_WANT_TO_TALK_TO_YOU').
+-define(REBOOT, ?'DIAMETER_BASE_DISCONNECT-CAUSE_REBOOTING').
+
+-define(NO_INBAND_SECURITY, 0).
+-define(TLS, 1).
+
+-define(LOOP_TIMEOUT, 2000).
+
+%% RFC 3588:
+%%
+%% Timeout An application-defined timer has expired while waiting
+%% for some event.
+%%
+-define(EVENT_TIMEOUT, 10000).
+
+%% How long to wait for a DPA in response to DPR before simply
+%% aborting. Used to distinguish between shutdown and not but there's
+%% not really any need. Stopping a service will require a timeout if
+%% the peer doesn't answer DPR so the value should be short-ish.
+-define(DPA_TIMEOUT, 1000).
+
+-record(state,
+ {state = 'Wait-Conn-Ack' %% state of RFC 3588 Peer State Machine
+ :: 'Wait-Conn-Ack' | recv_CER | 'Wait-CEA' | 'Open',
+ mode :: accept | connect | {connect, reference()},
+ parent :: pid(),
+ transport :: pid(),
+ service :: #diameter_service{},
+ dpr = false :: false | {'Unsigned32'(), 'Unsigned32'()}}).
+ %% | hop by hop and end to end identifiers
+
+%% There are non-3588 states possible as a consequence of 5.6.1 of the
+%% standard and the corresponding problem for incoming CEA's: we don't
+%% know who we're talking to until either a CER or CEA has been
+%% received. The CEA problem in particular makes it impossible to
+%% follow the state machine exactly as documented in 3588: there can
+%% be no election until the CEA arrives and we have an Origin-Host to
+%% elect.
+
+%%
+%% Once upon a time start/2 started a process akin to that started by
+%% start/3 below, which in turn started a watchdog/transport process
+%% with the result that the watchdog could send DWR/DWA regardless of
+%% whether or not the corresponding Peer State Machine was in its open
+%% state; that is, before capabilities exchange had taken place. This
+%% is not what RFC's 3588 and 3539 say (albeit not very clearly).
+%% Watchdog messages are only exchanged on *open* connections, so the
+%% 3539 state machine is more naturally placed on top of the 3588 Peer
+%% State Machine rather than closer to the transport. This is what we
+%% now do below: connect/accept call diameter_watchdog and return the
+%% pid of the watchdog process, and the watchdog in turn calls start/3
+%% below to start the process implementing the Peer State Machine. The
+%% former is a "peer" in diameter_service while the latter is a
+%% "conn". In a sense, diameter_service sees the watchdog as
+%% implementing the Peer State Machine and the process implemented
+%% here as being the transport, not being aware of the watchdog at
+%% all.
+%%
+
+%%% ---------------------------------------------------------------------------
+%%% # start({connect|accept, Ref}, Opts, Service)
+%%%
+%%% Output: Pid
+%%% ---------------------------------------------------------------------------
+
+%% diameter_config requires a non-empty list of applications on the
+%% service but diameter_service then constrains the list to any
+%% specified on the transport in question. Check here that the list is
+%% still non-empty.
+
+start({_, Ref} = Type, Opts, #diameter_service{applications = Apps} = Svc) ->
+ [] /= Apps orelse ?ERROR({no_apps, Type, Opts}),
+ T = {self(), Type, Opts, Svc},
+ {ok, Pid} = diameter_peer_fsm_sup:start_child(T),
+ diameter_stats:reg(Pid, Ref),
+ Pid.
+
+start_link(T) ->
+ {ok, _} = proc_lib:start_link(?MODULE,
+ init,
+ [T],
+ infinity,
+ diameter_lib:spawn_opts(server, [])).
+
+%%% ---------------------------------------------------------------------------
+%%% ---------------------------------------------------------------------------
+
+%% init/1
+
+init(T) ->
+ proc_lib:init_ack({ok, self()}),
+ gen_server:enter_loop(?MODULE, [], i(T)).
+
+i({WPid, {M, _} = T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
+ putr(dwa, dwa(Caps)),
+ {ok, TPid, Svc} = start_transport(T, Opts, Svc0),
+ erlang:monitor(process, TPid),
+ erlang:monitor(process, WPid),
+ #state{parent = WPid,
+ transport = TPid,
+ mode = M,
+ service = Svc}.
+%% The transport returns its local ip addresses so that different
+%% transports on the same service can use different local addresses.
+%% The local addresses are put into Host-IP-Address avps here when
+%% sending capabilities exchange messages.
+%%
+%% Invalid transport config may cause us to crash but note that the
+%% watchdog start (start/2) succeeds regardless so as not to crash the
+%% service.
+
+start_transport(T, Opts, Svc) ->
+ case diameter_peer:start(T, Opts, Svc) of
+ {ok, TPid} ->
+ {ok, TPid, Svc};
+ {ok, TPid, [_|_] = Addrs} ->
+ #diameter_service{capabilities = Caps0} = Svc,
+ Caps = Caps0#diameter_caps{host_ip_address = Addrs},
+ {ok, TPid, Svc#diameter_service{capabilities = Caps}};
+ No ->
+ exit({shutdown, No})
+ end.
+
+%% handle_call/3
+
+handle_call(_, _, State) ->
+ {reply, nok, State}.
+
+%% handle_cast/2
+
+handle_cast(_, State) ->
+ {noreply, State}.
+
+%% handle_info/1
+
+handle_info(T, #state{} = State) ->
+ try transition(T, State) of
+ ok ->
+ {noreply, State};
+ #state{state = X} = S ->
+ ?LOGC(X =/= State#state.state, transition, X),
+ {noreply, S};
+ {stop, Reason} ->
+ ?LOG(stop, Reason),
+ x(Reason, State);
+ stop ->
+ ?LOG(stop, T),
+ x(T, State)
+ catch
+ throw: {?MODULE, Tag, Reason} ->
+ ?LOG(Tag, {Reason, T}),
+ {stop, {shutdown, Reason}, State}
+ end.
+
+x(Reason, #state{} = S) ->
+ close_wd(Reason, S),
+ {stop, {shutdown, Reason}, S}.
+
+%% terminate/2
+
+terminate(_, _) ->
+ ok.
+
+%% code_change/3
+
+code_change(_, State, _) ->
+ {ok, State}.
+
+%%% ---------------------------------------------------------------------------
+%%% ---------------------------------------------------------------------------
+
+putr(Key, Val) ->
+ put({?MODULE, Key}, Val).
+
+getr(Key) ->
+ get({?MODULE, Key}).
+
+%% transition/2
+
+%% Connection to peer.
+transition({diameter, {TPid, connected, Remote}},
+ #state{state = PS,
+ mode = M}
+ = S) ->
+ 'Wait-Conn-Ack' = PS, %% assert
+ connect = M, %%
+ send_CER(S#state{mode = {M, Remote},
+ transport = TPid});
+
+%% Connection from peer.
+transition({diameter, {TPid, connected}},
+ #state{state = PS,
+ mode = M,
+ parent = Pid}
+ = S) ->
+ 'Wait-Conn-Ack' = PS, %% assert
+ accept = M, %%
+ Pid ! {accepted, self()},
+ start_timer(S#state{state = recv_CER,
+ transport = TPid});
+
+%% Incoming message from the transport.
+transition({diameter, {recv, Pkt}}, S) ->
+ recv(Pkt, S);
+
+%% Timeout when still in the same state ...
+transition({timeout, PS}, #state{state = PS}) ->
+ stop;
+
+%% ... or not.
+transition({timeout, _}, _) ->
+ ok;
+
+%% Outgoing message.
+transition({send, Msg}, #state{transport = TPid}) ->
+ send(TPid, Msg),
+ ok;
+
+%% Request for graceful shutdown.
+transition({shutdown, Pid}, #state{parent = Pid, dpr = false} = S) ->
+ dpr(?GOAWAY, S);
+transition({shutdown, Pid}, #state{parent = Pid}) ->
+ ok;
+
+%% Application shutdown.
+transition(shutdown, #state{dpr = false} = S) ->
+ dpr(?REBOOT, S);
+transition(shutdown, _) -> %% DPR already send: ensure expected timeout
+ dpa_timer(),
+ ok;
+
+%% Request to close the transport connection.
+transition({close = T, Pid}, #state{parent = Pid,
+ transport = TPid}) ->
+ diameter_peer:close(TPid),
+ {stop, T};
+
+%% DPA reception has timed out.
+transition(dpa_timeout, _) ->
+ stop;
+
+%% Someone wants to know a resolved port: forward to the transport process.
+transition({resolve_port, _Pid} = T, #state{transport = TPid}) ->
+ TPid ! T,
+ ok;
+
+%% Parent or transport has died.
+transition({'DOWN', _, process, P, _},
+ #state{parent = Pid,
+ transport = TPid})
+ when P == Pid;
+ P == TPid ->
+ stop;
+
+%% State query.
+transition({state, Pid}, #state{state = S, transport = TPid}) ->
+ Pid ! {self(), [S, TPid]},
+ ok.
+
+%% Crash on anything unexpected.
+
+%% send_CER/1
+
+send_CER(#state{mode = {connect, Remote},
+ service = #diameter_service{capabilities = Caps},
+ transport = TPid}
+ = S) ->
+ req_send_CER(Caps#diameter_caps.origin_host, Remote)
+ orelse
+ close(connected, S),
+ CER = build_CER(S),
+ ?LOG(send, 'CER'),
+ send(TPid, encode(CER)),
+ start_timer(S#state{state = 'Wait-CEA'}).
+
+%% Register ourselves as connecting to the remote endpoint in
+%% question. This isn't strictly necessary since a peer implementing
+%% the 3588 Peer State Machine should reject duplicate connection's
+%% from the same peer but there's little point in us setting up a
+%% duplicate connection in the first place. This could also include
+%% the transport protocol being used but since we're blind to
+%% transport just avoid duplicate connections to the same host/port.
+req_send_CER(OriginHost, Remote) ->
+ register_everywhere({?MODULE, connection, OriginHost, {remote, Remote}}).
+
+%% start_timer/1
+
+start_timer(#state{state = PS} = S) ->
+ erlang:send_after(?EVENT_TIMEOUT, self(), {timeout, PS}),
+ S.
+
+%% build_CER/1
+
+build_CER(#state{service = #diameter_service{capabilities = Caps}}) ->
+ {ok, CER} = diameter_capx:build_CER(Caps),
+ CER.
+
+%% encode/1
+
+encode(Rec) ->
+ #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Rec),
+ Bin.
+
+%% recv/2
+
+%% RFC 3588 has result code 5015 for an invalid length but if a
+%% transport is detecting message boundaries using the length header
+%% then a length error will likely lead to further errors.
+
+recv(#diameter_packet{header = #diameter_header{length = Len}
+ = Hdr,
+ bin = Bin},
+ S)
+ when Len < 20;
+ (0 /= Len rem 4 orelse bit_size(Bin) /= 8*Len) ->
+ discard(invalid_message_length, recv, [size(Bin),
+ bit_size(Bin) rem 8,
+ Hdr,
+ S]);
+
+recv(#diameter_packet{header = #diameter_header{} = Hdr}
+ = Pkt,
+ #state{parent = Pid}
+ = S) ->
+ Name = diameter_codec:msg_name(Hdr),
+ Pid ! {recv, self(), Name, Pkt},
+ diameter_stats:incr({msg_id(Name, Hdr), recv}), %% count received
+ rcv(Name, Pkt, S);
+
+recv(#diameter_packet{header = undefined,
+ bin = Bin}
+ = Pkt,
+ S) ->
+ recv(Pkt#diameter_packet{header = diameter_codec:decode_header(Bin)}, S);
+
+recv(Bin, S)
+ when is_binary(Bin) ->
+ recv(#diameter_packet{bin = Bin}, S);
+
+recv(#diameter_packet{header = false} = Pkt, S) ->
+ discard(truncated_header, recv, [Pkt, S]).
+
+msg_id({_,_,_} = T, _) ->
+ T;
+msg_id(_, Hdr) ->
+ diameter_codec:msg_id(Hdr).
+
+%% Treat invalid length as a transport error and die. Especially in
+%% the TCP case, in which there's no telling where the next message
+%% begins in the incoming byte stream, keeping a crippled connection
+%% alive may just make things worse.
+
+discard(Reason, F, A) ->
+ diameter_stats:incr(Reason),
+ diameter_lib:warning_report(Reason, {?MODULE, F, A}),
+ throw({?MODULE, abort, Reason}).
+
+%% rcv/3
+
+%% Incoming CEA.
+rcv('CEA', Pkt, #state{state = 'Wait-CEA'} = S) ->
+ handle_CEA(Pkt, S);
+
+%% Incoming CER
+rcv('CER' = N, Pkt, #state{state = recv_CER} = S) ->
+ handle_request(N, Pkt, S);
+
+%% Anything but CER/CEA in a non-Open state is an error, as is
+%% CER/CEA in anything but recv_CER/Wait-CEA.
+rcv(Name, _, #state{state = PS})
+ when PS /= 'Open';
+ Name == 'CER';
+ Name == 'CEA' ->
+ {stop, {Name, PS}};
+
+rcv(N, Pkt, S)
+ when N == 'DWR';
+ N == 'DPR' ->
+ handle_request(N, Pkt, S);
+
+%% DPA even though we haven't sent DPR: ignore.
+rcv('DPA', _Pkt, #state{dpr = false}) ->
+ ok;
+
+%% DPA in response to DPR. We could check the sequence numbers but
+%% don't bother, just close.
+rcv('DPA' = N, _Pkt, #state{transport = TPid}) ->
+ diameter_peer:close(TPid),
+ {stop, N};
+
+rcv(_, _, _) ->
+ ok.
+
+%% send/2
+
+%% Msg here could be a #diameter_packet or a binary depending on who's
+%% sending. In particular, the watchdog will send DWR as a binary
+%% while messages coming from clients will be in a #diameter_packet.
+send(Pid, Msg) ->
+ diameter_stats:incr({diameter_codec:msg_id(Msg), send}),
+ diameter_peer:send(Pid, Msg).
+
+%% handle_request/3
+
+handle_request(Type, #diameter_packet{} = Pkt, S) ->
+ ?LOG(recv, Type),
+ send_answer(Type, diameter_codec:decode(?BASE, Pkt), S).
+
+%% send_answer/3
+
+send_answer(Type, ReqPkt, #state{transport = TPid} = S) ->
+ #diameter_packet{header = #diameter_header{version = V,
+ end_to_end_id = Eid,
+ hop_by_hop_id = Hid,
+ is_proxiable = P},
+ transport_data = TD}
+ = ReqPkt,
+
+ {Answer, PostF} = build_answer(Type, V, ReqPkt, S),
+
+ Pkt = #diameter_packet{header = #diameter_header{version = V,
+ end_to_end_id = Eid,
+ hop_by_hop_id = Hid,
+ is_proxiable = P},
+ msg = Answer,
+ transport_data = TD},
+
+ send(TPid, diameter_codec:encode(?BASE, Pkt)),
+ eval(PostF, S).
+
+eval([F|A], S) ->
+ apply(F, A ++ [S]);
+eval(ok, S) ->
+ S.
+
+%% build_answer/4
+
+build_answer('CER',
+ ?DIAMETER_VERSION,
+ #diameter_packet{msg = CER,
+ header = #diameter_header{is_error = false},
+ errors = []}
+ = Pkt,
+ #state{service = Svc}
+ = S) ->
+ #diameter_service{capabilities = #diameter_caps{origin_host = OH}}
+ = Svc,
+
+ {SupportedApps,
+ #diameter_caps{origin_host = DH} = RCaps,
+ #diameter_base_CEA{'Result-Code' = RC}
+ = CEA}
+ = recv_CER(CER, S),
+
+ try
+ 2001 == RC %% DIAMETER_SUCCESS
+ orelse ?THROW({sent_CEA, RC}),
+ register_everywhere({?MODULE, connection, OH, DH})
+ orelse ?THROW({election_lost, 4003}),
+ #diameter_base_CEA{'Inband-Security-Id' = [IS]}
+ = CEA,
+ {CEA, [fun open/5, Pkt, SupportedApps, RCaps, {accept, IS}]}
+ catch
+ ?FAILURE({Reason, RC}) ->
+ {answer('CER', S) ++ [{'Result-Code', RC}],
+ [fun close/2, {'CER', Reason, DH}]}
+ end;
+
+%% The error checks below are similar to those in diameter_service for
+%% other messages. Should factor out the commonality.
+
+build_answer(Type, V, #diameter_packet{header = H, errors = Es} = Pkt, S) ->
+ FailedAvp = failed_avp([A || {_,A} <- Es]),
+ Ans = answer(answer(Type, S), V, H, Es),
+ {set(Ans, FailedAvp), if 'CER' == Type ->
+ [fun close/2, {Type, V, Pkt}];
+ true ->
+ ok
+ end}.
+
+failed_avp([] = No) ->
+ No;
+failed_avp(Avps) ->
+ [{'Failed-AVP', [[{'AVP', Avps}]]}].
+
+set(Ans, []) ->
+ Ans;
+set(['answer-message' | _] = Ans, FailedAvp) ->
+ Ans ++ [{'AVP', [FailedAvp]}];
+set([_|_] = Ans, FailedAvp) ->
+ Ans ++ FailedAvp.
+
+answer([_, OH, OR | _], _, #diameter_header{is_error = true}, _) ->
+ ['answer-message', OH, OR, {'Result-Code', 3008}];
+
+answer([_, OH, OR | _], _, _, [Bs|_])
+ when is_bitstring(Bs) ->
+ ['answer-message', OH, OR, {'Result-Code', 3009}];
+
+answer(Ans, ?DIAMETER_VERSION, _, Es) ->
+ Ans ++ [{'Result-Code', rc(Es)}];
+
+answer(Ans, _, _, _) ->
+ Ans ++ [{'Result-Code', 5011}]. %% DIAMETER_UNSUPPORTED_VERSION
+
+rc([]) ->
+ 2001; %% DIAMETER_SUCCESS
+rc([{RC,_}|_]) ->
+ RC;
+rc([RC|_]) ->
+ RC.
+
+%% DIAMETER_INVALID_HDR_BITS 3008
+%% A request was received whose bits in the Diameter header were
+%% either set to an invalid combination, or to a value that is
+%% inconsistent with the command code's definition.
+
+%% DIAMETER_INVALID_AVP_BITS 3009
+%% A request was received that included an AVP whose flag bits are
+%% set to an unrecognized value, or that is inconsistent with the
+%% AVP's definition.
+
+%% ELECTION_LOST 4003
+%% The peer has determined that it has lost the election process and
+%% has therefore disconnected the transport connection.
+
+%% DIAMETER_NO_COMMON_APPLICATION 5010
+%% This error is returned when a CER message is received, and there
+%% are no common applications supported between the peers.
+
+%% DIAMETER_UNSUPPORTED_VERSION 5011
+%% This error is returned when a request was received, whose version
+%% number is unsupported.
+
+%% answer/2
+
+answer('DWR', _) ->
+ getr(dwa);
+
+answer(Name, #state{service = #diameter_service{capabilities = Caps}}) ->
+ a(Name, Caps).
+
+a('CER', #diameter_caps{vendor_id = Vid,
+ origin_host = Host,
+ origin_realm = Realm,
+ host_ip_address = Addrs,
+ product_name = Name}) ->
+ ['CEA', {'Origin-Host', Host},
+ {'Origin-Realm', Realm},
+ {'Host-IP-Address', Addrs},
+ {'Vendor-Id', Vid},
+ {'Product-Name', Name}];
+
+a('DPR', #diameter_caps{origin_host = Host,
+ origin_realm = Realm}) ->
+ ['DPA', {'Origin-Host', Host},
+ {'Origin-Realm', Realm}].
+
+%% recv_CER/2
+
+recv_CER(CER, #state{service = Svc}) ->
+ {ok, T} = diameter_capx:recv_CER(CER, Svc),
+ T.
+
+%% handle_CEA/1
+
+handle_CEA(#diameter_packet{header = #diameter_header{version = V},
+ bin = Bin}
+ = Pkt,
+ #state{service = #diameter_service{capabilities = LCaps}}
+ = S)
+ when is_binary(Bin) ->
+ ?LOG(recv, 'CEA'),
+
+ ?DIAMETER_VERSION == V orelse close({version, V}, S),
+
+ #diameter_packet{msg = CEA, errors = Errors}
+ = DPkt
+ = diameter_codec:decode(?BASE, Pkt),
+
+ [] == Errors orelse close({errors, Errors}, S),
+
+ {SApps, [IS], #diameter_caps{origin_host = DH} = RCaps}
+ = recv_CEA(CEA, S),
+
+ #diameter_caps{origin_host = OH}
+ = LCaps,
+
+ %% Ensure that we don't already have a connection to the peer in
+ %% question. This isn't the peer election of 3588 except in the
+ %% sense that, since we don't know who we're talking to until we
+ %% receive a CER/CEA, the first that arrives wins the right to a
+ %% connection with the peer.
+
+ register_everywhere({?MODULE, connection, OH, DH})
+ orelse close({'CEA', DH}, S),
+
+ open(DPkt, SApps, RCaps, {connect, IS}, S).
+
+%% recv_CEA/2
+
+recv_CEA(CEA, #state{service = Svc} = S) ->
+ case diameter_capx:recv_CEA(CEA, Svc) of
+ {ok, {_,_}} -> %% return from old code
+ close({'CEA', update}, S);
+ {ok, {[], _, _}} ->
+ close({'CEA', no_common_application}, S);
+ {ok, {_, [], _}} ->
+ close({'CEA', no_common_security}, S);
+ {ok, {_,_,_} = T} ->
+ T;
+ {error, Reason} ->
+ close({'CEA', Reason}, S)
+ end.
+
+%% open/5
+
+open(Pkt, SupportedApps, RCaps, {Type, IS}, #state{parent = Pid,
+ service = Svc}
+ = S) ->
+ #diameter_service{capabilities = #diameter_caps{origin_host = OH,
+ inband_security_id = LS}
+ = LCaps}
+ = Svc,
+ #diameter_caps{origin_host = DH}
+ = RCaps,
+
+ tls_ack(lists:member(?TLS, LS), Type, IS, S),
+ Pid ! {open, self(), {OH,DH}, {capz(LCaps, RCaps), SupportedApps, Pkt}},
+
+ S#state{state = 'Open'}.
+
+%% We've advertised TLS support: tell the transport the result
+%% and expect a reply when the handshake is complete.
+tls_ack(true, Type, IS, #state{transport = TPid} = S) ->
+ Ref = make_ref(),
+ MRef = erlang:monitor(process, TPid),
+ TPid ! {diameter, {tls, Ref, Type, IS == ?TLS}},
+ receive
+ {diameter, {tls, Ref}} ->
+ erlang:demonitor(MRef, [flush]);
+ {'DOWN', MRef, process, _, _} = T ->
+ close({tls_ack, T}, S)
+ end;
+
+%% Or not. Don't send anything to the transport so that transports
+%% not supporting TLS work as before without modification.
+tls_ack(false, _, _, _) ->
+ ok.
+
+capz(#diameter_caps{} = L, #diameter_caps{} = R) ->
+ #diameter_caps{}
+ = list_to_tuple([diameter_caps | lists:zip(tl(tuple_to_list(L)),
+ tl(tuple_to_list(R)))]).
+
+%% close/2
+
+%% Tell the watchdog that our death isn't due to transport failure.
+close(Reason, #state{parent = Pid}) ->
+ close_wd(Reason, Pid),
+ throw({?MODULE, close, Reason}).
+
+%% close_wd/2
+
+%% Ensure the watchdog dies if DPR has been sent ...
+close_wd(_, #state{dpr = false}) ->
+ ok;
+close_wd(Reason, #state{parent = Pid}) ->
+ close_wd(Reason, Pid);
+
+%% ... or otherwise
+close_wd(Reason, Pid) ->
+ Pid ! {close, self(), Reason}.
+
+%% dwa/1
+
+dwa(#diameter_caps{origin_host = OH,
+ origin_realm = OR,
+ origin_state_id = OSI}) ->
+ ['DWA', {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Origin-State-Id', OSI}].
+
+%% dpr/2
+
+dpr(Cause, #state{transport = TPid,
+ service = #diameter_service{capabilities = Caps}}
+ = S) ->
+ #diameter_caps{origin_host = OH,
+ origin_realm = OR}
+ = Caps,
+
+ Bin = encode(['DPR', {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Disconnect-Cause', Cause}]),
+ send(TPid, Bin),
+ dpa_timer(),
+ ?LOG(send, 'DPR'),
+ S#state{dpr = diameter_codec:sequence_numbers(Bin)}.
+
+dpa_timer() ->
+ erlang:send_after(?DPA_TIMEOUT, self(), dpa_timeout).
+
+%% register_everywhere/1
+%%
+%% Register a term and ensure it's not registered elsewhere. Note that
+%% two process that simultaneously register the same term may well
+%% both fail to do so this isn't foolproof.
+
+register_everywhere(T) ->
+ diameter_reg:add_new(T)
+ andalso unregistered(T).
+
+unregistered(T) ->
+ {ResL, _} = rpc:multicall(?MODULE, match, [{node(), T}]),
+ lists:all(fun(L) -> [] == L end, ResL).
+
+match({Node, _})
+ when Node == node() ->
+ [];
+match({_, T}) ->
+ try
+ diameter_reg:match(T)
+ catch
+ _:_ -> []
+ end.
diff --git a/lib/diameter/src/base/diameter_peer_fsm_sup.erl b/lib/diameter/src/base/diameter_peer_fsm_sup.erl
new file mode 100644
index 0000000000..995eaf74d0
--- /dev/null
+++ b/lib/diameter/src/base/diameter_peer_fsm_sup.erl
@@ -0,0 +1,63 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% The supervisor of peer_fsm processes.
+%%
+
+-module(diameter_peer_fsm_sup).
+
+-behaviour(supervisor).
+
+-define(NAME, ?MODULE). %% supervisor name
+
+%%-----------------------------------------------------------------
+%% External exports
+%%-----------------------------------------------------------------
+
+-export([start_link/0, %% supervisor start
+ start_child/1]). %% peer fsm start
+
+-export([init/1]).
+
+%% start_link/0
+
+start_link() ->
+ SupName = {local, ?NAME},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+%% start_child/1
+%%
+%% Start a peer_fsm process.
+
+start_child(T) ->
+ supervisor:start_child(?NAME, [T]).
+
+%% init/1
+
+init([]) ->
+ Mod = diameter_peer_fsm,
+ Flags = {simple_one_for_one, 0, 1},
+ ChildSpec = {Mod,
+ {Mod, start_link, []},
+ temporary,
+ 1000,
+ worker,
+ [Mod]},
+ {ok, {Flags, [ChildSpec]}}.
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
new file mode 100644
index 0000000000..882b9da238
--- /dev/null
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -0,0 +1,327 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% The module implements a simple term -> pid registry.
+%%
+
+-module(diameter_reg).
+-compile({no_auto_import, [monitor/2]}).
+
+-behaviour(gen_server).
+
+-export([add/1,
+ add_new/1,
+ del/1,
+ repl/2,
+ match/1]).
+
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+ terminate/2,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ code_change/3]).
+
+%% test
+-export([pids/0,
+ terms/0]).
+
+%% debug
+-export([state/0,
+ uptime/0]).
+
+-include("diameter_internal.hrl").
+
+-define(SERVER, ?MODULE).
+-define(TABLE, ?MODULE).
+
+%% Table entry used to keep from starting more than one monitor on the
+%% same process. This isn't a problem but there's no point in starting
+%% multiple monitors if we can avoid it. Note that we can't have a 2-tuple
+%% keyed on Pid since a registered term can be anything. Want the entry
+%% keyed on Pid so that lookup is fast.
+-define(MONITOR(Pid, MRef), {Pid, monitor, MRef}).
+
+%% Table entry containing the Term -> Pid mapping.
+-define(MAPPING(Term, Pid), {Term, Pid}).
+
+-record(state, {id = now()}).
+
+%%% ----------------------------------------------------------
+%%% # add(T)
+%%%
+%%% Input: Term = term()
+%%%
+%%% Output: true
+%%%
+%%% Description: Associate the specified term with self(). The list of pids
+%%% having this or other assocations can be retrieved using
+%%% match/1.
+%%%
+%%% An association is removed when the calling process dies
+%%% or as a result of calling del/1. Adding the same term
+%%% more than once is equivalent to adding it exactly once.
+%%%
+%%% Note that since match/1 takes a pattern as argument,
+%%% specifying a term that contains match variables is
+%%% probably not a good idea
+%%% ----------------------------------------------------------
+
+-spec add(any())
+ -> true.
+
+add(T) ->
+ call({add, fun ets:insert/2, T, self()}).
+
+%%% ----------------------------------------------------------
+%%% # add_new(T)
+%%%
+%%% Input: T = term()
+%%%
+%%% Output: true | false
+%%%
+%%% Description: Like add/1 but only one process is allowed to have the
+%%% the association, false being returned if an association
+%%% already exists.
+%%% ----------------------------------------------------------
+
+-spec add_new(any())
+ -> boolean().
+
+add_new(T) ->
+ call({add, fun insert_new/2, T, self()}).
+
+%%% ----------------------------------------------------------
+%%% # repl(T, NewT)
+%%%
+%%% Input: T, NewT = term()
+%%%
+%%% Output: true | false
+%%%
+%%% Description: Like add/1 but only replace an existing association on T,
+%%% false being returned if it doesn't exist.
+%%% ----------------------------------------------------------
+
+-spec repl(any(), any())
+ -> boolean().
+
+repl(T, U) ->
+ call({repl, T, U, self()}).
+
+%%% ----------------------------------------------------------
+%%% # del(Term)
+%%%
+%%% Input: Term = term()
+%%%
+%%% Output: true
+%%%
+%%% Description: Remove any existing association of Term with self().
+%%% ----------------------------------------------------------
+
+-spec del(any())
+ -> true.
+
+del(T) ->
+ call({del, T, self()}).
+
+%%% ----------------------------------------------------------
+%%% # match(Pat)
+%%%
+%%% Input: Pat = pattern in the sense of ets:match_object/2.
+%%%
+%%% Output: list of {Term, Pid}
+%%%
+%%% Description: Return the list of associations whose Term, as specified
+%%% to add/1 or add_new/1, matches the specified pattern.
+%%%
+%%% Note that there's no guarantee that the returned processes
+%%% are still alive. (Although one that isn't will soon have
+%%% its associations removed.)
+%%% ----------------------------------------------------------
+
+-spec match(tuple())
+ -> [{term(), pid()}].
+
+match(Pat) ->
+ ets:match_object(?TABLE, ?MAPPING(Pat, '_')).
+
+%% ---------------------------------------------------------
+%% EXPORTED INTERNAL FUNCTIONS
+%% ---------------------------------------------------------
+
+start_link() ->
+ ServerName = {local, ?SERVER},
+ Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
+ gen_server:start_link(ServerName, ?MODULE, [], Options).
+
+state() ->
+ call(state).
+
+uptime() ->
+ call(uptime).
+
+%% pids/0
+%%
+%% Output: list of {Pid, [Term, ...]}
+
+pids() ->
+ to_list(fun swap/1).
+
+to_list(Fun) ->
+ ets:foldl(fun(T,A) -> acc(Fun, T, A) end, orddict:new(), ?TABLE).
+
+acc(Fun, ?MAPPING(Term, Pid), Dict) ->
+ append(Fun({Term, Pid}), Dict);
+acc(_, _, Dict) ->
+ Dict.
+
+append({K,V}, Dict) ->
+ orddict:append(K, V, Dict).
+
+id(T) -> T.
+
+%% terms/0
+%%
+%% Output: list of {Term, [Pid, ...]}
+
+terms() ->
+ to_list(fun id/1).
+
+swap({X,Y}) -> {Y,X}.
+
+%%% ----------------------------------------------------------
+%%% # init(Role)
+%%%
+%%% Output: {ok, State}
+%%% ----------------------------------------------------------
+
+init(_) ->
+ ets:new(?TABLE, [bag, named_table]),
+ {ok, #state{}}.
+
+%%% ----------------------------------------------------------
+%%% # handle_call(Request, From, State)
+%%% ----------------------------------------------------------
+
+handle_call({add, Fun, Key, Pid}, _, State) ->
+ B = Fun(?TABLE, {Key, Pid}),
+ monitor(B andalso no_monitor(Pid), Pid),
+ {reply, B, State};
+
+handle_call({del, Key, Pid}, _, State) ->
+ {reply, ets:delete_object(?TABLE, ?MAPPING(Key, Pid)), State};
+
+handle_call({repl, T, U, Pid}, _, State) ->
+ MatchSpec = [{?MAPPING('$1', Pid),
+ [{'=:=', '$1', {const, T}}],
+ ['$_']}],
+ {reply, repl(ets:select(?TABLE, MatchSpec), U, Pid), State};
+
+handle_call(state, _, State) ->
+ {reply, State, State};
+
+handle_call(uptime, _, #state{id = Time} = State) ->
+ {reply, diameter_lib:now_diff(Time), State};
+
+handle_call(Req, From, State) ->
+ ?UNEXPECTED([Req, From]),
+ {reply, nok, State}.
+
+%%% ----------------------------------------------------------
+%%% # handle_cast(Request, State)
+%%% ----------------------------------------------------------
+
+handle_cast(Msg, State)->
+ ?UNEXPECTED([Msg]),
+ {noreply, State}.
+
+%%% ----------------------------------------------------------
+%%% # handle_info(Request, State)
+%%% ----------------------------------------------------------
+
+handle_info({'DOWN', MRef, process, Pid, _}, State) ->
+ ets:delete_object(?TABLE, ?MONITOR(Pid, MRef)),
+ ets:match_delete(?TABLE, ?MAPPING('_', Pid)),
+ {noreply, State};
+
+handle_info(Info, State) ->
+ ?UNEXPECTED([Info]),
+ {noreply, State}.
+
+%%% ----------------------------------------------------------
+%%% # terminate(Reason, State)
+%%% ----------------------------------------------------------
+
+terminate(_Reason, _State)->
+ ok.
+
+%%% ----------------------------------------------------------
+%%% # code_change(OldVsn, State, Extra)
+%%% ----------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%% ---------------------------------------------------------
+%% INTERNAL FUNCTIONS
+%% ---------------------------------------------------------
+
+monitor(true, Pid) ->
+ ets:insert(?TABLE, ?MONITOR(Pid, erlang:monitor(process, Pid)));
+monitor(false, _) ->
+ ok.
+
+%% Do we need a monitor for the specified Pid?
+no_monitor(Pid) ->
+ [] == ets:match_object(?TABLE, ?MONITOR(Pid, '_')).
+
+%% insert_new/2
+
+insert_new(?TABLE, {Key, _} = T) ->
+ flush(ets:lookup(?TABLE, Key)),
+ ets:insert_new(?TABLE, T).
+
+%% Remove any processes that are dead but for which we may not have
+%% received 'DOWN' yet. This is to ensure that add_new can be used
+%% to register a unique name each time a process restarts.
+flush(List) ->
+ lists:foreach(fun({_,P} = T) ->
+ del(erlang:is_process_alive(P), T)
+ end,
+ List).
+
+del(Alive, T) ->
+ Alive orelse ets:delete_object(?TABLE, T).
+
+%% repl/3
+
+repl([?MAPPING(_, Pid) = M], Key, Pid) ->
+ ets:delete_object(?TABLE, M),
+ true = ets:insert(?TABLE, ?MAPPING(Key, Pid));
+repl([], _, _) ->
+ false.
+
+%% call/1
+
+call(Request) ->
+ gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
new file mode 100644
index 0000000000..421e36ccf5
--- /dev/null
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -0,0 +1,2903 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% Implements the process that represents a service.
+%%
+
+-module(diameter_service).
+-behaviour(gen_server).
+
+-export([start/1,
+ stop/1,
+ start_transport/2,
+ stop_transport/2,
+ info/2,
+ call/4]).
+
+%% towards diameter_watchdog
+-export([receive_message/3]).
+
+%% service supervisor
+-export([start_link/1]).
+
+-export([subscribe/1,
+ unsubscribe/1,
+ subscriptions/1,
+ subscriptions/0,
+ services/0,
+ services/1,
+ whois/1,
+ flush_stats/1]).
+
+%% test/debug
+-export([call_module/3,
+ state/1,
+ uptime/1]).
+
+%%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+%% Other callbacks.
+-export([send/1]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+-include("diameter_types.hrl").
+
+-define(STATE_UP, up).
+-define(STATE_DOWN, down).
+
+-define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1
+-define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
+-define(RESTART_TC, 1000). %% if restart was this recent
+
+%% Used to be able to swap this with anything else dict-like but now
+%% rely on the fact that a service's #state{} record does not change
+%% in storing in it ?STATE table and not always going through the
+%% service process. In particular, rely on the fact that operations on
+%% a ?Dict don't change the handle to it.
+-define(Dict, diameter_dict).
+
+%% Table containing outgoing requests for which a reply has yet to be
+%% received.
+-define(REQUEST_TABLE, diameter_request).
+
+%% Maintains state in a table. In contrast to previously, a service's
+%% stat is not constant and is accessed outside of the service
+%% process.
+-define(STATE_TABLE, ?MODULE).
+
+%% Workaround for dialyzer's lack of understanding of match specs.
+-type match(T)
+ :: T | '_' | '$1' | '$2' | '$3' | '$4'.
+
+%% State of service gen_server.
+-record(state,
+ {id = now(),
+ service_name, %% as passed to start_service/2, key in ?STATE_TABLE
+ service :: #diameter_service{},
+ peerT = ets_new(peers) :: ets:tid(), %% #peer{} at start_fsm
+ connT = ets_new(conns) :: ets:tid(), %% #conn{} at connection_up
+ share_peers = false :: boolean(), %% broadcast peers to remote nodes?
+ use_shared_peers = false :: boolean(), %% use broadcasted peers?
+ shared_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
+ local_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
+ monitor = false :: false | pid()}). %% process to die with
+%% shared_peers reflects the peers broadcast from remote nodes. Note
+%% that the state term itself doesn't change, which is relevant for
+%% the stateless application callbacks since the state is retrieved
+%% from ?STATE_TABLE from outside the service process. The pid in the
+%% service record is used to determine whether or not we need to call
+%% the process for a pick_peer callback.
+
+%% Record representing a watchdog process.
+-record(peer,
+ {pid :: match(pid()),
+ type :: match(connect | accept),
+ ref :: match(reference()), %% key into diameter_config
+ options :: match([transport_opt()]), %% as passed to start_transport
+ op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP),
+ started = now(), %% at process start
+ conn = false :: match(boolean() | pid())}).
+ %% true at accept, pid() at connection_up (connT key)
+
+%% Record representing a peer_fsm process.
+-record(conn,
+ {pid :: pid(),
+ apps :: [{0..16#FFFFFFFF, app_alias()}], %% {Id, Alias}
+ caps :: #diameter_caps{},
+ started = now(), %% at process start
+ peer :: pid()}). %% key into peerT
+
+%% Record stored in diameter_request for each outgoing request.
+-record(request,
+ {from, %% arg 2 of handle_call/3
+ handler :: match(pid()), %% request process
+ transport :: match(pid()), %% peer process
+ caps :: match(#diameter_caps{}),
+ app :: match(app_alias()), %% #diameter_app.alias
+ dictionary :: match(module()), %% #diameter_app.dictionary
+ module :: match(nonempty_improper_list(module(), list())),
+ %% #diameter_app.module
+ filter :: match(peer_filter()),
+ packet :: match(#diameter_packet{})}).
+
+%% Record call/4 options are parsed into.
+-record(options,
+ {filter = none :: peer_filter(),
+ extra = [] :: list(),
+ timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
+ detach = false :: boolean()}).
+
+%% Since RFC 3588 requires that a Diameter agent not modify End-to-End
+%% Identifiers, the possibility of explicitly setting an End-to-End
+%% Identifier would be needed to be able to implement an agent in
+%% which one side of the communication is not implemented on top of
+%% diameter. For example, Diameter being sent or received encapsulated
+%% in some other protocol, or even another Diameter stack in a
+%% non-Erlang environment. (Not that this is likely to be a normal
+%% case.)
+%%
+%% The implemented solution is not an option but to respect any header
+%% values set in a diameter_header record returned from a
+%% prepare_request callback. A call to diameter:call/4 can communicate
+%% values to the callback using the 'extra' option if so desired.
+
+%%% ---------------------------------------------------------------------------
+%%% # start(SvcName)
+%%% ---------------------------------------------------------------------------
+
+start(SvcName) ->
+ diameter_service_sup:start_child(SvcName).
+
+start_link(SvcName) ->
+ Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
+ gen_server:start_link(?MODULE, [SvcName], Options).
+%% Put the arbitrary term SvcName in a list in case we ever want to
+%% send more than this and need to distinguish old from new.
+
+%%% ---------------------------------------------------------------------------
+%%% # stop(SvcName)
+%%% ---------------------------------------------------------------------------
+
+stop(SvcName) ->
+ case whois(SvcName) of
+ undefined ->
+ {error, not_started};
+ Pid ->
+ stop(call_service(Pid, stop), Pid)
+ end.
+
+stop(ok, Pid) ->
+ MRef = erlang:monitor(process, Pid),
+ receive {'DOWN', MRef, process, _, _} -> ok end;
+stop(No, _) ->
+ No.
+
+%%% ---------------------------------------------------------------------------
+%%% # start_transport(SvcName, {Ref, Type, Opts})
+%%% ---------------------------------------------------------------------------
+
+start_transport(SvcName, {_,_,_} = T) ->
+ call_service_by_name(SvcName, {start, T}).
+
+%%% ---------------------------------------------------------------------------
+%%% # stop_transport(SvcName, Refs)
+%%% ---------------------------------------------------------------------------
+
+stop_transport(_, []) ->
+ ok;
+stop_transport(SvcName, [_|_] = Refs) ->
+ call_service_by_name(SvcName, {stop, Refs}).
+
+%%% ---------------------------------------------------------------------------
+%%% # info(SvcName, Item)
+%%% ---------------------------------------------------------------------------
+
+info(SvcName, Item) ->
+ info_rc(call_service_by_name(SvcName, {info, Item})).
+
+info_rc({error, _}) ->
+ undefined;
+info_rc(Info) ->
+ Info.
+
+%%% ---------------------------------------------------------------------------
+%%% # receive_message(TPid, Pkt, MessageData)
+%%% ---------------------------------------------------------------------------
+
+%% Handle an incoming message in the watchdog process. This used to
+%% come through the service process but this avoids that becoming a
+%% bottleneck.
+
+receive_message(TPid, Pkt, T)
+ when is_pid(TPid) ->
+ #diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
+ recv(R, (not R) andalso lookup_request(Pkt, TPid), TPid, Pkt, T).
+
+%% Incoming request ...
+recv(true, false, TPid, Pkt, T) ->
+ try
+ spawn(fun() -> recv_request(TPid, Pkt, T) end)
+ catch
+ error: system_limit = E -> %% discard
+ ?LOG({error, E}, now())
+ end;
+
+%% ... answer to known request ...
+recv(false, #request{from = {_, Ref}, handler = Pid} = Req, _, Pkt, _) ->
+ Pid ! {answer, Ref, Req, Pkt};
+%% Note that failover could have happened prior to this message being
+%% received and triggering failback. That is, both a failover message
+%% and answer may be on their way to the handler process. In the worst
+%% case the request process gets notification of the failover and
+%% sends to the alternate peer before an answer arrives, so it's
+%% always the case that we can receive more than one answer after
+%% failover. The first answer received by the request process wins,
+%% any others are discarded.
+
+%% ... or not.
+recv(false, false, _, _, _) ->
+ ok.
+
+%%% ---------------------------------------------------------------------------
+%%% # call(SvcName, App, Msg, Options)
+%%% ---------------------------------------------------------------------------
+
+call(SvcName, App, Msg, Options)
+ when is_list(Options) ->
+ Rec = make_options(Options),
+ Ref = make_ref(),
+ Caller = {self(), Ref},
+ Fun = fun() -> exit({Ref, call(SvcName, App, Msg, Rec, Caller)}) end,
+ try spawn_monitor(Fun) of
+ {_, MRef} ->
+ recv(MRef, Ref, Rec#options.detach, false)
+ catch
+ error: system_limit = E ->
+ {error, E}
+ end.
+
+%% Don't rely on gen_server:call/3 for the timeout handling since it
+%% makes no guarantees about not leaving a reply message in the
+%% mailbox if we catch its exit at timeout. It currently *can* do so,
+%% which is also undocumented.
+
+recv(MRef, _, true, true) ->
+ erlang:demonitor(MRef, [flush]),
+ ok;
+
+recv(MRef, Ref, Detach, Sent) ->
+ receive
+ Ref -> %% send has been attempted
+ recv(MRef, Ref, Detach, true);
+ {'DOWN', MRef, process, _, Reason} ->
+ call_rc(Reason, Ref, Sent)
+ end.
+
+%% call/5 has returned ...
+call_rc({Ref, Ans}, Ref, _) ->
+ Ans;
+
+%% ... or not. In this case failure/encode are documented.
+call_rc(_, _, Sent) ->
+ {error, choose(Sent, failure, encode)}.
+
+%% call/5
+%%
+%% In the process spawned for the outgoing request.
+
+call(SvcName, App, Msg, Opts, Caller) ->
+ c(ets:lookup(?STATE_TABLE, SvcName), App, Msg, Opts, Caller).
+
+c([#state{service_name = SvcName} = S], App, Msg, Opts, Caller) ->
+ case find_transport(App, Msg, Opts, S) of
+ {_,_,_} = T ->
+ send_request(T, Msg, Opts, Caller, SvcName);
+ false ->
+ {error, no_connection};
+ {error, _} = No ->
+ No
+ end;
+
+c([], _, _, _, _) ->
+ {error, no_service}.
+
+%% make_options/1
+
+make_options(Options) ->
+ lists:foldl(fun mo/2, #options{}, Options).
+
+mo({timeout, T}, Rec)
+ when is_integer(T), 0 =< T ->
+ Rec#options{timeout = T};
+
+mo({filter, F}, #options{filter = none} = Rec) ->
+ Rec#options{filter = F};
+mo({filter, F}, #options{filter = {all, Fs}} = Rec) ->
+ Rec#options{filter = {all, [F | Fs]}};
+mo({filter, F}, #options{filter = F0} = Rec) ->
+ Rec#options{filter = {all, [F0, F]}};
+
+mo({extra, L}, #options{extra = X} = Rec)
+ when is_list(L) ->
+ Rec#options{extra = X ++ L};
+
+mo(detach, Rec) ->
+ Rec#options{detach = true};
+
+mo(T, _) ->
+ ?ERROR({invalid_option, T}).
+
+%%% ---------------------------------------------------------------------------
+%%% # subscribe(SvcName)
+%%% # unsubscribe(SvcName)
+%%% ---------------------------------------------------------------------------
+
+subscribe(SvcName) ->
+ diameter_reg:add({?MODULE, subscriber, SvcName}).
+
+unsubscribe(SvcName) ->
+ diameter_reg:del({?MODULE, subscriber, SvcName}).
+
+subscriptions(Pat) ->
+ pmap(diameter_reg:match({?MODULE, subscriber, Pat})).
+
+subscriptions() ->
+ subscriptions('_').
+
+pmap(Props) ->
+ lists:map(fun({{?MODULE, _, Name}, Pid}) -> {Name, Pid} end, Props).
+
+%%% ---------------------------------------------------------------------------
+%%% # services(Pattern)
+%%% ---------------------------------------------------------------------------
+
+services(Pat) ->
+ pmap(diameter_reg:match({?MODULE, service, Pat})).
+
+services() ->
+ services('_').
+
+whois(SvcName) ->
+ case diameter_reg:match({?MODULE, service, SvcName}) of
+ [{_, Pid}] ->
+ Pid;
+ [] ->
+ undefined
+ end.
+
+%%% ---------------------------------------------------------------------------
+%%% # flush_stats/1
+%%%
+%%% Output: list of {{SvcName, Alias, Counter}, Value}
+%%% ---------------------------------------------------------------------------
+
+flush_stats(TPid) ->
+ diameter_stats:flush(TPid).
+
+%% ===========================================================================
+%% ===========================================================================
+
+state(Svc) ->
+ call_service(Svc, state).
+
+uptime(Svc) ->
+ call_service(Svc, uptime).
+
+%% call_module/3
+
+call_module(Service, AppMod, Request) ->
+ call_service(Service, {call_module, AppMod, Request}).
+
+%%% ---------------------------------------------------------------------------
+%%% # init([SvcName])
+%%% ---------------------------------------------------------------------------
+
+init([SvcName]) ->
+ process_flag(trap_exit, true), %% ensure terminate(shutdown, _)
+ i(SvcName, diameter_reg:add_new({?MODULE, service, SvcName})).
+
+i(SvcName, true) ->
+ {ok, i(SvcName)};
+i(_, false) ->
+ {stop, {shutdown, already_started}}.
+
+%%% ---------------------------------------------------------------------------
+%%% # handle_call(Req, From, State)
+%%% ---------------------------------------------------------------------------
+
+handle_call(state, _, S) ->
+ {reply, S, S};
+
+handle_call(uptime, _, #state{id = T} = S) ->
+ {reply, diameter_lib:now_diff(T), S};
+
+%% Start a transport.
+handle_call({start, {Ref, Type, Opts}}, _From, S) ->
+ {reply, start(Ref, {Type, Opts}, S), S};
+
+%% Stop transports.
+handle_call({stop, Refs}, _From, S) ->
+ shutdown(Refs, S),
+ {reply, ok, S};
+
+%% pick_peer with mutable state
+handle_call({pick_peer, Local, Remote, App}, _From, S) ->
+ #diameter_app{mutable = true} = App, %% assert
+ {reply, pick_peer(Local, Remote, self(), S#state.service_name, App), S};
+
+handle_call({call_module, AppMod, Req}, From, S) ->
+ call_module(AppMod, Req, From, S);
+
+handle_call({info, Item}, _From, S) ->
+ {reply, service_info(Item, S), S};
+
+handle_call(stop, _From, S) ->
+ shutdown(S),
+ {stop, normal, ok, S};
+%% The server currently isn't guaranteed to be dead when the caller
+%% gets the reply. We deal with this in the call to the server,
+%% stating a monitor that waits for DOWN before returning.
+
+handle_call(Req, From, S) ->
+ unexpected(handle_call, [Req, From], S),
+ {reply, nok, S}.
+
+%%% ---------------------------------------------------------------------------
+%%% # handle_cast(Req, State)
+%%% ---------------------------------------------------------------------------
+
+handle_cast(Req, S) ->
+ unexpected(handle_cast, [Req], S),
+ {noreply, S}.
+
+%%% ---------------------------------------------------------------------------
+%%% # handle_info(Req, State)
+%%% ---------------------------------------------------------------------------
+
+handle_info(T,S) ->
+ case transition(T,S) of
+ ok ->
+ {noreply, S};
+ #state{} = NS ->
+ {noreply, NS};
+ {stop, Reason} ->
+ {stop, {shutdown, Reason}, S}
+ end.
+
+%% transition/2
+
+%% Peer process is telling us to start a new accept process.
+transition({accepted, Pid, TPid}, S) ->
+ accepted(Pid, TPid, S),
+ ok;
+
+%% Peer process has a new open connection.
+transition({connection_up, Pid, T}, S) ->
+ connection_up(Pid, T, S);
+
+%% Peer process has left state open.
+transition({connection_down, Pid}, S) ->
+ connection_down(Pid, S);
+
+%% Peer process has returned to state open.
+transition({connection_up, Pid}, S) ->
+ connection_up(Pid, S);
+
+%% Accepting transport has lost connectivity.
+transition({close, Pid}, S) ->
+ close(Pid, S),
+ ok;
+
+%% Connecting transport is being restarted by watchdog.
+transition({reconnect, Pid}, S) ->
+ reconnect(Pid, S),
+ ok;
+
+%% Monitor process has died. Just die with a reason that tells
+%% diameter_config about the happening. If a cleaner shutdown is
+%% required then someone should stop us.
+transition({'DOWN', MRef, process, _, Reason}, #state{monitor = MRef}) ->
+ {stop, {monitor, Reason}};
+
+%% Local peer process has died.
+transition({'DOWN', _, process, Pid, Reason}, S)
+ when node(Pid) == node() ->
+ peer_down(Pid, Reason, S);
+
+%% Remote service wants to know about shared transports.
+transition({service, Pid}, S) ->
+ share_peers(Pid, S),
+ ok;
+
+%% Remote service is communicating a shared peer.
+transition({peer, TPid, Aliases, Caps}, S) ->
+ remote_peer_up(TPid, Aliases, Caps, S);
+
+%% Remote peer process has died.
+transition({'DOWN', _, process, TPid, _}, S) ->
+ remote_peer_down(TPid, S);
+
+%% Restart after tc expiry.
+transition({tc_timeout, T}, S) ->
+ tc_timeout(T, S),
+ ok;
+
+%% Request process is telling us it may have missed a failover message
+%% after a transport went down and the service process looked up
+%% outstanding requests.
+transition({failover, TRef, Seqs}, S) ->
+ failover(TRef, Seqs, S),
+ ok;
+
+transition(Req, S) ->
+ unexpected(handle_info, [Req], S),
+ ok.
+
+%%% ---------------------------------------------------------------------------
+%%% # terminate(Reason, State)
+%%% ---------------------------------------------------------------------------
+
+terminate(Reason, #state{service_name = Name} = S) ->
+ ets:delete(?STATE_TABLE, Name),
+ shutdown == Reason %% application shutdown
+ andalso shutdown(S).
+
+%%% ---------------------------------------------------------------------------
+%%% # code_change(FromVsn, State, Extra)
+%%% ---------------------------------------------------------------------------
+
+code_change(FromVsn,
+ #state{service_name = SvcName,
+ service = #diameter_service{applications = Apps}}
+ = S,
+ Extra) ->
+ lists:foreach(fun(A) ->
+ code_change(FromVsn, SvcName, Extra, A)
+ end,
+ Apps),
+ {ok, S}.
+
+code_change(FromVsn, SvcName, Extra, #diameter_app{alias = Alias} = A) ->
+ {ok, S} = cb(A, code_change, [FromVsn,
+ mod_state(Alias),
+ Extra,
+ SvcName]),
+ mod_state(Alias, S).
+
+%% ===========================================================================
+%% ===========================================================================
+
+unexpected(F, A, #state{service_name = Name}) ->
+ ?UNEXPECTED(F, A ++ [Name]).
+
+cb([_|_] = M, F, A) ->
+ eval(M, F, A);
+cb(Rec, F, A) ->
+ {_, M} = app(Rec),
+ eval(M, F, A).
+
+app(#request{app = A, module = M}) ->
+ {A,M};
+app(#diameter_app{alias = A, module = M}) ->
+ {A,M}.
+
+eval([M|X], F, A) ->
+ apply(M, F, A ++ X).
+
+%% Callback with state.
+
+state_cb(#diameter_app{mutable = false, init_state = S}, {ModX, F, A}) ->
+ eval(ModX, F, A ++ [S]);
+
+state_cb(#diameter_app{mutable = true, alias = Alias}, {_,_,_} = MFA) ->
+ state_cb(MFA, Alias);
+
+state_cb({ModX,F,A}, Alias)
+ when is_list(ModX) ->
+ eval(ModX, F, A ++ [mod_state(Alias)]).
+
+choose(true, X, _) -> X;
+choose(false, _, X) -> X.
+
+ets_new(Tbl) ->
+ ets:new(Tbl, [{keypos, 2}]).
+
+insert(Tbl, Rec) ->
+ ets:insert(Tbl, Rec),
+ Rec.
+
+monitor(Pid) ->
+ erlang:monitor(process, Pid),
+ Pid.
+
+%% Using the process dictionary for the callback state was initially
+%% just a way to make what was horrendous trace (big state record and
+%% much else everywhere) somewhat more readable. There's not as much
+%% need for it now but it's no worse (except possibly that we don't
+%% see the table identifier being passed around) than an ets table so
+%% keep it.
+
+mod_state(Alias) ->
+ get({?MODULE, mod_state, Alias}).
+
+mod_state(Alias, ModS) ->
+ put({?MODULE, mod_state, Alias}, ModS).
+
+%% have_transport/2
+
+have_transport(SvcName, Ref) ->
+ [] /= diameter_config:have_transport(SvcName, Ref).
+
+%%% ---------------------------------------------------------------------------
+%%% # shutdown/2
+%%% ---------------------------------------------------------------------------
+
+shutdown(Refs, #state{peerT = PeerT}) ->
+ ets:foldl(fun(P,ok) -> s(P, Refs), ok end, ok, PeerT).
+
+s(#peer{ref = Ref, pid = Pid}, Refs) ->
+ s(lists:member(Ref, Refs), Pid);
+
+s(true, Pid) ->
+ Pid ! {shutdown, self()}; %% 'DOWN' will cleanup as usual
+s(false, _) ->
+ ok.
+
+%%% ---------------------------------------------------------------------------
+%%% # shutdown/1
+%%% ---------------------------------------------------------------------------
+
+shutdown(#state{peerT = PeerT}) ->
+ %% A transport might not be alive to receive the shutdown request
+ %% but give those that are a chance to shutdown gracefully.
+ wait(fun st/2, PeerT),
+ %% Kill the watchdogs explicitly in case there was no transport.
+ wait(fun sw/2, PeerT).
+
+wait(Fun, T) ->
+ diameter_lib:wait(ets:foldl(Fun, [], T)).
+
+st(#peer{conn = B}, Acc)
+ when is_boolean(B) ->
+ Acc;
+st(#peer{conn = Pid}, Acc) ->
+ Pid ! shutdown,
+ [Pid | Acc].
+
+sw(#peer{pid = Pid}, Acc) ->
+ exit(Pid, shutdown),
+ [Pid | Acc].
+
+%%% ---------------------------------------------------------------------------
+%%% # call_service/2
+%%% ---------------------------------------------------------------------------
+
+call_service(Pid, Req)
+ when is_pid(Pid) ->
+ cs(Pid, Req);
+call_service(SvcName, Req) ->
+ call_service_by_name(SvcName, Req).
+
+call_service_by_name(SvcName, Req) ->
+ cs(whois(SvcName), Req).
+
+cs(Pid, Req)
+ when is_pid(Pid) ->
+ try
+ gen_server:call(Pid, Req, infinity)
+ catch
+ E: Reason when E == exit ->
+ {error, {E, Reason}}
+ end;
+
+cs(undefined, _) ->
+ {error, no_service}.
+
+%%% ---------------------------------------------------------------------------
+%%% # i/1
+%%%
+%%% Output: #state{}
+%%% ---------------------------------------------------------------------------
+
+%% Intialize the state of a service gen_server.
+
+i(SvcName) ->
+ %% Split the config into a server state and a list of transports.
+ {#state{} = S, CL} = lists:foldl(fun cfg_acc/2,
+ {false, []},
+ diameter_config:lookup(SvcName)),
+
+ %% Publish the state in order to be able to access it outside of
+ %% the service process. Originally table identifiers were only
+ %% known to the service process but we now want to provide the
+ %% option of application callbacks being 'stateless' in order to
+ %% avoid having to go through a common process. (Eg. An agent that
+ %% sends a request for every incoming request.)
+ true = ets:insert_new(?STATE_TABLE, S),
+
+ %% Start fsms for each transport.
+ lists:foreach(fun(T) -> start_fsm(T,S) end, CL),
+
+ init_shared(S),
+ S.
+
+cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
+ {false, Acc}) ->
+ lists:foreach(fun init_mod/1, Apps),
+ S = #state{service_name = SvcName,
+ service = Rec#diameter_service{pid = self()},
+ share_peers = get_value(share_peers, Opts),
+ use_shared_peers = get_value(use_shared_peers, Opts),
+ monitor = mref(get_value(monitor, Opts))},
+ {S, Acc};
+
+cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
+ when Type == connect;
+ Type == listen ->
+ {S, [T | Acc]}.
+
+mref(false = No) ->
+ No;
+mref(P) ->
+ erlang:monitor(process, P).
+
+init_shared(#state{use_shared_peers = true,
+ service_name = Svc}) ->
+ diameter_peer:notify(Svc, {service, self()});
+init_shared(#state{use_shared_peers = false}) ->
+ ok.
+
+init_mod(#diameter_app{alias = Alias,
+ init_state = S}) ->
+ mod_state(Alias, S).
+
+start_fsm({Ref, Type, Opts}, S) ->
+ start(Ref, {Type, Opts}, S).
+
+get_value(Key, Vs) ->
+ {_, V} = lists:keyfind(Key, 1, Vs),
+ V.
+
+%%% ---------------------------------------------------------------------------
+%%% # start/3
+%%% ---------------------------------------------------------------------------
+
+%% If the initial start/3 at service/transport start succeeds then
+%% subsequent calls to start/4 on the same service will also succeed
+%% since they involve the same call to merge_service/2. We merge here
+%% rather than earlier since the service may not yet be configured
+%% when the transport is configured.
+
+start(Ref, {T, Opts}, S)
+ when T == connect;
+ T == listen ->
+ try
+ {ok, start(Ref, type(T), Opts, S)}
+ catch
+ ?FAILURE(Reason) ->
+ {error, Reason}
+ end.
+%% TODO: don't actually raise any errors yet
+
+%% There used to be a difference here between the handling of
+%% configured listening and connecting transports but now we simply
+%% tell the transport_module to start an accepting or connecting
+%% process respectively, the transport implementation initiating
+%% listening on a port as required.
+type(listen) -> accept;
+type(accept) -> listen;
+type(connect = T) -> T.
+
+%% start/4
+
+start(Ref, Type, Opts, #state{peerT = PeerT,
+ connT = ConnT,
+ service_name = SvcName,
+ service = Svc})
+ when Type == connect;
+ Type == accept ->
+ Pid = monitor(s(Type, Ref, {ConnT,
+ Opts,
+ SvcName,
+ merge_service(Opts, Svc)})),
+ insert(PeerT, #peer{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts}),
+ Pid.
+
+%% Note that the service record passed into the watchdog is the merged
+%% record so that each watchdog (and peer_fsm) may get a different
+%% record. This record is what is passed back into application
+%% callbacks.
+
+s(Type, Ref, T) ->
+ diameter_watchdog:start({Type, Ref}, T).
+
+%% merge_service/2
+
+merge_service(Opts, Svc) ->
+ lists:foldl(fun ms/2, Svc, Opts).
+
+%% Limit the applications known to the fsm to those in the 'apps'
+%% option. That this might be empty is checked by the fsm. It's not
+%% checked at config-time since there's no requirement that the
+%% service be configured first. (Which could be considered a bit odd.)
+ms({applications, As}, #diameter_service{applications = Apps} = S)
+ when is_list(As) ->
+ S#diameter_service{applications
+ = [A || A <- Apps,
+ lists:member(A#diameter_app.alias, As)]};
+
+%% The fact that all capabilities can be configured on the transports
+%% means that the service doesn't necessarily represent a single
+%% locally implemented Diameter peer as identified by Origin-Host: a
+%% transport can configure its own Origin-Host. This means that the
+%% service little more than a placeholder for default capabilities
+%% plus a list of applications that individual transports can choose
+%% to support (or not).
+ms({capabilities, Opts}, #diameter_service{capabilities = Caps0} = Svc)
+ when is_list(Opts) ->
+ %% make_caps has already succeeded in diameter_config so it will succeed
+ %% again here.
+ {ok, Caps} = diameter_capx:make_caps(Caps0, Opts),
+ Svc#diameter_service{capabilities = Caps};
+
+ms(_, Svc) ->
+ Svc.
+
+%%% ---------------------------------------------------------------------------
+%%% # accepted/3
+%%% ---------------------------------------------------------------------------
+
+accepted(Pid, _TPid, #state{peerT = PeerT} = S) ->
+ #peer{ref = Ref, type = accept = T, conn = false, options = Opts}
+ = P
+ = fetch(PeerT, Pid),
+ insert(PeerT, P#peer{conn = true}), %% mark replacement transport started
+ start(Ref, T, Opts, S). %% start new peer
+
+fetch(Tid, Key) ->
+ [T] = ets:lookup(Tid, Key),
+ T.
+
+%%% ---------------------------------------------------------------------------
+%%% # connection_up/3
+%%%
+%%% Output: #state{}
+%%% ---------------------------------------------------------------------------
+
+%% Peer process has reached the open state.
+
+connection_up(Pid, {TPid, {Caps, SApps, Pkt}}, #state{peerT = PeerT,
+ connT = ConnT}
+ = S) ->
+ P = fetch(PeerT, Pid),
+ C = #conn{pid = TPid,
+ apps = SApps,
+ caps = Caps,
+ peer = Pid},
+
+ insert(ConnT, C),
+ connection_up([Pkt], P#peer{conn = TPid}, C, S).
+
+%%% ---------------------------------------------------------------------------
+%%% # connection_up/2
+%%%
+%%% Output: #state{}
+%%% ---------------------------------------------------------------------------
+
+%% Peer process has transitioned back into the open state. Note that there
+%% has been no new capabilties exchange in this case.
+
+connection_up(Pid, #state{peerT = PeerT,
+ connT = ConnT}
+ = S) ->
+ #peer{conn = TPid} = P = fetch(PeerT, Pid),
+ C = fetch(ConnT, TPid),
+ connection_up([], P, C, S).
+
+%% connection_up/4
+
+connection_up(T, P, C, #state{peerT = PeerT,
+ local_peers = LDict,
+ service_name = SvcName,
+ service
+ = #diameter_service{applications = Apps}}
+ = S) ->
+ #peer{conn = TPid, op_state = ?STATE_DOWN}
+ = P,
+ #conn{apps = SApps, caps = Caps}
+ = C,
+
+ insert(PeerT, P#peer{op_state = ?STATE_UP}),
+
+ request_peer_up(TPid),
+ report_status(up, P, C, S, T),
+ S#state{local_peers = insert_local_peer(SApps,
+ {{TPid, Caps}, {SvcName, Apps}},
+ LDict)}.
+
+insert_local_peer(SApps, T, LDict) ->
+ lists:foldl(fun(A,D) -> ilp(A, T, D) end, LDict, SApps).
+
+ilp({Id, Alias}, {TC, SA}, LDict) ->
+ init_conn(Id, Alias, TC, SA),
+ ?Dict:append(Alias, TC, LDict).
+
+init_conn(Id, Alias, TC, {SvcName, Apps}) ->
+ #diameter_app{module = ModX,
+ id = Id} %% assert
+ = find_app(Alias, Apps),
+
+ peer_cb({ModX, peer_up, [SvcName, TC]}, Alias).
+
+find_app(Alias, Apps) ->
+ lists:keyfind(Alias, #diameter_app.alias, Apps).
+
+%% A failing peer callback brings down the service. In the case of
+%% peer_up we could just kill the transport and emit an error but for
+%% peer_down we have no way to cleanup any state change that peer_up
+%% may have introduced.
+peer_cb(MFA, Alias) ->
+ try state_cb(MFA, Alias) of
+ ModS ->
+ mod_state(Alias, ModS)
+ catch
+ E: Reason ->
+ ?ERROR({E, Reason, MFA, ?STACK})
+ end.
+
+%%% ---------------------------------------------------------------------------
+%%% # connection_down/2
+%%%
+%%% Output: #state{}
+%%% ---------------------------------------------------------------------------
+
+%% Peer process has transitioned out of the open state.
+
+connection_down(Pid, #state{peerT = PeerT,
+ connT = ConnT}
+ = S) ->
+ #peer{conn = TPid}
+ = P
+ = fetch(PeerT, Pid),
+
+ C = fetch(ConnT, TPid),
+ insert(PeerT, P#peer{op_state = ?STATE_DOWN}),
+ connection_down(P,C,S).
+
+%% connection_down/3
+
+connection_down(#peer{conn = TPid,
+ op_state = ?STATE_UP}
+ = P,
+ #conn{caps = Caps,
+ apps = SApps}
+ = C,
+ #state{service_name = SvcName,
+ service = #diameter_service{applications = Apps},
+ local_peers = LDict}
+ = S) ->
+ report_status(down, P, C, S, []),
+ NewS = S#state{local_peers
+ = remove_local_peer(SApps,
+ {{TPid, Caps}, {SvcName, Apps}},
+ LDict)},
+ request_peer_down(TPid, NewS),
+ NewS.
+
+remove_local_peer(SApps, T, LDict) ->
+ lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
+
+rlp({Id, Alias}, {TC, SA}, LDict) ->
+ L = ?Dict:fetch(Alias, LDict),
+ down_conn(Id, Alias, TC, SA),
+ ?Dict:store(Alias, lists:delete(TC, L), LDict).
+
+down_conn(Id, Alias, TC, {SvcName, Apps}) ->
+ #diameter_app{module = ModX,
+ id = Id} %% assert
+ = find_app(Alias, Apps),
+
+ peer_cb({ModX, peer_down, [SvcName, TC]}, Alias).
+
+%%% ---------------------------------------------------------------------------
+%%% # peer_down/3
+%%%
+%%% Output: #state{}
+%%% ---------------------------------------------------------------------------
+
+%% Peer process has died.
+
+peer_down(Pid, _Reason, #state{peerT = PeerT} = S) ->
+ P = fetch(PeerT, Pid),
+ ets:delete_object(PeerT, P),
+ restart(P,S),
+ peer_down(P,S).
+
+%% peer_down/2
+
+%% The peer has never come up ...
+peer_down(#peer{conn = B}, S)
+ when is_boolean(B) ->
+ S;
+
+%% ... or it has.
+peer_down(#peer{ref = Ref,
+ conn = TPid,
+ type = Type,
+ options = Opts}
+ = P,
+ #state{service_name = SvcName,
+ connT = ConnT}
+ = S) ->
+ #conn{caps = Caps}
+ = C
+ = fetch(ConnT, TPid),
+ ets:delete_object(ConnT, C),
+ try
+ pd(P,C,S)
+ after
+ send_event(SvcName, {closed, Ref, {TPid, Caps}, {type(Type), Opts}})
+ end.
+
+pd(#peer{op_state = ?STATE_DOWN}, _, S) ->
+ S;
+pd(#peer{op_state = ?STATE_UP} = P, C, S) ->
+ connection_down(P,C,S).
+
+%% restart/2
+
+restart(P,S) ->
+ q_restart(restart(P), S).
+
+%% restart/1
+
+%% Always try to reconnect.
+restart(#peer{ref = Ref,
+ type = connect = T,
+ options = Opts,
+ started = Time}) ->
+ {Time, {Ref, T, Opts}};
+
+%% Transport connection hasn't yet been accepted ...
+restart(#peer{ref = Ref,
+ type = accept = T,
+ options = Opts,
+ conn = false,
+ started = Time}) ->
+ {Time, {Ref, T, Opts}};
+
+%% ... or it has: a replacement transport has already been spawned.
+restart(#peer{type = accept}) ->
+ false.
+
+%% q_restart/2
+
+%% Start the reconnect timer.
+q_restart({Time, {_Ref, Type, Opts} = T}, S) ->
+ start_tc(tc(Time, default_tc(Type, Opts)), T, S);
+q_restart(false, _) ->
+ ok.
+
+%% RFC 3588, 2.1:
+%%
+%% When no transport connection exists with a peer, an attempt to
+%% connect SHOULD be periodically made. This behavior is handled via
+%% the Tc timer, whose recommended value is 30 seconds. There are
+%% certain exceptions to this rule, such as when a peer has terminated
+%% the transport connection stating that it does not wish to
+%% communicate.
+
+default_tc(connect, Opts) ->
+ proplists:get_value(reconnect_timer, Opts, ?DEFAULT_TC);
+default_tc(accept, _) ->
+ 0.
+
+%% Bound tc below if the peer was restarted recently to avoid
+%% continuous in case of faulty config or other problems.
+tc(Time, Tc) ->
+ choose(Tc > ?RESTART_TC
+ orelse timer:now_diff(now(), Time) > 1000*?RESTART_TC,
+ Tc,
+ ?RESTART_TC).
+
+start_tc(0, T, S) ->
+ tc_timeout(T, S);
+start_tc(Tc, T, _) ->
+ erlang:send_after(Tc, self(), {tc_timeout, T}).
+
+%% tc_timeout/2
+
+tc_timeout({Ref, _Type, _Opts} = T, #state{service_name = SvcName} = S) ->
+ tc(have_transport(SvcName, Ref), T, S).
+
+tc(true, {Ref, Type, Opts}, #state{service_name = SvcName}
+ = S) ->
+ send_event(SvcName, {reconnect, Ref, Opts}),
+ start(Ref, Type, Opts, S);
+tc(false = No, _, _) -> %% removed
+ No.
+
+%%% ---------------------------------------------------------------------------
+%%% # close/2
+%%% ---------------------------------------------------------------------------
+
+%% The watchdog doesn't start a new fsm in the accept case, it
+%% simply stays alive until someone tells it to die in order for
+%% another watchdog to be able to detect that it should transition
+%% from initial into reopen rather than okay. That someone is either
+%% the accepting watchdog upon reception of a CER from the previously
+%% connected peer, or us after reconnect_timer timeout.
+
+close(Pid, #state{service_name = SvcName,
+ peerT = PeerT}) ->
+ #peer{pid = Pid,
+ type = accept,
+ ref = Ref,
+ options = Opts}
+ = fetch(PeerT, Pid),
+
+ c(Pid, have_transport(SvcName, Ref), Opts).
+
+%% Tell watchdog to (maybe) die later ...
+c(Pid, true, Opts) ->
+ Tc = proplists:get_value(reconnect_timer, Opts, 2*?DEFAULT_TC),
+ erlang:send_after(Tc, Pid, close);
+
+%% ... or now.
+c(Pid, false, _Opts) ->
+ Pid ! close.
+
+%% The RFC's only document the behaviour of Tc, our reconnect_timer,
+%% for the establishment of connections but we also give
+%% reconnect_timer semantics for a listener, being the time within
+%% which a new connection attempt is expected of a connecting peer.
+%% The value should be greater than the peer's Tc + jitter.
+
+%%% ---------------------------------------------------------------------------
+%%% # reconnect/2
+%%% ---------------------------------------------------------------------------
+
+reconnect(Pid, #state{service_name = SvcName,
+ peerT = PeerT}) ->
+ #peer{ref = Ref,
+ type = connect,
+ options = Opts}
+ = fetch(PeerT, Pid),
+ send_event(SvcName, {reconnect, Ref, Opts}).
+
+%%% ---------------------------------------------------------------------------
+%%% # call_module/4
+%%% ---------------------------------------------------------------------------
+
+%% Backwards compatibility and never documented/advertised. May be
+%% removed.
+
+call_module(Mod, Req, From, #state{service
+ = #diameter_service{applications = Apps},
+ service_name = Svc}
+ = S) ->
+ case cm([A || A <- Apps, Mod == hd(A#diameter_app.module)],
+ Req,
+ From,
+ Svc)
+ of
+ {reply = T, RC} ->
+ {T, RC, S};
+ noreply = T ->
+ {T, S};
+ Reason ->
+ {reply, {error, Reason}, S}
+ end.
+
+cm([#diameter_app{module = ModX, alias = Alias}], Req, From, Svc) ->
+ MFA = {ModX, handle_call, [Req, From, Svc]},
+
+ try state_cb(MFA, Alias) of
+ {noreply = T, ModS} ->
+ mod_state(Alias, ModS),
+ T;
+ {reply = T, RC, ModS} ->
+ mod_state(Alias, ModS),
+ {T, RC};
+ T ->
+ diameter_lib:error_report({invalid, T}, MFA),
+ invalid
+ catch
+ E: Reason ->
+ diameter_lib:error_report({failure, {E, Reason, ?STACK}}, MFA),
+ failure
+ end;
+
+cm([], _, _, _) ->
+ unknown;
+
+cm([_,_|_], _, _, _) ->
+ multiple.
+
+%%% ---------------------------------------------------------------------------
+%%% # send_request/5
+%%% ---------------------------------------------------------------------------
+
+%% Send an outgoing request in its dedicated process.
+%%
+%% Note that both encode of the outgoing request and of the received
+%% answer happens in this process. It's also this process that replies
+%% to the caller. The service process only handles the state-retaining
+%% callbacks.
+%%
+%% The mod field of the #diameter_app{} here includes any extra
+%% arguments passed to diameter:call/2.
+
+send_request({TPid, Caps, App}, Msg, Opts, Caller, SvcName) ->
+ #diameter_app{module = ModX}
+ = App,
+
+ Pkt = make_packet(Msg),
+
+ case cb(ModX, prepare_request, [Pkt, SvcName, {TPid, Caps}]) of
+ {send, P} ->
+ send_request(make_packet(P, Pkt),
+ TPid,
+ Caps,
+ App,
+ Opts,
+ Caller,
+ SvcName);
+ {discard, Reason} ->
+ {error, Reason};
+ discard ->
+ {error, discarded};
+ T ->
+ ?ERROR({invalid_return, prepare_request, App, T})
+ end.
+
+%% make_packet/1
+%%
+%% Turn an outgoing request as passed to call/4 into a diameter_packet
+%% record in preparation for a prepare_request callback.
+
+make_packet(Bin)
+ when is_binary(Bin) ->
+ #diameter_packet{header = diameter_codec:decode_header(Bin),
+ bin = Bin};
+
+make_packet(#diameter_packet{msg = [#diameter_header{} = Hdr | Avps]} = Pkt) ->
+ Pkt#diameter_packet{msg = [make_header(Hdr) | Avps]};
+
+make_packet(#diameter_packet{header = Hdr} = Pkt) ->
+ Pkt#diameter_packet{header = make_header(Hdr)};
+
+make_packet(Msg) ->
+ make_packet(#diameter_packet{msg = Msg}).
+
+%% make_header/1
+
+make_header(undefined) ->
+ Seq = diameter_session:sequence(),
+ make_header(#diameter_header{end_to_end_id = Seq,
+ hop_by_hop_id = Seq});
+
+make_header(#diameter_header{version = undefined} = Hdr) ->
+ make_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
+
+make_header(#diameter_header{end_to_end_id = undefined} = H) ->
+ Seq = diameter_session:sequence(),
+ make_header(H#diameter_header{end_to_end_id = Seq});
+
+make_header(#diameter_header{hop_by_hop_id = undefined} = H) ->
+ Seq = diameter_session:sequence(),
+ make_header(H#diameter_header{hop_by_hop_id = Seq});
+
+make_header(#diameter_header{} = Hdr) ->
+ Hdr;
+
+make_header(T) ->
+ ?ERROR({invalid_header, T}).
+
+%% make_packet/2
+%%
+%% Reconstruct a diameter_packet from the return value of
+%% prepare_request or prepare_retransmit callback.
+
+make_packet(Bin, _)
+ when is_binary(Bin) ->
+ make_packet(Bin);
+
+make_packet(#diameter_packet{msg = [#diameter_header{} | _]} = Pkt, _) ->
+ Pkt;
+
+%% Returning a diameter_packet with no header from a prepare_request
+%% or prepare_retransmit callback retains the header passed into it.
+%% This is primarily so that the end to end and hop by hop identifiers
+%% are retained.
+make_packet(#diameter_packet{header = Hdr} = Pkt,
+ #diameter_packet{header = Hdr0}) ->
+ Pkt#diameter_packet{header = fold_record(Hdr0, Hdr)};
+
+make_packet(Msg, Pkt) ->
+ Pkt#diameter_packet{msg = Msg}.
+
+%% fold_record/2
+
+fold_record(undefined, R) ->
+ R;
+fold_record(Rec, R) ->
+ diameter_lib:fold_tuple(2, Rec, R).
+
+%% send_request/7
+
+send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) ->
+ #diameter_app{alias = Alias,
+ dictionary = Dict,
+ module = ModX,
+ answer_errors = AE}
+ = App,
+
+ EPkt = encode(Dict, Pkt),
+
+ #options{filter = Filter,
+ timeout = Timeout}
+ = Opts,
+
+ Req = #request{packet = Pkt,
+ from = Caller,
+ handler = self(),
+ transport = TPid,
+ caps = Caps,
+ app = Alias,
+ filter = Filter,
+ dictionary = Dict,
+ module = ModX},
+
+ try
+ TRef = send_request(TPid, EPkt, Req, Timeout),
+ ack(Caller),
+ handle_answer(SvcName, AE, recv_answer(Timeout, SvcName, {TRef, Req}))
+ after
+ erase_request(EPkt)
+ end.
+
+%% Tell caller a send has been attempted.
+ack({Pid, Ref}) ->
+ Pid ! Ref.
+
+%% recv_answer/3
+
+recv_answer(Timeout,
+ SvcName,
+ {TRef, #request{from = {_, Ref}, packet = RPkt} = Req}
+ = T) ->
+
+ %% Matching on TRef below ensures we ignore messages that pertain
+ %% to a previous transport prior to failover. The answer message
+ %% includes the #request{} since it's not necessarily Req; that
+ %% is, from the last peer to which we've transmitted.
+
+ receive
+ {answer = A, Ref, Rq, Pkt} -> %% Answer from peer
+ {A, Rq, Pkt};
+ {timeout = Reason, TRef, _} -> %% No timely reply
+ {error, Req, Reason};
+ {failover = Reason, TRef, false} -> %% No alternate peer
+ {error, Req, Reason};
+ {failover, TRef, Transport} -> %% Resend to alternate peer
+ try_retransmit(Timeout, SvcName, Req, Transport);
+ {failover, TRef} -> %% May have missed failover notification
+ Seqs = diameter_codec:sequence_numbers(RPkt),
+ Pid = whois(SvcName),
+ is_pid(Pid) andalso (Pid ! {failover, TRef, Seqs}),
+ recv_answer(Timeout, SvcName, T)
+ end.
+%% Note that failover starts a new timer and that expiry of an old
+%% timer value is ignored. This means that an answer could be accepted
+%% from a peer after timeout in the case of failover.
+
+try_retransmit(Timeout, SvcName, Req, Transport) ->
+ try retransmit(Transport, Req, SvcName, Timeout) of
+ T -> recv_answer(Timeout, SvcName, T)
+ catch
+ ?FAILURE(Reason) -> {error, Req, Reason}
+ end.
+
+%% handle_error/3
+
+handle_error(Req, Reason, SvcName) ->
+ #request{module = ModX,
+ packet = Pkt,
+ transport = TPid,
+ caps = Caps}
+ = Req,
+ cb(ModX, handle_error, [Reason, msg(Pkt), SvcName, {TPid, Caps}]).
+
+msg(#diameter_packet{msg = undefined, bin = Bin}) ->
+ Bin;
+msg(#diameter_packet{msg = Msg}) ->
+ Msg.
+
+%% encode/2
+
+%% Note that prepare_request can return a diameter_packet containing
+%% header or transport_data. Even allow the returned record to contain
+%% an encoded binary. This isn't the usual case but could some in
+%% handy, for test at least. (For example, to send garbage.)
+
+%% The normal case: encode the returned message.
+encode(Dict, #diameter_packet{msg = Msg, bin = undefined} = Pkt) ->
+ D = pick_dictionary([Dict, ?BASE], Msg),
+ diameter_codec:encode(D, Pkt);
+
+%% Callback has returned an encoded binary: just send.
+encode(_, #diameter_packet{} = Pkt) ->
+ Pkt.
+
+%% pick_dictionary/2
+
+%% Pick the first dictionary that declares the application id in the
+%% specified header.
+pick_dictionary(Ds, [#diameter_header{application_id = Id} | _]) ->
+ pd(Ds, fun(D) -> Id = D:id() end);
+
+%% Pick the first dictionary that knows the specified message name.
+pick_dictionary(Ds, [MsgName|_]) ->
+ pd(Ds, fun(D) -> D:msg2rec(MsgName) end);
+
+%% Pick the first dictionary that knows the name of the specified
+%% message record.
+pick_dictionary(Ds, Rec) ->
+ Name = element(1, Rec),
+ pd(Ds, fun(D) -> D:rec2msg(Name) end).
+
+pd([D|Ds], F) ->
+ try
+ F(D),
+ D
+ catch
+ error:_ ->
+ pd(Ds, F)
+ end;
+
+pd([], _) ->
+ ?ERROR(no_dictionary).
+
+%% send_request/4
+
+send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, Timeout)
+ when node() == node(TPid) ->
+ %% Store the outgoing request before sending to avoid a race with
+ %% reply reception.
+ TRef = store_request(TPid, Bin, Req, Timeout),
+ send(TPid, Pkt),
+ TRef;
+
+%% Send using a remote transport: spawn a process on the remote node
+%% to relay the answer.
+send_request(TPid, #diameter_packet{} = Pkt, Req, Timeout) ->
+ TRef = erlang:start_timer(Timeout, self(), timeout),
+ T = {TPid, Pkt, Req, Timeout, TRef},
+ spawn(node(TPid), ?MODULE, send, [T]),
+ TRef.
+
+%% send/1
+
+send({TPid, Pkt, #request{handler = Pid} = Req, Timeout, TRef}) ->
+ Ref = send_request(TPid, Pkt, Req#request{handler = self()}, Timeout),
+ Pid ! reref(receive T -> T end, Ref, TRef).
+
+reref({T, Ref, R}, Ref, TRef) ->
+ {T, TRef, R};
+reref(T, _, _) ->
+ T.
+
+%% send/2
+
+send(Pid, Pkt) ->
+ Pid ! {send, Pkt}.
+
+%% retransmit/4
+
+retransmit({TPid, Caps, #diameter_app{alias = Alias} = App},
+ #request{app = Alias,
+ packet = Pkt}
+ = Req,
+ SvcName,
+ Timeout) ->
+ have_request(Pkt, TPid) %% Don't failover to a peer we've
+ andalso ?THROW(timeout), %% already sent to.
+
+ case cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]) of
+ {send, P} ->
+ retransmit(make_packet(P, Pkt), TPid, Caps, Req, Timeout);
+ {discard, Reason} ->
+ ?THROW(Reason);
+ discard ->
+ ?THROW(discarded);
+ T ->
+ ?ERROR({invalid_return, prepare_retransmit, App, T})
+ end.
+
+%% retransmit/5
+
+retransmit(Pkt, TPid, Caps, #request{dictionary = Dict} = Req, Timeout) ->
+ EPkt = encode(Dict, Pkt),
+
+ NewReq = Req#request{transport = TPid,
+ packet = Pkt,
+ caps = Caps},
+
+ ?LOG(retransmission, NewReq),
+ TRef = send_request(TPid, EPkt, NewReq, Timeout),
+ {TRef, NewReq}.
+
+%% store_request/4
+
+store_request(TPid, Bin, Req, Timeout) ->
+ Seqs = diameter_codec:sequence_numbers(Bin),
+ TRef = erlang:start_timer(Timeout, self(), timeout),
+ ets:insert(?REQUEST_TABLE, {Seqs, Req, TRef}),
+ ets:member(?REQUEST_TABLE, TPid)
+ orelse (self() ! {failover, TRef}), %% possibly missed failover
+ TRef.
+
+%% lookup_request/2
+
+lookup_request(Msg, TPid)
+ when is_pid(TPid) ->
+ lookup(Msg, TPid, '_');
+
+lookup_request(Msg, TRef)
+ when is_reference(TRef) ->
+ lookup(Msg, '_', TRef).
+
+lookup(Msg, TPid, TRef) ->
+ Seqs = diameter_codec:sequence_numbers(Msg),
+ Spec = [{{Seqs, #request{transport = TPid, _ = '_'}, TRef},
+ [],
+ ['$_']}],
+ case ets:select(?REQUEST_TABLE, Spec) of
+ [{_, Req, _}] ->
+ Req;
+ [] ->
+ false
+ end.
+
+%% erase_request/1
+
+erase_request(Pkt) ->
+ ets:delete(?REQUEST_TABLE, diameter_codec:sequence_numbers(Pkt)).
+
+%% match_requests/1
+
+match_requests(TPid) ->
+ Pat = {'_', #request{transport = TPid, _ = '_'}, '_'},
+ ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]).
+
+%% have_request/2
+
+have_request(Pkt, TPid) ->
+ Seqs = diameter_codec:sequence_numbers(Pkt),
+ Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'},
+ '$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1).
+
+%% request_peer_up/1
+
+request_peer_up(TPid) ->
+ ets:insert(?REQUEST_TABLE, {TPid}).
+
+%% request_peer_down/2
+
+request_peer_down(TPid, S) ->
+ ets:delete(?REQUEST_TABLE, TPid),
+ lists:foreach(fun(T) -> failover(T,S) end, match_requests(TPid)).
+%% Note that a request process can store its request after failover
+%% notifications are sent here: store_request/4 sends the notification
+%% in that case. Note also that we'll send as many notifications to a
+%% given handler as there are peers its sent to. All but one of these
+%% will be ignored.
+
+%%% ---------------------------------------------------------------------------
+%%% recv_request/3
+%%% ---------------------------------------------------------------------------
+
+recv_request(TPid, Pkt, {ConnT, SvcName, Apps}) ->
+ try ets:lookup(ConnT, TPid) of
+ [C] ->
+ recv_request(C, TPid, Pkt, SvcName, Apps);
+ [] -> %% transport has gone down
+ ok
+ catch
+ error: badarg -> %% service has gone down (and taken table with it)
+ ok
+ end.
+
+%% recv_request/5
+
+recv_request(#conn{apps = SApps, caps = Caps}, TPid, Pkt, SvcName, Apps) ->
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}}
+ = Caps,
+
+ #diameter_packet{header = #diameter_header{application_id = Id}}
+ = Pkt,
+
+ recv_request(find_recv_app(Id, SApps),
+ {SvcName, OH, OR},
+ TPid,
+ Apps,
+ Caps,
+ Pkt).
+
+%% find_recv_app/2
+
+%% No one should be sending the relay identifier.
+find_recv_app(?APP_ID_RELAY, _) ->
+ false;
+
+%% With any other id we either support it locally or as a relay.
+find_recv_app(Id, SApps) ->
+ keyfind([Id, ?APP_ID_RELAY], 1, SApps).
+
+%% keyfind/3
+
+keyfind([], _, _) ->
+ false;
+keyfind([Key | Rest], Pos, L) ->
+ case lists:keyfind(Key, Pos, L) of
+ false ->
+ keyfind(Rest, Pos, L);
+ T ->
+ T
+ end.
+
+%% recv_request/6
+
+recv_request({Id, Alias}, T, TPid, Apps, Caps, Pkt) ->
+ #diameter_app{dictionary = Dict}
+ = A
+ = find_app(Alias, Apps),
+ recv_request(T, {TPid, Caps}, A, diameter_codec:decode(Id, Dict, Pkt));
+%% Note that the decode is different depending on whether or not Id is
+%% ?APP_ID_RELAY.
+
+%% DIAMETER_APPLICATION_UNSUPPORTED 3007
+%% A request was sent for an application that is not supported.
+
+recv_request(false, T, TPid, _, _, Pkt) ->
+ As = collect_avps(Pkt),
+ protocol_error(3007, T, TPid, Pkt#diameter_packet{avps = As}).
+
+collect_avps(Pkt) ->
+ case diameter_codec:collect_avps(Pkt) of
+ {_Bs, As} ->
+ As;
+ As ->
+ As
+ end.
+
+%% recv_request/4
+
+%% Wrong number of bits somewhere in the message: reply.
+%%
+%% DIAMETER_INVALID_AVP_BITS 3009
+%% A request was received that included an AVP whose flag bits are
+%% set to an unrecognized value, or that is inconsistent with the
+%% AVP's definition.
+%%
+recv_request(T, {TPid, _}, _, #diameter_packet{errors = [Bs | _]} = Pkt)
+ when is_bitstring(Bs) ->
+ protocol_error(3009, T, TPid, Pkt);
+
+%% Either we support this application but don't recognize the command
+%% or we're a relay and the command isn't proxiable.
+%%
+%% DIAMETER_COMMAND_UNSUPPORTED 3001
+%% The Request contained a Command-Code that the receiver did not
+%% recognize or support. This MUST be used when a Diameter node
+%% receives an experimental command that it does not understand.
+%%
+recv_request(T,
+ {TPid, _},
+ #diameter_app{id = Id},
+ #diameter_packet{header = #diameter_header{is_proxiable = P},
+ msg = M}
+ = Pkt)
+ when ?APP_ID_RELAY /= Id, undefined == M;
+ ?APP_ID_RELAY == Id, not P ->
+ protocol_error(3001, T, TPid, Pkt);
+
+%% Error bit was set on a request.
+%%
+%% DIAMETER_INVALID_HDR_BITS 3008
+%% A request was received whose bits in the Diameter header were
+%% either set to an invalid combination, or to a value that is
+%% inconsistent with the command code's definition.
+%%
+recv_request(T,
+ {TPid, _},
+ _,
+ #diameter_packet{header = #diameter_header{is_error = true}}
+ = Pkt) ->
+ protocol_error(3008, T, TPid, Pkt);
+
+%% A message in a locally supported application or a proxiable message
+%% in the relay application. Don't distinguish between the two since
+%% each application has its own callback config. That is, the user can
+%% easily distinguish between the two cases.
+recv_request(T, TC, App, Pkt) ->
+ request_cb(T, TC, App, examine(Pkt)).
+
+%% Note that there may still be errors but these aren't protocol
+%% (3xxx) errors that lead to an answer-message.
+
+request_cb({SvcName, _OH, _OR} = T, TC, App, Pkt) ->
+ request_cb(cb(App, handle_request, [Pkt, SvcName, TC]), App, T, TC, Pkt).
+
+%% examine/1
+%%
+%% Look for errors in a decoded message. Length errors result in
+%% decode failure in diameter_codec.
+
+examine(#diameter_packet{header = #diameter_header{version
+ = ?DIAMETER_VERSION}}
+ = Pkt) ->
+ Pkt;
+
+%% DIAMETER_UNSUPPORTED_VERSION 5011
+%% This error is returned when a request was received, whose version
+%% number is unsupported.
+
+examine(#diameter_packet{errors = Es} = Pkt) ->
+ Pkt#diameter_packet{errors = [5011 | Es]}.
+%% It's odd/unfortunate that this isn't a protocol error.
+
+%% request_cb/5
+
+%% A reply may be an answer-message, constructed either here or by
+%% the handle_request callback. The header from the incoming request
+%% is passed into the encode so that it can retrieve the relevant
+%% command code in this case. It will also then ignore Dict and use
+%% the base encoder.
+request_cb({reply, Ans},
+ #diameter_app{dictionary = Dict},
+ _,
+ {TPid, _},
+ Pkt) ->
+ reply(Ans, Dict, TPid, Pkt);
+
+%% An 3xxx result code, for which the E-bit is set in the header.
+request_cb({protocol_error, RC}, _, T, {TPid, _}, Pkt)
+ when 3000 =< RC, RC < 4000 ->
+ protocol_error(RC, T, TPid, Pkt);
+
+%% RFC 3588 says we must reply 3001 to anything unrecognized or
+%% unsupported. 'noreply' is undocumented (and inappropriately named)
+%% backwards compatibility for this, protocol_error the documented
+%% alternative.
+request_cb(noreply, _, T, {TPid, _}, Pkt) ->
+ protocol_error(3001, T, TPid, Pkt);
+
+%% Relay a request to another peer. This is equivalent to doing an
+%% explicit call/4 with the message in question except that (1) a loop
+%% will be detected by examining Route-Record AVP's, (3) a
+%% Route-Record AVP will be added to the outgoing request and (3) the
+%% End-to-End Identifier will default to that in the
+%% #diameter_header{} without the need for an end_to_end_identifier
+%% option.
+%%
+%% relay and proxy are similar in that they require the same handling
+%% with respect to Route-Record and End-to-End identifier. The
+%% difference is that a proxy advertises specific applications, while
+%% a relay advertises the relay application. If a callback doesn't
+%% want to distinguish between the cases in the callback return value
+%% then 'resend' is a neutral alternative.
+%%
+request_cb({A, Opts},
+ #diameter_app{id = Id}
+ = App,
+ T,
+ TC,
+ Pkt)
+ when A == relay, Id == ?APP_ID_RELAY;
+ A == proxy, Id /= ?APP_ID_RELAY;
+ A == resend ->
+ resend(Opts, App, T, TC, Pkt);
+
+request_cb(discard, _, _, _, _) ->
+ ok;
+
+request_cb({eval, RC, F}, App, T, TC, Pkt) ->
+ request_cb(RC, App, T, TC, Pkt),
+ diameter_lib:eval(F).
+
+%% protocol_error/4
+
+protocol_error(RC, {_, OH, OR}, TPid, #diameter_packet{avps = Avps} = Pkt) ->
+ ?LOG({error, RC}, Pkt),
+ reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Pkt).
+
+%% resend/5
+%%
+%% Resend a message as a relay or proxy agent.
+
+resend(Opts,
+ #diameter_app{} = App,
+ {_SvcName, OH, _OR} = T,
+ {_TPid, _Caps} = TC,
+ #diameter_packet{avps = Avps} = Pkt) ->
+ {Code, _Flags, Vid} = ?BASE:avp_header('Route-Record'),
+ resend(is_loop(Code, Vid, OH, Avps), Opts, App, T, TC, Pkt).
+
+%% DIAMETER_LOOP_DETECTED 3005
+%% An agent detected a loop while trying to get the message to the
+%% intended recipient. The message MAY be sent to an alternate peer,
+%% if one is available, but the peer reporting the error has
+%% identified a configuration problem.
+
+resend(true, _, _, T, {TPid, _}, Pkt) -> %% Route-Record loop
+ protocol_error(3005, T, TPid, Pkt);
+
+%% 6.1.8. Relaying and Proxying Requests
+%%
+%% A relay or proxy agent MUST append a Route-Record AVP to all requests
+%% forwarded. The AVP contains the identity of the peer the request was
+%% received from.
+
+resend(false,
+ Opts,
+ App,
+ {SvcName, _, _} = T,
+ {TPid, #diameter_caps{origin_host = {_, OH}}},
+ #diameter_packet{header = Hdr0,
+ avps = Avps}
+ = Pkt) ->
+ Route = #diameter_avp{data = {?BASE, 'Route-Record', OH}},
+ Seq = diameter_session:sequence(),
+ Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
+ Msg = [Hdr, Route | Avps],
+ resend(call(SvcName, App, Msg, Opts), T, TPid, Pkt).
+%% The incoming request is relayed with the addition of a
+%% Route-Record. Note the requirement on the return from call/4 below,
+%% which places a requirement on the value returned by the
+%% handle_answer callback of the application module in question.
+%%
+%% Note that there's nothing stopping the request from being relayed
+%% back to the sender. A pick_peer callback may want to avoid this but
+%% a smart peer might recognize the potential loop and choose another
+%% route. A less smart one will probably just relay the request back
+%% again and force us to detect the loop. A pick_peer that wants to
+%% avoid this can specify filter to avoid the possibility.
+%% Eg. {neg, {host, OH} where #diameter_caps{origin_host = {OH, _}}.
+%%
+%% RFC 6.3 says that a relay agent does not modify Origin-Host but
+%% says nothing about a proxy. Assume it should behave the same way.
+
+%% resend/4
+%%
+%% Relay a reply to a relayed request.
+
+%% Answer from the peer: reset the hop by hop identifier and send.
+resend(#diameter_packet{bin = B}
+ = Pkt,
+ _,
+ TPid,
+ #diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
+ transport_data = TD}) ->
+ send(TPid, Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
+ transport_data = TD});
+%% TODO: counters
+
+%% Or not: DIAMETER_UNABLE_TO_DELIVER.
+resend(_, T, TPid, Pkt) ->
+ protocol_error(3002, T, TPid, Pkt).
+
+%% is_loop/4
+%%
+%% Is there a Route-Record AVP with our Origin-Host?
+
+is_loop(Code,
+ Vid,
+ Bin,
+ [#diameter_avp{code = Code, vendor_id = Vid, data = Bin} | _]) ->
+ true;
+
+is_loop(_, _, _, []) ->
+ false;
+
+is_loop(Code, Vid, OH, [_ | Avps])
+ when is_binary(OH) ->
+ is_loop(Code, Vid, OH, Avps);
+
+is_loop(Code, Vid, OH, Avps) ->
+ is_loop(Code, Vid, ?BASE:avp(encode, OH, 'Route-Record'), Avps).
+
+%% reply/4
+%%
+%% Send a locally originating reply.
+
+%% No errors or a diameter_header/avp list.
+reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
+ transport_data = TD}
+ = ReqPkt)
+ when [] == Es;
+ is_record(hd(Msg), diameter_header) ->
+ Pkt = diameter_codec:encode(Dict, make_reply_packet(Msg, ReqPkt)),
+ incr(send, Pkt, Dict, TPid), %% count result codes in sent answers
+ send(TPid, Pkt#diameter_packet{transport_data = TD});
+
+%% Or not: set Result-Code and Failed-AVP AVP's.
+reply(Msg, Dict, TPid, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
+ reply(rc(Msg, rc(H), [A || {_,A} <- Es], Dict),
+ Dict,
+ TPid,
+ Pkt#diameter_packet{errors = []}).
+
+%% make_reply_packet/2
+
+%% Binaries and header/avp lists are sent as-is.
+make_reply_packet(Bin, _)
+ when is_binary(Bin) ->
+ #diameter_packet{bin = Bin};
+make_reply_packet([#diameter_header{} | _] = Msg, _) ->
+ #diameter_packet{msg = Msg};
+
+%% Otherwise a reply message clears the R and T flags and retains the
+%% P flag. The E flag will be set at encode.
+make_reply_packet(Msg, #diameter_packet{header = ReqHdr}) ->
+ Hdr = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
+ is_request = false,
+ is_error = undefined,
+ is_retransmitted = false},
+ #diameter_packet{header = Hdr,
+ msg = Msg}.
+
+%% rc/1
+
+rc({RC, _}) ->
+ RC;
+rc(RC) ->
+ RC.
+
+%% rc/4
+
+rc(Rec, RC, Failed, Dict)
+ when is_integer(RC) ->
+ set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict).
+
+%% Reply as name and tuple list ...
+set([_|_] = Ans, Avps, _) ->
+ Ans ++ Avps; %% Values nearer tail take precedence.
+
+%% ... or record.
+set(Rec, Avps, Dict) ->
+ Dict:'#set-'(Avps, Rec).
+
+%% failed_avp/3
+
+failed_avp(_, [] = No, _) ->
+ No;
+
+failed_avp(Rec, Failed, Dict) ->
+ [fa(Rec, [{'AVP', Failed}], Dict)].
+
+%% Reply as name and tuple list ...
+fa([MsgName | Values], FailedAvp, Dict) ->
+ R = Dict:msg2rec(MsgName),
+ try
+ Dict:'#info-'(R, {index, 'Failed-AVP'}),
+ {'Failed-AVP', [FailedAvp]}
+ catch
+ error: _ ->
+ Avps = proplists:get_value('AVP', Values, []),
+ A = #diameter_avp{name = 'Failed-AVP',
+ value = FailedAvp},
+ {'AVP', [A|Avps]}
+ end;
+
+%% ... or record.
+fa(Rec, FailedAvp, Dict) ->
+ try
+ {'Failed-AVP', [FailedAvp]}
+ catch
+ error: _ ->
+ Avps = Dict:'get-'('AVP', Rec),
+ A = #diameter_avp{name = 'Failed-AVP',
+ value = FailedAvp},
+ {'AVP', [A|Avps]}
+ end.
+
+%% 3. Diameter Header
+%%
+%% E(rror) - If set, the message contains a protocol error,
+%% and the message will not conform to the ABNF
+%% described for this command. Messages with the 'E'
+%% bit set are commonly referred to as error
+%% messages. This bit MUST NOT be set in request
+%% messages. See Section 7.2.
+
+%% 3.2. Command Code ABNF specification
+%%
+%% e-bit = ", ERR"
+%% ; If present, the 'E' bit in the Command
+%% ; Flags is set, indicating that the answer
+%% ; message contains a Result-Code AVP in
+%% ; the "protocol error" class.
+
+%% 7.1.3. Protocol Errors
+%%
+%% Errors that fall within the Protocol Error category SHOULD be treated
+%% on a per-hop basis, and Diameter proxies MAY attempt to correct the
+%% error, if it is possible. Note that these and only these errors MUST
+%% only be used in answer messages whose 'E' bit is set.
+
+%% Thus, only construct answers to protocol errors. Other errors
+%% require an message-specific answer and must be handled by the
+%% application.
+
+%% 6.2. Diameter Answer Processing
+%%
+%% When a request is locally processed, the following procedures MUST be
+%% applied to create the associated answer, in addition to any
+%% additional procedures that MAY be discussed in the Diameter
+%% application defining the command:
+%%
+%% - The same Hop-by-Hop identifier in the request is used in the
+%% answer.
+%%
+%% - The local host's identity is encoded in the Origin-Host AVP.
+%%
+%% - The Destination-Host and Destination-Realm AVPs MUST NOT be
+%% present in the answer message.
+%%
+%% - The Result-Code AVP is added with its value indicating success or
+%% failure.
+%%
+%% - If the Session-Id is present in the request, it MUST be included
+%% in the answer.
+%%
+%% - Any Proxy-Info AVPs in the request MUST be added to the answer
+%% message, in the same order they were present in the request.
+%%
+%% - The 'P' bit is set to the same value as the one in the request.
+%%
+%% - The same End-to-End identifier in the request is used in the
+%% answer.
+%%
+%% Note that the error messages (see Section 7.3) are also subjected to
+%% the above processing rules.
+
+%% 7.3. Error-Message AVP
+%%
+%% The Error-Message AVP (AVP Code 281) is of type UTF8String. It MAY
+%% accompany a Result-Code AVP as a human readable error message. The
+%% Error-Message AVP is not intended to be useful in real-time, and
+%% SHOULD NOT be expected to be parsed by network entities.
+
+%% answer_message/2
+
+answer_message({OH, OR, RC}, Avps) ->
+ {Code, _, Vid} = ?BASE:avp_header('Session-Id'),
+ ['answer-message', {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Result-Code', RC}
+ | session_id(Code, Vid, Avps)].
+
+session_id(Code, Vid, Avps)
+ when is_list(Avps) ->
+ try
+ {value, #diameter_avp{data = D}} = find_avp(Code, Vid, Avps),
+ [{'Session-Id', [?BASE:avp(decode, D, 'Session-Id')]}]
+ catch
+ error: _ ->
+ []
+ end.
+
+%% find_avp/3
+
+find_avp(Code, Vid, Avps)
+ when is_integer(Code), (undefined == Vid orelse is_integer(Vid)) ->
+ find(fun(A) -> is_avp(Code, Vid, A) end, Avps).
+
+%% The final argument here could be a list of AVP's, depending on the case,
+%% but we're only searching at the top level.
+is_avp(Code, Vid, #diameter_avp{code = Code, vendor_id = Vid}) ->
+ true;
+is_avp(_, _, _) ->
+ false.
+
+find(_, []) ->
+ false;
+find(Pred, [H|T]) ->
+ case Pred(H) of
+ true ->
+ {value, H};
+ false ->
+ find(Pred, T)
+ end.
+
+%% 7. Error Handling
+%%
+%% There are certain Result-Code AVP application errors that require
+%% additional AVPs to be present in the answer. In these cases, the
+%% Diameter node that sets the Result-Code AVP to indicate the error
+%% MUST add the AVPs. Examples are:
+%%
+%% - An unrecognized AVP is received with the 'M' bit (Mandatory bit)
+%% set, causes an answer to be sent with the Result-Code AVP set to
+%% DIAMETER_AVP_UNSUPPORTED, and the Failed-AVP AVP containing the
+%% offending AVP.
+%%
+%% - An AVP that is received with an unrecognized value causes an
+%% answer to be returned with the Result-Code AVP set to
+%% DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
+%% AVP causing the error.
+%%
+%% - A command is received with an AVP that is omitted, yet is
+%% mandatory according to the command's ABNF. The receiver issues an
+%% answer with the Result-Code set to DIAMETER_MISSING_AVP, and
+%% creates an AVP with the AVP Code and other fields set as expected
+%% in the missing AVP. The created AVP is then added to the Failed-
+%% AVP AVP.
+%%
+%% The Result-Code AVP describes the error that the Diameter node
+%% encountered in its processing. In case there are multiple errors,
+%% the Diameter node MUST report only the first error it encountered
+%% (detected possibly in some implementation dependent order). The
+%% specific errors that can be described by this AVP are described in
+%% the following section.
+
+%% 7.5. Failed-AVP AVP
+%%
+%% The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+%% debugging information in cases where a request is rejected or not
+%% fully processed due to erroneous information in a specific AVP. The
+%% value of the Result-Code AVP will provide information on the reason
+%% for the Failed-AVP AVP.
+%%
+%% The possible reasons for this AVP are the presence of an improperly
+%% constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
+%% value, the omission of a required AVP, the presence of an explicitly
+%% excluded AVP (see tables in Section 10), or the presence of two or
+%% more occurrences of an AVP which is restricted to 0, 1, or 0-1
+%% occurrences.
+%%
+%% A Diameter message MAY contain one Failed-AVP AVP, containing the
+%% entire AVP that could not be processed successfully. If the failure
+%% reason is omission of a required AVP, an AVP with the missing AVP
+%% code, the missing vendor id, and a zero filled payload of the minimum
+%% required length for the omitted AVP will be added.
+
+%%% ---------------------------------------------------------------------------
+%%% # handle_answer/3
+%%% ---------------------------------------------------------------------------
+
+%% Process an answer message in call-specific process.
+
+handle_answer(SvcName, _, {error, Req, Reason}) ->
+ handle_error(Req, Reason, SvcName);
+
+handle_answer(SvcName,
+ AnswerErrors,
+ {answer, #request{dictionary = Dict} = Req, Pkt}) ->
+ a(examine(diameter_codec:decode(Dict, Pkt)),
+ SvcName,
+ AnswerErrors,
+ Req).
+
+%% We don't really need to do a full decode if we're a relay and will
+%% just resend with a new hop by hop identifier, but might a proxy
+%% want to examine the answer?
+
+a(#diameter_packet{errors = []}
+ = Pkt,
+ SvcName,
+ AE,
+ #request{transport = TPid,
+ dictionary = Dict,
+ caps = Caps,
+ packet = P}
+ = Req) ->
+ try
+ incr(in, Pkt, Dict, TPid)
+ of
+ _ ->
+ cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}])
+ catch
+ exit: {invalid_error_bit, _} = E ->
+ e(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
+ end;
+
+a(#diameter_packet{} = Pkt, SvcName, AE, Req) ->
+ e(Pkt, SvcName, AE, Req).
+
+e(Pkt, SvcName, callback, #request{transport = TPid,
+ caps = Caps,
+ packet = Pkt}
+ = Req) ->
+ cb(Req, handle_answer, [Pkt, msg(Pkt), SvcName, {TPid, Caps}]);
+e(Pkt, SvcName, report, Req) ->
+ x(errors, handle_answer, [SvcName, Req, Pkt]);
+e(Pkt, SvcName, discard, Req) ->
+ x({errors, handle_answer, [SvcName, Req, Pkt]}).
+
+%% Note that we don't check that the application id in the answer's
+%% header is what we expect. (TODO: Does the rfc says anything about
+%% this?)
+
+%% incr/4
+%%
+%% Increment a stats counter for an incoming or outgoing message.
+
+%% TODO: fix
+incr(_, #diameter_packet{msg = undefined}, _, _) ->
+ ok;
+
+incr(Dir, Pkt, Dict, TPid)
+ when is_pid(TPid) ->
+ #diameter_packet{header = #diameter_header{is_error = E}
+ = Hdr,
+ msg = Rec}
+ = Pkt,
+
+ D = choose(E, ?BASE, Dict),
+ RC = int(get_avp_value(D, 'Result-Code', Rec)),
+ PE = is_protocol_error(RC),
+
+ %% Check that the E bit is set only for 3xxx result codes.
+ (not (E orelse PE))
+ orelse (E andalso PE)
+ orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
+
+ Ctr = rc_counter(D, Rec, RC),
+ is_tuple(Ctr)
+ andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
+
+%% incr/2
+
+incr(TPid, Counter) ->
+ diameter_stats:incr(Counter, TPid, 1).
+
+%% RFC 3588, 7.6:
+%%
+%% All Diameter answer messages defined in vendor-specific
+%% applications MUST include either one Result-Code AVP or one
+%% Experimental-Result AVP.
+%%
+%% Maintain statistics assuming one or the other, not both, which is
+%% surely the intent of the RFC.
+
+rc_counter(_, _, RC)
+ when is_integer(RC) ->
+ {'Result-Code', RC};
+rc_counter(D, Rec, _) ->
+ rcc(get_avp_value(D, 'Experimental-Result', Rec)).
+
+%% Outgoing answers may be in any of the forms messages can be sent
+%% in. Incoming messages will be records. We're assuming here that the
+%% arity of the result code AVP's is 0 or 1.
+
+rcc([{_,_,RC} = T])
+ when is_integer(RC) ->
+ T;
+rcc({_,_,RC} = T)
+ when is_integer(RC) ->
+ T;
+rcc(_) ->
+ undefined.
+
+int([N])
+ when is_integer(N) ->
+ N;
+int(N)
+ when is_integer(N) ->
+ N;
+int(_) ->
+ undefined.
+
+is_protocol_error(RC) ->
+ 3000 =< RC andalso RC < 4000.
+
+-spec x(any(), atom(), list()) -> no_return().
+
+%% Warn and exit request process on errors in an incoming answer.
+x(Reason, F, A) ->
+ diameter_lib:warning_report(Reason, {?MODULE, F, A}),
+ x(Reason).
+
+x(T) ->
+ exit(T).
+
+%%% ---------------------------------------------------------------------------
+%%% # failover/[23]
+%%% ---------------------------------------------------------------------------
+
+%% Failover as a consequence of request_peer_down/2.
+failover({_, #request{handler = Pid} = Req, TRef}, S) ->
+ Pid ! {failover, TRef, rt(Req, S)}.
+
+%% Failover as a consequence of store_request/4.
+failover(TRef, Seqs, S)
+ when is_reference(TRef) ->
+ case lookup_request(Seqs, TRef) of
+ #request{} = Req ->
+ failover({Seqs, Req, TRef}, S);
+ false ->
+ ok
+ end.
+
+%% prepare_request returned a binary ...
+rt(#request{packet = #diameter_packet{msg = undefined}}, _) ->
+ false; %% TODO: Not what we should do.
+
+%% ... or not.
+rt(#request{packet = #diameter_packet{msg = Msg}, dictionary = D} = Req, S) ->
+ find_transport(get_destination(Msg, D), Req, S).
+
+%%% ---------------------------------------------------------------------------
+%%% # report_status/5
+%%% ---------------------------------------------------------------------------
+
+report_status(Status,
+ #peer{ref = Ref,
+ conn = TPid,
+ type = Type,
+ options = Opts},
+ #conn{apps = [_|_] = As,
+ caps = Caps},
+ #state{service_name = SvcName}
+ = S,
+ Extra) ->
+ share_peer(Status, Caps, As, TPid, S),
+ Info = [Status, Ref, {TPid, Caps}, {type(Type), Opts} | Extra],
+ send_event(SvcName, list_to_tuple(Info)).
+
+%% send_event/2
+
+send_event(SvcName, Info) ->
+ send_event(#diameter_event{service = SvcName,
+ info = Info}).
+
+send_event(#diameter_event{service = SvcName} = E) ->
+ lists:foreach(fun({_, Pid}) -> Pid ! E end, subscriptions(SvcName)).
+
+%%% ---------------------------------------------------------------------------
+%%% # share_peer/5
+%%% ---------------------------------------------------------------------------
+
+share_peer(up, Caps, Aliases, TPid, #state{share_peers = true,
+ service_name = Svc}) ->
+ diameter_peer:notify(Svc, {peer, TPid, Aliases, Caps});
+
+share_peer(_, _, _, _, _) ->
+ ok.
+
+%%% ---------------------------------------------------------------------------
+%%% # share_peers/2
+%%% ---------------------------------------------------------------------------
+
+share_peers(Pid, #state{share_peers = true,
+ local_peers = PDict}) ->
+ ?Dict:fold(fun(A,Ps,ok) -> sp(Pid, A, Ps), ok end, ok, PDict);
+
+share_peers(_, #state{share_peers = false}) ->
+ ok.
+
+sp(Pid, Alias, Peers) ->
+ lists:foreach(fun({P,C}) -> Pid ! {peer, P, [Alias], C} end, Peers).
+
+%%% ---------------------------------------------------------------------------
+%%% # remote_peer_up/4
+%%% ---------------------------------------------------------------------------
+
+remote_peer_up(Pid, Aliases, Caps, #state{use_shared_peers = true,
+ service = Svc,
+ shared_peers = PDict}
+ = S) ->
+ #diameter_service{applications = Apps} = Svc,
+ Update = lists:filter(fun(A) ->
+ lists:keymember(A, #diameter_app.alias, Apps)
+ end,
+ Aliases),
+ S#state{shared_peers = rpu(Pid, Caps, PDict, Update)};
+
+remote_peer_up(_, _, _, #state{use_shared_peers = false} = S) ->
+ S.
+
+rpu(_, _, PDict, []) ->
+ PDict;
+rpu(Pid, Caps, PDict, Aliases) ->
+ erlang:monitor(process, Pid),
+ T = {Pid, Caps},
+ lists:foldl(fun(A,D) -> ?Dict:append(A, T, D) end,
+ PDict,
+ Aliases).
+
+%%% ---------------------------------------------------------------------------
+%%% # remote_peer_down/2
+%%% ---------------------------------------------------------------------------
+
+remote_peer_down(Pid, #state{use_shared_peers = true,
+ shared_peers = PDict}
+ = S) ->
+ S#state{shared_peers = lists:foldl(fun(A,D) -> rpd(Pid, A, D) end,
+ PDict,
+ ?Dict:fetch_keys(PDict))}.
+
+rpd(Pid, Alias, PDict) ->
+ ?Dict:update(Alias, fun(Ps) -> lists:keydelete(Pid, 1, Ps) end, PDict).
+
+%%% ---------------------------------------------------------------------------
+%%% find_transport/[34]
+%%%
+%%% Output: {TransportPid, #diameter_caps{}, #diameter_app{}}
+%%% | false
+%%% | {error, Reason}
+%%% ---------------------------------------------------------------------------
+
+%% Initial call, from an arbitrary process.
+find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) ->
+ #diameter_service{applications = Apps} = Svc,
+ ft(find_send_app(Alias, Apps), Msg, Opts, S);
+
+%% Relay or proxy send.
+find_transport(#diameter_app{} = App, Msg, Opts, S) ->
+ ft(App, Msg, Opts, S).
+
+ft(#diameter_app{module = Mod, dictionary = D} = App, Msg, Opts, S) ->
+ #options{filter = Filter,
+ extra = Xtra}
+ = Opts,
+ pick_peer(App#diameter_app{module = Mod ++ Xtra},
+ get_destination(Msg, D),
+ Filter,
+ S);
+ft(false = No, _, _, _) ->
+ No.
+
+%% This can't be used if we're a relay and sending a message
+%% in an application not known locally. (TODO)
+find_send_app(Alias, Apps) ->
+ case lists:keyfind(Alias, #diameter_app.alias, Apps) of
+ #diameter_app{id = ?APP_ID_RELAY} ->
+ false;
+ T ->
+ T
+ end.
+
+%% Retransmission, in the service process.
+find_transport([_,_] = RH,
+ Req,
+ #state{service = #diameter_service{pid = Pid,
+ applications = Apps}}
+ = S)
+ when self() == Pid ->
+ #request{app = Alias,
+ filter = Filter,
+ module = ModX}
+ = Req,
+ #diameter_app{}
+ = App
+ = lists:keyfind(Alias, #diameter_app.alias, Apps),
+
+ pick_peer(App#diameter_app{module = ModX},
+ RH,
+ Filter,
+ S).
+
+%% get_destination/2
+
+get_destination(Msg, Dict) ->
+ [str(get_avp_value(Dict, 'Destination-Realm', Msg)),
+ str(get_avp_value(Dict, 'Destination-Host', Msg))].
+
+%% This is not entirely correct. The avp could have an arity 1, in
+%% which case an empty list is a DiameterIdentity of length 0 rather
+%% than the list of no values we treat it as by mapping to undefined.
+%% This behaviour is documented.
+str([]) ->
+ undefined;
+str(T) ->
+ T.
+
+%% get_avp_value/3
+%%
+%% Find an AVP in a message of one of three forms:
+%%
+%% - a message record (as generated from a .dia spec) or
+%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
+%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
+%%
+%% In the first two forms a dictionary module is used at encode to
+%% identify the type of the AVP and its arity in the message in
+%% question. The third form allows messages to be sent as is, without
+%% a dictionary, which is needed in the case of relay agents, for one.
+
+get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
+ try
+ {Code, _, VId} = Dict:avp_header(Name),
+ [A|_] = lists:dropwhile(fun(#diameter_avp{code = C, vendor_id = V}) ->
+ C /= Code orelse V /= VId
+ end,
+ Avps),
+ avp_decode(Dict, Name, A)
+ catch
+ error: _ ->
+ undefined
+ end;
+
+get_avp_value(_, Name, [_MsgName | Avps]) ->
+ case lists:keyfind(Name, 1, Avps) of
+ {_, V} ->
+ V;
+ _ ->
+ undefined
+ end;
+
+%% Message is typically a record but not necessarily: diameter:call/4
+%% can be passed an arbitrary term.
+get_avp_value(Dict, Name, Rec) ->
+ try
+ Dict:'#get-'(Name, Rec)
+ catch
+ error:_ ->
+ undefined
+ end.
+
+avp_decode(Dict, Name, #diameter_avp{value = undefined,
+ data = Bin}) ->
+ Dict:avp(decode, Bin, Name);
+avp_decode(_, _, #diameter_avp{value = V}) ->
+ V.
+
+%%% ---------------------------------------------------------------------------
+%%% # pick_peer(App, [DestRealm, DestHost], Filter, #state{})
+%%%
+%%% Output: {TransportPid, #diameter_caps{}, App}
+%%% | false
+%%% | {error, Reason}
+%%% ---------------------------------------------------------------------------
+
+%% Find transports to a given realm/host.
+
+pick_peer(#diameter_app{alias = Alias}
+ = App,
+ [_,_] = RH,
+ Filter,
+ #state{local_peers = L,
+ shared_peers = S,
+ service_name = SvcName,
+ service = #diameter_service{pid = Pid}}) ->
+ pick_peer(peers(Alias, RH, Filter, L),
+ peers(Alias, RH, Filter, S),
+ Pid,
+ SvcName,
+ App).
+
+%% pick_peer/5
+
+pick_peer([], [], _, _, _) ->
+ false;
+
+%% App state is mutable but we're not in the service process: go there.
+pick_peer(Local, Remote, Pid, _SvcName, #diameter_app{mutable = true} = App)
+ when self() /= Pid ->
+ call_service(Pid, {pick_peer, Local, Remote, App});
+
+%% App state isn't mutable or it is and we're in the service process:
+%% do the deed.
+pick_peer(Local,
+ Remote,
+ _Pid,
+ SvcName,
+ #diameter_app{module = ModX,
+ alias = Alias,
+ init_state = S,
+ mutable = M}
+ = App) ->
+ MFA = {ModX, pick_peer, [Local, Remote, SvcName]},
+
+ try state_cb(App, MFA) of
+ {ok, {TPid, #diameter_caps{} = Caps}} when is_pid(TPid) ->
+ {TPid, Caps, App};
+ {{TPid, #diameter_caps{} = Caps}, ModS} when is_pid(TPid), M ->
+ mod_state(Alias, ModS),
+ {TPid, Caps, App};
+ {false = No, ModS} when M ->
+ mod_state(Alias, ModS),
+ No;
+ {ok, false = No} ->
+ No;
+ false = No ->
+ No;
+ {{TPid, #diameter_caps{} = Caps}, S} when is_pid(TPid) ->
+ {TPid, Caps, App}; %% Accept returned state in the immutable
+ {false = No, S} -> %% case as long it isn't changed.
+ No;
+ T ->
+ diameter_lib:error_report({invalid, T, App}, MFA)
+ catch
+ E: Reason ->
+ diameter_lib:error_report({failure, {E, Reason, ?STACK}}, MFA)
+ end.
+
+%% peers/4
+
+peers(Alias, RH, Filter, Peers) ->
+ case ?Dict:find(Alias, Peers) of
+ {ok, L} ->
+ ps(L, RH, Filter, {[],[]});
+ error ->
+ []
+ end.
+
+%% Place a peer whose Destination-Host/Realm matches those of the
+%% request at the front of the result list. Could add some sort of
+%% 'sort' option to allow more control.
+
+ps([], _, _, {Ys, Ns}) ->
+ lists:reverse(Ys, Ns);
+ps([{_TPid, #diameter_caps{} = Caps} = TC | Rest], RH, Filter, Acc) ->
+ ps(Rest, RH, Filter, pacc(caps_filter(Caps, RH, Filter),
+ caps_filter(Caps, RH, {all, [host, realm]}),
+ TC,
+ Acc)).
+
+pacc(true, true, Peer, {Ts, Fs}) ->
+ {[Peer|Ts], Fs};
+pacc(true, false, Peer, {Ts, Fs}) ->
+ {Ts, [Peer|Fs]};
+pacc(_, _, _, Acc) ->
+ Acc.
+
+%% caps_filter/3
+
+caps_filter(C, RH, {neg, F}) ->
+ not caps_filter(C, RH, F);
+
+caps_filter(C, RH, {all, L})
+ when is_list(L) ->
+ lists:all(fun(F) -> caps_filter(C, RH, F) end, L);
+
+caps_filter(C, RH, {any, L})
+ when is_list(L) ->
+ lists:any(fun(F) -> caps_filter(C, RH, F) end, L);
+
+caps_filter(#diameter_caps{origin_host = {_,OH}}, [_,DH], host) ->
+ eq(undefined, DH, OH);
+
+caps_filter(#diameter_caps{origin_realm = {_,OR}}, [DR,_], realm) ->
+ eq(undefined, DR, OR);
+
+caps_filter(C, _, Filter) ->
+ caps_filter(C, Filter).
+
+%% caps_filter/2
+
+caps_filter(_, none) ->
+ true;
+
+caps_filter(#diameter_caps{origin_host = {_,OH}}, {host, H}) ->
+ eq(any, H, OH);
+
+caps_filter(#diameter_caps{origin_realm = {_,OR}}, {realm, R}) ->
+ eq(any, R, OR);
+
+%% Anything else is expected to be an eval filter. Filter failure is
+%% documented as being equivalent to a non-matching filter.
+
+caps_filter(C, T) ->
+ try
+ {eval, F} = T,
+ diameter_lib:eval([F,C])
+ catch
+ _:_ -> false
+ end.
+
+eq(Any, Id, PeerId) ->
+ Any == Id orelse try
+ iolist_to_binary(Id) == iolist_to_binary(PeerId)
+ catch
+ _:_ -> false
+ end.
+%% OctetString() can be specified as an iolist() so test for string
+%% rather then term equality.
+
+%% transports/1
+
+transports(#state{peerT = PeerT}) ->
+ ets:select(PeerT, [{#peer{conn = '$1', _ = '_'},
+ [{'is_pid', '$1'}],
+ ['$1']}]).
+
+%%% ---------------------------------------------------------------------------
+%%% # service_info/2
+%%% ---------------------------------------------------------------------------
+
+%% The config passed to diameter:start_service/2.
+-define(CAP_INFO, ['Origin-Host',
+ 'Origin-Realm',
+ 'Vendor-Id',
+ 'Product-Name',
+ 'Origin-State-Id',
+ 'Host-IP-Address',
+ 'Supported-Vendor-Id',
+ 'Auth-Application-Id',
+ 'Inband-Security-Id',
+ 'Acct-Application-Id',
+ 'Vendor-Specific-Application-Id',
+ 'Firmware-Revision']).
+
+-define(ALL_INFO, [capabilities,
+ applications,
+ transport,
+ pending,
+ statistics]).
+
+service_info(Items, S)
+ when is_list(Items) ->
+ [{complete(I), service_info(I,S)} || I <- Items];
+service_info(Item, S)
+ when is_atom(Item) ->
+ service_info(Item, S, true).
+
+service_info(Item, #state{service = Svc} = S, Complete) ->
+ case Item of
+ name ->
+ S#state.service_name;
+ 'Origin-Host' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.origin_host;
+ 'Origin-Realm' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.origin_realm;
+ 'Vendor-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.vendor_id;
+ 'Product-Name' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.product_name;
+ 'Origin-State-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.origin_state_id;
+ 'Host-IP-Address' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.host_ip_address;
+ 'Supported-Vendor-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.supported_vendor_id;
+ 'Auth-Application-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.auth_application_id;
+ 'Inband-Security-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.inband_security_id;
+ 'Acct-Application-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.acct_application_id;
+ 'Vendor-Specific-Application-Id' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.vendor_specific_application_id;
+ 'Firmware-Revision' ->
+ (Svc#diameter_service.capabilities)
+ #diameter_caps.firmware_revision;
+ capabilities -> service_info(?CAP_INFO, S);
+ applications -> info_apps(S);
+ transport -> info_transport(S);
+ pending -> info_pending(S);
+ statistics -> info_stats(S);
+ keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test
+ all -> service_info(?ALL_INFO, S);
+ _ when Complete -> service_info(complete(Item), S, false);
+ _ -> undefined
+ end.
+
+complete(Pre) ->
+ P = atom_to_list(Pre),
+ case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO,
+ lists:prefix(P, atom_to_list(I))]
+ of
+ [I] -> I;
+ _ -> Pre
+ end.
+
+info_stats(#state{peerT = PeerT}) ->
+ Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'},
+ [{'is_pid', '$2'}],
+ [['$1', '$2']]}]),
+ diameter_stats:read(lists:append(Peers)).
+%% TODO: include peer identities in return value
+
+info_transport(#state{peerT = PeerT, connT = ConnT}) ->
+ dict:fold(fun it/3,
+ [],
+ ets:foldl(fun(T,A) -> it_acc(ConnT, A, T) end,
+ dict:new(),
+ PeerT)).
+
+it(Ref, [[{type, connect} | _] = L], Acc) ->
+ [[{ref, Ref} | L] | Acc];
+it(Ref, [[{type, accept}, {options, Opts} | _] | _] = L, Acc) ->
+ [[{ref, Ref},
+ {type, listen},
+ {options, Opts},
+ {accept, [lists:nthtail(2,A) || A <- L]}]
+ | Acc].
+%% Each entry has the same Opts. (TODO)
+
+it_acc(ConnT, Acc, #peer{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts,
+ op_state = OS,
+ started = T,
+ conn = TPid}) ->
+ dict:append(Ref,
+ [{type, Type},
+ {options, Opts},
+ {watchdog, {Pid, T, OS}}
+ | info_conn(ConnT, TPid)],
+ Acc).
+
+info_conn(ConnT, TPid) ->
+ info_conn(ets:lookup(ConnT, TPid)).
+
+info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) ->
+ [{peer, {Pid, T}},
+ {apps, SApps},
+ {caps, info_caps(Caps)}];
+info_conn([] = No) ->
+ No.
+
+info_caps(#diameter_caps{} = C) ->
+ lists:zip(record_info(fields, diameter_caps), tl(tuple_to_list(C))).
+
+info_apps(#state{service = #diameter_service{applications = Apps}}) ->
+ lists:map(fun mk_app/1, Apps).
+
+mk_app(#diameter_app{alias = Alias,
+ dictionary = Dict,
+ module = ModX,
+ id = Id}) ->
+ [{alias, Alias},
+ {dictionary, Dict},
+ {module, ModX},
+ {id, Id}].
+
+info_pending(#state{} = S) ->
+ MatchSpec = [{{'$1',
+ #request{transport = '$2',
+ from = '$3',
+ app = '$4',
+ _ = '_'},
+ '_'},
+ [?ORCOND([{'==', T, '$2'} || T <- transports(S)])],
+ [{{'$1', [{{app, '$4'}},
+ {{transport, '$2'}},
+ {{from, '$3'}}]}}]}],
+
+ ets:select(?REQUEST_TABLE, MatchSpec).
diff --git a/lib/diameter/src/base/diameter_service_sup.erl b/lib/diameter/src/base/diameter_service_sup.erl
new file mode 100644
index 0000000000..153fff902f
--- /dev/null
+++ b/lib/diameter/src/base/diameter_service_sup.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% The supervisor of service processes.
+%%
+
+-module(diameter_service_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0, %% supervisor start
+ start_child/1]). %% service start
+
+%% supervisor callback
+-export([init/1]).
+
+-define(NAME, ?MODULE). %% supervisor name
+
+%% start_link/0
+
+start_link() ->
+ SupName = {local, ?NAME},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+%% start_child/1
+%%
+%% A service and its related processes (transport, peer_fwm and
+%% watchdog) are all temporary since they're all restarted in
+%% application code. A Transport and peer_fsm is restarted by a
+%% watchdog as required by the RFC 3539 state machine, a watchdog is
+%% restarted by service, and services are restarted by diameter_config.
+
+start_child(ServiceName) ->
+ supervisor:start_child(?NAME, [ServiceName]).
+
+%% init/1
+
+init([]) ->
+ Mod = diameter_service,
+ Flags = {simple_one_for_one, 0, 1},
+ ChildSpec = {Mod,
+ {Mod, start_link, []},
+ temporary,
+ 1000,
+ worker,
+ [Mod]},
+ {ok, {Flags, [ChildSpec]}}.
diff --git a/lib/diameter/src/base/diameter_session.erl b/lib/diameter/src/base/diameter_session.erl
new file mode 100644
index 0000000000..bb91e97f39
--- /dev/null
+++ b/lib/diameter/src/base/diameter_session.erl
@@ -0,0 +1,172 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_session).
+
+-export([sequence/0,
+ session_id/1,
+ origin_state_id/0]).
+
+%% towards diameter_sup
+-export([init/0]).
+
+-include("diameter_types.hrl").
+
+-define(INT64, 16#FFFFFFFFFFFFFFFF).
+-define(INT32, 16#FFFFFFFF).
+
+%% ---------------------------------------------------------------------------
+%% # sequence/0
+%%
+%% Output: 32-bit
+%% ---------------------------------------------------------------------------
+
+%% 3588, 3:
+%%
+%% Hop-by-Hop Identifier
+%% The Hop-by-Hop Identifier is an unsigned 32-bit integer field (in
+%% network byte order) and aids in matching requests and replies.
+%% The sender MUST ensure that the Hop-by-Hop identifier in a request
+%% is unique on a given connection at any given time, and MAY attempt
+%% to ensure that the number is unique across reboots. The sender of
+%% an Answer message MUST ensure that the Hop-by-Hop Identifier field
+%% contains the same value that was found in the corresponding
+%% request. The Hop-by-Hop identifier is normally a monotonically
+%% increasing number, whose start value was randomly generated. An
+%% answer message that is received with an unknown Hop-by-Hop
+%% Identifier MUST be discarded.
+%%
+%% End-to-End Identifier
+%% The End-to-End Identifier is an unsigned 32-bit integer field (in
+%% network byte order) and is used to detect duplicate messages.
+%% Upon reboot implementations MAY set the high order 12 bits to
+%% contain the low order 12 bits of current time, and the low order
+%% 20 bits to a random value. Senders of request messages MUST
+%% insert a unique identifier on each message. The identifier MUST
+%% remain locally unique for a period of at least 4 minutes, even
+%% across reboots. The originator of an Answer message MUST ensure
+%% that the End-to-End Identifier field contains the same value that
+%% was found in the corresponding request. The End-to-End Identifier
+%% MUST NOT be modified by Diameter agents of any kind. The
+%% combination of the Origin-Host (see Section 6.3) and this field is
+%% used to detect duplicates. Duplicate requests SHOULD cause the
+%% same answer to be transmitted (modulo the hop-by-hop Identifier
+%% field and any routing AVPs that may be present), and MUST NOT
+%% affect any state that was set when the original request was
+%% processed. Duplicate answer messages that are to be locally
+%% consumed (see Section 6.2) SHOULD be silently discarded.
+
+-spec sequence()
+ -> 'Unsigned32'().
+
+sequence() ->
+ Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT32, _SetVal = 0},
+ ets:update_counter(diameter_sequence, sequence, Instr).
+
+%% ---------------------------------------------------------------------------
+%% # origin_state_id/0
+%% ---------------------------------------------------------------------------
+
+%% 3588, 8.16:
+%%
+%% The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
+%% monotonically increasing value that is advanced whenever a Diameter
+%% entity restarts with loss of previous state, for example upon reboot.
+%% Origin-State-Id MAY be included in any Diameter message, including
+%% CER.
+%%
+%% A Diameter entity issuing this AVP MUST create a higher value for
+%% this AVP each time its state is reset. A Diameter entity MAY set
+%% Origin-State-Id to the time of startup, or it MAY use an incrementing
+%% counter retained in non-volatile memory across restarts.
+
+-spec origin_state_id()
+ -> 'Unsigned32'().
+
+origin_state_id() ->
+ ets:lookup_element(diameter_sequence, origin_state_id, 2).
+
+%% ---------------------------------------------------------------------------
+%% # session_id/1
+%% ---------------------------------------------------------------------------
+
+%% 3588, 8.8:
+%%
+%% The Session-Id MUST begin with the sender's identity encoded in the
+%% DiameterIdentity type (see Section 4.4). The remainder of the
+%% Session-Id is delimited by a ";" character, and MAY be any sequence
+%% that the client can guarantee to be eternally unique; however, the
+%% following format is recommended, (square brackets [] indicate an
+%% optional element):
+%%
+%% ;;[;]
+%%
+%% and are decimal representations of the
+%% high and low 32 bits of a monotonically increasing 64-bit value. The
+%% 64-bit value is rendered in two part to simplify formatting by 32-bit
+%% processors. At startup, the high 32 bits of the 64-bit value MAY be
+%% initialized to the time, and the low 32 bits MAY be initialized to
+%% zero. This will for practical purposes eliminate the possibility of
+%% overlapping Session-Ids after a reboot, assuming the reboot process
+%% takes longer than a second. Alternatively, an implementation MAY
+%% keep track of the increasing value in non-volatile memory.
+%%
+%% is implementation specific but may include a modem's
+%% device Id, a layer 2 address, timestamp, etc.
+
+-spec session_id('DiameterIdentity'())
+ -> 'OctetString'().
+%% Note that Session-Id has type UTF8String and that any OctetString
+%% is a UTF8String.
+
+session_id(Host) ->
+ Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT64, _Set = 0},
+ N = ets:update_counter(diameter_sequence, session_base, Instr),
+ Hi = N bsr 32,
+ Lo = N band ?INT32,
+ [Host, ";", integer_to_list(Hi),
+ ";", integer_to_list(Lo),
+ ";", atom_to_list(node())].
+
+%% ---------------------------------------------------------------------------
+%% # init/0
+%% ---------------------------------------------------------------------------
+
+init() ->
+ Now = now(),
+ random:seed(Now),
+ Time = time32(Now),
+ Seq = (?INT32 band (Time bsl 20)) bor (random:uniform(1 bsl 20) - 1),
+ ets:insert(diameter_sequence, [{origin_state_id, Time},
+ {session_base, Time bsl 32},
+ {sequence, Seq}]),
+ Time.
+
+%% ---------------------------------------------------------
+%% INTERNAL FUNCTIONS
+%% ---------------------------------------------------------
+
+%% The minimum value represented by a Time value. (See diameter_types.)
+%% 32 bits extends to 2104.
+-define(TIME0, 62105714048). %% {{1968,1,20},{3,14,8}}
+
+time32(Now) ->
+ Time = calendar:now_to_universal_time(Now),
+ Diff = calendar:datetime_to_gregorian_seconds(Time) - ?TIME0,
+ Diff band ?INT32.
diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl
new file mode 100644
index 0000000000..71479afa95
--- /dev/null
+++ b/lib/diameter/src/base/diameter_stats.erl
@@ -0,0 +1,342 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% Statistics collector.
+%%
+
+-module(diameter_stats).
+-compile({no_auto_import, [monitor/2]}).
+
+-behaviour(gen_server).
+
+-export([reg/1, reg/2,
+ incr/1, incr/2, incr/3,
+ read/1,
+ flush/0, flush/1]).
+
+%% supervisor callback
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+ terminate/2,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ code_change/3]).
+
+%% debug
+-export([state/0,
+ uptime/0]).
+
+-include("diameter_internal.hrl").
+
+%% ets table containing stats. reg(Pid, Ref) inserts a {Pid, Ref},
+%% incr(Counter, X, N) updates the counter keyed at {Counter, X}, and
+%% Pid death causes counters keyed on {Counter, Pid} to be deleted and
+%% added to those keyed on {Counter, Ref}.
+-define(TABLE, ?MODULE).
+
+%% Name of registered server.
+-define(SERVER, ?MODULE).
+
+%% Entries in the table.
+-define(REC(Key, Value), {Key, Value}).
+
+%% Server state.
+-record(state, {id = now()}).
+
+-type counter() :: any().
+-type contrib() :: any().
+
+%%% ---------------------------------------------------------------------------
+%%% # reg(Pid, Contrib)
+%%%
+%%% Description: Register a process as a contributor of statistics
+%%% associated with a specified term. Statistics can be
+%%% contributed by specifying either Pid or Contrib as
+%%% the second argument to incr/3. Statistics contributed
+%%% by Pid are folded into the corresponding entry for
+%%% Contrib when the process dies.
+%%%
+%%% Contrib can be any term but should not be a pid
+%%% passed as the first argument to reg/2. Subsequent
+%%% registrations for the same Pid overwrite the association
+%%% ---------------------------------------------------------------------------
+
+-spec reg(pid(), contrib())
+ -> true.
+
+reg(Pid, Contrib)
+ when is_pid(Pid) ->
+ call({reg, Pid, Contrib}).
+
+-spec reg(contrib())
+ -> true.
+
+reg(Ref) ->
+ reg(self(), Ref).
+
+%%% ---------------------------------------------------------------------------
+%%% # incr(Counter, Contrib, N)
+%%%
+%%% Description: Increment a counter for the specified contributor.
+%%%
+%%% Contrib will typically be an argument passed to reg/2
+%%% but there's nothing that requires this. In particular,
+%%% if Contrib is a pid that hasn't been registered then
+%%% counters are unaffected by the death of the process.
+%%% ---------------------------------------------------------------------------
+
+-spec incr(counter(), contrib(), integer())
+ -> integer().
+
+incr(Ctr, Contrib, N) ->
+ update_counter({Ctr, Contrib}, N).
+
+incr(Ctr, N)
+ when is_integer(N) ->
+ incr(Ctr, self(), N);
+
+incr(Ctr, Contrib) ->
+ incr(Ctr, Contrib, 1).
+
+incr(Ctr) ->
+ incr(Ctr, self(), 1).
+
+%%% ---------------------------------------------------------------------------
+%%% # read(Contribs)
+%%%
+%%% Description: Retrieve counters for the specified contributors.
+%%% ---------------------------------------------------------------------------
+
+-spec read([contrib()])
+ -> [{contrib(), [{counter(), integer()}]}].
+
+read(Contribs) ->
+ lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end,
+ orddict:new(),
+ ets:select(?TABLE, [{?REC({'_', '$1'}, '_'),
+ [?ORCOND([{'=:=', '$1', {const, C}}
+ || C <- Contribs])],
+ ['$_']}])).
+
+%%% ---------------------------------------------------------------------------
+%%% # flush(Contrib)
+%%%
+%%% Description: Retrieve and delete statistics for the specified
+%%% contributor.
+%%%
+%%% If Contrib is a pid registered with reg/2 then statistics
+%%% for both and its associated contributor are retrieved.
+%%% ---------------------------------------------------------------------------
+
+-spec flush(contrib())
+ -> [{counter(), integer()}].
+
+flush(Contrib) ->
+ try
+ call({flush, Contrib})
+ catch
+ exit: _ ->
+ []
+ end.
+
+flush() ->
+ flush(self()).
+
+%%% ---------------------------------------------------------
+%%% EXPORTED INTERNAL FUNCTIONS
+%%% ---------------------------------------------------------
+
+start_link() ->
+ ServerName = {local, ?SERVER},
+ Module = ?MODULE,
+ Args = [],
+ Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
+ gen_server:start_link(ServerName, Module, Args, Options).
+
+state() ->
+ call(state).
+
+uptime() ->
+ call(uptime).
+
+%%% ----------------------------------------------------------
+%%% # init(_)
+%%%
+%%% Output: {ok, State}
+%%% ----------------------------------------------------------
+
+init([]) ->
+ ets:new(?TABLE, [named_table, ordered_set, public]),
+ {ok, #state{}}.
+
+%% ----------------------------------------------------------
+%% handle_call(Request, From, State)
+%% ----------------------------------------------------------
+
+handle_call(state, _, State) ->
+ {reply, State, State};
+
+handle_call(uptime, _, #state{id = Time} = State) ->
+ {reply, diameter_lib:now_diff(Time), State};
+
+handle_call({reg, Pid, Contrib}, _From, State) ->
+ monitor(not ets:member(?TABLE, Pid), Pid),
+ {reply, insert(?REC(Pid, Contrib)), State};
+
+handle_call({flush, Contrib}, _From, State) ->
+ {reply, fetch(Contrib), State};
+
+handle_call(Req, From, State) ->
+ ?UNEXPECTED([Req, From]),
+ {reply, nok, State}.
+
+%% ----------------------------------------------------------
+%% handle_cast(Request, State)
+%% ----------------------------------------------------------
+
+handle_cast({incr, Rec}, State) ->
+ update_counter(Rec),
+ {noreply, State};
+
+handle_cast(Msg, State) ->
+ ?UNEXPECTED([Msg]),
+ {noreply, State}.
+
+%% ----------------------------------------------------------
+%% handle_info(Request, State)
+%% ----------------------------------------------------------
+
+handle_info({'DOWN', _MRef, process, Pid, _}, State) ->
+ down(Pid),
+ {noreply, State};
+
+handle_info(Info, State) ->
+ ?UNEXPECTED([Info]),
+ {noreply, State}.
+
+%% ----------------------------------------------------------
+%% terminate(Reason, State)
+%% ----------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%% ----------------------------------------------------------
+%% code_change(OldVsn, State, Extra)
+%% ----------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%% ---------------------------------------------------------
+%%% INTERNAL FUNCTIONS
+%%% ---------------------------------------------------------
+
+%% monitor/2
+
+monitor(true, Pid) ->
+ erlang:monitor(process, Pid);
+monitor(false = No, _) ->
+ No.
+
+%% down/1
+
+down(Pid) ->
+ L = ets:match_object(?TABLE, ?REC({'_', Pid}, '_')),
+ [?REC(_, Ref) = T] = lookup(Pid),
+ fold(Ref, L),
+ delete_object(T),
+ delete(L).
+
+%% Fold Pid-based entries into Ref-based ones.
+fold(Ref, L) ->
+ lists:foreach(fun(?REC({K, _}, V)) -> update_counter({{K, Ref}, V}) end,
+ L).
+
+delete(Objs) ->
+ lists:foreach(fun delete_object/1, Objs).
+
+%% fetch/1
+
+fetch(X) ->
+ MatchSpec = [{?REC({'_', '$1'}, '_'),
+ [?ORCOND([{'==', '$1', {const, T}} || T <- [X | ref(X)]])],
+ ['$_']}],
+ L = ets:select(?TABLE, MatchSpec),
+ delete(L),
+ D = lists:foldl(fun sum/2, dict:new(), L),
+ dict:to_list(D).
+
+sum({{Ctr, _}, N}, Dict) ->
+ dict:update(Ctr, fun(V) -> V+N end, N, Dict).
+
+ref(Pid)
+ when is_pid(Pid) ->
+ ets:select(?TABLE, [{?REC(Pid, '$1'), [], ['$1']}]);
+ref(_) ->
+ [].
+
+%% update_counter/2
+%%
+%% From an arbitrary request process. Cast to the server process to
+%% insert a new element if the counter doesn't exists so that two
+%% processes don't do so simultaneously.
+
+update_counter(Key, N) ->
+ try
+ ets:update_counter(?TABLE, Key, N)
+ catch
+ error: badarg ->
+ cast({incr, ?REC(Key, N)})
+ end.
+
+%% update_counter/1
+%%
+%% From the server process.
+
+update_counter(?REC(Key, N) = T) ->
+ try
+ ets:update_counter(?TABLE, Key, N)
+ catch
+ error: badarg ->
+ insert(T)
+ end.
+
+insert(T) ->
+ ets:insert(?TABLE, T).
+
+lookup(Key) ->
+ ets:lookup(?TABLE, Key).
+
+delete_object(T) ->
+ ets:delete_object(?TABLE, T).
+
+%% cast/1
+
+cast(Msg) ->
+ gen_server:cast(?SERVER, Msg).
+
+%% call/1
+
+call(Request) ->
+ gen_server:call(?SERVER, Request, infinity).
diff --git a/lib/diameter/src/base/diameter_sup.erl b/lib/diameter/src/base/diameter_sup.erl
new file mode 100644
index 0000000000..e5afd23dcd
--- /dev/null
+++ b/lib/diameter/src/base/diameter_sup.erl
@@ -0,0 +1,101 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% The top supervisor for the diameter application.
+%%
+
+-module(diameter_sup).
+
+-behaviour(supervisor).
+
+%% interface
+-export([start_link/0, %% supervisor start
+ tree/0]). %% supervision tree
+
+%% supervisor callback
+-export([init/1]).
+
+-define(CHILDREN, [diameter_misc_sup,
+ diameter_watchdog_sup,
+ diameter_peer_fsm_sup,
+ diameter_transport_sup,
+ diameter_service_sup]).
+
+-define(TABLES, [{diameter_sequence, [set]},
+ {diameter_service, [set, {keypos, 3}]},
+ {diameter_request, [bag]},
+ {diameter_config, [bag, {keypos, 2}]}]).
+
+%% start_link/0
+
+start_link() ->
+ SupName = {local, ?MODULE},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+%% init/1
+
+init([]) ->
+ ets_new(?TABLES),
+ diameter_session:init(),
+ Flags = {one_for_one, 1, 5},
+ ChildSpecs = lists:map(fun spec/1, ?CHILDREN),
+ {ok, {Flags, ChildSpecs}}.
+
+%% spec/1
+
+spec(Mod) ->
+ {Mod,
+ {Mod, start_link, []},
+ permanent,
+ 1000,
+ supervisor,
+ [Mod]}.
+
+%% ets_new/1
+
+ets_new(List)
+ when is_list(List) ->
+ lists:foreach(fun ets_new/1, List);
+
+ets_new({Table, Opts}) ->
+ ets:new(Table, [named_table, public | Opts]).
+
+%% tree/0
+
+tree() ->
+ [{?MODULE, whereis(?MODULE), tree(?MODULE)}].
+
+tree(Sup) ->
+ lists:map(fun t/1, supervisor:which_children(Sup)).
+
+t({Name, Pid, supervisor, _}) ->
+ t(Name, Pid, tree(Pid));
+t({Name, Pid, worker, _}) ->
+ t(Name, Pid).
+
+t(undefined, Pid, Children) ->
+ {Pid, Children};
+t(Name, Pid, Children) ->
+ {Name, Pid, Children}.
+
+t(undefined, Pid) ->
+ Pid;
+t(Name, Pid) ->
+ {Name, Pid}.
diff --git a/lib/diameter/src/base/diameter_sync.erl b/lib/diameter/src/base/diameter_sync.erl
new file mode 100644
index 0000000000..ce2db4b3a2
--- /dev/null
+++ b/lib/diameter/src/base/diameter_sync.erl
@@ -0,0 +1,550 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% This module implements a server that serializes requests in named
+%% queues. A request is an MFA or fun and a name can be any term. A
+%% request is applied in a dedicated process that terminates when
+%% the request function returns.
+%%
+
+-module(diameter_sync).
+-behaviour(gen_server).
+
+-export([call/4, call/5,
+ cast/4, cast/5,
+ carp/1, carp/2]).
+
+%% supervisor callback
+-export([start_link/0]).
+
+%% gen_server interface
+-export([init/1,
+ terminate/2,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ code_change/3]).
+
+%% test/debug
+-export([state/0,
+ uptime/0,
+ flush/1,
+ pending/0,
+ pending/1,
+ queues/0,
+ pids/1]).
+
+-include("diameter_internal.hrl").
+
+%% Locally registered server name.
+-define(SERVER, ?MODULE).
+
+%% Message to the server to queue a request ...
+-define(REQUEST(CallOrCast, Name, Req, Max, Timeout),
+ {request, CallOrCast, Name, Req, Max, Timeout}).
+
+%% ... and to retrieve the pid of the prevailing request process.
+-define(CARP(Name),
+ {carp, Name}).
+
+%% Forever ...
+-define(TIMEOUT, 30000).
+
+%% Server state.
+-record(state,
+ {time = now(),
+ pending = 0 :: non_neg_integer(), %% outstanding requests
+ monitor = new() :: ets:tid(), %% MonitorRef -> {Name, From}
+ queue = new() :: ets:tid()}). %% Name -> queue of {Pid, Ref}
+
+%% ----------------------------------------------------------
+%% # call(Node, Name, Req, Max, Timeout)
+%% # call(Name, Req, Max, Timeout)
+%%
+%% Input: Name = term() identifying the queue in which the request is
+%% to be evaluated.
+%% Req = {M,F,A}
+%% | {Fun, Arg}
+%% | [Fun | Args]
+%% | Fun
+%% Max = Upper bound for the number of outstanding requests
+%% in the named queue for Req to be queued.
+%% If more than this number are in the queue then
+%% 'rejected' is returned to the caller. Can be any
+%% term but integer() | infinity is sufficient.
+%% Timeout = 32 bit integer() number of milliseconds after which
+%% request is cancelled (if not already started), causing
+%% 'timeout' to be returned to the caller.
+%% | infinity
+%%
+%% Output: Req() | rejected | timeout
+%%
+%% Description: Serialize a request in a named queue. Note that if
+%% 'timeout' is returned and the request itself does not
+%% return this atom then request has not been evaluated.
+%% ----------------------------------------------------------
+
+call(Name, Req, Max, Timeout) ->
+ call(node(), Name, Req, Max, Timeout).
+
+call(Node, Name, Req, Max, Timeout) ->
+ gen_call({?SERVER, Node}, ?REQUEST(call, Name, Req, Max, Timeout)).
+
+%%% ----------------------------------------------------------
+%%% # cast(Node, Name, Req, Max, Timeout)
+%%% # cast(Name, Req, Max, Timeout)
+%%%
+%%% Output: ok | rejected | timeout
+%%%
+%%% Description: Serialize a request without returning the result to the
+%%% caller. Returns after the task is queued.
+%%% ----------------------------------------------------------
+
+cast(Name, Req, Max, Timeout) ->
+ cast(node(), Name, Req, Max, Timeout).
+
+cast(Node, Name, Req, Max, Timeout) ->
+ gen_call({?SERVER, Node}, ?REQUEST(cast, Name, Req, Max, Timeout)).
+
+%% 'timeout' is only return if the server process that processes
+%% requests isn't alive. Ditto for call/carp.
+
+%%% ----------------------------------------------------------
+%%% # carp(Node, Name)
+%%% # carp(Name)
+%%%
+%%% Output: {value, Pid} | false | timeout
+%%%
+%%% Description: Return the pid of the process processing the task
+%%% at the head of the named queue. Note that the value
+%%% returned by subsequent calls changes as tasks are
+%%% completed, each task executing in a dedicated
+%%% process. The exit value of this process will be
+%%% {value, Req()} if the task returns.
+%%% ----------------------------------------------------------
+
+%% The intention of this is to let a process enqueue a task that waits
+%% for a message before completing, the target pid being retrieved
+%% with carp/[12].
+
+carp(Name) ->
+ carp(node(), Name).
+
+carp(Node, Name) ->
+ gen_call({?SERVER, Node}, ?CARP(Name)).
+
+%%% ---------------------------------------------------------
+%%% EXPORTED INTERNAL FUNCTIONS
+%%% ---------------------------------------------------------
+
+state() ->
+ call(state).
+
+uptime() ->
+ call(uptime).
+
+flush(Name) ->
+ call({flush, Name}).
+
+pending() ->
+ call(pending).
+
+pending(Name) ->
+ call({pending, Name}).
+
+queues() ->
+ call(queues).
+
+pids(Name) ->
+ call({pids, Name}).
+
+%%% ----------------------------------------------------------
+%%% # start_link()
+%%% ----------------------------------------------------------
+
+start_link() ->
+ ServerName = {local, ?SERVER},
+ Module = ?MODULE,
+ Args = [],
+ Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
+ gen_server:start_link(ServerName, Module, Args, Options).
+
+%%% ----------------------------------------------------------
+%%% # init(_)
+%%% ----------------------------------------------------------
+
+init(_) ->
+ {ok, #state{}}.
+
+%%% ----------------------------------------------------------
+%%% # handle_call(Request, From, State)
+%%% ----------------------------------------------------------
+
+%% Enqueue a new request.
+handle_call(?REQUEST(Type, Name, Req, Max, Timeout),
+ From,
+ #state{queue = QD} = State) ->
+ T = find(Name, QD),
+ nq(queued(T) =< Max, T, {Type, From}, Name, Req, Timeout, State);
+
+handle_call(Request, From, State) ->
+ {reply, call(Request, From, State), State}.
+
+%% call/3
+
+call(?CARP(Name), _, #state{queue = QD}) ->
+ pcar(find(Name, QD));
+
+call(state, _, State) ->
+ State;
+
+call(uptime, _, #state{time = T}) ->
+ diameter_lib:now_diff(T);
+
+call({flush, Name}, _, #state{queue = QD}) ->
+ cancel(find(Name, QD));
+
+call(pending, _, #state{pending = N}) ->
+ N;
+
+call({pending, Name}, _, #state{queue = QD}) ->
+ queued(find(Name, QD));
+
+call(queues, _, #state{queue = QD}) ->
+ fetch_keys(QD);
+
+call({pids, Name}, _, #state{queue = QD}) ->
+ plist(find(Name, QD));
+
+call(Req, From, _State) -> %% ignore
+ ?UNEXPECTED(handle_call, [Req, From]),
+ nok.
+
+%%% ----------------------------------------------------------
+%%% handle_cast(Request, State)
+%%% ----------------------------------------------------------
+
+handle_cast(Msg, State) ->
+ ?UNEXPECTED([Msg]),
+ {noreply, State}.
+
+%%% ----------------------------------------------------------
+%%% handle_info(Request, State)
+%%% ----------------------------------------------------------
+
+handle_info(Request, State) ->
+ {noreply, info(Request, State)}.
+
+%% info/2
+
+%% A request has completed execution or timed out.
+info({'DOWN', MRef, process, Pid, Info},
+ #state{pending = N,
+ monitor = MD,
+ queue = QD}
+ = State) ->
+ {Name, From} = fetch(MRef, MD),
+ reply(From, rc(Info)),
+ State#state{pending = N-1,
+ monitor = erase(MRef, MD),
+ queue = dq(fetch(Name, QD), Pid, Info, Name, QD)};
+
+info(Info, State) ->
+ ?UNEXPECTED(handle_info, [Info]),
+ State.
+
+reply({call, From}, T) ->
+ gen_server:reply(From, T);
+reply(cast, _) ->
+ ok.
+
+rc({value, T}) ->
+ T;
+rc(_) ->
+ timeout.
+
+%%% ----------------------------------------------------------
+%%% code_change(OldVsn, State, Extra)
+%%% ----------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%% ----------------------------------------------------------
+%%% terminate(Reason, State)
+%%% ----------------------------------------------------------
+
+terminate(_Reason, _State)->
+ ok.
+
+%%% ---------------------------------------------------------
+%%% INTERNAL FUNCTIONS
+%%% ---------------------------------------------------------
+
+%% queued/1
+
+queued({ok, {N,_}}) ->
+ N;
+queued(error) ->
+ 0.
+
+%% nq/7
+
+%% Maximum number of pending requests exceeded ...
+nq(false, _, _, _Name, _Req, _Timeout, State) ->
+ {reply, rejected, State};
+
+%% ... or not.
+nq(true, T, From, Name, Req, Timeout, #state{pending = N,
+ monitor = MD,
+ queue = QD}
+ = State) ->
+ Ref = make_ref(),
+ Pid = init(Ref, Req, timeout(Timeout, T)),
+ MRef = erlang:monitor(process, Pid),
+ {noreply, State#state{pending = N+1,
+ monitor = store(MRef, {Name, from(From)}, MD),
+ queue = store(Name, nq(T, {Pid, Ref}), QD)}}.
+
+from({call, _} = T) ->
+ T;
+from({cast = T, From}) ->
+ gen_server:reply(From, ok),
+ T.
+
+%% nq/2
+
+%% Other requests in the queue: append.
+nq({ok, {N,Q}}, T) ->
+ {N+1, queue:in(T,Q)};
+
+%% Queue is empty: start execution.
+nq(error, T) ->
+ go(T),
+ {1, queue:from_list([T])}.
+
+%% Don't timeout if the request is evaluated immediately so as to
+%% avoid a race between getting a 'go' and a 'timeout'. Queueing a
+%% request in an empty queue always results in execution.
+timeout(_, error) ->
+ infinity;
+timeout(Timeout, _) ->
+ Timeout.
+
+%% dq/5
+%%
+%% A request process has terminated.
+
+dq({N,Q}, Pid, _Info, Name, QD) ->
+ {{value, T}, TQ} = queue:out(Q),
+ dq(N-1, Pid, T, TQ, Name, QD).
+
+%% dq/6
+
+%% Request was at the head of the queue: start another.
+dq(N, Pid, {Pid, _}, TQ, Name, QD) ->
+ dq(N, TQ, Name, QD);
+
+%% Or not: remove the offender from the queue.
+dq(N, Pid, T, TQ, Name, QD) ->
+ store(Name, {N, req(Pid, queue:from_list([T]), TQ)}, QD).
+
+%% dq/4
+
+%% Queue is empty: erase.
+dq(0, TQ, Name, QD) ->
+ true = queue:is_empty(TQ), %% assert
+ erase(Name, QD);
+
+%% Start the next request.
+dq(N, TQ, Name, QD) ->
+ go(queue:head(TQ)),
+ store(Name, {N, TQ}, QD).
+
+%% req/3
+%%
+%% Find and remove the queue element for the specified pid.
+
+req(Pid, HQ, Q) ->
+ {{value, T}, TQ} = queue:out(Q),
+ req(Pid, T, HQ, TQ).
+
+req(Pid, {Pid, _}, HQ, TQ) ->
+ queue:join(HQ, TQ);
+req(Pid, T, HQ, TQ) ->
+ req(Pid, queue:in(T,HQ), TQ).
+
+%% go/1
+
+go({Pid, Ref}) ->
+ Pid ! {Ref, ok}.
+
+%% init/4
+%%
+%% Start the dedicated process for handling a request. The exit value
+%% is as promised by carp/1.
+
+init(Ref, Req, Timeout) ->
+ spawn(fun() -> exit(i(Ref, Req, Timeout)) end).
+
+i(Ref, Req, Timeout) ->
+ Timer = send_timeout(Ref, Timeout),
+ MRef = erlang:monitor(process, ?SERVER),
+ receive
+ {Ref, ok} -> %% Do the deed.
+ %% Ensure we don't leave messages in the mailbox since the
+ %% request itself might receive. Alternatively, could have
+ %% done the eval in a new process but then we'd have to
+ %% relay messages arriving at this one.
+ cancel_timer(Timer),
+ erlang:demonitor(MRef, [flush]),
+ %% Ref is to ensure that we don't extract any message that
+ %% a client may have sent after retrieving self() with
+ %% carp/1, there being no guarantee that the message
+ %% banged by go/1 is received before the pid becomes
+ %% accessible.
+ {value, eval(Req)};
+ {Ref, timeout = T} ->
+ T;
+ {'DOWN', MRef, process, _Pid, _Info} = D -> %% server death
+ D
+ end.
+
+send_timeout(_Ref, infinity = No) ->
+ No;
+send_timeout(Ref, Ms) ->
+ Msg = {Ref, timeout},
+ TRef = erlang:send_after(Ms, self(), Msg),
+ {TRef, Msg}.
+
+cancel_timer(infinity = No) ->
+ No;
+cancel_timer({TRef, Msg}) ->
+ flush(Msg, erlang:cancel_timer(TRef)).
+
+flush(Msg, false) -> %% Message has already been sent ...
+ %% 'error' should never happen but crash if it does so as not to
+ %% hang the process.
+ ok = receive Msg -> ok after ?TIMEOUT -> error end;
+flush(_, _) -> %% ... or not.
+ ok.
+
+eval({M,F,A}) ->
+ apply(M,F,A);
+eval([Fun | Args]) ->
+ apply(Fun, Args);
+eval({Fun, A}) ->
+ Fun(A);
+eval(Fun) ->
+ Fun().
+
+%% pcar/1
+
+pcar({ok, {_,Q}}) ->
+ {Pid, _Ref} = queue:head(Q),
+ {value, Pid};
+pcar(error) ->
+ false.
+
+%% plist/1
+
+plist({ok, {_,Q}}) ->
+ lists:map(fun({Pid, _Ref}) -> Pid end, queue:to_list(Q));
+plist(error) ->
+ [].
+
+%% cancel/1
+%%
+%% Cancel all but the active request from the named queue. Return the
+%% number of requests cancelled.
+
+%% Just send timeout messages to each request to make them die. Note
+%% that these are guaranteed to arrive before a go message after the
+%% current request completes since both messages are sent from the
+%% server process.
+cancel({ok, {N,Q}}) ->
+ {_,TQ} = queue:split(1,Q),
+ foreach(fun({Pid, Ref}) -> Pid ! {Ref, timeout} end, N-1, TQ),
+ N-1;
+cancel(error) ->
+ 0.
+
+%% foreach/3
+
+foreach(_, 0, _) ->
+ ok;
+foreach(Fun, N, Q) ->
+ Fun(queue:head(Q)),
+ foreach(Fun, N-1, queue:tail(Q)).
+
+%% call/1
+
+%% gen_server:call/3 will exit if the target process dies.
+call(Request) ->
+ try
+ gen_server:call(?SERVER, Request, ?TIMEOUT)
+ catch
+ exit: Reason ->
+ {error, Reason}
+ end.
+
+%% dict-like table manipulation.
+
+erase(Key, Dict) ->
+ ets:delete(Dict, Key),
+ Dict.
+
+fetch(Key, Dict) ->
+ {ok, V} = find(Key, Dict),
+ V.
+
+fetch_keys(Dict) ->
+ ets:foldl(fun({K,_}, Acc) -> [K | Acc] end, [], Dict).
+
+find(Key, Dict) ->
+ case ets:lookup(Dict, Key) of
+ [{Key, V}] ->
+ {ok, V};
+ [] ->
+ error
+ end.
+
+new() ->
+ ets:new(?MODULE, [set]).
+
+store(Key, Value, Dict) ->
+ store({Key, Value}, Dict).
+
+store({_,_} = T, Dict) ->
+ ets:insert(Dict, T),
+ Dict.
+
+%% gen_call/1
+
+gen_call(Server, Req) ->
+ gen_call(Server, Req, infinity).
+
+gen_call(Server, Req, Timeout) ->
+ try
+ gen_server:call(Server, Req, Timeout)
+ catch
+ exit: _ ->
+ timeout
+ end.
diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl
new file mode 100644
index 0000000000..6b1b1b8d39
--- /dev/null
+++ b/lib/diameter/src/base/diameter_types.erl
@@ -0,0 +1,537 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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(diameter_types).
+
+%%
+%% Encode/decode of RFC 3588 Data Formats, Basic (section 4.2) and
+%% Derived (section 4.3).
+%%
+
+%% Basic types.
+-export(['OctetString'/2,
+ 'Integer32'/2,
+ 'Integer64'/2,
+ 'Unsigned32'/2,
+ 'Unsigned64'/2,
+ 'Float32'/2,
+ 'Float64'/2]).
+
+%% Derived types.
+-export(['Address'/2,
+ 'Time'/2,
+ 'UTF8String'/2,
+ 'DiameterIdentity'/2,
+ 'DiameterURI'/2,
+ 'IPFilterRule'/2,
+ 'QoSFilterRule'/2]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+-define(UINT(N,X), ((0 =< X) andalso (X < 1 bsl N))).
+-define(SINT(N,X), ((-1*(1 bsl (N-1)) < X) andalso (X < 1 bsl (N-1)))).
+
+%% The Grouped and Enumerated types are dealt with directly in
+%% generated decode modules by way of diameter_gen.hrl and
+%% diameter_codec.erl. Padding and the setting of Length and other
+%% fields are also dealt with there.
+
+%% 3588:
+%%
+%% DIAMETER_INVALID_AVP_LENGTH 5014
+%% The request contained an AVP with an invalid length. A Diameter
+%% message indicating this error MUST include the offending AVPs
+%% within a Failed-AVP AVP.
+%%
+-define(INVALID_LENGTH(Bin), erlang:error({'DIAMETER', 5014, Bin})).
+
+%% -------------------------------------------------------------------------
+%% 3588, 4.2. Basic AVP Data Formats
+%%
+%% The Data field is zero or more octets and contains information
+%% specific to the Attribute. The format and length of the Data field
+%% is determined by the AVP Code and AVP Length fields. The format of
+%% the Data field MUST be one of the following base data types or a data
+%% type derived from the base data types. In the event that a new Basic
+%% AVP Data Format is needed, a new version of this RFC must be created.
+%% --------------------
+
+'OctetString'(decode, Bin)
+ when is_binary(Bin) ->
+ binary_to_list(Bin);
+
+'OctetString'(encode = M, zero) ->
+ 'OctetString'(M, []);
+
+'OctetString'(encode, Str) ->
+ iolist_to_binary(Str).
+
+%% --------------------
+
+'Integer32'(decode, <>) ->
+ X;
+
+'Integer32'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Integer32'(encode = M, zero) ->
+ 'Integer32'(M, 0);
+
+'Integer32'(encode, I)
+ when ?SINT(32,I) ->
+ <>.
+
+%% --------------------
+
+'Integer64'(decode, <>) ->
+ X;
+
+'Integer64'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Integer64'(encode = M, zero) ->
+ 'Integer64'(M, 0);
+
+'Integer64'(encode, I)
+ when ?SINT(64,I) ->
+ <>.
+
+%% --------------------
+
+'Unsigned32'(decode, <>) ->
+ X;
+
+'Unsigned32'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Unsigned32'(encode = M, zero) ->
+ 'Unsigned32'(M, 0);
+
+'Unsigned32'(encode, I)
+ when ?UINT(32,I) ->
+ <>.
+
+%% --------------------
+
+'Unsigned64'(decode, <>) ->
+ X;
+
+'Unsigned64'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Unsigned64'(encode = M, zero) ->
+ 'Unsigned64'(M, 0);
+
+'Unsigned64'(encode, I)
+ when ?UINT(64,I) ->
+ <>.
+
+%% --------------------
+
+%% Decent summaries of the IEEE floating point formats can be
+%% found at http://en.wikipedia.org/wiki/IEEE_754-1985 and
+%% http://www.psc.edu/general/software/packages/ieee/ieee.php.
+%%
+%% That the bit syntax uses these formats isn't well documented but
+%% this does indeed appear to be the case. However, the bit syntax
+%% only encodes numeric values, not the standard's (signed) infinity
+%% or NaN. It also encodes any large value as 'infinity', never 'NaN'.
+%% Treat these equivalently on decode for this reason.
+%%
+%% An alternative would be to decode infinity/NaN to the largest
+%% possible float but could likely lead to misleading results if
+%% arithmetic is performed on the decoded value. Better to be explicit
+%% that precision has been lost.
+
+'Float32'(decode, <>) ->
+ choose(S, infinity, '-infinity');
+
+'Float32'(decode, <>) ->
+ X;
+
+'Float32'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Float32'(encode = M, zero) ->
+ 'Float32'(M, 0.0);
+
+'Float32'(encode, infinity) ->
+ <<0:1, 255:8, 0:23>>;
+
+'Float32'(encode, '-infinity') ->
+ <<1:1, 255:8, 0:23>>;
+
+'Float32'(encode, X)
+ when is_float(X) ->
+ <>.
+%% Note that this could also encode infinity/-infinity for large
+%% (signed) numeric values. Note also that precision is lost just in
+%% using the floating point syntax. For example:
+%%
+%% 1> B = <<3.14159:32/float>>.
+%% <<64,73,15,208>>
+%% 2> <> = B.
+%% <<64,73,15,208>>
+%% 3> F.
+%% 3.141590118408203
+%%
+%% (The 64 bit type does better.)
+
+%% --------------------
+
+%% The 64 bit format is entirely analogous to the 32 bit format.
+
+'Float64'(decode, <>) ->
+ choose(S, infinity, '-infinity');
+
+'Float64'(decode, <>) ->
+ X;
+
+'Float64'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Float64'(encode, infinity) ->
+ <<0:1, 2047:11, 0:52>>;
+
+'Float64'(encode, '-infinity') ->
+ <<1:1, 2047:11, 0:52>>;
+
+'Float64'(encode = M, zero) ->
+ 'Float64'(M, 0.0);
+
+'Float64'(encode, X)
+ when is_float(X) ->
+ <>.
+
+%% -------------------------------------------------------------------------
+%% 3588, 4.3. Derived AVP Data Formats
+%%
+%% In addition to using the Basic AVP Data Formats, applications may
+%% define data formats derived from the Basic AVP Data Formats. An
+%% application that defines new AVP Derived Data Formats MUST include
+%% them in a section entitled "AVP Derived Data Formats", using the same
+%% format as the definitions below. Each new definition must be either
+%% defined or listed with a reference to the RFC that defines the
+%% format.
+%% --------------------
+
+'Address'(encode, zero) ->
+ <<0:48>>;
+
+'Address'(decode, <<1:16, B/binary>>)
+ when size(B) == 4 ->
+ list_to_tuple(binary_to_list(B));
+
+'Address'(decode, <<2:16, B/binary>>)
+ when size(B) == 16 ->
+ list_to_tuple(v6dec(B, []));
+
+'Address'(decode, <> = B)
+ when 1 == A;
+ 2 == A ->
+ ?INVALID_LENGTH(B);
+
+'Address'(encode, T) ->
+ ipenc(diameter_lib:ipaddr(T)).
+
+ipenc(T)
+ when is_tuple(T), size(T) == 4 ->
+ B = list_to_binary(tuple_to_list(T)),
+ <<1:16, B/binary>>;
+
+ipenc(T)
+ when is_tuple(T), size(T) == 8 ->
+ B = v6enc(lists:reverse(tuple_to_list(T)), <<>>),
+ <<2:16, B/binary>>.
+
+v6dec(<>, Acc) ->
+ v6dec(B, [N | Acc]);
+
+v6dec(<<>>, Acc) ->
+ lists:reverse(Acc).
+
+v6enc([N | Rest], B)
+ when ?UINT(16,N) ->
+ v6enc(Rest, <>);
+
+v6enc([], B) ->
+ B.
+
+%% --------------------
+
+%% A DiameterIdentity is a FQDN as definined in RFC 1035, which is at
+%% least one character.
+
+'DiameterIdentity'(encode = M, zero) ->
+ 'OctetString'(M, [0]);
+
+'DiameterIdentity'(encode = M, X) ->
+ <<_,_/binary>> = 'OctetString'(M, X);
+
+'DiameterIdentity'(decode = M, <<_,_/binary>> = X) ->
+ 'OctetString'(M, X).
+
+%% --------------------
+
+'DiameterURI'(decode, Bin)
+ when is_binary(Bin) ->
+ scan_uri(Bin);
+
+%% The minimal DiameterURI is "aaa://x", 7 characters.
+'DiameterURI'(encode = M, zero) ->
+ 'OctetString'(M, lists:duplicate(0,7));
+
+'DiameterURI'(encode, #diameter_uri{type = Type,
+ fqdn = D,
+ port = P,
+ transport = T,
+ protocol = Prot}
+ = U) ->
+ S = lists:append([atom_to_list(Type), "://", D,
+ ":", integer_to_list(P),
+ ";transport=", atom_to_list(T),
+ ";protocol=", atom_to_list(Prot)]),
+ U = scan_uri(S), %% assert
+ list_to_binary(S);
+
+'DiameterURI'(encode, Str) ->
+ Bin = iolist_to_binary(Str),
+ #diameter_uri{} = scan_uri(Bin), %% type check
+ Bin.
+
+%% --------------------
+
+%% This minimal rule is "deny in 0 from 0.0.0.0 to 0.0.0.0", 33 characters.
+'IPFilterRule'(encode = M, zero) ->
+ 'OctetString'(M, lists:duplicate(0,33));
+
+%% TODO: parse grammar.
+'IPFilterRule'(M, X) ->
+ 'OctetString'(M, X).
+
+%% --------------------
+
+%% This minimal rule is the same as for an IPFilterRule.
+'QoSFilterRule'(encode = M, zero = X) ->
+ 'IPFilterRule'(M, X);
+
+%% TODO: parse grammar.
+'QoSFilterRule'(M, X) ->
+ 'OctetString'(M, X).
+
+%% --------------------
+
+'UTF8String'(decode, Bin) ->
+ udec(Bin, []);
+
+'UTF8String'(encode = M, zero) ->
+ 'UTF8String'(M, []);
+
+'UTF8String'(encode, S) ->
+ uenc(S, []).
+
+udec(<<>>, Acc) ->
+ lists:reverse(Acc);
+
+udec(<>, Acc) ->
+ udec(Rest, [C | Acc]).
+
+uenc(E, Acc)
+ when E == [];
+ E == <<>> ->
+ list_to_binary(lists:reverse(Acc));
+
+uenc(<>, Acc) ->
+ uenc(Rest, [<> | Acc]);
+
+uenc([[] | Rest], Acc) ->
+ uenc(Rest, Acc);
+
+uenc([[H|T] | Rest], Acc) ->
+ uenc([H, T | Rest], Acc);
+
+uenc([C | Rest], Acc) ->
+ uenc(Rest, [<> | Acc]).
+
+%% --------------------
+
+%% RFC 3588, 4.3:
+%%
+%% Time
+%% The Time format is derived from the OctetString AVP Base Format.
+%% The string MUST contain four octets, in the same format as the
+%% first four bytes are in the NTP timestamp format. The NTP
+%% Timestamp format is defined in chapter 3 of [SNTP].
+%%
+%% This represents the number of seconds since 0h on 1 January 1900
+%% with respect to the Coordinated Universal Time (UTC).
+%%
+%% On 6h 28m 16s UTC, 7 February 2036 the time value will overflow.
+%% SNTP [SNTP] describes a procedure to extend the time to 2104.
+%% This procedure MUST be supported by all DIAMETER nodes.
+
+%% RFC 2030, 3:
+%%
+%% As the NTP timestamp format has been in use for the last 17 years,
+%% it remains a possibility that it will be in use 40 years from now
+%% when the seconds field overflows. As it is probably inappropriate
+%% to archive NTP timestamps before bit 0 was set in 1968, a
+%% convenient way to extend the useful life of NTP timestamps is the
+%% following convention: If bit 0 is set, the UTC time is in the
+%% range 1968-2036 and UTC time is reckoned from 0h 0m 0s UTC on 1
+%% January 1900. If bit 0 is not set, the time is in the range 2036-
+%% 2104 and UTC time is reckoned from 6h 28m 16s UTC on 7 February
+%% 2036. Note that when calculating the correspondence, 2000 is not a
+%% leap year. Note also that leap seconds are not counted in the
+%% reckoning.
+%%
+%% The statement regarding year 2000 is wrong: errata id 518 at
+%% http://www.rfc-editor.org/errata_search.php?rfc=2030 notes this.
+
+-define(TIME_1900, 59958230400). %% {{1900,1,1},{0,0,0}}
+-define(TIME_2036, 64253197696). %% {{2036,2,7},{6,28,16}}
+%% TIME_2036 = TIME_1900 + (1 bsl 32)
+
+%% Time maps [0, 1 bsl 31) onto [TIME_1900 + 1 bsl 31, TIME_2036 + 1 bsl 31)
+%% by taking integers with the high-order bit set relative to TIME_1900
+%% and those without relative to TIME_2036. This corresponds to the
+%% following dates.
+-define(TIME_MIN, {{1968,1,20},{3,14,8}}). %% TIME_1900 + 1 bsl 31
+-define(TIME_MAX, {{2104,2,26},{9,42,24}}). %% TIME_2036 + 1 bsl 31
+
+'Time'(decode, <>) ->
+ Offset = msb(1 == Time bsr 31),
+ calendar:gregorian_seconds_to_datetime(Time + Offset);
+
+'Time'(decode, B) ->
+ ?INVALID_LENGTH(B);
+
+'Time'(encode, {{_Y,_M,_D},{_HH,_MM,_SS}} = Datetime)
+ when ?TIME_MIN =< Datetime, Datetime < ?TIME_MAX ->
+ S = calendar:datetime_to_gregorian_seconds(Datetime),
+ T = S - msb(S < ?TIME_2036),
+ 0 = T bsr 32, %% sanity check
+ <>;
+
+'Time'(encode, zero) ->
+ <<0:32>>.
+
+%% ===========================================================================
+%% ===========================================================================
+
+choose(0, X, _) -> X;
+choose(1, _, X) -> X.
+
+msb(true) -> ?TIME_1900;
+msb(false) -> ?TIME_2036.
+
+%% RFC 3588, 4.3:
+%%
+%% The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
+%% syntax [URI] rules specified below:
+%%
+%% "aaa://" FQDN [ port ] [ transport ] [ protocol ]
+%%
+%% ; No transport security
+%%
+%% "aaas://" FQDN [ port ] [ transport ] [ protocol ]
+%%
+%% ; Transport security used
+%%
+%% FQDN = Fully Qualified Host Name
+%%
+%% port = ":" 1*DIGIT
+%%
+%% ; One of the ports used to listen for
+%% ; incoming connections.
+%% ; If absent,
+%% ; the default Diameter port (3868) is
+%% ; assumed.
+%%
+%% transport = ";transport=" transport-protocol
+%%
+%% ; One of the transports used to listen
+%% ; for incoming connections. If absent,
+%% ; the default SCTP [SCTP] protocol is
+%% ; assumed. UDP MUST NOT be used when
+%% ; the aaa-protocol field is set to
+%% ; diameter.
+%%
+%% transport-protocol = ( "tcp" / "sctp" / "udp" )
+%%
+%% protocol = ";protocol=" aaa-protocol
+%%
+%% ; If absent, the default AAA protocol
+%% ; is diameter.
+%%
+%% aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
+
+scan_uri(Bin)
+ when is_binary(Bin) ->
+ scan_uri(binary_to_list(Bin));
+scan_uri("aaa://" ++ Rest) ->
+ scan_fqdn(Rest, #diameter_uri{type = aaa});
+scan_uri("aaas://" ++ Rest) ->
+ scan_fqdn(Rest, #diameter_uri{type = aaas}).
+
+scan_fqdn(S, U) ->
+ {[_|_] = F, Rest} = lists:splitwith(fun is_fqdn/1, S),
+ scan_opt_port(Rest, U#diameter_uri{fqdn = F}).
+
+scan_opt_port(":" ++ S, U) ->
+ {[_|_] = P, Rest} = lists:splitwith(fun is_digit/1, S),
+ scan_opt_transport(Rest, U#diameter_uri{port = list_to_integer(P)});
+scan_opt_port(S, U) ->
+ scan_opt_transport(S, U).
+
+scan_opt_transport(";transport=" ++ S, U) ->
+ {P, Rest} = transport(S),
+ scan_opt_protocol(Rest, U#diameter_uri{transport = P});
+scan_opt_transport(S, U) ->
+ scan_opt_protocol(S, U).
+
+scan_opt_protocol(";protocol=" ++ S, U) ->
+ {P, ""} = protocol(S),
+ U#diameter_uri{protocol = P};
+scan_opt_protocol("", U) ->
+ U.
+
+transport("tcp" ++ S) ->
+ {tcp, S};
+transport("sctp" ++ S) ->
+ {sctp, S};
+transport("udp" ++ S) ->
+ {udp, S}.
+
+protocol("diameter" ++ S) ->
+ {diameter, S};
+protocol("radius" ++ S) ->
+ {radius, S};
+protocol("tacacs+" ++ S) ->
+ {'tacacs+', S}.
+
+is_fqdn(C) ->
+ is_digit(C) orelse is_alpha(C) orelse C == $. orelse C == $-.
+
+is_alpha(C) ->
+ ($a =< C andalso C =< $z) orelse ($A =< C andalso C =< $Z).
+
+is_digit(C) ->
+ $0 =< C andalso C =< $9.
diff --git a/lib/diameter/src/base/diameter_types.hrl b/lib/diameter/src/base/diameter_types.hrl
new file mode 100644
index 0000000000..02bf8a74dd
--- /dev/null
+++ b/lib/diameter/src/base/diameter_types.hrl
@@ -0,0 +1,139 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% Types for function specifications, primarily in diameter.erl. This
+%% has nothing specifically to do with diameter_types.erl.
+%%
+
+-type evaluable()
+ :: {module(), atom(), list()}
+ | fun()
+ | nonempty_improper_list(evaluable(), list()). %% [evaluable() | Args]
+
+-type app_alias()
+ :: any().
+
+-type service_name()
+ :: any().
+
+%% Diameter basic types
+
+-type 'OctetString'() :: iolist().
+-type 'Integer32'() :: -2147483647..2147483647.
+-type 'Integer64'() :: -9223372036854775807..9223372036854775807.
+-type 'Unsigned32'() :: 0..4294967295.
+-type 'Unsigned64'() :: 0..18446744073709551615.
+-type 'Float32'() :: '-infinity' | float() | infinity.
+-type 'Float64'() :: '-infinity' | float() | infinity.
+-type 'Grouped'() :: list() | tuple().
+
+%% Diameter derived types
+
+-type 'Address'()
+ :: inet:ip_address()
+ | string().
+
+-type 'Time'() :: {{integer(), 1..12, 1..31},
+ {0..23, 0..59, 0..59}}.
+-type 'UTF8String'() :: iolist().
+-type 'DiameterIdentity'() :: 'OctetString'().
+-type 'DiameterURI'() :: 'OctetString'().
+-type 'Enumerated'() :: 'Integer32'().
+-type 'IPFilterRule'() :: 'OctetString'().
+-type 'QoSFilterRule'() :: 'OctetString'().
+
+%% Capabilities options/avps on start_service/2 and/or add_transport/2
+
+-type capability()
+ :: {'Origin-Host', 'DiameterIdentity'()}
+ | {'Origin-Realm', 'DiameterIdentity'()}
+ | {'Host-IP-Address', ['Address'()]}
+ | {'Vendor-Id', 'Unsigned32'()}
+ | {'Product-Name', 'UTF8String'()}
+ | {'Supported-Vendor-Id', ['Unsigned32'()]}
+ | {'Auth-Application-Id', ['Unsigned32'()]}
+ | {'Vendor-Specific-Application-Id', ['Grouped'()]}
+ | {'Firmware-Revision', 'Unsigned32'()}.
+
+%% Filters for call/4
+
+-type peer_filter()
+ :: none
+ | host
+ | realm
+ | {host, any|'DiameterIdentity'()}
+ | {realm, any|'DiameterIdentity'()}
+ | {eval, evaluable()}
+ | {neg, peer_filter()}
+ | {all, [peer_filter()]}
+ | {any, [peer_filter()]}.
+
+%% Options passed to start_service/2
+
+-type service_opt()
+ :: capability()
+ | {application, [application_opt()]}.
+
+-type application_opt()
+ :: {alias, app_alias()}
+ | {dictionary, module()}
+ | {module, app_module()}
+ | {state, any()}
+ | {call_mutates_state, boolean()}
+ | {answer_errors, callback|report|discard}.
+
+-type app_module()
+ :: module()
+ | nonempty_improper_list(module(), list()). %% list with module() head
+
+%% Identifier returned by add_transport/2
+
+-type transport_ref()
+ :: reference().
+
+%% Options passed to add_transport/2
+
+-type transport_opt()
+ :: {transport_module, atom()}
+ | {transport_config, any()}
+ | {applications, [app_alias()]}
+ | {capabilities, [capability()]}
+ | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
+ | {reconnect_timer, 'Unsigned32'()}
+ | {private, any()}.
+
+%% Predicate passed to remove_transport/2
+
+-type transport_pred()
+ :: fun((reference(), connect|listen, list()) -> boolean())
+ | fun((reference(), list()) -> boolean())
+ | fun((list()) -> boolean())
+ | reference()
+ | list()
+ | {connect|listen, transport_pred()}
+ | {atom(), atom(), list()}.
+
+%% Options passed to call/4
+
+-type call_opt()
+ :: {extra, list()}
+ | {filter, peer_filter()}
+ | {timeout, 'Unsigned32'()}
+ | detach.
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
new file mode 100644
index 0000000000..b7c1491f4b
--- /dev/null
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -0,0 +1,571 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% This module implements (as a process) the state machine documented
+%% in Appendix A of RFC 3539.
+%%
+
+-module(diameter_watchdog).
+-behaviour(gen_server).
+
+%% towards diameter_service
+-export([start/2]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+%% diameter_watchdog_sup callback
+-export([start_link/1]).
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+-define(DEFAULT_TW_INIT, 30000). %% RFC 3539 ch 3.4.1
+
+-record(watchdog,
+ {%% PCB - Peer Control Block; see RFC 3539, Appendix A
+ status = initial :: initial | okay | suspect | down | reopen,
+ pending = false :: boolean(),
+ tw :: 6000..16#FFFFFFFF | {module(), atom(), list()},
+ %% {M,F,A} -> integer() >= 0
+ num_dwa = 0 :: -1 | non_neg_integer(),
+ %% number of DWAs received during reopen
+ %% end PCB
+ parent = self() :: pid(),
+ transport :: pid(),
+ tref :: reference(), %% reference for current watchdog timer
+ message_data}). %% term passed into diameter_service with message
+
+%% start/2
+
+start({_,_} = Type, T) ->
+ {ok, Pid} = diameter_watchdog_sup:start_child({Type, self(), T}),
+ Pid.
+
+start_link(T) ->
+ {ok, _} = proc_lib:start_link(?MODULE,
+ init,
+ [T],
+ infinity,
+ diameter_lib:spawn_opts(server, [])).
+
+%% ===========================================================================
+%% ===========================================================================
+
+%% init/1
+
+init(T) ->
+ proc_lib:init_ack({ok, self()}),
+ gen_server:enter_loop(?MODULE, [], i(T)).
+
+i({T, Pid, {ConnT, Opts, SvcName, #diameter_service{applications = Apps,
+ capabilities = Caps}
+ = Svc}}) ->
+ {M,S,U} = now(),
+ random:seed(M,S,U),
+ putr(restart, {T, Opts, Svc}), %% save seeing it in trace
+ putr(dwr, dwr(Caps)), %%
+ #watchdog{parent = monitor(Pid),
+ transport = monitor(diameter_peer_fsm:start(T, Opts, Svc)),
+ tw = proplists:get_value(watchdog_timer,
+ Opts,
+ ?DEFAULT_TW_INIT),
+ message_data = {ConnT, SvcName, Apps}}.
+
+%% handle_call/3
+
+handle_call(_, _, State) ->
+ {reply, nok, State}.
+
+%% handle_cast/2
+
+handle_cast(_, State) ->
+ {noreply, State}.
+
+%% handle_info/2
+
+handle_info(T, State) ->
+ case transition(T, State) of
+ ok ->
+ {noreply, State};
+ #watchdog{status = X} = S ->
+ ?LOGC(X =/= State#watchdog.status, transition, X),
+ {noreply, S};
+ stop ->
+ ?LOG(stop, T),
+ {stop, {shutdown, T}, State}
+ end.
+
+%% terminate/2
+
+terminate(_, _) ->
+ ok.
+
+%% code_change/3
+
+code_change(_, State, _) ->
+ {ok, State}.
+
+%% ===========================================================================
+%% ===========================================================================
+
+%% transition/2
+%%
+%% The state transitions documented here are extracted from RFC 3539,
+%% the commentary is ours.
+
+%% Service or watchdog is telling the watchdog of an accepting
+%% transport to die after reconnect_timer expiry or reestablished
+%% connection (in another transport process) respectively.
+transition(close, #watchdog{status = down}) ->
+ {{accept, _}, _, _} = getr(restart), %% assert
+ stop;
+transition(close, #watchdog{}) ->
+ ok;
+
+%% Service is asking for the peer to be taken down gracefully.
+transition({shutdown, Pid}, #watchdog{parent = Pid,
+ transport = undefined,
+ status = S}) ->
+ down = S, %% sanity check
+ stop;
+transition({shutdown = T, Pid}, #watchdog{parent = Pid,
+ transport = TPid}) ->
+ TPid ! {T, self()},
+ ok;
+
+%% Parent process has died,
+transition({'DOWN', _, process, Pid, _Reason},
+ #watchdog{parent = Pid}) ->
+ stop;
+
+%% Transport has accepted a connection.
+transition({accepted = T, TPid}, #watchdog{transport = TPid,
+ parent = Pid}) ->
+ Pid ! {T, self(), TPid},
+ ok;
+
+%% Transport is telling us that its impending death isn't failure.
+transition({close, TPid, _Reason}, #watchdog{transport = TPid}) ->
+ stop;
+
+%% STATE Event Actions New State
+%% ===== ------ ------- ----------
+%% INITIAL Connection up SetWatchdog() OKAY
+
+%% By construction, the watchdog timer isn't set until we move into
+%% state okay as the result of the Peer State Machine reaching the
+%% Open state.
+%%
+%% If we're an acceptor then we may be resuming a connection that went
+%% down in another acceptor process, in which case this is the
+%% transition below, from down into reopen. That is, it's not until
+%% we know the identity of the peer (ie. now) that we know that we're
+%% in state down rather than initial.
+
+transition({open, TPid, Hosts, T} = Open,
+ #watchdog{transport = TPid,
+ status = initial,
+ parent = Pid}
+ = S) ->
+ case okay(getr(restart), Hosts) of
+ okay ->
+ open(Pid, {TPid, T}),
+ set_watchdog(S#watchdog{status = okay});
+ reopen ->
+ transition(Open, S#watchdog{status = down})
+ end;
+
+%% DOWN Connection up NumDWA = 0
+%% SendWatchdog()
+%% SetWatchdog()
+%% Pending = TRUE REOPEN
+
+transition({open = P, TPid, _Hosts, T},
+ #watchdog{transport = TPid,
+ status = down}
+ = S) ->
+ %% Store the info we need to notify the parent to reopen the
+ %% connection after the requisite DWA's are received, at which
+ %% time we eraser(open).
+ putr(P, {TPid, T}),
+ set_watchdog(send_watchdog(S#watchdog{status = reopen,
+ num_dwa = 0}));
+
+%% OKAY Connection down CloseConnection()
+%% Failover()
+%% SetWatchdog() DOWN
+%% SUSPECT Connection down CloseConnection()
+%% SetWatchdog() DOWN
+%% REOPEN Connection down CloseConnection()
+%% SetWatchdog() DOWN
+
+transition({'DOWN', _, process, TPid, _},
+ #watchdog{transport = TPid,
+ status = initial}) ->
+ stop;
+
+transition({'DOWN', _, process, Pid, _},
+ #watchdog{transport = Pid}
+ = S) ->
+ failover(S),
+ close(S),
+ set_watchdog(S#watchdog{status = down,
+ pending = false,
+ transport = undefined});
+%% Any outstanding pending (or other messages from the transport) will
+%% have arrived before 'DOWN' since the message comes from the same
+%% process. Note that we could also get this message in the initial
+%% state.
+
+%% Incoming message.
+transition({recv, TPid, Name, Pkt}, #watchdog{transport = TPid} = S) ->
+ recv(Name, Pkt, S);
+
+%% Current watchdog has timed out.
+transition({timeout, TRef, tw}, #watchdog{tref = TRef} = S) ->
+ set_watchdog(timeout(S));
+
+%% Timer was canceled after message was already sent.
+transition({timeout, _, tw}, #watchdog{}) ->
+ ok;
+
+%% State query.
+transition({state, Pid}, #watchdog{status = S}) ->
+ Pid ! {self(), S},
+ ok.
+
+%% ===========================================================================
+
+monitor(Pid) ->
+ erlang:monitor(process, Pid),
+ Pid.
+
+putr(Key, Val) ->
+ put({?MODULE, Key}, Val).
+
+getr(Key) ->
+ get({?MODULE, Key}).
+
+eraser(Key) ->
+ erase({?MODULE, Key}).
+
+%% encode/1
+
+encode(Msg) ->
+ #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Msg),
+ Bin.
+
+%% okay/2
+
+okay({{accept, Ref}, _, _}, Hosts) ->
+ T = {?MODULE, connection, Ref, Hosts},
+ diameter_reg:add(T),
+ okay(diameter_reg:match(T));
+%% Register before matching so that at least one of two registering
+%% processes will match the other. (Which can't happen as long as
+%% diameter_peer_fsm guarantees at most one open connection to the same
+%% peer.)
+
+okay({{connect, _}, _, _}, _) ->
+ okay.
+
+%% The peer hasn't been connected recently ...
+okay([{_,P}]) ->
+ P = self(), %% assert
+ okay;
+
+%% ... or it has.
+okay(C) ->
+ [_|_] = [P ! close || {_,P} <- C, self() /= P],
+ reopen.
+
+%% set_watchdog/1
+
+set_watchdog(#watchdog{tw = TwInit,
+ tref = TRef}
+ = S) ->
+ cancel(TRef),
+ S#watchdog{tref = erlang:start_timer(tw(TwInit), self(), tw)}.
+
+cancel(undefined) ->
+ ok;
+cancel(TRef) ->
+ erlang:cancel_timer(TRef).
+
+tw(T)
+ when is_integer(T), T >= 6000 ->
+ T - 2000 + (random:uniform(4001) - 1); %% RFC3539 jitter of +/- 2 sec.
+tw({M,F,A}) ->
+ apply(M,F,A).
+
+%% open/2
+
+open(Pid, {_,_} = T) ->
+ Pid ! {connection_up, self(), T}.
+
+%% failover/1
+
+failover(#watchdog{status = okay,
+ parent = Pid}) ->
+ Pid ! {connection_down, self()};
+
+failover(_) ->
+ ok.
+
+%% close/1
+
+close(#watchdog{status = down}) ->
+ ok;
+
+close(#watchdog{parent = Pid}) ->
+ {{T, _}, _, _} = getr(restart),
+ T == accept andalso (Pid ! {close, self()}).
+
+%% send_watchdog/1
+
+send_watchdog(#watchdog{pending = false,
+ transport = TPid}
+ = S) ->
+ TPid ! {send, encode(getr(dwr))},
+ ?LOG(send, 'DWR'),
+ S#watchdog{pending = true}.
+
+%% recv/3
+
+recv(Name, Pkt, S) ->
+ try rcv(Name, S) of
+ #watchdog{} = NS ->
+ rcv(Name, Pkt, S),
+ NS
+ catch
+ throw: {?MODULE, throwaway, #watchdog{} = NS} ->
+ NS
+ end.
+
+%% rcv/3
+
+rcv(N, _, _)
+ when N == 'CER';
+ N == 'CEA';
+ N == 'DWR';
+ N == 'DWA';
+ N == 'DPR';
+ N == 'DPA' ->
+ false;
+
+rcv(_, Pkt, #watchdog{transport = TPid,
+ message_data = T}) ->
+ diameter_service:receive_message(TPid, Pkt, T).
+
+throwaway(S) ->
+ throw({?MODULE, throwaway, S}).
+
+%% rcv/2
+
+%% INITIAL Receive DWA Pending = FALSE
+%% Throwaway() INITIAL
+%% INITIAL Receive non-DWA Throwaway() INITIAL
+
+rcv('DWA', #watchdog{status = initial} = S) ->
+ throwaway(S#watchdog{pending = false});
+
+rcv(_, #watchdog{status = initial} = S) ->
+ throwaway(S);
+
+%% DOWN Receive DWA Pending = FALSE
+%% Throwaway() DOWN
+%% DOWN Receive non-DWA Throwaway() DOWN
+
+rcv('DWA', #watchdog{status = down} = S) ->
+ throwaway(S#watchdog{pending = false});
+
+rcv(_, #watchdog{status = down} = S) ->
+ throwaway(S);
+
+%% OKAY Receive DWA Pending = FALSE
+%% SetWatchdog() OKAY
+%% OKAY Receive non-DWA SetWatchdog() OKAY
+
+rcv('DWA', #watchdog{status = okay} = S) ->
+ set_watchdog(S#watchdog{pending = false});
+
+rcv(_, #watchdog{status = okay} = S) ->
+ set_watchdog(S);
+
+%% SUSPECT Receive DWA Pending = FALSE
+%% Failback()
+%% SetWatchdog() OKAY
+%% SUSPECT Receive non-DWA Failback()
+%% SetWatchdog() OKAY
+
+rcv('DWA', #watchdog{status = suspect} = S) ->
+ failback(S),
+ set_watchdog(S#watchdog{status = okay,
+ pending = false});
+
+rcv(_, #watchdog{status = suspect} = S) ->
+ failback(S),
+ set_watchdog(S#watchdog{status = okay});
+
+%% REOPEN Receive DWA & Pending = FALSE
+%% NumDWA == 2 NumDWA++
+%% Failback() OKAY
+
+rcv('DWA', #watchdog{status = reopen,
+ num_dwa = 2 = N,
+ parent = Pid}
+ = S) ->
+ open(Pid, eraser(open)),
+ S#watchdog{status = okay,
+ num_dwa = N+1,
+ pending = false};
+
+%% REOPEN Receive DWA & Pending = FALSE
+%% NumDWA < 2 NumDWA++ REOPEN
+
+rcv('DWA', #watchdog{status = reopen,
+ num_dwa = N}
+ = S) ->
+ S#watchdog{num_dwa = N+1,
+ pending = false};
+
+%% REOPEN Receive non-DWA Throwaway() REOPEN
+
+rcv(_, #watchdog{status = reopen} = S) ->
+ throwaway(S).
+
+%% failback/1
+
+failback(#watchdog{parent = Pid}) ->
+ Pid ! {connection_up, self()}.
+
+%% timeout/1
+%%
+%% The caller sets the watchdog on the return value.
+
+%% OKAY Timer expires & SendWatchdog()
+%% !Pending SetWatchdog()
+%% Pending = TRUE OKAY
+%% REOPEN Timer expires & SendWatchdog()
+%% !Pending SetWatchdog()
+%% Pending = TRUE REOPEN
+
+timeout(#watchdog{status = T,
+ pending = false}
+ = S)
+ when T == okay;
+ T == reopen ->
+ send_watchdog(S);
+
+%% OKAY Timer expires & Failover()
+%% Pending SetWatchdog() SUSPECT
+
+timeout(#watchdog{status = okay,
+ pending = true}
+ = S) ->
+ failover(S),
+ S#watchdog{status = suspect};
+
+%% SUSPECT Timer expires CloseConnection()
+%% SetWatchdog() DOWN
+%% REOPEN Timer expires & CloseConnection()
+%% Pending & SetWatchdog()
+%% NumDWA < 0 DOWN
+
+timeout(#watchdog{status = T,
+ pending = P,
+ num_dwa = N,
+ transport = TPid}
+ = S)
+ when T == suspect;
+ T == reopen, P, N < 0 ->
+ exit(TPid, shutdown),
+ close(S),
+ S#watchdog{status = down};
+
+%% REOPEN Timer expires & NumDWA = -1
+%% Pending & SetWatchdog()
+%% NumDWA >= 0 REOPEN
+
+timeout(#watchdog{status = reopen,
+ pending = true,
+ num_dwa = N}
+ = S)
+ when 0 =< N ->
+ S#watchdog{num_dwa = -1};
+
+%% DOWN Timer expires AttemptOpen()
+%% SetWatchdog() DOWN
+%% INITIAL Timer expires AttemptOpen()
+%% SetWatchdog() INITIAL
+
+%% RFC 3539, 3.4.1:
+%%
+%% [5] While the connection is in the closed state, the AAA client MUST
+%% NOT attempt to send further watchdog messages on the connection.
+%% However, after the connection is closed, the AAA client continues
+%% to periodically attempt to reopen the connection.
+%%
+%% The AAA client SHOULD wait for the transport layer to report
+%% connection failure before attempting again, but MAY choose to
+%% bound this wait time by the watchdog interval, Tw.
+
+%% Don't bound, restarting the peer process only when the previous
+%% process has died. We only need to handle state down since we start
+%% the first watchdog when transitioning out of initial.
+
+timeout(#watchdog{status = down} = S) ->
+ restart(S).
+
+%% restart/1
+
+restart(#watchdog{transport = undefined} = S) ->
+ restart(getr(restart), S);
+restart(S) ->
+ S.
+
+%% Only restart the transport in the connecting case. For an accepting
+%% transport, we've registered the peer connection when leaving state
+%% initial and this is used by a new accepting process to realize that
+%% it's actually in state down rather then initial when receiving
+%% notification of an open connection.
+
+restart({{connect, _} = T, Opts, Svc}, #watchdog{parent = Pid} = S) ->
+ Pid ! {reconnect, self()},
+ S#watchdog{transport = monitor(diameter_peer_fsm:start(T, Opts, Svc))};
+restart({{accept, _}, _, _}, S) ->
+ S.
+%% Don't currently use Opts/Svc in the accept case but having them in
+%% the process dictionary is helpful if the process dies unexpectedly.
+
+%% dwr/1
+
+dwr(#diameter_caps{origin_host = OH,
+ origin_realm = OR,
+ origin_state_id = OSI}) ->
+ ['DWR', {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Origin-State-Id', OSI}].
diff --git a/lib/diameter/src/base/diameter_watchdog_sup.erl b/lib/diameter/src/base/diameter_watchdog_sup.erl
new file mode 100644
index 0000000000..fc837fe4ef
--- /dev/null
+++ b/lib/diameter/src/base/diameter_watchdog_sup.erl
@@ -0,0 +1,60 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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%
+%%
+
+%%
+%% Supervisor for all watchdog processes.
+%%
+
+-module(diameter_watchdog_sup).
+
+-behaviour(supervisor).
+
+%% interface
+-export([start_link/0, %% supervisor start
+ start_child/1]). %% watchdog start
+
+-export([init/1]).
+
+-define(NAME, ?MODULE). %% supervisor name
+
+%% start_link/0
+
+start_link() ->
+ SupName = {local, ?NAME},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+%% start_child/1
+%%
+%% Start a watchdog process.
+
+start_child(T) ->
+ supervisor:start_child(?NAME, [T]).
+
+%% init/1
+
+init([]) ->
+ Mod = diameter_watchdog,
+ Flags = {simple_one_for_one, 0, 1},
+ ChildSpec = {Mod,
+ {Mod, start_link, []},
+ temporary,
+ 1000,
+ worker,
+ [Mod]},
+ {ok, {Flags, [ChildSpec]}}.
diff --git a/lib/diameter/src/compiler/.gitignore b/lib/diameter/src/compiler/.gitignore
deleted file mode 100644
index d9f072e262..0000000000
--- a/lib/diameter/src/compiler/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-
-/depend.mk
-
diff --git a/lib/diameter/src/compiler/Makefile b/lib/diameter/src/compiler/Makefile
deleted file mode 100644
index 779013bfbc..0000000000
--- a/lib/diameter/src/compiler/Makefile
+++ /dev/null
@@ -1,131 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-#
-#
-
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-else
-include $(DIAMETER_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-endif
-
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../../vsn.mk
-VSN=$(DIAMETER_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-
-RELSYSDIR = $(RELEASE_PATH)/lib/diameter-$(VSN)
-
-INCDIR = ../../include
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-
-include modules.mk
-
-ERL_FILES = \
- $(MODULES:%=%.erl)
-
-TARGET_FILES = \
- $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-
-ifeq ($(TYPE),debug)
-ERL_COMPILE_FLAGS += -Ddebug
-endif
-
-include ../app/diameter.mk
-
-ERL_COMPILE_FLAGS += \
- $(DIAMETER_ERL_COMPILE_FLAGS) \
- -I$(INCDIR)
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug:
- @${MAKE} TYPE=debug opt
-
-opt: $(TARGET_FILES)
-
-clean:
- rm -f $(TARGET_FILES)
- rm -f errs core *~
- rm -f depend.mk
-
-docs:
-
-info:
- @echo ""
- @echo "ERL_FILES = $(ERL_FILES)"
- @echo "HRL_FILES = $(HRL_FILES)"
- @echo ""
- @echo "TARGET_FILES = $(TARGET_FILES)"
- @echo ""
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/otp_release_targets.mk
-else
-include $(DIAMETER_TOP)/make/release_targets.mk
-endif
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/src/compiler
- $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/compiler
-
-release_docs_spec:
-
-force:
-
-# ----------------------------------------------------
-# Dependencies
-# ----------------------------------------------------
-
-depend: depend.mk
-
-# Generate dependencies makefile.
-depend.mk: ../app/depend.sed $(ERL_FILES) Makefile
- for f in $(MODULES); do \
- sed -f $< $$f.erl | sed "s@/@/$$f@"; \
- done \
- > $@
-
--include depend.mk
-
-.PHONY: clean debug depend docs force info opt release_docs_spec release_spec
diff --git a/lib/diameter/src/compiler/modules.mk b/lib/diameter/src/compiler/modules.mk
deleted file mode 100644
index 6fbc8fa8d4..0000000000
--- a/lib/diameter/src/compiler/modules.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-
-MODULES = \
- diameter_codegen \
- diameter_exprecs \
- diameter_spec_scan \
- diameter_spec_util \
- diameter_make
-
-HRL_FILES = \
- diameter_forms.hrl
-
diff --git a/lib/diameter/src/depend.sed b/lib/diameter/src/depend.sed
new file mode 100644
index 0000000000..8f999f646f
--- /dev/null
+++ b/lib/diameter/src/depend.sed
@@ -0,0 +1,51 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010-2011. 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%
+#
+
+#
+# Extract include dependencies from .erl files. First line of input
+# is the path to the module in question (minus the .erl extension),
+# the rest is the contents of the module.
+#
+
+1{
+ s@^[^/]*/@@
+ h
+ d
+}
+
+# Only interested in includes of diameter hrls.
+/^-include/!d
+/"diameter/!d
+
+# Extract the name of the included files in one of two forms:
+#
+# $(INCDIR)/diameter.hrl
+# diameter_internal.hrl
+
+s@^-include_lib(".*/@$(INCDIR)/@
+s@^-include("@@
+s@".*@@
+
+# Retrieve the path to our module from the hold space, morph it
+# into a beam path and turn it into a dependency like this:
+#
+# $(EBIN)/diameter_service.$(EMULATOR): $(INCDIR)/diameter.hrl
+
+G
+s@^\(.*\)\n\(.*\)@$(EBIN)/\2.$(EMULATOR): \1@
diff --git a/lib/diameter/src/gen/.gitignore b/lib/diameter/src/gen/.gitignore
new file mode 100644
index 0000000000..d490642eb7
--- /dev/null
+++ b/lib/diameter/src/gen/.gitignore
@@ -0,0 +1,2 @@
+
+/diameter_gen*rl
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
new file mode 100644
index 0000000000..ef72bab17b
--- /dev/null
+++ b/lib/diameter/src/modules.mk
@@ -0,0 +1,93 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010-2011. 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%
+
+# Runtime dictionary files in ./dict. Modules will be generated from
+# these are included in the app file.
+RT_DICTS = \
+ base_rfc3588 \
+ base_accounting \
+ relay
+
+# Handwritten (runtime) modules included in the app file.
+RT_MODULES = \
+ base/diameter \
+ base/diameter_app \
+ base/diameter_capx \
+ base/diameter_config \
+ base/diameter_codec \
+ base/diameter_dict \
+ base/diameter_lib \
+ base/diameter_misc_sup \
+ base/diameter_peer \
+ base/diameter_peer_fsm \
+ base/diameter_peer_fsm_sup \
+ base/diameter_reg \
+ base/diameter_service \
+ base/diameter_service_sup \
+ base/diameter_session \
+ base/diameter_stats \
+ base/diameter_sup \
+ base/diameter_sync \
+ base/diameter_types \
+ base/diameter_watchdog \
+ base/diameter_watchdog_sup \
+ transport/diameter_etcp \
+ transport/diameter_etcp_sup \
+ transport/diameter_tcp \
+ transport/diameter_tcp_sup \
+ transport/diameter_sctp \
+ transport/diameter_sctp_sup \
+ transport/diameter_transport_sup
+
+# Handwritten (compile time) modules not included in the app file.
+CT_MODULES = \
+ base/diameter_callback \
+ base/diameter_dbg \
+ base/diameter_info \
+ compiler/diameter_codegen \
+ compiler/diameter_exprecs \
+ compiler/diameter_spec_scan \
+ compiler/diameter_spec_util \
+ compiler/diameter_make
+
+# Released hrl files intended for public consumption.
+EXTERNAL_HRL_FILES = \
+ ../include/diameter.hrl \
+ ../include/diameter_gen.hrl
+
+# Release hrl files intended for private use.
+INTERNAL_HRL_FILES = \
+ base/diameter_internal.hrl \
+ base/diameter_types.hrl \
+ compiler/diameter_forms.hrl
+
+# Released files relative to ../bin.
+BINS = \
+ diameterc
+
+# Released files relative to ../examples.
+EXAMPLES = \
+ GNUmakefile \
+ peer.erl \
+ client.erl \
+ client_cb.erl \
+ server.erl \
+ server_cb.erl \
+ relay.erl \
+ relay_cb.erl
diff --git a/lib/diameter/src/subdirs.mk b/lib/diameter/src/subdirs.mk
deleted file mode 100644
index 3e12d935bc..0000000000
--- a/lib/diameter/src/subdirs.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-
-SUB_DIRS = compiler app transport
-SUB_DIRECTORIES = $(SUB_DIRS)
\ No newline at end of file
diff --git a/lib/diameter/src/transport/.gitignore b/lib/diameter/src/transport/.gitignore
deleted file mode 100644
index d9f072e262..0000000000
--- a/lib/diameter/src/transport/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-
-/depend.mk
-
diff --git a/lib/diameter/src/transport/Makefile b/lib/diameter/src/transport/Makefile
deleted file mode 100644
index 4b53100fd2..0000000000
--- a/lib/diameter/src/transport/Makefile
+++ /dev/null
@@ -1,141 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-#
-#
-
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-else
-include $(DIAMETER_TOP)/make/target.mk
-EBIN = ../../ebin
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-endif
-
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-
-include ../../vsn.mk
-VSN=$(DIAMETER_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-
-RELSYSDIR = $(RELEASE_PATH)/lib/diameter-$(VSN)
-
-INCDIR = ../../include
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-
-include modules.mk
-
-ERL_FILES = \
- $(MODULES:%=%.erl)
-
-TARGET_FILES = \
- $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-
-ifeq ($(TYPE),debug)
-ERL_COMPILE_FLAGS += -Ddebug
-endif
-
-include ../app/diameter.mk
-
-ERL_COMPILE_FLAGS += \
- $(DIAMETER_ERL_COMPILE_FLAGS) \
- -I$(INCDIR)
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug:
- @${MAKE} TYPE=debug opt
-
-opt: $(TARGET_FILES)
-
-clean:
- rm -f $(TARGET_FILES)
- rm -f errs core *~
- rm -f depend.mk
-
-docs:
-
-info:
- @echo ""
- @echo "ERL_FILES = $(ERL_FILES)"
- @echo "HRL_FILES = $(HRL_FILES)"
- @echo ""
- @echo "TARGET_FILES = $(TARGET_FILES)"
- @echo ""
-
-# ----------------------------------------------------
-# Special Build Targets
-# ----------------------------------------------------
-
-# Invoked from ../app to add modules to the app file.
-$(APP_TARGET): force
- M=`echo $(MODULES) | sed -e 's/^ *//' -e 's/ *$$//' -e 'y/ /,/'`; \
- echo "/%TRANSPORT_MODULES%/s//$$M/;w;q" | tr ';' '\n' \
- | ed -s $@
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/otp_release_targets.mk
-else
-include $(DIAMETER_TOP)/make/release_targets.mk
-endif
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
- $(INSTALL_DIR) $(RELSYSDIR)/src/transport
- $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/transport
-
-release_docs_spec:
-
-force:
-
-# ----------------------------------------------------
-# Dependencies
-# ----------------------------------------------------
-
-depend: depend.mk
-
-# Generate dependencies makefile.
-depend.mk: ../app/depend.sed $(ERL_FILES) Makefile
- for f in $(MODULES); do \
- sed -f $< $$f.erl | sed "s@/@/$$f@"; \
- done \
- > $@
-
--include depend.mk
-
-.PHONY: clean debug depend docs force info opt release_docs_spec release_spec
diff --git a/lib/diameter/src/transport/modules.mk b/lib/diameter/src/transport/modules.mk
deleted file mode 100644
index a0dc3cf2c0..0000000000
--- a/lib/diameter/src/transport/modules.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2010-2011. 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%
-
-MODULES = \
- diameter_etcp \
- diameter_etcp_sup \
- diameter_tcp \
- diameter_tcp_sup \
- diameter_sctp \
- diameter_sctp_sup \
- diameter_transport_sup
-
-HRL_FILES =
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 04e686c969..8df7cc85fe 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -17,15 +17,14 @@
# %CopyrightEnd%
ifeq ($(ERL_TOP),)
-TOP = $(DIAMETER_TOP)
+include $(DIAMETER_TOP)/make/target.mk
+include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
else
-TOP = $(ERL_TOP)
-DIAMETER_TOP = $(TOP)/lib/diameter
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+DIAMETER_TOP = $(ERL_TOP)/lib/diameter
endif
-include $(TOP)/make/target.mk
-include $(TOP)/make/$(TARGET)/otp.mk
-
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
@@ -63,13 +62,11 @@ RELTEST_FILES = $(TEST_SPEC_FILE) $(COVER_SPEC_FILE) $(SOURCE)
# FLAGS
# ----------------------------------------------------
-include ../src/app/diameter.mk
-
# This is only used to compile suite locally when running with a
# target like 'all' below. Target release_tests only installs source.
-ERL_COMPILE_FLAGS += $(DIAMETER_ERL_COMPILE_FLAGS) \
+ERL_COMPILE_FLAGS += +warn_unused_vars \
-DDIAMETER_CT=true \
- -I $(DIAMETER_TOP)/src/app
+ -I $(DIAMETER_TOP)/src/gen
# ----------------------------------------------------
# Targets
@@ -156,7 +153,11 @@ log:
# Release Targets
# ----------------------------------------------------
-include $(TOP)/make/otp_release_targets.mk
+ifeq ($(ERL_TOP),)
+include $(DIAMETER_TOP)/make/release_targets.mk
+else
+include $(ERL_TOP)/make/otp_release_targets.mk
+endif
release_spec:
--
cgit v1.2.3
From 28580ff54b09710358ad366975289ef9a86bb867 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Sun, 16 Oct 2011 12:10:08 +0200
Subject: Use secondary expansion for src subdirectory rules
Makes for a quieter rule with no recursion.
---
lib/diameter/src/Makefile | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index a72bf4420b..861fc0c435 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -171,11 +171,6 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
app: $(APP_TARGET) $(APPUP_TARGET)
dict: $(DICT_ERL_FILES)
-# Make the modules from a subdirectory.
-$(TARGET_DIRS:%/=%):
- $(MAKE) \
- $(patsubst $@/%,$(EBIN)/%.$(EMULATOR),$(filter $@/%,$(TARGET_MODULES)))
-
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -241,3 +236,9 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
.PHONY: app clean depend dict info release_subdir
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%)
+
+.SECONDEXPANSION:
+
+# Make the modules from a subdirectory.
+$(TARGET_DIRS:%/=%): \
+ $$(patsubst $$@/%,$(EBIN)/%.$(EMULATOR),$$(filter $$@/%,$(TARGET_MODULES)))
--
cgit v1.2.3
From 36a4d64ae7f39448ceb7c87bc4c8c0a9a9b72e1c Mon Sep 17 00:00:00 2001
From: Richard Carlsson
Date: Tue, 14 Jun 2011 15:34:11 +0200
Subject: Make epp search directory of current file first when including
another file
The expected behaviour of a C-style preprocessor (such as Erlang's epp) is
to allow a header file to include another header file in the same directory
even though that directory is not explicitly in the include path, and even
if the actual include path might reference another directory containing a
file with the same name. For example, if src/foo.erl explicitly includes
"../include/foo.hrl", then foo.hrl should be able to include "bar.hrl" in
that same directory even though "../include" might not be in the search
path, and even if another file named bar.hrl could be found using the search
path it should not override the one in the same directory as foo.hrl.
In Erlang, the most common situation is that a user of an installed
application includes a main public header file using include_lib
("appname/include/foo.hrl") and that file includes a secondary header file
"bar.hrl". However, if it does this using include_lib, it causes a
bootstrapping problem - in the build environment for the application itself,
the application is not necessarily found by name. On the other hand, if
foo.hrl uses a plain include, then bar.hrl might be found when the
application is built (if explicit paths are set in the makefils) but not
later on when a user includes the main header file of the installed
application via include_lib.
By making -include always look in the directory of the current file before
it uses the search path, this problem is remedied, and include directives
behave in a more intuitive way.
This completes a partial fix in R11 that only worked for include_lib().
---
lib/stdlib/src/epp.erl | 28 ++++++++++++++----------
lib/stdlib/test/epp_SUITE.erl | 20 +++++++++++++++--
lib/stdlib/test/epp_SUITE_data/bar.hrl | 4 ++++
lib/stdlib/test/epp_SUITE_data/include/bar.hrl | 3 +++
lib/stdlib/test/epp_SUITE_data/include/foo.hrl | 4 ++++
lib/stdlib/test/epp_SUITE_data/include_local.erl | 6 +++++
6 files changed, 51 insertions(+), 14 deletions(-)
create mode 100644 lib/stdlib/test/epp_SUITE_data/bar.hrl
create mode 100644 lib/stdlib/test/epp_SUITE_data/include/bar.hrl
create mode 100644 lib/stdlib/test/epp_SUITE_data/include/foo.hrl
create mode 100644 lib/stdlib/test/epp_SUITE_data/include_local.erl
(limited to 'lib')
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 230a4a0612..ccc14610d7 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -267,8 +267,10 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
epp_reply(Pid, {ok,self()}),
+ %% ensure directory of current source file is first in path
+ Path1 = [filename:dirname(Name) | Path],
St = #epp{file=File, location=AtLocation, delta=0, name=Name,
- name2=Name, path=Path, macs=Ms1, pre_opened = Pre},
+ name2=Name, path=Path1, macs=Ms1, pre_opened = Pre},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
@@ -360,18 +362,18 @@ wait_req_skip(St, Sis) ->
From = wait_request(St),
skip_toks(From, St, Sis).
-%% enter_file(Path, FileName, IncludeToken, From, EppState)
+%% enter_file(FileName, IncludeToken, From, EppState)
%% leave_file(From, EppState)
%% Handle entering and leaving included files. Notify caller when the
%% current file is changed. Note it is an error to exit a file if we are
%% in a conditional. These functions never return.
-enter_file(_Path, _NewName, Inc, From, St)
+enter_file(_NewName, Inc, From, St)
when length(St#epp.sstk) >= 8 ->
epp_reply(From, {error,{abs_loc(Inc),epp,{depth,"include"}}}),
wait_req_scan(St);
-enter_file(Path, NewName, Inc, From, St) ->
- case file:path_open(Path, NewName, [read]) of
+enter_file(NewName, Inc, From, St) ->
+ case file:path_open(St#epp.path, NewName, [read]) of
{ok,NewF,Pname} ->
Loc = start_loc(St#epp.location),
wait_req_scan(enter_file2(NewF, Pname, From, St, Loc));
@@ -384,13 +386,16 @@ enter_file(Path, NewName, Inc, From, St) ->
%% Set epp to use this file and "enter" it.
enter_file2(NewF, Pname, From, St, AtLocation) ->
- enter_file2(NewF, Pname, From, St, AtLocation, []).
-
-enter_file2(NewF, Pname, From, St, AtLocation, ExtraPath) ->
Loc = start_loc(AtLocation),
enter_file_reply(From, Pname, Loc, AtLocation),
Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs),
- Path = St#epp.path ++ ExtraPath,
+ %% update the head of the include path to be the directory of the new
+ %% source file, so that an included file can always include other files
+ %% relative to its current location (this is also how C does it); note
+ %% that the directory of the parent source file (the previous head of
+ %% the path) must be dropped, otherwise the path used within the current
+ %% file will depend on the order of file inclusions in the parent files
+ Path = [filename:dirname(Pname) | tl(St#epp.path)],
#epp{file=NewF,location=Loc,name=Pname,delta=0,
sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
@@ -655,7 +660,7 @@ scan_undef(_Toks, Undef, From, St) ->
scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
From, St) ->
NewName = expand_var(NewName0),
- enter_file(St#epp.path, NewName, Inc, From, St);
+ enter_file(NewName, Inc, From, St);
scan_include(_Toks, Inc, From, St) ->
epp_reply(From, {error,{abs_loc(Inc),epp,{bad,include}}}),
wait_req_scan(St).
@@ -687,9 +692,8 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
LibName = fname_join([LibDir | Rest]),
case file:open(LibName, [read]) of
{ok,NewF} ->
- ExtraPath = [filename:dirname(LibName)],
wait_req_scan(enter_file2(NewF, LibName, From,
- St, Loc, ExtraPath));
+ St, Loc));
{error,_E2} ->
epp_reply(From,
{error,{abs_loc(Inc),epp,
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 57f3f4eddb..e58022f4da 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -20,7 +20,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([rec_1/1, predef_mac/1,
+-export([rec_1/1, include_local/1, predef_mac/1,
upcase_mac_1/1, upcase_mac_2/1,
variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
@@ -63,7 +63,7 @@ end_per_testcase(_, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [rec_1, {group, upcase_mac}, predef_mac,
+ [rec_1, {group, upcase_mac}, include_local, predef_mac,
{group, variable}, otp_4870, otp_4871, otp_5362, pmod,
not_circular, skip_header, otp_6277, otp_7702, otp_8130,
overload_mac, otp_8388, otp_8470, otp_8503, otp_8562,
@@ -97,6 +97,22 @@ rec_1(Config) when is_list(Config) ->
?line check_errors(List),
ok.
+include_local(doc) ->
+ [];
+include_local(suite) ->
+ [];
+include_local(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line File = filename:join(DataDir, "include_local.erl"),
+ %% include_local.erl includes include/foo.hrl which
+ %% includes bar.hrl (also in include/) without requiring
+ %% any additional include path, and overriding any file
+ %% of the same name that the path points to
+ ?line {ok, List} = epp:parse_file(File, [DataDir], []),
+ ?line {value, {attribute,_,a,{true,true}}} =
+ lists:keysearch(a,3,List),
+ ok.
+
%%% Here is a little reimplementation of epp:parse_file, which times out
%%% after 4 seconds if the epp server doesn't respond. If we use the
%%% regular epp:parse_file, the test case will time out, and then epp
diff --git a/lib/stdlib/test/epp_SUITE_data/bar.hrl b/lib/stdlib/test/epp_SUITE_data/bar.hrl
new file mode 100644
index 0000000000..01c527d549
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/bar.hrl
@@ -0,0 +1,4 @@
+%% should not be included from include/foo.hrl even though the
+%% include path points here - include/bar.hrl overrides it
+
+-define(BAR_HRL, false).
diff --git a/lib/stdlib/test/epp_SUITE_data/include/bar.hrl b/lib/stdlib/test/epp_SUITE_data/include/bar.hrl
new file mode 100644
index 0000000000..038d3c900e
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/include/bar.hrl
@@ -0,0 +1,3 @@
+%% included from foo.hrl in same directory
+
+-define(BAR_HRL, true).
diff --git a/lib/stdlib/test/epp_SUITE_data/include/foo.hrl b/lib/stdlib/test/epp_SUITE_data/include/foo.hrl
new file mode 100644
index 0000000000..a6dfa3d18a
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/include/foo.hrl
@@ -0,0 +1,4 @@
+%% includes bar.hrl in same directory
+
+-define(FOO_HRL, true).
+-include("bar.hrl").
diff --git a/lib/stdlib/test/epp_SUITE_data/include_local.erl b/lib/stdlib/test/epp_SUITE_data/include_local.erl
new file mode 100644
index 0000000000..c8e155a064
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/include_local.erl
@@ -0,0 +1,6 @@
+
+-module(include_local).
+
+-include("include/foo.hrl").
+
+-a({?FOO_HRL, ?BAR_HRL}).
--
cgit v1.2.3
From f0db3eff30b298e765a9af78a86f4865cad21da8 Mon Sep 17 00:00:00 2001
From: Paul Guyot
Date: Tue, 6 Sep 2011 14:38:39 +0200
Subject: [crypto] Add DES and Triple DES cipher feedback (CFB) mode functions
---
lib/crypto/c_src/crypto.c | 48 ++++++++++++++++++
lib/crypto/doc/src/crypto.xml | 79 ++++++++++++++++++++++++++++-
lib/crypto/src/crypto.erl | 51 +++++++++++++++++++
lib/crypto/test/crypto_SUITE.erl | 104 +++++++++++++++++++++++++++++++++++++--
4 files changed, 278 insertions(+), 4 deletions(-)
(limited to 'lib')
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index c781ccb302..10fe333d18 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -134,8 +134,10 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM des_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM des_ede3_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -210,8 +212,10 @@ static ErlNifFunc nif_funcs[] = {
{"hmac_final", 1, hmac_final},
{"hmac_final_n", 2, hmac_final},
{"des_cbc_crypt", 4, des_cbc_crypt},
+ {"des_cfb_crypt", 4, des_cfb_crypt},
{"des_ecb_crypt", 3, des_ecb_crypt},
{"des_ede3_cbc_crypt", 6, des_ede3_cbc_crypt},
+ {"des_ede3_cfb_crypt", 6, des_ede3_cfb_crypt},
{"aes_cfb_128_crypt", 4, aes_cfb_128_crypt},
{"aes_ctr_encrypt", 3, aes_ctr_encrypt},
{"aes_ctr_decrypt", 3, aes_ctr_encrypt},
@@ -693,6 +697,25 @@ static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
return ret;
}
+static ERL_NIF_TERM des_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Ivec, Text, IsEncrypt) */
+ ErlNifBinary key, ivec, text;
+ DES_key_schedule schedule;
+ DES_cblock ivec_clone; /* writable copy */
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 8
+ || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 8
+ || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(&ivec_clone, ivec.data, 8);
+ DES_set_key((const_DES_cblock*)key.data, &schedule);
+ DES_cfb_encrypt(text.data, enif_make_new_binary(env, text.size, &ret),
+ 8, text.size, &schedule, &ivec_clone, (argv[3] == atom_true));
+ return ret;
+}
+
static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, Text/Cipher, IsEncrypt) */
ErlNifBinary key, text;
@@ -735,6 +758,31 @@ static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T
return ret;
}
+static ERL_NIF_TERM des_ede3_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key1, Key2, Key3, IVec, Text/Cipher, IsEncrypt) */
+ ErlNifBinary key1, key2, key3, ivec, text;
+ DES_key_schedule schedule1, schedule2, schedule3;
+ DES_cblock ivec_clone; /* writable copy */
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key1) || key1.size != 8
+ || !enif_inspect_iolist_as_binary(env, argv[1], &key2) || key2.size != 8
+ || !enif_inspect_iolist_as_binary(env, argv[2], &key3) || key3.size != 8
+ || !enif_inspect_binary(env, argv[3], &ivec) || ivec.size != 8
+ || !enif_inspect_iolist_as_binary(env, argv[4], &text)) {
+ return enif_make_badarg(env);
+ }
+
+ memcpy(&ivec_clone, ivec.data, 8);
+ DES_set_key((const_DES_cblock*)key1.data, &schedule1);
+ DES_set_key((const_DES_cblock*)key2.data, &schedule2);
+ DES_set_key((const_DES_cblock*)key3.data, &schedule3);
+ DES_ede3_cfb_encrypt(text.data, enif_make_new_binary(env,text.size,&ret),
+ 8, text.size, &schedule1, &schedule2, &schedule3,
+ &ivec_clone, (argv[5] == atom_true));
+ return ret;
+}
+
static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, IVec, Data, IsEncrypt) */
ErlNifBinary key, ivec, text;
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 179ba4498c..110ab5b746 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -403,6 +403,51 @@ Mpint() = >]]>
data from the previous iteration step.
+
+ des_cfb_encrypt(Key, IVec, Text) -> Cipher
+ Encrypt Textaccording to DES in CFB mode
+
+ Key = Text = iolist() | binary()
+ IVec = Cipher = binary()
+
+
+ Encrypts Text according to DES in 8-bit CFB
+ mode. Key is the DES key, and IVec is an
+ arbitrary initializing vector. The lengths of Key and
+ IVec must be 64 bits (8 bytes).
+
+
+
+ des_cfb_decrypt(Key, IVec, Cipher) -> Text
+ Decrypt Cipheraccording to DES in CFB mode
+
+ Key = Cipher = iolist() | binary()
+ IVec = Text = binary()
+
+
+ Decrypts Cipher according to DES in 8-bit CFB mode.
+ Key is the DES key, and IVec is an arbitrary
+ initializing vector. Key and IVec must have
+ the same values as those used when encrypting. The lengths of
+ Key and IVec must be 64 bits (8 bytes).
+
+
+
+ des_cfb_ivec(IVec, Data) -> NextIVec
+ Get IVec to be used in next iteration of
+ des_cfb_[ecrypt|decrypt]
+
+ IVec = iolist() | binary()
+ Data = iolist() | binary()
+ NextIVec = binary()
+
+
+ Returns the IVec to be used in a next iteration of
+ des_cfb_[encrypt|decrypt]. IVec is the vector
+ used in the previous iteration step. Data is the encrypted
+ data from the previous iteration step.
+
+
des3_cbc_encrypt(Key1, Key2, Key3, IVec, Text) -> Cipher
Encrypt Textaccording to DES3 in CBC mode
@@ -421,7 +466,7 @@ Mpint() = >]]>
des3_cbc_decrypt(Key1, Key2, Key3, IVec, Cipher) -> Text
- Decrypt Cipheraccording to DES in CBC mode
+ Decrypt Cipheraccording to DES3 in CBC mode
Key1 = Key2 = Key3 = Cipher = iolist() | binary()
IVec = Text = binary()
@@ -437,6 +482,38 @@ Mpint() = >]]>
Key3, and IVec must be 64 bits (8 bytes).
+
+ des3_cfb_encrypt(Key1, Key2, Key3, IVec, Text) -> Cipher
+ Encrypt Textaccording to DES3 in CFB mode
+
+ Key1 =Key2 = Key3 Text = iolist() | binary()
+ IVec = Cipher = binary()
+
+
+ Encrypts Text according to DES3 in 8-bit CFB
+ mode. Key1, Key2, Key3, are the DES
+ keys, and IVec is an arbitrary initializing
+ vector. The lengths of each of Key1, Key2,
+ Key3 and IVec must be 64 bits (8 bytes).
+
+
+
+ des3_cfb_decrypt(Key1, Key2, Key3, IVec, Cipher) -> Text
+ Decrypt Cipheraccording to DES3 in CFB mode
+
+ Key1 = Key2 = Key3 = Cipher = iolist() | binary()
+ IVec = Text = binary()
+
+
+ Decrypts Cipher according to DES3 in 8-bit CFB mode.
+ Key1, Key2, Key3 are the DES key, and
+ IVec is an arbitrary initializing vector.
+ Key1, Key2, Key3 and IVec must
+ and IVec must have the same values as those used when
+ encrypting. The lengths of Key1, Key2,
+ Key3, and IVec must be 64 bits (8 bytes).
+
+
des_ecb_encrypt(Key, Text) -> Cipher
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index c35dfcebab..862f3df0f8 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -31,7 +31,9 @@
-export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]).
-export([des_ecb_encrypt/2, des_ecb_decrypt/2]).
+-export([des_cfb_encrypt/3, des_cfb_decrypt/3, des_cfb_ivec/2]).
-export([des3_cbc_encrypt/5, des3_cbc_decrypt/5]).
+-export([des3_cfb_encrypt/5, des3_cfb_decrypt/5]).
-export([blowfish_ecb_encrypt/2, blowfish_ecb_decrypt/2]).
-export([blowfish_cbc_encrypt/3, blowfish_cbc_decrypt/3]).
-export([blowfish_cfb64_encrypt/3, blowfish_cfb64_decrypt/3]).
@@ -68,8 +70,10 @@
sha_mac, sha_mac_96,
sha_mac_init, sha_mac_update, sha_mac_final,
des_cbc_encrypt, des_cbc_decrypt,
+ des_cfb_encrypt, des_cfb_decrypt,
des_ecb_encrypt, des_ecb_decrypt,
des_ede3_cbc_encrypt, des_ede3_cbc_decrypt,
+ des_ede3_cfb_encrypt, des_ede3_cfb_decrypt,
aes_cfb_128_encrypt, aes_cfb_128_decrypt,
rand_bytes,
strong_rand_bytes,
@@ -293,6 +297,33 @@ des_cbc_ivec(Data) when is_binary(Data) ->
des_cbc_ivec(Data) when is_list(Data) ->
des_cbc_ivec(list_to_binary(Data)).
+%%
+%% DES - in 8-bits cipher feedback mode (CFB)
+%%
+-spec des_cfb_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec des_cfb_decrypt(iodata(), binary(), iodata()) -> binary().
+
+des_cfb_encrypt(Key, IVec, Data) ->
+ des_cfb_crypt(Key, IVec, Data, true).
+
+des_cfb_decrypt(Key, IVec, Data) ->
+ des_cfb_crypt(Key, IVec, Data, false).
+
+des_cfb_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
+
+%%
+%% dec_cfb_ivec(IVec, Data) -> binary()
+%%
+%% Returns the IVec to be used in the next iteration of
+%% des_cfb_[encrypt|decrypt].
+%%
+-spec des_cfb_ivec(iodata(), iodata()) -> binary().
+
+des_cfb_ivec(IVec, Data) ->
+ IVecAndData = list_to_binary([IVec, Data]),
+ {_, NewIVec} = split_binary(IVecAndData, byte_size(IVecAndData) - 8),
+ NewIVec.
+
%%
%% DES - in electronic codebook mode (ECB)
%%
@@ -325,6 +356,26 @@ des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) ->
des_ede3_cbc_crypt(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
+%%
+%% DES3 - in 8-bits cipher feedback mode (CFB)
+%%
+-spec des3_cfb_encrypt(iodata(), iodata(), iodata(), binary(), iodata()) ->
+ binary().
+-spec des3_cfb_decrypt(iodata(), iodata(), iodata(), binary(), iodata()) ->
+ binary().
+
+des3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) ->
+ des_ede3_cfb_encrypt(Key1, Key2, Key3, IVec, Data).
+des_ede3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) ->
+ des_ede3_cfb_crypt(Key1, Key2, Key3, IVec, Data, true).
+
+des3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) ->
+ des_ede3_cfb_decrypt(Key1, Key2, Key3, IVec, Data).
+des_ede3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) ->
+ des_ede3_cfb_crypt(Key1, Key2, Key3, IVec, Data, false).
+
+des_ede3_cfb_crypt(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
+
%%
%% Blowfish
%%
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 283aadb6ea..b86575ad21 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -44,7 +44,11 @@
md5_mac_io/1,
des_cbc/1,
des_cbc_iter/1,
+ des_cfb/1,
+ des_cfb_iter/1,
des_ecb/1,
+ des3_cbc/1,
+ des3_cfb/1,
aes_cfb/1,
aes_cbc/1,
aes_cbc_iter/1,
@@ -75,8 +79,8 @@ all() ->
md5_mac_io, sha, sha_update,
hmac_update_sha, hmac_update_sha_n, hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5,
%% sha256, sha256_update, sha512,sha512_update,
- des_cbc, aes_cfb, aes_cbc,
- aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb,
+ des_cbc, des_cfb, des3_cbc, des3_cfb, aes_cfb, aes_cbc,
+ aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_cfb_iter, des_ecb,
rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test,
@@ -291,7 +295,7 @@ sha(Config) when is_list(Config) ->
hexstr2bin("84983E441C3BD26EBAAE4AA1F95129E5E54670F1")).
-%%
+%%
hmac_update_sha_n(doc) ->
["Request a larger-than-allowed SHA1 HMAC using hmac_init, hmac_update, and hmac_final_n. "
"Expected values for examples are generated using crypto:sha_mac." ];
@@ -544,6 +548,40 @@ des_cbc_iter(Config) when is_list(Config) ->
?line m(Cipher, hexstr2bin("e5c7cdde872bf27c43e934008c389c"
"0f683788499a7c05f6")).
+%%
+%%
+des_cfb(doc) ->
+ "Encrypt and decrypt according to CFB DES. and check the result. "
+ "Example is from FIPS-81.";
+des_cfb(suite) ->
+ [];
+des_cfb(Config) when is_list(Config) ->
+ ?line Key = hexstr2bin("0123456789abcdef"),
+ ?line IVec = hexstr2bin("1234567890abcdef"),
+ ?line Plain = "Now is the",
+ ?line Cipher = crypto:des_cfb_encrypt(Key, IVec, Plain),
+ ?line m(Cipher, hexstr2bin("f31fda07011462ee187f")),
+ ?line m(list_to_binary(Plain),
+ crypto:des_cfb_decrypt(Key, IVec, Cipher)).
+
+%%
+%%
+des_cfb_iter(doc) ->
+ "Encrypt and decrypt according to CFB DES in two steps, and "
+ "check the result. Example is from FIPS-81.";
+des_cfb_iter(suite) ->
+ [];
+des_cfb_iter(Config) when is_list(Config) ->
+ ?line Key = hexstr2bin("0123456789abcdef"),
+ ?line IVec = hexstr2bin("1234567890abcdef"),
+ ?line Plain1 = "Now i",
+ ?line Plain2 = "s the",
+ ?line Cipher1 = crypto:des_cfb_encrypt(Key, IVec, Plain1),
+ ?line IVec2 = crypto:des_cfb_ivec(IVec, Cipher1),
+ ?line Cipher2 = crypto:des_cfb_encrypt(Key, IVec2, Plain2),
+ ?line Cipher = list_to_binary([Cipher1, Cipher2]),
+ ?line m(Cipher, hexstr2bin("f31fda07011462ee187f")).
+
%%
%%
des_ecb(doc) ->
@@ -566,6 +604,66 @@ des_ecb(Config) when is_list(Config) ->
?line Cipher6 = crypto:des_ecb_decrypt(Key, hexstr2bin("893d51ec4b563b53")),
?line m(Cipher6, <<"for all ">>).
+%%
+%%
+des3_cbc(doc) ->
+ "Encrypt and decrypt according to CBC 3DES, and check the result.";
+des3_cbc(suite) ->
+ [];
+des3_cbc(Config) when is_list(Config) ->
+ ?line Key1 = hexstr2bin("0123456789abcdef"),
+ ?line Key2 = hexstr2bin("fedcba9876543210"),
+ ?line Key3 = hexstr2bin("0f2d4b6987a5c3e1"),
+ ?line IVec = hexstr2bin("1234567890abcdef"),
+ ?line Plain = "Now is the time for all ",
+ ?line Cipher = crypto:des3_cbc_encrypt(Key1, Key2, Key3, IVec, Plain),
+ ?line m(Cipher, hexstr2bin("8a2667ee5577267cd9b1af2c5a0480"
+ "0bac1ae66970fb2b89")),
+ ?line m(list_to_binary(Plain),
+ crypto:des3_cbc_decrypt(Key1, Key2, Key3, IVec, Cipher)),
+ ?line Plain2 = "7654321 Now is the time for " ++ [0, 0, 0, 0],
+ ?line Cipher2 = crypto:des3_cbc_encrypt(Key1, Key2, Key3, IVec, Plain2),
+ ?line m(Cipher2, hexstr2bin("eb33ec6ede2c8e90f6877e77b95d5"
+ "4c83cee22907f7f0041ca1b7abe202bfafe")),
+ ?line m(list_to_binary(Plain2),
+ crypto:des3_cbc_decrypt(Key1, Key2, Key3, IVec, Cipher2)),
+
+ ?line Key = hexstr2bin("0123456789abcdef"),
+ ?line DESCipher = crypto:des3_cbc_encrypt(Key, Key, Key, IVec, Plain),
+ ?line m(DESCipher, hexstr2bin("e5c7cdde872bf27c43e934008c389c"
+ "0f683788499a7c05f6")),
+ ?line m(list_to_binary(Plain),
+ crypto:des3_cbc_decrypt(Key, Key, Key, IVec, DESCipher)),
+ ?line DESCipher2 = crypto:des3_cbc_encrypt(Key, Key, Key, IVec, Plain2),
+ ?line m(DESCipher2, hexstr2bin("b9916b8ee4c3da64b4f44e3cbefb9"
+ "9484521388fa59ae67d58d2e77e86062733")),
+ ?line m(list_to_binary(Plain2),
+ crypto:des3_cbc_decrypt(Key, Key, Key, IVec, DESCipher2)).
+
+%%
+%%
+des3_cfb(doc) ->
+ "Encrypt and decrypt according to CFB 3DES, and check the result.";
+des3_cfb(suite) ->
+ [];
+des3_cfb(Config) when is_list(Config) ->
+ ?line Key1 = hexstr2bin("0123456789abcdef"),
+ ?line Key2 = hexstr2bin("fedcba9876543210"),
+ ?line Key3 = hexstr2bin("0f2d4b6987a5c3e1"),
+ ?line IVec = hexstr2bin("1234567890abcdef"),
+ ?line Plain = "Now is the time for all ",
+ ?line Cipher = crypto:des3_cfb_encrypt(Key1, Key2, Key3, IVec, Plain),
+ ?line m(Cipher, hexstr2bin("fc0ba7a20646ba53cc8bff263f0937"
+ "1deab42a00666db02c")),
+ ?line m(list_to_binary(Plain),
+ crypto:des3_cfb_decrypt(Key1, Key2, Key3, IVec, Cipher)),
+ ?line Plain2 = "7654321 Now is the time for " ++ [0, 0, 0, 0],
+ ?line Cipher2 = crypto:des3_cfb_encrypt(Key1, Key2, Key3, IVec, Plain2),
+ ?line m(Cipher2, hexstr2bin("8582c59ac01897422632c0accb66c"
+ "e413f5efab838fce7e41e2ba67705bad5bc")),
+ ?line m(list_to_binary(Plain2),
+ crypto:des3_cfb_decrypt(Key1, Key2, Key3, IVec, Cipher2)).
+
%%
%%
aes_cfb(doc) ->
--
cgit v1.2.3
From f8f2b8fbf9c19e582b21bb701d38f8fb769821e8 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 6 Sep 2011 14:41:26 +0200
Subject: [crypto] Remove swedish characters from test code
---
lib/crypto/test/crypto_SUITE.erl | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
(limited to 'lib')
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index b86575ad21..21df8b5526 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -1323,8 +1323,8 @@ rc4_test(doc) ->
rc4_test(suite) ->
[];
rc4_test(Config) when is_list(Config) ->
- CT1 = <<"hej på dig">>,
- R1 = <<71,112,14,44,140,33,212,144,155,47>>,
+ CT1 = <<"Yo baby yo">>,
+ R1 = <<118,122,68,110,157,166,141,212,139,39>>,
K = "apaapa",
R1 = crypto:rc4_encrypt(K, CT1),
CT1 = crypto:rc4_encrypt(K, R1),
@@ -1338,14 +1338,14 @@ rc4_stream_test(doc) ->
rc4_stream_test(suite) ->
[];
rc4_stream_test(Config) when is_list(Config) ->
- CT1 = <<"hej">>,
- CT2 = <<" på dig">>,
+ CT1 = <<"Yo ">>,
+ CT2 = <<"baby yo">>,
K = "apaapa",
State0 = crypto:rc4_set_key(K),
{State1, R1} = crypto:rc4_encrypt_with_state(State0, CT1),
{_State2, R2} = crypto:rc4_encrypt_with_state(State1, CT2),
R = list_to_binary([R1, R2]),
- <<71,112,14,44,140,33,212,144,155,47>> = R,
+ <<118,122,68,110,157,166,141,212,139,39>> = R,
ok.
blowfish_cfb64(doc) -> ["Test Blowfish encrypt/decrypt."];
--
cgit v1.2.3
From 0f5d56109d7b51298a2c90dae1b11ba5819471b2 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Sun, 16 Oct 2011 12:27:23 +0200
Subject: Simpler release targets for src subdirectories
---
lib/diameter/src/Makefile | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 861fc0c435..f6ceb7217f 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -194,15 +194,12 @@ release_spec: opt
$(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(DICT_HRL_FILES) \
$(RELSYSDIR)/include
$(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
- for dir in $(TARGET_DIRS); do \
- $(MAKE) release_subdir SRCDIR=$$dir; \
- done
+ $(MAKE) $(TARGET_DIRS:%/=release_src_%)
-release_subdir:
- [ -d "$(SRCDIR)" ]
- $(INSTALL_DATA) $(filter $(SRCDIR)%,$(TARGET_MODULES:%=%.erl) \
- $(INTERNAL_HRL_FILES)) \
- $(RELSYSDIR)/src/$(SRCDIR)
+$(TARGET_DIRS:%/=release_src_%): release_src_%:
+ $(INSTALL_DATA) $(filter $*/%,$(TARGET_MODULES:%=%.erl) \
+ $(INTERNAL_HRL_FILES)) \
+ $(RELSYSDIR)/src/$*
release_docs_spec:
@@ -235,7 +232,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
.PRECIOUS: $(DICT_ERL_FILES) $(DICT_HRL_FILES)
.PHONY: app clean depend dict info release_subdir
.PHONY: debug opt release_docs_spec release_spec
-.PHONY: $(TARGET_DIRS:%/=%)
+.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
.SECONDEXPANSION:
--
cgit v1.2.3
From af9759875f237353339fad921ad82a712f889dcc Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Mon, 17 Oct 2011 18:36:51 +0200
Subject: Need absolute -pa for bootstrap build
Otherwise include_lib will fail.
---
lib/diameter/src/Makefile | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'lib')
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index f6ceb7217f..6b3a5e340e 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -106,9 +106,11 @@ ERL_COMPILE_FLAGS += \
+'{parse_transform,sys_pre_attributes}' \
+'{attribute,insert,app_vsn,$(APP_VSN)}' \
+warn_unused_vars \
- -pa $(EBIN) \
+ -pa $(realpath $(EBIN)) \
-I $(INCDIR) \
-I gen
+# -pa is so that we can include_lib from our include directory. The
+# path has to be absolute to contain the application name.
# ----------------------------------------------------
# Targets
--
cgit v1.2.3
From 955bbc378a146611929551cdabcfe63264a570ac Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Sun, 16 Oct 2011 15:09:35 +0200
Subject: Minor tweaks and cleanup
Tweak some comments and variable names, move things around a bit
(default src target is now opt, not debug), only clean what's built,
use +warn_export_vars.
---
lib/diameter/src/Makefile | 101 ++++++++++++++++++++-----------------------
lib/diameter/src/modules.mk | 14 +++---
lib/diameter/test/Makefile | 77 ++++++++++++++-------------------
lib/diameter/test/modules.mk | 2 +-
4 files changed, 88 insertions(+), 106 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 6b3a5e340e..ea1e43875e 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -46,15 +46,14 @@ INCDIR = ../include
VPATH = .:base:compiler:transport:gen
# ----------------------------------------------------
-# Target Specs
+# Target specs
# ----------------------------------------------------
include modules.mk
-DICT_FILES = $(RT_DICTS:%=dict/%.dia)
-DICT_MODULES = $(RT_DICTS:%=gen/diameter_gen_%)
-DICT_ERL_FILES = $(DICT_MODULES:%=%.erl)
-DICT_HRL_FILES = $(DICT_MODULES:%=%.hrl)
+DICT_MODULES = $(DICTS:%=gen/diameter_gen_%)
+DICT_ERLS = $(DICT_MODULES:%=%.erl)
+DICT_HRLS = $(DICT_MODULES:%=%.hrl)
# Modules to build before compiling dictionaries.
COMPILER_MODULES = $(filter compiler/%, $(CT_MODULES))
@@ -91,11 +90,8 @@ APPUP_FILE = diameter.appup
APPUP_SRC = $(APPUP_FILE).src
APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
-EXAMPLE_FILES = $(EXAMPLES:%=../examples/%)
-BIN_FILES = $(BINS:%=../bin/%)
-
# ----------------------------------------------------
-# FLAGS
+# Flags
# ----------------------------------------------------
ifeq ($(TYPE),debug)
@@ -105,27 +101,39 @@ endif
ERL_COMPILE_FLAGS += \
+'{parse_transform,sys_pre_attributes}' \
+'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ +warn_export_vars \
+warn_unused_vars \
-pa $(realpath $(EBIN)) \
-I $(INCDIR) \
-I gen
-# -pa is so that we can include_lib from our include directory. The
-# path has to be absolute to contain the application name.
+# -pa is to be able to include_lib from the include directory: the
+# path must contain the application name.
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
+# erl/hrl from dictionary file.
+gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
+ ../bin/diameterc -o gen -i $(EBIN) $<
+
+opt: $(TARGET_FILES)
+
debug:
@$(MAKE) TYPE=debug opt
-opt: $(TARGET_FILES)
+# Generate the app file.
+$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
+ M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ sed -e 's;%VSN%;$(VSN);' \
+ -e "s;%MODULES%;$$M;" \
+ $< > $@
-clean:
- rm -f $(TARGET_FILES) $(DICT_ERL_FILES) $(DICT_HRL_FILES)
- rm -f $(APP_TARGET) $(APPUP_TARGET)
- rm -f errs core *~ gen/diameter_gen_*.forms gen/diameter_gen_*.spec
- rm -f depend.mk
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+app: $(APP_TARGET) $(APPUP_TARGET)
+dict: $(DICT_ERLS)
docs:
@@ -133,7 +141,7 @@ list = echo $(1):; echo $($(1)) | tr ' ' '\n' | sort | sed 's@^@ @'
info:
@echo ========================================
- @$(call list,RT_DICTS)
+ @$(call list,DICTS)
@echo
@$(call list,RT_MODULES)
@echo
@@ -143,38 +151,21 @@ info:
@echo
@$(call list,TARGET_DIRS)
@echo
- @$(call list,EXTERNAL_HRL_FILES)
+ @$(call list,EXTERNAL_HRLS)
@echo
- @$(call list,INTERNAL_HRL_FILES)
+ @$(call list,INTERNAL_HRLS)
@echo
- @$(call list,EXAMPLE_FILES)
+ @$(call list,EXAMPLES)
@echo
- @$(call list,BIN_FILES)
+ @$(call list,BINS)
@echo ========================================
-# ----------------------------------------------------
-# Special Build Targets
-# ----------------------------------------------------
-
-# erl/hrl from dictionary file.
-gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
- ../bin/diameterc -o gen -i $(EBIN) $<
-
-# Generate the app file.
-$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
- M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
- sed -e 's;%VSN%;$(VSN);' \
- -e "s;%MODULES%;$$M;" \
- $< > $@
-
-$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-app: $(APP_TARGET) $(APPUP_TARGET)
-dict: $(DICT_ERL_FILES)
+clean:
+ rm -f $(TARGET_FILES) $(DICT_ERLS) $(DICT_HRLS)
+ rm -f depend.mk
# ----------------------------------------------------
-# Release Target
+# Release targets
# ----------------------------------------------------
ifeq ($(ERL_TOP),)
@@ -186,21 +177,21 @@ endif
release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/bin \
$(RELSYSDIR)/ebin \
- $(RELSYSDIR)/src/dict \
- $(TARGET_DIRS:%=$(RELSYSDIR)/src/%) \
+ $(RELSYSDIR)/examples \
$(RELSYSDIR)/include \
- $(RELSYSDIR)/examples
- $(INSTALL_SCRIPT) $(BIN_FILES) $(RELSYSDIR)/bin
+ $(RELSYSDIR)/src/dict \
+ $(TARGET_DIRS:%/=$(RELSYSDIR)/src/%)
+ $(INSTALL_SCRIPT) $(BINS:%=../bin/%) $(RELSYSDIR)/bin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(DICT_FILES) $(RELSYSDIR)/src/dict
- $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(DICT_HRL_FILES) \
+ $(INSTALL_DATA) $(EXAMPLES:%=../examples/%) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(EXTERNAL_HRLS:%=../include/%) $(DICT_HRLS) \
$(RELSYSDIR)/include
- $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(DICTS:%=dict/%.dia) $(RELSYSDIR)/src/dict
$(MAKE) $(TARGET_DIRS:%/=release_src_%)
$(TARGET_DIRS:%/=release_src_%): release_src_%:
$(INSTALL_DATA) $(filter $*/%,$(TARGET_MODULES:%=%.erl) \
- $(INTERNAL_HRL_FILES)) \
+ $(INTERNAL_HRLS)) \
$(RELSYSDIR)/src/$*
release_docs_spec:
@@ -231,13 +222,17 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
-include depend.mk
-.PRECIOUS: $(DICT_ERL_FILES) $(DICT_HRL_FILES)
+.PRECIOUS: $(DICT_ERLS) $(DICT_HRLS)
.PHONY: app clean depend dict info release_subdir
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
+# ----------------------------------------------------
+# Targets using secondary expansion
+# ----------------------------------------------------
+
.SECONDEXPANSION:
-# Make the modules from a subdirectory.
+# Make beams from a subdirectory.
$(TARGET_DIRS:%/=%): \
$$(patsubst $$@/%,$(EBIN)/%.$(EMULATOR),$$(filter $$@/%,$(TARGET_MODULES)))
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index ef72bab17b..c7cbe598af 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -19,7 +19,7 @@
# Runtime dictionary files in ./dict. Modules will be generated from
# these are included in the app file.
-RT_DICTS = \
+DICTS = \
base_rfc3588 \
base_accounting \
relay
@@ -66,13 +66,13 @@ CT_MODULES = \
compiler/diameter_spec_util \
compiler/diameter_make
-# Released hrl files intended for public consumption.
-EXTERNAL_HRL_FILES = \
- ../include/diameter.hrl \
- ../include/diameter_gen.hrl
+# Released hrl files in ../include intended for public consumption.
+EXTERNAL_HRLS = \
+ diameter.hrl \
+ diameter_gen.hrl
-# Release hrl files intended for private use.
-INTERNAL_HRL_FILES = \
+# Released hrl files intended for private use.
+INTERNAL_HRLS = \
base/diameter_internal.hrl \
base/diameter_types.hrl \
compiler/diameter_forms.hrl
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 8df7cc85fe..69bcabbfbb 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -22,7 +22,6 @@ include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
else
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-DIAMETER_TOP = $(ERL_TOP)/lib/diameter
endif
# ----------------------------------------------------
@@ -45,18 +44,11 @@ RELSYSDIR = $(RELEASE_PATH)/diameter_test
include modules.mk
-EBIN = .
-
-HRL_FILES = $(INTERNAL_HRL_FILES)
-ERL_FILES = $(MODULES:%=%.erl)
-
-SOURCE = $(HRL_FILES) $(ERL_FILES)
+ERL_FILES = $(MODULES:%=%.erl)
TARGET_FILES = $(MODULES:%=%.$(EMULATOR))
SUITE_MODULES = $(filter diameter_%_SUITE, $(MODULES))
-SUITES = $(SUITE_MODULES:diameter_%_SUITE=%)
-
-RELTEST_FILES = $(TEST_SPEC_FILE) $(COVER_SPEC_FILE) $(SOURCE)
+SUITES = $(SUITE_MODULES:diameter_%_SUITE=%)
# ----------------------------------------------------
# FLAGS
@@ -64,9 +56,10 @@ RELTEST_FILES = $(TEST_SPEC_FILE) $(COVER_SPEC_FILE) $(SOURCE)
# This is only used to compile suite locally when running with a
# target like 'all' below. Target release_tests only installs source.
-ERL_COMPILE_FLAGS += +warn_unused_vars \
+ERL_COMPILE_FLAGS += +warn_export_vars \
+ +warn_unused_vars \
-DDIAMETER_CT=true \
- -I $(DIAMETER_TOP)/src/gen
+ -I ../src/gen
# ----------------------------------------------------
# Targets
@@ -82,62 +75,52 @@ clean:
realclean: clean
rm -rf log
- rm -f errs core *~
-
-.PHONY: all tests debug opt clean realclean
docs:
+list = echo $(1):; echo $($(1)) | tr ' ' '\n' | sort | sed 's@^@ @'
+
info:
- @echo "TARGET_FILES = $(TARGET_FILES)"
+ @echo ========================================
+ @$(call list,MODULES)
@echo
- @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)"
- @echo "ERL = $(ERL)"
- @echo "ERLC = $(ERLC)"
- @echo
- @echo "HRL_FILES = $(HRL_FILES)"
- @echo "ERL_FILES = $(ERL_FILES)"
- @echo "TARGET_FILES = $(TARGET_FILES)"
- @echo
- @echo "SUITE_MODULES = $(SUITE_MODULES)"
- @echo "SUITES = $(SUITES)"
+ @$(call list,HRL_FILES)
@echo
+ @$(call list,SUITES)
+ @echo ========================================
help:
+ @echo ========================================
+ @echo "Useful targets:"
@echo
- @echo "Targets:"
- @echo
- @echo " all"
- @echo " Run all test suites."
+ @echo " all:"
+ @echo " Compile and run all test suites."
@echo
- @echo " $(SUITES)"
- @echo " Run a specific test suite."
+ @echo " $(SUITES):"
+ @echo " Compile and run a specific test suite."
@echo
- @echo " tests"
+ @echo " beam:"
@echo " Compile all test-code."
@echo
- @echo " clean | realclean"
+ @echo " clean | realclean:"
@echo " Remove generated files."
@echo
- @echo " info"
- @echo " Prints various environment variables."
- @echo " May be useful when debugging this Makefile."
- @echo
- @echo " help"
- @echo " Print this info."
- @echo
+ @echo " info:"
+ @echo " Echo some interesting variables."
+ @echo ========================================
-.PHONY: docs info help
+.PHONY: all beam clean debug docs help info opt realclean tests
# ----------------------------------------------------
# Special Targets
# ----------------------------------------------------
# Exit with a non-zero status if the output looks to indicate failure.
-# diameter_ct:run/1 itself can't tell (it seems).
+# diameter_ct:run/1 itself can't tell (it seems). The absolute -pa is
+# because ct will change directories.
$(SUITES): log tests
$(ERL) -noshell \
- -pa $(DIAMETER_TOP)/ebin \
+ -pa $(realpath ../ebin) \
-sname diameter_test_$@ \
-s diameter_ct run diameter_$@_SUITE \
-s init stop \
@@ -165,7 +148,11 @@ release_docs_spec:
release_tests_spec:
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(RELTEST_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(TEST_SPEC_FILE) \
+ $(COVER_SPEC_FILE) \
+ $(HRL_FILES) \
+ $(ERL_FILES) \
+ $(RELSYSDIR)
.PHONY: release_spec release_docs_spec release_test_specs
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index 531aca2799..75fdd0bd1d 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -38,5 +38,5 @@ MODULES = \
diameter_tls_SUITE \
diameter_failover_SUITE
-INTERNAL_HRL_FILES = \
+HRL_FILES = \
diameter_ct.hrl
--
cgit v1.2.3
From e89a59263762c1b625b1b054784b73112fb440a3 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Tue, 18 Oct 2011 14:10:53 +0200
Subject: Dumb down opt/release targets to make 3.80
---
lib/diameter/src/Makefile | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index ea1e43875e..15196bc002 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -42,6 +42,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/diameter-$(VSN)
EBIN = ../ebin
INCDIR = ../include
+# Dumbed down to make 3.80. In 3.81 and later it's just $(realpath $(EBIN)).
+ABS_EBIN := $(shell cd $(EBIN) && pwd)
+
# Where make should look for dependencies.
VPATH = .:base:compiler:transport:gen
@@ -103,7 +106,7 @@ ERL_COMPILE_FLAGS += \
+'{attribute,insert,app_vsn,$(APP_VSN)}' \
+warn_export_vars \
+warn_unused_vars \
- -pa $(realpath $(EBIN)) \
+ -pa $(ABS_EBIN) \
-I $(INCDIR) \
-I gen
# -pa is to be able to include_lib from the include directory: the
@@ -228,7 +231,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
# ----------------------------------------------------
-# Targets using secondary expansion
+# Targets using secondary expansion (make >= 3.81)
# ----------------------------------------------------
.SECONDEXPANSION:
--
cgit v1.2.3
From 6c048c57a4714e033f484ff79425ce847e9a43e9 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Wed, 19 Oct 2011 11:55:46 +0200
Subject: Dumb down release target to Solaris /usr/ucb/install
---
lib/diameter/src/Makefile | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
(limited to 'lib')
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 15196bc002..a057632d15 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -177,13 +177,12 @@ else
include $(ERL_TOP)/make/otp_release_targets.mk
endif
+# Can't $(INSTALL_DIR) more than on directory at a time on Solaris.
+
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/bin \
- $(RELSYSDIR)/ebin \
- $(RELSYSDIR)/examples \
- $(RELSYSDIR)/include \
- $(RELSYSDIR)/src/dict \
- $(TARGET_DIRS:%/=$(RELSYSDIR)/src/%)
+ for d in bin ebin examples include src/dict $(TARGET_DIRS:%/=src/%); do \
+ $(INSTALL_DIR) $(RELSYSDIR)/$$d; \
+ done
$(INSTALL_SCRIPT) $(BINS:%=../bin/%) $(RELSYSDIR)/bin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(EXAMPLES:%=../examples/%) $(RELSYSDIR)/examples
--
cgit v1.2.3
From 98df4ff16c9018bd61d04f35b0bdd92c9fb16942 Mon Sep 17 00:00:00 2001
From: Hans Bolinder
Date: Mon, 17 Oct 2011 13:53:25 +0200
Subject: Fix a few tests that used to fail on the HiPE platform
---
lib/stdlib/test/dets_SUITE.erl | 195 +++++++++++++++++++++--------------------
lib/stdlib/test/epp_SUITE.erl | 13 ++-
lib/stdlib/test/ets_SUITE.erl | 21 +++--
3 files changed, 123 insertions(+), 106 deletions(-)
(limited to 'lib')
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 63767aeda6..6f77cff2b9 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1865,10 +1865,10 @@ fixtable(Config, Version) when is_list(Config) ->
?line {ok, _} = dets:open_file(T, Args),
%% badarg
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:safe_fixtable(no_table,true)),
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[T,undefined],_}|_]}} =
- (catch dets:safe_fixtable(T,undefined)),
+ ?line check_badarg(catch dets:safe_fixtable(no_table,true),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:safe_fixtable(T,undefined),
+ dets, safe_fixtable, [T,undefined]),
%% The table is not allowed to grow while the elements are inserted:
@@ -1948,22 +1948,22 @@ match(Config, Version) ->
%% match, badarg
MSpec = [{'_',[],['$_']}],
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:match(no_table, '_')),
- ?line {'EXIT', {badarg, [{dets,match,[T,'_',not_a_number],_}|_]}} =
- (catch dets:match(T, '_', not_a_number)),
+ ?line check_badarg(catch dets:match(no_table, '_'),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:match(T, '_', not_a_number),
+ dets, match, [T,'_',not_a_number]),
?line {EC1, _} = dets:select(T, MSpec, 1),
- ?line {'EXIT', {badarg, [{dets,match,[EC1],_}|_]}} =
- (catch dets:match(EC1)),
+ ?line check_badarg(catch dets:match(EC1),
+ dets, match, [EC1]),
%% match_object, badarg
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:match_object(no_table, '_')),
- ?line {'EXIT', {badarg, [{dets,match_object,[T,'_',not_a_number],_}|_]}} =
- (catch dets:match_object(T, '_', not_a_number)),
+ ?line check_badarg(catch dets:match_object(no_table, '_'),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:match_object(T, '_', not_a_number),
+ dets, match_object, [T,'_',not_a_number]),
?line {EC2, _} = dets:select(T, MSpec, 1),
- ?line {'EXIT', {badarg, [{dets,match_object,[EC2],_}|_]}} =
- (catch dets:match_object(EC2)),
+ ?line check_badarg(catch dets:match_object(EC2),
+ dets, match_object, [EC2]),
dets:safe_fixtable(T, true),
?line {[_, _], C1} = dets:match_object(T, '_', 2),
@@ -2126,17 +2126,17 @@ select(Config, Version) ->
%% badarg
MSpec = [{'_',[],['$_']}],
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:select(no_table, MSpec)),
- ?line {'EXIT', {badarg, [{dets,select,[T,<<17>>],_}|_]}} =
- (catch dets:select(T, <<17>>)),
- ?line {'EXIT', {badarg, [{dets,select,[T,[]],_}|_]}} =
- (catch dets:select(T, [])),
- ?line {'EXIT', {badarg, [{dets,select,[T,MSpec,not_a_number],_}|_]}} =
- (catch dets:select(T, MSpec, not_a_number)),
+ ?line check_badarg(catch dets:select(no_table, MSpec),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:select(T, <<17>>),
+ dets, select, [T,<<17>>]),
+ ?line check_badarg(catch dets:select(T, []),
+ dets, select, [T,[]]),
+ ?line check_badarg(catch dets:select(T, MSpec, not_a_number),
+ dets, select, [T,MSpec,not_a_number]),
?line {EC, _} = dets:match(T, '_', 1),
- ?line {'EXIT', {badarg, [{dets,select,[EC],_}|_]}} =
- (catch dets:select(EC)),
+ ?line check_badarg(catch dets:select(EC),
+ dets, select, [EC]),
AllSpec = [{'_',[],['$_']}],
@@ -2218,8 +2218,8 @@ update_counter(Config) when is_list(Config) ->
?line file:delete(Fname),
P0 = pps(),
- ?line {'EXIT', {badarg, [{dets,update_counter,[no_table,1,1],_}|_]}} =
- (catch dets:update_counter(no_table, 1, 1)),
+ ?line check_badarg(catch dets:update_counter(no_table, 1, 1),
+ dets, update_counter, [no_table,1,1]),
Args = [{file,Fname},{keypos,2}],
?line {ok, _} = dets:open_file(T, [{type,set} | Args]),
@@ -2262,67 +2262,66 @@ badarg(Config) when is_list(Config) ->
%% badargs are tested in match, select and fixtable too.
%% open
- ?line {'EXIT', {badarg, [{dets,open_file,[{a,tuple},[]],_}|_]}} =
- (catch dets:open_file({a,tuple},[])),
- ?line {'EXIT', {badarg, [{dets,open_file,[{a,tuple}],_}|_]}} =
- (catch dets:open_file({a,tuple})),
- ?line {'EXIT', {badarg, [{dets,open_file,[file,[foo]],_}|_]}} =
- (catch dets:open_file(file,[foo])),
- ?line {'EXIT', {badarg,[{dets,open_file,
- [{hej,san},[{type,set}|3]],_}|_]}} =
- (catch dets:open_file({hej,san},[{type,set}|3])),
+ ?line check_badarg(catch dets:open_file({a,tuple},[]),
+ dets, open_file, [{a,tuple},[]]),
+ ?line check_badarg(catch dets:open_file({a,tuple}),
+ dets, open_file,[{a,tuple}]),
+ ?line check_badarg(catch dets:open_file(file,[foo]),
+ dets, open_file, [file,[foo]]),
+ ?line check_badarg(catch dets:open_file({hej,san},[{type,set}|3]),
+ dets, open_file, [{hej,san},[{type,set}|3]]),
%% insert
- ?line {'EXIT', {badarg, [{dets,insert,[no_table,{1,2}],_}|_]}} =
- (catch dets:insert(no_table, {1,2})),
- ?line {'EXIT', {badarg, [{dets,insert,[no_table,[{1,2}]],_}|_]}} =
- (catch dets:insert(no_table, [{1,2}])),
- ?line {'EXIT', {badarg, [{dets,insert,[T,{1,2}],_}|_]}} =
- (catch dets:insert(T, {1,2})),
- ?line {'EXIT', {badarg, [{dets,insert,[T,[{1,2}]],_}|_]}} =
- (catch dets:insert(T, [{1,2}])),
- ?line {'EXIT', {badarg, [{dets,insert,[T,[{1,2,3}|3]],_}|_]}} =
- (catch dets:insert(T, [{1,2,3} | 3])),
+ ?line check_badarg(catch dets:insert(no_table, {1,2}),
+ dets, insert, [no_table,{1,2}]),
+ ?line check_badarg(catch dets:insert(no_table, [{1,2}]),
+ dets, insert, [no_table,[{1,2}]]),
+ ?line check_badarg(catch dets:insert(T, {1,2}),
+ dets, insert, [T,{1,2}]),
+ ?line check_badarg(catch dets:insert(T, [{1,2}]),
+ dets, insert, [T,[{1,2}]]),
+ ?line check_badarg(catch dets:insert(T, [{1,2,3} | 3]),
+ dets, insert, [T,[{1,2,3}|3]]),
%% lookup{_keys}
- ?line {'EXIT', {badarg, [{dets,lookup_keys,[badarg,[]],_}|_]}} =
- (catch dets:lookup_keys(T, [])),
- ?line {'EXIT', {badarg, [{dets,lookup,[no_table,1],_}|_]}} =
- (catch dets:lookup(no_table, 1)),
- ?line {'EXIT', {badarg, [{dets,lookup_keys,[T,[1|2]],_}|_]}} =
- (catch dets:lookup_keys(T, [1 | 2])),
+ ?line check_badarg(catch dets:lookup_keys(T, []),
+ dets, lookup_keys, [badarg,[]]),
+ ?line check_badarg(catch dets:lookup(no_table, 1),
+ dets, lookup, [no_table,1]),
+ ?line check_badarg(catch dets:lookup_keys(T, [1 | 2]),
+ dets, lookup_keys, [T,[1|2]]),
%% member
- ?line {'EXIT', {badarg, [{dets,member,[no_table,1],_}|_]}} =
- (catch dets:member(no_table, 1)),
+ ?line check_badarg(catch dets:member(no_table, 1),
+ dets, member, [no_table,1]),
%% sync
- ?line {'EXIT', {badarg, [{dets,sync,[no_table],_}|_]}} =
- (catch dets:sync(no_table)),
+ ?line check_badarg(catch dets:sync(no_table),
+ dets, sync, [no_table]),
%% delete{_keys}
- ?line {'EXIT', {badarg, [{dets,delete,[no_table,1],_}|_]}} =
- (catch dets:delete(no_table, 1)),
+ ?line check_badarg(catch dets:delete(no_table, 1),
+ dets, delete, [no_table,1]),
%% delete_object
- ?line {'EXIT', {badarg, [{dets,delete_object,[no_table,{1,2,3}],_}|_]}} =
- (catch dets:delete_object(no_table, {1,2,3})),
- ?line {'EXIT', {badarg, [{dets,delete_object,[T,{1,2}],_}|_]}} =
- (catch dets:delete_object(T, {1,2})),
- ?line {'EXIT', {badarg, [{dets,delete_object,[no_table,[{1,2,3}]],_}|_]}} =
- (catch dets:delete_object(no_table, [{1,2,3}])),
- ?line {'EXIT', {badarg, [{dets,delete_object,[T,[{1,2}]],_}|_]}} =
- (catch dets:delete_object(T, [{1,2}])),
- ?line {'EXIT', {badarg, [{dets,delete_object,[T,[{1,2,3}|3]],_}|_]}} =
- (catch dets:delete_object(T, [{1,2,3} | 3])),
+ ?line check_badarg(catch dets:delete_object(no_table, {1,2,3}),
+ dets, delete_object, [no_table,{1,2,3}]),
+ ?line check_badarg(catch dets:delete_object(T, {1,2}),
+ dets, delete_object, [T,{1,2}]),
+ ?line check_badarg(catch dets:delete_object(no_table, [{1,2,3}]),
+ dets, delete_object, [no_table,[{1,2,3}]]),
+ ?line check_badarg(catch dets:delete_object(T, [{1,2}]),
+ dets, delete_object, [T,[{1,2}]]),
+ ?line check_badarg(catch dets:delete_object(T, [{1,2,3} | 3]),
+ dets, delete_object, [T,[{1,2,3}|3]]),
%% first,next,slot
- ?line {'EXIT', {badarg, [{dets,first,[no_table],_}|_]}} =
- (catch dets:first(no_table)),
- ?line {'EXIT', {badarg, [{dets,next,[no_table,1],_}|_]}} =
- (catch dets:next(no_table, 1)),
- ?line {'EXIT', {badarg, [{dets,slot,[no_table,0],_}|_]}} =
- (catch dets:slot(no_table, 0)),
+ ?line check_badarg(catch dets:first(no_table),
+ dets, first, [no_table]),
+ ?line check_badarg(catch dets:next(no_table, 1),
+ dets, next, [no_table,1]),
+ ?line check_badarg(catch dets:slot(no_table, 0),
+ dets, slot, [no_table,0]),
%% info
?line undefined = dets:info(no_table),
@@ -2330,27 +2329,27 @@ badarg(Config) when is_list(Config) ->
?line undefined = dets:info(T, foo),
%% match_delete
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:match_delete(no_table, '_')),
+ ?line check_badarg(catch dets:match_delete(no_table, '_'),
+ dets, safe_fixtable, [no_table,true]),
%% delete_all_objects
- ?line {'EXIT', {badarg, [{dets,delete_all_objects,[no_table],_}|_]}} =
- (catch dets:delete_all_objects(no_table)),
+ ?line check_badarg(catch dets:delete_all_objects(no_table),
+ dets, delete_all_objects, [no_table]),
%% select_delete
MSpec = [{'_',[],['$_']}],
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:select_delete(no_table, MSpec)),
- ?line {'EXIT', {badarg, [{dets,select_delete,[T, <<17>>],_}|_]}} =
- (catch dets:select_delete(T, <<17>>)),
+ ?line check_badarg(catch dets:select_delete(no_table, MSpec),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:select_delete(T, <<17>>),
+ dets, select_delete, [T, <<17>>]),
%% traverse, fold
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:traverse(no_table, fun(_) -> continue end)),
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:foldl(fun(_, A) -> A end, [], no_table)),
- ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true],_}|_]}} =
- (catch dets:foldr(fun(_, A) -> A end, [], no_table)),
+ ?line check_badarg(catch dets:traverse(no_table, fun(_) -> continue end),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:foldl(fun(_, A) -> A end, [], no_table),
+ dets, safe_fixtable, [no_table,true]),
+ ?line check_badarg(catch dets:foldr(fun(_, A) -> A end, [], no_table),
+ dets, safe_fixtable, [no_table,true]),
%% close
?line ok = dets:close(T),
@@ -2358,15 +2357,16 @@ badarg(Config) when is_list(Config) ->
?line {error, not_owner} = dets:close(T),
%% init_table
- ?line {'EXIT', {badarg,[{dets,init_table,[no_table,_,[]],_}|_]}} =
- (catch dets:init_table(no_table, fun(X) -> X end)),
- ?line {'EXIT', {badarg,[{dets,init_table,[no_table,_,[]],_}|_]}} =
- (catch dets:init_table(no_table, fun(X) -> X end, [])),
+ IF = fun(X) -> X end,
+ ?line check_badarg(catch dets:init_table(no_table, IF),
+ dets, init_table, [no_table,IF,[]]),
+ ?line check_badarg(catch dets:init_table(no_table, IF, []),
+ dets, init_table, [no_table,IF,[]]),
%% from_ets
Ets = ets:new(ets,[]),
- ?line {'EXIT', {badarg,[{dets,from_ets,[no_table,_],_}|_]}} =
- (catch dets:from_ets(no_table, Ets)),
+ ?line check_badarg(catch dets:from_ets(no_table, Ets),
+ dets, from_ets, [no_table,Ets]),
ets:delete(Ets),
?line {ok, T} = dets:open_file(T, Args),
@@ -4358,6 +4358,11 @@ bad_object({error,{{bad_object,_}, FileName}}, FileName) ->
bad_object({error,{{{bad_object,_,_},_,_,_}, FileName}}, FileName) ->
ok. % Debug.
+check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) ->
+ true;
+check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) ->
+ true = test_server:is_native(M) andalso length(Args) =:= A.
+
check_pps(P0) ->
case pps() of
P0 ->
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index e58022f4da..f79414db49 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -250,16 +250,23 @@ otp_4871(Config) when is_list(Config) ->
%% so there are some sanity checks before killing.
?line {ok,Epp} = epp:open(File, []),
timer:sleep(1),
- ?line {current_function,{epp,_,_}} = process_info(Epp, current_function),
+ ?line true = current_module(Epp, epp),
?line {monitored_by,[Io]} = process_info(Epp, monitored_by),
- ?line {current_function,{file_io_server,_,_}} =
- process_info(Io, current_function),
+ ?line true = current_module(Io, file_io_server),
?line exit(Io, emulate_crash),
timer:sleep(1),
?line {error,{_Line,epp,cannot_parse}} = otp_4871_parse_file(Epp),
?line epp:close(Epp),
ok.
+current_module(Pid, Mod) ->
+ case process_info(Pid, current_function) of
+ {current_function, undefined} ->
+ true = test_server:is_native(Mod);
+ {current_function, {Mod, _, _}} ->
+ true
+ end.
+
otp_4871_parse_file(Epp) ->
case epp:parse_erl_form(Epp) of
{ok,_} -> otp_4871_parse_file(Epp);
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index e048764a55..2f4958760b 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -795,21 +795,26 @@ t_ets_dets(Config, Opts) ->
?line true = ets:from_dets(ETab,DTab),
?line 3000 = ets:info(ETab,size),
?line ets:delete(ETab),
- ?line {'EXIT',{badarg,[{ets,to_dets,[ETab,DTab],_}|_]}} =
- (catch ets:to_dets(ETab,DTab)),
- ?line {'EXIT',{badarg,[{ets,from_dets,[ETab,DTab],_}|_]}} =
- (catch ets:from_dets(ETab,DTab)),
+ ?line check_badarg(catch ets:to_dets(ETab,DTab),
+ ets, to_dets, [ETab,DTab]),
+ ?line check_badarg(catch ets:from_dets(ETab,DTab),
+ ets, from_dets, [ETab,DTab]),
?line ETab2 = ets_new(x,Opts),
?line filltabint(ETab2,3000),
?line dets:close(DTab),
- ?line {'EXIT',{badarg,[{ets,to_dets,[ETab2,DTab],_}|_]}} =
- (catch ets:to_dets(ETab2,DTab)),
- ?line {'EXIT',{badarg,[{ets,from_dets,[ETab2,DTab],_}|_]}} =
- (catch ets:from_dets(ETab2,DTab)),
+ ?line check_badarg(catch ets:to_dets(ETab2,DTab),
+ ets, to_dets, [ETab2,DTab]),
+ ?line check_badarg(catch ets:from_dets(ETab2,DTab),
+ ets, from_dets, [ETab2,DTab]),
?line ets:delete(ETab2),
?line (catch file:delete(Fname)),
ok.
+check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) ->
+ true;
+check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) ->
+ true = test_server:is_native(M) andalso length(Args) =:= A.
+
t_delete_all_objects(doc) ->
["Test ets:delete_all_objects/1"];
t_delete_all_objects(suite) ->
--
cgit v1.2.3
From 0133d7aeabeabca47d112b039823d0ba492905cb Mon Sep 17 00:00:00 2001
From: Siri Hansen
Date: Fri, 21 Oct 2011 11:43:57 +0200
Subject: Flush trace file driver before stopping dbg
Earlier dbg:stop only did erlang:trace_delivered and did not flush the
trace file driver. Therefore there could still be trace messages that
were delivered to the driver (guaranteed by erlang:trace_delivered)
but not yet written to the file. This commit adds this flushing on
each node before the dbg process terminates.
---
lib/runtime_tools/src/dbg.erl | 52 +++++++++++++++++++++----------------------
1 file changed, 26 insertions(+), 26 deletions(-)
(limited to 'lib')
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 446de63064..385047ee73 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -32,7 +32,7 @@
-export([fun2ms/1]).
%% Local exports
--export([erlang_trace/3,get_info/0]).
+-export([erlang_trace/3,get_info/0,deliver_and_flush/1]).
%% Debug exports
-export([wrap_presort/2, wrap_sort/2, wrap_postsort/1, wrap_sortfix/2,
@@ -348,17 +348,16 @@ trace_port_control(Operation) ->
trace_port_control(node(), Operation).
trace_port_control(Node, flush) ->
- Ref = erlang:trace_delivered(all),
- receive
- {trace_delivered,all,Ref} -> ok
- end,
- case trace_port_control(Node, $f, "") of
- {ok, [0]} ->
- ok;
- {ok, _} ->
- {error, not_supported_by_trace_driver};
- Other ->
- Other
+ case get_tracer(Node) of
+ {ok, Port} when is_port(Port) ->
+ case catch rpc:call(Node,?MODULE,deliver_and_flush,[Port]) of
+ [0] ->
+ ok;
+ _ ->
+ {error, not_supported_by_trace_driver}
+ end;
+ _ ->
+ {error, no_trace_driver}
end;
trace_port_control(Node,get_listen_port) ->
case trace_port_control(Node,$p, "") of
@@ -378,7 +377,14 @@ trace_port_control(Node, Command, Arg) ->
{error, no_trace_driver}
end.
-
+%% A bit more than just flush - it also makes sure all trace messages
+%% are delivered first, before flushing the driver.
+deliver_and_flush(Port) ->
+ Ref = erlang:trace_delivered(all),
+ receive
+ {trace_delivered,all,Ref} -> ok
+ end,
+ erlang:port_control(Port, $f, "").
trace_port(file, {Filename, wrap, Tail}) ->
@@ -684,18 +690,12 @@ loop({C,T}=SurviveLinks, Table) ->
%% tracing on the node it removes from the list of active trace nodes,
%% we will call erlang:trace_delivered/1 on ALL nodes that we have
%% connections to.
- Delivered = fun() ->
- Ref = erlang:trace_delivered(all),
- receive
- {trace_delivered,all,Ref} -> ok
- end
- end,
- catch rpc:multicall(nodes(), erlang, apply, [Delivered,[]]),
- Ref = erlang:trace_delivered(all),
- receive
- {trace_delivered,all,Ref} ->
- exit(done)
- end;
+ %% If it is a file trace driver, we will also flush the port.
+ lists:foreach(fun({Node,{_Relay,Port}}) ->
+ rpc:call(Node,?MODULE,deliver_and_flush,[Port])
+ end,
+ get()),
+ exit(done);
{From, {link_to, Pid}} ->
case (catch link(Pid)) of
{'EXIT', Reason} ->
--
cgit v1.2.3
From c2e6879aee84f04b2132a52580219b13ce586a13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 21 Oct 2011 17:30:37 +0200
Subject: eprof: Fix invalid references to removed functions
---
lib/tools/doc/src/eprof.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'lib')
diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml
index 6d68c90768..1dbc41ec8e 100644
--- a/lib/tools/doc/src/eprof.xml
+++ b/lib/tools/doc/src/eprof.xml
@@ -147,7 +147,7 @@
This function ensures that the results displayed by
- analyse/0 and total_analyse/0 are printed both to
+ analyze/0,1,2 are printed both to
the file File and the screen.
--
cgit v1.2.3
From 32c475cfe5bbc2c2eb55d83102112233d799a01a Mon Sep 17 00:00:00 2001
From: Andreas Schultz
Date: Mon, 3 Oct 2011 12:46:12 +0200
Subject: fix handling of block_decipher/5 failure
A wrong decryption key would cause a badmatch in
generic_block_cipher_from_bin/2. The try in block_decipher/5 was
probably intendend to deal with that, but was misplace for this.
Additionaly, generating a failure alert erly, without computing the
record MAC, creates vector for a timing attack on CBC padding (for
details check TLS 1.2 RFC 5246, Sect. 6.2.3.2.). This attach vector
and the counter meassure applies to all SSL/TLS versions.
As a counter messure, compute the MAC even when decryption or padding
checks fail. A invalid padding will force a MAC failure by intentionaly
invalidating the content.
---
lib/ssl/src/ssl_cipher.erl | 80 +++++++++++++------
lib/ssl/test/Makefile | 2 +
lib/ssl/test/ssl_cipher_SUITE.erl | 163 ++++++++++++++++++++++++++++++++++++++
3 files changed, 220 insertions(+), 25 deletions(-)
create mode 100644 lib/ssl/test/ssl_cipher_SUITE.erl
(limited to 'lib')
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 72f02a4362..95a5efd6d0 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -154,18 +154,23 @@ decipher(?AES, HashSz, CipherState, Fragment, Version) ->
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
HashSz, Fragment, Version) ->
- try Fun(Key, IV, Fragment) of
- Text ->
- GBC = generic_block_cipher_from_bin(Text, HashSz),
- case is_correct_padding(GBC, Version) of
- true ->
- Content = GBC#generic_block_cipher.content,
- Mac = GBC#generic_block_cipher.mac,
- CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
- {Content, Mac, CipherState1};
- false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
- end
+ try
+ Text = Fun(Key, IV, Fragment),
+ GBC = generic_block_cipher_from_bin(Text, HashSz),
+ Content = GBC#generic_block_cipher.content,
+ Mac = GBC#generic_block_cipher.mac,
+ CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
+ case is_correct_padding(GBC, Version) of
+ true ->
+ {Content, Mac, CipherState1};
+ false ->
+ %% decryption failed or invalid padding,
+ %% intentionally break Content to make
+ %% sure a packet with a an invalid padding
+ %% but otherwise correct data will fail
+ %% the MAC test later
+ {<<16#F0, Content/binary>>, Mac, CipherState1}
+ end
catch
_:_ ->
%% This is a DECRYPTION_FAILED but
@@ -500,14 +505,38 @@ hash_size(md5) ->
hash_size(sha) ->
20.
+%% RFC 5246: 6.2.3.2. CBC Block Cipher
+%%
+%% Implementation note: Canvel et al. [CBCTIME] have demonstrated a
+%% timing attack on CBC padding based on the time required to compute
+%% the MAC. In order to defend against this attack, implementations
+%% MUST ensure that record processing time is essentially the same
+%% whether or not the padding is correct. In general, the best way to
+%% do this is to compute the MAC even if the padding is incorrect, and
+%% only then reject the packet. For instance, if the pad appears to be
+%% incorrect, the implementation might assume a zero-length pad and then
+%% compute the MAC. This leaves a small timing channel, since MAC
+%% performance depends to some extent on the size of the data fragment,
+%% but it is not believed to be large enough to be exploitable, due to
+%% the large block size of existing MACs and the small size of the
+%% timing signal.
+%%
+%% implementation note:
+%% We return the original (possibly invalid) PadLength in any case.
+%% A invalid PadLength will be cought by is_correct_padding/2
+%%
generic_block_cipher_from_bin(T, HashSize) ->
Sz1 = byte_size(T) - 1,
- <<_:Sz1/binary, ?BYTE(PadLength)>> = T,
+ <<_:Sz1/binary, ?BYTE(PadLength0)>> = T,
+ PadLength = if
+ PadLength0 >= Sz1 -> 0;
+ true -> PadLength0
+ end,
CompressedLength = byte_size(T) - PadLength - 1 - HashSize,
<> = T,
+ Padding:PadLength/binary, ?BYTE(PadLength0)>> = T,
#generic_block_cipher{content=Content, mac=Mac,
- padding=Padding, padding_length=PadLength}.
+ padding=Padding, padding_length=PadLength0}.
generic_stream_cipher_from_bin(T, HashSz) ->
Sz = byte_size(T),
@@ -516,17 +545,18 @@ generic_stream_cipher_from_bin(T, HashSz) ->
#generic_stream_cipher{content=Content,
mac=Mac}.
-is_correct_padding(_, {3, 0}) ->
- true;
-%% For interoperability reasons we do not check the padding in TLS 1.0 as it
-%% is not strictly required and breaks interopability with for instance
-%% Google.
-is_correct_padding(_, {3, 1}) ->
- true;
+%% For interoperability reasons we do not check the padding content in
+%% SSL 3.0 and TLS 1.0 as it is not strictly required and breaks
+%% interopability with for instance Google.
+is_correct_padding(#generic_block_cipher{padding_length = Len,
+ padding = Padding}, {3, N})
+ when N == 0; N == 1 ->
+ Len == byte_size(Padding);
%% Padding must be check in TLS 1.1 and after
-is_correct_padding(#generic_block_cipher{padding_length = Len, padding = Padding}, _) ->
- list_to_binary(lists:duplicate(Len, Len)) == Padding.
-
+is_correct_padding(#generic_block_cipher{padding_length = Len,
+ padding = Padding}, _) ->
+ Len == byte_size(Padding) andalso
+ list_to_binary(lists:duplicate(Len, Len)) == Padding.
get_padding(Length, BlockSize) ->
get_padding_aux(BlockSize, Length rem BlockSize).
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 23a9a23190..6b1da63d08 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -39,6 +39,7 @@ MODULES = \
ssl_basic_SUITE \
ssl_handshake_SUITE \
ssl_packet_SUITE \
+ ssl_cipher_SUITE \
ssl_payload_SUITE \
ssl_to_openssl_SUITE \
ssl_session_cache_SUITE \
@@ -55,6 +56,7 @@ HRL_FILES_SRC = \
ssl_internal.hrl\
ssl_alert.hrl \
ssl_handshake.hrl \
+ ssl_cipher.hrl \
ssl_record.hrl
HRL_FILES_INC =
diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl
new file mode 100644
index 0000000000..87478e13bc
--- /dev/null
+++ b/lib/ssl/test/ssl_cipher_SUITE.erl
@@ -0,0 +1,163 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2011. 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(ssl_cipher_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-include("ssl_internal.hrl").
+-include("ssl_record.hrl").
+-include("ssl_cipher.hrl").
+
+-define(TIMEOUT, 600000).
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initialization before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [aes_decipher_good, aes_decipher_fail].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+aes_decipher_good(doc) ->
+ ["Decipher a known cryptotext."];
+
+aes_decipher_good(suite) ->
+ [];
+
+aes_decipher_good(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
+ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Version = {3,3},
+ Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56,72,69,76,76,79,10>>,
+ Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>,
+ {Content, Mac, CipherState1} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ ok.
+
+%%--------------------------------------------------------------------
+
+aes_decipher_fail(doc) ->
+ ["Decipher a known cryptotext."];
+
+aes_decipher_fail(suite) ->
+ [];
+
+%% same as above, last byte of key replaced
+aes_decipher_fail(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
+ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Version = {3,3},
+ {Content, Mac, CipherState1} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ 32 = byte_size(Content),
+ 32 = byte_size(Mac),
+ ok.
+
+%%--------------------------------------------------------------------
--
cgit v1.2.3
From 3326bd4efa5ff4965a1bab2f6451a4bf47ca0ebb Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 25 Oct 2011 10:19:42 +0200
Subject: Upped the version number. Some cosmetics related to the profile tool.
---
lib/megaco/test/megaco_codec_v1_test.erl | 4 ++--
lib/megaco/test/megaco_codec_v2_test.erl | 4 ++--
lib/megaco/vsn.mk | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
(limited to 'lib')
diff --git a/lib/megaco/test/megaco_codec_v1_test.erl b/lib/megaco/test/megaco_codec_v1_test.erl
index 3a548c4d9e..e9c19605dd 100644
--- a/lib/megaco/test/megaco_codec_v1_test.erl
+++ b/lib/megaco/test/megaco_codec_v1_test.erl
@@ -371,9 +371,9 @@ profile_decode_text_messages(Slogan, Codec, Config, Msgs0) ->
decode_text_messages(Codec, Config, Bins, [])
end,
%% Make a dry run, just to make sure all modules are loaded:
- io:format("make a dry run..~n", []),
+ io:format("make a dry run...~n", []),
(catch Fun()),
- io:format("make the run..~n", []),
+ io:format("make the run...~n", []),
megaco_profile:profile(Slogan, Fun).
%% (catch megaco_codec_v1_test:profile_encode_compact_text_messages()).
diff --git a/lib/megaco/test/megaco_codec_v2_test.erl b/lib/megaco/test/megaco_codec_v2_test.erl
index c3a80febba..1d3fcb36e9 100644
--- a/lib/megaco/test/megaco_codec_v2_test.erl
+++ b/lib/megaco/test/megaco_codec_v2_test.erl
@@ -346,9 +346,9 @@ profile_decode_text_messages(Slogan, Codec, Config, Msgs0) ->
decode_text_messages(Codec, Config, Bins, [])
end,
%% Make a dry run, just to make sure all modules are loaded:
- io:format("make a dry run..~n", []),
+ io:format("make a dry run...~n", []),
(catch Fun()),
- io:format("make the run..~n", []),
+ io:format("make the run...~n", []),
megaco_profile:profile(Slogan, Fun).
%% (catch megaco_codec_v2_test:profile_encode_compact_text_messages()).
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index c1476488ca..35acffcb64 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.15.1.1
+MEGACO_VSN = 3.15.1.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
--
cgit v1.2.3
From b0e8fa11c98c9a2bad42ac2f8f411a84bc937398 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 25 Oct 2011 10:20:45 +0200
Subject: =?UTF-8?q?H=E5kan=20Mattsson=20rewrote=20the=20profile=20tool.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lib/megaco/test/megaco_profile.erl | 343 ++++++++++++++++++++++++++++---------
1 file changed, 258 insertions(+), 85 deletions(-)
(limited to 'lib')
diff --git a/lib/megaco/test/megaco_profile.erl b/lib/megaco/test/megaco_profile.erl
index d0b62610e1..344c551970 100644
--- a/lib/megaco/test/megaco_profile.erl
+++ b/lib/megaco/test/megaco_profile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -24,50 +24,102 @@
-module(megaco_profile).
--export([profile/2]).
-
+-export([profile/2, prepare/2, analyse/1,
+ fprof_to_calltree/1, fprof_to_calltree/2, fprof_to_calltree/3]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Execute Fun and profile it with fprof.
-profile(Slogan, Fun) when is_function(Fun) ->
+profile(Slogan, Fun) when is_function(Fun, 0) ->
Pids = [self()],
- profile(Slogan, Fun, Pids).
+ {ok, TraceFile} = prepare(Slogan, Pids),
+ Res = (catch Fun()),
+ {ok, _DestFile} = analyse(Slogan),
+ ok = file:delete(TraceFile),
+ {ok, _TreeFile} = fprof_to_calltree(Slogan),
+ Res.
-profile(Slogan, Fun, Pids) ->
+%% Prepare for tracing
+prepare(Slogan, Pids) ->
TraceFile = lists:concat(["profile_", Slogan, "-fprof.trace"]),
- DestFile = lists:concat(["profile_", Slogan, ".fprof"]),
- TreeFile = lists:concat(["profile_", Slogan, ".calltree"]),
- erlang:garbage_collect(),
{ok, _Pid} = fprof:start(),
+ erlang:garbage_collect(),
TraceOpts = [start,
- {cpu_time, false},
- {procs, Pids},
- {file, TraceFile}
+ {cpu_time, false},
+ {procs, Pids},
+ {file, TraceFile}
],
- ok = fprof:trace(TraceOpts),
- Res = (catch Fun()),
- ok = fprof:trace(stop),
- ok = fprof:profile([{file, TraceFile}]),
- ok = fprof:analyse([{dest, DestFile}]),
- ok = fprof:stop(),
- ok = file:delete(TraceFile),
- reformat_total(DestFile, TreeFile),
- Res.
+ ok = fprof:trace(TraceOpts),
+ {ok, TraceFile}.
-reformat_total(FromFile, ToFile) ->
- {ok, ConsultedFromFile} = file:consult(FromFile),
- [_AnalysisOpts, [Totals] | Terms] = ConsultedFromFile,
- {totals, _, TotalAcc, _} = Totals,
+%% Stop tracing and analyse it
+analyse(Slogan) ->
+ fprof:trace(stop),
+ TraceFile = lists:concat(["profile_", Slogan, "-fprof.trace"]),
+ DestFile = lists:concat(["profile_", Slogan, ".fprof"]),
+ try
+ case fprof:profile([{file, TraceFile}]) of
+ ok ->
+ ok = fprof:analyse([{dest, DestFile}, {totals, false}]),
+ {ok, DestFile};
+ {error, Reason} ->
+ {error, Reason}
+ end
+ after
+ fprof:stop()
+ end.
+
+fprof_to_calltree(Slogan) ->
+ fprof_to_calltree(Slogan, 0).
+
+fprof_to_calltree(Slogan, MinPercent) ->
+ DestFile = lists:concat(["profile_", Slogan, ".fprof"]),
+ TreeFile = lists:concat(["profile_", Slogan, ".calltree"]),
+ fprof_to_calltree(DestFile, TreeFile, MinPercent).
+
+%% Create a calltree from an fprof file
+fprof_to_calltree(FromFile, ToFile, MinPercent) ->
+ ReplyTo = self(),
+ Ref = make_ref(),
+ spawn_link(fun() ->
+ ReplyTo ! {Ref, do_fprof_to_calltree(FromFile, ToFile, MinPercent)}
+ end),
+ wait_for_reply(Ref).
+
+wait_for_reply(Ref) ->
+ receive
+ {Ref, Res} ->
+ Res;
+ {'EXIT', normal} ->
+ wait_for_reply(Ref);
+ {'EXIT', Reason} ->
+ exit(Reason)
+ end.
+
+do_fprof_to_calltree(FromFile, ToFile, MinPercent) ->
{ok, Fd} = file:open(ToFile, [write, raw]),
- Indent = "",
- log(Fd, Indent, TotalAcc, Totals),
- Processes = split_processes(Terms, [], []),
- Reformat = fun(P) -> reformat_process(Fd, " " ++ Indent, TotalAcc, P) end,
- lists:foreach(Reformat, Processes),
- file:close(Fd).
+ {ok, ConsultedFromFile} = file:consult(FromFile),
+ [_AnalysisOpts, [_Totals] | Terms] = ConsultedFromFile,
+ Processes = split_processes(Terms, [], []),
+ Indent = "",
+ Summary = collapse_processes(Processes),
+ {_Label, _Cnt, Acc, _Own, _Roots, Details} = Summary,
+ %% log(Fd, Label, Indent, Acc, {Label, Cnt, Acc, Own}, [], 0),
+ gen_calltree(Fd, Indent, Acc, Summary, MinPercent),
+ Delim = io_lib:format("\n~80..=c\n\n", [$=]),
+ Write =
+ fun(P) ->
+ file:write(Fd, Delim),
+ gen_calltree(Fd, Indent, Acc, P, MinPercent)
+ end,
+ lists:foreach(Write, Processes),
+ file:write(Fd, Delim),
+ gen_details(Fd, Acc, Details),
+ file:close(Fd),
+ {ok, ToFile}.
+%% Split all fprof terms into a list of processes
split_processes([H | T], ProcAcc, TotalAcc) ->
if
is_tuple(H) ->
@@ -75,64 +127,185 @@ split_processes([H | T], ProcAcc, TotalAcc) ->
is_list(H), ProcAcc =:= [] ->
split_processes(T, [H], TotalAcc);
is_list(H) ->
- split_processes(T, [H], [lists:reverse(ProcAcc) | TotalAcc])
+ ProcAcc2 = rearrange_process(lists:reverse(ProcAcc)),
+ split_processes(T, [H], [ProcAcc2 | TotalAcc])
end;
split_processes([], [], TotalAcc) ->
- lists:reverse(TotalAcc);
+ lists:reverse(lists:keysort(3, TotalAcc));
split_processes([], ProcAcc, TotalAcc) ->
- lists:reverse([lists:reverse(ProcAcc) | TotalAcc]).
-
-reformat_process(Fd, Indent, TotalAcc, Terms) ->
- case Terms of
- [[{ProcLabel, _, _, _}] | All] -> ok;
- [[{ProcLabel,_,_,_} | _] | All] -> ok
- end,
- [{_, {TopKey, TopCnt, TopAcc, TopOwn}, _} | _] = All,
- Process = {ProcLabel, TopCnt, TopAcc, TopOwn},
- log(Fd, Indent, TotalAcc, Process),
- reformat_calls(Fd, " " ++ Indent, TotalAcc, TopKey, All, []).
-
-reformat_calls(Fd, Indent, TotalAcc, Key, Terms, Stack) ->
- {_CalledBy, Current, Calls} = find(Key, Terms),
- log(Fd, Indent, TotalAcc, Current),
- case lists:member(Key, Stack) of
- true ->
- ok;
- false ->
- case Key of
- {io_lib, _, _} ->
- ok;
- {disk_log, _, _} ->
- ok;
- {lists, flatten, _} ->
- ok;
- {lists, keysort, _} ->
- ok;
- _ ->
- Fun = fun({NextKey, _, _, _}) ->
- reformat_calls(Fd,
- " " ++ Indent,
- TotalAcc,
- NextKey,
- Terms,
- [Key | Stack])
- end,
- lists:foreach(Fun, Calls)
- end
+ ProcAcc2 = rearrange_process(lists:reverse(ProcAcc)),
+ lists:reverse(lists:keysort(3, [ProcAcc2 | TotalAcc])).
+
+%% Rearrange the raw process list into a more useful format
+rearrange_process([[{Label, _Cnt, _Acc, _Own} | _ ] | Details]) ->
+ do_rearrange_process(Details, Details, Label, [], []).
+
+do_rearrange_process([{CalledBy, Current, _Calls} | T], Orig, Label, Roots, Undefs) ->
+ case [{undefined, Cnt, safe_max(Acc, Own), Own} ||
+ {undefined, Cnt, Acc, Own} <- CalledBy] of
+ [] ->
+ do_rearrange_process(T, Orig, Label, Roots, Undefs);
+ NewUndefs ->
+ do_rearrange_process(T, Orig, Label, [Current | Roots], NewUndefs ++ Undefs)
+ end;
+do_rearrange_process([], Details, Label, Roots, Undefs) ->
+ [{undefined, Cnt, Acc, Own}] = collapse_calls(Undefs, []),
+ Details2 = sort_details(3, Details),
+ {Label, Cnt, Acc, Own, lists:reverse(lists:keysort(3, Roots)), Details2}.
+
+%% Compute a summary of the rearranged process info
+collapse_processes(Processes) ->
+ Headers = lists:map(fun({_L, C, A, O, _R, _D}) -> {"SUMMARY", C, A, O} end,
+ Processes),
+ [{Label, Cnt, Acc, Own}] = collapse_calls(Headers, []),
+ Details = lists:flatmap(fun({_L, _C, _A, _O, _R, D}) -> D end, Processes),
+ Details2 = do_collapse_processes(sort_details(1, Details), []),
+ Roots = lists:flatmap(fun({_L, _C, _A, _O, R, _D}) -> R end, Processes),
+ RootMFAs = lists:usort([MFA || {MFA, _, _, _} <- Roots]),
+ Roots2 = [R || RootMFA <- RootMFAs,
+ {_, {MFA, _, _, _} = R, _} <- Details2,
+ MFA =:= RootMFA],
+ Roots3 = collapse_calls(Roots2, []),
+ {Label, Cnt, Acc, Own, Roots3, Details2}.
+
+do_collapse_processes([{CalledBy1, {MFA, Cnt1, Acc1, Own1}, Calls1} | T1],
+ [{CalledBy2, {MFA, Cnt2, Acc2, Own2}, Calls2} | T2]) ->
+ Cnt = Cnt1 + Cnt2,
+ Acc = Acc1 + Acc2,
+ Own = Own1 + Own2,
+ Current = {MFA, Cnt, Acc, Own},
+ CalledBy0 = CalledBy1 ++ CalledBy2,
+ Calls0 = Calls1 ++ Calls2,
+ CalledBy = collapse_calls(lists:keysort(3, CalledBy0), []),
+ Calls = collapse_calls(lists:keysort(3, Calls0), []),
+ do_collapse_processes(T1, [{CalledBy, Current, Calls} | T2]);
+do_collapse_processes([{CalledBy, Current, Calls} | T1],
+ T2) ->
+ do_collapse_processes(T1, [{CalledBy, Current, Calls} | T2]);
+do_collapse_processes([],
+ T2) ->
+ sort_details(3, T2).
+
+%% Reverse sort on acc field
+sort_details(Pos, Details) ->
+ Pivot = fun({_CalledBy1, Current1, _Calls1},
+ {_CalledBy2, Current2, _Calls2}) ->
+ element(Pos, Current1) =< element(Pos, Current2)
+ end,
+ lists:reverse(lists:sort(Pivot, Details)).
+
+%% Compute a summary from a list of call tuples
+collapse_calls([{MFA, Cnt1, Acc1, Own1} | T1],
+ [{MFA, Cnt2, Acc2, Own2} | T2]) ->
+ Cnt = Cnt1 + Cnt2,
+ Acc = safe_sum(Acc1, Acc2),
+ Own = Own1 + Own2,
+ collapse_calls(T1, [{MFA, Cnt, Acc, Own} | T2]);
+collapse_calls([{MFA, Cnt, Acc, Own} | T1],
+ T2) ->
+ collapse_calls(T1, [{MFA, Cnt, Acc, Own} | T2]);
+collapse_calls([],
+ T2) ->
+ lists:reverse(lists:keysort(3, T2)).
+
+safe_sum(Int1, Int2) ->
+ if
+ Int1 =:= undefined -> Int2;
+ Int2 =:= undefined -> Int1;
+ true -> Int1 + Int2
+ end.
+
+safe_max(Int1, Int2) ->
+ if
+ Int1 =:= undefined ->
+ io:format("111\n", []),
+ Int2;
+ Int2 =:= undefined ->
+ io:format("222\n", []),
+ Int1;
+ Int2 > Int1 -> Int2;
+ true -> Int1
+ end.
+
+%% Compute a calltree and write it to file
+gen_calltree(Fd, Indent, TotalAcc, {Label, Cnt, Acc, Own, Roots, Details}, MinPercent) ->
+ Header = {Label, Cnt, Acc, Own},
+ MetaLabel = "Process",
+ Diff = length(Label) - length(MetaLabel),
+ IoList = io_lib:format("~s~s Lvl Pct Cnt Acc Own Calls => MFA\n",
+ [MetaLabel, lists:duplicate(Diff, $\ )]),
+ file:write(Fd, IoList),
+ log(Fd, Label, Indent, TotalAcc, Header, Roots, MinPercent),
+ NewIndent = " " ++ Indent,
+ Fun = fun({MFA, _C, _A, _O}) ->
+ [put_detail(Label, D) || D <- Details],
+ gen_calls(Fd, Label, NewIndent, TotalAcc, MFA, MinPercent)
+ end,
+ lists:foreach(Fun, Roots).
+
+gen_calls(Fd, Label, Indent, TotalAcc, MFA, MinPercent) ->
+ case get_detail(Label, MFA) of
+ {read, {_CalledBy, Current, _Calls}} ->
+ log(Fd, Label, Indent, TotalAcc, Current, -1, MinPercent);
+ {unread, {_CalledBy, Current, Calls}} ->
+ log(Fd, Label, Indent, TotalAcc, Current, Calls, MinPercent),
+ NewIndent = " " ++ Indent,
+ Fun = fun({NextMFA, _, _, _}) ->
+ gen_calls(Fd, Label, NewIndent, TotalAcc,
+ NextMFA, MinPercent)
+ end,
+ lists:foreach(Fun, Calls)
+ end.
+
+put_detail(Label, {_, {MFA, _, _, _}, _} = Detail) ->
+ put({Label, MFA}, {unread, Detail}).
+
+get_detail(Label, MFA) ->
+ Val = get({Label, MFA}),
+ case Val of
+ {unread, Detail} ->
+ put({Label, MFA}, {read, Detail}),
+ Val;
+ {read, _Detail} ->
+ Val
+ end.
+
+gen_details(Fd, Total, Details) ->
+ IoList = io_lib:format("Pct Cnt Acc Own MFA\n", []),
+ file:write(Fd, IoList),
+ do_gen_details(Fd, Total, Details).
+
+do_gen_details(Fd, Total, [{_CalledBy, {MFA, Cnt, Acc, Own}, _Calls} | Details]) ->
+ MFAStr = io_lib:format("~p", [MFA]),
+ {_, Percent} = calc_percent(Acc, Own, Total),
+ IoList = io_lib:format("~3.. B% ~10.3B ~10.3f ~10.3f => ~s\n",
+ [Percent, Cnt, Acc, Own, MFAStr]),
+ file:write(Fd, IoList),
+ do_gen_details(Fd, Total, Details);
+do_gen_details(_Fd, _Total, []) ->
+ ok.
+
+log(Fd, Label, Indent, Acc, Current, Calls, MinPercent) when is_list(Calls) ->
+ log(Fd, Label, Indent, Acc, Current, length(Calls), MinPercent);
+log(Fd, Label, Indent, Total, {MFA, Cnt, Acc, Own}, N, MinPercent) ->
+ {Max, Percent} = calc_percent(Acc, Own, Total),
+ if
+ Percent >= MinPercent ->
+ do_log(Fd, Label, Indent, Percent, MFA, Cnt, Max, Own, N);
+ true ->
+ ok
end.
-find(Key, [{_, {Key, _, _, _}, _} = H | _]) ->
- H;
-find(Key, [{_, {_, _, _, _}, _} | T]) ->
- find(Key, T).
-
-log(Fd, Indent, Total, {Label, Cnt, Acc, Own}) ->
- Percent = case Acc of
- undefined -> 100;
- _ -> trunc((lists:max([Acc, Own]) * 100) / Total)
- end,
- Label2 = io_lib:format("~p", [Label]),
- IoList = io_lib:format("~s~p% ~s \t~p \t~p \t~p\n",
- [Indent, Percent, Label2, Cnt, trunc(Acc), trunc(Own)]),
+do_log(Fd, Label, Indent, Percent, MFA, Cnt, Acc, Own, N) ->
+ MFAStr = io_lib:format("~p", [MFA]),
+ CallsStr = io_lib:format(" ~5.. s ", [lists:concat([N])]),
+ IoList = io_lib:format("~s ~3.. B "
+ "~s~3.. B% ~10.. B ~10.. B ~10.. B ~s => ~s\n",
+ [Label, length(Indent) div 2,
+ Indent, Percent, Cnt,
+ round(Acc), round(Own), CallsStr, MFAStr]),
file:write(Fd, IoList).
+calc_percent(Acc, Own, Total) ->
+ Max = safe_max(Acc, Own),
+ {Max, round((Max * 100) / Total)}.
--
cgit v1.2.3
From fe084d5d867de8839385c3652a0f727f97500a0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Tue, 9 Aug 2011 14:17:44 +0200
Subject: inviso: Eliminate use of deprecated regexp module
---
lib/inviso/src/inviso_tool_lib.erl | 8 ++++----
lib/runtime_tools/src/inviso_rt.erl | 4 ++--
lib/runtime_tools/src/inviso_rt_lib.erl | 16 ++++++++--------
lib/runtime_tools/test/inviso_SUITE.erl | 22 ++++++++++++----------
4 files changed, 26 insertions(+), 24 deletions(-)
(limited to 'lib')
diff --git a/lib/inviso/src/inviso_tool_lib.erl b/lib/inviso/src/inviso_tool_lib.erl
index 7953acedd6..f221c4b6de 100644
--- a/lib/inviso/src/inviso_tool_lib.erl
+++ b/lib/inviso/src/inviso_tool_lib.erl
@@ -19,7 +19,7 @@
%% Support module to the inviso tool.
%%
%% Authors:
-%% Lennart Öhman, lennart.ohman@st.se
+%% Lennart Öhman, lennart.ohman@st.se
%% -----------------------------------------------------------------------------
-module(inviso_tool_lib).
@@ -145,10 +145,10 @@ expand_module_names_2(Nodes,DirStr,ModStr,Opts) ->
%% Always returns a regexp or {error,Reason}.
expand_module_names_special_regexp(Str) ->
StrLen=length(Str),
- case regexp:first_match(Str,"[0-9a-zA-Z_/]*") of
- {match,1,StrLen} -> % Ok, it is the special case.
+ case re:run(Str,"[0-9a-zA-Z_/]*") of
+ {match,[{0,StrLen}]} -> % Ok, it is the special case.
{ok,".*"++Str++".*"}; % Convert it to a proper regexp.
- {match,_,_} ->
+ {match,_} ->
{ok,Str}; % Keep it and hope it is a regexp.
nomatch ->
{ok,Str}; % Keep it and hope it is a regexp.
diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl
index ac7ac2a584..b162f5b045 100644
--- a/lib/runtime_tools/src/inviso_rt.erl
+++ b/lib/runtime_tools/src/inviso_rt.erl
@@ -2359,8 +2359,8 @@ list_wrapset(Prefix,Suffix) ->
list_wrapset_2([File|Rest],RegExp) ->
Length=length(File),
- case regexp:first_match(File,RegExp) of
- {match,1,Length} -> % This is a member of the set.
+ case re:run(File,RegExp) of
+ {match,[{0,Length}]} -> % This is a member of the set.
[File|list_wrapset_2(Rest,RegExp)];
_ ->
list_wrapset_2(Rest,RegExp)
diff --git a/lib/runtime_tools/src/inviso_rt_lib.erl b/lib/runtime_tools/src/inviso_rt_lib.erl
index 2c6964e53e..ee6a72ae0c 100644
--- a/lib/runtime_tools/src/inviso_rt_lib.erl
+++ b/lib/runtime_tools/src/inviso_rt_lib.erl
@@ -197,15 +197,15 @@ match_modules(RegExpDir,RegExpMod,Actions) ->
handle_expand_regexp_2([{Mod,Path}|Rest],RegExpDir,RegExpMod,Result) ->
ModStr=atom_to_list(Mod),
ModLen=length(ModStr),
- case regexp:first_match(ModStr,RegExpMod) of
- {match,1,ModLen} -> % Ok, The regexp matches the module.
+ case re:run(ModStr,RegExpMod) of
+ {match,[{0,ModLen}]} -> % Ok, The regexp matches the module.
if
is_list(RegExpDir),is_atom(Path) -> % Preloaded or covercompiled...
handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result);
is_list(RegExpDir),is_list(Path) -> % Dir reg-exp is used!
PathOnly=filename:dirname(Path), % Must remove beam-file name.
- case regexp:first_match(PathOnly,RegExpDir) of
- {match,_,_} -> % Did find a match, that is enough!
+ case re:run(PathOnly,RegExpDir,[{capture,none}]) of
+ match -> % Did find a match, that is enough!
handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result]);
_ -> % Either error or nomatch.
handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result)
@@ -233,8 +233,8 @@ handle_expand_regexp_3([Path|Rest],RegExpDir,RegExpMod,AllLoaded,Result) ->
volumerelative -> % Only on Windows!?
filename:absname(Path)
end,
- case regexp:first_match(AbsPath,RegExpDir) of
- {match,_,_} -> % Ok, the directory is allowed.
+ case re:run(AbsPath,RegExpDir,[{capture,none}]) of
+ match -> % Ok, the directory is allowed.
NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result),
handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult);
_ -> % This directory does not qualify.
@@ -262,8 +262,8 @@ handle_expand_regexp_3_2([File|Rest],RegExpMod,AllLoaded,Result) ->
case {lists:keysearch(Mod,1,AllLoaded),lists:member(Mod,Result)} of
{false,false} -> % This module is not tried before.
ModLen=length(ModStr),
- case regexp:first_match(ModStr,RegExpMod) of
- {match,1,ModLen} -> % This module satisfies the regexp.
+ case re:run(ModStr,RegExpMod) of
+ {match,[{0,ModLen}]} -> % This module satisfies the regexp.
handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,[Mod|Result]);
_ -> % Error or not perfect match.
handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
diff --git a/lib/runtime_tools/test/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl
index 3ae8d34dd6..758867cf45 100644
--- a/lib/runtime_tools/test/inviso_SUITE.erl
+++ b/lib/runtime_tools/test/inviso_SUITE.erl
@@ -1380,9 +1380,10 @@ fetch_log_dist_trace_2(Config) ->
io:format("~p~n",[NodeResults]),
CheckFun=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
Fun2=fun({ok,File}) ->
- {match,1,_}=
- regexp:first_match(File,
- "^"++"p1"++Name++atom_to_list(N)),
+ match=
+ re:run(File,
+ "^"++"p1"++Name++atom_to_list(N),
+ [{capture,none}]),
true;
(_) ->
false
@@ -1425,8 +1426,8 @@ fetch_log_dist_trace_3(Config) ->
CheckFun=fun({N,{ok,[{trace_log,PrivDir2,[F1,F2]},{ti_log,PrivDir2,[F3]}]}})->
PrivDir2=PrivDir,
RegExp="^"++Name++atom_to_list(N)++"[0-9]+"++"\.log",
- {match,1,_}=regexp:first_match(F1,RegExp),
- {match,1,_}=regexp:first_match(F2,RegExp),
+ match=re:run(F1,RegExp,[{capture,none}]),
+ match=re:run(F2,RegExp,[{capture,none}]),
F3=Name++"_ti_"++atom_to_list(N)++".ti",
true;
(_) ->
@@ -1439,9 +1440,10 @@ fetch_log_dist_trace_3(Config) ->
io:format("~p~n",[NodeResults2]),
CheckFun2=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
Fun2=fun({ok,File}) ->
- {match,1,_}=
- regexp:first_match(File,
- "^"++"p1"++Name++atom_to_list(N)),
+ match=
+ re:run(File,
+ "^"++"p1"++Name++atom_to_list(N),
+ [{capture,none}]),
true;
(_) ->
false
@@ -2649,8 +2651,8 @@ check_on_nodes([],_,_,_,_) ->
how_many_files_regexp([],_,N) ->
{ok,N};
how_many_files_regexp([FName|Rest],RegExp,N) ->
- case regexp:first_match(FName,RegExp) of
- {match,1,_} ->
+ case re:run(FName,RegExp,[{capture,none}]) of
+ match ->
how_many_files_regexp(Rest,RegExp,N+1);
nomatch ->
how_many_files_regexp(Rest,RegExp,N);
--
cgit v1.2.3
From 9e034e51ca0f051b62e389545b3f5a56b9cec393 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 25 Aug 2011 12:08:29 +0200
Subject: gs: Eliminate use of deprecated regexp module
---
lib/gs/src/gstk_editor.erl | 4 ++--
lib/gs/src/gstk_generic.erl | 2 +-
lib/gs/src/gstk_image.erl | 4 ++--
lib/gs/src/tool_utils.erl | 3 ++-
4 files changed, 7 insertions(+), 6 deletions(-)
(limited to 'lib')
diff --git a/lib/gs/src/gstk_editor.erl b/lib/gs/src/gstk_editor.erl
index 3e0c8240e4..8cc7021cc6 100644
--- a/lib/gs/src/gstk_editor.erl
+++ b/lib/gs/src/gstk_editor.erl
@@ -243,14 +243,14 @@ option(Option, Gstkid, _MainW, DB, Editor) ->
Editor, " ins ",AI," ", gstk:to_ascii(Text)]};
clear -> {c, [Editor, " delete 1.0 end"]};
{load, File} ->
- {ok, F2,_} = regexp:gsub(File, [92,92], "/"),
+ F2 = re:replace(File, [92,92], "/", [global,{return,list}]),
case gstk:call(["ed_load ", Editor, " ", gstk:to_ascii(F2)]) of
{result, _} -> none;
{bad_result,Re} ->
{error,{no_such_file,editor,load,F2,Re}}
end;
{save, File} ->
- {ok, F2,_} = regexp:gsub(File, [92,92], "/"),
+ F2 = re:replace(File, [92,92], "/", [global,{return,list}]),
case gstk:call(["ed_save ",Editor," ",gstk:to_ascii(F2)]) of
{result, _} -> none;
{bad_result,Re} ->
diff --git a/lib/gs/src/gstk_generic.erl b/lib/gs/src/gstk_generic.erl
index 3ddb69efc5..2cc6c4c2d3 100644
--- a/lib/gs/src/gstk_generic.erl
+++ b/lib/gs/src/gstk_generic.erl
@@ -414,7 +414,7 @@ gen_font(_Opt,Gstkid,_TkW,DB,_ExtraArg) ->
gen_label({text,Text},Opts,Gstkid,TkW,DB,ExtraArg,S,P,C) ->
out_opts(Opts,Gstkid,TkW,DB,ExtraArg,[" -text ", gstk:to_ascii(Text), " -bi {}"|S],P,C);
gen_label({image,Img},Opts,Gstkid,TkW,DB,ExtraArg,S,P,C) ->
- {ok, I2,_} = regexp:gsub(Img, [92,92], "/"),
+ I2 = re:replace(Img, [92,92], "/", [global,{return,list}]),
out_opts(Opts,Gstkid,TkW,DB,ExtraArg,[" -bi \"@", I2, "\" -text {}"|S],P,C).
gen_label(_Opt,_Gstkid,TkW,_DB,_ExtraArg) ->
case gstk:call([TkW, " cg -bit"]) of
diff --git a/lib/gs/src/gstk_image.erl b/lib/gs/src/gstk_image.erl
index 5ad37cf6de..9adbe42386 100644
--- a/lib/gs/src/gstk_image.erl
+++ b/lib/gs/src/gstk_image.erl
@@ -227,10 +227,10 @@ event(DB, Gstkid, Etype, Edata, Args) ->
option(Option, Gstkid, _Canvas, _DB, _AItem) ->
case Option of
{bitmap, Bitmap} ->
- {ok, BF,_} = regexp:gsub(Bitmap, [92,92], "/"),
+ BF = re:replace(Bitmap, [92,92], "/", [global,{return,list}]),
{s, [" -bi @", BF]};
{load_gif, File} ->
- {ok, F2,_} = regexp:gsub(File, [92,92], "/"),
+ F2 = re:replace(File, [92,92], "/", [global,{return,list}]),
{Photo_item, _item} = Gstkid#gstkid.widget_data,
{c,[Photo_item, " configure -file ", gstk:to_ascii(F2)]};
{pix_val, {Coords,Color}} ->
diff --git a/lib/gs/src/tool_utils.erl b/lib/gs/src/tool_utils.erl
index b07e92c4f0..d09af5f22f 100644
--- a/lib/gs/src/tool_utils.erl
+++ b/lib/gs/src/tool_utils.erl
@@ -98,7 +98,8 @@ open_help_default(Parent, File) ->
_Else -> "netscape -remote \"openURL(file:" ++ File ++ ")\""
end;
{win32,_AnyType} ->
- "netscape.exe -h " ++ regexp:gsub(File,"\\\\","/");
+ "netscape.exe -h " ++
+ re:replace(File,"\\\\","/",[global,{return,list}]);
_Other ->
unknown
end;
--
cgit v1.2.3
From b22894a3b82675d88b06428fe38eceeef8ca3870 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 25 Aug 2011 14:02:43 +0200
Subject: tv: Eliminate use of deprecated regexp module
---
lib/tv/src/tv_db_search.erl | 32 +++++---------------------------
1 file changed, 5 insertions(+), 27 deletions(-)
(limited to 'lib')
diff --git a/lib/tv/src/tv_db_search.erl b/lib/tv/src/tv_db_search.erl
index edd3c188e2..7634bc63b6 100644
--- a/lib/tv/src/tv_db_search.erl
+++ b/lib/tv/src/tv_db_search.erl
@@ -244,10 +244,10 @@ get_entry_text() ->
string_to_regexp(Str) ->
- case regexp:parse(Str) of
+ case re:compile(Str) of
{ok, RegExp} ->
{ok, RegExp};
- _Error ->
+ {error, _Error} ->
case get(error_msg_mode) of
normal ->
{error, {not_a_regexp, "Please enter a regular expression!"}};
@@ -410,33 +410,11 @@ search_for_regexp(Pattern, Elem, ListAsStr) ->
lists:flatten(tv_io_lib:write(Elem))
end,
- case regexp:first_match(ListToSearch, Pattern) of
- {match, _, _} ->
+ case re:run(ListToSearch, Pattern, [{capture,none}]) of
+ match ->
found;
- _Other ->
+ nomatch ->
not_found
- %% The code below shall be used instead if it is desired to
- %% compare each *element* in the tuples to the regular expression,
- %% i.e., treat each element as a new line/string.
- %% The difference is most easily explained through an example:
- %% If we treat each tuple as a new line/string, the regular expression
- %% "^{win" will match the string "{win, 1, 2, 3}", but not the string
- %% "{1, {win,2}}".
- %% If we treat each element as a new line/string, the RE "^{win" will match
- %% both strings above.
-
- %% SearchList = tuple_to_list(Elem),
- %% case lists:dropwhile(
- %% fun(H) ->
- %% nomatch == regexp:first_match(lists:flatten(io_lib:write(H)),
- %% Pattern)
- %% end,
- %% SearchList) of
- %% [] ->
- %% not_found;
- %% _AnyList ->
- %% found
- %% end
end.
--
cgit v1.2.3
From b5599c98a4e38c029b1cf00f46a2323c83f59f1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Fri, 26 Aug 2011 09:08:52 +0200
Subject: appmon: Eliminate use of deprecated regexp module
---
lib/appmon/src/appmon_web.erl | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'lib')
diff --git a/lib/appmon/src/appmon_web.erl b/lib/appmon/src/appmon_web.erl
index fb7144246c..7c0451c3c3 100644
--- a/lib/appmon/src/appmon_web.erl
+++ b/lib/appmon/src/appmon_web.erl
@@ -578,9 +578,9 @@ htmlify_pid([],New)->
%% the HTTP protocol %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
urlify_pid(Pid) ->
- case regexp:first_match(Pid,"[<].*[>]") of
- {match,Start,Len}->
- "%3C"++string:substr(Pid,Start+1,Len-2)++"%3E";
+ case re:run(Pid,"[<](.*)[>]",[{capture,all_but_first,list}]) of
+ {match,[PidStr]}->
+ "%3C"++PidStr++"%3E";
_->
Pid
end.
--
cgit v1.2.3
From a527bdefdd7232a4f9d180d097dbec383542f245 Mon Sep 17 00:00:00 2001
From: Siri Hansen
Date: Fri, 21 Oct 2011 11:06:18 +0200
Subject: Cleanup after testcases in ttb_SUITE
Slave nodes were earlier stopped inside each test case. If a test case
failed before this point, a slave node would survive and it might
interfere with the next test case causing multiple failures. This
commit moves the stopping of slave nodes out to a separate function
for each test case - called during end_per_testcase.
A minor correction is also done in ttb:ensure_no_overloaded_nodes -
the reply message sent back from the ttb process is tagged so only the
expected message will be picked from the message queue. Otherwise, for
instance nodedown messages from the monitoring of slave nodes (by the
test cases) could be received here.
Finally, the sleep timer when waiting for trace messages to arrive
over tcp/ip is extended a bit since test cases sometimes failed with
missing trace messages here.
---
lib/observer/src/ttb.erl | 6 +-
lib/observer/test/ttb_SUITE.erl | 284 ++++++++++++++++++++++++---------------
lib/observer/test/ttb_helper.erl | 2 +-
3 files changed, 180 insertions(+), 112 deletions(-)
(limited to 'lib')
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 072aa165e7..ce88de6852 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. 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
@@ -519,7 +519,7 @@ ensure_no_overloaded_nodes() ->
[];
_ ->
?MODULE ! {get_overloaded, self()},
- receive O -> O end
+ receive {overloaded,O} -> O end
end,
case Overloaded of
[] -> ok;
@@ -715,7 +715,7 @@ loop(NodeInfo, SessionInfo) ->
lists:keydelete(overloaded, 1, SessionInfo)},
loop(NodeInfo, [{overloaded, [Node|Overloaded]} | SI]);
{get_overloaded, Pid} ->
- Pid ! proplists:get_value(overloaded, SessionInfo, []),
+ Pid ! {overloaded,proplists:get_value(overloaded, SessionInfo, [])},
loop(NodeInfo, SessionInfo);
trace_started ->
case proplists:get_value(timer, SessionInfo) of
diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl
index 1fd8b4c892..f8647bd087 100644
--- a/lib/observer/test/ttb_SUITE.erl
+++ b/lib/observer/test/ttb_SUITE.erl
@@ -46,9 +46,12 @@ init_per_testcase(_Case, Config) ->
os:cmd("rm -rf ttb_last_config"),
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
+end_per_testcase(Case, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
+ try apply(?MODULE,Case,[cleanup,Config])
+ catch error:undef -> ok
+ end,
ok.
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -119,7 +122,6 @@ file(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(filename:join(Privdir,atom_to_list(Node)++"-file")),
?line ok = ttb:format(filename:join(Privdir,
atom_to_list(OtherNode)++"-file")),
@@ -130,6 +132,9 @@ file(Config) when is_list(Config) ->
end_of_trace] = flush(),
ok.
+file(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
file_no_pi(suite) ->
[];
file_no_pi(doc) ->
@@ -150,7 +155,6 @@ file_no_pi(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(filename:join(Privdir,atom_to_list(Node)++"-file")),
?line ok = ttb:format(filename:join(Privdir,
atom_to_list(OtherNode)++"-file")),
@@ -163,6 +167,10 @@ file_no_pi(Config) when is_list(Config) ->
?line true = is_pid(RemoteProc),
ok.
+file_no_pi(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
+
file_fetch(suite) ->
[];
file_fetch(doc) ->
@@ -201,7 +209,6 @@ file_fetch(Config) when is_list(Config) ->
?line [StoreString] = ?t:capture_get(),
?line UploadDir =
lists:last(string:tokens(lists:flatten(StoreString),"$ \n")),
- ?line ?t:stop_node(OtherNode),
%% check that files are no longer in original directories...
?line ok = check_gone(ThisDir,atom_to_list(Node)++"-file_fetch"),
@@ -228,6 +235,10 @@ file_fetch(Config) when is_list(Config) ->
?line ok = file:set_cwd(Cwd),
ok.
+file_fetch(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
+
wrap(suite) ->
[];
wrap(doc) ->
@@ -251,7 +262,6 @@ wrap(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(filename:join(Privdir,
atom_to_list(Node)++"-wrap.*.wrp")),
?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}},
@@ -280,6 +290,9 @@ wrap(Config) when is_list(Config) ->
end_of_trace] = flush(),
ok.
+wrap(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
wrap_merge(suite) ->
[];
wrap_merge(doc) ->
@@ -303,7 +316,6 @@ wrap_merge(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(
[filename:join(Privdir,
atom_to_list(Node)++"-wrap_merge.*.wrp"),
@@ -318,6 +330,9 @@ wrap_merge(Config) when is_list(Config) ->
end_of_trace] = flush(),
ok.
+wrap_merge(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
wrap_merge_fetch_format(suite) ->
[];
@@ -348,7 +363,6 @@ wrap_merge_fetch_format(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([format]),
- ?line ?t:stop_node(OtherNode),
?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},_},
{trace_ts,{_,_,OtherNode},call,{?MODULE,foo,[]},_},
{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},_},
@@ -360,6 +374,8 @@ wrap_merge_fetch_format(Config) when is_list(Config) ->
?line ok = file:set_cwd(Cwd),
ok.
+wrap_merge_fetch_format(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
write_config1(suite) ->
[];
@@ -384,7 +400,6 @@ write_config1(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(
[filename:join(Privdir,
atom_to_list(Node)++"-write_config1"),
@@ -410,6 +425,10 @@ write_config1(Config) when is_list(Config) ->
end,
ok.
+
+write_config1(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
write_config2(suite) ->
[];
write_config2(doc) ->
@@ -433,7 +452,6 @@ write_config2(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(
[filename:join(Privdir,
atom_to_list(Node)++"-write_config2"),
@@ -459,6 +477,10 @@ write_config2(Config) when is_list(Config) ->
end,
ok.
+write_config2(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
+
write_config3(suite) ->
[];
write_config3(doc) ->
@@ -496,7 +518,6 @@ write_config3(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(
[filename:join(Privdir,
atom_to_list(Node)++"-write_config3"),
@@ -522,6 +543,10 @@ write_config3(Config) when is_list(Config) ->
end,
ok.
+write_config3(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
+
history(suite) ->
[];
@@ -552,14 +577,15 @@ history(Config) when is_list(Config) ->
?line ok = ttb:run_history([3,4]),
?line ?MODULE:foo(),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(
[filename:join(Privdir,atom_to_list(Node)++"-history"),
filename:join(Privdir,atom_to_list(OtherNode)++"-history")]),
?line [{trace_ts,{S,_,Node},call,{?MODULE,foo,[]},{_,_,_}},
end_of_trace] = flush(),
ok.
-
+
+history(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
write_trace_info(suite) ->
@@ -582,7 +608,6 @@ write_trace_info(Config) when is_list(Config) ->
?line ?MODULE:foo(),
?line rpc:call(OtherNode,?MODULE,foo,[]),
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(OtherNode),
?line ok = ttb:format(
[filename:join(Privdir,atom_to_list(Node)++"-write_trace_info"),
filename:join(Privdir,
@@ -594,6 +619,9 @@ write_trace_info(Config) when is_list(Config) ->
ok.
+write_trace_info(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
seq_trace(suite) ->
[];
@@ -680,7 +708,6 @@ diskless(Config) when is_list(Config) ->
?line rpc:call(RemoteNode,?MODULE,foo,[]),
?line timer:sleep(500), % needed for the IP port to flush
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(RemoteNode),
?line ok = ttb:format(filename:join(Privdir,
atom_to_list(RemoteNode)++"-diskless")),
@@ -688,6 +715,9 @@ diskless(Config) when is_list(Config) ->
end_of_trace] = flush(),
ok.
+diskless(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
diskless_wrap(suite) ->
[];
diskless_wrap(doc) ->
@@ -707,7 +737,6 @@ diskless_wrap(Config) when is_list(Config) ->
?line rpc:call(RemoteNode,?MODULE,foo,[]),
?line timer:sleep(500), % needed for the IP port to flush
?line ttb:stop([nofetch]),
- ?line ?t:stop_node(RemoteNode),
?line ok = ttb:format(filename:join(Privdir,
atom_to_list(RemoteNode)++"-diskless.*.wrp")),
@@ -715,6 +744,9 @@ diskless_wrap(Config) when is_list(Config) ->
end_of_trace] = flush(),
ok.
+diskless_wrap(cleanup,_Config) ->
+ ?line ?t:stop_node(ttb_helper:get_node(node2)).
+
otp_4967_1(suite) ->
[];
otp_4967_1(doc) ->
@@ -767,8 +799,6 @@ otp_4967_2(Config) when is_list(Config) ->
ok.
-
-
myhandler(_Fd,Trace,_,Relay) ->
Relay ! Trace,
Relay.
@@ -872,6 +902,27 @@ start_client_and_server() ->
?line ttb_helper:clear(),
{ServerNode, ClientNode}.
+stop_client_and_server() ->
+ ClientNode = ttb_helper:get_node(client),
+ ServerNode = ttb_helper:get_node(server),
+ erlang:monitor_node(ClientNode,true),
+ erlang:monitor_node(ServerNode,true),
+ ?line ?t:stop_node(ClientNode),
+ ?line ?t:stop_node(ServerNode),
+ wait_for_client_and_server_stop(ClientNode,ServerNode).
+
+wait_for_client_and_server_stop(undefined,undefined) ->
+ ok;
+wait_for_client_and_server_stop(ClientNode,ServerNode) ->
+ receive
+ {nodedown,ClientNode} ->
+ erlang:monitor_node(ClientNode,false),
+ wait_for_client_and_server_stop(undefined,ServerNode);
+ {nodedown,ServerNode} ->
+ erlang:monitor_node(ServerNode,false),
+ wait_for_client_and_server_stop(ClientNode,undefined)
+ end.
+
begin_trace(ServerNode, ClientNode, Dest) ->
?line {ok, _} =
ttb:tracer([ServerNode,ClientNode],[{file, Dest}]),
@@ -912,10 +963,12 @@ fetch_when_no_option_given(Config) when is_list(Config) ->
begin_trace(ServerNode, ClientNode, ?FNAME),
?line ttb_helper:msgs(4),
?line stopped = ttb:stop(),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line [_] = filelib:wildcard(filename:join(Privdir,"ttb_upload_temptest*")).
+fetch_when_no_option_given(cleanup,_Config) ->
+ ?line stop_client_and_server().
+
+
basic_ttb_run_ip_port(suite) ->
[];
basic_ttb_run_ip_port(doc) ->
@@ -924,9 +977,9 @@ basic_ttb_run_ip_port(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line check_size(1, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode),
?line check_size(2, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode),
- ?line check_size(10, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line check_size(10, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode).
+basic_ttb_run_ip_port(cleanup,_Config) ->
+ ?line stop_client_and_server().
basic_ttb_run_file_port(suite) ->
[];
@@ -936,9 +989,9 @@ basic_ttb_run_file_port(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line check_size(1, ?FNAME, ?OUTPUT, ServerNode, ClientNode),
?line check_size(2, ?FNAME, ?OUTPUT, ServerNode, ClientNode),
- ?line check_size(10, ?FNAME, ?OUTPUT, ServerNode, ClientNode),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line check_size(10, ?FNAME, ?OUTPUT, ServerNode, ClientNode).
+basic_ttb_run_file_port(cleanup,_Config) ->
+ ?line stop_client_and_server().
return_fetch_dir_implies_fetch(suite) ->
[];
@@ -948,9 +1001,9 @@ return_fetch_dir_implies_fetch(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line begin_trace(ServerNode, ClientNode, ?FNAME),
?line ttb_helper:msgs(2),
- ?line {_,_} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line {_,_} = ttb:stop([return_fetch_dir]).
+return_fetch_dir_implies_fetch(cleanup,_Config) ->
+ ?line stop_client_and_server().
logfile_name_in_fetch_dir(suite) ->
[];
@@ -960,11 +1013,11 @@ logfile_name_in_fetch_dir(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line begin_trace(ServerNode, ClientNode, {local, ?FNAME}),
?line {_,Dir} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line P1 = lists:nth(3, string:tokens(filename:basename(Dir), "_")),
?line P2 = hd(string:tokens(P1, "-")),
?line _File = P2.
+logfile_name_in_fetch_dir(cleanup,_Config) ->
+ ?line stop_client_and_server().
upload_to_my_logdir(suite) ->
[];
@@ -975,10 +1028,10 @@ upload_to_my_logdir(Config) when is_list(Config) ->
?line {ok, _} =
ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]),
?line {stopped,_} = ttb:stop([return_fetch_dir, {fetch_dir, ?DIRNAME}]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line true = filelib:is_file(?DIRNAME),
?line [] = filelib:wildcard("ttb_upload_"++?FNAME).
+upload_to_my_logdir(cleanup,_Config) ->
+ ?line stop_client_and_server().
upload_to_my_existing_logdir(suite) ->
[];
@@ -990,9 +1043,9 @@ upload_to_my_existing_logdir(Config) when is_list(Config) ->
?line {ok, _} =
ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]),
?line {error,_,_} = (catch ttb:stop([return_fetch_dir, {fetch_dir, ?DIRNAME}])),
- ?line {stopped,_} = ttb:stop(return_fetch_dir),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line {stopped,_} = ttb:stop(return_fetch_dir).
+upload_to_my_existing_logdir(cleanup,_Config) ->
+ ?line stop_client_and_server().
fetch_with_options_not_as_list(suite) ->
[];
@@ -1003,11 +1056,11 @@ fetch_with_options_not_as_list(Config) when is_list(Config) ->
?line {ok, _} =
ttb:tracer([ServerNode,ClientNode],[{file, ?FNAME}]),
?line {stopped, D} = ttb:stop(return_fetch_dir),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line false = filelib:is_file(?OUTPUT),
?line ttb:format(D, {out, ?OUTPUT}),
?line true = filelib:is_file(?OUTPUT).
+fetch_with_options_not_as_list(cleanup,_Config) ->
+ ?line stop_client_and_server().
error_when_formatting_multiple_files_4393(suite) ->
[];
@@ -1018,11 +1071,11 @@ error_when_formatting_multiple_files_4393(Config) when is_list(Config) ->
?line begin_trace(ServerNode, ClientNode, ?FNAME),
?line ttb_helper:msgs(2),
?line {_, Dir} = ttb:stop(return_fetch_dir),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
- ?line Files = [filename:join(Dir, atom_to_list(ttb_helper:get_node(server)) ++ "-" ++ ?FNAME),
- filename:join(Dir, atom_to_list(ttb_helper:get_node(client)) ++ "-" ++ ?FNAME)],
+ ?line Files = [filename:join(Dir, atom_to_list(ServerNode) ++ "-" ++ ?FNAME),
+ filename:join(Dir, atom_to_list(ClientNode) ++ "-" ++ ?FNAME)],
?line ok = ttb:format(Files).
+error_when_formatting_multiple_files_4393(cleanup,_Config) ->
+ ?line stop_client_and_server().
format_on_trace_stop(suite) ->
[];
@@ -1034,10 +1087,10 @@ format_on_trace_stop(Config) when is_list(Config) ->
?line ttb_helper:msgs_ip(2),
?line file:delete("HANDLER_OK"),
?line {_,_} = ttb:stop([fetch, return_fetch_dir, {format, {handler, marking_call_handler()}}]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line true = filelib:is_file("HANDLER_OK"),
?line ok = file:delete("HANDLER_OK").
+format_on_trace_stop(cleanup,_Config) ->
+ ?line stop_client_and_server().
%% The following three tests are for the issue "fixes fetch fail when nodes on the same host
%% have different cwd"
@@ -1050,9 +1103,9 @@ trace_to_remote_files_on_localhost_with_different_pwd(Config) when is_list(Confi
?line ok = file:set_cwd(".."),
?line {ServerNode, ClientNode} = start_client_and_server(),
?line check_size(2, ?FNAME, ?OUTPUT, ServerNode, ClientNode),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ok = file:set_cwd(OldDir).
+trace_to_remote_files_on_localhost_with_different_pwd(cleanup,_Config) ->
+ ?line stop_client_and_server().
trace_to_local_files_on_localhost_with_different_pwd(suite) ->
[];
@@ -1063,9 +1116,9 @@ trace_to_local_files_on_localhost_with_different_pwd(Config) when is_list(Config
?line ok = file:set_cwd(".."),
?line {ServerNode, ClientNode} = start_client_and_server(),
?line check_size(2, {local, ?FNAME}, ?OUTPUT, ServerNode, ClientNode),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ok = file:set_cwd(OldDir).
+trace_to_local_files_on_localhost_with_different_pwd(cleanup,_Config) ->
+ ?line stop_client_and_server().
trace_to_remote_files_on_localhost_with_different_pwd_abs(suite) ->
[];
@@ -1078,9 +1131,9 @@ trace_to_remote_files_on_localhost_with_different_pwd_abs(Config) when is_list(C
?line {ServerNode, ClientNode} = start_client_and_server(),
?line File = filename:join(Path, ?FNAME),
?line check_size(2, File, ?OUTPUT, ServerNode, ClientNode),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ok = file:set_cwd(OldDir).
+trace_to_remote_files_on_localhost_with_different_pwd_abs(cleanup,_Config) ->
+ ?line stop_client_and_server().
%% Trace is not affected by changes of cwd on control node or remote nodes during tracing
%% (three tests)
@@ -1100,9 +1153,9 @@ changing_cwd_on_control_node(Config) when is_list(Config) ->
?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
?line true = (2*(NumMsgs + 1) == length(Ret)),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ok = file:set_cwd(OldDir).
+changing_cwd_on_control_node(cleanup,_Config) ->
+ ?line stop_client_and_server().
changing_cwd_on_control_node_with_local_trace(suite) ->
[];
@@ -1120,9 +1173,9 @@ changing_cwd_on_control_node_with_local_trace(Config) when is_list(Config) ->
?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
?line true = (2*(NumMsgs + 1) == length(Ret)),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ok = file:set_cwd(OldDir).
+changing_cwd_on_control_node_with_local_trace(cleanup,_Config) ->
+ ?line stop_client_and_server().
changing_cwd_on_remote_node(suite) ->
[];
@@ -1138,9 +1191,9 @@ changing_cwd_on_remote_node(Config) when is_list(Config) ->
?line {_, D} = ttb:stop([fetch, return_fetch_dir]),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
- ?line true = (2*(NumMsgs + 1) == length(Ret)),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line true = (2*(NumMsgs + 1) == length(Ret)).
+changing_cwd_on_remote_node(cleanup,_Config) ->
+ ?line stop_client_and_server().
one_command_trace_setup(suite) ->
[];
@@ -1148,19 +1201,19 @@ one_command_trace_setup(doc) ->
["One command trace setup"];
one_command_trace_setup(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
- ?line ttb:start_trace([ttb_helper:get_node(client), ttb_helper:get_node(server)],
- [{server, received, '_', []},
- {client, put, 1, []},
- {client, get, '_', []}],
- {all, call},
- [{file, ?FNAME}]),
+ ?line ttb:start_trace([ClientNode, ServerNode],
+ [{server, received, '_', []},
+ {client, put, 1, []},
+ {client, get, '_', []}],
+ {all, call},
+ [{file, ?FNAME}]),
?line ttb_helper:msgs(2),
?line {_, D} = ttb:stop(return_fetch_dir),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, simple_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
?line 5 = length(Ret).
+one_command_trace_setup(cleanup,_Config) ->
+ ?line stop_client_and_server().
dbg_style_fetch(suite) ->
[];
@@ -1169,12 +1222,12 @@ dbg_style_fetch(doc) ->
dbg_style_fetch(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line DirSize = length(element(2, file:list_dir("."))),
- ?line ttb:start_trace([ttb_helper:get_node(client), ttb_helper:get_node(server)],
- [{server, received, '_', []},
- {client, put, 1, []},
- {client, get, '_', []}],
- {all, call},
- [{shell, only}]),
+ ?line ttb:start_trace([ClientNode, ServerNode],
+ [{server, received, '_', []},
+ {client, put, 1, []},
+ {client, get, '_', []}],
+ {all, call},
+ [{shell, only}]),
?line DirSize = length(element(2, file:list_dir("."))),
?line ttb_helper:msgs(2),
?line DirSize = length(element(2, file:list_dir("."))),
@@ -1182,15 +1235,15 @@ dbg_style_fetch(Config) when is_list(Config) ->
%%+1 -> ttb_last_trace
?line true = (DirSize + 1 == length(element(2, file:list_dir(".")))),
?line {ok,[{all, [{matched,_,_}, {matched,_,_}]}]} =
- ttb:start_trace([ttb_helper:get_node(client), ttb_helper:get_node(server)],
+ ttb:start_trace([ClientNode, ServerNode],
[{server, received, '_', []},
- {client, put, 1, []},
- {client, get, '_', []}],
+ {client, put, 1, []},
+ {client, get, '_', []}],
{all, call},
[{shell, only}]),
- ?line ttb:stop(),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line ttb:stop().
+dbg_style_fetch(cleanup,_Config) ->
+ ?line stop_client_and_server().
shell_tracing_init(suite) ->
[];
@@ -1198,19 +1251,19 @@ shell_tracing_init(doc) ->
["Shell tracing init"];
shell_tracing_init(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
- ?line ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)], shell),
+ ?line ttb:tracer([ClientNode, ServerNode], shell),
?line ttb:stop(),
- ?line ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)],
+ ?line ttb:tracer([ClientNode, ServerNode],
[{file, {local, ?FNAME}}, shell]),
?line ttb:stop(),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
- ?line local_client_required_on_shell_tracing = try ttb:tracer([ttb_helper:get_node(client), ttb_helper:get_node(server)],
- [{file, ?FNAME}, shell])
- catch
- exit:local_client_required_on_shell_tracing ->
- local_client_required_on_shell_tracing
- end.
+ ?line local_client_required_on_shell_tracing =
+ try ttb:tracer([ClientNode, ServerNode],[{file, ?FNAME}, shell])
+ catch
+ exit:local_client_required_on_shell_tracing ->
+ local_client_required_on_shell_tracing
+ end.
+shell_tracing_init(cleanup,_Config) ->
+ ?line stop_client_and_server().
only_one_state_for_format_handler(suite) ->
[];
@@ -1221,11 +1274,11 @@ only_one_state_for_format_handler(Config) when is_list(Config) ->
?line begin_trace_local(ServerNode, ClientNode, ?FNAME),
?line ttb_helper:msgs(2),
?line {_, D} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, counter_call_handler()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
?line [5] = Ret.
+only_one_state_for_format_handler(cleanup,_Config) ->
+ ?line stop_client_and_server().
only_one_state_with_default_format_handler(suite) ->
[];
@@ -1236,10 +1289,10 @@ only_one_state_with_default_format_handler(Config) when is_list(Config) ->
?line begin_trace_local(ServerNode, ClientNode, ?FNAME),
?line ttb_helper:msgs(2),
?line {_, D} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}]),
?line true = filelib:is_file(?OUTPUT).
+only_one_state_with_default_format_handler(cleanup,_Config) ->
+ ?line stop_client_and_server().
only_one_state_with_initial_format_handler(suite) ->
[];
@@ -1255,11 +1308,11 @@ only_one_state_with_initial_format_handler(Config) when is_list(Config) ->
?line ttb:tpl(client, get, []),
?line ttb_helper:msgs(2),
?line {_, D} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}]),
?line {ok, Ret} = file:consult(?OUTPUT),
?line [5] = Ret.
+only_one_state_with_initial_format_handler(cleanup,_Config) ->
+ ?line stop_client_and_server().
run_trace_with_shortcut(Shortcut, Ret, F) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
@@ -1271,8 +1324,7 @@ run_trace_with_shortcut(Shortcut, Ret, F) ->
?line {_, D} = ttb:stop([return_fetch_dir]),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, ret_caller_call_handler()}]),
?line {ok, Ret} =file:consult(?OUTPUT),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line stop_client_and_server().
fun_for(return) ->
{codestr, "fun(_) -> return_trace() end"};
@@ -1286,6 +1338,8 @@ run_trace_with_shortcut1(doc) ->
run_trace_with_shortcut1(Config) when is_list(Config) ->
?line run_trace_with_shortcut(caller, [ok,ok], tp),
?line run_trace_with_shortcut(caller, [ok,ok], tpl).
+run_trace_with_shortcut1(cleanup,_Config) ->
+ ?line stop_client_and_server().
run_trace_with_shortcut2(suite) ->
[];
@@ -1294,6 +1348,8 @@ run_trace_with_shortcut2(doc) ->
run_trace_with_shortcut2(Config) when is_list(Config) ->
?line run_trace_with_shortcut(return, [ok,ok], tp),
?line run_trace_with_shortcut(return, [ok,ok], tpl).
+run_trace_with_shortcut2(cleanup,_Config) ->
+ ?line stop_client_and_server().
run_trace_with_shortcut3(suite) ->
[];
@@ -1302,6 +1358,8 @@ run_trace_with_shortcut3(doc) ->
run_trace_with_shortcut3(Config) when is_list(Config) ->
?line run_trace_with_shortcut(fun_for(return), [ok,ok], tp),
?line run_trace_with_shortcut(fun_for(return), [ok,ok], tpl).
+run_trace_with_shortcut3(cleanup,_Config) ->
+ ?line stop_client_and_server().
run_trace_with_shortcut4(suite) ->
[];
@@ -1310,6 +1368,8 @@ run_trace_with_shortcut4(doc) ->
run_trace_with_shortcut4(Config) when is_list(Config) ->
?line run_trace_with_shortcut(fun_for(msg_false), [], tp),
?line run_trace_with_shortcut(fun_for(msg_false), [], tpl).
+run_trace_with_shortcut4(cleanup,_Config) ->
+ ?line stop_client_and_server().
cant_specify_local_and_flush(suite) ->
[];
@@ -1317,13 +1377,15 @@ cant_specify_local_and_flush(doc) ->
["Can't specify local and flush"];
cant_specify_local_and_flush(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
- ?line flush_unsupported_with_ip_trace_port = try ttb:tracer([ServerNode, ClientNode], [{flush, 1000}, {file, {local, ?FNAME}}])
- catch
- exit:flush_unsupported_with_ip_trace_port ->
- flush_unsupported_with_ip_trace_port
- end,
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode).
+ ?line flush_unsupported_with_ip_trace_port =
+ try ttb:tracer([ServerNode, ClientNode],
+ [{flush, 1000}, {file, {local, ?FNAME}}])
+ catch
+ exit:flush_unsupported_with_ip_trace_port ->
+ flush_unsupported_with_ip_trace_port
+ end.
+cant_specify_local_and_flush(cleanup,_Config) ->
+ ?line stop_client_and_server().
trace_sorted_by_default(suite) ->
[];
@@ -1334,11 +1396,11 @@ trace_sorted_by_default(Config) when is_list(Config) ->
?line begin_trace_local(ServerNode, ClientNode, ?FILE),
?line ttb_helper:msgs(2),
?line {_, D} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, false}]),
{ok, Ret} = file:consult(?OUTPUT),
?line [ClientNode,ServerNode,ClientNode,ServerNode,ServerNode] = Ret.
+trace_sorted_by_default(cleanup,_Config) ->
+ ?line stop_client_and_server().
disable_sorting(suite) ->
[];
@@ -1349,11 +1411,11 @@ disable_sorting(Config) when is_list(Config) ->
?line begin_trace_local(ServerNode, ClientNode, ?FILE),
?line ttb_helper:msgs(2),
?line {_, D} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ServerNode),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, node_call_handler()}, {disable_sort, true}]),
{ok, Ret} = file:consult(?OUTPUT),
?line [ClientNode,ClientNode,ServerNode,ServerNode,ServerNode] = Ret.
+disable_sorting(cleanup,_Config) ->
+ ?line stop_client_and_server().
%% -----------------------------------------------------------------------------
%% tests for autoresume of tracing
@@ -1367,6 +1429,8 @@ trace_resumed_after_node_restart(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line begin_trace_with_resume(ServerNode, ClientNode, ?FNAME),
?line logic(2,6,file).
+trace_resumed_after_node_restart(cleanup,_Config) ->
+ ?line stop_client_and_server().
trace_resumed_after_node_restart_ip(suite) ->
[];
@@ -1376,6 +1440,8 @@ trace_resumed_after_node_restart_ip(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line begin_trace_with_resume(ServerNode, ClientNode, {local, ?FNAME}),
?line logic(2,6,local).
+trace_resumed_after_node_restart_ip(cleanup,_Config) ->
+ ?line stop_client_and_server().
trace_resumed_after_node_restart_wrap(suite) ->
[];
@@ -1385,6 +1451,8 @@ trace_resumed_after_node_restart_wrap(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line begin_trace_with_resume(ServerNode, ClientNode, {wrap, ?FNAME, 10, 4}),
?line logic(1,4,file).
+trace_resumed_after_node_restart_wrap(cleanup,_Config) ->
+ ?line stop_client_and_server().
trace_resumed_after_node_restart_wrap_mult(suite) ->
[];
@@ -1394,18 +1462,18 @@ trace_resumed_after_node_restart_wrap_mult(Config) when is_list(Config) ->
?line {ServerNode, ClientNode} = start_client_and_server(),
?line begin_trace_with_resume(ServerNode, ClientNode, {wrap, ?FNAME, 10, 4}),
?line logic(20,8,file).
+trace_resumed_after_node_restart_wrap_mult(cleanup,_Config) ->
+ ?line stop_client_and_server().
logic(N, M, TracingType) ->
helper_msgs(N, TracingType),
?t:stop_node(ttb_helper:get_node(client)),
timer:sleep(2500),
- ?line {ok,ClientNode} = ?t:start_node(client,slave,[]),
+ ?line {ok,_ClientNode} = ?t:start_node(client,slave,[]),
?line ok = ttb_helper:c(code, add_paths, [code:get_path()]),
?line ttb_helper:c(client, init, []),
?line helper_msgs(N, TracingType),
?line {_, D} = ttb:stop([return_fetch_dir]),
- ?line ?t:stop_node(ttb_helper:get_node(server)),
- ?line ?t:stop_node(ClientNode),
?line ttb:format(D, [{out, ?OUTPUT}, {handler, ret_caller_call_handler2()}]),
?line {ok, Ret} = file:consult(?OUTPUT),
?line M = length(Ret).
diff --git a/lib/observer/test/ttb_helper.erl b/lib/observer/test/ttb_helper.erl
index 19fdc0e159..76b06cd3ce 100644
--- a/lib/observer/test/ttb_helper.erl
+++ b/lib/observer/test/ttb_helper.erl
@@ -70,7 +70,7 @@ msgs(N) ->
msgs_ip(N) ->
[c(client, put, [test_msg]) || _ <- lists:seq(1, N)],
s(server, received, [a,b]),
- timer:sleep(100). %% allow trace messages to arrive over tcp/ip
+ timer:sleep(200). %% allow trace messages to arrive over tcp/ip
run() ->
ttb({local, "A"}),
--
cgit v1.2.3
From ecdf8f06ccd9506084676e496222e0697730728d Mon Sep 17 00:00:00 2001
From: Siri Hansen
Date: Wed, 26 Oct 2011 10:07:05 +0200
Subject: Adjust ttb_SUITE to work better on windows
There is a problem with long paths on windows, which causes some of
the ttb logs in this suite not to be created. To go around this, the
original priv_dir from the Config is no longer used for writing the
logs. Instead a new priv_dir is created in the data_dir - which makes
the path much shorter.
There is also a problem caused by the lower resolution of the system
clock on windows. It makes the test cases for sorting trace messages
fail. To get around this a sleep of 2 ms is added in "appropriate
places", and also the messages sent between client and server when
creating the trace log for these test cases is now better synched.
The cleanup functions, which terminate slave nodes, was called in
end_per_testcase. However, it seems to be a bug in the test_server
which causes this to hang if the test case failed with a
timetrap_timeout. Workaround for this is to do the cleanup in
init_per_testcase instead - i.e. make sure that nodes that are to be
started by the test case do not already live when the test case
starts.
---
lib/observer/test/client.erl | 2 +-
lib/observer/test/server.erl | 3 +-
lib/observer/test/ttb_SUITE.erl | 86 +++++++++++++++++++++++++++++------------
3 files changed, 64 insertions(+), 27 deletions(-)
(limited to 'lib')
diff --git a/lib/observer/test/client.erl b/lib/observer/test/client.erl
index e756f9d6e8..90b72d3f8f 100644
--- a/lib/observer/test/client.erl
+++ b/lib/observer/test/client.erl
@@ -23,6 +23,6 @@ get() ->
put(Thing) ->
erlang:send({server,server_node()}, {put,self(),Thing}),
- receive ok -> ok
+ receive ok -> timer:sleep(2), ok
after 1000 -> no_reply
end.
diff --git a/lib/observer/test/server.erl b/lib/observer/test/server.erl
index c1b1fea562..f6d3542c96 100644
--- a/lib/observer/test/server.erl
+++ b/lib/observer/test/server.erl
@@ -16,8 +16,9 @@ stop() ->
loop(Data, Num) ->
receive
- {put,From,Ting} -> From ! ok,
+ {put,From,Ting} -> timer:sleep(2),
received(From,Ting),
+ From ! ok,
loop([Ting|Data], Num+1);
{get,From} -> From ! Data,
loop(Data, Num+1);
diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl
index f8647bd087..695d41b48a 100644
--- a/lib/observer/test/ttb_SUITE.erl
+++ b/lib/observer/test/ttb_SUITE.erl
@@ -37,21 +37,32 @@
-define(FNAME, "temptest").
-define(DIRNAME, "ddtemp").
-init_per_testcase(_Case, Config) ->
- ttb:stop(),
- os:cmd("rm -rf " ++ ?OUTPUT),
- os:cmd("rm -rf ttb_upload*"),
- os:cmd("rm -rf " ++ ?DIRNAME),
- os:cmd("rm -rf *@*"),
- os:cmd("rm -rf ttb_last_config"),
+init_per_testcase(Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
+ ttb:stop(),
+ rm(?OUTPUT),
+ [rm(Upload) || Upload<-filelib:wildcard("ttb_upload*")],
+ rm(?DIRNAME),
+ [rm(At) || At <- filelib:wildcard("*@*")],
+ rm("ttb_last_config"),
+ %% Workaround for bug(?) in test_server - if the test case fails
+ %% with a timetrap timeout, then end_per_testcase will run with
+ %% faulty group_leader - which in turn makes test_server:stop_node
+ %% hang (stop_node is called by most of the cleanup functions).
+ %% Therefore we do the cleanup before each testcase instead - this
+ %% is obviously not 100% correct, but it will at least make sure
+ %% that the nodes which are to be started in a test case at are
+ %% terminated.
+ try apply(?MODULE,Case,[cleanup,Config])
+ catch error:undef -> ok
+ end,
[{watchdog, Dog}|Config].
end_per_testcase(Case, Config) ->
+ %% try apply(?MODULE,Case,[cleanup,Config])
+ %% catch error:undef -> ok
+ %% end,
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
- try apply(?MODULE,Case,[cleanup,Config])
- catch error:undef -> ok
- end,
ok.
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -84,6 +95,7 @@ groups() ->
[].
init_per_suite(Config) ->
+ clean_priv_dir(Config),
Config.
end_per_suite(_Config) ->
@@ -105,7 +117,7 @@ file(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"file"),
?line {ok,[Node]} =
ttb:tracer(Node,[{file, File},
@@ -144,7 +156,7 @@ file_no_pi(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"file"),
?line {ok,[_,_]} =
ttb:tracer([Node,OtherNode],[{file, File},
@@ -180,7 +192,7 @@ file_fetch(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line ThisDir = filename:join(Privdir,this),
?line ok = file:make_dir(ThisDir),
?line OtherDir = filename:join(Privdir,other),
@@ -248,7 +260,7 @@ wrap(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"wrap"),
?line {ok,[_,_]} =
ttb:tracer([Node,OtherNode],[{file, {wrap,File,200,3}},
@@ -302,7 +314,7 @@ wrap_merge(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"wrap_merge"),
?line {ok,[_,_]} =
ttb:tracer([Node,OtherNode],[{file, {wrap,File,200,3}},
@@ -343,7 +355,7 @@ wrap_merge_fetch_format(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"wrap_merge_fetch_format"),
%% I'm setting priv_dir as cwd, so ttb_upload directory is created there
@@ -387,7 +399,7 @@ write_config1(Config) when is_list(Config) ->
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"write_config1"),
?line ok = ttb:write_config(File,
[{ttb,tracer,[[Node,OtherNode],
@@ -438,7 +450,7 @@ write_config2(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"write_config2"),
?line {ok,[_,_]} =
ttb:tracer([Node,OtherNode],[{file, File},
@@ -490,7 +502,7 @@ write_config3(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"write_config3"),
?line {ok,[_,_]} =
ttb:tracer([Node,OtherNode],[{file, File},
@@ -559,7 +571,7 @@ history(Config) when is_list(Config) ->
?line S = self(),
?line Nodes = [Node,OtherNode],
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"history"),
?line StartOpts = [{file, File},
{handler,{fun myhandler/4, S}}],
@@ -597,7 +609,7 @@ write_trace_info(Config) when is_list(Config) ->
?line {ok,OtherNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"write_trace_info"),
?line {ok,[_,_]} =
ttb:tracer([Node,OtherNode],[{file, File},
@@ -630,7 +642,7 @@ seq_trace(doc) ->
seq_trace(Config) when is_list(Config) ->
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"seq_trace"),
?line {ok,[Node]} = ttb:tracer(node(),[{file,File},
{handler,{fun myhandler/4, S}}]),
@@ -697,7 +709,7 @@ diskless(Config) when is_list(Config) ->
?line {ok,RemoteNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"diskless"),
?line {ok,[RemoteNode]} =
ttb:tracer([RemoteNode],[{file, {local, File}},
@@ -726,7 +738,7 @@ diskless_wrap(Config) when is_list(Config) ->
?line {ok,RemoteNode} = ?t:start_node(node2,slave,[]),
?line c:nl(?MODULE),
?line S = self(),
- ?line Privdir=?config(priv_dir, Config),
+ ?line Privdir=priv_dir(Config),
?line File = filename:join(Privdir,"diskless"),
?line {ok,[RemoteNode]} =
ttb:tracer([RemoteNode],[{file, {local, {wrap,File,200,3}}},
@@ -765,7 +777,7 @@ otp_4967_2(doc) ->
["OTP-4967: Trace message sent to {Name, Node}"];
otp_4967_2(Config) when is_list(Config) ->
io:format("1: ~p",[now()]),
- ?line Privdir = ?config(priv_dir,Config),
+ ?line Privdir = priv_dir(Config),
io:format("2: ~p",[now()]),
?line File = filename:join(Privdir,"otp_4967"),
io:format("3: ~p",[now()]),
@@ -1496,3 +1508,27 @@ helper_msgs(N, TracingType) ->
_ ->
ttb_helper:msgs(N)
end.
+
+priv_dir(Conf) ->
+ %% Due to problem with long paths on windows => creating a new
+ %% priv_dir under data_dir
+ Dir = filename:absname(filename:join(?config(data_dir, Conf),priv_dir)),
+ filelib:ensure_dir(filename:join(Dir,"*")),
+ Dir.
+
+clean_priv_dir(Config) ->
+ PrivDir = priv_dir(Config),
+ case filelib:is_dir(PrivDir) of
+ true -> rm(PrivDir);
+ false -> ok
+ end.
+
+rm(This) ->
+ case filelib:is_dir(This) of
+ true ->
+ {ok,Files} = file:list_dir(This),
+ [rm(filename:join(This,F)) || F <- Files],
+ file:del_dir(This);
+ false ->
+ file:delete(This)
+ end.
--
cgit v1.2.3
From 1d6a8d2c28f6a113ab123f74d8a647d14779baf1 Mon Sep 17 00:00:00 2001
From: Siri Hansen
Date: Thu, 27 Oct 2011 10:40:16 +0200
Subject: Close ip_to_file trace port in ttb:stop
This is a relay port opened from the IP trace client when tracing
diskless nodes. The port was not closed properly in ttb:stop, which
caused problems on windows since the file could not be moved to the
upload directory before the file descriptor was closed.
---
lib/observer/src/ttb.erl | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 072aa165e7..d2d9010c0f 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. 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
@@ -687,6 +687,11 @@ loop(NodeInfo, SessionInfo) ->
{MetaFile,undefined}
end,
loop(dict:store(Node,{AbsoluteMetaFile,MetaPid},NodeInfo), SessionInfo);
+ {ip_to_file_trace_port,Port,Sender} ->
+ Ports = proplists:get_value(ip_to_file_trace_ports, SessionInfo, []),
+ NewSessionInfo = [{ip_to_file_trace_ports,[Port|Ports]}|SessionInfo],
+ Sender ! {?MODULE,ok},
+ loop(NodeInfo, NewSessionInfo);
{get_nodes,Sender} ->
Sender ! {?MODULE,dict:fetch_keys(NodeInfo)},
loop(NodeInfo, SessionInfo);
@@ -736,7 +741,7 @@ loop(NodeInfo, SessionInfo) ->
end
end.
-do_stop(nofetch, Sender, NodeInfo, _) ->
+do_stop(nofetch, Sender, NodeInfo, SessionInfo) ->
write_config(?last_config, all),
dict:fold(
fun(Node,{_,MetaPid},_) ->
@@ -744,6 +749,7 @@ do_stop(nofetch, Sender, NodeInfo, _) ->
end,
ok,
NodeInfo),
+ stop_ip_to_file_trace_ports(SessionInfo),
dbg:stop_clear(),
ets:delete(?history_table),
Sender ! {?MODULE, stopped};
@@ -766,6 +772,7 @@ do_stop({FetchOrFormat, UserDir}, Sender, NodeInfo, SessionInfo) ->
end,
[],
NodeInfo),
+ stop_ip_to_file_trace_ports(SessionInfo),
dbg:stop_clear(),
AllNodes =
lists:map(
@@ -784,6 +791,19 @@ do_stop({FetchOrFormat, UserDir}, Sender, NodeInfo, SessionInfo) ->
end,
Sender ! {?MODULE,{stopped,Absname}}.
+stop_ip_to_file_trace_ports(SessionInfo) ->
+ lists:foreach(fun(Port) ->
+ case lists:member(Port,erlang:ports()) of
+ true ->
+ dbg:deliver_and_flush(Port),
+ erlang:port_close(Port);
+ false ->
+ ok
+ end
+ end,
+ proplists:get_value(ip_to_file_trace_ports,SessionInfo,[])).
+
+
make_node_dead(Node, NodeInfo, SessionInfo) ->
{MetaFile,_} = dict:fetch(Node, NodeInfo),
NewDeadNodes = [{Node, MetaFile} | proplists:get_value(dead_nodes, SessionInfo)],
@@ -1263,6 +1283,9 @@ ip_to_file(Trace, {_, only} = State) ->
ip_to_file(Trace,{{file,File}, ShellOutput}) ->
Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec
Port = Fun(),
+ %% Store the port so it can be properly closed
+ ?MODULE ! {ip_to_file_trace_port, Port, self()},
+ receive {?MODULE,ok} -> ok end,
case Trace of
{metadata, _, _} -> ok;
Trace -> show_trace(Trace, ShellOutput)
--
cgit v1.2.3
From 043335119b1625d04d87c33851749a9f742b024a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Tue, 9 Aug 2011 10:40:37 +0200
Subject: xmerl test suite: Eliminate use of deprecated regexp module
---
lib/xmerl/test/xmerl_test_lib.erl | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'lib')
diff --git a/lib/xmerl/test/xmerl_test_lib.erl b/lib/xmerl/test/xmerl_test_lib.erl
index a83956c076..e82ad283b2 100644
--- a/lib/xmerl/test/xmerl_test_lib.erl
+++ b/lib/xmerl/test/xmerl_test_lib.erl
@@ -87,6 +87,6 @@ keysearch_delete(Key,N,List) ->
%% the original data directory.
get_data_dir(Config) ->
- Data0 = ?config(data_dir, Config),
- {ok,Data,_} = regexp:sub(Data0, "xmerl_sax_std_SUITE", "xmerl_std_SUITE"),
- Data.
+ Data = ?config(data_dir, Config),
+ Opts = [{return,list}],
+ re:replace(Data, "xmerl_sax_std_SUITE", "xmerl_std_SUITE", Opts).
--
cgit v1.2.3
From f772969e11e114e2060730cb618b7b528bfc5172 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Tue, 9 Aug 2011 11:10:20 +0200
Subject: tools test suite: Eliminate use of deprecated regexp module
Also extend the test suite so that the changed code will be
executed.
---
lib/tools/test/eprof_SUITE_data/ed.script | 2 ++
lib/tools/test/eprof_SUITE_data/eed.erl | 39 +++++++++++++++++--------------
2 files changed, 23 insertions(+), 18 deletions(-)
(limited to 'lib')
diff --git a/lib/tools/test/eprof_SUITE_data/ed.script b/lib/tools/test/eprof_SUITE_data/ed.script
index 94531a9e98..fe1625bc50 100644
--- a/lib/tools/test/eprof_SUITE_data/ed.script
+++ b/lib/tools/test/eprof_SUITE_data/ed.script
@@ -1,5 +1,7 @@
H
r eed.erl
+1,$s/Created :/Skapad :/p
+/^cmd_line/,/^file/-1p
g/^[a-z][a-zA-Z_]*\(/i\
%%% -------------------------------------------------------------\
%%% A stupid function header.\
diff --git a/lib/tools/test/eprof_SUITE_data/eed.erl b/lib/tools/test/eprof_SUITE_data/eed.erl
index 0175abdd0e..75524a4f2c 100644
--- a/lib/tools/test/eprof_SUITE_data/eed.erl
+++ b/lib/tools/test/eprof_SUITE_data/eed.erl
@@ -238,8 +238,8 @@ scan_forward(End, Patt0, State) ->
scan_forward1(Dot+1, After, NewState, Rest).
scan_forward1(Linenum, [Line|Rest], State, RestCmd) ->
- case regexp:first_match(Line#line.contents, State#state.pattern) of
- {match, _, _} ->
+ case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of
+ match ->
{ok, Linenum, RestCmd, State};
nomatch ->
scan_forward1(Linenum+1, Rest, State, RestCmd)
@@ -259,8 +259,9 @@ scan_forward2(0, [], State, RestCmd) ->
scan_forward2(Linenum, [Line|Rest], State, RestCmd) ->
case scan_forward2(Linenum-1, Rest, State, RestCmd) of
false ->
- case regexp:first_match(Line#line.contents, State#state.pattern) of
- {match, _, _} ->
+ case re:run(Line#line.contents, State#state.pattern,
+ [{capture, none}]) of
+ match ->
{ok, Linenum, RestCmd, State};
nomatch ->
false
@@ -612,9 +613,10 @@ subst_command([Sep|Cmd0], [First, Last], St0) ->
St1 = save_for_undo(St0),
{ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1),
{ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1),
- {ok, Sub, Cmd3} = subst_check_gflag(Cmd2),
+ {ok, Opts, Cmd3} = subst_check_gflag(Cmd2),
St3 = check_trailing_p(Cmd3, St2),
- subst_command(Last-First+1, Sub, Replacement, move_to(First-1, St3), nomatch);
+ subst_command(Last-First+1, Opts, Replacement,
+ move_to(First-1, St3), nomatch);
subst_command([], _, _) ->
error(bad_delimiter).
@@ -622,21 +624,22 @@ subst_command(0, _, _, _, nomatch) ->
error(nomatch);
subst_command(0, _, _, _, StLast) when record(StLast, state) ->
StLast;
-subst_command(Left, Sub, Repl, St0, LastMatch) ->
+subst_command(Left, Opts, Repl, St0, LastMatch) ->
St1 = next_line(St0),
[Line|_] = St1#state.upto_dot,
- case regexp:Sub(Line#line.contents, St1#state.pattern, Repl) of
- {ok, _, 0} ->
- subst_command(Left-1, Sub, Repl, St1, LastMatch);
- {ok, NewContents, _} ->
+ Contents = Line#line.contents,
+ case re:replace(Contents, St1#state.pattern, Repl, Opts) of
+ Contents ->
+ subst_command(Left-1, Opts, Repl, St1, LastMatch);
+ NewContents ->
%% XXX This doesn't work with marks.
St2 = delete_current_line(St1),
St3 = insert_line(NewContents, St2),
- subst_command(Left-1, Sub, Repl, St3, St3)
+ subst_command(Left-1, Opts, Repl, St3, St3)
end.
-subst_check_gflag([$g|Cmd]) -> {ok, gsub, Cmd};
-subst_check_gflag(Cmd) -> {ok, sub, Cmd}.
+subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd};
+subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}.
%% u - undo
@@ -721,7 +724,7 @@ get_pattern(End, Cmd, State) ->
get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined ->
{ok, Rest, State};
get_pattern(End, [End|Rest], State, Result) ->
- case regexp:parse(lists:reverse(Result)) of
+ case re:compile(lists:reverse(Result)) of
{error, _} ->
error(bad_pattern);
{ok, Re} ->
@@ -765,9 +768,9 @@ match(State) when State#state.dot == 0 ->
match(State) ->
[Line|_] = State#state.upto_dot,
Re = State#state.pattern,
- case regexp:first_match(Line#line.contents, Re) of
- {match, _, _} -> true;
- nomatch -> false
+ case re:run(Line#line.contents, Re, [{capture, none}]) of
+ match -> true;
+ nomatch -> false
end.
skip_blanks([$ |Rest]) ->
--
cgit v1.2.3
From 3077842ea7098c51b0b0e5b4fad88fbe5e8e29a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Tue, 9 Aug 2011 11:21:57 +0200
Subject: tools test suite: Eliminate compilation warnings for the eed module
---
lib/tools/test/eprof_SUITE_data/eed.erl | 52 +++++++++++++++++----------------
1 file changed, 27 insertions(+), 25 deletions(-)
(limited to 'lib')
diff --git a/lib/tools/test/eprof_SUITE_data/eed.erl b/lib/tools/test/eprof_SUITE_data/eed.erl
index 75524a4f2c..520c5f3dd1 100644
--- a/lib/tools/test/eprof_SUITE_data/eed.erl
+++ b/lib/tools/test/eprof_SUITE_data/eed.erl
@@ -10,6 +10,8 @@
-export([edit/0, edit/1, file/1, cmd_line/1]).
+-compile({no_auto_import,[error/1]}).
+
-record(state, {dot = 0, % Line number of dot.
upto_dot = [], % Lines up to dot (reversed).
after_dot = [], % Lines after dot.
@@ -60,7 +62,7 @@ loop(St0) ->
ok;
{error, Reason} ->
loop(print_error(Reason, St1));
- St2 when record(St2, state) ->
+ St2 when is_record(St2, state) ->
loop(St2)
end.
@@ -68,7 +70,7 @@ command(Cmd, St) ->
case parse_command(Cmd, St) of
quit ->
quit;
- St1 when function(St1#state.print) ->
+ St1 when is_function(St1#state.print) ->
if
St1#state.dot /= 0 ->
print_current(St1);
@@ -76,7 +78,7 @@ command(Cmd, St) ->
ok
end,
St1#state{print=false};
- St1 when record(St1, state) ->
+ St1 when is_record(St1, state) ->
St1
end.
@@ -103,13 +105,13 @@ get_input([C|Rest], St, Result) ->
get_line1(Io, Prompt, Result) ->
get_line2(Io, io:get_line(Io, Prompt), Result).
-get_line2(Io, eof, []) ->
+get_line2(_Io, eof, []) ->
eof;
-get_line2(Io, eof, Result) ->
+get_line2(_Io, eof, Result) ->
lists:reverse(Result);
get_line2(Io, [$\\, $\n], Result) ->
get_line1(Io, '', [$\n|Result]);
-get_line2(Io, [$\n], Result) ->
+get_line2(_Io, [$\n], Result) ->
lists:reverse(Result, [$\n]);
get_line2(Io, [C|Rest], Result) ->
get_line2(Io, Rest, [C|Result]).
@@ -193,7 +195,7 @@ get_one1([$+|Rest], Sum, St) ->
get_one2({ok, 1, Rest}, 1, Sum, St);
get_one1([$-|Rest], Sum, St) ->
get_one2({ok, 1, Rest}, -1, Sum, St);
-get_one1(Cmd, false, St) ->
+get_one1(_Cmd, false, _St) ->
false;
get_one1(Cmd, Sum, St) ->
{ok, Sum, Cmd, St}.
@@ -222,13 +224,13 @@ get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z ->
false ->
{ok, 0, Rest, St}
end;
-get_address([$'|Rest], State) ->
+get_address([$'|_Rest], _State) ->
error(bad_mark);
get_address([$/|Rest], State) ->
scan_forward($/, Rest, State);
-get_address([$?|Rest], State) ->
+get_address([$?|_Rest], _State) ->
error(not_implemented);
-get_address(Cmd, St) ->
+get_address(_Cmd, _St) ->
false.
scan_forward(End, Patt0, State) ->
@@ -254,7 +256,7 @@ scan_forward1(_, [], State, RestCmd) ->
Other
end.
-scan_forward2(0, [], State, RestCmd) ->
+scan_forward2(0, [], _State, _RestCmd) ->
false;
scan_forward2(Linenum, [Line|Rest], State, RestCmd) ->
case scan_forward2(Linenum-1, Rest, State, RestCmd) of
@@ -297,7 +299,7 @@ parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot);
parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none);
parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all);
parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all);
-parse_cmd_char(_, Cont) -> error(bad_command).
+parse_cmd_char(_, _Cont) -> error(bad_command).
execute_command(Fun, NumLines, Def, State, Nums, Rest) ->
Lines = check_lines(NumLines, Def, Nums, State),
@@ -381,7 +383,7 @@ change_command(Rest, Lines, St0) ->
%% (.,.)d - delete lines
-delete_command(Rest, [0, Last], St) ->
+delete_command(_Rest, [0, _Last], _St) ->
error(bad_linenum);
delete_command(Rest, [First, Last], St0) ->
St1 = check_trailing_p(Rest, save_for_undo(St0)),
@@ -397,7 +399,7 @@ delete(Left, St0) ->
%% e file - replace buffer with new file
-enter_command(Name, [], St) when St#state.modified == true ->
+enter_command(_Name, [], St) when St#state.modified == true ->
error(buffer_modified);
enter_command(Name, [], St0) ->
enter_always_command(Name, [], St0).
@@ -440,7 +442,7 @@ mark(Sense, [First, Last], St0) ->
St1 = move_to(Last, St0),
mark1(Sense, First-1, St1).
-mark1(Sense, First, St) when St#state.dot == First ->
+mark1(_Sense, First, St) when St#state.dot == First ->
St;
mark1(Sense, First, St) ->
[Line|Prev] = St#state.upto_dot,
@@ -508,16 +510,16 @@ help_always_command([], [], St) ->
%% (.)i - insert text
-insert_command(Rest, [0], State) ->
+insert_command(_Rest, [0], _State) ->
error(bad_linenum);
insert_command(Rest, [Line], State) ->
append_command(Rest, [Line-1], State).
%% (.)kx - mark line
-mark_command(_, [0], St) ->
+mark_command(_, [0], _St) ->
error(bad_linenum);
-mark_command([Mark|Rest], [Line], St) when $a =< Mark, Mark =< $z ->
+mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z ->
error(not_implemented);
mark_command(_, _, _) ->
error(bad_mark).
@@ -529,12 +531,12 @@ list_command(Rest, Lines, St) ->
%% (.,.)m - move lines
-move_command(Cmd, [First, Last], St) ->
+move_command(_Cmd, [_First, _Last], _St) ->
error(not_implemented).
%% (.,.)t - copy lines
-transpose_command(Cmd, [First, Last], St) ->
+transpose_command(_Cmd, [_First, _Last], _St) ->
error(not_implemented).
%% (.,.)n - print lines with line numbers
@@ -605,9 +607,9 @@ read(After, Name, St0) ->
subst_command(_, [0, _], _) ->
error(bad_linenum);
-subst_command([$ |Cmd0], [First, Last], St0) ->
+subst_command([$ |_Cmd0], [_First, _Last], _St0) ->
error(bad_delimiter);
-subst_command([$\n|Cmd0], [First, Last], St0) ->
+subst_command([$\n|_Cmd0], [_First, _Last], _St0) ->
error(bad_delimiter);
subst_command([Sep|Cmd0], [First, Last], St0) ->
St1 = save_for_undo(St0),
@@ -622,7 +624,7 @@ subst_command([], _, _) ->
subst_command(0, _, _, _, nomatch) ->
error(nomatch);
-subst_command(0, _, _, _, StLast) when record(StLast, state) ->
+subst_command(0, _, _, _, StLast) when is_record(StLast, state) ->
StLast;
subst_command(Left, Opts, Repl, St0, LastMatch) ->
St1 = next_line(St0),
@@ -652,7 +654,7 @@ undo_command(_, _, _) ->
%% (1,$)w - write buffer to file
-write_command(Cmd, [First, Last], St) ->
+write_command(_Cmd, [_First, _Last], _St) ->
error(not_implemented).
@@ -757,7 +759,7 @@ check_trailing_p([$p], St) ->
St#state{print=fun(Line, _) -> io:put_chars(Line) end};
check_trailing_p([], State) ->
State;
-check_trailing_p(Other, State) ->
+check_trailing_p(_Other, _State) ->
error(garbage_after_command).
error(Reason) ->
--
cgit v1.2.3
From 69542150b2863a2b715e03682539ec68402cb0ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Fri, 26 Aug 2011 10:09:27 +0200
Subject: erl_interface tests: Eliminate use of deprecated regexp module
---
lib/erl_interface/test/all_SUITE_data/init_tc.erl | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
(limited to 'lib')
diff --git a/lib/erl_interface/test/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
index 8157d590fc..8db4667bf9 100644
--- a/lib/erl_interface/test/all_SUITE_data/init_tc.erl
+++ b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
@@ -40,23 +40,11 @@ run([]) ->
run1(Name) ->
CFile = Name ++ ".c",
{ok, Bin} = file:read_file(CFile),
- String = binary_to_list(Bin),
-
- %% This ConstPart stuff is because you can't retrieve part of a match.
- %% Long live Perl!
-
- ConstPart = "\nTESTCASE\\(",
- ConstPartLen = 10,
- {match, Matches} = regexp:matches(String, ConstPart++"[_a-zA-Z]*"),
- Cases = get_names(Matches, ConstPartLen, Bin, []),
+ RE = "\nTESTCASE\\(([_a-zA-Z]*)\\)",
+ {match, Cases0} = re:run(Bin, RE, [{capture,all_but_first,list},global]),
+ Cases = lists:concat(Cases0),
generate(Name, Cases).
-get_names([{Start, Length}|Rest], Skip, Bin, Result) ->
- Name = binary_to_list(Bin, Start+Skip, Start+Length-1),
- get_names(Rest, Skip, Bin, [Name|Result]);
-get_names([], _Skip, _Bin, Result) ->
- lists:reverse(Result).
-
generate(TcName, Cases) ->
Hrl = TcName ++ "_cases.hrl",
{ok, HrlFile} = file:open(Hrl, write),
--
cgit v1.2.3
From 3e8720728abe875683ad54fa4d93ba83df609f57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Wed, 26 Oct 2011 15:37:43 +0200
Subject: erl_tidy: Eliminate two references to 'regexp' in the documentation
---
lib/syntax_tools/src/erl_tidy.erl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 1cfdc7234a..09efc9c392 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -103,7 +103,7 @@ dir(Dir) ->
%% {regexp, string()}
%%
%% The value denotes a regular expression (see module
-%% `regexp'). Tidying will only be applied to those
+%% `re'). Tidying will only be applied to those
%% regular files whose names match this pattern. The default
%% value is `".*\\.erl$"', which matches normal
%% Erlang source file names.
@@ -124,7 +124,7 @@ dir(Dir) ->
%%
%% See the function {@link file/2} for further options.
%%
-%% @see //stdlib/regexp
+%% @see //stdlib/re
%% @see file/2
-record(dir, {follow_links = false :: boolean(),
--
cgit v1.2.3
From 688002a16af52b2348fbc9e285ef4df32ee0e5fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 20 Oct 2011 14:30:21 +0200
Subject: doc Makefiles: Eliminate DOCSUPPORT ifdefs
Some applications still have support for an ancient documentation
build system. Eliminate the DOCSUPPORT define in otp.mk.in and the
not taken arm of the ifdefs in the Makefiles.
---
lib/asn1/doc/src/Makefile | 90 -----------------------
lib/common_test/doc/src/Makefile | 15 ----
lib/cosEvent/doc/src/Makefile | 84 ----------------------
lib/cosEventDomain/doc/src/Makefile | 87 ----------------------
lib/cosFileTransfer/doc/src/Makefile | 87 ----------------------
lib/cosNotification/doc/src/Makefile | 86 ----------------------
lib/cosProperty/doc/src/Makefile | 98 -------------------------
lib/cosTime/doc/src/Makefile | 86 ----------------------
lib/cosTransactions/doc/src/Makefile | 86 ----------------------
lib/diameter/doc/src/Makefile | 6 --
lib/diameter/make/rules.mk.in | 2 -
lib/edoc/doc/Makefile | 9 ---
lib/ic/doc/src/Makefile | 103 --------------------------
lib/inets/doc/src/Makefile | 96 -------------------------
lib/megaco/doc/src/Makefile | 105 ---------------------------
lib/mnesia/doc/src/Makefile | 86 ----------------------
lib/odbc/doc/src/Makefile | 87 ----------------------
lib/orber/doc/src/Makefile | 89 -----------------------
lib/public_key/doc/src/Makefile | 86 ----------------------
lib/snmp/doc/src/Makefile | 136 -----------------------------------
lib/ssh/doc/src/Makefile | 85 ----------------------
lib/syntax_tools/doc/Makefile | 9 ---
lib/test_server/doc/src/Makefile | 6 --
lib/tv/doc/src/Makefile | 83 ---------------------
24 files changed, 1707 deletions(-)
(limited to 'lib')
diff --git a/lib/asn1/doc/src/Makefile b/lib/asn1/doc/src/Makefile
index d29225f6c9..566173837c 100644
--- a/lib/asn1/doc/src/Makefile
+++ b/lib/asn1/doc/src/Makefile
@@ -27,15 +27,6 @@ include ../../vsn.mk
VSN=$(ASN1_VSN)
APPLICATION=asn1
-
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -86,34 +77,10 @@ EXTRA_FILES = \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex) \
- $(BOOK_FILES:%.xml=%.sgml) part.tex
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -126,8 +93,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -142,32 +107,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f $(GEN_XML) errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -179,8 +118,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -191,31 +128,4 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(HTML_APPHISTORY) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-endif
-endif
-
-endif
-
release_spec:
-
-
-
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index 3ea6ae65d5..964f7c76c1 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -138,12 +138,6 @@ man: $(MAN6_FILES) $(MAN3_FILES) $(MAN1_FILES)
debug opt:
-#
-# checkout make.dep before generating new dependecies
-#
-#make_doc_depend: xml
-# docdepend > make.dep
-
clean clean_docs:
rm -f $(CT_XML_FILES)
rm -rf $(HTMLDIR)/*
@@ -176,12 +170,3 @@ release_docs_spec: docs
release_spec:
release_tests_spec:
-
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-include make.dep
-# DO NOT DELETE
-
-
diff --git a/lib/cosEvent/doc/src/Makefile b/lib/cosEvent/doc/src/Makefile
index 4b76a64b7d..db2f7e6da5 100644
--- a/lib/cosEvent/doc/src/Makefile
+++ b/lib/cosEvent/doc/src/Makefile
@@ -26,13 +26,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include ../../vsn.mk
VSN=$(COSEVENT_VSN)
APPLICATION=cosEvent
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
# ----------------------------------------------------
# Release directory specification
@@ -98,32 +91,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -136,8 +107,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -153,31 +122,6 @@ clean clean_docs:
rm -f errs core *~
rm -f $(JD_HTML) $(JD_PACK)
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -192,8 +136,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -204,30 +146,4 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
-
release_spec:
-
diff --git a/lib/cosEventDomain/doc/src/Makefile b/lib/cosEventDomain/doc/src/Makefile
index 6a0d3c353a..b2cdef278a 100644
--- a/lib/cosEventDomain/doc/src/Makefile
+++ b/lib/cosEventDomain/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN=$(COSEVENTDOMAIN_VSN)
APPLICATION=cosEventDomain
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -93,33 +85,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -132,8 +101,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -149,32 +116,6 @@ clean clean_docs:
rm -f errs core *~
rm -f $(JD_HTML) $(JD_PACK)
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -189,9 +130,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -201,30 +139,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/cosFileTransfer/doc/src/Makefile b/lib/cosFileTransfer/doc/src/Makefile
index 2286db43ff..e62738daba 100644
--- a/lib/cosFileTransfer/doc/src/Makefile
+++ b/lib/cosFileTransfer/doc/src/Makefile
@@ -26,14 +26,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include ../../vsn.mk
VSN=$(COSFILETRANSFER_VSN)
APPLICATION=cosFileTransfer
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
@@ -97,33 +89,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -136,8 +105,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -152,32 +119,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -192,9 +133,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -204,30 +142,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/cosNotification/doc/src/Makefile b/lib/cosNotification/doc/src/Makefile
index bfdd2f1f8c..2ead9aba7b 100644
--- a/lib/cosNotification/doc/src/Makefile
+++ b/lib/cosNotification/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN=$(COSNOTIFICATION_VSN)
APPLICATION=cosNotification
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -122,33 +114,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -161,8 +130,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -177,32 +144,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -217,8 +158,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -228,30 +167,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/cosProperty/doc/src/Makefile b/lib/cosProperty/doc/src/Makefile
index baf995d35e..d4c89ff44f 100644
--- a/lib/cosProperty/doc/src/Makefile
+++ b/lib/cosProperty/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN=$(COSPROPERTY_VSN)
APPLICATION=cosProperty
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -99,35 +91,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
MAN6_FILES = $(XML_REF6_FILES:%.xml=$(MAN6DIR)/%.6)
-
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_REF6_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -140,8 +107,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -156,42 +121,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-tex_users_guide: $(TEX_FILES_USERS_GUIDE)
-tex_ref_man: $(TEX_FILES_REF_MAN)
-tex: tex_users_guide tex_ref_man $(TEX_FILES_BOOK)
-
-$(DOCDIR)/latexlog: $(BOOK_FILES:%.xml=%.dvi)
- -fgrep -i "latex warning" $(BOOK_FILES:%.xml=%.log) >$(DOCDIR)/latexlog
-
-clean_tex:
- -rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
-
-clean:
- rm -f ../html/* $(MAN3_FILES) $(MAN6_FILES) $(TEX_FILES_USERS_GUIDE)
- rm -f *xmls_output *xmls_errs
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -206,8 +135,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -217,30 +144,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/cosTime/doc/src/Makefile b/lib/cosTime/doc/src/Makefile
index 83abc5e7c2..af418896aa 100644
--- a/lib/cosTime/doc/src/Makefile
+++ b/lib/cosTime/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN=$(COSTIME_VSN)
APPLICATION=cosTime
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -93,33 +85,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -132,8 +101,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -148,32 +115,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -188,8 +129,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -199,30 +138,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/cosTransactions/doc/src/Makefile b/lib/cosTransactions/doc/src/Makefile
index 1af9ed24b7..7d959cbc8f 100644
--- a/lib/cosTransactions/doc/src/Makefile
+++ b/lib/cosTransactions/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN=$(COSTRANSACTIONS_VSN)
APPLICATION=cosTransactions
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -97,33 +89,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -136,8 +105,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -152,32 +119,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -192,8 +133,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -203,30 +142,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index 1453138cb6..bc3e649e6b 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -126,8 +126,6 @@ debug opt:
info:
@echo "->Makefile<-"
@echo ""
- @echo "DOCSUPPORT = $(DOCSUPPORT)"
- @echo ""
@echo "INDEX_FILE = $(INDEX_FILE)"
@echo "INDEX_SRC = $(INDEX_SRC)"
@echo "INDEX_TARGET = $(INDEX_TARGET)"
@@ -141,10 +139,6 @@ info:
@echo ""
@echo "GIF_FILES = $(GIF_FILES)"
@echo ""
- @echo "TEX_FILES_USERS_GUIDE = $(TEX_FILES_USERS_GUIDE)"
- @echo "TEX_FILES_REF_MAN = $(TEX_FILES_REF_MAN)"
- @echo "TEX_FILES_BOOK = $(TEX_FILES_BOOK)"
- @echo ""
@echo "MAN1_FILES = $(MAN1_FILES)"
@echo "MAN3_FILES = $(MAN3_FILES)"
@echo "MAN4_FILES = $(MAN4_FILES)"
diff --git a/lib/diameter/make/rules.mk.in b/lib/diameter/make/rules.mk.in
index 6318f2bc9c..cd3c297d75 100644
--- a/lib/diameter/make/rules.mk.in
+++ b/lib/diameter/make/rules.mk.in
@@ -112,8 +112,6 @@ $(EBIN)/%.beam: $(ESRC)/%.erl
# ----------------------------------------------------
# export VSN
-# DOCSUPPORT = 1
-
# TOPDOCDIR=../../../../doc
DOCDIR = ..
diff --git a/lib/edoc/doc/Makefile b/lib/edoc/doc/Makefile
index c5f68b25d0..7a59809d9b 100644
--- a/lib/edoc/doc/Makefile
+++ b/lib/edoc/doc/Makefile
@@ -78,12 +78,3 @@ release_docs_spec: docs
release_spec:
-
-
-
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-#-include make.dep
-
-
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index f255183e1f..1e93578cb1 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -26,13 +26,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include ../../vsn.mk
VSN=$(IC_VSN)
APPLICATION=ic
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
# ----------------------------------------------------
# Java specific
@@ -96,32 +89,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
JAVA_SOURCE_FILES = \
Holder.java \
BooleanHolder.java \
@@ -209,8 +180,6 @@ JAVADOCFLAGS = \
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
ifneq (,$(JAVA))
docs: pdf html man $(JAVADOC_GENERATED_FILES)
else
@@ -229,34 +198,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html $(JAVADOC_GENERATED_FILES) gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
- rm -rf $(JAVA_OUT_DIR)
- rm -f JAVADOC-GENERATED
-
-endif
-
$(JAVADOC_GENERATED_FILES): JAVADOC-GENERATED
JAVADOC-GENERATED: $(JAVA_SOURCE_FILES:%=$(JAVA_SOURCE_DIR)/%)
@@ -278,8 +219,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -289,46 +228,4 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
-ifneq (,$(JAVA))
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/resources
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/com
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/com/ericsson
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/com/ericsson/otp
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/com/ericsson/otp/ic
- $(INSTALL_DATA) $(JAVADOC_INDEX_HTML_FILES) \
- $(RELSYSDIR)/doc/html/java
- $(INSTALL_DATA) $(JD_GIF_FILES) \
- $(RELSYSDIR)/doc/html/java/resources
- $(INSTALL_DATA) $(JAVADOC_PACK_HTML_FILES) \
- $(RELSYSDIR)/doc/html/java/com/ericsson/otp/ic
-endif
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
-
-
release_spec:
-
-
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index e4cb0c4e48..82f2a5829f 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -26,16 +26,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include ../../vsn.mk
VSN=$(INETS_VSN)
-
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -98,37 +88,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = \
- $(XML_PART_FILES:%.xml=%.tex) \
- $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_REF6_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-TOP_HTML_FILES =
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -141,8 +104,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
ldocs: local_docs
@@ -156,33 +117,6 @@ html: gifs $(HTML_REF_MAN_FILE)
clean clean_docs: clean_html clean_man clean_pdf
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(TOP_HTML_FILES) gifs
-
-clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
-
-clean: clean_tex clean_html clean_man
- rm -f *.xmls_output *.xmls_errs
- rm -f $(TOP_PDF_FILE)
- rm -f errs core *~
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -204,10 +138,7 @@ clean_man:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
- @echo "release_docs_spec(docs) when DOCSUPPORT=$DOCSUPPORT"
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
$(INSTALL_DIR) $(RELSYSDIR)/doc/html
@@ -215,33 +146,6 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- @echo "release_docs_spec(pdf)"
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- @echo "release_docs_spec(ps)"
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- @echo "release_docs_spec(docs)"
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile
index 4b3c117b20..f782afc3f6 100644
--- a/lib/megaco/doc/src/Makefile
+++ b/lib/megaco/doc/src/Makefile
@@ -26,14 +26,6 @@ include ../../vsn.mk
VSN=$(MEGACO_VSN)
APPLICATION=megaco
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -73,35 +65,10 @@ EXTRA_FILES = \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi $(APP_FILE)
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-
-TOP_HTML_FILES = $(INDEX_TARGET)
-
-endif
-
INDEX_FILE = index.html
INDEX_SRC = $(INDEX_FILE).src
INDEX_TARGET = $(DOCDIR)/$(INDEX_FILE)
@@ -131,8 +98,6 @@ $(HTMLDIR)/%.jpg: %.jpg
$(HTMLDIR)/%.png: %.png
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
ldocs: local_docs $(INDEX_TARGET)
@@ -147,41 +112,6 @@ clean clean_docs: clean_html clean_man
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html imgs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: imgs $(HTML_FILES) $(TOP_HTML_FILES)
-
-mhtml: html $(HTML_REF3_FILES) $(HTML_CHAPTER_FILES)
-
-clean: clean_html clean_man clean_pdf
- rm -f core *~
- rm -f *.aux *.cites *.citeshd *.dvi *.idx *.ilg *.ind
- rm -f *.indhd *.lof *.lofhd *.lot *.lothd *.otpdef
- rm -f *.otpuse *.terms *.termshd *.toc *.makeindexlog *.dvipslog
- rm -f *.bib *.bbl *.blg *.bibhd
-
-clean_pdf:
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f $(TEX_FILES_USERS_GUIDE)
- rm -f $(TEX_FILES_REF_MAN)
- rm -f $(TEX_FILES_BOOK)
-
-endif
-
clean_man:
rm -f $(MAN3DIR)/*
@@ -203,8 +133,6 @@ debug opt:
info:
@echo "->Makefile<-"
@echo ""
- @echo "DOCSUPPORT = $(DOCSUPPORT)"
- @echo ""
@echo "INDEX_FILE = $(INDEX_FILE)"
@echo "INDEX_SRC = $(INDEX_SRC)"
@echo "INDEX_TARGET = $(INDEX_TARGET)"
@@ -216,10 +144,6 @@ info:
@echo ""
@echo "IMG_FILES = $(IMG_FILES)"
@echo ""
- @echo "TEX_FILES_USERS_GUIDE = $(TEX_FILES_USERS_GUIDE)"
- @echo "TEX_FILES_REF_MAN = $(TEX_FILES_REF_MAN)"
- @echo "TEX_FILES_BOOK = $(TEX_FILES_BOOK)"
- @echo ""
@echo "MAN3_FILES = $(MAN3_FILES)"
@echo ""
@echo "HTML_FILES = $(HTML_FILES)"
@@ -236,8 +160,6 @@ info:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -250,33 +172,6 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/standard
$(INSTALL_DATA) $(STANDARDS) $(RELSYSDIR)/doc/standard
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(IMG_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(TOP_HTML_FILES) $(RELSYSDIR)/doc
- $(INSTALL_DIR) $(RELSYSDIR)/doc/standard
- $(INSTALL_DATA) $(STANDARDS) $(RELSYSDIR)/doc/standard
-endif
-endif
-
-endif
-
release_spec:
$(HTMLDIR)/megaco_architecture.html: megaco_architecture.xml
diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile
index f45b5137a3..f2e581f9d3 100644
--- a/lib/mnesia/doc/src/Makefile
+++ b/lib/mnesia/doc/src/Makefile
@@ -28,14 +28,6 @@ include ../../vsn.mk
VSN=$(MNESIA_VSN)
APPLICATION=mnesia
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -105,31 +97,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -142,8 +113,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -158,33 +127,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES)
-
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -199,8 +141,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -211,30 +151,4 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-endif
-endif
-
-endif
-
-
release_spec:
-
diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile
index e2f09733d0..3e648d854d 100644
--- a/lib/odbc/doc/src/Makefile
+++ b/lib/odbc/doc/src/Makefile
@@ -28,14 +28,6 @@ include ../../vsn.mk
VSN=$(ODBC_VSN)
APPLICATION=odbc
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -89,32 +81,10 @@ EXTRA_FILES = $(DEFAULT_GIF_FILES) \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -128,8 +98,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif # Copy them to ../html
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -144,32 +112,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%) # We depend just to copy them to ../html
@@ -182,8 +124,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -193,32 +133,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-endif
-endif
-
-endif
release_spec:
-
-
-
-
diff --git a/lib/orber/doc/src/Makefile b/lib/orber/doc/src/Makefile
index b8e26d5ba3..8a555cb408 100644
--- a/lib/orber/doc/src/Makefile
+++ b/lib/orber/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN=$(ORBER_VSN)
APPLICATION=orber
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -128,32 +120,10 @@ EXTRA_FILES = summary.html.src \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -167,8 +137,6 @@ $(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -184,35 +152,6 @@ clean clean_docs:
rm -f errs core *~
rm -f $(JD_HTML) $(JD_PACK)
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(INTERNAL_HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTMLDIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
- rm -f $(JD_HTML) $(JD_PACK)
-
-endif
-
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -224,9 +163,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -236,30 +172,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-
-endif
-endif
-
-endif
release_spec:
-
diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile
index afb17399da..9616a96195 100644
--- a/lib/public_key/doc/src/Makefile
+++ b/lib/public_key/doc/src/Makefile
@@ -28,14 +28,6 @@ include ../../vsn.mk
VSN=$(PUBLIC_KEY_VSN)
APPLICATION=public_key
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -79,33 +71,10 @@ EXTRA_FILES = \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_PART_FILES:%.xml=%.tex) \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = public_key-$(VSN).pdf
-TOP_PS_FILE = public_key-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -118,8 +87,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -134,33 +101,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ min_head.gif \
- $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -173,8 +113,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -184,30 +122,6 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-endif
-endif
-
-endif
-
release_spec:
info:
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index aa9431477c..df597c8ba7 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -27,14 +27,6 @@ include ../../vsn.mk
VSN = $(SNMP_VSN)
APPLICATION=snmp
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -88,40 +80,10 @@ MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
MAN7_FILES = $(MIB_FILES:$(MIBSDIR)/%.mib=$(MAN7DIR)/%.7)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = \
- $(XML_REF1_FILES:%.xml=%.tex) \
- $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_REF6_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_PART_FILES = $(XML_PART_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = snmp-$(VSN).pdf
-TOP_PS_FILE = snmp-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- @echo "building $(TOP_PDF_FILE)"
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- @echo "building $(TOP_PS_FILE)"
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-TOP_HTML_FILES = $(INDEX_TARGET)
-
-endif
-
INDEX_FILE = index.html
INDEX_SRC = $(INDEX_FILE).src
INDEX_TARGET = $(DOCDIR)/$(INDEX_FILE)
@@ -141,8 +103,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif # Copy them to ../html
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
ldocs: local_docs $(INDEX_TARGET)
@@ -157,47 +117,6 @@ html2: html $(INDEX_TARGET)
clean clean_docs: clean_html clean_man clean_pdf
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) $(TOP_HTML_FILES) gifs
-
-html2: gifs $(TOP_HTML_FILES) $(HTML_FILES) $(HTML_REF1_FILES) $(HTML_REF3_FILES) $(HTML_REF6_FILES) $(HTML_CHAP_FILES)
-
-clean: clean_tex clean_html clean_man clean_docs
-
-
-clean_tex:
- @echo "cleaning tex:"
- rm -f $(TEX_FILES_USERS_GUIDE)
- rm -f $(TEX_FILES_REF_MAN)
- rm -f $(TEX_PART_FILES)
- rm -f $(TEX_FILES_BOOK)
-
-clean_docs:
- @echo "cleaning docs:"
- rm -f $(TOP_PDF_FILE)
- rm -f $(TOP_PS_FILE)
- rm -f core $(LATEX_CLEAN)
-
-
-$(HTML_PART_FILES): notes.xml
-
-endif
-
$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk # Create top make file
sed -e 's;%VSN%;$(VSN);' $< > $@ # inserting version number
@@ -250,8 +169,6 @@ $(MAN1DIR)/snmpc.1: snmpc_cmd.xml
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -268,55 +185,13 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELEASE_PATH)/man/man7
$(INSTALL_DATA) $(MAN7DIR)/* $(RELEASE_PATH)/man/man7
-else
-
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man1
- $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
- $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man7
- $(INSTALL_DATA) $(MAN7_FILES) $(RELEASE_PATH)/man/man7
- $(INSTALL_DATA) $(TOP_HTML_FILES) \
- $(RELSYSDIR)/doc
-endif
-endif
-
-endif
-
release_spec:
-ifdef DOCSUPPORT
info: info_xml info_man info_html
@echo "MAN1DIR: $(MAN1DIR)"
@echo "MAN3DIR: $(MAN3DIR)"
@echo "MAN6DIR: $(MAN6DIR)"
@echo "MAN7DIR: $(MAN7DIR)"
-else
-info: info_xml info_man info_html info_tex
- @echo "DVI2PS = $(DVI2PS)"
- @echo "DVIPS_FLAGS = $(DVIPS_FLAGS)"
- @echo ""
- @echo "DISTILL = $(DISTILL)"
- @echo "DISTILL_FLAGS = $(DISTILL_FLAGS)"
-endif
info_man:
@echo "man files:"
@@ -362,14 +237,3 @@ info_html:
@echo "HTML_REF3_FILES = $(HTML_REF3_FILES)"
@echo "HTML_REF6_FILES = $(HTML_REF6_FILES)"
@echo "HTML_CHAP_FILES = $(HTML_CHAP_FILES)"
-
-info_tex:
- @echo "tex files:"
- @echo "TEX_FILES_USERS_GUIDE = $(TEX_FILES_USERS_GUIDE)"
- @echo "TEX_FILES_REF_MAN = $(TEX_FILES_REF_MAN)"
- @echo "TEX_PART_FILES = $(TEX_PART_FILES)"
- @echo "TEX_FILES_BOOK = $(TEX_FILES_BOOK)"
-
-ifndef DOCSUPPORT
-include depend.mk
-endif
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index c4d8d9901c..c97c99cf52 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -28,15 +28,6 @@ include ../../vsn.mk
VSN=$(SSH_VSN)
APPLICATION=ssh
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -76,33 +67,10 @@ EXTRA_FILES = \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
-TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -115,8 +83,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -131,32 +97,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES)
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
-
-endif
-
man: $(MAN3_FILES)
@@ -168,8 +108,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -179,28 +117,5 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-endif
-endif
-
-endif
release_spec:
diff --git a/lib/syntax_tools/doc/Makefile b/lib/syntax_tools/doc/Makefile
index 6afd16f669..d9981de880 100644
--- a/lib/syntax_tools/doc/Makefile
+++ b/lib/syntax_tools/doc/Makefile
@@ -78,12 +78,3 @@ release_docs_spec: docs
release_spec:
-
-
-
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-#-include make.dep
-
-
diff --git a/lib/test_server/doc/src/Makefile b/lib/test_server/doc/src/Makefile
index c7ba415e5b..f0be284324 100644
--- a/lib/test_server/doc/src/Makefile
+++ b/lib/test_server/doc/src/Makefile
@@ -133,9 +133,3 @@ release_docs_spec: docs
release_spec:
release_tests_spec:
-
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-include make.dep
diff --git a/lib/tv/doc/src/Makefile b/lib/tv/doc/src/Makefile
index f30e0307a9..5a41b28d48 100644
--- a/lib/tv/doc/src/Makefile
+++ b/lib/tv/doc/src/Makefile
@@ -25,14 +25,6 @@ include ../../vsn.mk
VSN=$(TV_VSN)
APPLICATION=tv
-# ----------------------------------------------------
-# Include dependency
-# ----------------------------------------------------
-
-ifndef DOCSUPPORT
-include make.dep
-endif
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -89,32 +81,10 @@ EXTRA_FILES = \
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-ifdef DOCSUPPORT
-
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-else
-
-TEX_FILES_BOOK = \
- $(BOOK_FILES:%.xml=%.tex)
-TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
- $(XML_APPLICATION_FILES:%.xml=%.tex)
-TEX_FILES_USERS_GUIDE = \
- $(XML_CHAPTER_FILES:%.xml=%.tex)
-
-TOP_PDF_FILE = tv-$(VSN).pdf
-TOP_PS_FILE = tv-$(VSN).ps
-
-$(TOP_PDF_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
-
-$(TOP_PS_FILE): book.dvi ../../vsn.mk
- $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
-
-endif
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -127,8 +97,6 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-ifdef DOCSUPPORT
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -144,31 +112,6 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-else
-
-ifeq ($(DOCTYPE),pdf)
-docs: pdf
-else
-ifeq ($(DOCTYPE),ps)
-docs: ps
-else
-docs: html gifs man
-endif
-endif
-
-pdf: $(TOP_PDF_FILE)
-
-ps: $(TOP_PS_FILE)
-
-html: $(HTML_FILES) gifs
-
-clean clean_docs clean_tex:
- rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
- rm -f $(HTML_FILES) $(MAN3_FILES)
- rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
- rm -f errs core *~ $(LATEX_CLEAN)
-endif
-
man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
@@ -181,8 +124,6 @@ debug opt:
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifdef DOCSUPPORT
-
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
$(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
@@ -193,29 +134,5 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-else
-
-ifeq ($(DOCTYPE),pdf)
-release_docs_spec: pdf
- $(INSTALL_DIR) $(RELEASE_PATH)/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
-else
-ifeq ($(DOCTYPE),ps)
-release_docs_spec: ps
- $(INSTALL_DIR) $(RELEASE_PATH)/ps
- $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
-else
-release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
-endif
-endif
-
-endif
-
release_spec:
--
cgit v1.2.3
From c8053ef7b602e8c47b6fa8919b9bbff996d3a81b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 20 Oct 2011 14:17:52 +0200
Subject: Remove unused */doc/src/make.dep files
These dependency files was once used when building the documentation,
but are no longer needed.
---
lib/appmon/doc/src/make.dep | 26 -----------
lib/asn1/doc/src/make.dep | 31 --------------
lib/common_test/doc/src/make.dep | 27 ------------
lib/compiler/doc/src/make.dep | 19 ---------
lib/cosEvent/doc/src/make.dep | 34 ---------------
lib/cosEventDomain/doc/src/make.dep | 23 ----------
lib/cosFileTransfer/doc/src/make.dep | 30 -------------
lib/cosNotification/doc/src/make.dep | 48 ---------------------
lib/cosProperty/doc/src/make.dep | 26 -----------
lib/cosTime/doc/src/make.dep | 22 ----------
lib/cosTransactions/doc/src/make.dep | 27 ------------
lib/crypto/doc/src/make.dep | 20 ---------
lib/debugger/doc/src/make.dep | 29 -------------
lib/dialyzer/doc/src/make.dep | 20 ---------
lib/edoc/doc/src/make.dep | 21 ---------
lib/erl_interface/doc/src/make.dep | 24 -----------
lib/eunit/doc/src/make.dep | 19 ---------
lib/gs/doc/src/make.dep | 58 -------------------------
lib/hipe/doc/src/make.dep | 13 ------
lib/ic/doc/src/make.dep | 24 -----------
lib/inets/doc/src/make.dep | 47 --------------------
lib/inviso/doc/src/make.dep | 27 ------------
lib/jinterface/doc/src/make.dep | 20 ---------
lib/kernel/doc/src/make.dep | 28 ------------
lib/megaco/doc/src/make.dep | 59 -------------------------
lib/mnesia/doc/src/make.dep | 46 --------------------
lib/observer/doc/src/make.dep | 29 -------------
lib/odbc/doc/src/make.dep | 27 ------------
lib/orber/doc/src/make.dep | 62 ---------------------------
lib/os_mon/doc/src/make.dep | 21 ---------
lib/otp_mibs/doc/src/make.dep | 20 ---------
lib/parsetools/doc/src/make.dep | 21 ---------
lib/percept/doc/src/make.dep | 34 ---------------
lib/pman/doc/src/make.dep | 26 -----------
lib/public_key/doc/src/make.dep | 21 ---------
lib/reltool/doc/src/make.dep | 20 ---------
lib/runtime_tools/doc/src/make.dep | 20 ---------
lib/sasl/doc/src/make.dep | 22 ----------
lib/snmp/doc/src/depend.mk | 83 ------------------------------------
lib/snmp/doc/src/make.dep | 77 ---------------------------------
lib/ssh/doc/src/make.dep | 19 ---------
lib/stdlib/doc/src/make.dep | 40 -----------------
lib/syntax_tools/doc/src/make.dep | 22 ----------
lib/test_server/doc/src/make.dep | 24 -----------
lib/toolbar/doc/src/make.dep | 26 -----------
lib/tools/doc/src/make.dep | 33 --------------
lib/tv/doc/src/make.dep | 32 --------------
lib/webtool/doc/src/make.dep | 20 ---------
lib/wx/doc/src/make.dep | 13 ------
lib/xmerl/doc/src/make.dep | 24 -----------
50 files changed, 1504 deletions(-)
delete mode 100644 lib/appmon/doc/src/make.dep
delete mode 100644 lib/asn1/doc/src/make.dep
delete mode 100644 lib/common_test/doc/src/make.dep
delete mode 100644 lib/compiler/doc/src/make.dep
delete mode 100644 lib/cosEvent/doc/src/make.dep
delete mode 100644 lib/cosEventDomain/doc/src/make.dep
delete mode 100644 lib/cosFileTransfer/doc/src/make.dep
delete mode 100644 lib/cosNotification/doc/src/make.dep
delete mode 100644 lib/cosProperty/doc/src/make.dep
delete mode 100644 lib/cosTime/doc/src/make.dep
delete mode 100644 lib/cosTransactions/doc/src/make.dep
delete mode 100644 lib/crypto/doc/src/make.dep
delete mode 100644 lib/debugger/doc/src/make.dep
delete mode 100755 lib/dialyzer/doc/src/make.dep
delete mode 100644 lib/edoc/doc/src/make.dep
delete mode 100644 lib/erl_interface/doc/src/make.dep
delete mode 100644 lib/eunit/doc/src/make.dep
delete mode 100644 lib/gs/doc/src/make.dep
delete mode 100644 lib/hipe/doc/src/make.dep
delete mode 100644 lib/ic/doc/src/make.dep
delete mode 100644 lib/inets/doc/src/make.dep
delete mode 100644 lib/inviso/doc/src/make.dep
delete mode 100644 lib/jinterface/doc/src/make.dep
delete mode 100644 lib/kernel/doc/src/make.dep
delete mode 100644 lib/megaco/doc/src/make.dep
delete mode 100644 lib/mnesia/doc/src/make.dep
delete mode 100644 lib/observer/doc/src/make.dep
delete mode 100644 lib/odbc/doc/src/make.dep
delete mode 100644 lib/orber/doc/src/make.dep
delete mode 100644 lib/os_mon/doc/src/make.dep
delete mode 100644 lib/otp_mibs/doc/src/make.dep
delete mode 100644 lib/parsetools/doc/src/make.dep
delete mode 100644 lib/percept/doc/src/make.dep
delete mode 100644 lib/pman/doc/src/make.dep
delete mode 100644 lib/public_key/doc/src/make.dep
delete mode 100644 lib/reltool/doc/src/make.dep
delete mode 100644 lib/runtime_tools/doc/src/make.dep
delete mode 100644 lib/sasl/doc/src/make.dep
delete mode 100644 lib/snmp/doc/src/depend.mk
delete mode 100644 lib/snmp/doc/src/make.dep
delete mode 100644 lib/ssh/doc/src/make.dep
delete mode 100644 lib/stdlib/doc/src/make.dep
delete mode 100644 lib/syntax_tools/doc/src/make.dep
delete mode 100644 lib/test_server/doc/src/make.dep
delete mode 100644 lib/toolbar/doc/src/make.dep
delete mode 100644 lib/tools/doc/src/make.dep
delete mode 100644 lib/tv/doc/src/make.dep
delete mode 100644 lib/webtool/doc/src/make.dep
delete mode 100644 lib/wx/doc/src/make.dep
delete mode 100644 lib/xmerl/doc/src/make.dep
(limited to 'lib')
diff --git a/lib/appmon/doc/src/make.dep b/lib/appmon/doc/src/make.dep
deleted file mode 100644
index ce0d7571a3..0000000000
--- a/lib/appmon/doc/src/make.dep
+++ /dev/null
@@ -1,26 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: appmon.tex appmon_chapter.tex book.tex part.tex \
- ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: app_win.ps listbox_win.ps main_win.ps pinfo_win.ps
-
diff --git a/lib/asn1/doc/src/make.dep b/lib/asn1/doc/src/make.dep
deleted file mode 100644
index eb2c0e9a98..0000000000
--- a/lib/asn1/doc/src/make.dep
+++ /dev/null
@@ -1,31 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/gandalf/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: asn1_spec.tex asn1_ug.tex asn1ct.tex asn1rt.tex \
- book.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-asn1_spec.tex: Seq.asn Seq.asn1config
-
-book.tex: part.xml ref_man.xml
-
-asn1_ug.tex: ../../../../system/doc/definitions/cite.defs
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: exclusive_Win_But.ps selective_TypeList.ps \
- selective_Window2.ps
-
diff --git a/lib/common_test/doc/src/make.dep b/lib/common_test/doc/src/make.dep
deleted file mode 100644
index e34075888d..0000000000
--- a/lib/common_test/doc/src/make.dep
+++ /dev/null
@@ -1,27 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: basics_chapter.tex book.tex common_test_app.tex \
- config_file_chapter.tex cover_chapter.tex \
- ct.tex ct_cover.tex ct_ftp.tex ct_master.tex \
- ct_master_chapter.tex ct_rpc.tex ct_snmp.tex \
- ct_ssh.tex ct_telnet.tex dependencies_chapter.tex \
- event_handler_chapter.tex example_chapter.tex \
- install_chapter.tex part.tex ref_man.tex run_test.tex \
- run_test_chapter.tex test_structure_chapter.tex \
- unix_telnet.tex why_test_chapter.tex write_test_chapter.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/compiler/doc/src/make.dep b/lib/compiler/doc/src/make.dep
deleted file mode 100644
index f5c097afad..0000000000
--- a/lib/compiler/doc/src/make.dep
+++ /dev/null
@@ -1,19 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex compile.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/cosEvent/doc/src/make.dep b/lib/cosEvent/doc/src/make.dep
deleted file mode 100644
index b8a95c2d58..0000000000
--- a/lib/cosEvent/doc/src/make.dep
+++ /dev/null
@@ -1,34 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosEventChannelAdmin.tex CosEventChannelAdmin_ConsumerAdmin.tex \
- CosEventChannelAdmin_EventChannel.tex CosEventChannelAdmin_ProxyPullConsumer.tex \
- CosEventChannelAdmin_ProxyPullSupplier.tex \
- CosEventChannelAdmin_ProxyPushConsumer.tex \
- CosEventChannelAdmin_ProxyPushSupplier.tex \
- CosEventChannelAdmin_SupplierAdmin.tex book.tex \
- ch_contents.tex ch_event_service.tex ch_introduction.tex \
- cosEventApp.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-CosEventChannelAdmin.tex: ../../src/CosEventChannelAdmin.idl
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: e_s_components.ps e_s_models.ps
-
diff --git a/lib/cosEventDomain/doc/src/make.dep b/lib/cosEventDomain/doc/src/make.dep
deleted file mode 100644
index 2f3f1ae53d..0000000000
--- a/lib/cosEventDomain/doc/src/make.dep
+++ /dev/null
@@ -1,23 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosEventDomainAdmin.tex CosEventDomainAdmin_EventDomain.tex \
- CosEventDomainAdmin_EventDomainFactory.tex \
- book.tex ch_QoS.tex ch_contents.tex ch_event_domain_service.tex \
- ch_introduction.tex cosEventDomainApp.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/cosFileTransfer/doc/src/make.dep b/lib/cosFileTransfer/doc/src/make.dep
deleted file mode 100644
index 3be0c185c3..0000000000
--- a/lib/cosFileTransfer/doc/src/make.dep
+++ /dev/null
@@ -1,30 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosFileTransfer_Directory.tex CosFileTransfer_File.tex \
- CosFileTransfer_FileIterator.tex CosFileTransfer_FileTransferSession.tex \
- CosFileTransfer_VirtualFileSystem.tex book.tex \
- ch_contents.tex ch_example.tex ch_install.tex \
- ch_introduction.tex ch_system.tex cosFileTransferApp.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosFileTransfer.ps
-
diff --git a/lib/cosNotification/doc/src/make.dep b/lib/cosNotification/doc/src/make.dep
deleted file mode 100644
index 031a2b3e98..0000000000
--- a/lib/cosNotification/doc/src/make.dep
+++ /dev/null
@@ -1,48 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosNotification.tex CosNotification_AdminPropertiesAdmin.tex \
- CosNotification_QoSAdmin.tex CosNotifyChannelAdmin_ConsumerAdmin.tex \
- CosNotifyChannelAdmin_EventChannel.tex CosNotifyChannelAdmin_EventChannelFactory.tex \
- CosNotifyChannelAdmin_ProxyConsumer.tex CosNotifyChannelAdmin_ProxyPullConsumer.tex \
- CosNotifyChannelAdmin_ProxyPullSupplier.tex \
- CosNotifyChannelAdmin_ProxyPushConsumer.tex \
- CosNotifyChannelAdmin_ProxyPushSupplier.tex \
- CosNotifyChannelAdmin_ProxySupplier.tex CosNotifyChannelAdmin_SequenceProxyPullConsumer.tex \
- CosNotifyChannelAdmin_SequenceProxyPullSupplier.tex \
- CosNotifyChannelAdmin_SequenceProxyPushConsumer.tex \
- CosNotifyChannelAdmin_SequenceProxyPushSupplier.tex \
- CosNotifyChannelAdmin_StructuredProxyPullConsumer.tex \
- CosNotifyChannelAdmin_StructuredProxyPullSupplier.tex \
- CosNotifyChannelAdmin_StructuredProxyPushConsumer.tex \
- CosNotifyChannelAdmin_StructuredProxyPushSupplier.tex \
- CosNotifyChannelAdmin_SupplierAdmin.tex CosNotifyComm_NotifyPublish.tex \
- CosNotifyComm_NotifySubscribe.tex CosNotifyFilter_Filter.tex \
- CosNotifyFilter_FilterAdmin.tex CosNotifyFilter_FilterFactory.tex \
- CosNotifyFilter_MappingFilter.tex book.tex \
- ch_BNF.tex ch_QoS.tex ch_contents.tex ch_example.tex \
- ch_install.tex ch_introduction.tex ch_system.tex \
- cosNotificationApp.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: eventstructure.ps
-
-book.dvi: notificationFlow.ps
-
diff --git a/lib/cosProperty/doc/src/make.dep b/lib/cosProperty/doc/src/make.dep
deleted file mode 100644
index 383af54244..0000000000
--- a/lib/cosProperty/doc/src/make.dep
+++ /dev/null
@@ -1,26 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosPropertyService_PropertiesIterator.tex \
- CosPropertyService_PropertyNamesIterator.tex \
- CosPropertyService_PropertySet.tex CosPropertyService_PropertySetDef.tex \
- CosPropertyService_PropertySetDefFactory.tex \
- CosPropertyService_PropertySetFactory.tex \
- book.tex ch_contents.tex ch_example.tex ch_install.tex \
- ch_introduction.tex cosProperty.tex part.tex \
- ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/cosTime/doc/src/make.dep b/lib/cosTime/doc/src/make.dep
deleted file mode 100644
index 69a584ab95..0000000000
--- a/lib/cosTime/doc/src/make.dep
+++ /dev/null
@@ -1,22 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosTime_TIO.tex CosTime_TimeService.tex CosTime_UTO.tex \
- CosTimerEvent_TimerEventHandler.tex CosTimerEvent_TimerEventService.tex \
- book.tex ch_contents.tex ch_example.tex ch_install.tex \
- ch_introduction.tex cosTime.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/cosTransactions/doc/src/make.dep b/lib/cosTransactions/doc/src/make.dep
deleted file mode 100644
index bd45aea286..0000000000
--- a/lib/cosTransactions/doc/src/make.dep
+++ /dev/null
@@ -1,27 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosTransactions_Control.tex CosTransactions_Coordinator.tex \
- CosTransactions_RecoveryCoordinator.tex CosTransactions_Resource.tex \
- CosTransactions_SubtransactionAwareResource.tex \
- CosTransactions_Terminator.tex CosTransactions_TransactionFactory.tex \
- book.tex ch_contents.tex ch_example.tex ch_install.tex \
- ch_introduction.tex ch_skeletons.tex cosTransactions.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-ch_example.tex: ../../../../system/doc/definitions/term.defs
-
diff --git a/lib/crypto/doc/src/make.dep b/lib/crypto/doc/src/make.dep
deleted file mode 100644
index 73b090bbb6..0000000000
--- a/lib/crypto/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex crypto.tex crypto_app.tex licenses.tex \
- ref_man.tex usersguide.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/debugger/doc/src/make.dep b/lib/debugger/doc/src/make.dep
deleted file mode 100644
index c11fd3c21c..0000000000
--- a/lib/debugger/doc/src/make.dep
+++ /dev/null
@@ -1,29 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex debugger.tex debugger_chapter.tex \
- i.tex int.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: part.xml ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: images/attach.ps images/cond_break_dialog.ps \
- images/function_break_dialog.ps images/interpret.ps \
- images/line_break_dialog.ps images/monitor.ps \
- images/view.ps
-
diff --git a/lib/dialyzer/doc/src/make.dep b/lib/dialyzer/doc/src/make.dep
deleted file mode 100755
index f8177cd419..0000000000
--- a/lib/dialyzer/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex dialyzer.tex dialyzer_chapter.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/edoc/doc/src/make.dep b/lib/edoc/doc/src/make.dep
deleted file mode 100644
index b46e36314f..0000000000
--- a/lib/edoc/doc/src/make.dep
+++ /dev/null
@@ -1,21 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex chapter.tex edoc.tex edoc_doclet.tex \
- edoc_extract.tex edoc_layout.tex edoc_lib.tex \
- edoc_run.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/erl_interface/doc/src/make.dep b/lib/erl_interface/doc/src/make.dep
deleted file mode 100644
index 3f43cf64fe..0000000000
--- a/lib/erl_interface/doc/src/make.dep
+++ /dev/null
@@ -1,24 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin//docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex ei.tex ei_connect.tex ei_users_guide.tex \
- erl_call.tex erl_connect.tex erl_error.tex \
- erl_eterm.tex erl_format.tex erl_global.tex \
- erl_malloc.tex erl_marshal.tex part_ei.tex \
- ref_man.tex ref_man_ei.tex ref_man_erl_interface.tex \
- registry.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml ref_man_ei.xml ref_man_erl_interface.xml
-
diff --git a/lib/eunit/doc/src/make.dep b/lib/eunit/doc/src/make.dep
deleted file mode 100644
index d68f888403..0000000000
--- a/lib/eunit/doc/src/make.dep
+++ /dev/null
@@ -1,19 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex chapter.tex eunit.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/gs/doc/src/make.dep b/lib/gs/doc/src/make.dep
deleted file mode 100644
index b33ed3b2de..0000000000
--- a/lib/gs/doc/src/make.dep
+++ /dev/null
@@ -1,58 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex gs.tex gs_chapter1.tex gs_chapter2.tex \
- gs_chapter3.tex gs_chapter4.tex gs_chapter5.tex \
- gs_chapter6.tex gs_chapter7.tex gs_chapter8.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-gs_chapter2.tex: examples/ex1.erl examples/ex2.erl
-
-gs_chapter4.tex: examples/ex3.erl examples/ex4.erl examples/ex5.erl \
- examples/ex6.erl
-
-gs_chapter5.tex: examples/ex15.erl
-
-gs_chapter6.tex: examples/ex16.erl
-
-gs_chapter7.tex: examples/ex17.erl
-
-gs_chapter8.tex: examples/ex10.erl examples/ex11.erl examples/ex12.erl \
- examples/ex13.erl examples/ex14.erl examples/ex7.erl \
- examples/ex8.erl examples/ex9.erl
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: pics/gs1-1-image-1.ps pics/gs1-1-image-2.ps \
- pics/gs1-1-image-3.ps
-
-book.dvi: pics/ex1.ps pics/gs1-1-image-4.ps
-
-book.dvi: pics/ex15.ps
-
-book.dvi: pics/ex16.ps
-
-book.dvi: pics/packer1.ps pics/packer2.ps
-
-book.dvi: pics/arc.ps pics/buttons.ps pics/ex10.ps pics/ex11.ps \
- pics/ex12.ps pics/ex13.ps pics/ex14.ps pics/ex8.ps \
- pics/ex9.ps pics/image.ps pics/line.ps pics/oval.ps \
- pics/polygon.ps pics/rectangle.ps pics/text.ps \
- pics/window.ps
-
diff --git a/lib/hipe/doc/src/make.dep b/lib/hipe/doc/src/make.dep
deleted file mode 100644
index d5f5844c21..0000000000
--- a/lib/hipe/doc/src/make.dep
+++ /dev/null
@@ -1,13 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex
-
diff --git a/lib/ic/doc/src/make.dep b/lib/ic/doc/src/make.dep
deleted file mode 100644
index 64694ee85a..0000000000
--- a/lib/ic/doc/src/make.dep
+++ /dev/null
@@ -1,24 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex c-part.tex ch_basic_idl.tex ch_c_client.tex \
- ch_c_corba_env.tex ch_c_mapping.tex ch_c_server.tex \
- ch_erl_genserv.tex ch_erl_plain.tex ch_ic_protocol.tex \
- ch_introduction.tex ch_java.tex erl-part.tex \
- ic.tex ic_c_protocol.tex ic_clib.tex java-part.tex \
- ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/inets/doc/src/make.dep b/lib/inets/doc/src/make.dep
deleted file mode 100644
index 8deb7e7a5a..0000000000
--- a/lib/inets/doc/src/make.dep
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2010. 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%
-#
-#
-
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex ftp.tex ftp_client.tex httpc.tex http_client.tex \
- http_server.tex httpd.tex httpd_conf.tex httpd_socket.tex \
- httpd_util.tex inets.tex inets_services.tex \
- mod_alias.tex mod_auth.tex mod_esi.tex mod_security.tex \
- part.tex ref_man.tex tftp.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-ftp.tex: ../../../../system/doc/definitions/term.defs
-
-inets_services.tex: ../../../../system/doc/definitions/term.defs
-
diff --git a/lib/inviso/doc/src/make.dep b/lib/inviso/doc/src/make.dep
deleted file mode 100644
index 9459f74e6d..0000000000
--- a/lib/inviso/doc/src/make.dep
+++ /dev/null
@@ -1,27 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex inviso.tex inviso_as_lib.tex inviso_chapter.tex \
- inviso_lfm.tex inviso_lfm_tpfreader.tex inviso_rt.tex \
- inviso_rt_meta.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: inviso_users_guide_pic1.ps
-
diff --git a/lib/jinterface/doc/src/make.dep b/lib/jinterface/doc/src/make.dep
deleted file mode 100644
index a1b3c322c6..0000000000
--- a/lib/jinterface/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex jinterface.tex jinterface_users_guide.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/kernel/doc/src/make.dep b/lib/kernel/doc/src/make.dep
deleted file mode 100644
index f79d1c6367..0000000000
--- a/lib/kernel/doc/src/make.dep
+++ /dev/null
@@ -1,28 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: app.tex application.tex auth.tex book.tex \
- code.tex config.tex disk_log.tex erl_boot_server.tex \
- erl_ddll.tex erl_prim_loader_stub.tex erlang_stub.tex \
- error_handler.tex error_logger.tex file.tex \
- gen_sctp.tex gen_tcp.tex gen_udp.tex global.tex \
- global_group.tex heart.tex inet.tex inet_res.tex \
- init_stub.tex kernel_app.tex net_adm.tex net_kernel.tex \
- os.tex packages.tex pg2.tex ref_man.tex rpc.tex \
- seq_trace.tex user.tex wrap_log_reader.tex \
- zlib_stub.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/megaco/doc/src/make.dep b/lib/megaco/doc/src/make.dep
deleted file mode 100644
index 0e2040ab50..0000000000
--- a/lib/megaco/doc/src/make.dep
+++ /dev/null
@@ -1,59 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2001-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%
-
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex megaco.tex megaco_architecture.tex \
- megaco_codec_meas.tex \
- megaco_codec_mstone1.tex megaco_codec_mstone2.tex \
- megaco_codec_transform.tex \
- megaco_debug.tex megaco_edist_compress.tex \
- megaco_encode.tex megaco_encoder.tex megaco_examples.tex \
- megaco_flex_scanner.tex megaco_intro.tex megaco_mib.tex \
- megaco_performance.tex megaco_run.tex megaco_tcp.tex \
- megaco_transport.tex megaco_transport_mechanisms.tex \
- megaco_udp.tex megaco_user.tex part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: call_flow.ps call_flow_cont.ps distr_node_config.ps \
- megaco_sys_arch.ps single_node_config.ps
-
-book.dvi: mstone1.ps
-
-book.dvi: MG-startup_flow_noMID.ps MGC_startup_call_flow.ps \
- MG_startup_call_flow.ps
-
diff --git a/lib/mnesia/doc/src/make.dep b/lib/mnesia/doc/src/make.dep
deleted file mode 100644
index 6e79484cb3..0000000000
--- a/lib/mnesia/doc/src/make.dep
+++ /dev/null
@@ -1,46 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: Mnesia_App_A.tex Mnesia_App_B.tex Mnesia_App_C.tex \
- Mnesia_App_D.tex Mnesia_chap1.tex Mnesia_chap2.tex \
- Mnesia_chap3.tex Mnesia_chap4.tex Mnesia_chap5.tex \
- Mnesia_chap7.tex Mnesia_chap8.tex book.tex \
- mnesia.tex mnesia_frag_hash.tex mnesia_registry.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-Mnesia_App_B.tex: ../../src/mnesia_backup.erl
-
-Mnesia_App_C.tex: ../../src/mnesia_frag.erl
-
-Mnesia_App_D.tex: ../../src/mnesia_frag_hash.erl
-
-Mnesia_chap2.tex: company.erl company.hrl
-
-Mnesia_chap3.tex: company.erl
-
-Mnesia_chap4.tex: company.erl
-
-Mnesia_chap5.tex: FRUITS company.erl company_o.erl company_o.hrl
-
-Mnesia_chap7.tex: bup.erl
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: company.ps
-
diff --git a/lib/observer/doc/src/make.dep b/lib/observer/doc/src/make.dep
deleted file mode 100644
index 3c1b3df771..0000000000
--- a/lib/observer/doc/src/make.dep
+++ /dev/null
@@ -1,29 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex crashdump.tex crashdump_ug.tex etop.tex \
- etop_ug.tex observer_app.tex part.tex ref_man.tex \
- ttb.tex ttb_ug.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: etop_5.ps etop_lines.ps etop_main.ps etop_opt.ps
-
-book.dvi: et_modsprocs.ps et_processes.ps
-
diff --git a/lib/odbc/doc/src/make.dep b/lib/odbc/doc/src/make.dep
deleted file mode 100644
index d62e8dd8f0..0000000000
--- a/lib/odbc/doc/src/make.dep
+++ /dev/null
@@ -1,27 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex databases.tex error_handling.tex \
- getting_started.tex introduction.tex odbc.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: odbc_app_arc.ps
-
diff --git a/lib/orber/doc/src/make.dep b/lib/orber/doc/src/make.dep
deleted file mode 100644
index cf5aad747d..0000000000
--- a/lib/orber/doc/src/make.dep
+++ /dev/null
@@ -1,62 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: CosNaming.tex CosNaming_BindingIterator.tex \
- CosNaming_NamingContext.tex CosNaming_NamingContextExt.tex \
- Module_Interface.tex any.tex book.tex ch_contents.tex \
- ch_debugging.tex ch_exceptions.tex \
- ch_idl_to_erlang_mapping.tex ch_ifr.tex ch_install.tex \
- ch_interceptors.tex ch_introduction.tex ch_naming_service.tex \
- ch_orber_kernel.tex ch_orberweb.tex ch_security.tex \
- ch_stubs.tex corba.tex corba_object.tex example_part.tex \
- fixed.tex interceptors.tex intro_part.tex \
- lname.tex lname_component.tex orber.tex orber_acl.tex \
- orber_diagnostics.tex orber_ifr.tex orber_tc.tex \
- ref_man.tex tools_debugging_part.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-ch_contents.tex: ../../../../system/doc/definitions/term.defs
-
-ch_idl_to_erlang_mapping.tex: ../../../../system/doc/definitions/term.defs
-
-ch_install.tex: ../../../../system/doc/definitions/term.defs
-
-ch_introduction.tex: ../../../../system/doc/definitions/term.defs
-
-ch_naming_service.tex: ../../../../system/doc/definitions/term.defs
-
-ch_orber_kernel.tex: ../../../../system/doc/definitions/term.defs
-
-orber_ifr.tex: ../../../../system/doc/definitions/term.defs
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: firewall_nat.ps
-
-book.dvi: interceptor_operations.ps
-
-book.dvi: dependent.ps orbs.ps
-
-book.dvi: name.ps
-
-book.dvi: iiop.ps theORB.ps
-
-book.dvi: dataframe1.ps dataframe2.ps dataframe3.ps \
- dataframe4.ps dataframe5.ps dataframe6.ps \
- dataframe7.ps dataframe8.ps menuframe.ps
-
diff --git a/lib/os_mon/doc/src/make.dep b/lib/os_mon/doc/src/make.dep
deleted file mode 100644
index b657f2e036..0000000000
--- a/lib/os_mon/doc/src/make.dep
+++ /dev/null
@@ -1,21 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex cpu_sup.tex disksup.tex memsup.tex \
- nteventlog.tex os_mon.tex os_mon_mib.tex os_sup.tex \
- ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/otp_mibs/doc/src/make.dep b/lib/otp_mibs/doc/src/make.dep
deleted file mode 100644
index 2885155315..0000000000
--- a/lib/otp_mibs/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex introduction.tex mibs.tex otp_mib.tex \
- part.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/parsetools/doc/src/make.dep b/lib/parsetools/doc/src/make.dep
deleted file mode 100644
index 3a09ecdedd..0000000000
--- a/lib/parsetools/doc/src/make.dep
+++ /dev/null
@@ -1,21 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex leex.tex ref_man.tex yecc.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-ref_man.tex: ../../../../system/doc/definitions/term.defs
-
diff --git a/lib/percept/doc/src/make.dep b/lib/percept/doc/src/make.dep
deleted file mode 100644
index df16cffd4f..0000000000
--- a/lib/percept/doc/src/make.dep
+++ /dev/null
@@ -1,34 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex egd.tex egd_ug.tex part.tex percept.tex \
- percept_profile.tex percept_ug.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-egd_ug.tex: img.erl img_esi.erl
-
-percept_ug.tex: sorter.erl
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: img_esi_result.ps test1.ps test2.ps test3.ps \
- test4.ps
-
-book.dvi: percept_compare.ps percept_overview.ps percept_processes.ps \
- percept_processinfo.ps
-
diff --git a/lib/pman/doc/src/make.dep b/lib/pman/doc/src/make.dep
deleted file mode 100644
index 2f6a8a06cd..0000000000
--- a/lib/pman/doc/src/make.dep
+++ /dev/null
@@ -1,26 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex pman.tex pman_chapter.tex \
- ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: main_window.ps options.ps trace.ps
-
diff --git a/lib/public_key/doc/src/make.dep b/lib/public_key/doc/src/make.dep
deleted file mode 100644
index 2675556f1b..0000000000
--- a/lib/public_key/doc/src/make.dep
+++ /dev/null
@@ -1,21 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex cert_records.tex introduction.tex \
- part.tex public_key.tex public_key_records.tex \
- ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/reltool/doc/src/make.dep b/lib/reltool/doc/src/make.dep
deleted file mode 100644
index 59e77e8162..0000000000
--- a/lib/reltool/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex ref_man.tex reltool.tex \
- reltool_examples.tex reltool_intro.tex reltool_usage.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/runtime_tools/doc/src/make.dep b/lib/runtime_tools/doc/src/make.dep
deleted file mode 100644
index 85eae88adf..0000000000
--- a/lib/runtime_tools/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex dbg.tex erts_alloc_config.tex refman.tex \
- runtime_tools_app.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: refman.xml
-
diff --git a/lib/sasl/doc/src/make.dep b/lib/sasl/doc/src/make.dep
deleted file mode 100644
index 4843b7934a..0000000000
--- a/lib/sasl/doc/src/make.dep
+++ /dev/null
@@ -1,22 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: alarm_handler.tex appup.tex book.tex error_logging.tex \
- overload.tex part.tex rb.tex ref_man.tex rel.tex \
- release_handler.tex relup.tex sasl_app.tex \
- sasl_intro.tex script.tex systools.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/snmp/doc/src/depend.mk b/lib/snmp/doc/src/depend.mk
deleted file mode 100644
index 20a523dd8c..0000000000
--- a/lib/snmp/doc/src/depend.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2004-2011. 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%
-
-$(HTMLDIR)/part_notes.html: \
- part_notes_history.xml \
- part_notes.xml \
- notes_history.xml \
- notes.xml
-
-$(HTMLDIR)/part.html: \
- part.xml \
- snmp_intro.xml \
- snmp_agent_funct_descr.xml \
- snmp_manager_funct_descr.xml \
- snmp_mib_compiler.xml \
- snmp_config.xml \
- snmp_agent_config_files.xml \
- snmp_manager_config_files.xml \
- snmp_impl_example_agent.xml \
- snmp_impl_example_manager.xml \
- snmp_instr_functions.xml \
- snmp_def_instr_functions.xml \
- snmp_agent_netif.xml \
- snmp_manager_netif.xml \
- snmp_audit_trail_log.xml \
- snmp_advanced_agent.xml \
- snmp_app_a.xml \
- snmp_app_b.xml
-
-$(HTMLDIR)/ref_man.html: \
- ref_man.xml \
- snmp_app.xml \
- snmp.xml \
- snmpc.xml \
- snmpc_cmd.xml \
- snmpa.xml \
- snmpa_conf.xml \
- snmpa_discovery_handler.xml \
- snmpa_error_report.xml \
- snmpa_error.xml \
- snmpa_error_io.xml \
- snmpa_error_logger.xml \
- snmpa_local_db.xml \
- snmpa_mpd.xml \
- snmpa_network_interface.xml \
- snmpa_network_interface_filter.xml \
- snmpa_notification_delivery_info_receiver.xml \
- snmpa_notification_filter.xml \
- snmpa_supervisor.xml \
- snmp_community_mib.xml \
- snmp_framework_mib.xml \
- snmp_generic.xml \
- snmp_index.xml \
- snmp_notification_mib.xml \
- snmp_pdus.xml \
- snmp_standard_mib.xml \
- snmp_target_mib.xml \
- snmp_user_based_sm_mib.xml \
- snmp_view_based_acm_mib.xml \
- snmpm.xml \
- snmpm_conf.xml \
- snmpm_mpd.xml \
- snmpm_network_interface.xml \
- snmpm_network_interface_filter.xml \
- snmpm_user.xml
-
-
diff --git a/lib/snmp/doc/src/make.dep b/lib/snmp/doc/src/make.dep
deleted file mode 100644
index 223e197f25..0000000000
--- a/lib/snmp/doc/src/make.dep
+++ /dev/null
@@ -1,77 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2011. 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%
-
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex ref_man.tex snmp.tex snmp_advanced_agent.tex \
- snmp_agent_config_files.tex snmp_agent_funct_descr.tex \
- snmp_agent_netif.tex snmp_app.tex snmp_app_a.tex \
- snmp_app_b.tex snmp_audit_trail_log.tex \
- snmp_community_mib.tex \
- snmp_config.tex snmp_def_instr_functions.tex \
- snmp_framework_mib.tex snmp_generic.tex \
- snmp_impl_example_agent.tex \
- snmp_impl_example_manager.tex snmp_index.tex \
- snmp_instr_functions.tex snmp_intro.tex \
- snmp_manager_config_files.tex \
- snmp_manager_funct_descr.tex snmp_manager_netif.tex \
- snmp_mib_compiler.tex snmp_notification_mib.tex \
- snmp_pdus.tex snmp_standard_mib.tex snmp_target_mib.tex \
- snmp_user_based_sm_mib.tex snmp_view_based_acm_mib.tex \
- snmpa.tex snmpa_conf.tex snmpa_error.tex snmpa_error_io.tex \
- snmpa_error_logger.tex snmpa_error_report.tex \
- snmpa_local_db.tex snmpa_mpd.tex \
- snmpa_discovery_handler.tex \
- snmpa_network_interface.tex \
- snmpa_network_interface_filter.tex \
- snmpa_notification_delivery_info_receiver.tex \
- snmpa_notification_filter.tex \
- snmpa_supervisor.tex \
- snmpc.tex snmpc_cmd.tex snmpm.tex snmpm_conf.tex snmpm_mpd.tex \
- snmpm_network_interface.tex snmpm_network_interface_filter.tex \
- snmpm_user.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: MIB_mechanism.ps snmp-um-1-image-1.ps snmp-um-1-image-2.ps \
- snmp-um-1-image-3.ps
-
-book.dvi: snmp_agent_netif_1.ps
-
-book.dvi: getnext1.ps getnext2.ps getnext3.ps getnext4.ps
-
-book.dvi: snmp_manager_netif_1.ps
-
diff --git a/lib/ssh/doc/src/make.dep b/lib/ssh/doc/src/make.dep
deleted file mode 100644
index cfe2f9617b..0000000000
--- a/lib/ssh/doc/src/make.dep
+++ /dev/null
@@ -1,19 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex ref_man.tex ssh.tex ssh_channel.tex \
- ssh_connection.tex ssh_sftp.tex ssh_sftpd.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
diff --git a/lib/stdlib/doc/src/make.dep b/lib/stdlib/doc/src/make.dep
deleted file mode 100644
index 48ee6209ef..0000000000
--- a/lib/stdlib/doc/src/make.dep
+++ /dev/null
@@ -1,40 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: array.tex base64.tex beam_lib.tex book.tex \
- c.tex calendar.tex dets.tex dict.tex digraph.tex \
- digraph_utils.tex epp.tex erl_eval.tex erl_expand_records.tex \
- erl_id_trans.tex erl_internal.tex erl_lint.tex \
- erl_parse.tex erl_pp.tex erl_scan.tex erl_tar.tex \
- ets.tex file_sorter.tex filelib.tex filename.tex \
- gb_sets.tex gb_trees.tex gen_event.tex gen_fsm.tex \
- gen_server.tex io.tex io_lib.tex io_protocol.tex \
- lib.tex lists.tex log_mf_h.tex math.tex ms_transform.tex \
- orddict.tex ordsets.tex part.tex pg.tex pool.tex \
- proc_lib.tex proplists.tex qlc.tex queue.tex \
- random.tex re.tex ref_man.tex regexp.tex sets.tex \
- shell.tex shell_default.tex slave.tex sofs.tex \
- stdlib_app.tex string.tex supervisor.tex supervisor_bridge.tex \
- sys.tex timer.tex unicode.tex unicode_usage.tex \
- win32reg.tex zip.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: ushell1.ps
-
diff --git a/lib/syntax_tools/doc/src/make.dep b/lib/syntax_tools/doc/src/make.dep
deleted file mode 100644
index acc76857bb..0000000000
--- a/lib/syntax_tools/doc/src/make.dep
+++ /dev/null
@@ -1,22 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex chapter.tex epp_dodger.tex erl_comment_scan.tex \
- erl_prettypr.tex erl_recomment.tex erl_syntax.tex \
- erl_syntax_lib.tex erl_tidy.tex igor.tex part.tex \
- prettypr.tex ref_man.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/test_server/doc/src/make.dep b/lib/test_server/doc/src/make.dep
deleted file mode 100644
index ee9100bd08..0000000000
--- a/lib/test_server/doc/src/make.dep
+++ /dev/null
@@ -1,24 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: basics_chapter.tex book.tex example_chapter.tex \
- part.tex ref_man.tex run_test_chapter.tex \
- test_server_app.tex test_server_ctrl.tex \
- test_server.tex test_spec_chapter.tex \
- write_framework_chapter.tex \
- write_test_chapter.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/toolbar/doc/src/make.dep b/lib/toolbar/doc/src/make.dep
deleted file mode 100644
index d93ff2a315..0000000000
--- a/lib/toolbar/doc/src/make.dep
+++ /dev/null
@@ -1,26 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex ref_man.tex toolbar.tex \
- toolbar_chapter.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: bar.ps create_tool.ps
-
diff --git a/lib/tools/doc/src/make.dep b/lib/tools/doc/src/make.dep
deleted file mode 100644
index 11fa090d6f..0000000000
--- a/lib/tools/doc/src/make.dep
+++ /dev/null
@@ -1,33 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex cover.tex cover_chapter.tex cprof.tex \
- cprof_chapter.tex eprof.tex erlang_mode.tex \
- erlang_mode_chapter.tex fprof.tex fprof_chapter.tex \
- instrument.tex make.tex part.tex ref_man.tex \
- tags.tex xref.tex xref_chapter.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-cprof.tex: ../../../../system/doc/definitions/term.defs
-
-xref.tex: ../../../../system/doc/definitions/term.defs
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: venn1.ps venn2.ps
-
diff --git a/lib/tv/doc/src/make.dep b/lib/tv/doc/src/make.dep
deleted file mode 100644
index 8437e320c6..0000000000
--- a/lib/tv/doc/src/make.dep
+++ /dev/null
@@ -1,32 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex ref_man.tex table_visualizer_chapter.tex \
- tv.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-# ----------------------------------------------------
-# Pictures that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: info_window.ps set_poll_int.ps tv_create_table.ps \
- tv_record_editor_mnesia.ps tv_row_marked.ps \
- tv_row_marked_popup.ps tv_search_result.ps \
- tv_search_window.ps tv_start.ps tv_start_mnesia.ps \
- tv_start_other_node.ps tv_start_pid_sorted.ps \
- tv_start_system.ps tv_start_system_unreadable.ps \
- tv_table_browser.ps tv_table_browser_updated.ps
-
diff --git a/lib/webtool/doc/src/make.dep b/lib/webtool/doc/src/make.dep
deleted file mode 100644
index 87526b3f73..0000000000
--- a/lib/webtool/doc/src/make.dep
+++ /dev/null
@@ -1,20 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex ref_man.tex start_webtool.tex \
- webtool.tex webtool_chapter.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/wx/doc/src/make.dep b/lib/wx/doc/src/make.dep
deleted file mode 100644
index 91001be438..0000000000
--- a/lib/wx/doc/src/make.dep
+++ /dev/null
@@ -1,13 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex chapter.tex part.tex
-
diff --git a/lib/xmerl/doc/src/make.dep b/lib/xmerl/doc/src/make.dep
deleted file mode 100644
index 9c303fc41c..0000000000
--- a/lib/xmerl/doc/src/make.dep
+++ /dev/null
@@ -1,24 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex part.tex ref_man.tex xmerl.tex xmerl_eventp.tex \
- xmerl_scan.tex xmerl_ug.tex xmerl_xpath.tex \
- xmerl_xs.tex xmerl_xsd.tex xmerl_sax_parser.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
-xmerl_ug.tex: motorcycles.txt motorcycles2html.erl motorcycles_dtd.txt \
- new_motorcycles.txt new_motorcycles2.txt
-
--
cgit v1.2.3
From 7d5028832f3638852be41bb32a328859d8d11f93 Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Tue, 1 Nov 2011 12:56:49 +0100
Subject: Skipping the dist test case. Needs a major re-write (sometime).
---
lib/megaco/test/megaco_mess_test.erl | 92 +++++++++++++++++++++++++++---------
lib/megaco/test/megaco_test_lib.erl | 10 ++--
2 files changed, 76 insertions(+), 26 deletions(-)
(limited to 'lib')
diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl
index ded1506271..383e3df774 100644
--- a/lib/megaco/test/megaco_mess_test.erl
+++ b/lib/megaco/test/megaco_mess_test.erl
@@ -34,12 +34,12 @@
%% -compile(export_all).
-export([
- all/0,groups/0,init_per_group/2,end_per_group/2,
- init_per_testcase/2,
- end_per_testcase/2,
+ all/0, groups/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
connect/1,
-
request_and_reply_plain/1,
request_and_no_reply/1,
@@ -347,39 +347,83 @@ end_per_testcase(Case, Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
- [connect, {group, request_and_reply},
- {group, pending_ack}, dist, {group, tickets}].
+ [
+ connect,
+ {group, request_and_reply},
+ {group, pending_ack},
+ dist,
+ {group, tickets}
+ ].
groups() ->
- [{request_and_reply, [],
- [request_and_reply_plain, request_and_no_reply,
+ [
+ {request_and_reply, [],
+ [request_and_reply_plain,
+ request_and_no_reply,
request_and_reply_pending_ack_no_pending,
request_and_reply_pending_ack_one_pending,
single_trans_req_and_reply,
single_trans_req_and_reply_sendopts,
- request_and_reply_and_ack, request_and_reply_and_no_ack,
+ request_and_reply_and_ack,
+ request_and_reply_and_no_ack,
request_and_reply_and_late_ack,
trans_req_and_reply_and_req]},
{pending_ack, [],
[pending_ack_plain,
request_and_pending_and_late_reply]},
{tickets, [],
- [otp_4359, otp_4836, otp_5805, otp_5881, otp_5887,
- otp_6253, otp_6275, otp_6276, {group, otp_6442},
- {group, otp_6865}, otp_7189, otp_7259, otp_7713,
- {group, otp_8183}, otp_8212]},
+ [otp_4359,
+ otp_4836,
+ otp_5805,
+ otp_5881,
+ otp_5887,
+ otp_6253,
+ otp_6275,
+ otp_6276,
+ {group, otp_6442},
+ {group, otp_6865},
+ otp_7189,
+ otp_7259,
+ otp_7713,
+ {group, otp_8183},
+ otp_8212]},
{otp_6442, [],
- [otp_6442_resend_request1, otp_6442_resend_request2,
- otp_6442_resend_reply1, otp_6442_resend_reply2]},
+ [otp_6442_resend_request1,
+ otp_6442_resend_request2,
+ otp_6442_resend_reply1,
+ otp_6442_resend_reply2]},
{otp_6865, [],
[otp_6865_request_and_reply_plain_extra1,
otp_6865_request_and_reply_plain_extra2]},
- {otp_8183, [], [otp_8183_request1]}].
+ {otp_8183, [], [otp_8183_request1]}
+ ].
+
+
+init_per_suite(Config) ->
+ io:format("~w:init_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n", [?MODULE, Config]),
+ Config.
+
+end_per_suite(_Config) ->
+ io:format("~w:end_per_suite -> entry with"
+ "~n _Config: ~p"
+ "~n", [?MODULE, _Config]),
+ ok.
+
init_per_group(_GroupName, Config) ->
+ io:format("~w:init_per_group -> entry with"
+ "~n _GroupName: ~p"
+ "~n Config: ~p"
+ "~n", [?MODULE, _GroupName, Config]),
Config.
end_per_group(_GroupName, Config) ->
+ io:format("~w:end_per_group -> entry with"
+ "~n _GroupName: ~p"
+ "~n Config: ~p"
+ "~n", [?MODULE, _GroupName, Config]),
Config.
@@ -394,12 +438,16 @@ connect(Config) when is_list(Config) ->
PrelMid = preliminary_mid,
MgMid = ipv4_mid(4711),
+ d("connect -> start megaco app",[]),
?VERIFY(ok, application:start(megaco)),
+ d("connect -> start (MG) user ~p",[MgMid]),
?VERIFY(ok, megaco:start_user(MgMid, [{send_mod, bad_send_mod},
{request_timer, infinity},
{reply_timer, infinity}])),
+ d("connect -> get receive info for ~p",[MgMid]),
MgRH = user_info(MgMid, receive_handle),
+ d("connect -> (MG) try connect to MGC",[]),
{ok, PrelCH} = ?VERIFY({ok, _}, megaco:connect(MgRH, PrelMid, sh, self())),
connections([PrelCH]),
@@ -6776,16 +6824,12 @@ rapalr_mg_notify_request_ar(Rid, Tid, Cid) ->
-
-
-
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dist(suite) ->
[];
dist(Config) when is_list(Config) ->
+ ?SKIP("Needs a re-write..."),
[_Local, Dist] = ?ACQUIRE_NODES(2, Config),
d("dist -> start proxy",[]),
megaco_mess_user_test:start_proxy(),
@@ -6897,7 +6941,11 @@ dist(Config) when is_list(Config) ->
?VERIFY(ok, application:stop(megaco)),
?RECEIVE([]),
- d("dist -> done",[]),
+
+ d("dist -> stop proxy",[]),
+ megaco_mess_user_test:stop_proxy(),
+
+ d("dist -> done", []),
ok.
diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl
index 41f6c2c4cb..282fd91b44 100644
--- a/lib/megaco/test/megaco_test_lib.erl
+++ b/lib/megaco/test/megaco_test_lib.erl
@@ -21,6 +21,7 @@
%%----------------------------------------------------------------------
%% Purpose: Lightweight test server
%%----------------------------------------------------------------------
+%%
-module(megaco_test_lib).
@@ -684,7 +685,7 @@ skip(Actual, File, Line) ->
fatal_skip(Actual, File, Line) ->
error(Actual, File, Line),
- exit(shutdown).
+ exit({skipped, {fatal, Actual, File, Line}}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -749,6 +750,7 @@ proxy_loop(OwnId, Controller) ->
proxy_loop(OwnId, Controller)
end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test server callbacks
init_per_testcase(_Case, Config) ->
@@ -852,10 +854,10 @@ watchdog(Pid, Time) ->
prepare_test_case(Actions, N, Config, File, Line) ->
OrigNodes = lookup_config(nodes, Config),
TestNodes = lookup_config(nodenames, Config), %% For testserver
- This = node(),
+ This = node(),
SomeNodes = OrigNodes ++ (TestNodes -- OrigNodes),
- AllNodes = [This | (SomeNodes -- [This])],
- Nodes = pick_n_nodes(N, AllNodes, File, Line),
+ AllNodes = [This | (SomeNodes -- [This])],
+ Nodes = pick_n_nodes(N, AllNodes, File, Line),
start_nodes(Nodes, File, Line),
do_prepare_test_case(Actions, Nodes, Config, File, Line).
--
cgit v1.2.3
From b9e610cf12419c6ffe2c19df4f70009bd98ae50c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Sun, 30 May 2010 08:27:57 +0200
Subject: Test calling a parameterized module within a fun
It is tempting to transform code such as:
fun(X) -> M:foo(X) end
to:
fun M:foo/1
Unfortunately, that transformation is not safe if
M happens to be a parameterized module. Add a test
case so that we don't attempt to do such an optimization
in the future.
---
lib/compiler/test/pmod_SUITE.erl | 4 ++++
1 file changed, 4 insertions(+)
(limited to 'lib')
diff --git a/lib/compiler/test/pmod_SUITE.erl b/lib/compiler/test/pmod_SUITE.erl
index 9a317b5762..3d02adaf52 100644
--- a/lib/compiler/test/pmod_SUITE.erl
+++ b/lib/compiler/test/pmod_SUITE.erl
@@ -96,6 +96,10 @@ basic_1(Config, Opts) ->
?line error = Prop4:bar_bar({s,a,b}),
?line error = Prop4:bar_bar([]),
+ %% Call from a fun.
+ Fun = fun(Arg) -> Prop4:bar(Arg) end,
+ ?line ok = Fun({s,0}),
+
ok.
otp_8447(Config) when is_list(Config) ->
--
cgit v1.2.3
From 841ef48db4b8df0e8a057d5d006e10c3b7e4f325 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Fri, 4 Nov 2011 11:12:03 +0100
Subject: beam_type: Improve FP optimizations in the presence of line numbers
Allow line/1 instructions to be part of a sequence of floating point
instructions to avoid outputting fclearerror / fcheckerror around
every floating point instruction.
---
lib/compiler/src/beam_type.erl | 2 ++
1 file changed, 2 insertions(+)
(limited to 'lib')
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 7fdb8d072a..0c51251f1b 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -168,6 +168,8 @@ simplify_float_1([{set,[D0],[A,B],{alloc,_,{gc_bif,Op0,{f,0}}}}=I|Is]=Is0, Ts0,
simplify_float_1([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
Acc = flush_all(Rs0, Is0, Acc0),
simplify_float_1(Is, tdb_new(), Rs0, [I|Acc]);
+simplify_float_1([{set,_,_,{line,_}}=I|Is], Ts, Rs, Acc) ->
+ simplify_float_1(Is, Ts, Rs, [I|Acc]);
simplify_float_1([I|Is]=Is0, Ts0, Rs0, Acc0) ->
Ts = update(I, Ts0),
{Rs,Acc} = flush(Rs0, Is0, Acc0),
--
cgit v1.2.3
From 887c163802c7c39decd727240cd307964a1d692b Mon Sep 17 00:00:00 2001
From: Micael Karlberg
Date: Fri, 4 Nov 2011 12:25:59 +0100
Subject: Updated release notes and appup file. OTP-9679
---
lib/megaco/doc/src/notes.xml | 43 +++++++++++++++++++++++++++++++++++++
lib/megaco/src/app/megaco.appup.src | 11 ++++++++++
2 files changed, 54 insertions(+)
(limited to 'lib')
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 2aba8db71b..80123e3810 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -36,6 +36,49 @@
section is the version number of Megaco.
+ Megaco 3.15.1.2
+
+ Version 3.15.1.2 supports code replacement in runtime from/to
+ version 3.15.1.1, 3.15.1 and 3.15.
+
+
+ Improvements and new features
+
+
+
+
+ -
+
The profiling test tool has been rewritten.
+ Håkan Mattsson
+ Own Id: OTP-9679
+
+
+
+
+
+
+
+ Fixed bugs and malfunctions
+
+ -
+
+
+
+
+
+
+
+
Megaco 3.15.1.1
Version 3.15.1.1 supports code replacement in runtime from/to
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 7107178d1a..7f6fe0c733 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -139,10 +139,17 @@
%% |
%% v
%% 3.15.1.1
+%% |
+%% v
+%% 3.15.1.2
%%
%%
{"%VSN%",
[
+ {"3.15.1.1",
+ [
+ ]
+ },
{"3.15.1",
[
]
@@ -160,6 +167,10 @@
}
],
[
+ {"3.15.1.1",
+ [
+ ]
+ },
{"3.15.1",
[
]
--
cgit v1.2.3
From ff432e262e65243cbc983fcb002527f8fae8c78b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Fri, 9 Apr 2010 15:50:17 +0200
Subject: EEP-23: Allow variables in fun M:F/A
Currently, the external fun syntax "fun M:F/A" only supports
literals. That is, "fun lists:reverse/1" is allowed but not
"fun M:F/A".
In many real-life situations, some or all of M, F, A are
not known until run-time, and one is forced to either use
the undocumented erlang:make_fun/3 BIF or to use a
"tuple fun" (which is deprecated).
EEP-23 suggests that the parser (erl_parse) should immediately
transform "fun M:F/A" to "erlang:make_fun(M, F, A)". We have
not followed that approach in this implementation, because we
want the abstract code to mirror the source code as closely
as possible, and we also consider erlang:make_fun/3 to
be an implementation detail that we might want to remove in
the future.
Instead, we will change the abstract format for "fun M:F/A" (in a way
that is not backwards compatible), and while we are at it, we will
move the translation from "fun M:F/A" to "erlang:make_fun(M, F, A)"
from sys_pre_expand down to the v3_core pass. We will also update
the debugger and xref to use the new format.
We did consider making the abstract format backward compatible if
no variables were used in the fun, but decided against it. Keeping
it backward compatible would mean that there would be different
abstract formats for the no-variable and variable case, and tools
would have to handle both formats, probably forever.
Reference: http://www.erlang.org/eeps/eep-0023.html
---
lib/compiler/src/sys_pre_expand.erl | 11 ++-
lib/compiler/src/v3_core.erl | 7 ++
lib/compiler/test/fun_SUITE.erl | 52 +++++++++++++-
lib/debugger/src/dbg_ieval.erl | 15 ++++
lib/debugger/src/dbg_iload.erl | 8 +++
lib/debugger/test/fun_SUITE.erl | 52 +++++++++++++-
lib/stdlib/examples/erl_id_trans.erl | 9 ++-
lib/stdlib/src/erl_eval.erl | 3 +-
lib/stdlib/src/erl_lint.erl | 9 ++-
lib/stdlib/src/erl_parse.yrl | 12 +++-
lib/stdlib/src/erl_pp.erl | 10 ++-
lib/stdlib/src/qlc.erl | 5 +-
lib/stdlib/test/erl_eval_SUITE.erl | 6 ++
lib/stdlib/test/erl_pp_SUITE.erl | 5 +-
lib/stdlib/test/qlc_SUITE.erl | 2 +-
lib/syntax_tools/src/erl_syntax.erl | 7 +-
lib/tools/src/xref_reader.erl | 25 ++++---
lib/tools/test/xref_SUITE.erl | 87 +++++++++++++++++++++++-
lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam | Bin 0 -> 1116 bytes
lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl | 18 +++++
20 files changed, 309 insertions(+), 34 deletions(-)
create mode 100644 lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam
create mode 100644 lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl
(limited to 'lib')
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 0fa1fea09f..7facbafb6d 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -554,9 +554,14 @@ fun_tq(Lf, {function,F,A}=Function, St0) ->
{{'fun',Lf,Function,{Index,Uniq,Fname}},
St2#expand{fun_index=Index+1}}
end;
-fun_tq(L, {function,M,F,A}, St) ->
- {{call,L,{remote,L,{atom,L,erlang},{atom,L,make_fun}},
- [{atom,L,M},{atom,L,F},{integer,L,A}]},St};
+fun_tq(L, {function,M,F,A}, St) when is_atom(M), is_atom(F), is_integer(A) ->
+ %% This is the old format for external funs, generated by a pre-R15
+ %% compiler. That means that a tool, such as the debugger or xref,
+ %% directly invoked this module with the abstract code from a
+ %% pre-R15 BEAM file. Be helpful, and translate it to the new format.
+ fun_tq(L, {function,{atom,L,M},{atom,L,F},{integer,L,A}}, St);
+fun_tq(Lf, {function,_,_,_}=ExtFun, St) ->
+ {{'fun',Lf,ExtFun},St};
fun_tq(Lf, {clauses,Cs0}, St0) ->
Uniq = erlang:hash(Cs0, (1 bsl 27)-1),
{Cs1,St1} = fun_clauses(Cs0, St0),
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 6f3590b156..6885405ae0 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -573,6 +573,13 @@ expr({'catch',L,E0}, St0) ->
expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) ->
Lanno = lineno_anno(L, St),
{#c_var{anno=Lanno++[{id,Id}],name={F,A}},[],St};
+expr({'fun',L,{function,M,F,A}}, St0) ->
+ {As,Aps,St1} = safe_list([M,F,A], St0),
+ Lanno = lineno_anno(L, St1),
+ {#icall{anno=#a{anno=Lanno},
+ module=#c_literal{val=erlang},
+ name=#c_literal{val=make_fun},
+ args=As},Aps,St1};
expr({'fun',L,{clauses,Cs},Id}, St) ->
fun_tq(Id, Cs, L, St);
expr({call,L,{remote,_,M,F},As0}, #core{wanted=Wanted}=St0) ->
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index 368a5815bf..6067ee8e06 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -20,7 +20,11 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
- test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1]).
+ test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1,
+ external/1]).
+
+%% Internal export.
+-export([call_me/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -28,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [test1, overwritten_fun, otp_7202, bif_fun].
+ [test1,overwritten_fun,otp_7202,bif_fun,external].
groups() ->
[].
@@ -45,7 +49,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
%%% The help functions below are copied from emulator:bs_construct_SUITE.
-define(T(B, L), {B, ??B, L}).
@@ -152,4 +155,47 @@ bif_fun(Config) when is_list(Config) ->
?line F = fun abs/1,
?line 5 = F(-5),
ok.
+
+-define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)).
+-define(APPLY2(M, F, A),
+ (fun(Map) ->
+ Id = fun(I) -> I end,
+ List = [x,y],
+ List = Map(Id, List),
+ {type,external} = erlang:fun_info(Map, type)
+ end)(fun M:F/A)).
+external(Config) when is_list(Config) ->
+ Mod = id(?MODULE),
+ Func = id(call_me),
+ Arity = id(1),
+
+ ?APPLY(?MODULE, call_me, 1),
+ ?APPLY(?MODULE, call_me, Arity),
+ ?APPLY(?MODULE, Func, 1),
+ ?APPLY(?MODULE, Func, Arity),
+ ?APPLY(Mod, call_me, 1),
+ ?APPLY(Mod, call_me, Arity),
+ ?APPLY(Mod, Func, 1),
+ ?APPLY(Mod, Func, Arity),
+
+ ListsMod = id(lists),
+ ListsMap = id(map),
+ ListsArity = id(2),
+
+ ?APPLY2(lists, map, 2),
+ ?APPLY2(lists, map, ListsArity),
+ ?APPLY2(lists, ListsMap, 2),
+ ?APPLY2(lists, ListsMap, ListsArity),
+ ?APPLY2(ListsMod, map, 2),
+ ?APPLY2(ListsMod, map, ListsArity),
+ ?APPLY2(ListsMod, ListsMap, 2),
+ ?APPLY2(ListsMod, ListsMap, ListsArity),
+
+ ok.
+
+call_me(I) ->
+ {ok,I}.
+
+id(I) ->
+ I.
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index df725ed9e5..2e88c35741 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -768,6 +768,21 @@ expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) ->
end,
{value,Fun,Bs};
+%% Construct an external fun.
+expr({make_ext_fun,Line,MFA0}, Bs0, Ieval0) ->
+ {[M,F,A],Bs} = eval_list(MFA0, Bs0, Ieval0),
+ try erlang:make_fun(M, F, A) of
+ Value ->
+ {value,Value,Bs}
+ catch
+ error:badarg ->
+ Ieval1 = Ieval0#ieval{line=Line},
+ Ieval2 = dbg_istk:push(Bs0, Ieval1, false),
+ Ieval = Ieval2#ieval{module=erlang,function=make_fun,
+ arguments=[M,F,A],line=-1},
+ exception(error, badarg, Bs, Ieval, true)
+ end;
+
%% Common test adaptation
expr({call_remote,0,ct_line,line,As0,Lc}, Bs0, Ieval0) ->
{As,_Bs} = eval_list(As0, Bs0, Ieval0),
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index ce5631e45f..3c95ef8068 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -369,6 +369,14 @@ expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}, _Lc) ->
As = new_vars(A, Line),
Cs = [{clause,Line,As,[],[{local_call,Line,F,As,true}]}],
{make_fun,Line,Name,Cs};
+expr({'fun',Line,{function,{atom,_,M},{atom,_,F},{integer,_,A}}}, _Lc)
+ when 0 =< A, A =< 255 ->
+ %% New format in R15 for fun M:F/A (literal values).
+ {value,Line,erlang:make_fun(M, F, A)};
+expr({'fun',Line,{function,M,F,A}}, _Lc) ->
+ %% New format in R15 for fun M:F/A (one or more variables).
+ MFA = expr_list([M,F,A]),
+ {make_ext_fun,Line,MFA};
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}, _Lc) ->
{dbg,Line,self,[]};
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}, _Lc) ->
diff --git a/lib/debugger/test/fun_SUITE.erl b/lib/debugger/test/fun_SUITE.erl
index 8103d9c692..a06cdc7165 100644
--- a/lib/debugger/test/fun_SUITE.erl
+++ b/lib/debugger/test/fun_SUITE.erl
@@ -24,8 +24,10 @@
init_per_testcase/2,end_per_testcase/2,
init_per_suite/1,end_per_suite/1,
good_call/1,bad_apply/1,bad_fun_call/1,badarity/1,
- ext_badarity/1,otp_6061/1]).
--export([nothing/0]).
+ ext_badarity/1,otp_6061/1,external/1]).
+
+%% Internal exports.
+-export([nothing/0,call_me/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -46,7 +48,7 @@ end_per_group(_GroupName, Config) ->
cases() ->
[good_call, bad_apply, bad_fun_call, badarity,
- ext_badarity, otp_6061].
+ ext_badarity, otp_6061, external].
init_per_testcase(_Case, Config) ->
test_lib:interpret(?MODULE),
@@ -244,3 +246,47 @@ test_otp_6061(Starter) ->
fun() -> Starter ! working end,
fun() -> Starter ! not_working end],
lists:foreach(fun(P)->(lists:nth(P,PassesF))() end,Passes).
+
+-define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)).
+-define(APPLY2(M, F, A),
+ (fun(Map) ->
+ Id = fun(I) -> I end,
+ List = [x,y],
+ List = Map(Id, List),
+ {type,external} = erlang:fun_info(Map, type)
+ end)(fun M:F/A)).
+
+external(Config) when is_list(Config) ->
+ Mod = id(?MODULE),
+ Func = id(call_me),
+ Arity = id(1),
+
+ ?APPLY(?MODULE, call_me, 1),
+ ?APPLY(?MODULE, call_me, Arity),
+ ?APPLY(?MODULE, Func, 1),
+ ?APPLY(?MODULE, Func, Arity),
+ ?APPLY(Mod, call_me, 1),
+ ?APPLY(Mod, call_me, Arity),
+ ?APPLY(Mod, Func, 1),
+ ?APPLY(Mod, Func, Arity),
+
+ ListsMod = id(lists),
+ ListsMap = id(map),
+ ListsArity = id(2),
+
+ ?APPLY2(lists, map, 2),
+ ?APPLY2(lists, map, ListsArity),
+ ?APPLY2(lists, ListsMap, 2),
+ ?APPLY2(lists, ListsMap, ListsArity),
+ ?APPLY2(ListsMod, map, 2),
+ ?APPLY2(ListsMod, map, ListsArity),
+ ?APPLY2(ListsMod, ListsMap, 2),
+ ?APPLY2(ListsMod, ListsMap, ListsArity),
+
+ ok.
+
+call_me(I) ->
+ {ok,I}.
+
+id(I) ->
+ I.
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index b63acdd40a..72e41d6473 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -419,7 +419,14 @@ expr({'fun',Line,Body}) ->
{'fun',Line,{clauses,Cs1}};
{function,F,A} ->
{'fun',Line,{function,F,A}};
- {function,M,F,A} -> %R10B-6: fun M:F/A.
+ {function,M,F,A} when is_atom(M), is_atom(F), is_integer(A) ->
+ %% R10B-6: fun M:F/A. (Backward compatibility)
+ {'fun',Line,{function,M,F,A}};
+ {function,M0,F0,A0} ->
+ %% R15: fun M:F/A with variables.
+ M = expr(M0),
+ F = expr(F0),
+ A = expr(A0),
{'fun',Line,{function,M,F,A}}
end;
expr({call,Line,F0,As0}) ->
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 4f4fa16040..88a0094d57 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -256,7 +256,8 @@ expr({'receive',_,Cs}, Bs, Lf, Ef, RBs) ->
expr({'receive',_, Cs, E, TB}, Bs0, Lf, Ef, RBs) ->
{value,T,Bs} = expr(E, Bs0, Lf, Ef, none),
receive_clauses(T, Cs, {TB,Bs}, Bs0, Lf, Ef, [], RBs);
-expr({'fun',_Line,{function,Mod,Name,Arity}}, Bs, _Lf, _Ef, RBs) ->
+expr({'fun',_Line,{function,Mod0,Name0,Arity0}}, Bs0, Lf, Ef, RBs) ->
+ {[Mod,Name,Arity],Bs} = expr_list([Mod0,Name0,Arity0], Bs0, Lf, Ef),
F = erlang:make_fun(Mod, Name, Arity),
ret_expr(F, Bs, RBs);
expr({'fun',_Line,{function,Name,Arity}}, _Bs0, _Lf, _Ef, _RBs) -> % R8
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 78b996d94b..5d45260fe9 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2127,8 +2127,13 @@ expr({'fun',Line,Body}, Vt, St) ->
true -> {[],St};
false -> {[],call_function(Line, F, A, St)}
end;
- {function,_M,_F,_A} ->
- {[],St}
+ {function,M,F,A} when is_atom(M), is_atom(F), is_integer(A) ->
+ %% Compatibility with pre-R15 abstract format.
+ {[],St};
+ {function,M,F,A} ->
+ %% New in R15.
+ {Bvt, St1} = expr_list([M,F,A], Vt, St),
+ {vtupdate(Bvt, Vt),St1}
end;
expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
{Rvt,St1} = expr(E, Vt, St0),
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 709bd83e6f..928c10f7f2 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -35,7 +35,7 @@ tuple
%struct
record_expr record_tuple record_field record_fields
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
-fun_expr fun_clause fun_clauses
+fun_expr fun_clause fun_clauses atom_or_var integer_or_var
try_expr try_catch try_clause try_clauses query_expr
function_call argument_list
exprs guard
@@ -395,11 +395,17 @@ receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' :
fun_expr -> 'fun' atom '/' integer :
{'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4')}}.
-fun_expr -> 'fun' atom ':' atom '/' integer :
- {'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4'),element(3,'$6')}}.
+fun_expr -> 'fun' atom_or_var ':' atom_or_var '/' integer_or_var :
+ {'fun',?line('$1'),{function,'$2','$4','$6'}}.
fun_expr -> 'fun' fun_clauses 'end' :
build_fun(?line('$1'), '$2').
+atom_or_var -> atom : '$1'.
+atom_or_var -> var : '$1'.
+
+integer_or_var -> integer : '$1'.
+integer_or_var -> var : '$1'.
+
fun_clauses -> fun_clause : ['$1'].
fun_clauses -> fun_clause ';' fun_clauses : ['$1' | '$3'].
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 7dc19f2e9b..6b5aa951cf 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -457,8 +457,16 @@ lexpr({'fun',_,{function,F,A}}, _Prec, _Hook) ->
leaf(format("fun ~w/~w", [F,A]));
lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Hook) ->
{force_nl,fun_info(Extra),leaf(format("fun ~w/~w", [F,A]))};
-lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook) ->
+lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook)
+ when is_atom(M), is_atom(F), is_integer(A) ->
+ %% For backward compatibility with pre-R15 abstract format.
leaf(format("fun ~w:~w/~w", [M,F,A]));
+lexpr({'fun',_,{function,M,F,A}}, _Prec, Hook) ->
+ %% New format in R15.
+ NameItem = lexpr(M, Hook),
+ CallItem = lexpr(F, Hook),
+ ArityItem = lexpr(A, Hook),
+ ["fun ",NameItem,$:,CallItem,$/,ArityItem];
lexpr({'fun',_,{clauses,Cs}}, _Prec, Hook) ->
{list,[{first,'fun',fun_clauses(Cs, Hook)},'end']};
lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Hook) ->
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index f5e180b4bd..2b691e6abf 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1272,7 +1272,10 @@ abstr_term(Fun, Line) when is_function(Fun) ->
case erlang:fun_info(Fun, type) of
{type, external} ->
{module, Module} = erlang:fun_info(Fun, module),
- {'fun', Line, {function,Module,Name,Arity}};
+ {'fun', Line, {function,
+ {atom,Line,Module},
+ {atom,Line,Name},
+ {integer,Line,Arity}}};
{type, local} ->
{'fun', Line, {function,Name,Arity}}
end
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index 784c7cb86e..369d8b224e 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1036,6 +1036,12 @@ funs(Config) when is_list(Config) ->
lists:usort([run_many_args(SAs) || SAs <- many_args(MaxArgs)]),
?line {'EXIT',{{argument_limit,_},_}} =
(catch run_many_args(many_args1(MaxArgs+1))),
+
+ ?line check(fun() -> M = lists, F = fun M:reverse/1,
+ [1,2] = F([2,1]), ok end,
+ "begin M = lists, F = fun M:reverse/1,"
+ " [1,2] = F([2,1]), ok end.",
+ ok),
ok.
run_many_args({S, As}) ->
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 280c95b1aa..64853ca078 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -116,7 +116,6 @@ func(Config) when is_list(Config) ->
{func_3,
<<"t() -> fun t/0.">>},
{func_4,
- %% Has already been expanded away in sys_pre_expand.
<<"t() -> fun modul:foo/3.">>},
{func_5, % 'when' is moved down one line
<<"tkjlksjflksdjflsdjlk()
@@ -127,7 +126,9 @@ func(Config) when is_list(Config) ->
<<"t() ->
(fun() ->
true
- end)().">>}
+ end)().">>},
+ {func_7,
+ <<"t(M, F, A) -> fun M:F/A.">>}
],
?line compile(Config, Ts),
ok.
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 98eeaee118..8a9d8f7883 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -6632,7 +6632,7 @@ otp_7232(Config) when is_list(Config) ->
{call,_,
{remote,_,{atom,_,qlc},{atom,_,sort}},
[{cons,_,
- {'fun',_,{function,math,sqrt,_}},
+ {'fun',_,{function,{atom,_,math},{atom,_,sqrt},_}},
{cons,_,
{string,_,\"<0.4.1>\"}, % could use list_to_pid..
{cons,_,{string,_,\"#Ref<\"++_},{nil,_}}}},
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 9df5f26454..7f58fda519 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -6093,11 +6093,16 @@ implicit_fun_name(Node) ->
{'fun', Pos, {function, Atom, Arity}} ->
arity_qualifier(set_pos(atom(Atom), Pos),
set_pos(integer(Arity), Pos));
- {'fun', Pos, {function, Module, Atom, Arity}} ->
+ {'fun', Pos, {function, Module, Atom, Arity}}
+ when is_atom(Module), is_atom(Atom), is_integer(Arity) ->
+ %% Backward compatibility with pre-R15 abstract format.
module_qualifier(set_pos(atom(Module), Pos),
arity_qualifier(
set_pos(atom(Atom), Pos),
set_pos(integer(Arity), Pos)));
+ {'fun', Pos, {function, Module, Atom, Arity}} ->
+ %% New in R15: fun M:F/A.
+ module_qualifier(Module, arity_qualifier(Atom, Arity));
Node1 ->
data(Node1)
end.
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index d22f0df164..92f0c45c7b 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -158,15 +158,20 @@ expr({'try',_Line,Es,Scs,Ccs,As}, S) ->
S2 = clauses(Scs, S1),
S3 = clauses(Ccs, S2),
expr(As, S3);
-expr({call, Line,
- {remote, _, {atom,_,erlang}, {atom,_,make_fun}},
- [{atom,_,Mod}, {atom,_,Fun}, {integer,_,Arity}]}, S) ->
- %% Added in R10B-6. M:F/A.
- expr({'fun', Line, {function, Mod, Fun, Arity}}, S);
-expr({'fun', Line, {function, Mod, Name, Arity}}, S) ->
- %% Added in R10B-6. M:F/A.
+expr({'fun', Line, {function, {atom,_,Mod},
+ {atom,_,Name},
+ {integer,_,Arity}}}, S) ->
+ %% New format in R15. M:F/A (literals).
As = lists:duplicate(Arity, {atom, Line, foo}),
external_call(Mod, Name, As, Line, false, S);
+expr({'fun', Line, {function, Mod, Name, {integer,_,Arity}}}, S) ->
+ %% New format in R15. M:F/A (one or more variables).
+ As = lists:duplicate(Arity, {atom, Line, foo}),
+ external_call(erlang, apply, [Mod, Name, list2term(As)], Line, true, S);
+expr({'fun', Line, {function, Mod, Name, _Arity}}, S) ->
+ %% New format in R15. M:F/A (one or more variables).
+ As = {var, Line, '_'},
+ external_call(erlang, apply, [Mod, Name, As], Line, true, S);
expr({'fun', Line, {function, Name, Arity}, _Extra}, S) ->
%% Added in R8.
handle_call(local, S#xrefr.module, Name, Arity, Line, S);
@@ -286,10 +291,10 @@ check_funarg(W, ArgsList, Line, S) ->
expr(ArgsList, S1).
funarg({'fun', _, _Clauses, _Extra}, _S) -> true;
-funarg({var, _, Var}, S) -> member(Var, S#xrefr.funvars);
-funarg({call,_,{remote,_,{atom,_,erlang},{atom,_,make_fun}},_MFA}, _S) ->
- %% R10B-6. M:F/A.
+funarg({'fun', _, {function,_,_,_}}, _S) ->
+ %% New abstract format for fun M:F/A in R15.
true;
+funarg({var, _, Var}, S) -> member(Var, S#xrefr.funvars);
funarg(_, _S) -> false.
fun_args(apply2, [FunArg, Args]) -> {FunArg, Args};
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 2f83ab4995..e0876381ca 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -46,7 +46,8 @@
-export([
add/1, default/1, info/1, lib/1, read/1, read2/1, remove/1,
replace/1, update/1, deprecated/1, trycatch/1,
- abstract_modules/1, fun_mfa/1, qlc/1]).
+ abstract_modules/1, fun_mfa/1, fun_mfa_r14/1,
+ fun_mfa_vars/1, qlc/1]).
-export([
analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
@@ -82,7 +83,7 @@ groups() ->
{files, [],
[add, default, info, lib, read, read2, remove, replace,
update, deprecated, trycatch, abstract_modules, fun_mfa,
- qlc]},
+ fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
[analyze, basic, md, q, variables, unused_locals]},
{misc, [], [format_error, otp_7423, otp_7831]}].
@@ -1771,6 +1772,88 @@ fun_mfa(Conf) when is_list(Conf) ->
?line ok = file:delete(Beam),
ok.
+%% Same as the previous test case, except that we use a BEAM file
+%% that was compiled by an R14 compiler to test backward compatibility.
+fun_mfa_r14(Conf) when is_list(Conf) ->
+ Dir = ?config(data_dir, Conf),
+ MFile = fname(Dir, "fun_mfa_r14"),
+
+ A = fun_mfa_r14,
+ {ok, _} = xref:start(s),
+ {ok, A} = xref:add_module(s, MFile, {warnings,false}),
+ {ok, [{{{A,t,0},{'$M_EXPR','$F_EXPR',0}},[7]},
+ {{{A,t,0},{A,t,0}},[6]},
+ {{{A,t1,0},{'$M_EXPR','$F_EXPR',0}},[11]},
+ {{{A,t1,0},{A,t,0}},[10]},
+ {{{A,t2,0},{A,t,0}},[14]},
+ {{{A,t3,0},{A,t3,0}},[17]}]} =
+ xref:q(s, "(Lin) E"),
+
+ ok = check_state(s),
+ xref:stop(s),
+
+ ok.
+
+%% fun M:F/A with varibles.
+fun_mfa_vars(Conf) when is_list(Conf) ->
+ Dir = ?copydir,
+ File = fname(Dir, "fun_mfa_vars.erl"),
+ MFile = fname(Dir, "fun_mfa_vars"),
+ Beam = fname(Dir, "fun_mfa_vars.beam"),
+ Test = <<"-module(fun_mfa_vars).
+
+ -export([t/1, t1/1, t2/3]).
+
+ t(Mod) ->
+ F = fun Mod:bar/2,
+ (F)(a, b).
+
+ t1(Name) ->
+ F = fun ?MODULE:Name/1,
+ (F)(a).
+
+ t2(Mod, Name, Arity) ->
+ F = fun Mod:Name/Arity,
+ (F)(a).
+
+ t3(Arity) ->
+ F = fun ?MODULE:t/Arity,
+ (F)(1, 2, 3).
+
+ t4(Mod, Name) ->
+ F = fun Mod:Name/3,
+ (F)(a, b, c).
+
+ t5(Mod, Arity) ->
+ F = fun Mod:t/Arity,
+ (F)().
+ ">>,
+
+ ok = file:write_file(File, Test),
+ A = fun_mfa_vars,
+ {ok, A} = compile:file(File, [report,debug_info,{outdir,Dir}]),
+ {ok, _} = xref:start(s),
+ {ok, A} = xref:add_module(s, MFile, {warnings,false}),
+ {ok, [{{{A,t,1},{'$M_EXPR','$F_EXPR',2}},[7]},
+ {{{A,t,1},{'$M_EXPR',bar,2}},[6]},
+ {{{A,t1,1},{'$M_EXPR','$F_EXPR',1}},[11]},
+ {{{A,t1,1},{A,'$F_EXPR',1}},[10]},
+ {{{A,t2,3},{'$M_EXPR','$F_EXPR',-1}},[14]},
+ {{{A,t2,3},{'$M_EXPR','$F_EXPR',1}},[15]},
+ {{{A,t3,1},{'$M_EXPR','$F_EXPR',3}},[19]},
+ {{{A,t3,1},{fun_mfa_vars,t,-1}},[18]},
+ {{{A,t4,2},{'$M_EXPR','$F_EXPR',3}},[22,23]},
+ {{{A,t5,2},{'$M_EXPR','$F_EXPR',0}},[27]},
+ {{{A,t5,2},{'$M_EXPR',t,-1}},[26]}]} =
+ xref:q(s, "(Lin) E"),
+
+ ok = check_state(s),
+ xref:stop(s),
+
+ ok = file:delete(File),
+ ok = file:delete(Beam),
+ ok.
+
qlc(suite) -> [];
qlc(doc) -> ["OTP-5195: A bug fix when using qlc:q/1,2."];
qlc(Conf) when is_list(Conf) ->
diff --git a/lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam
new file mode 100644
index 0000000000..4645525690
Binary files /dev/null and b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam differ
diff --git a/lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl
new file mode 100644
index 0000000000..293bd83a8b
--- /dev/null
+++ b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl
@@ -0,0 +1,18 @@
+-module(fun_mfa_r14).
+
+-export([t/0, t1/0, t2/0, t3/0]).
+
+t() ->
+ F = fun ?MODULE:t/0,
+ (F)().
+
+t1() ->
+ F = fun t/0,
+ (F)().
+
+t2() ->
+ fun ?MODULE:t/0().
+
+t3() ->
+ fun t3/0().
+
--
cgit v1.2.3
From af5edfe20b24116fa892a37a746f4053fde6039b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 29 Sep 2011 08:13:24 +0200
Subject: beam_asm: Strenghten the calculation of Uniq for funs
Funs are identified by a triple, , where Module is
the module name, Uniq is a 27 bit hash value of some intermediate
representation of the code for the fun, and index is a small integer.
When a fun is loaded, the triple for the fun will be compared to
previously loaded funs. If all elements in the triple in the newly
loaded fun are the same, the newly loaded fun will replace the previous
fun. The idea is that if Uniq are the same, the code for the fun is also
the same.
The problem is that Uniq is only based on the intermediate representation
of the fun itself. If the fun calls local functions in the same module,
Uniq may remain the same even if the behavior of the fun has been changed.
See
http://erlang.org/pipermail/erlang-bugs/2007-June/000368.htlm
for an example.
As a long-term plan to fix this problem, the NewIndex and NewUniq
fields was added to each fun in the R8 release (where NewUniq is the
MD5 of the BEAM code for the module). Unfortunately, it turns
out that the compiler does not assign unique value to NewIndex (if it
isn't tested, it doesn't work), so we cannot use the
triple as identification.
It would be possible to use , but that seems
ugly. Therefore, fix the problem by making Uniq more unique by
taking 27 bits from the MD5 for the BEAM code. That only requires
a change to the compiler.
Also update a test case for cover, which now fails because of the
stronger Uniq calculation. (The comment in test case about why the
Pid2 process survived is not correct.)
---
lib/compiler/src/beam_asm.erl | 52 +++++++++++++++++--------
lib/compiler/src/beam_dict.erl | 13 ++++---
lib/tools/test/cover_SUITE.erl | 31 +++++----------
lib/tools/test/cover_SUITE_data/otp_6115/f1.erl | 11 +++---
4 files changed, 59 insertions(+), 48 deletions(-)
(limited to 'lib')
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 6e63c4d0f2..9b46abe697 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -146,8 +146,10 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->
Essentials0 = [AtomChunk,CodeChunk,StringChunk,ImportChunk,
ExpChunk,LambdaChunk,LiteralChunk],
- Essentials = [iolist_to_binary(C) || C <- Essentials0],
- {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, Essentials),
+ Essentials1 = [iolist_to_binary(C) || C <- Essentials0],
+ MD5 = module_md5(Essentials1),
+ Essentials = finalize_fun_table(Essentials1, MD5),
+ {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, MD5),
AttrChunk = chunk(<<"Attr">>, Attributes),
CompileChunk = chunk(<<"CInf">>, Compile),
@@ -166,6 +168,24 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->
end,
build_form(<<"BEAM">>, Chunks).
+%% finalize_fun_table(Essentials, MD5) -> FinalizedEssentials
+%% Update the 'old_uniq' field in the entry for each fun in the
+%% 'FunT' chunk. We'll use part of the MD5 for the module as a
+%% unique value.
+
+finalize_fun_table(Essentials, MD5) ->
+ [finalize_fun_table_1(E, MD5) || E <- Essentials].
+
+finalize_fun_table_1(<<"FunT",Keep:8/binary,Table0/binary>>, MD5) ->
+ <> = MD5,
+ Table = finalize_fun_table_2(Table0, Uniq, <<>>),
+ <<"FunT",Keep/binary,Table/binary>>;
+finalize_fun_table_1(Chunk, _) -> Chunk.
+
+finalize_fun_table_2(<>, Uniq, Acc) ->
+ finalize_fun_table_2(T, Uniq, <>);
+finalize_fun_table_2(<<>>, _, Acc) -> Acc.
+
%% Build an IFF form.
build_form(Id, Chunks0) when byte_size(Id) =:= 4, is_list(Chunks0) ->
@@ -202,7 +222,7 @@ flatten_exports(Exps) ->
flatten_imports(Imps) ->
list_to_binary(map(fun({M,F,A}) -> <> end, Imps)).
-build_attributes(Opts, SourceFile, Attr, Essentials) ->
+build_attributes(Opts, SourceFile, Attr, MD5) ->
Misc = case member(slim, Opts) of
false ->
{{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(),
@@ -210,7 +230,7 @@ build_attributes(Opts, SourceFile, Attr, Essentials) ->
true -> []
end,
Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],
- {term_to_binary(calc_vsn(Attr, Essentials)),term_to_binary(Compile)}.
+ {term_to_binary(set_vsn_attribute(Attr, MD5)),term_to_binary(Compile)}.
build_line_table(Dict) ->
{NumLineInstrs,NumFnames0,Fnames0,NumLines,Lines0} =
@@ -243,32 +263,30 @@ encode_line_items([], _) -> [].
%% We'll not change an existing 'vsn' attribute.
%%
-calc_vsn(Attr, Essentials0) ->
+set_vsn_attribute(Attr, MD5) ->
case keymember(vsn, 1, Attr) of
true -> Attr;
false ->
- Essentials = filter_essentials(Essentials0),
- <> = erlang:md5(Essentials),
+ <> = MD5,
[{vsn,[Number]}|Attr]
end.
+module_md5(Essentials0) ->
+ Essentials = filter_essentials(Essentials0),
+ erlang:md5(Essentials).
+
%% filter_essentials([Chunk]) -> [Chunk']
%% Filter essentials so that we obtain the same MD5 as code:module_md5/1 and
-%% beam_lib:md5/1 would calculate for this module.
+%% beam_lib:md5/1 would calculate for this module. Note that at this
+%% point, the 'old_uniq' entry for each fun in the 'FunT' chunk is zeroed,
+%% so there is no need to go through the 'FunT' chunk.
-filter_essentials([<<"FunT",_Sz:4/binary,Entries:4/binary,Table0/binary>>|T]) ->
- Table = filter_funtab(Table0, <<0:32>>),
- [Entries,Table|filter_essentials(T)];
filter_essentials([<<_Tag:4/binary,Sz:32,Data:Sz/binary,_Padding/binary>>|T]) ->
[Data|filter_essentials(T)];
filter_essentials([<<>>|T]) ->
filter_essentials(T);
filter_essentials([]) -> [].
-filter_funtab(<>, Zero) ->
- [Important,Zero|filter_funtab(T, Zero)];
-filter_funtab(<<>>, _) -> [].
-
bif_type(fnegate, 1) -> {op,fnegate};
bif_type(fadd, 2) -> {op,fadd};
bif_type(fsub, 2) -> {op,fsub};
@@ -310,8 +328,8 @@ make_op({test,Cond,Fail,Ops}, Dict) when is_list(Ops) ->
encode_op(Cond, [Fail|Ops], Dict);
make_op({test,Cond,Fail,Live,[Op|Ops],Dst}, Dict) when is_list(Ops) ->
encode_op(Cond, [Fail,Op,Live|Ops++[Dst]], Dict);
-make_op({make_fun2,{f,Lbl},Index,OldUniq,NumFree}, Dict0) ->
- {Fun,Dict} = beam_dict:lambda(Lbl, Index, OldUniq, NumFree, Dict0),
+make_op({make_fun2,{f,Lbl},Index,_OldUniq,NumFree}, Dict0) ->
+ {Fun,Dict} = beam_dict:lambda(Lbl, Index, NumFree, Dict0),
make_op({make_fun2,Fun}, Dict);
make_op({kill,Y}, Dict) ->
make_op({init,Y}, Dict);
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index ee76623976..f82a8f2e3e 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -22,7 +22,7 @@
-export([new/0,opcode/2,highest_opcode/1,
atom/2,local/4,export/4,import/4,
- string/2,lambda/5,literal/2,line/2,fname/2,
+ string/2,lambda/4,literal/2,line/2,fname/2,
atom_table/1,local_table/1,export_table/1,import_table/1,
string_table/1,lambda_table/1,literal_table/1,
line_table/1]).
@@ -133,13 +133,16 @@ string(Str, Dict) when is_list(Str) ->
{NextOffset-Offset,Dict}
end.
-%% Returns the index for a funentry (adding it to the table if necessary).
-%% lambda(Lbl, Index, Uniq, NumFree, Dict) -> {Index,Dict'}
--spec lambda(label(), non_neg_integer(), integer(), non_neg_integer(), bdict()) ->
+%% Returns the index for a fun entry.
+%% lambda(Lbl, Index, NumFree, Dict) -> {Index,Dict'}
+-spec lambda(label(), non_neg_integer(), non_neg_integer(), bdict()) ->
{non_neg_integer(), bdict()}.
-lambda(Lbl, Index, OldUniq, NumFree, #asm{lambdas=Lambdas0}=Dict) ->
+lambda(Lbl, Index, NumFree, #asm{lambdas=Lambdas0}=Dict) ->
OldIndex = length(Lambdas0),
+ %% Initialize OldUniq to 0. It will be set to an unique value
+ %% based on the MD5 checksum of the BEAM code for the module.
+ OldUniq = 0,
Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0],
{OldIndex,Dict#asm{lambdas=Lambdas}}.
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index fe7f92de78..881a3c2997 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -583,21 +583,14 @@ otp_6115_1(Config) ->
%% called -- running cover compiled code when there is no cover
%% server and thus no ets tables to bump counters in, makes no
%% sense.
- ?line Pid1 = f1:start_fail(),
-
- %% If f1 is cover compiled, a process P is started with a
- %% reference to the fun created in start_ok/0, and
- %% cover:stop() is called, then P should survive.
- %% This is because (the fun held by) P always references the current
- %% version of the module, and is thus not affected by the cover
- %% compiled version being unloaded.
- ?line Pid2 = f1:start_ok(),
+ Pid1 = f1:start_a(),
+ Pid2 = f1:start_b(),
%% Now stop cover
?line cover:stop(),
- %% Ensure that f1 is loaded (and not cover compiled), that Pid1
- %% is dead and Pid2 is alive, but with no reference to old code
+ %% Ensure that f1 is loaded (and not cover compiled), and that
+ %% both Pid1 and Pid2 are dead.
case code:which(f1) of
Beam when is_list(Beam) ->
ok;
@@ -608,19 +601,15 @@ otp_6115_1(Config) ->
undefined ->
ok;
_PI1 ->
- RefToOldP = erlang:check_process_code(Pid1, f1),
- ?line ?t:fail({"Pid1 still alive", RefToOldP})
+ RefToOldP1 = erlang:check_process_code(Pid1, f1),
+ ?t:fail({"Pid1 still alive", RefToOldP1})
end,
case process_info(Pid2) of
- PI2 when is_list(PI2) ->
- case erlang:check_process_code(Pid2, f2) of
- false ->
- ok;
- true ->
- ?line ?t:fail("Pid2 has ref to old code")
- end;
undefined ->
- ?line ?t:fail("Pid2 has died")
+ ok;
+ _PI2 ->
+ RefToOldP2 = erlang:check_process_code(Pid1, f2),
+ ?t:fail({"Pid2 still alive", RefToOldP2})
end,
?line file:set_cwd(CWD),
diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
index b659e5d818..5399b33f19 100644
--- a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
+++ b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
@@ -1,12 +1,13 @@
-module(f1).
--export([start_fail/0, start_ok/0]).
+-export([start_a/0, start_b/0]).
-start_fail() ->
+start_a() ->
f2:start(fun() ->
- io:format("this does not work\n",[])
+ ok
end).
-start_ok() ->
+start_b() ->
f2:start(fun fun1/0).
+
fun1() ->
- io:format("this works\n",[]).
+ ok.
--
cgit v1.2.3
From a772f06a2de3b83e8b21072eeae837e05f7b46e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 29 Sep 2011 15:05:20 +0200
Subject: beam_asm: Fix broken NewIndex in fun entries
The calculation of the NewIndex field in fun entries is broken: the
sys_pre_expand and v3_kernel modules keep separate index counters
starting at zero; thus there is no guarantee that each fun within a
module will have its own unique NewIndex.
We don't really need the NewIndex any more (see below), but since
we do need the NewUniq field, we should fix NewIndex for cleanliness
sake. The simplest way is to assign NewIndex as late as possible,
namely in beam_asm, and to set it to the same value as Index.
Historical Note: Why NewIndex Was Introduced
There was once an idea that the debugger should be able to interpret
only a single function in a module (for speed). To make sure that
interpreted funs could be called from BEAM code and vice versa,
the fun identification must be visible in the abstract code.
Therefore a NewIndex field was introduced in each fun in the abstract
code.
However, it turned out that interpreting single functions does not
play well with aggressive code optimization. For example, in this
code:
f() ->
X = 1,
fun() -> X+2 end.
the variable X will seem to be free in the fun, but an aggressive
optimizer will replace X with 1 in the fun; thus the fun will no
longer have any free variables. Therefore, the debugger will always
interpret entire modules.
---
lib/compiler/src/beam_asm.erl | 4 ++--
lib/compiler/src/beam_dict.erl | 10 ++++++----
2 files changed, 8 insertions(+), 6 deletions(-)
(limited to 'lib')
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 9b46abe697..4a9c12dfea 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -328,8 +328,8 @@ make_op({test,Cond,Fail,Ops}, Dict) when is_list(Ops) ->
encode_op(Cond, [Fail|Ops], Dict);
make_op({test,Cond,Fail,Live,[Op|Ops],Dst}, Dict) when is_list(Ops) ->
encode_op(Cond, [Fail,Op,Live|Ops++[Dst]], Dict);
-make_op({make_fun2,{f,Lbl},Index,_OldUniq,NumFree}, Dict0) ->
- {Fun,Dict} = beam_dict:lambda(Lbl, Index, NumFree, Dict0),
+make_op({make_fun2,{f,Lbl},_Index,_OldUniq,NumFree}, Dict0) ->
+ {Fun,Dict} = beam_dict:lambda(Lbl, NumFree, Dict0),
make_op({make_fun2,Fun}, Dict);
make_op({kill,Y}, Dict) ->
make_op({init,Y}, Dict);
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index f82a8f2e3e..531968b3c8 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -22,7 +22,7 @@
-export([new/0,opcode/2,highest_opcode/1,
atom/2,local/4,export/4,import/4,
- string/2,lambda/4,literal/2,line/2,fname/2,
+ string/2,lambda/3,literal/2,line/2,fname/2,
atom_table/1,local_table/1,export_table/1,import_table/1,
string_table/1,lambda_table/1,literal_table/1,
line_table/1]).
@@ -134,12 +134,14 @@ string(Str, Dict) when is_list(Str) ->
end.
%% Returns the index for a fun entry.
-%% lambda(Lbl, Index, NumFree, Dict) -> {Index,Dict'}
--spec lambda(label(), non_neg_integer(), non_neg_integer(), bdict()) ->
+%% lambda(Lbl, NumFree, Dict) -> {Index,Dict'}
+-spec lambda(label(), non_neg_integer(), bdict()) ->
{non_neg_integer(), bdict()}.
-lambda(Lbl, Index, NumFree, #asm{lambdas=Lambdas0}=Dict) ->
+lambda(Lbl, NumFree, #asm{lambdas=Lambdas0}=Dict) ->
OldIndex = length(Lambdas0),
+ %% Set Index the same as OldIndex.
+ Index = OldIndex,
%% Initialize OldUniq to 0. It will be set to an unique value
%% based on the MD5 checksum of the BEAM code for the module.
OldUniq = 0,
--
cgit v1.2.3
From 7f434109e62fb7b36a350240729aeed2477fd594 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Sat, 8 Oct 2011 10:06:13 +0200
Subject: compiler: Eliminate use of deprecated erlang:hash/2
Now that beam_asm computes the Index and Uniq values for funs, there
is no need to compute those values in the sys_pre_expand and v3_kernel
modules, thus eliminating the calls to the deprecated erlang:hash/2
function.
It would be tempting to stop generating the name for the fun in
sys_pre_expand so that we did not have to add the Info field to a tuple.
But:
* The debugger depends on the name being there. (Simple solution:
Let the debugger generate the name itself.)
* When a fun has been inlined into another function, the fun name in
'id' annotation will be used to notice the inlining and change the
final clause of the top-level case from generating a 'function_clause'
exception to a case_clause exception. (Possible workaround: Have the
inliner set an inlined attribute on functions that have been inlined, or
have the inliner rewrite 'function_clause' exceptions itself.)
---
lib/compiler/src/sys_pre_expand.erl | 23 +++++++++--------------
lib/compiler/src/v3_kernel.erl | 22 ++++++++++------------
2 files changed, 19 insertions(+), 26 deletions(-)
(limited to 'lib')
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 0fa1fea09f..4fabcd947b 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -31,8 +31,6 @@
-import(ordsets, [from_list/1,add_element/2,union/2]).
-import(lists, [member/2,foldl/3,foldr/3]).
--compile({nowarn_deprecated_function, {erlang,hash,2}}).
-
-include("../include/erl_bits.hrl").
-record(expand, {module=[], %Module name
@@ -49,7 +47,6 @@
func=[], %Current function
arity=[], %Arity for current function
fcount=0, %Local fun count
- fun_index=0, %Global index for funs
bitdefault,
bittypes
}).
@@ -542,28 +539,26 @@ lc_tq(_Line, [], St0) ->
%% "Implicit" funs {'fun', Line, {function, F, A}} are not changed.
fun_tq(Lf, {function,F,A}=Function, St0) ->
- {As,St1} = new_vars(A, Lf, St0),
- Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}],
case erl_internal:bif(F, A) of
true ->
+ {As,St1} = new_vars(A, Lf, St0),
+ Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}],
fun_tq(Lf, {clauses,Cs}, St1);
false ->
- Index = St0#expand.fun_index,
- Uniq = erlang:hash(Cs, (1 bsl 27)-1),
- {Fname,St2} = new_fun_name(St1),
- {{'fun',Lf,Function,{Index,Uniq,Fname}},
- St2#expand{fun_index=Index+1}}
+ {Fname,St1} = new_fun_name(St0),
+ Index = Uniq = 0,
+ {{'fun',Lf,Function,{Index,Uniq,Fname}},St1}
end;
fun_tq(L, {function,M,F,A}, St) ->
{{call,L,{remote,L,{atom,L,erlang},{atom,L,make_fun}},
[{atom,L,M},{atom,L,F},{integer,L,A}]},St};
fun_tq(Lf, {clauses,Cs0}, St0) ->
- Uniq = erlang:hash(Cs0, (1 bsl 27)-1),
{Cs1,St1} = fun_clauses(Cs0, St0),
- Index = St1#expand.fun_index,
{Fname,St2} = new_fun_name(St1),
- {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},
- St2#expand{fun_index=Index+1}}.
+ %% Set dummy values for Index and Uniq -- the real values will
+ %% be assigned by beam_asm.
+ Index = Uniq = 0,
+ {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},St2}.
fun_clauses([{clause,L,H0,G0,B0}|Cs0], St0) ->
{H,St1} = head(H0, St0),
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 4e06b464a4..47e5e49a76 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -84,8 +84,6 @@
keymember/3,keyfind/3]).
-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
--compile({nowarn_deprecated_function, {erlang,hash,2}}).
-
-include("core_parse.hrl").
-include("v3_kernel.hrl").
@@ -1658,31 +1656,31 @@ uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
{Ns,St3} = new_vars(1 - length(Rs0), St2),
Rs1 = Rs0 ++ Ns,
{#k_catch{anno=#k{us=Bu,ns=lit_list_vars(Rs1),a=A},body=B1,ret=Rs1},Bu,St3};
-uexpr(#ifun{anno=A,vars=Vs,body=B0}=IFun, {break,Rs}, St0) ->
+uexpr(#ifun{anno=A,vars=Vs,body=B0}, {break,Rs}, St0) ->
{B1,Bu,St1} = ubody(B0, return, St0), %Return out of new function
Ns = lit_list_vars(Vs),
Free = subtract(Bu, Ns), %Free variables in fun
Fvs = make_vars(Free),
Arity = length(Vs) + length(Free),
- {{Index,Uniq,Fname}, St3} =
+ {Fname,St} =
case lists:keyfind(id, 1, A) of
- {id,Id} ->
- {Id, St1};
+ {id,{_,_,Fname0}} ->
+ {Fname0,St1};
false ->
- %% No id annotation. Must invent one.
- I = St1#kern.fcount,
- U = erlang:hash(IFun, (1 bsl 27)-1),
- {N, St2} = new_fun_name(St1),
- {{I,U,N}, St2}
+ %% No id annotation. Must invent a fun name.
+ new_fun_name(St1)
end,
Fun = #k_fdef{anno=#k{us=[],ns=[],a=A},func=Fname,arity=Arity,
vars=Vs ++ Fvs,body=B1},
+ %% Set dummy values for Index and Uniq -- the real values will
+ %% be assigned by beam_asm.
+ Index = Uniq = 0,
{#k_bif{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
op=#k_internal{name=make_fun,arity=length(Free)+3},
args=[#k_atom{val=Fname},#k_int{val=Arity},
#k_int{val=Index},#k_int{val=Uniq}|Fvs],
ret=Rs},
- Free,add_local_function(Fun, St3)};
+ Free,add_local_function(Fun, St)};
uexpr(Lit, {break,Rs}, St) ->
%% Transform literals to puts here.
%%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,Lit]),
--
cgit v1.2.3
From b2cd72d36c5dbd559659213ca6d0d1fab3df8aa1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Sun, 9 Oct 2011 10:13:59 +0200
Subject: sys_pre_expand: Remove incorrect comment
sys_pre_expand does not keep track of used or new variables (and have
not done since about the time Core Erlang was introduced).
---
lib/compiler/src/sys_pre_expand.erl | 1 -
1 file changed, 1 deletion(-)
(limited to 'lib')
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 4fabcd947b..63dea024b4 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -535,7 +535,6 @@ lc_tq(_Line, [], St0) ->
%% Transform an "explicit" fun {'fun', Line, {clauses, Cs}} into an
%% extended form {'fun', Line, {clauses, Cs}, Info}, unless it is the
%% name of a BIF (erl_lint has checked that it is not an import).
-%% Process the body sequence directly to get the new and used variables.
%% "Implicit" funs {'fun', Line, {function, F, A}} are not changed.
fun_tq(Lf, {function,F,A}=Function, St0) ->
--
cgit v1.2.3
From 3d5cdb216fa5652bd8552ef4fa4eee310e543b42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Sun, 4 Sep 2011 09:27:50 +0200
Subject: Optimize filename:basename/1 to produce less garbage
In most cases, we can simply return a tail of the flattened
filename.
---
lib/stdlib/src/filename.erl | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
(limited to 'lib')
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index b9665d7dec..2fc9128e4e 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -147,9 +147,10 @@ basename(Name) when is_binary(Name) ->
end;
basename(Name0) ->
- Name = flatten(Name0),
+ Name1 = flatten(Name0),
{DirSep2, DrvSep} = separators(),
- basename1(skip_prefix(Name, DrvSep), [], DirSep2).
+ Name = skip_prefix(Name1, DrvSep),
+ basename1(Name, Name, DirSep2).
win_basenameb(<>) when ?IS_DRIVELETTER(Letter) ->
basenameb(Rest,[<<"/">>,<<"\\">>]);
@@ -167,16 +168,18 @@ basenameb(Bin,Sep) ->
-basename1([$/|[]], Tail, DirSep2) ->
- basename1([], Tail, DirSep2);
+basename1([$/], Tail0, _DirSep2) ->
+ %% End of filename -- must get rid of trailing directory separator.
+ [_|Tail] = lists:reverse(Tail0),
+ lists:reverse(Tail);
basename1([$/|Rest], _Tail, DirSep2) ->
- basename1(Rest, [], DirSep2);
+ basename1(Rest, Rest, DirSep2);
basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) ->
basename1([$/|Rest], Tail, DirSep2);
basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) ->
- basename1(Rest, [Char|Tail], DirSep2);
+ basename1(Rest, Tail, DirSep2);
basename1([], Tail, _DirSep2) ->
- lists:reverse(Tail).
+ Tail.
skip_prefix(Name, false) ->
Name;
--
cgit v1.2.3