aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--erts/configure.in5
-rw-r--r--erts/doc/src/erlc.xml2
-rw-r--r--erts/doc/src/erlsrv.xml13
-rw-r--r--erts/emulator/beam/erl_async.c7
-rw-r--r--erts/emulator/beam/erl_time_sup.c12
-rw-r--r--erts/emulator/drivers/common/efile_drv.c2
-rw-r--r--erts/emulator/sys/win32/sys_interrupt.c4
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c3
-rw-r--r--erts/etc/win32/port_entry.c2
-rw-r--r--erts/etc/win32/start_erl.c3
-rw-r--r--erts/preloaded/src/prim_file.erl2
-rw-r--r--lib/asn1/src/asn1rt_ber_bin_v2.erl4
-rw-r--r--lib/asn1/src/asn1rt_check.erl3
-rw-r--r--lib/common_test/doc/src/config_file_chapter.xml2
-rw-r--r--lib/common_test/doc/src/ct_master_chapter.xml49
-rw-r--r--lib/common_test/doc/src/ct_run.xml8
-rw-r--r--lib/common_test/doc/src/example_chapter.xml3
-rw-r--r--lib/common_test/doc/src/getting_started_chapter.xml55
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml530
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml111
-rw-r--r--lib/common_test/src/ct.erl317
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl2
-rw-r--r--lib/common_test/src/ct_framework.erl23
-rw-r--r--lib/common_test/src/ct_run.erl55
-rw-r--r--lib/common_test/src/ct_testspec.erl20
-rw-r--r--lib/common_test/src/ct_util.erl20
-rw-r--r--lib/common_test/src/ct_util.hrl3
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg2
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl10
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl91
-rw-r--r--lib/common_test/test/ct_testspec_2_SUITE.erl18
-rw-r--r--lib/compiler/doc/src/compile.xml2
-rw-r--r--lib/crypto/c_src/crypto.c2
-rw-r--r--lib/crypto/test/crypto_SUITE.erl32
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl8
-rw-r--r--lib/dialyzer/src/dialyzer_coordinator.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl324
-rw-r--r--lib/diameter/doc/src/diameter.xml922
-rw-r--r--lib/diameter/doc/src/diameter_app.xml148
-rw-r--r--lib/diameter/doc/src/diameter_compile.xml45
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml8
-rw-r--r--lib/diameter/doc/src/diameter_examples.xml3
-rw-r--r--lib/diameter/doc/src/diameter_intro.xml27
-rw-r--r--lib/diameter/doc/src/diameter_sctp.xml52
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml53
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml66
-rw-r--r--lib/diameter/doc/src/diameter_using.xml4
-rw-r--r--lib/diameter/examples/code/peer.erl5
-rw-r--r--lib/diameter/include/diameter.hrl3
-rw-r--r--lib/diameter/src/.gitignore1
-rw-r--r--lib/diameter/src/Makefile30
-rw-r--r--lib/diameter/src/base/diameter.appup.src25
-rw-r--r--lib/diameter/src/base/diameter.erl10
-rw-r--r--lib/diameter/src/base/diameter_codec.erl11
-rw-r--r--lib/diameter/src/base/diameter_config.erl5
-rw-r--r--lib/diameter/src/base/diameter_peer.erl141
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl176
-rw-r--r--lib/diameter/src/base/diameter_service.erl530
-rw-r--r--lib/diameter/src/base/diameter_stats.erl265
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl47
-rw-r--r--lib/diameter/src/modules.mk1
-rw-r--r--lib/diameter/src/transport/diameter_etcp.erl22
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl129
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl79
-rw-r--r--lib/diameter/src/transport/diameter_transport.erl55
-rw-r--r--lib/diameter/test/Makefile7
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl6
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_ct.erl13
-rw-r--r--lib/diameter/test/diameter_dict_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_failover_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl19
-rw-r--r--lib/diameter/test/diameter_reg_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl11
-rw-r--r--lib/diameter/test/diameter_stats_SUITE.erl94
-rw-r--r--lib/diameter/test/diameter_sync_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_util.erl37
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl11
-rw-r--r--lib/hipe/cerl/erl_types.erl4
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl37
-rw-r--r--lib/hipe/icode/hipe_icode.hrl6
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl6
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl36
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl2
-rw-r--r--lib/inets/src/http_server/httpd_log.erl12
-rw-r--r--lib/kernel/doc/src/code.xml2
-rw-r--r--lib/megaco/doc/src/notes.xml72
-rw-r--r--lib/megaco/src/app/megaco.appup.src11
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/observer/src/observer_lib.erl54
-rw-r--r--lib/observer/src/observer_tv_table.erl30
-rw-r--r--lib/reltool/doc/src/reltool.xml50
-rw-r--r--lib/reltool/src/reltool.hrl3
-rw-r--r--lib/reltool/src/reltool_server.erl2
-rw-r--r--lib/reltool/src/reltool_target.erl72
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl67
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/Makefile.src11
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl49
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl37
-rw-r--r--lib/ssh/doc/src/ssh.xml14
-rw-r--r--lib/ssh/src/ssh.appup.src24
-rw-r--r--lib/ssh/src/ssh.erl35
-rw-r--r--lib/ssh/src/ssh_connection.erl16
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl10
-rw-r--r--lib/ssh/src/ssh_file.erl11
-rw-r--r--lib/ssh/src/ssh_sftpd_file_api.erl62
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/examples/src/client_server.erl24
-rw-r--r--lib/ssl/src/ssl_tls1.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl2
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl10
-rw-r--r--lib/test_server/doc/src/test_server_ctrl.xml6
-rw-r--r--lib/tools/doc/src/xref_chapter.xml2
-rw-r--r--lib/tools/emacs/Makefile16
119 files changed, 3811 insertions, 1844 deletions
diff --git a/README.md b/README.md
index c38f097b0f..537d3ec774 100644
--- a/README.md
+++ b/README.md
@@ -73,4 +73,4 @@ Copyright and License
[1]: http://www.erlang.org
[2]: http://wiki.github.com/erlang/otp/submitting-patches
- [3]: http://www.erlang.org/faq.html
+ [3]: http://www.erlang.org/static/doc/mailinglist.html
diff --git a/erts/configure.in b/erts/configure.in
index 281f61f86e..8d70a1b74a 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1882,9 +1882,10 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop
pread pwrite writev memmove strerror strerror_r strncasecmp \
gethrtime localtime_r gmtime_r inet_pton mmap mremap memcpy mallopt \
sbrk _sbrk __sbrk brk _brk __brk \
- flockfile fstat strlcpy strlcat setsid posix2time setlocale nl_langinfo poll])
+ flockfile fstat strlcpy strlcat setsid posix2time time2posix \
+ setlocale nl_langinfo poll])
-AC_CHECK_DECLS([posix2time],,,[#include <time.h>])
+AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>])
disable_vfork=false
if test "x$EMU_THR_LIB_NAME" != "x"; then
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index 3358b8f115..09ed4bd372 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -67,7 +67,7 @@
<item>
<p>Instructs the compiler to search for include files in
the specified directory. When encountering an
- <c><![CDATA[-include]]></c> or <c><![CDATA[-include_dir]]></c> directive, the
+ <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> directive, the
compiler searches for header files in the following
directories:</p>
<list type="ordered">
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml
index c1ecbc7b77..b2f34ed247 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv.xml
@@ -357,11 +357,12 @@ The environment of an Erlang machine started
the console subsystem and programs running as window
applications. An application which runs in the console subsystem
(normal for port programs) uses the win32 function
- <c><![CDATA[SetConsoleCtrlHandler]]></c> to a control handler that returns
- TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>. Other
- applications just forward <c><![CDATA[WM_ENDSESSION]]></c> and
- <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure. Here
- is a brief example in C of how to set the console control
+ <c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler
+ that returns TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>
+ and <c><![CDATA[CTRL_SHUTDOWN_EVENT]]></c> events. Other applications
+ just forward <c><![CDATA[WM_ENDSESSION]]></c> and
+ <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.
+ Here is a brief example in C of how to set the console control
handler:</p>
<code type="none"><![CDATA[
#include <windows.h>
@@ -372,6 +373,8 @@ The environment of an Erlang machine started
BOOL WINAPI service_aware_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
+ if(ctrl == CTRL_SHUTDOWN_EVENT)
+ return TRUE;
return FALSE;
}
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index cb975d64b0..f308039baf 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -394,6 +394,8 @@ static ERTS_INLINE void call_async_ready(ErtsAsync *a)
}
erts_port_release(p);
}
+ if (a->pdl)
+ driver_pdl_dec_refc(a->pdl);
if (a->hndl)
erts_ddll_dereference_driver(a->hndl);
}
@@ -403,9 +405,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq)
#if ERTS_USE_ASYNC_READY_Q
ErtsAsyncReadyQ *arq;
- if (a->pdl)
- driver_pdl_dec_refc(a->pdl);
-
#if ERTS_ASYNC_PRINT_JOB
erts_fprintf(stderr, "=>> %ld\n", a->async_id);
#endif
@@ -425,8 +424,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq)
#else /* ERTS_USE_ASYNC_READY_Q */
call_async_ready(a);
- if (a->pdl)
- driver_pdl_dec_refc(a->pdl);
erts_free(ERTS_ALC_T_ASYNC, (void *) a);
#endif /* ERTS_USE_ASYNC_READY_Q */
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index e6962a700b..a44401d7b1 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -351,7 +351,7 @@ static int clock_resolution;
/*
** The clock resolution should really be the resolution of the
** time function in use, which on most platforms
-** is 1. On VxWorks the resolution shold be
+** is 1. On VxWorks the resolution should be
** the number of ticks per second (or 1, which would work nicely to).
**
** Setting lower resolutions is mostly interesting when timers are used
@@ -717,6 +717,11 @@ int univ_to_seconds(Sint year, Sint month, Sint day, Sint hour, Sint minute, Sin
return 1;
}
+#if defined(HAVE_TIME2POSIX) && defined(HAVE_DECL_TIME2POSIX) && \
+ !HAVE_DECL_TIME2POSIX
+extern time_t time2posix(time_t);
+#endif
+
int
local_to_univ(Sint *year, Sint *month, Sint *day,
Sint *hour, Sint *minute, Sint *second, int isdst)
@@ -766,6 +771,11 @@ local_to_univ(Sint *year, Sint *month, Sint *day,
return 0;
}
}
+
+#ifdef HAVE_TIME2POSIX
+ the_clock = time2posix(the_clock);
+#endif
+
#ifdef HAVE_GMTIME_R
tm = gmtime_r(&the_clock, &tmbuf);
#else
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 347247ee7b..f24098025e 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -2597,7 +2597,7 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
#ifdef USE_VM_PROBES
dt_s1 = d->b;
dt_s2 = d->b + namelen;
- dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+ dt_utag = buf + namelen + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE;
#endif
d->flags = desc->flags;
d->fd = fd;
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c
index 347c31053b..a507a19480 100644
--- a/erts/emulator/sys/win32/sys_interrupt.c
+++ b/erts/emulator/sys/win32/sys_interrupt.c
@@ -75,11 +75,11 @@ BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType)
return TRUE;
break;
case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
if (nohup)
return TRUE;
/* else pour through... */
case CTRL_CLOSE_EVENT:
- case CTRL_SHUTDOWN_EVENT:
erl_exit(0, "");
break;
}
@@ -127,11 +127,11 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
SetEvent(erts_sys_break_event);
break;
case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
if (nohup)
return TRUE;
/* else pour through... */
case CTRL_CLOSE_EVENT:
- case CTRL_SHUTDOWN_EVENT:
erl_exit(0, "");
break;
}
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c
index 242e2905a9..8b734b0c05 100644
--- a/erts/etc/win32/erlsrv/erlsrv_service.c
+++ b/erts/etc/win32/erlsrv/erlsrv_service.c
@@ -104,11 +104,10 @@ static VOID WINAPI handler(DWORD control){
log_debug(buffer);
switch(control){
case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
set_stop_pending(30000,1);
SetEvent(eventStop);
return;
- case SERVICE_CONTROL_SHUTDOWN:
- return;
default:
reset_current();
break;
diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c
index 49b5ad2f34..9c3e750cbc 100644
--- a/erts/etc/win32/port_entry.c
+++ b/erts/etc/win32/port_entry.c
@@ -45,6 +45,8 @@ extern void mainCRTStartup(void);
BOOL WINAPI erl_port_default_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
+ if(ctrl == CTRL_SHUTDOWN_EVENT)
+ return TRUE;
return FALSE;
}
diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c
index 28c8e55bd3..41b221d5bc 100644
--- a/erts/etc/win32/start_erl.c
+++ b/erts/etc/win32/start_erl.c
@@ -585,6 +585,9 @@ BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType )
if(dwCtrlType == CTRL_LOGOFF_EVENT) {
return TRUE;
}
+ if(dwCtrlType == CTRL_SHUTDOWN_EVENT) {
+ return TRUE;
+ }
return FALSE;
}
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 0fc56c8c8e..ec158ba970 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -453,7 +453,7 @@ position(#file_descriptor{module = ?MODULE, data = {Port, _}}, At) ->
{error, Reason}
end.
-%% Returns {error, Reaseon} | ok.
+%% Returns {error, Reason} | ok.
truncate(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
drv_command(Port, <<?FILE_TRUNCATE>>).
diff --git a/lib/asn1/src/asn1rt_ber_bin_v2.erl b/lib/asn1/src/asn1rt_ber_bin_v2.erl
index 17e66f77c9..420e3e1d91 100644
--- a/lib/asn1/src/asn1rt_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1rt_ber_bin_v2.erl
@@ -610,8 +610,8 @@ match_tags(Vlist = [{T,_V}|_], [T]) ->
Vlist;
match_tags(Tlv, []) ->
Tlv;
-match_tags({Tag,_V},[T|_Tt]) ->
- {error,{asn1,{wrong_tag,{Tag,T}}}}.
+match_tags(Tlv = {Tag,_V},[T|_Tt]) ->
+ exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}).
cindex(Ix,Val,Cname) ->
diff --git a/lib/asn1/src/asn1rt_check.erl b/lib/asn1/src/asn1rt_check.erl
index d9856901b8..35b993fc71 100644
--- a/lib/asn1/src/asn1rt_check.erl
+++ b/lib/asn1/src/asn1rt_check.erl
@@ -311,7 +311,8 @@ transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc)
when is_binary(Data_value)->
list_to_tuple(lists:reverse([{'single-ASN1-type',Data_value},
Data_val_desc|Acc]));
-transform_to_EXTERNAL1990([Data_value],Acc) when is_list(Data_value)->
+transform_to_EXTERNAL1990([Data_value],Acc)
+ when is_list(Data_value); is_binary(Data_value) ->
list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml
index e843ed3ba4..3e6fb21659 100644
--- a/lib/common_test/doc/src/config_file_chapter.xml
+++ b/lib/common_test/doc/src/config_file_chapter.xml
@@ -29,6 +29,8 @@
<file>config_file_chapter.xml</file>
</header>
+ <marker id="top"></marker>
+
<section>
<title>General</title>
diff --git a/lib/common_test/doc/src/ct_master_chapter.xml b/lib/common_test/doc/src/ct_master_chapter.xml
index f4f0ecad62..21deed099d 100644
--- a/lib/common_test/doc/src/ct_master_chapter.xml
+++ b/lib/common_test/doc/src/ct_master_chapter.xml
@@ -124,7 +124,8 @@
<p><c>NodeRef = NodeAlias | node() | master</c></p>
<p>A <c>NodeAlias</c> (<c>atom()</c>) is used in a test specification as a
- reference to a node name (so the actual node name only needs to be declared once).
+ reference to a node name (so the actual node name only needs to be declared once,
+ which can of course also be achieved using constants).
The alias is declared with a <c>node</c> term:</p>
<p><c>{node, NodeAlias, NodeName}</c></p>
@@ -141,30 +142,32 @@
CT Master:</p>
<pre>
- {node, node1, ct_node@host_x}.
- {node, node2, ct_node@host_y}.
-
- {logdir, master, "/home/test/master_logs"}.
- {logdir, "/home/test/logs"}.
+ {define, 'Top', "/home/test"}.
+ {define, 'T1', "'Top'/t1"}.
+ {define, 'T2', "'Top'/t2"}.
+ {define, 'T3', "'Top'/t3"}.
+ {define, 'CfgFile', "config.cfg"}.
+ {define, 'Node', ct_node}.
+
+ {node, node1, 'Node@host_x'}.
+ {node, node2, 'Node@host_y'}.
+
+ {logdir, master, "'Top'/master_logs"}.
+ {logdir, "'Top'/logs"}.
- {config, node1, "/home/test/t1/cfg/config.cfg"}.
- {config, node2, "/home/test/t2/cfg/config.cfg"}.
- {config, "/home/test/t3/cfg/config.cfg"}.
+ {config, node1, "'T1'/'CfgFile'"}.
+ {config, node2, "'T2'/'CfgFile'"}.
+ {config, "'T3'/'CfgFile'"}.
- {alias, t1, "/home/test/t1"}.
- {alias, t2, "/home/test/t2"}.
- {alias, t3, "/home/test/t3"}.
+ {suites, node1, 'T1', all}.
+ {skip_suites, node1, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
+ {skip_cases, node1, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
+ {skip_cases, node1, 'T1', t1C_SUITE, [test1], "Ignore"}.
- {suites, node1, t1, all}.
- {skip_suites, node1, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}.
- {skip_cases, node1, t1, t1A_SUITE, [test3,test4], "Irrelevant"}.
- {skip_cases, node1, t1, t1C_SUITE, [test1], "Ignore"}.
+ {suites, node2, 'T2', [t2B_SUITE,t2C_SUITE]}.
+ {cases, node2, 'T2', t2A_SUITE, [test4,test1,test7]}.
- {suites, node2, t2, [t2B_SUITE,t2C_SUITE]}.
- {cases, node2, t2, t2A_SUITE, [test4,test1,test7]}.
-
- {skip_suites, t3, all, "Not implemented"}.
- </pre>
+ {skip_suites, 'T3', all, "Not implemented"}.</pre>
<p>This example specifies the same tests as the original example. But
now if started with a call to <c>ct_master:run(TestSpecName)</c>, the
@@ -190,10 +193,6 @@
name as the Common Test node in question (typically <c>ct@somehost</c> if started
with the <c>ct_run</c> program), will be performed. Tests without explicit
node association will always be performed too of course!</p>
-
- <note><p>It is recommended that absolute paths are used for log directories,
- config files and test directory aliases in the test specifications so that
- current working directory settings are not important.</p></note>
</section>
<section>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 8061c840b0..9cc5495af7 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -36,6 +36,8 @@
OS command line.
</comsummary>
+ <marker id="top"></marker>
+
<description>
<p>The <c>ct_run</c> program is automatically installed with Erlang/OTP
and Common Test (please see the Installation chapter in the Common
@@ -72,6 +74,10 @@
following <c>-erl_args</c> on the command line. These directories are added
to the code path normally (i.e. on specified form)</p>
+ <p>Exit status is set before the program ends. Value <c>0</c> indicates a successful
+ test result, <c>1</c> indicates one or more failed or auto-skipped test cases, and
+ <c>2</c> indicates test execution failure.</p>
+
<p>If <c>ct_run</c> is called with option:</p>
<pre>-help</pre>
<p>it prints all valid start flags to stdout.</p>
@@ -112,6 +118,7 @@
[-basic_html]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
</pre>
</section>
<section>
@@ -145,6 +152,7 @@
[-basic_html]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
</pre>
</section>
<section>
diff --git a/lib/common_test/doc/src/example_chapter.xml b/lib/common_test/doc/src/example_chapter.xml
index f269dba2cd..3bba7bd783 100644
--- a/lib/common_test/doc/src/example_chapter.xml
+++ b/lib/common_test/doc/src/example_chapter.xml
@@ -28,7 +28,8 @@
<rev></rev>
<file>example_chapter.xml</file>
</header>
-
+
+ <marker id="top"></marker>
<section>
<title>Test suite example</title>
diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml
index 891cbc49f3..3cf04bb1a2 100644
--- a/lib/common_test/doc/src/getting_started_chapter.xml
+++ b/lib/common_test/doc/src/getting_started_chapter.xml
@@ -214,11 +214,56 @@
<section>
<title>What happens next?</title>
- <p>
- You will find detailed information about the basics introduced here in this
- chapter in the following chapters in the User's Guide, as well as
- presentations of many more useful features. Have fun!
- </p>
+
+ <p>Well, you might already be asking yourself questions such as:</p>
+
+ <list>
+ <item>"How and where can I specify variable data for my tests that mustn't
+ be hard-coded in the test suites (such as host names, addresses,
+ user login data, etc)?" The
+ <seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ chapter will give you that information.
+ </item>
+ <item>"Is there a way to declare a number of different tests and run them
+ in one session without having to write my own scripts? And can such
+ declarations be used for regression testing?" The
+ <seealso marker="run_test_chapter#test_specifications">Test Specifications</seealso>
+ chapter answers these questions.
+ </item>
+ <item>"Can test cases and/or test runs be automatically repeated?" Learn more about
+ <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso>
+ and also read about start flags/options in the
+ <seealso marker="run_test_chapter#ct_run">Running Tests</seealso> chapter and
+ the Reference Manual.
+ </item>
+ <item>"Will Common Test execute my test cases in sequence or in parallel?" The
+ <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso>
+ section in the Running Tests chapter will give you the answer.
+ </item>
+ <item>"What's the syntax for timetraps (mentioned above), and how do I set them?"
+ This is explained in the
+ <seealso marker="write_test_chapter#timetraps">Timetrap Timeouts</seealso>
+ part of the Writing Test Suites chapter.
+ </item>
+ <item>"What functions are available for logging and printing?" Check the
+ <seealso marker="write_test_chapter#logging">Logging</seealso>
+ section in the Writing Test Suites chapter.
+ </item>
+ <item>"I need data files for my tests. Where do I store them preferrably?"
+ You should read about
+ <seealso marker="write_test_chapter#data_priv_dir">Data and Private
+ Directories</seealso> for information about this.
+ </item>
+ <item>"May I start with a test suite example, please?"
+ <seealso marker="example_chapter#top">Sure!</seealso>
+ </item>
+ </list>
+ <p>You will probably want to get started on your own first test suites now, while
+ at the same time digging deeper into the Common Test User's Guide and Reference Manual.
+ You will find that there's lots more to learn about the things that have been introduced
+ in this chapter. You will of course also be presented many more useful features, such as the
+ ones listed above. Have fun!
+ </p>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 058b27d622..ea62df27cc 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -178,6 +178,8 @@
<item><c><![CDATA[-basic_html]]></c>, switches off html enhancements that might not be compatible with older browsers.</item>
<item><c><![CDATA[-logopts <opts>]]></c>, makes it possible to modify aspects of the logging behaviour, see
<seealso marker="run_test_chapter#logopts">Log options</seealso> below.</item>
+ <item><c><![CDATA[-verbosity <levels>]]></c>, sets <seealso marker="write_test_chapter#logging">verbosity levels
+ for printouts</seealso>.</item>
</list>
<note><p>Directories passed to Common Test may have either relative or absolute paths.</p></note>
@@ -196,60 +198,73 @@
the current working directory of the Erlang Runtime System during the test run!</p>
</note>
- <p>For more information about the <c>ct_run</c> program, see the
- <seealso marker="install_chapter#general">Installation</seealso> chapter.
- </p>
- </section>
-
- <section>
- <title>Running tests from the Web based GUI</title>
-
- <p>The web based GUI, VTS, is started with the <c>ct_run</c>
- program. From the GUI you can load config files, and select
- directories, suites and cases to run. You can also state the
- config files, directories, suites and cases on the command line
- when starting the web based GUI.
- </p>
-
+ <p>The <c>ct_run</c> program sets the exit status before shutting down. The following values
+ are defined:</p>
<list>
- <item><c>ct_run -vts</c></item>
- <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item>
- <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath>
- -case <casename>]]></c></item>
+ <item><c>0</c> indicates a successful testrun, i.e. one without failed or auto-skipped test cases.</item>
+ <item><c>1</c> indicates that one or more test cases have failed, or have been auto-skipped.</item>
+ <item><c>2</c> indicates that the test execution has failed because of e.g. compilation errors, an
+ illegal return value from an info function, etc.</item>
</list>
+ <p>If auto-skipped test cases should not affect the exit status, you may change the default
+ behaviour using start flag:</p>
+ <pre>-exit_status ignore_config</pre>
- <p>From the GUI you can run tests and view the result and the logs.
+ <p>For more information about the <c>ct_run</c> program, see the
+ <seealso marker="ct_run#top">Reference Manual</seealso> and the
+ <seealso marker="install_chapter#general">Installation</seealso> chapter.
</p>
-
- <p>Note that <c>ct_run -vts</c> will try to open the Common Test start
- page in an existing web browser window or start the browser if it is
- not running. Which browser should be started may be specified with
- the browser start command option:</p>
- <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p>
- <p>Example:</p>
- <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p>
- <p>Note that the browser must run as a separate OS process or VTS will hang!</p>
- <p>If no specific browser start command is specified, Firefox will
- be the default browser on Unix platforms and Internet Explorer on Windows.
- If Common Test fails to start a browser automatically, or <c>'none'</c> is
- specified as the value for -browser (i.e. <c>-browser none</c>), start your
- favourite browser manually and type in the URL that Common Test
- displays in the shell.</p>
</section>
-
+
<section>
<title>Running tests from the Erlang shell or from an Erlang program</title>
<p>Common Test provides an Erlang API for running tests. The main (and most
flexible) function for specifying and executing tests is called
- <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>. This function takes the same start parameters as
- the <c>ct_run</c> program described above, only the flags are instead
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>.
+ This function takes the same start parameters as
+ the <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></c>
+ program described above, only the flags are instead
given as options in a list of key-value tuples. E.g. a test specified
with <c>ct_run</c> like:</p>
+
<p><c>$ ct_run -suite ./my_SUITE -logdir ./results</c></p>
<p>is with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> specified as:</p>
<p><c>1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).</c></p>
- <p>For detailed documentation, please see the <c>ct</c> manual page.</p>
+
+ <p>The function returns the test result, represented by the tuple:
+ <c>{Ok,Failed,{UserSkipped,AutoSkipped}}</c>, where each element is an
+ integer. If test execution fails, the function returns the tuple:
+ <c>{error,Reason}</c>, where the term <c>Reason</c> explains the
+ failure.</p>
+
+ <section>
+ <title>Releasing the Erlang shell</title>
+ <p>During execution of tests, started with
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>,
+ the Erlang shell process, controlling stdin, will remain the top
+ level process of the Common Test system of processes. The result
+ is that the Erlang shell is not available for interaction during
+ the test run. If this is not desirable, maybe because the shell is needed
+ for debugging purposes or for interaction with the SUT during test
+ execution, you may set the <c>release_shell</c> start option to
+ <c>true</c> (in the call to <c>ct:run_test/1</c> or by
+ using the corresponding test specification term, see below). This will
+ make Common Test release the shell immediately after the test suite
+ compilation stage. To accomplish this, a test runner process
+ is spawned to take control of the test execution, and the effect is that
+ <c>ct:run_test/1</c> returns the pid of this process rather than the
+ test result - which instead is printed to tty at the end of the test run.</p>
+ <note><p>Note that in order to use the
+ <c><seealso marker="ct#break-1">ct:break/1/2</seealso></c> and
+ <c><seealso marker="ct#continue-0">ct:continue/0/1</seealso></c> functions,
+ <c>release_shell</c> <em>must</em> be set to <c>true</c>.</p></note>
+ </section>
+
+ <p>For detailed documentation about
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>,
+ please see the
+ <c><seealso marker="ct#run_test-1">ct</seealso></c> manual page.</p>
</section>
<section>
@@ -353,31 +368,34 @@
<marker id="test_specifications"></marker>
<section>
- <title>Using test specifications</title>
+ <title>Test Specifications</title>
<p>The most flexible way to specify what to test, is to use a so
called test specification. A test specification is a sequence of
- Erlang terms. The terms may be declared in a text file or passed
- to the test server at runtime as a list
- (see <c>run_testspec/1</c> in the manual page
- for <c>ct</c>). There are two general types of terms:
- configuration terms and test specification terms.</p>
+ Erlang terms. The terms are normally declared in a text file (see
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), but
+ may also be passed to Common Test on the form of a list (see
+ <c><seealso marker="ct#run_testspec-1">ct:run_testspec/1</seealso></c>).
+ There are two general types of terms: configuration terms and test
+ specification terms.</p>
<p>With configuration terms it is possible to e.g. label the test
run (similar to <c>ct_run -label</c>), evaluate arbitrary expressions
- before starting a test, import configuration
- data (similar to
- <c>ct_run -config/-userconfig</c>), specify HTML log directories (similar
- to
- <c>ct_run -logdir</c>), give aliases to test nodes and test
- directories (to make a specification easier to read and
- maintain), enable code coverage analysis (see
- the <seealso marker="cover_chapter#cover">Code Coverage
- Analysis</seealso> chapter) and specify event_handler plugins
- (see the <seealso marker="event_handler_chapter#event_handling">
- Event Handling</seealso> chapter). There is also a term for
- specifying include directories that should be passed on to the
- compiler when automatic compilation is performed (similar
- to <c>ct_run -include</c>, see above).</p>
+ before starting the test, import configuration data (similar to
+ <c>ct_run -config/-userconfig</c>), specify the top level HTML log
+ directory (similar to <c>ct_run -logdir</c>), enable code coverage
+ analysis (similar to <c>ct_run -cover</c>), install Common Test Hooks
+ (similar to <c>ct_run -ch_hooks</c>), install event_handler plugins
+ (similar to <c>ct_run -event_handler</c>), specify include directories
+ that should be passed to the compiler for automatic compilation
+ (similar to <c>ct_run -include</c>), disable the auto compilation
+ feature (similar to <c>ct_run -no_auto_compile</c>), set verbosity
+ levels (similar to <c>ct_run -verbosity</c>), and more.</p>
+ <p>Configuration terms can be combined with <c>ct_run</c> start flags,
+ or <c>ct:run_test/1</c> options. The result will for some flags/options
+ and terms be that the values are merged (e.g. configuration files,
+ include directories, verbosity levels, silent connections), and for
+ others that the start flags/options override the test specification
+ terms (e.g. log directory, label, style sheet, auto compilation).</p>
<p>With test specification terms it is possible to state exactly
which tests should run and in which order. A test term specifies
either one or more suites, one or more test case groups, or one
@@ -392,11 +410,12 @@
S, is a test of all cases in S. However, if a term specifying
test case X and Y in S is merged with a term specifying case Z
in S, the result is a test of X, Y and Z in S. To disable this
- behaviour, it is possible in test specification to set the
- <c>merge_tests</c> term to <c>false</c>.</p>
+ behaviour, i.e. to instead perform each test sequentially in a "script-like"
+ manner, the term <c>merge_tests</c> can be set to <c>false</c> in
+ the test specification.</p>
<p>A test term can also specify one or more test suites, groups,
or test cases to be skipped. Skipped suites, groups and cases
- are not executed and show up in the HTML test log files as
+ are not executed and show up in the HTML log files as
SKIPPED.</p>
<p>When a test case group is specified, the resulting test
executes the
@@ -429,12 +448,27 @@
Testing). The node parameters in the <c>init</c> term are only
relevant in the latter (see the
<seealso marker="ct_master_chapter#test_specifications">Large
- Scale Testing</seealso> chapter for information). For details on
- the event_handler term, see the
+ Scale Testing</seealso> chapter for information). For more information
+ about the various terms, please see the corresponding sections in the
+ User's Guide, such as e.g. the
+ <seealso marker="run_test_chapter#ct_run"><c>ct_run</c>
+ program</seealso> for an overview of available start flags
+ (since most flags have a corresponding configuration term), and
+ more detailed explanation of e.g.
+ <seealso marker="write_test_chapter#logging">Logging</seealso>
+ (for the <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c> terms),
+ <seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ (for the <c>config</c> and <c>userconfig</c> terms),
<seealso marker="event_handler_chapter#event_handling">Event
- Handling</seealso> chapter.</p>
+ Handling</seealso> (for the <c>event_handler</c> term),
+ <seealso marker="ct_hooks_chapter#installing">Common Test Hooks</seealso>
+ (for the <c>ct_hooks</c> term), etc.</p>
<p>Config terms:</p>
<pre>
+ {merge_tests, Bool}.
+
+ {define, Constant, Value}.
+
{node, NodeAlias, Node}.
{init, InitOptions}.
@@ -443,6 +477,15 @@
{label, Label}.
{label, NodeRefs, Label}.
+ {verbosity, VerbosityLevels}.
+ {verbosity, NodeRefs, VerbosityLevels}.
+
+ {stylesheet, CSSFile}.
+ {stylesheet, NodeRefs, CSSFile}.
+
+ {silent_connections, ConnTypes}.
+ {silent_connections, NodeRefs, ConnTypes}.
+
{multiply_timetraps, N}.
{multiply_timetraps, NodeRefs, N}.
@@ -455,19 +498,23 @@
{include, IncludeDirs}.
{include, NodeRefs, IncludeDirs}.
+ {auto_compile, Bool},
+ {auto_compile, NodeRefs, Bool},
+
{config, ConfigFiles}.
+ {config, ConfigDir, ConfigBaseNames}.
{config, NodeRefs, ConfigFiles}.
+ {config, NodeRefs, ConfigDir, ConfigBaseNames}.
{userconfig, {CallbackModule, ConfigStrings}}.
{userconfig, NodeRefs, {CallbackModule, ConfigStrings}}.
- {alias, DirAlias, Dir}.
-
- {merge_tests, Bool}.
-
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
+ {logopts, LogOpts}.
+ {logopts, NodeRefs, LogOpts}.
+
{create_priv_dir, PrivDirOption}.
{create_priv_dir, NodeRefs, PrivDirOption}.
@@ -480,83 +527,176 @@
{ct_hooks, NodeRefs, CTHModules}.
{enable_builtin_hooks, Bool}.
- </pre>
+
+ {basic_html, Bool}.
+ {basic_html, NodeRefs, Bool}.
+
+ {release_shell, Bool}.</pre>
+
<p>Test terms:</p>
<pre>
- {suites, DirRef, Suites}.
- {suites, NodeRefs, DirRef, Suites}.
+ {suites, Dir, Suites}.
+ {suites, NodeRefs, Dir, Suites}.
- {groups, DirRef, Suite, Groups}.
- {groups, NodeRefsDirRef, Suite, Groups}.
+ {groups, Dir, Suite, Groups}.
+ {groups, NodeRefs, Dir, Suite, Groups}.
- {groups, DirRef, Suite, GroupSpec, {cases,Cases}}.
- {groups, NodeRefsDirRef, Suite, GroupSpec, {cases,Cases}}.
+ {groups, Dir, Suite, GroupSpec, {cases,Cases}}.
+ {groups, NodeRefs, Dir, Suite, GroupSpec, {cases,Cases}}.
- {cases, DirRef, Suite, Cases}.
- {cases, NodeRefs, DirRef, Suite, Cases}.
+ {cases, Dir, Suite, Cases}.
+ {cases, NodeRefs, Dir, Suite, Cases}.
- {skip_suites, DirRef, Suites, Comment}.
- {skip_suites, NodeRefs, DirRef, Suites, Comment}.
+ {skip_suites, Dir, Suites, Comment}.
+ {skip_suites, NodeRefs, Dir, Suites, Comment}.
- {skip_groups, DirRef, Suite, GroupNames, Comment}.
- {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}.
+ {skip_groups, Dir, Suite, GroupNames, Comment}.
+ {skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}.
- {skip_cases, DirRef, Suite, Cases, Comment}.
- {skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}.
- </pre>
+ {skip_cases, Dir, Suite, Cases, Comment}.
+ {skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.</pre>
+
<p>Types:</p>
<pre>
- NodeAlias = atom()
- InitOptions = term()
- Node = node()
- NodeRef = NodeAlias | Node | master
- NodeRefs = all_nodes | [NodeRef] | NodeRef
- N = integer()
- Bool = true | false
- CoverSpecFile = string()
- IncludeDirs = string() | [string()]
- ConfigFiles = string() | [string()]
- DirAlias = atom()
- Dir = string()
- LogDir = string()
- PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
- EventHandlers = atom() | [atom()]
- InitArgs = [term()]
- CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
- CTHModule = atom()
- CTHInitArgs = term()
- DirRef = DirAlias | Dir
- Suites = atom() | [atom()] | all
- Suite = atom()
- Groups = GroupSpec | [GroupSpec] | all
- GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
- GroupName = atom()
- GroupNames = GroupName | [GroupName]
- Cases = atom() | [atom()] | all
- Comment = string() | ""
- </pre>
- <p>Example:</p>
+ Bool = true | false
+ Constant = atom()
+ Value = term()
+ NodeAlias = atom()
+ Node = node()
+ NodeRef = NodeAlias | Node | master
+ NodeRefs = all_nodes | [NodeRef] | NodeRef
+ InitOptions = term()
+ Label = atom() | string()
+ VerbosityLevels = integer() | [{Category,integer()}]
+ Category = atom()
+ CSSFile = string()
+ ConnTypes = all | [atom()]
+ N = integer()
+ CoverSpecFile = string()
+ IncludeDirs = string() | [string()]
+ ConfigFiles = string() | [string()]
+ ConfigDir = string()
+ ConfigBaseNames = string() | [string()]
+ CallbackModule = atom()
+ ConfigStrings = string() | [string()]
+ LogDir = string()
+ LogOpts = [term()]
+ PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
+ EventHandlers = atom() | [atom()]
+ InitArgs = [term()]
+ CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
+ CTHModule = atom()
+ CTHInitArgs = term()
+ Dir = string()
+ Suites = atom() | [atom()] | all
+ Suite = atom()
+ Groups = GroupSpec | [GroupSpec] | all
+ GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
+ GroupName = atom()
+ GroupNames = GroupName | [GroupName]
+ Cases = atom() | [atom()] | all
+ Comment = string() | ""</pre>
+
+ <p>The difference between the <c>config</c> terms above, is that with
+ <c>ConfigDir</c>, <c>ConfigBaseNames</c> is a list of base names,
+ i.e. without directory paths. <c>ConfigFiles</c> must be full names,
+ including paths. E.g, these two terms have the same meaning:</p>
<pre>
- {logdir, "/home/test/logs"}.
+ {config, ["/home/testuser/tests/config/nodeA.cfg",
+ "/home/testuser/tests/config/nodeB.cfg"]}.
+
+ {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.</pre>
+
+ <note><p>Any relative paths specified in the test specification, will be
+ relative to the directory which contains the test specification file, if
+ <c>ct_run -spec TestSpecFile ...</c> or
+ <c>ct:run:test([{spec,TestSpecFile},...])</c>
+ executes the test. The path will be relative to the top level log directory, if
+ <c>ct:run:testspec(TestSpec)</c> executes the test.</p></note>
- {config, "/home/test/t1/cfg/config.cfg"}.
- {config, "/home/test/t2/cfg/config.cfg"}.
- {config, "/home/test/t3/cfg/config.cfg"}.
+ <p>The <c>define</c> term introduces a constant, which is used to
+ replace the name <c>Constant</c> with <c>Value</c>, wherever it's found in
+ the test specification. This replacement happens during an initial iteration
+ through the test specification. Constants may be used anywhere in the test
+ specification, e.g. in arbitrary lists and tuples, and even in strings
+ and inside the value part of other constant definitions! A constant can
+ also be part of a node name, but that is the only place where a constant
+ can be part of an atom.</p>
+
+ <note><p>For the sake of readability, the name of the constant must always
+ begin with an upper case letter, or a <c>$</c>, <c>?</c>, or <c>_</c>.
+ This also means that it must always be single quoted (obviously, since
+ the constant name is actually an atom, not text).</p></note>
+
+ <p>The main benefit of constants is that they can be used to reduce the size
+ (and avoid repetition) of long strings, such as file paths. Compare these
+ terms:</p>
+
+ <pre>
+ %% 1a. no constant
+ {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, "/home/testuser/tests/suites", all}.
- {alias, t1, "/home/test/t1"}.
- {alias, t2, "/home/test/t2"}.
- {alias, t3, "/home/test/t3"}.
+ %% 1b. with constant
+ {define, 'TESTDIR', "/home/testuser/tests"}.
+ {config, "'TESTDIR'/config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, "'TESTDIR'/suites", all}.
+
+ %% 2a. no constants
+ {config, [testnode@host1, testnode@host2], "../config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, [testnode@host1, testnode@host2], "../suites", [x_SUITE, y_SUITE]}.
+
+ %% 2b. with constants
+ {define, 'NODE', testnode}.
+ {define, 'NODES', ['NODE'@host1, 'NODE'@host2]}.
+ {config, 'NODES', "../config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, 'NODES', "../suites", [x_SUITE, y_SUITE]}.</pre>
+
+ <p>Constants make the test specification term <c>alias</c>, in previous
+ versions of Common Test, redundant. This term has been deprecated but will
+ remain supported in upcoming Common Test releases. Replacing <c>alias</c>
+ terms with <c>define</c> is strongly recommended though! Here's an example
+ of such a replacement:</p>
+
+ <pre>
+ %% using the old alias term
+ {config, "/home/testuser/tests/config/nodeA.cfg"}.
+ {alias, suite_dir, "/home/testuser/tests/suites"}.
+ {groups, suite_dir, x_SUITE, group1}.
+
+ %% replacing with constants
+ {define, 'TestDir', "/home/testuser/tests"}.
+ {define, 'CfgDir', "'TestDir'/config"}.
+ {define, 'SuiteDir', "'TestDir'/suites"}.
+ {config, 'CfgDir', "nodeA.cfg"}.
+ {groups, 'SuiteDir', x_SUITE, group1}.</pre>
+
+ <p>Actually, constants could well replace the <c>node</c> term too, but
+ this still has declarative value, mainly when used in combination
+ with <c>NodeRefs == all_nodes</c> (see types above).</p>
+
+ <p>Here follows a simple test specification example:</p>
+ <pre>
+ {define, 'Top', "/home/test"}.
+ {define, 'T1', "'Top'/t1"}.
+ {define, 'T2', "'Top'/t2"}.
+ {define, 'T3', "'Top'/t3"}.
+ {define, 'CfgFile', "config.cfg"}.
+
+ {logdir, "'Top'/logs"}.
- {suites, t1, all}.
- {skip_suites, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}.
- {skip_cases, t1, t1A_SUITE, [test3,test4], "Irrelevant"}.
- {skip_cases, t1, t1C_SUITE, [test1], "Ignore"}.
+ {config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}.
- {suites, t2, [t2B_SUITE,t2C_SUITE]}.
- {cases, t2, t2A_SUITE, [test4,test1,test7]}.
+ {suites, 'T1', all}.
+ {skip_suites, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
+ {skip_cases, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
+ {skip_cases, 'T1', t1C_SUITE, [test1], "Ignore"}.
- {skip_suites, t3, all, "Not implemented"}.
- </pre>
+ {suites, 'T2', [t2B_SUITE,t2C_SUITE]}.
+ {cases, 'T2', t2A_SUITE, [test4,test1,test7]}.
+
+ {skip_suites, 'T3', all, "Not implemented"}.</pre>
+
<p>The example specifies the following:</p>
<list>
<item>The specified logdir directory will be used for storing
@@ -564,8 +704,6 @@
date and time).</item>
<item>The variables in the specified test system config files will be
imported for the test.</item>
- <item>Aliases are given for three test system directories. The suites in
- this example are stored in "/home/test/tX/test".</item>
<item>The first test to run includes all suites for system t1. Excluded from
the test are however the t1B and t1D suites. Also test cases test3 and
test4 in t1A as well as the test1 case in t1C are excluded from
@@ -590,8 +728,46 @@
If <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> is used for starting the tests, the relaxed scanner
mode is enabled by means of the tuple: <c>{allow_user_terms,true}</c></p>
</section>
+
+ <section>
+ <title>Running tests from the Web based GUI</title>
+
+ <p>The web based GUI, VTS, is started with the
+ <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></c>
+ program. From the GUI you can load config files, and select
+ directories, suites and cases to run. You can also state the
+ config files, directories, suites and cases on the command line
+ when starting the web based GUI.
+ </p>
+
+ <list>
+ <item><c>ct_run -vts</c></item>
+ <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item>
+ <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath>
+ -case <casename>]]></c></item>
+ </list>
+
+ <p>From the GUI you can run tests and view the result and the logs.
+ </p>
+
+ <p>Note that <c>ct_run -vts</c> will try to open the Common Test start
+ page in an existing web browser window or start the browser if it is
+ not running. Which browser should be started may be specified with
+ the browser start command option:</p>
+ <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p>
+ <p>Example:</p>
+ <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p>
+ <p>Note that the browser must run as a separate OS process or VTS will hang!</p>
+ <p>If no specific browser start command is specified, Firefox will
+ be the default browser on Unix platforms and Internet Explorer on Windows.
+ If Common Test fails to start a browser automatically, or <c>'none'</c> is
+ specified as the value for -browser (i.e. <c>-browser none</c>), start your
+ favourite browser manually and type in the URL that Common Test
+ displays in the shell.</p>
+ </section>
<section>
+ <marker id="log_files"></marker>
<title>Log files</title>
<p>As the execution of the test suites proceed, events are logged in
@@ -719,17 +895,30 @@
<p>instead of each <c>x</c> printed on a new line, which is the default behaviour.</p>
</section>
+ <section>
+ <marker id="table_sorting"></marker>
+ <title>Sorting HTML table columns</title>
+ <p>By clicking the name in the column header of any table (e.g. "Ok", "Case", "Time", etc),
+ the table rows are sorted in whatever order makes sense for the type of value (e.g.
+ numerical for "Ok" or "Time", and alphabetical for "Case"). The sorting is performed
+ by means of JavaScript code, automatically inserted into the HTML log files. Common Test
+ uses the <url href="http://jquery.com">jQuery</url> library and the
+ <url href="http://tablesorter.com">tablesorter</url> plugin, with customized sorting
+ functions, for this implementation.</p>
+ </section>
</section>
<section>
<marker id="html_stylesheet"></marker>
<title>HTML Style Sheets</title>
- <p>Common Test uses a CSS file to control the look of the HTML
- files generated during test runs. If, for some reason, the
- log files are not displayed correctly in the HTML browser of your
- choice, or you prefer the "pre Common Test v1.6 look"
- of the log files (i.e. not using CSS), use the start flag/option
- <c>basic_html</c> to revert to the old style.</p>
+ <p>Common Test uses an HTML Style Sheet (CSS file) to control the look of
+ the HTML log files generated during test runs. If, for some reason, the
+ log files are not displayed correctly in the browser of your
+ choice, or you prefer a more primitive ("pre Common Test v1.6") look
+ of the logs, use the start flag/option:</p>
+ <pre>basic_html</pre>
+ <p>This disables the use of Style Sheets, as well as JavaScripts (see
+ table sorting above).</p>
<p>Common Test includes an <em>optional</em> feature to allow
user HTML style sheets for customizing printouts. The
@@ -882,75 +1071,82 @@
<section>
<marker id="silent_connections"></marker>
<title>Silent Connections</title>
- <p>The protocol handling processes in Common Test, implemented by ct_telnet, ct_ftp etc,
- do verbose printing to the test case logs. This can be switched off by means
- of the <c>-silent_connections</c> flag:</p>
+ <p>The protocol handling processes in Common Test, implemented by ct_telnet,
+ ct_ssh, ct_ftp etc, do verbose printing to the test case logs. This can be switched off
+ by means of the <c>-silent_connections</c> flag:</p>
<pre>
ct_run -silent_connections [conn_types]
</pre>
- <p>where <c>conn_types</c> specifies <c>telnet, ftp, rpc</c> and/or <c>snmp</c>.</p>
+ <p>where <c>conn_types</c> specifies <c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>.</p>
<p>Example:</p>
<pre>
- ct_run ... -silent_connections telnet ftp</pre>
- <p>switches off logging for telnet and ftp connections.</p>
+ ct_run ... -silent_connections ssh telnet</pre>
+ <p>switches off logging for ssh and telnet connections.</p>
<pre>
ct_run ... -silent_connections</pre>
<p>switches off logging for all connection types.</p>
- <p>Basic and important information such as opening and closing a connection,
- fatal communication error and reconnection attempts will always be printed even
- if logging has been suppressed for the connection type in question. However, operations
- such as sending and receiving data may be performed silently.</p>
+ <p>Fatal communication error and reconnection attempts will always be printed even
+ if logging has been suppressed for the connection type in question. However, operations
+ such as sending and receiving data will be performed silently.</p>
<p>It is possible to also specify <c>silent_connections</c> in a test suite. This is
accomplished by returning a tuple, <c>{silent_connections,ConnTypes}</c>, in the
<c>suite/0</c> or test case info list. If <c>ConnTypes</c> is a list of atoms
- (<c>telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections
+ (<c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections
will be suppressed. Full logging is per default enabled for any connection of type not
specified in <c>ConnTypes</c>. Hence, if <c>ConnTypes</c> is the empty list, logging
is enabled for all connections.</p>
- <p>The <c>silent_connections</c> setting returned from a test case info function overrides,
- for the test case in question, any setting made with <c>suite/0</c> (which is the setting
- used for all cases in the suite). Example:</p>
+ <p>Example:</p>
<pre>
-module(my_SUITE).
+
+ suite() -> [..., {silent_connections,[telnet,ssh]}, ...].
+
...
- suite() -> [..., {silent_connections,[telnet,ftp]}, ...].
- ...
+
my_testcase1() ->
- [{silent_connections,[ftp]}].
+ [{silent_connections,[ssh]}].
+
my_testcase1(_) ->
- ...
+ ...
+
my_testcase2(_) ->
- ...
+ ...
</pre>
<p>In this example, <c>suite/0</c> tells Common Test to suppress
- printouts from telnet and ftp connections. This is valid for
+ printouts from telnet and ssh connections. This is valid for
all test cases. However, <c>my_testcase1/0</c> specifies that
- for this test case, only ftp should be silent. The result is
+ for this test case, only ssh should be silent. The result is
that <c>my_testcase1</c> will get telnet info (if any) printed
- in the log, but not ftp info. <c>my_testcase2</c> will get no
+ in the log, but not ssh info. <c>my_testcase2</c> will get no
info from either connection printed.</p>
- <p>The <c>-silent_connections</c> tag (or
- <c>silent_connections</c> tagged tuple in the call to
- <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>) overrides any settings in the test
- suite.</p>
+ <p><c>silent_connections</c> may also be specified with a term
+ in a test specification
+ (see <seealso marker="run_test_chapter#test_specifications">Test
+ Specifications</seealso>). Connections provided with the
+ <c>silent_connections</c> start flag/option, will be merged with
+ any connections listed in the test specification.</p>
+
+ <p>The <c>silent_connections</c> start flag/option and test
+ specification term, overrides any settings made by the info functions
+ inside the test suite.</p>
- <p>Note that in the current Common Test version, the
+ <note><p>Note that in the current Common Test version, the
<c>silent_connections</c> feature only works for telnet
- connections. Support for other connection types will be added
- in future Common Test versions.</p>
+ and ssh connections! Support for other connection types will be added
+ in future Common Test versions.</p></note>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 1fae50577e..248d7de8b6 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -47,7 +47,7 @@
module for details about these functions.</p>
<p>The CT application also includes other modules named
- <c><![CDATA[ct_<something>]]></c> that
+ <c><![CDATA[ct_<component>]]></c> that
provide various support, mainly simplified use of communication
protocols such as rpc, snmp, ftp, telnet, etc.</p>
@@ -886,7 +886,7 @@
of its sub-groups. If a timetrap value is defined by <c>group/1</c>
for a sub-group, it overrides that of its higher level groups. Timetrap
values set by individual test cases (by means of the test case info
- function) overrides both group- and suite- level timetraps.</p>
+ function) override both group- and suite- level timetraps.</p>
<p>It is also possible to dynamically set/reset a timetrap during the
excution of a test case, or configuration function. This is done by calling
@@ -935,6 +935,99 @@
</section>
<section>
+ <marker id="logging"></marker>
+ <title>Logging - categories and verbosity levels</title>
+ <p>Common Test provides three main functions for printing strings:</p>
+ <list>
+ <item><c>ct:log(Category, Importance, Format, Args)</c></item>
+ <item><c>ct:print(Category, Importance, Format, Args)</c></item>
+ <item><c>ct:pal(Category, Importance, Format, Args)</c></item>
+ </list>
+ <p>The <c>log/1/2/3/4</c> function will print a string to the test case
+ log file. The <c>print/1/2/3/4</c> function will print the string to screen,
+ and the <c>pal/1/2/3/4</c> function will print the same string both to file and
+ screen. (The functions are documented in the <c>ct</c> reference manual).</p>
+
+ <p>The optional <c>Category</c> argument may be used to categorize the
+ log printout, and categories can be used for two things:</p>
+ <list>
+ <item>To compare the importance of the printout to a specific
+ verbosity level, and</item>
+ <item>to format the printout according to a user specific HTML
+ Style Sheet (CSS).</item>
+ </list>
+
+ <p>The <c>Importance</c> argument specifies a level of importance
+ which, compared to a verbosity level (general and/or set per category),
+ determines if the printout should be visible or not. <c>Importance</c>
+ is an arbitrary integer in the range 0..99. Pre-defined constants
+ exist in the <c>ct.hrl</c> header file. The default importance level,
+ <c>?STD_IMPORTANCE</c> (used if the <c>Importance</c> argument is not
+ provided), is 50. This is also the importance used for standard IO, e.g.
+ from printouts made with <c>io:format/2</c>, <c>io:put_chars/1</c>, etc.</p>
+
+ <p><c>Importance</c> is compared to a verbosity level set by means of the
+ <c>verbosity</c> start flag/option. The verbosity level can be set per
+ category and/or generally. The default verbosity level, <c>?STD_VERBOSITY</c>,
+ is 50, i.e. all standard IO gets printed. If a lower verbosity level is set,
+ standard IO printouts will be ignored. Common Test performs the following test:</p>
+ <pre>Importance >= (100-VerbosityLevel)</pre>
+ <p>This also means that verbosity level 0 effectively turns all logging off
+ (with the exception of printouts made by Common Test itself).</p>
+
+ <p>The general verbosity level is not associated with any particular
+ category. This level sets the threshold for the standard IO printouts,
+ uncategorized <c>ct:log/print/pal</c> printouts, as well as
+ printouts for categories with undefined verbosity level.</p>
+
+ <p>Example:</p>
+ <pre>
+
+ Some printouts during test case execution:
+
+ io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]),
+ ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]),
+ ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]),
+ ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]),
+ ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
+ ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),
+
+ If starting the test without specifying any verbosity levels:
+
+ $ ct_run ...
+
+ the following gets printed:
+
+ 1. Standard IO, importance = 50
+ 2. Uncategorized, importance = 50
+ 3. Categorized info, importance = 50
+ 5. Categorized error, importance = 75
+ 6. Categorized error, importance = 99
+
+ If starting the test with:
+
+ $ ct_run -verbosity 1 and info 75
+
+ the following gets printed:
+
+ 3. Categorized info, importance = 50
+ 4. Categorized info, importance = 25
+ 6. Categorized error, importance = 99
+ </pre>
+
+ <p>How categories can be mapped to CSS tags is documented in the
+ <seealso marker="run_test_chapter#html_stylesheet">Running Tests</seealso>
+ chapter.</p>
+
+ <p>The <c>Format</c> and <c>Args</c> arguments in <c>ct:log/print/pal</c> are
+ always passed on to the <c>io:format/3</c> function in <c>stdlib</c>
+ (please see the <c>io</c> manual page for details).</p>
+
+ <p>For more information about log files, please see the
+ <seealso marker="run_test_chapter#log_files">Running Tests</seealso> chapter.</p>
+ </section>
+
+ <section>
<title>Illegal dependencies</title>
<p>Even though it is highly efficient to write test suites with
@@ -944,7 +1037,6 @@
Erlang/OTP test suites.</p>
<list>
-
<item>Depending on current directory, and writing there:<br></br>
<p>This is a common error in test suites. It is assumed that
@@ -956,19 +1048,10 @@
</p>
</item>
- <item>Depending on the Clearcase (file version control system)
- paths and files:<br></br>
-
- <p>The test suites are stored in Clearcase but are not
- (necessarily) run within this environment. The directory
- structure may vary from test run to test run.
- </p>
- </item>
-
<item>Depending on execution order:<br></br>
- <p>During development of test suites, no assumption should be made
- (preferrably) about the execution order of the test cases or suites.
+ <p>During development of test suites, no assumption should preferrably
+ be made about the execution order of the test cases or suites.
E.g. a test case should not assume that a server it depends on,
has already been started by a previous test case. There are
several reasons for this:
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index e369c9f4ef..49b51c9207 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -25,24 +25,24 @@
%%%
%%% <p><strong>Test Suite Support Macros</strong></p>
%%%
-%%% <p>The <code>config</code> macro is defined in <code>ct.hrl</code>. This
+%%% <p>The <c>config</c> macro is defined in <c>ct.hrl</c>. This
%%% macro should be used to retrieve information from the
-%%% <code>Config</code> variable sent to all test cases. It is used with two
+%%% <c>Config</c> variable sent to all test cases. It is used with two
%%% arguments, where the first is the name of the configuration
-%%% variable you wish to retrieve, and the second is the <code>Config</code>
+%%% variable you wish to retrieve, and the second is the <c>Config</c>
%%% variable supplied to the test case.</p>
%%%
%%% <p>Possible configuration variables include:</p>
%%% <ul>
-%%% <li><code>data_dir</code> - Data file directory.</li>
-%%% <li><code>priv_dir</code> - Scratch file directory.</li>
-%%% <li>Whatever added by <code>init_per_suite/1</code> or
-%%% <code>init_per_testcase/2</code> in the test suite.</li>
+%%% <li><c>data_dir</c> - Data file directory.</li>
+%%% <li><c>priv_dir</c> - Scratch file directory.</li>
+%%% <li>Whatever added by <c>init_per_suite/1</c> or
+%%% <c>init_per_testcase/2</c> in the test suite.</li>
%%% </ul>
%%% @type var_name() = atom(). A variable name which is specified when
-%%% <code>ct:require/2</code> is called,
-%%% e.g. <code>ct:require(mynodename,{node,[telnet]})</code>
+%%% <c>ct:require/2</c> is called,
+%%% e.g. <c>ct:require(mynodename,{node,[telnet]})</c>
%%%
%%% @type target_name() = var_name(). The name of a target.
%%%
@@ -99,10 +99,10 @@
%%% <p>Run this function once before first test.</p>
%%%
%%% <p>Example:<br/>
-%%% <code>install([{config,["config_node.ctc","config_user.ctc"]}])</code>.</p>
+%%% <c>install([{config,["config_node.ctc","config_user.ctc"]}])</c>.</p>
%%%
%%% <p>Note that this function is automatically run by the
-%%% <code>ct_run</code> program.</p>
+%%% <c>ct_run</c> program.</p>
install(Opts) ->
ct_run:install(Opts).
@@ -115,10 +115,10 @@ install(Opts) ->
%%%
%%% @doc Run the given test case(s).
%%%
-%%% <p>Requires that <code>ct:install/1</code> has been run first.</p>
+%%% <p>Requires that <c>ct:install/1</c> has been run first.</p>
%%%
%%% <p>Suites (*_SUITE.erl) files must be stored in
-%%% <code>TestDir</code> or <code>TestDir/test</code>. All suites
+%%% <c>TestDir</c> or <c>TestDir/test</c>. All suites
%%% will be compiled when test is run.</p>
run(TestDir,Suite,Cases) ->
ct_run:run(TestDir,Suite,Cases).
@@ -157,7 +157,7 @@ run(TestDirs) ->
%%% {refresh_logs,LogDir} | {logopts,LogOpts} |
%%% {verbosity,VLevels} | {basic_html,Bool} |
%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
-%%% {noinput,Bool}
+%%% {release_shell,Bool}
%%% TestDirs = [string()] | string()
%%% Suites = [string()] | [atom()] | string() | atom()
%%% Cases = [atom()] | atom()
@@ -194,23 +194,39 @@ run(TestDirs) ->
%%% CTHs = [CTHModule | {CTHModule, CTHInitArgs}]
%%% CTHModule = atom()
%%% CTHInitArgs = term()
-%%% Result = [TestResult] | {error,Reason}
-%%% @doc Run tests as specified by the combination of options in <code>Opts</code>.
+%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | TestRunnerPid | {error,Reason}
+%%% Ok = integer()
+%%% Failed = integer()
+%%% UserSkipped = integer()
+%%% AutoSkipped = integer()
+%%% TestRunnerPid = pid()
+%%% Reason = term()
+%%% @doc <p>Run tests as specified by the combination of options in <c>Opts</c>.
%%% The options are the same as those used with the
-%%% <seealso marker="ct_run#ct_run"><code>ct_run</code></seealso> program.
-%%% Note that here a <code>TestDir</code> can be used to point out the path to
-%%% a <code>Suite</code>. Note also that the option <code>testcase</code>
-%%% corresponds to the <code>-case</code> option in the <code>ct_run</code>
-%%% program. Configuration files specified in <code>Opts</code> will be
-%%% installed automatically at startup.
+%%% <seealso marker="ct_run#ct_run"><c>ct_run</c></seealso> program.
+%%% Note that here a <c>TestDir</c> can be used to point out the path to
+%%% a <c>Suite</c>. Note also that the option <c>testcase</c>
+%%% corresponds to the <c>-case</c> option in the <c>ct_run</c>
+%%% program. Configuration files specified in <c>Opts</c> will be
+%%% installed automatically at startup.</p>
+%%% <p><c>TestRunnerPid</c> is returned if <c>release_shell == true</c>
+%%% (see <c>break/1</c> for details).</p>
+%%% <p><c>Reason</c> indicates what type of error has been encountered.</p>
run_test(Opts) ->
ct_run:run_test(Opts).
%%%-----------------------------------------------------------------
%%% @spec run_testspec(TestSpec) -> Result
%%% TestSpec = [term()]
-%%% @doc Run test specified by <code>TestSpec</code>. The terms are
+%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | {error,Reason}
+%%% Ok = integer()
+%%% Failed = integer()
+%%% UserSkipped = integer()
+%%% AutoSkipped = integer()
+%%% Reason = term()
+%%% @doc Run test specified by <c>TestSpec</c>. The terms are
%%% the same as those used in test specification files.
+%%% <p><c>Reason</c> indicates what type of error has been encountered.</p>
run_testspec(TestSpec) ->
ct_run:run_testspec(TestSpec).
@@ -230,8 +246,8 @@ step(TestDir,Suite,Case) ->
%%% Opt = config | keep_inactive
%%%
%%% @doc Step through a test case with the debugger. If the
-%%% <code>config</code> option has been given, breakpoints will
-%%% be set also on the configuration functions in <code>Suite</code>.
+%%% <c>config</c> option has been given, breakpoints will
+%%% be set also on the configuration functions in <c>Suite</c>.
%%% @see run/3
step(TestDir,Suite,Case,Opts) ->
ct_run:step(TestDir,Suite,Case,Opts).
@@ -243,20 +259,20 @@ step(TestDir,Suite,Case,Opts) ->
%%%
%%% <p>From this mode all test case support functions can be executed
%%% directly from the erlang shell. The interactive mode can also be
-%%% started from the OS command line with <code>ct_run -shell
-%%% [-config File...]</code>.</p>
+%%% started from the OS command line with <c>ct_run -shell
+%%% [-config File...]</c>.</p>
%%%
%%% <p>If any functions using "required config data" (e.g. telnet or
%%% ftp functions) are to be called from the erlang shell, config data
-%%% must first be required with <code>ct:require/2</code>.</p>
+%%% must first be required with <c>ct:require/2</c>.</p>
%%%
%%% <p>Example:<br/>
-%%% <code>&gt; ct:require(unix_telnet, unix).</code><br/>
-%%% <code>ok</code><br/>
-%%% <code>&gt; ct_telnet:open(unix_telnet).</code><br/>
-%%% <code>{ok,&lt;0.105.0&gt;}</code><br/>
-%%% <code>&gt; ct_telnet:cmd(unix_telnet, "ls .").</code><br/>
-%%% <code>{ok,["ls","file1 ...",...]}</code></p>
+%%% <c>&gt; ct:require(unix_telnet, unix).</c><br/>
+%%% <c>ok</c><br/>
+%%% <c>&gt; ct_telnet:open(unix_telnet).</c><br/>
+%%% <c>{ok,&lt;0.105.0&gt;}</c><br/>
+%%% <c>&gt; ct_telnet:cmd(unix_telnet, "ls .").</c><br/>
+%%% <c>{ok,["ls","file1 ...",...]}</c></p>
start_interactive() ->
ct_util:start(interactive).
@@ -283,21 +299,21 @@ stop_interactive() ->
%%% to specify arbitrarily deep tuples as <c>Required</c>. Note that it is
%%% only the last element of the tuple which can be a list of <c>SubKey</c>s.
%%%
-%%% <p>Example 1: require the variable <code>myvar</code>:</p>
+%%% <p>Example 1: require the variable <c>myvar</c>:</p>
%%% <pre>ok = ct:require(myvar).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
%%% <pre>{myvar,Value}.</pre>
%%%
-%%% <p>Example 2: require the key <code>myvar</code> with
-%%% subkeys <code>sub1</code> and <code>sub2</code>:</p>
+%%% <p>Example 2: require the key <c>myvar</c> with
+%%% subkeys <c>sub1</c> and <c>sub2</c>:</p>
%%% <pre>ok = ct:require({myvar,[sub1,sub2]}).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
%%% <pre>{myvar,[{sub1,Value},{sub2,Value}]}.</pre>
%%%
-%%% <p>Example 3: require the key <code>myvar</code> with
-%%% subkey <code>sub1</code> with <code>subsub1</code>:</p>
+%%% <p>Example 3: require the key <c>myvar</c> with
+%%% subkey <c>sub1</c> with <c>subsub1</c>:</p>
%%% <pre>ok = ct:require({myvar,sub1,sub2}).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
@@ -323,12 +339,12 @@ require(Required) ->
%%% of <c>SubKey</c>s.
%%%
%%% <p>If the requested data is available, the sub entry will be
-%%% associated with <code>Name</code> so that the value of the element
-%%% can be read with <code>get_config/1,2</code> provided
-%%% <code>Name</code> instead of the whole <code>Required</code> term.</p>
+%%% associated with <c>Name</c> so that the value of the element
+%%% can be read with <c>get_config/1,2</c> provided
+%%% <c>Name</c> instead of the whole <c>Required</c> term.</p>
%%%
%%% <p>Example: Require one node with a telnet connection and an
-%%% ftp connection. Name the node <code>a</code>:
+%%% ftp connection. Name the node <c>a</c>:
%%% <pre>ok = ct:require(a,{machine,node}).</pre>
%%% All references to this node may then use the node name.
%%% E.g. you can fetch a file over ftp like this:</p>
@@ -337,12 +353,12 @@ require(Required) ->
%%% <p>For this to work, the config file must at least contain:</p>
%%% <pre>{machine,[{node,[{telnet,IpAddr},{ftp,IpAddr}]}]}.</pre>
%%%
-%%% <note>The behaviour of this function changed radically in common_test
+%%% <note><p>The behaviour of this function changed radically in common_test
%%% 1.6.2. In order too keep some backwards compatability it is still possible
%%% to do: <br/><c>ct:require(a,{node,[telnet,ftp]}).</c><br/>
%%% This will associate the name <c>a</c> with the top level <c>node</c> entry.
%%% For this to work, the config file must at least contain:<br/>
-%%% <c>{node,[{telnet,IpAddr},{ftp,IpAddr}]}.</c></note>
+%%% <c>{node,[{telnet,IpAddr},{ftp,IpAddr}]}.</c></p></note>
%%%
%%% @see require/1
%%% @see get_config/1
@@ -377,7 +393,7 @@ get_config(Required,Default) ->
%%%
%%% <p>This function returns the matching value(s) or config element(s),
%%% given a config variable key or its associated name
-%%% (if one has been specified with <code>require/2</code> or a
+%%% (if one has been specified with <c>require/2</c> or a
%%% require statement).</p>
%%%
%%% <p>Example, given the following config file:</p>
@@ -385,33 +401,33 @@ get_config(Required,Default) ->
%%% {unix,[{telnet,IpAddr},
%%% {user,[{username,Username},
%%% {password,Password}]}]}.</pre>
-%%% <p><code>ct:get_config(unix,Default) ->
+%%% <p><c>ct:get_config(unix,Default) ->
%%% [{telnet,IpAddr},
%%% {user, [{username,Username},
-%%% {password,Password}]}]</code><br/>
-%%% <code>ct:get_config({unix,telnet},Default) -> IpAddr</code><br/>
-%%% <code>ct:get_config({unix,user,username},Default) -> Username</code><br/>
-%%% <code>ct:get_config({unix,ftp},Default) -> Default</code><br/>
-%%% <code>ct:get_config(unknownkey,Default) -> Default</code></p>
+%%% {password,Password}]}]</c><br/>
+%%% <c>ct:get_config({unix,telnet},Default) -> IpAddr</c><br/>
+%%% <c>ct:get_config({unix,user,username},Default) -> Username</c><br/>
+%%% <c>ct:get_config({unix,ftp},Default) -> Default</c><br/>
+%%% <c>ct:get_config(unknownkey,Default) -> Default</c></p>
%%%
%%% <p>If a config variable key has been associated with a name (by
-%%% means of <code>require/2</code> or a require statement), the name
+%%% means of <c>require/2</c> or a require statement), the name
%%% may be used instead of the key to read the value:</p>
%%%
-%%% <p><code>ct:require(myuser,{unix,user}) -> ok.</code><br/>
-%%% <code>ct:get_config(myuser,Default) ->
+%%% <p><c>ct:require(myuser,{unix,user}) -> ok.</c><br/>
+%%% <c>ct:get_config(myuser,Default) ->
%%% [{username,Username},
-%%% {password,Password}]</code></p>
+%%% {password,Password}]</c></p>
%%%
%%% <p>If a config variable is defined in multiple files and you want to
-%%% access all possible values, use the <code>all</code> option. The
+%%% access all possible values, use the <c>all</c> option. The
%%% values will be returned in a list and the order of the elements
%%% corresponds to the order that the config files were specified at
%%% startup.</p>
%%%
%%% <p>If you want config elements (key-value tuples) returned as result
-%%% instead of values, use the <code>element</code> option.
-%%% The returned elements will then be on the form <code>{Required,Value}</code></p>
+%%% instead of values, use the <c>element</c> option.
+%%% The returned elements will then be on the form <c>{Required,Value}</c></p>
%%%
%%% @see get_config/1
%%% @see get_config/2
@@ -484,11 +500,11 @@ log(X1,X2,X3) ->
%%% <p>This function is meant for printing a string directly from a
%%% test case to the test case log file.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code>,
-%%% default <code>Importance</code> is <code>?STD_IMPORTANCE</code>,
-%%% and default value for <code>Args</code> is <code>[]</code>.</p>
-%%% <p>Please see the User's Guide for details on <code>Category</code>
-%%% and <code>Importance</code>.</p>
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
log(Category,Importance,Format,Args) ->
ct_logs:tc_log(Category,Importance,Format,Args).
@@ -538,11 +554,11 @@ print(X1,X2,X3) ->
%%% <p>This function is meant for printing a string from a test case
%%% to the console.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code>,
-%%% default <code>Importance</code> is <code>?STD_IMPORTANCE</code>,
-%%% and default value for <code>Args</code> is <code>[]</code>.</p>
-%%% <p>Please see the User's Guide for details on <code>Category</code>
-%%% and <code>Importance</code>.</p>
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
print(Category,Importance,Format,Args) ->
ct_logs:tc_print(Category,Importance,Format,Args).
@@ -592,11 +608,11 @@ pal(X1,X2,X3) ->
%%% <p>This function is meant for printing a string from a test case,
%%% both to the test case log file and to the console.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code>,
-%%% default <code>Importance</code> is <code>?STD_IMPORTANCE</code>,
-%%% and default value for <code>Args</code> is <code>[]</code>.</p>
-%%% <p>Please see the User's Guide for details on <code>Category</code>
-%%% and <code>Importance</code>.</p>
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
pal(Category,Importance,Format,Args) ->
ct_logs:tc_pal(Category,Importance,Format,Args).
@@ -615,7 +631,7 @@ capture_start() ->
%%% @spec capture_stop() -> ok
%%%
%%% @doc Stop capturing text strings (a session started with
-%%% <code>capture_start/0</code>).
+%%% <c>capture_start/0</c>).
%%%
%%% @see capture_start/0
%%% @see capture_get/1
@@ -638,9 +654,9 @@ capture_get() ->
%%%
%%% @doc Return and purge the list of text strings buffered
%%% during the latest session of capturing printouts to stdout.
-%%% With <code>ExclCategories</code> it's possible to specify
-%%% log categories that should be ignored in <code>ListOfStrings</code>.
-%%% If <code>ExclCategories = []</code>, no filtering takes place.
+%%% With <c>ExclCategories</c> it's possible to specify
+%%% log categories that should be ignored in <c>ListOfStrings</c>.
+%%% If <c>ExclCategories = []</c>, no filtering takes place.
%%%
%%% @see capture_start/0
%%% @see capture_stop/0
@@ -665,7 +681,7 @@ capture_get([]) ->
%%% Reason = term()
%%%
%%% @doc Terminate a test case with the given error
-%%% <code>Reason</code>.
+%%% <c>Reason</c>.
fail(Reason) ->
try
exit({test_case_failed,Reason})
@@ -685,7 +701,7 @@ fail(Reason) ->
%%%
%%% @doc Terminate a test case with an error message specified
%%% by a format string and a list of values (used as arguments to
-%%% <code>io_lib:format/2</code>).
+%%% <c>io_lib:format/2</c>).
fail(Format, Args) ->
try io_lib:format(Format, Args) of
Str ->
@@ -708,11 +724,11 @@ fail(Format, Args) ->
%%% @spec comment(Comment) -> void()
%%% Comment = term()
%%%
-%%% @doc Print the given <code>Comment</code> in the comment field in
+%%% @doc Print the given <c>Comment</c> in the comment field in
%%% the table on the test suite result page.
%%%
%%% <p>If called several times, only the last comment is printed.
-%%% The test case return value <code>{comment,Comment}</code>
+%%% The test case return value <c>{comment,Comment}</c>
%%% overwrites the string set by this function.</p>
comment(Comment) when is_list(Comment) ->
Formatted =
@@ -735,10 +751,10 @@ comment(Comment) ->
%%% @doc Print the formatted string in the comment field in
%%% the table on the test suite result page.
%%%
-%%% <p>The <code>Format</code> and <code>Args</code> arguments are
-%%% used in call to <code>io_lib:format/2</code> in order to create
-%%% the comment string. The behaviour of <code>comment/2</code> is
-%%% otherwise the same as the <code>comment/1</code> function (see
+%%% <p>The <c>Format</c> and <c>Args</c> arguments are
+%%% used in call to <c>io_lib:format/2</c> in order to create
+%%% the comment string. The behaviour of <c>comment/2</c> is
+%%% otherwise the same as the <c>comment/1</c> function (see
%%% above for details).</p>
comment(Format, Args) when is_list(Format), is_list(Args) ->
Formatted =
@@ -783,11 +799,11 @@ get_target_name(Handle) ->
%%% @doc Parse the printout from an SQL table and return a list of tuples.
%%%
%%% <p>The printout to parse would typically be the result of a
-%%% <code>select</code> command in SQL. The returned
-%%% <code>Table</code> is a list of tuples, where each tuple is a row
+%%% <c>select</c> command in SQL. The returned
+%%% <c>Table</c> is a list of tuples, where each tuple is a row
%%% in the table.</p>
%%%
-%%% <p><code>Heading</code> is a tuple of strings representing the
+%%% <p><c>Heading</c> is a tuple of strings representing the
%%% headings of each column in the table.</p>
parse_table(Data) ->
ct_util:parse_table(Data).
@@ -859,8 +875,8 @@ make_and_load(Dir, Suite) ->
%%% SuiteUserData = [term()]
%%% Reason = term()
%%%
-%%% @doc Returns any data specified with the tag <code>userdata</code>
-%%% in the list of tuples returned from <code>Suite:suite/0</code>.
+%%% @doc Returns any data specified with the tag <c>userdata</c>
+%%% in the list of tuples returned from <c>Suite:suite/0</c>.
userdata(TestDir, Suite) ->
case make_and_load(TestDir, Suite) of
E = {error,_} ->
@@ -895,9 +911,9 @@ get_userdata(_BadTerm, Spec) ->
%%% TCUserData = [term()]
%%% Reason = term()
%%%
-%%% @doc Returns any data specified with the tag <code>userdata</code>
-%%% in the list of tuples returned from <code>Suite:group(GroupName)</code>
-%%% or <code>Suite:Case()</code>.
+%%% @doc Returns any data specified with the tag <c>userdata</c>
+%%% in the list of tuples returned from <c>Suite:group(GroupName)</c>
+%%% or <c>Suite:Case()</c>.
userdata(TestDir, Suite, {group,GroupName}) ->
case make_and_load(TestDir, Suite) of
E = {error,_} ->
@@ -960,6 +976,8 @@ get_testdata(Key) ->
Error;
{'EXIT',_Reason} ->
no_tests_running;
+ undefined ->
+ {error,no_testdata};
[CurrTC] when Key == curr_tc ->
{ok,CurrTC};
Data ->
@@ -975,7 +993,7 @@ get_testdata(Key) ->
%%% executing. The function is therefore only safe to call from a function which
%%% has been called (or synchronously invoked) by the test case.</p>
%%%
-%%% <p><code>Reason</code>, the reason for aborting the test case, is printed
+%%% <p><c>Reason</c>, the reason for aborting the test case, is printed
%%% in the test case log.</p>
abort_current_testcase(Reason) ->
test_server_ctrl:abort_current_testcase(Reason).
@@ -988,13 +1006,13 @@ abort_current_testcase(Reason) ->
%%% Reason = term()
%%%
%%% @doc <p>This function encrypts the source config file with DES3 and
-%%% saves the result in file <code>EncryptFileName</code>. The key,
+%%% saves the result in file <c>EncryptFileName</c>. The key,
%%% a string, must be available in a text file named
-%%% <code>.ct_config.crypt</code> in the current directory, or the
+%%% <c>.ct_config.crypt</c> in the current directory, or the
%%% home directory of the user (it is searched for in that order).</p>
%%% <p>See the Common Test User's Guide for information about using
%%% encrypted config files when running tests.</p>
-%%% <p>See the <code>crypto</code> application for details on DES3
+%%% <p>See the <c>crypto</c> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName) ->
ct_config:encrypt_config_file(SrcFileName, EncryptFileName).
@@ -1008,13 +1026,13 @@ encrypt_config_file(SrcFileName, EncryptFileName) ->
%%% Reason = term()
%%%
%%% @doc <p>This function encrypts the source config file with DES3 and
-%%% saves the result in the target file <code>EncryptFileName</code>.
+%%% saves the result in the target file <c>EncryptFileName</c>.
%%% The encryption key to use is either the value in
-%%% <code>{key,Key}</code> or the value stored in the file specified
-%%% by <code>{file,File}</code>.</p>
+%%% <c>{key,Key}</c> or the value stored in the file specified
+%%% by <c>{file,File}</c>.</p>
%%% <p>See the Common Test User's Guide for information about using
%%% encrypted config files when running tests.</p>
-%%% <p>See the <code>crypto</code> application for details on DES3
+%%% <p>See the <c>crypto</c> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
ct_config:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile).
@@ -1026,11 +1044,11 @@ encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
%%% TargetFileName = string()
%%% Reason = term()
%%%
-%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously
-%%% generated with <code>encrypt_config_file/2/3</code>. The original
+%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously
+%%% generated with <c>encrypt_config_file/2/3</c>. The original
%%% file contents is saved in the target file. The encryption key, a
%%% string, must be available in a text file named
-%%% <code>.ct_config.crypt</code> in the current directory, or the
+%%% <c>.ct_config.crypt</c> in the current directory, or the
%%% home directory of the user (it is searched for in that order).</p>
decrypt_config_file(EncryptFileName, TargetFileName) ->
ct_config:decrypt_config_file(EncryptFileName, TargetFileName).
@@ -1043,8 +1061,8 @@ decrypt_config_file(EncryptFileName, TargetFileName) ->
%%% KeyOrFile = {key,string()} | {file,string()}
%%% Reason = term()
%%%
-%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously
-%%% generated with <code>encrypt_config_file/2/3</code>. The original
+%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously
+%%% generated with <c>encrypt_config_file/2/3</c>. The original
%%% file contents is saved in the target file. The key must have the
%%% the same value as that used for encryption.</p>
decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
@@ -1061,7 +1079,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
%%% given callback module and configuration string. Callback module
%%% should be either loaded or present in the code part. Loaded
%%% configuration variables can later be removed using
-%%% <code>remove_config/2</code> function.</p>
+%%% <c>remove_config/2</c> function.</p>
add_config(Callback, Config)->
ct_config:add_config(Callback, Config).
@@ -1090,9 +1108,9 @@ remove_config(Callback, Config) ->
%%% A = list()
%%%
%%% @doc <p>Use this function to set a new timetrap for the running test case.
-%%% If the argument is <code>Func</code>, the timetrap will be triggered
-%%% when this function returns. <code>Func</code> may also return a new
-%%% <code>Time</code> value, which in that case will be the value for the
+%%% If the argument is <c>Func</c>, the timetrap will be triggered
+%%% when this function returns. <c>Func</c> may also return a new
+%%% <c>Time</c> value, which in that case will be the value for the
%%% new timetrap.</p>
timetrap(Time) ->
test_server:timetrap_cancel(),
@@ -1159,51 +1177,84 @@ sync_notify(Name,Data) ->
%%%-----------------------------------------------------------------
%%% @spec break(Comment) -> ok | {error,Reason}
%%% Comment = string()
-%%% Reason = {multiple_cases_running,TestCases}
+%%% Reason = {multiple_cases_running,TestCases} |
+%%% 'enable break with release_shell option'
%%% TestCases = [atom()]
%%%
-%%% @doc <p>This function will cancel all timetraps and pause the
+%%% @doc <p>This function will cancel any active timetrap and pause the
%%% execution of the current test case until the user calls the
%%% <c>continue/0</c> function. It gives the user the opportunity
%%% to interact with the erlang node running the tests, e.g. for
%%% debugging purposes or for manually executing a part of the
%%% test case. If a parallel group is executing, <c>break/2</c>
%%% should be called instead.</p>
+%%% <p>A cancelled timetrap will not be automatically
+%%% reactivated after the break, but must be started exlicitly with
+%%% <c>ct:timetrap/1</c></p>
+%%% <p>In order for the break/continue functionality to work,
+%%% Common Test must release the shell process controlling stdin.
+%%% This is done by setting the <c>release_shell</c> start option
+%%% to <c>true</c>. See the User's Guide for more information.</p>
+
break(Comment) ->
- case get_testdata(curr_tc) of
- {ok,{_,TestCase}} ->
- test_server:break(?MODULE, Comment);
- {ok,Cases} when is_list(Cases) ->
- {error,{multiple_cases_running,
- [TC || {_,TC} <- Cases]}};
- Error ->
- {error,Error}
+ case {ct_util:get_testdata(starter),
+ ct_util:get_testdata(release_shell)} of
+ {ct,ReleaseSh} when ReleaseSh /= true ->
+ Warning = "ct:break/1 can only be used if release_shell == true.\n",
+ ct_logs:log("Warning!", Warning, []),
+ io:format(user, "Warning! " ++ Warning, []),
+ {error,'enable break with release_shell option'};
+ _ ->
+ case get_testdata(curr_tc) of
+ {ok,{_,_TestCase}} ->
+ test_server:break(?MODULE, Comment);
+ {ok,Cases} when is_list(Cases) ->
+ {error,{'multiple cases running',
+ [TC || {_,TC} <- Cases]}};
+ Error = {error,_} ->
+ Error;
+ Error ->
+ {error,Error}
+ end
end.
%%%-----------------------------------------------------------------
%%% @spec break(TestCase, Comment) -> ok | {error,Reason}
%%% TestCase = atom()
%%% Comment = string()
-%%% Reason = test_case_not_running
+%%% Reason = 'test case not running' |
+%%% 'enable break with release_shell option'
%%%
%%% @doc <p>This function works the same way as <c>break/1</c>,
%%% only the <c>TestCase</c> argument makes it possible to
%%% pause a test case executing in a parallel group. The
%%% <c>continue/1</c> function should be used to resume
%%% execution of <c>TestCase</c>.</p>
+%%% <p>See <c>break/1</c> for more details.</p>
break(TestCase, Comment) ->
- case get_testdata(curr_tc) of
- {ok,Cases} when is_list(Cases) ->
- case lists:keymember(TestCase, 2, Cases) of
- true ->
+ case {ct_util:get_testdata(starter),
+ ct_util:get_testdata(release_shell)} of
+ {ct,ReleaseSh} when ReleaseSh /= true ->
+ Warning = "ct:break/2 can only be used if release_shell == true.\n",
+ ct_logs:log("Warning!", Warning, []),
+ io:format(user, "Warning! " ++ Warning, []),
+ {error,'enable break with release_shell option'};
+ _ ->
+ case get_testdata(curr_tc) of
+ {ok,Cases} when is_list(Cases) ->
+ case lists:keymember(TestCase, 2, Cases) of
+ true ->
+ test_server:break(?MODULE, TestCase, Comment);
+ false ->
+ {error,'test case not running'}
+ end;
+ {ok,{_,TestCase}} ->
test_server:break(?MODULE, TestCase, Comment);
- false ->
- {error,test_case_not_running}
- end;
- {ok,{_,TestCase}} ->
- test_server:break(?MODULE, TestCase, Comment);
- Error ->
- {error,Error}
+ Error = {error,_} ->
+ Error;
+ Error ->
+ {error,Error}
+ end
end.
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
index f3b6781971..bf27238121 100644
--- a/lib/common_test/src/ct_conn_log_h.erl
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -84,7 +84,7 @@ handle_call(_Query, State) ->
{ok, {error, bad_query}, State}.
terminate(_,#state{logs=Logs}) ->
- [file:close(Fd) || {_,_,Fds} <- Logs, Fd <- Fds],
+ [file:close(Fd) || {_,{_,Fds}} <- Logs, {_,Fd} <- Fds],
ok.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index e8d5d4708b..4d47731239 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -71,16 +71,13 @@ init_tc(Mod,Func,Config) ->
{skip,{require_failed_in_suite0,Reason}};
{Suite,{suite0_failed,_}=Failure} ->
{skip,Failure};
- CurrTC ->
- case CurrTC of
- undefined ->
- ct_util:set_testdata({curr_tc,[{Suite,Func}]});
- _ when is_list(CurrTC) ->
- ct_util:update_testdata(curr_tc,
- fun(Running) ->
- [{Suite,Func}|Running]
- end)
- end,
+ _ ->
+ ct_util:update_testdata(curr_tc,
+ fun(undefined) ->
+ [{Suite,Func}];
+ (Running) ->
+ [{Suite,Func}|Running]
+ end, [create]),
case ct_util:read_suite_data({seq,Suite,Func}) of
undefined ->
init_tc1(Mod,Suite,Func,Config);
@@ -671,13 +668,15 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
%% reset the curr_tc state, or delete this TC from the list of
%% executing cases (if in a parallel group)
ClearCurrTC = fun(Running = [_,_|_]) ->
- lists:delete({Mod,Func},Running);
+ lists:keydelete(Func,2,Running);
({_,{suite0_failed,_}}) ->
undefined;
([{_,CurrTC}]) when CurrTC == Func ->
undefined;
(undefined) ->
- undefined
+ undefined;
+ (Unexpected) ->
+ exit({error,{reset_curr_tc,{Mod,Func},Unexpected}})
end,
ct_util:update_testdata(curr_tc,ClearCurrTC),
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index a202ca15e2..d80d216f9e 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -70,13 +70,14 @@
enable_builtin_hooks,
include = [],
auto_compile,
- silent_connections,
+ silent_connections = [],
stylesheet,
multiply_timetraps = 1,
scale_timetraps = false,
create_priv_dir,
testspecs = [],
- tests}).
+ tests,
+ starter}).
%%%-----------------------------------------------------------------
%%% @spec script_start() -> void()
@@ -304,9 +305,9 @@ script_start1(Parent, Args) ->
%% silent connections
SilentConns =
get_start_opt(silent_connections,
- fun(["all"]) -> [];
+ fun(["all"]) -> [all];
(Conns) -> [list_to_atom(Conn) || Conn <- Conns]
- end, Args),
+ end, [], Args),
%% stylesheet
Stylesheet = get_start_opt(stylesheet,
fun([SS]) -> ?abs(SS) end, Args),
@@ -334,7 +335,8 @@ script_start1(Parent, Args) ->
stylesheet = Stylesheet,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir},
+ create_priv_dir = CreatePrivDir,
+ starter = script},
%% check if log files should be refreshed or go on to run tests...
Result = run_or_refresh(StartOpts, Args),
@@ -401,6 +403,9 @@ script_start2(StartOpts = #opts{vts = undefined,
AllVerbosity =
merge_keyvals([StartOpts#opts.verbosity,
SpecStartOpts#opts.verbosity]),
+ AllSilentConns =
+ merge_vals([StartOpts#opts.silent_connections,
+ SpecStartOpts#opts.silent_connections]),
Cover =
choose_val(StartOpts#opts.cover,
SpecStartOpts#opts.cover),
@@ -467,6 +472,7 @@ script_start2(StartOpts = #opts{vts = undefined,
logopts = AllLogOpts,
basic_html = BasicHtml,
verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
config = SpecStartOpts#opts.config,
event_handlers = AllEvHs,
ct_hooks = AllCTHooks,
@@ -914,9 +920,9 @@ run_test2(StartOpts) ->
%% silent connections
SilentConns = get_start_opt(silent_connections,
- fun(all) -> [];
+ fun(all) -> [all];
(Conns) -> Conns
- end, StartOpts),
+ end, [], StartOpts),
%% stylesheet
Stylesheet = get_start_opt(stylesheet,
fun(SS) -> ?abs(SS) end,
@@ -999,7 +1005,8 @@ run_test2(StartOpts) ->
stylesheet = Stylesheet,
multiply_timetraps = MultiplyTT,
scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir},
+ create_priv_dir = CreatePrivDir,
+ starter = ct},
%% test specification
case proplists:get_value(spec, StartOpts) of
@@ -1043,6 +1050,8 @@ run_spec_file(Relaxed,
SpecOpts#opts.stylesheet),
AllVerbosity = merge_keyvals([Opts#opts.verbosity,
SpecOpts#opts.verbosity]),
+ AllSilentConns = merge_vals([Opts#opts.silent_connections,
+ SpecOpts#opts.silent_connections]),
AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]),
Cover = choose_val(Opts#opts.cover,
SpecOpts#opts.cover),
@@ -1091,6 +1100,7 @@ run_spec_file(Relaxed,
stylesheet = Stylesheet,
basic_html = BasicHtml,
verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
config = AllConfig,
event_handlers = AllEvHs,
auto_compile = AutoCompile,
@@ -1354,6 +1364,7 @@ get_data_for_node(#testspec{label = Labels,
basic_html = BHs,
stylesheet = SSs,
verbosity = VLvls,
+ silent_connections = SilentConnsList,
cover = CoverFs,
config = Cfgs,
userconfig = UsrCfgs,
@@ -1381,6 +1392,10 @@ get_data_for_node(#testspec{label = Labels,
undefined -> [];
Lvls -> Lvls
end,
+ SilentConns = case proplists:get_value(Node, SilentConnsList) of
+ undefined -> [];
+ SCs -> SCs
+ end,
Cover = proplists:get_value(Node, CoverFs),
MT = proplists:get_value(Node, MTs),
ST = proplists:get_value(Node, STs),
@@ -1398,6 +1413,7 @@ get_data_for_node(#testspec{label = Labels,
basic_html = BasicHtml,
stylesheet = Stylesheet,
verbosity = Verbosity,
+ silent_connections = SilentConns,
cover = Cover,
config = ConfigFiles,
event_handlers = EvHandlers,
@@ -1619,6 +1635,7 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
"run ct:start_interactive()\n\n",[]),
{error,interactive_mode};
_Pid ->
+ ct_util:set_testdata({starter,Opts#opts.starter}),
compile_and_run(Tests, Skip,
Opts1#opts{verbosity=Verbosity}, Args)
end
@@ -1632,13 +1649,16 @@ compile_and_run(Tests, Skip, Opts, Args) ->
%% enable silent connections
case Opts#opts.silent_connections of
[] ->
- Conns = ct_util:override_silence_all_connections(),
- ct_logs:log("Silent connections", "~p", [Conns]);
- Conns when is_list(Conns) ->
- ct_util:override_silence_connections(Conns),
- ct_logs:log("Silent connections", "~p", [Conns]);
- _ ->
- ok
+ ok;
+ Conns ->
+ case lists:member(all, Conns) of
+ true ->
+ Conns1 = ct_util:override_silence_all_connections(),
+ ct_logs:log("Silent connections", "~p", [Conns1]);
+ false ->
+ ct_util:override_silence_connections(Conns),
+ ct_logs:log("Silent connections", "~p", [Conns])
+ end
end,
log_ts_names(Opts#opts.testspecs),
TestSuites = suite_tuples(Tests),
@@ -1661,8 +1681,9 @@ compile_and_run(Tests, Skip, Opts, Args) ->
{Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors),
- possibly_spawn(true == proplists:get_value(noinput, Args),
- Tests1, Skip1, Opts);
+ ReleaseSh = proplists:get_value(release_shell, Args),
+ ct_util:set_testdata({release_shell,ReleaseSh}),
+ possibly_spawn(ReleaseSh == true, Tests1, Skip1, Opts);
false ->
io:nl(),
ct_util:stop(clean),
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 7759aca16b..a8b67d0329 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -779,8 +779,8 @@ add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) ->
add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool});
-add_tests([{noinput,Bool}|Ts],Spec) ->
- add_tests(Ts, Spec#testspec{noinput = Bool});
+add_tests([{release_shell,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{release_shell = Bool});
%% --- handled/errors ---
add_tests([{define,_,_}|Ts],Spec) -> % handled
@@ -940,6 +940,12 @@ handle_data(verbosity,Node,VLvls,_Spec) when is_list(VLvls) ->
VLvls1 = lists:map(fun(VLvl = {_Cat,_Lvl}) -> VLvl;
(Lvl) -> {'$unspecified',Lvl} end, VLvls),
[{Node,VLvls1}];
+handle_data(silent_connections,Node,all,_Spec) ->
+ [{Node,[all]}];
+handle_data(silent_connections,Node,Conn,_Spec) when is_atom(Conn) ->
+ [{Node,[Conn]}];
+handle_data(silent_connections,Node,Conns,_Spec) ->
+ [{Node,Conns}];
handle_data(_Tag,Node,Data,_Spec) ->
[{Node,Data}].
@@ -947,10 +953,10 @@ handle_data(_Tag,Node,Data,_Spec) ->
should_be_added(Tag,Node,_Data,Spec) ->
if
%% list terms *without* possible duplicates here
- Tag == logdir; Tag == logopts;
- Tag == basic_html; Tag == label;
+ Tag == logdir; Tag == logopts;
+ Tag == basic_html; Tag == label;
Tag == auto_compile; Tag == stylesheet;
- Tag == verbosity ->
+ Tag == verbosity; Tag == silent_connections ->
lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
read_field(Spec,Tag)) == false;
%% for terms *with* possible duplicates
@@ -1267,6 +1273,8 @@ valid_terms() ->
{basic_html,3},
{verbosity,2},
{verbosity,3},
+ {silent_connections,2},
+ {silent_connections,3},
{label,2},
{label,3},
{event_handler,2},
@@ -1275,7 +1283,7 @@ valid_terms() ->
{ct_hooks,2},
{ct_hooks,3},
{enable_builtin_hooks,2},
- {noinput,2},
+ {release_shell,2},
{multiply_timetraps,2},
{multiply_timetraps,3},
{scale_timetraps,2},
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index efc85543ac..cf891ed043 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -39,7 +39,7 @@
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
delete_testdata/0, delete_testdata/1,
set_testdata/1, get_testdata/1, get_testdata/2,
- set_testdata_async/1, update_testdata/2]).
+ set_testdata_async/1, update_testdata/2, update_testdata/3]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
@@ -252,7 +252,10 @@ delete_testdata(Key) ->
call({delete_testdata, Key}).
update_testdata(Key, Fun) ->
- call({update_testdata, Key, Fun}).
+ update_testdata(Key, Fun, []).
+
+update_testdata(Key, Fun, Opts) ->
+ call({update_testdata, Key, Fun, Opts}).
set_testdata(TestData) ->
call({set_testdata, TestData}).
@@ -333,7 +336,7 @@ loop(Mode,TestData,StartDir) ->
return(From,undefined)
end,
loop(From,TestData,StartDir);
- {{update_testdata,Key,Fun},From} ->
+ {{update_testdata,Key,Fun,Opts},From} ->
TestData1 =
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
@@ -341,8 +344,15 @@ loop(Mode,TestData,StartDir) ->
return(From,NewVal),
[{Key,NewVal}|lists:keydelete(Key,1,TestData)];
_ ->
- return(From,undefined),
- TestData
+ case lists:member(create,Opts) of
+ true ->
+ InitVal = Fun(undefined),
+ return(From,InitVal),
+ [{Key,InitVal}|TestData];
+ false ->
+ return(From,undefined),
+ TestData
+ end
end,
loop(From,TestData1,StartDir);
{{set_cwd,Dir},From} ->
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 7015014441..196b5e46d0 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -36,13 +36,14 @@
logopts=[],
basic_html=[],
verbosity=[],
+ silent_connections=[],
cover=[],
config=[],
userconfig=[],
event_handler=[],
ct_hooks=[],
enable_builtin_hooks=true,
- noinput=false,
+ release_shell=false,
include=[],
auto_compile=[],
stylesheet=[],
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
index 6466571623..b431301df6 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
@@ -1,5 +1,5 @@
%% -*- erlang -*-
-{netconf1,[{ssh,"localhost"},
+{netconf1,[{ssh,"127.0.0.1"},
{port,2060},
{user,"xxx"},
{password,"xxx"}]}.
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
index 79768a9a6a..d337158bce 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -107,7 +107,8 @@ all() ->
connection_crash,
get_event_streams,
create_subscription,
- receive_event]
+ receive_event
+ ]
end.
@@ -216,6 +217,7 @@ hello_required_exists(Config) ->
?NS:expect_do_reply('close-session',close,ok),
?ok = ct_netconfc:close_session(my_named_connection),
+ timer:sleep(500),
%% Then check that it can be used again after the first is closed
{ok,_Client2} = open_configured_success(my_named_connection,DataDir),
@@ -234,7 +236,7 @@ hello_global_pwd(Config) ->
hello_no_session_id(Config) ->
DataDir = ?config(data_dir,Config),
?NS:hello(no_session_id),
- ?NS:expect(hello),
+ ?NS:expect(no_session_id,hello),
{error,{incorrect_hello,no_session_id_found}} = open(DataDir),
ok.
@@ -261,7 +263,7 @@ hello_no_caps(Config) ->
no_server_hello(Config) ->
DataDir = ?config(data_dir,Config),
- ?NS:expect(hello),
+ ?NS:expect(undefined,hello),
{error,{hello_session_failed,timeout}} = open(DataDir,[{timeout,2000}]),
ok.
@@ -435,7 +437,7 @@ kill_session(Config) ->
{ok,Client} = open_success(DataDir),
?NS:hello(2),
- ?NS:expect(hello),
+ ?NS:expect(2,hello),
{ok,_OtherClient} = open(DataDir),
?NS:expect_do_reply('kill-session',{kill,2},ok),
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
index 665b0e556c..2427f37f52 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -31,9 +31,13 @@
hello/1,
hello/2,
expect/1,
+ expect/2,
expect_reply/2,
+ expect_reply/3,
expect_do/2,
+ expect_do/3,
expect_do_reply/3,
+ expect_do_reply/4,
hupp/1,
hupp/2]).
@@ -110,22 +114,30 @@ hello(SessionId,Stuff) ->
%% actions. To be called directly before sending a request.
expect(Expect) ->
expect_do_reply(Expect,undefined,undefined).
+expect(SessionId,Expect) ->
+ expect_do_reply(SessionId,Expect,undefined,undefined).
%% Tell server to expect the given message and reply with the give
%% reply. To be called directly before sending a request.
expect_reply(Expect,Reply) ->
expect_do_reply(Expect,undefined,Reply).
+expect_reply(SessionId,Expect,Reply) ->
+ expect_do_reply(SessionId,Expect,undefined,Reply).
%% Tell server to expect the given message and perform an action. To
%% be called directly before sending a request.
expect_do(Expect,Do) ->
expect_do_reply(Expect,Do,undefined).
+expect_do(SessionId,Expect,Do) ->
+ expect_do_reply(SessionId,Expect,Do,undefined).
%% Tell server to expect the given message, perform an action and
%% reply with the given reply. To be called directly before sending a
%% request.
expect_do_reply(Expect,Do,Reply) ->
- add_expect({Expect,Do,Reply}).
+ add_expect(1,{Expect,Do,Reply}).
+expect_do_reply(SessionId,Expect,Do,Reply) ->
+ add_expect(SessionId,{Expect,Do,Reply}).
%% Hupp the server - i.e. tell it to do something -
%% e.g. hupp(send_event) will cause send_event(State) to be called on
@@ -133,17 +145,19 @@ expect_do_reply(Expect,Do,Reply) ->
hupp(send_event) ->
hupp(send,[make_msg(event)]);
hupp(kill) ->
- hupp(fun hupp_kill/1,[]).
+ hupp(1,fun hupp_kill/1,[]).
hupp(send,Data) ->
- hupp(fun hupp_send/2,[Data]);
-hupp(Fun,Args) when is_function(Fun) ->
- [{_,Pid}] = lookup(channel_process),
+ hupp(1,fun hupp_send/2,[Data]).
+
+hupp(SessionId,Fun,Args) when is_function(Fun) ->
+ [{_,Pid}] = lookup({channel_process,SessionId}),
Pid ! {hupp,Fun,Args}.
%%%-----------------------------------------------------------------
%%% Main loop of the netconf server
init_server(Dir) ->
+ register(main_ns_proc,self()),
ets:new(ns_tab,[set,named_table,public]),
Config = ?ssh_config(Dir),
{_,Host} = lists:keyfind(interface, 1, Config),
@@ -165,7 +179,12 @@ loop(Daemon) ->
receive
{stop,From} ->
ssh:stop_daemon(Daemon),
- From ! stopped
+ From ! stopped;
+ {table_trans,Fun,Args,From} ->
+ %% Simple transaction mechanism for ets table
+ R = apply(Fun,Args),
+ From ! {table_trans_done,R},
+ loop(Daemon)
end.
%%----------------------------------------------------------------------
@@ -178,7 +197,7 @@ terminate(_Reason, _State) ->
ok.
handle_ssh_msg({ssh_cm,CM,{data, Ch, _Type = 0, Data}}, State) ->
- %% erlang:display({self(),data,CM,Ch,State}),
+ %% io:format("~p~n",[{self(),Data,CM,Ch,State}]),
data_for_channel(CM, Ch, Data, State);
handle_ssh_msg({ssh_cm,CM,{closed, Ch}}, State) ->
%% erlang:display({self(),closed,CM,Ch,State}),
@@ -194,7 +213,7 @@ handle_msg({ssh_channel_up,Ch,CM},undefined) ->
%% erlang:display({self(),up,CM,Ch}),
ConnRef = {CM,Ch},
SessionId = maybe_hello(ConnRef),
- insert(channel_process,self()), % used to hupp the server
+ insert({channel_process,SessionId},self()), % used to hupp the server
{ok, #session{connection = ConnRef,
session_id = SessionId}};
handle_msg({hupp,Fun,Args},State) ->
@@ -214,17 +233,19 @@ data_for_channel(CM, Ch, Data, State) ->
Stacktrace = erlang:get_stacktrace(),
error_logger:error_report([{?MODULE, data_for_channel},
{request, Data},
+ {buffer, State#session.buffer},
{reason, {Class, Reason}},
{stacktrace, Stacktrace}]),
stop_channel(CM, Ch, State)
end.
data(Data, State = #session{connection = ConnRef,
- buffer = Buffer}) ->
+ buffer = Buffer,
+ session_id = SessionId}) ->
AllData = <<Buffer/binary,Data/binary>>,
case find_endtag(AllData) of
{ok,Msgs,Rest} ->
- [check_expected(ConnRef,Msg) || Msg <- Msgs],
+ [check_expected(SessionId,ConnRef,Msg) || Msg <- Msgs],
{ok,State#session{buffer=Rest}};
need_more ->
{ok,State#session{buffer=AllData}}
@@ -258,15 +279,42 @@ send({CM,Ch},Data) ->
kill({CM,_Ch}) ->
ssh:close(CM).
-add_expect(Add) ->
- case lookup(expect) of
+add_expect(SessionId,Add) ->
+ table_trans(fun do_add_expect/2,[SessionId,Add]).
+
+table_trans(Fun,Args) ->
+ S = self(),
+ case whereis(main_ns_proc) of
+ S ->
+ apply(Fun,Args);
+ Pid ->
+ Pid ! {table_trans,Fun,Args,self()},
+ receive
+ {table_trans_done,Result} ->
+ Result
+ after 5000 ->
+ exit(table_trans_timeout)
+ end
+ end.
+
+do_add_expect(SessionId,Add) ->
+ case lookup({expect,SessionId}) of
[] ->
- insert(expect,[Add]);
- [{expect,First}] ->
- insert(expect,First ++ [Add])
+ insert({expect,SessionId},[Add]);
+ [{_,First}] ->
+ insert({expect,SessionId},First ++ [Add])
end,
ok.
+do_get_expect(SessionId) ->
+ case lookup({expect,SessionId}) of
+ [{_,[{Expect,Do,Reply}|Rest]}] ->
+ insert({expect,SessionId},Rest),
+ {Expect,Do,Reply};
+ _ ->
+ error
+ end.
+
insert(Key,Value) ->
ets:insert(ns_tab,{Key,Value}).
lookup(Key) ->
@@ -292,17 +340,18 @@ find_endtag(Data) ->
{ok,lists:sublist(Msgs,length(Msgs)-1),lists:last(Msgs)}
end.
-check_expected(ConnRef,Msg) ->
- case lookup(expect) of
- [{expect,[{Expect,Do,Reply}|Rest]}] ->
- insert(expect,Rest),
+check_expected(SessionId,ConnRef,Msg) ->
+ %% io:format("~p~n",[{check_expected,SessionId,Msg}]),
+ case table_trans(fun do_get_expect/1,[SessionId]) of
+ {Expect,Do,Reply} ->
%% erlang:display({got,io_lib:format("~s",[Msg])}),
%% erlang:display({expected,Expect}),
match(Msg,Expect),
do(ConnRef, Do),
reply(ConnRef,Reply);
- Expected ->
- exit({error,{got_unexpected,Msg,Expected}})
+ error ->
+ timer:sleep(1000),
+ exit({error,{got_unexpected,SessionId,Msg,ets:tab2list(ns_tab)}})
end.
match(Msg,Expect) ->
diff --git a/lib/common_test/test/ct_testspec_2_SUITE.erl b/lib/common_test/test/ct_testspec_2_SUITE.erl
index 93f3520f95..411529b52a 100644
--- a/lib/common_test/test/ct_testspec_2_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_2_SUITE.erl
@@ -79,6 +79,10 @@ all() ->
%% {logopts,3}
%% {basic_html,2}
%% {basic_html,3}
+%% {verbosity,2}
+%% {verbosity,3}
+%% {silent_connections,2}
+%% {silent_connections,3}
%% {label,2}
%% {label,3}
%% {event_handler,2}
@@ -87,7 +91,7 @@ all() ->
%% {ct_hooks,2}
%% {ct_hooks,3}
%% {enable_builtin_hooks,2}
-%% {noinput,2}
+%% {release_shell,2}
%% {multiply_timetraps,2}
%% {multiply_timetraps,3}
%% {scale_timetraps,2}
@@ -194,7 +198,7 @@ basic_compatible_no_nodes(_Config) ->
ct_hooks = [{Node,{cth_mod1,[]}},
{Node,{cth_mod2,[]}}],
enable_builtin_hooks = true,
- noinput = false,
+ release_shell = false,
include = Incls,
auto_compile = [],
stylesheet = [],
@@ -321,7 +325,7 @@ basic_compatible_nodes(_Config) ->
{Node1,{cth_mod2,[]}},
{Node2,{cth_mod2,[]}}],
enable_builtin_hooks = true,
- noinput = false,
+ release_shell = false,
include = Incls,
auto_compile = [],
stylesheet = [],
@@ -533,9 +537,12 @@ misc_config_terms(_Config) ->
{basic_html,n1@h1,false},
{basic_html,n2@h2,true},
+ {silent_connections,n1@h1,all},
+ {silent_connections,n2@h2,[ssh]},
+
{enable_builtin_hooks,false},
- {noinput,true},
+ {release_shell,true},
{auto_compile,false},
{auto_compile,n1@h1,true},
@@ -571,6 +578,7 @@ misc_config_terms(_Config) ->
Verify = #testspec{spec_dir = SpecDir,
nodes = [{undefined,Node},{x,n1@h1},{y,n2@h2}],
basic_html = [{Node,true},{n1@h1,false},{n2@h2,true}],
+ silent_connections = [{n1@h1,[all]},{n2@h2,[ssh]}],
config = [{Node,CfgA},
{n1@h1,CfgA},
{n2@h2,CfgA},
@@ -582,7 +590,7 @@ misc_config_terms(_Config) ->
{n1@h1,CfgD},
{n2@h2,CfgD}],
enable_builtin_hooks = false,
- noinput = true,
+ release_shell = true,
auto_compile = [{Node,false},
{n1@h1,true},
{n2@h2,false}],
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index be9eb1cd75..27d750f929 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -316,7 +316,7 @@ module.beam: module.erl \
<item>
<p>Add <c>Dir</c> to the list of directories to be searched
when including a file. When encountering an
- <c>-include</c> or <c>-include_dir</c> directive,
+ <c>-include</c> or <c>-include_lib</c> directive,
the compiler searches for header files in the following
directories:</p>
<list type="ordered">
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index a24747a872..91ab244620 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1875,7 +1875,7 @@ static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Data|{digest,Digest}, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */
ErlNifBinary data_bin, ret_bin;
- unsigned char hmacbuf[SHA_DIGEST_LENGTH];
+ unsigned char hmacbuf[SHA512_LEN];
unsigned rsa_s_len;
RSA* rsa;
int i;
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 1b5bc44dde..7ac693f371 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -349,6 +349,14 @@ hmac_update_sha256(doc) ->
hmac_update_sha256(suite) ->
[];
hmac_update_sha256(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ hmac_update_sha256_do()
+ end.
+
+hmac_update_sha256_do() ->
?line Key = hexstr2bin("00010203101112132021222330313233"
"04050607141516172425262734353637"
"08090a0b18191a1b28292a2b38393a3b"
@@ -368,6 +376,14 @@ hmac_update_sha512(doc) ->
hmac_update_sha512(suite) ->
[];
hmac_update_sha512(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ hmac_update_sha512_do()
+ end.
+
+hmac_update_sha512_do() ->
?line Key = hexstr2bin("00010203101112132021222330313233"
"04050607141516172425262734353637"
"08090a0b18191a1b28292a2b38393a3b"
@@ -406,6 +422,14 @@ hmac_rfc4231(doc) ->
hmac_rfc4231(suite) ->
[];
hmac_rfc4231(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ hmac_rfc4231_do()
+ end.
+
+hmac_rfc4231_do() ->
%% Test Case 1
Case1Key = binary:copy(<<16#0b>>, 20),
Case1Data = <<"Hi There">>,
@@ -1927,3 +1951,11 @@ my_dss_sign(Data,Key) ->
?line S3 = crypto:dss_sign(none, crypto:sha(Raw), Key),
[S1,S2,S3].
+openssl_version() ->
+ case crypto:info_lib() of
+ [{<<"OpenSSL">>,LibVer,_}] when is_integer(LibVer) ->
+ LibVer;
+ _ ->
+ undefined
+ end.
+
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 496d317f8a..c237d4e0e9 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -184,6 +184,7 @@ analysis_start(Parent, Analysis) ->
false -> Callgraph
end,
State3 = analyze_callgraph(NewCallgraph, State2#analysis_state{plt = Plt1}),
+ dialyzer_callgraph:dispose_race_server(NewCallgraph),
rcv_and_send_ext_types(Parent),
NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
NonExportsList = sets:to_list(NonExports),
@@ -519,9 +520,10 @@ rcv_and_send_ext_types(Parent) ->
Self ! {Self, done},
case rcv_ext_types(Self, []) of
[] -> ok;
- ExtTypes -> Parent ! {Self, ext_types, ExtTypes}
- end,
- ok.
+ ExtTypes ->
+ Parent ! {Self, ext_types, ExtTypes},
+ ok
+ end.
rcv_ext_types(Self, ExtTypes) ->
receive
diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl
index 5719132215..28dcf098af 100644
--- a/lib/dialyzer/src/dialyzer_coordinator.erl
+++ b/lib/dialyzer/src/dialyzer_coordinator.erl
@@ -116,7 +116,7 @@ spawn_jobs(Mode, Jobs, InitData, Timing) ->
fun(Job, Count) ->
Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator),
case TypesigOrDataflow of
- true -> true = ets:insert(SCCtoPID, {Job, Pid});
+ true -> true = ets:insert(SCCtoPID, {Job, Pid}), ok;
false -> request_activation(Regulator, Pid)
end,
Count + 1
@@ -217,7 +217,8 @@ request_activation({_Collector, Regulator, _SCCtoPID}) ->
wait_activation().
request_activation(Regulator, Pid) ->
- Regulator ! {req, Pid}.
+ Regulator ! {req, Pid},
+ ok.
spawn_regulator() ->
InitTickets = dialyzer_utils:parallelism(),
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index b26aa695bd..c6f7c56227 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -192,48 +192,50 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
RunButtons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons = wxFlexGridSizer:new(3),
- wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
- wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(ChooseItem, Lab1, Center),
- wxSizer:add(ChooseItem, ChosenBox, Opts),
- wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
- wxSizer:add(FileTypeItem, OptionsLabel),
- wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
- wxSizer:add(LogItem, LogLabel, Center),
- wxSizer:add(LogItem, LogBox, Opts3),
- wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
- wxSizer:add(FileItem, FileLabel),
- wxSizer:add(FileItem, FilePicker),
- wxSizer:add(DirItem, DirLabel),
- wxSizer:add(DirItem, DirPicker),
- wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
- wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
- wxSizer:add(FileDirItem, FileItem),
- wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
- wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
- wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
- wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
- wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
- wxSizer:add(RunButtons, RunButton, ?BorderOpt),
- wxSizer:add(RunButtons, StopButton, ?BorderOpt),
- wxSizer:add(Buttons, WarnButtons),
- wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), [{flag, ?wxEXPAND}]),
- wxSizer:add(Buttons, RunButtons),
- wxFlexGridSizer:addGrowableCol(Buttons, 1),
- wxSizer:add(WarningsItem, WarningsLabel, Center),
- wxSizer:add(WarningsItem, WarningsBox, Opts3),
- wxSizer:add(WarningsItem, Buttons, [{flag, ?wxEXPAND bor ?wxALL},?Border]),
-
- wxSizer:add(Left, ChooseItem, Opts),
- wxSizer:add(Left, FileDirItem, [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
- wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
- wxSizer:add(RightUp, LogItem, Opts3),
- wxSizer:add(Right, RightUp, Opts3),
- wxSizer:add(Right, WarningsItem, Opts3),
- wxSizer:add(Top, Left, Opts),
- wxSizer:add(Top, Right, Opts3),
-
- wxSizer:add(All, Top, Opts),
+ _ = wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(ChooseItem, Lab1, Center),
+ _ = wxSizer:add(ChooseItem, ChosenBox, Opts),
+ _ = wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
+ _ = wxSizer:add(FileTypeItem, OptionsLabel),
+ _ = wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
+ _ = wxSizer:add(LogItem, LogLabel, Center),
+ _ = wxSizer:add(LogItem, LogBox, Opts3),
+ _ = wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
+ _ = wxSizer:add(FileItem, FileLabel),
+ _ = wxSizer:add(FileItem, FilePicker),
+ _ = wxSizer:add(DirItem, DirLabel),
+ _ = wxSizer:add(DirItem, DirPicker),
+ _ = wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
+ _ = wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, FileItem),
+ _ = wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
+ _ = wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
+ _ = wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
+ _ = wxSizer:add(RunButtons, RunButton, ?BorderOpt),
+ _ = wxSizer:add(RunButtons, StopButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, WarnButtons),
+ _ = wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""),
+ [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(Buttons, RunButtons),
+ _ = wxFlexGridSizer:addGrowableCol(Buttons, 1),
+ _ = wxSizer:add(WarningsItem, WarningsLabel, Center),
+ _ = wxSizer:add(WarningsItem, WarningsBox, Opts3),
+ _ = wxSizer:add(WarningsItem, Buttons,
+ [{flag, ?wxEXPAND bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Left, ChooseItem, Opts),
+ _ = wxSizer:add(Left, FileDirItem,
+ [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
+ _ = wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
+ _ = wxSizer:add(RightUp, LogItem, Opts3),
+ _ = wxSizer:add(Right, RightUp, Opts3),
+ _ = wxSizer:add(Right, WarningsItem, Opts3),
+ _ = wxSizer:add(Top, Left, Opts),
+ _ = wxSizer:add(Top, Right, Opts3),
+
+ _ = wxSizer:add(All, Top, Opts),
wxWindow:setSizer(Frame, All),
wxWindow:setSizeHints(Frame, {1150,600}),
wxWindow:show(Frame),
@@ -294,91 +296,67 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
createFileMenu() ->
FileMenu = wxMenu:new(),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
- {text, "Save &Warnings"}])),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
- {text, "Save &Log"}])),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
- {text, "E&xit\tAlt-X"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
+ {text, "Save &Warnings"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
+ {text, "Save &Log"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
+ {text, "E&xit\tAlt-X"}])),
FileMenu.
createWarningsMenu() ->
WarningsMenu = wxMenu:new(),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_MATCH_FAILURES,
- "Match failures"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_FAIL_FUN_CALLS,
- "Failing function calls"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_BAD_FUN,
- "Bad fun applications"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_BAD_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_OPAQUE,
- "Opaqueness violations"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_OPAQUE, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_LIST_CONSTR,
- "Improper list constructions"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_LIST_CONSTR, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_UNUSED_FUN,
- "Unused functions"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_UNUSED_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_ERROR_HANDLING_FUN,
- "Error handling functions"),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_NO_RETURN_FUN,
- "Functions of no return"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_UNEXPORTED_FUN,
- "Call to unexported function"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_RACE_CONDITIONS,
- "Possible race conditions"),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_WRONG_CONTRACTS,
- "Wrong contracts"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_CONTRACT_SYNTAX,
- "Wrong contract syntax"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, true),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, "Match failures"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS,
+ "Failing function calls"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_BAD_FUN, "Bad fun applications"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opaqueness violations"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_LIST_CONSTR,
+ "Improper list constructions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_UNUSED_FUN, "Unused functions"),
+ _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_ERROR_HANDLING_FUN,
+ "Error handling functions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN,
+ "Functions of no return"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN,
+ "Call to unexported function"),
+ _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_RACE_CONDITIONS,
+ "Possible race conditions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, "Wrong contracts"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX,
+ "Wrong contract syntax"),
WarningsMenu.
+addCheckedItem(Menu, ItemId, Str) ->
+ _ = wxMenu:appendCheckItem(Menu, ItemId, Str),
+ wxMenu:check(Menu, ItemId, true).
+
createPltMenu() ->
PltMenu = wxMenu:new(),
- wxMenu:appendCheckItem(PltMenu,
- ?menuID_PLT_INIT_EMPTY,
- "Init with empty PLT"),
- wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
- {text, "Show contents"}])),
- wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
- {text, "Search contents"}])),
+ _ = wxMenu:appendCheckItem(PltMenu, ?menuID_PLT_INIT_EMPTY,
+ "Init with empty PLT"),
+ _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
+ {text, "Show contents"}])),
+ _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
+ {text, "Search contents"}])),
PltMenu.
createOptionsMenu() ->
OptsMenu = wxMenu:new(),
- wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
- {text, "Manage Macro Definitions"}])),
- wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
- {text, "Manage Include Directories"}])),
+ _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
+ {text, "Manage Macro Definitions"}])),
+ _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
+ {text, "Manage Include Directories"}])),
OptsMenu.
createHelpMenu() ->
HelpMenu = wxMenu:new(),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
- {text, "Manual"}])),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
- {text, "Warning Options"}])),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
- {text, "About"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
+ {text, "Manual"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
+ {text, "Warning Options"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
+ {text, "About"}])),
HelpMenu.
%% ----------------------------------------------------------------
@@ -583,20 +561,20 @@ search_doc_plt(#gui_state{gui = Wx} = State) ->
ArLayout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
- wxSizer:add(ModLayout,ModText, ?BorderOpt),
- wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
- wxSizer:add(FunLayout,FunText, ?BorderOpt),
- wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
- wxSizer:add(ArLayout,ArText, ?BorderOpt),
- wxSizer:add(Buttons, SearchButton, ?BorderOpt),
- wxSizer:add(Buttons,Cancel, ?BorderOpt),
-
- wxSizer:add(Top, ModLayout),
- wxSizer:add(Top, FunLayout),
- wxSizer:add(Top, ArLayout),
- wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
- wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
+ _ = wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
+ _ = wxSizer:add(ModLayout, ModText, ?BorderOpt),
+ _ = wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
+ _ = wxSizer:add(FunLayout,FunText, ?BorderOpt),
+ _ = wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
+ _ = wxSizer:add(ArLayout,ArText, ?BorderOpt),
+ _ = wxSizer:add(Buttons, SearchButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons,Cancel, ?BorderOpt),
+
+ _ = wxSizer:add(Top, ModLayout),
+ _ = wxSizer:add(Top, FunLayout),
+ _ = wxSizer:add(Top, ArLayout),
+ _ = wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
+ _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
wxFrame:show(Dialog),
@@ -654,14 +632,15 @@ error_sms(State, Message) ->
output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error).
output_sms(#gui_state{frame = Frame}, Title, Message, Type) ->
- case Type of
- error ->
- MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_ERROR}]);
- info ->
- MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_INFORMATION}])
- end,
+ Style = case Type of
+ error -> ?wxOK bor ?wxICON_ERROR;
+ info -> ?wxOK bor ?wxICON_INFORMATION
+ end,
+ Options = [{caption, Title}, {style, Style}],
+ MessageWin = wxMessageDialog:new(Frame, Message, Options),
wxWindow:setSizeHints(MessageWin, {350,100}),
- wxDialog:showModal(MessageWin).
+ wxDialog:showModal(MessageWin),
+ ok.
free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
Contents = lists:flatten(Contents0),
@@ -685,8 +664,9 @@ free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(Layout, Editor, ?BorderOpt),
- wxSizer:add(Layout, Ok, [{flag, ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Editor, ?BorderOpt),
+ Flag = ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL,
+ _ = wxSizer:add(Layout, Ok, [{flag, Flag}, ?Border]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
show_info_loop(Frame, Win).
@@ -696,7 +676,7 @@ show_info_loop(Frame, Win) ->
#wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win);
#wx{id = ?Message, event = #wxClose{type = close_window}} ->
- wxWindow:destroy(Win);
+ wxWindow:destroy(Win);
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Frame)
end.
@@ -921,13 +901,14 @@ save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Typ
{message, Message},
{style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(FileDialog) of
- ?wxID_OK -> Path = wxFileDialog:getPath(FileDialog),
- case wxTextCtrl:saveFile(Box,[{file,Path}]) of
- true -> ok;
- false -> error_sms(State,"Could not write to file:\n" ++ Path)
- end;
+ ?wxID_OK ->
+ Path = wxFileDialog:getPath(FileDialog),
+ case wxTextCtrl:saveFile(Box,[{file,Path}]) of
+ true -> ok;
+ false -> error_sms(State, "Could not write to file:\n" ++ Path)
+ end;
?wxID_CANCEL -> wxWindow:destroy(FileDialog);
- _ -> error_sms(State,"Could not write to file:\n")
+ _ -> error_sms(State, "Could not write to file:\n")
end
end.
@@ -961,16 +942,16 @@ include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
- wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Buttons1, Ok, ?BorderOpt),
- wxSizer:add(Buttons1,Cancel, ?BorderOpt),
- wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
+ _ = wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons1, Ok, ?BorderOpt),
+ _ = wxSizer:add(Buttons1,Cancel, ?BorderOpt),
+ _ = wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
@@ -1058,21 +1039,21 @@ macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
- wxSizer:add(MacroItem, MacroText, ?BorderOpt),
- wxSizer:add(TermItem, TermLabel, ?BorderOpt),
- wxSizer:add(TermItem, TermText, ?BorderOpt),
- wxSizer:add(Item, MacroItem),
- wxSizer:add(Item, TermItem),
- wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
- wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Buttons1, Ok, ?BorderOpt),
- wxSizer:add(Buttons1, Cancel, ?BorderOpt),
- wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
+ _ = wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
+ _ = wxSizer:add(MacroItem, MacroText, ?BorderOpt),
+ _ = wxSizer:add(TermItem, TermLabel, ?BorderOpt),
+ _ = wxSizer:add(TermItem, TermText, ?BorderOpt),
+ _ = wxSizer:add(Item, MacroItem),
+ _ = wxSizer:add(Item, TermItem),
+ _ = wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons1, Ok, ?BorderOpt),
+ _ = wxSizer:add(Buttons1, Cancel, ?BorderOpt),
+ _ = wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
@@ -1216,13 +1197,14 @@ show_explanation(#gui_state{gui = Wx} = State, Explanation) ->
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Buttons, ExplButton, ?BorderOpt),
- wxSizer:add(Buttons, Ok, ?BorderOpt),
- wxSizer:add(Layout, Editor,[{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons, ExplButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, Ok, ?BorderOpt),
+ _ = wxSizer:add(Layout, Editor, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
- show_explanation_loop(State#gui_state{explanation_box = Editor}, Win, Explanation)
+ NewState = State#gui_state{explanation_box = Editor},
+ show_explanation_loop(NewState, Win, Explanation)
end.
show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) ->
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 93e2603c10..b8652a7482 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,13 +43,13 @@ under the License.
<description>
<p>
-This module provides the interface with which a user
-creates a service that sends and receives messages using the
+This module provides the interface with which a user can
+implement a Diameter node that sends and receives messages using the
Diameter protocol as defined in RFC 3588.</p>
<p>
Basic usage consists of creating a representation of a
-locally implemented Diameter peer and its capabilities with <seealso
+locally implemented Diameter node and its capabilities with <seealso
marker="#start_service">start_service/2</seealso>, adding transport
capability using <seealso
marker="#add_transport">add_transport/2</seealso> and sending Diameter
@@ -61,17 +61,15 @@ marker="diameter_app">diameter_app(3)</seealso> callback modules as
specified in the service configuration.</p>
<p>
-Beware the difference between <em>diameter application</em> and
-<em>Diameter application</em>.
+Beware the difference between <em>diameter</em> (not capitalised) and
+<em>Diameter</em> (capitalised).
The former refers to the Erlang application named diameter whose main
-api is defined here, the latter to an application of the Diameter
-protocol in the sense of RFC 3588.
-More generally, capitalized Diameter refers to the RFC
-and diameter to this implementation.</p>
+api is defined here, the latter to Diameter protocol in the sense of
+RFC 3588.</p>
<p>
-The diameter application must be started before calling functions in
-this module.</p>
+The diameter application must be started before calling most functions
+in this module.</p>
</description>
@@ -84,13 +82,15 @@ this module.</p>
<tag><c>Address()</c></tag>
<tag><c>DiameterIdentity()</c></tag>
+<tag><c>Grouped()</c></tag>
+<tag><c>OctetString()</c></tag>
<tag><c>Time()</c></tag>
<tag><c>Unsigned32()</c></tag>
<tag><c>UTF8String()</c></tag>
<item>
<p>
Types corresponding to RFC 3588 AVP Data Formats.
-Defined in <seealso marker="diameter_dict">diameter_dict(4)</seealso>.</p>
+Defined in <seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>.</p>
<marker id="application_alias"/>
</item>
@@ -99,10 +99,9 @@ Defined in <seealso marker="diameter_dict">diameter_dict(4)</seealso>.</p>
<item>
<p>
A name identifying a Diameter application in
-service configuration passed to <seealso
-marker="#start_service">start_service/2</seealso> and passed to
-<seealso marker="#call">call/4</seealso> when sending requests
-belonging to the application.</p>
+service configuration.
+Passed to <seealso marker="#call">call/4</seealso> when sending requests
+defined by the application.</p>
<marker id="application_module"/>
</item>
@@ -120,19 +119,14 @@ ExtraArgs = list()
A module implementing the callback interface defined in <seealso
marker="diameter_app">diameter_app(3)</seealso>, along with any
extra arguments to be appended to those documented for the interface.
-Any extra arguments are appended to the documented list of arguments for
-each function.
-Note that additional arguments specific to an outgoing request be
+Note that extra arguments specific to an outgoing request can be
specified to <seealso marker="#call">call/4</seealso>, in which case
-the call-specific arguments are appended to any specified with the
-callback module.</p>
+those are are appended to any module-specific extra arguments.</p>
<p>
Specifying a <c>#diameter_callback{}</c> record allows individual
functions to be configured in place of the usual <seealso
-marker="diameter_app">diameter_app(3)</seealso> callbacks, with
-default implementations provided by module <c>diameter_callback</c>
-unless otherwise specified.
+marker="diameter_app">diameter_app(3)</seealso> callbacks.
See that module for details.</p>
<marker id="application_opt"/>
@@ -142,18 +136,18 @@ See that module for details.</p>
<item>
<p>
-Options defining a Diameter application as configured in an
-<c>application</c> option passed to
-<seealso marker="#start_service">start_service/2</seealso>.</p>
+Options defining a Diameter application.
+Has one the following types.</p>
<taglist>
-<tag><c>{alias, application_alias()}</c></tag>
+<tag><c>{alias, <seealso marker="#application_alias">application_alias()</seealso>}</c></tag>
<item>
<p>
An unique identifier for the application in the scope of the
service.
-Optional, defaults to the value of the <c>dictionary</c> option.</p>
+Defaults to the value of the <c>dictionary</c> option if
+unspecified.</p>
</item>
<tag><c>{dictionary, atom()}</c></tag>
@@ -166,23 +160,23 @@ documented in <seealso
marker="diameter_dict">diameter_dict(4)</seealso>.</p>
</item>
-<tag><c>{module, application_module()}</c></tag>
+<tag><c>{module, <seealso marker="#application_module">application_module()</seealso>}</c></tag>
<item>
<p>
-A callback module with which messages of the Diameter application are
+The callback module with which messages of the Diameter application are
handled.
See <seealso marker="diameter_app">diameter_app(3)</seealso> for
-information on the required interface and semantics.</p>
+the required interface and semantics.</p>
</item>
<tag><c>{state, term()}</c></tag>
<item>
<p>
The initial callback state.
-Defaults to the value of the <c>alias</c> option if unspecified.
-The prevailing state is passed to certain
+The prevailing state is passed to some
<seealso marker="diameter_app">diameter_app(3)</seealso>
-callbacks, which can then return a new state.</p>
+callbacks, which can then return a new state.
+Defaults to the value of the <c>alias</c> option if unspecified.</p>
</item>
<tag><c>{call_mutates_state, true|false}</c></tag>
@@ -190,19 +184,18 @@ callbacks, which can then return a new state.</p>
<p>
Specifies whether or not the <seealso
marker="diameter_app#pick_peer">pick_peer/4</seealso>
-application callback (following from a call to
-<seealso marker="#call">call/4</seealso>)
-can modifiy state,
+application callback can modify the application state,
Defaults to <c>false</c> if unspecified.</p>
+<note>
<p>
-Note that <seealso
-marker="diameter_app#pick_peer">pick_peer</seealso> callbacks are
-serialized when these are allowed to modify state, which is a
+<seealso marker="diameter_app#pick_peer">pick_peer</seealso> callbacks
+are serialized when these are allowed to modify state, which is a
potential performance bottleneck.
A simple Diameter client may suffer no ill effects from using mutable
-state but a server or agent that responds to incoming request but
-sending its own requests should probably avoid it.</p>
+state but a server or agent that responds to incoming request should
+probably avoid it.</p>
+</note>
</item>
<tag><c>{answer_errors, callback|report|discard}</c></tag>
@@ -213,9 +206,9 @@ decode errors are handled.
If <c>callback</c> then errors result in a <seealso
marker="diameter_app#handle_answer">handle_answer/4</seealso>
callback in the same fashion as for <seealso
-marker="diameter_app#handle_request">handle_request/3</seealso>, in the
-<c>errors</c> field of the <c>diameter_packet</c> record passed into
-the callback.
+marker="diameter_app#handle_request">handle_request/3</seealso>, with
+errors communicated in the <c>errors</c> field of the
+<c>#diameter_packet{}</c> record passed to the callback.
If <c>report</c> then an answer containing errors is discarded
without a callback and a warning report is written to the log.
If <c>discard</c> then an answer containing errors is silently
@@ -239,7 +232,8 @@ Defaults to <c>report</c> if unspecified.</p>
<p>
Options available to <seealso marker="#call">call/4</seealso> when
-sending an outgoing Diameter request.</p>
+sending an outgoing Diameter request.
+Has one of the following types.</p>
<taglist>
@@ -248,12 +242,12 @@ sending an outgoing Diameter request.</p>
<p>
Extra arguments to append to callbacks to the callback
module in question.
-These are appended to any extra arguments configured with the callback
+These are appended to any extra arguments configured on the callback
itself.
Multiple options append to the argument list.</p>
</item>
-<tag><c>{filter, peer_filter()}</c></tag>
+<tag><c>{filter, <seealso marker="#peer_filter">peer_filter()</seealso>}</c></tag>
<item>
<p>
A filter to apply to the list of available peers before passing them to
@@ -264,7 +258,7 @@ corresponding list of filters.
Defaults to <c>none</c>.</p>
</item>
-<tag><c>{timeout, Unsigned32()}</c></tag>
+<tag><c>{timeout, <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
<item>
<p>
The number of milliseconds after which the request should
@@ -298,121 +292,55 @@ to fail.</p>
<item>
<p>
-AVP values used in outgoing CER/CEA messages during capabilities exchange.
-Can be configured both on a service and a transport, the latter taking
-precedence over the former.</p>
+AVP values sent in outgoing CER or CEA messages during capabilities
+exchange.
+Can be configured both on a service and a transport, values specified
+on the latter taking precedence over any specified on the former.
+Has one of the following types.</p>
<taglist>
-<tag><c>{'Origin-Host', DiameterIdentity()}</c></tag>
-<item>
-<p>
-Value of the Origin-Host AVP in outgoing messages.</p>
-</item>
-
-<tag><c>{'Origin-Realm', DiameterIdentity()}</c></tag>
-<item>
-<p>
-Value of the Origin-Realm AVP in outgoing messages.</p>
-</item>
-
-<tag><c>{'Host-IP-Address', [Address()]}</c></tag>
-<item>
-<p>
-Values of Host-IP-Address AVPs.
-Optional.</p>
-
-<p>
-The list of addresses is available to the start function of a
-transport module, which either uses them as is or returns a new list
-(typically as configured as <c>transport_config()</c> on the
-transport module in question) in order for the correct list of
-addresses to be sent in capabilities exchange messages.</p>
-</item>
-
-<tag><c>{'Vendor-Id', Unsigned32()}</c></tag>
-<item>
-<p>
-Value of the Vendor-Id AVP sent in an outgoing capabilities
-exchange message.</p>
-</item>
-
-<tag><c>{'Product-Name', UTF8String()}</c></tag>
+<tag><c>{'Origin-Host', <seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c></tag>
+<tag><c>{'Origin-Realm', <seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c></tag>
+<tag><c>{'Host-IP-Address', [<seealso marker="diameter_dict#DATA_TYPES">Address()</seealso>]}</c></tag>
<item>
<p>
-Value of the Product-Name AVP sent in an outgoing capabilities
-exchange message.</p>
+An address list is available to the start function of a
+<seealso marker="diameter_transport">transport module</seealso>, which
+can return a new list for use in the subsequent CER or CEA.
+Host-IP-Address need not be specified if the transport start function
+returns an address list.</p>
</item>
-<tag><c>{'Origin-State-Id', Unsigned32()}</c></tag>
+<tag><c>{'Vendor-Id', <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
+<tag><c>{'Product-Name', <seealso marker="diameter_dict#DATA_TYPES">UTF8String()</seealso>}</c></tag>
+<tag><c>{'Origin-State-Id', <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
<item>
<p>
-Value of Origin-State-Id to be included in outgoing messages sent by
-diameter itself.
-In particular, the AVP will be included in CER/CEA and DWR/DWA messages.
-Optional.</p>
-
-<p>
+Origin-State-Id is optional but will be included in outgoing messages
+sent by diameter itself: CER/CEA, DWR/DWA and DPR/DPA.
Setting a value of <c>0</c> (zero) is equivalent to not setting a
value as documented in RFC 3588.
The function <seealso
marker="#origin_state_id">origin_state_id/0</seealso>
-can be used as to retrieve a value that is set when the diameter
+can be used as to retrieve a value that is computed when the diameter
application is started.</p>
</item>
-<tag><c>{'Supported-Vendor-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Supported-Vendor-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Auth-Application-Id', [Unsigned32()]}</c></tag>
+<tag><c>{'Supported-Vendor-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
+<tag><c>{'Auth-Application-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
+<tag><c>{'Inband-Security-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
<item>
<p>
-Values of Auth-Application-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Inband-Security-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Inband-Security-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list, which is equivalent to a
-list containing only 0 (= NO_INBAND_SECURITY).</p>
-
-<p>
+Inband-Security-Id defaults to the empty list, which is equivalent to a
+list containing only 0 (= NO_INBAND_SECURITY).
If 1 (= TLS) is specified then TLS is selected if the CER/CEA received
from the peer offers it.</p>
</item>
-<tag><c>{'Acct-Application-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Acct-Application-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Vendor-Specific-Application-Id', [Grouped()]}</c></tag>
-<item>
-<p>
-Values of Vendor-Specific-Application-Id AVPs sent in
-an outgoing capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Firmware-Revision', Unsigned32()}</c></tag>
-<item>
-<p>
-Value of the Firmware-Revision AVP sent in an outgoing capabilities
-exchange message.
-Optional.</p>
-</item>
+<tag><c>{'Acct-Application-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
+<tag><c>{'Vendor-Specific-Application-Id', [<seealso marker="diameter_dict#DATA_TYPES">Grouped()</seealso>]}</c></tag>
+<tag><c>{'Firmware-Revision', <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
</taglist>
@@ -441,14 +369,17 @@ eval(F) ->
</code>
<p>
-Applying an evaluable() <c>E</c> to an argument list <c>A</c>
+Applying an <c><seealso marker="#evaluable">evaluable()</seealso></c>
+<c>E</c> to an argument list <c>A</c>
is meant in the sense of <c>eval([E|A])</c>.</p>
+<warning>
<p>
-Beware of using local funs (that is, fun expressions not of the
-form <c>fun Module:Name/Arity</c>) in situations in which the fun is
-not short-lived and code is to be upgraded at runtime since any
-processes retaining such a fun will have a reference to old code.</p>
+Beware of using fun expressions of the form <c>fun Name/Arity</c> (not
+fun Mod:Name/Arity) in situations in which the fun is not short-lived
+and code is to be upgraded at runtime since any processes retaining
+such a fun will have a reference to old code.</p>
+</warning>
<marker id="peer_filter"/>
</item>
@@ -490,63 +421,76 @@ or any peer if the request does not contain
a <c>Destination-Realm</c> AVP.</p>
</item>
-<tag><c>{host, any|DiameterIdentity()}</c></tag>
+<tag><c>{host, any|<seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c></tag>
<item>
<p>
Matches only those peers whose <c>Origin-Host</c> has the
specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{realm, any|DiameterIdentity()</c></tag>
+<tag><c>{realm, any|<seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso></c></tag>
<item>
<p>
Matches only those peers whose <c>Origin-Realm</c> has the
-value, or all peers if the atom <c>any</c>.</p>
+specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{eval, evaluable()}</c></tag>
+<tag><c>{eval, <seealso marker="#evaluable">evaluable()</seealso>}</c></tag>
<item>
<p>
-Matches only those peers for which the specified evaluable() returns
+Matches only those peers for which the specified <c><seealso
+marker="#evaluable">evaluable()</seealso></c> returns
<c>true</c> on the connection's <c>diameter_caps</c> record.
Any other return value or exception is equivalent to <c>false</c>.</p>
</item>
-<tag><c>{neg, peer_filter()}</c></tag>
+<tag><c>{neg, <seealso marker="#peer_filter">peer_filter()</seealso>}</c></tag>
<item>
<p>
Matches only those peers not matched by the specified filter.</p>
</item>
-<tag><c>{all, [peer_filter()]}</c></tag>
+<tag><c>{all, [<seealso marker="#peer_filter">peer_filter()</seealso>]}</c></tag>
<item>
<p>
-Matches only those peers matched by each filter of the specified list.</p>
+Matches only those peers matched by each filter in the specified list.</p>
</item>
-<tag><c>{any, [peer_filter()]}</c></tag>
+<tag><c>{any, [<seealso marker="#peer_filter">peer_filter()</seealso>]}</c></tag>
<item>
<p>
-Matches only those peers matched by at least one filter of the
+Matches only those peers matched by at least one filter in the
specified list.</p>
</item>
</taglist>
<p>
-Note that the <c>host</c> and <c>realm</c> filters examine the
+An invalid filter is equivalent to <c>{any,[]}</c>, a filter
+that matches no peer.</p>
+
+<note>
+<p>
+The <c>host</c> and <c>realm</c> filters examine the
outgoing request as passed to <seealso marker="#call">call/4</seealso>,
-assuming that this is a record- or list-valued message() as documented
-in <seealso marker="diameter_app">diameter_app(3)</seealso>, and that
-the message contains at most one of each AVP.
-If this is not the case then the <c>{host|realm, DiameterIdentity()}</c>
+assuming that this is a record- or list-valued <c><seealso
+marker="diameter_app#message">diameter_app:message()</seealso></c>,
+and that the message contains at most one of each AVP.
+If this is not the case then the <c>{host|realm, <seealso
+marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c>
filters must be used to achieve the desired result.
-Note also that an empty host/realm (which should not be typical)
-is equivalent to an unspecified one for the purposes of filtering.</p>
+An empty <c><seealso
+marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso></c>
+(which should not be typical)
+matches all hosts/realms for the purposes of filtering.</p>
+</note>
+<warning>
<p>
-An invalid filter is equivalent to <c>{any, []}</c>, a filter
-that matches no peer.</p>
+A <c>host</c> filter is not typically desirable when setting
+Destination-Host since it will remove peer agents from the
+candidates list.</p>
+</warning>
<marker id="service_event"/>
</item>
@@ -554,12 +498,12 @@ that matches no peer.</p>
<tag><c>service_event() = #diameter_event{}</c></tag>
<item>
<p>
-Event message sent to processes that have subscribed using <seealso
-marker="#subscribe">subscribe/1</seealso>.</p>
+An event message sent to processes that have subscribed to these using
+<seealso marker="#subscribe">subscribe/1</seealso>.</p>
<p>
-The <c>info</c> field of the event record can be one of the
-following.</p>
+The <c>info</c> field of the event record can have one of the
+following types.</p>
<taglist>
@@ -568,27 +512,27 @@ following.</p>
<tag><c>{down, Ref, Peer, Config}</c></tag>
<item>
<code>
-Ref = transport_ref()
-Peer = diameter_app:peer()
-Config = {connect|listen, [transport_opt()]}
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+Peer = <seealso marker="diameter_app#peer">diameter_app:peer()</seealso>
+Config = {connect|listen, [<seealso marker="#transport_opt">transport_opt()</seealso>]}
Pkt = #diameter_packet{}
</code>
<p>
The RFC 3539 watchdog state machine has
-transitioned into (<c>up</c>) or out of (<c>down</c>) the open
+transitioned into (<c>up</c>) or out of (<c>down</c>) the OKAY
state.
-If a <c>diameter_packet</c> record is present in an <c>up</c> tuple
-then there has been an exchange of capabilities exchange messages and
-the record contains the received CER or CEA, otherwise the
-connection has reestablished without the loss or transport
+If a <c>#diameter_packet{}</c> record is present in an <c>up</c> event
+then there has been a capabilties exchange on a newly established
+transport connection and the record contains the received CER or CEA.
+Otherwise a connection has reestablished without the loss or
connectivity.</p>
<p>
-Note that a single up/down event for a given peer corresponds to
-as many <seealso marker="diameter_app#peer_up">peer_up/peer_down</seealso>
-callbacks as there are Diameter applications shared by the peer,
-as determined during capablilities exchange.
+Note that a single <c>up</c>/<c>down</c> event for a given peer
+corresponds to one <seealso marker="diameter_app#peer_up">peer_up/peer_down</seealso>
+callback for each of the Diameter applications negotiated during
+capablilities exchange.
That is, the event communicates connectivity with the
peer as a whole while the callbacks communicate connectivity with
respect to individual Diameter applications.</p>
@@ -597,26 +541,28 @@ respect to individual Diameter applications.</p>
<tag><c>{reconnect, Ref, Opts}</c></tag>
<item>
<code>
-Ref = transport_ref()
-Opts = [transport_opt()]
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+Opts = [<seealso marker="#transport_opt">transport_opt()</seealso>]
</code>
<p>
A connecting transport is attempting to establish/reestablish a
-transport connection with a peer following <c>reconnect_timer</c> or
-<c>watchdog_timer</c> expiry.</p>
+transport connection with a peer following <seealso
+marker="#reconnect_timer">reconnect_timer</seealso> or
+<seealso marker="#watchdog_timer">watchdog_timer</seealso>
+expiry.</p>
</item>
<tag><c>{closed, Ref, Reason, Config}</c></tag>
<item>
<code>
-Ref = transport_ref()
-Config = {connect|listen, [transport_opt()]}
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+Config = {connect|listen, [<seealso marker="#transport_opt">transport_opt()</seealso>]}
</code>
<p>
-Capabilities exchange has failed. <c>Reason</c> can be one of
-the following.</p>
+Capabilities exchange has failed.
+<c>Reason</c> can have one of the following types.</p>
<taglist>
@@ -627,17 +573,17 @@ Result = ResultCode | {capabilities_cb, CB, ResultCode|discard}
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
ResultCode = integer()
-CB = evaluable()
+CB = <seealso marker="#evaluable">evaluable()</seealso>
</code>
<p>
An incoming CER has been answered with the indicated result code or
discarded.
-The capabilities record contains pairs of values for the the local
-node and remote peer.
-The packet record contains the CER in question.
+<c>Caps</c> contains pairs of values for the the local node and remote
+peer.
+<c>Pkt</c> contains the CER in question.
In the case of rejection by a capabilities callback, the tuple
-indicates the rejecting callback.</p>
+contains the rejecting callback.</p>
</item>
<tag><c>{'CER', Caps, {ResultCode, Pkt}}</c></tag>
@@ -651,9 +597,8 @@ Pkt = #diameter_packet{}
<p>
An incoming CER contained errors and has been answered with the
indicated result code.
-The capabilities record contains only values for the the local
-node.
-The packet record contains the CER in question.</p>
+<c>Caps</c> contains only values for the the local node.
+<c>Pkt</c> contains the CER in question.</p>
</item>
<tag><c>{'CEA', Result, Caps, Pkt}</c></tag>
@@ -669,11 +614,11 @@ ResultCode = integer()
An incoming CEA has been rejected for the indicated reason.
An integer-valued <c>Result</c> indicates the result code sent
by the peer.
-The capabilities record contains pairs of values for the the local
-node and remote peer.
-The packet record contains the CEA in question.
+<c>Caps</c> contains pairs of values for the the local node and remote
+peer.
+<c>Pkt</c> contains the CEA in question.
In the case of rejection by a capabilities callback, the tuple
-indicates the rejecting callback.</p>
+contains the rejecting callback.</p>
</item>
<tag><c>{'CEA', Caps, Pkt}</c></tag>
@@ -684,14 +629,27 @@ Pkt = #diameter_packet{}
</code>
<p>
-An incoming CER contained errors and has been rejected.
-The capabilities record contains only values for the the local node.
-The packet record contains the CEA in question.</p>
+An incoming CEA contained errors and has been rejected.
+<c>Caps</c> contains only values for the the local node.
+<c>Pkt</c> contains the CEA in question.</p>
</item>
</taglist>
</item>
+<tag><c>{watchdog, Ref, PeerRef, {From, To}, Config}</c></tag>
+<item>
+<code>
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+PeerRef = <seealso marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso>
+From, To = initial | okay | suspect | down | reopen
+Config = {connect|listen, [transport_opt()]}
+</code>
+
+<p>
+An RFC 3539 watchdog state machine has changed state.</p>
+</item>
+
</taglist>
<p>
@@ -708,8 +666,8 @@ The name of a service as passed to <seealso
marker="#start_service">start_service/2</seealso> and with which the
service is identified.
There can be at most one service with a given name on a given node.
-Note that <c>erlang:make_ref/0</c> can be used to generate a service name
-that is somewhat unique.</p>
+Note that <seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso>
+can be used to generate a service name that is somewhat unique.</p>
<marker id="service_opt"/>
</item>
@@ -717,29 +675,28 @@ that is somewhat unique.</p>
<tag><c>service_opt()</c></tag>
<item>
<p>
-Options accepted by <seealso
+An option passed to <seealso
marker="#start_service">start_service/2</seealso>.
-Can be any <c>capability()</c> tuple as
+Can be any <c><seealso marker="#capability">capability()</seealso></c> as
well as the following.</p>
<taglist>
-<tag><c>{application, [application_opt()]}</c></tag>
+<tag><c>{application, [<seealso marker="#application_opt">application_opt()</seealso>]}</c></tag>
<item>
<p>
Defines a Diameter application supported by the service.</p>
<p>
-A service must define one application for each Diameter application it
-intends to support.
-For an outgoing Diameter request, the application is specified by
-passing the desired application's <c>application_alias()</c> to
-<seealso marker="#call">call/4</seealso>, while for an
+A service must configure one <c>application</c> for each Diameter
+application it intends to support.
+For an outgoing Diameter request, the relevant <c><seealso
+marker="#application_alias">application_alias()</seealso></c> is
+passed to <seealso marker="#call">call/4</seealso>, while for an
incoming request the application identifier in the message
-header determines the application (and callback module), the
-application identifier being specified in the <seealso
-marker="diameter_dict">dictionary</seealso> file defining the
-application.</p>
+header determines the application, the identifier being specified in
+the application's <seealso marker="diameter_dict">dictionary</seealso>
+file.</p>
</item>
</taglist>
@@ -750,8 +707,9 @@ application.</p>
<tag><c>transport_opt()</c></tag>
<item>
<p>
-Options accepted by <seealso
-marker="#add_transport">add_transport/2</seealso>.</p>
+An option passed to <seealso
+marker="#add_transport">add_transport/2</seealso>.
+Has one of the following types.</p>
<taglist>
<tag><c>{transport_module, atom()}</c></tag>
@@ -762,53 +720,77 @@ marker="diameter_transport">diameter_transport(3)</seealso>.
Defaults to <c>diameter_tcp</c> if unspecified.</p>
<p>
-The interface required of a transport module is documented in <seealso
-marker="diameter_transport">diameter_transport(3)</seealso>.</p>
+Multiple <c>transport_module</c> and <c>transport_config</c>
+options are allowed.
+The order of these is significant in this case (and only in this case),
+a <c>transport_module</c> being paired with the first
+<c>transport_config</c> following it in the options list, or the
+default value for trailing modules.
+Transport starts will be attempted with each of the
+modules in order until one establishes a connection within the
+corresponding timeout (see below) or all fail.</p>
</item>
<tag><c>{transport_config, term()}</c></tag>
+<tag><c>{transport_config, term(), <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
<item>
<p>
A term passed as the third argument to the <seealso
marker="diameter_transport#start">start/3</seealso> function of
the relevant <c>transport_module</c> in order to start a transport process.
Defaults to the empty list if unspecified.</p>
+
+<p>
+The 3-tuple form additionally specifies an interval, in milliseconds,
+after which a started transport process should be terminated if it has
+not yet established a connection.
+For example, the following options on a connecting transport
+request a connection with one peer over SCTP or another
+(typically the same) over TCP.</p>
+
+<code>
+{transport_module, diameter_sctp}
+{transport_config, SctpOpts, 5000}
+{transport_module, diameter_tcp}
+{transport_config, TcpOpts}
+</code>
+
+<p>
+To listen on both SCTP and TCP, define one transport for each.</p>
</item>
-<tag><c>{applications, [application_alias()]}</c></tag>
+<tag><c>{applications, [<seealso marker="#application_alias">application_alias()</seealso>]}</c></tag>
<item>
<p>
-The list of Diameter applications to which usage of the transport
-should be restricted.
-Defaults to all applications configured on the service
-in question.</p>
+The list of Diameter applications to which the transport should be
+restricted.
+Defaults to all applications configured on the service in question.
+Applications not configured on the service in question are ignored.</p>
</item>
-<tag><c>{capabilities, [capability()]}</c></tag>
+<tag><c>{capabilities, [<seealso marker="#capability">capability()</seealso>]}</c></tag>
<item>
<p>
AVP's used to construct outgoing CER/CEA messages.
-Any AVP specified takes precedence over a corresponding value specified
-for the service in question.</p>
+Values take precedence over any specified on the service in
+question.</p>
<p>
Specifying a capability as a transport option
-may be particularly appropriate for Inband-Security-Id in case
+may be particularly appropriate for Inband-Security-Id, in case
TLS is desired over TCP as implemented by
-<seealso marker="diameter_tcp">diameter_tcp(3)</seealso> but
-not over SCTP as implemented by
-<seealso marker="diameter_sctp">diameter_sctp(3)</seealso>.</p>
+<seealso marker="diameter_tcp">diameter_tcp(3)</seealso>.</p>
</item>
-<tag><c>{capabilities_cb, evaluable()}</c></tag>
+<tag><c>{capabilities_cb, <seealso marker="#evaluable">evaluable()</seealso>}</c></tag>
<item>
<p>
A callback invoked upon reception of CER/CEA during capabilities
exchange in order to ask whether or not the connection should
be accepted.
-Applied to the transport reference (as returned by <seealso
-marker="#add_transport">add_transport/2</seealso>) and
-<c>diameter_caps</c> record of the connection.
+Applied to the relevant <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c> and the
+<c>#diameter_caps{}</c> record of the connection.
Returning <c>ok</c> accepts the connection.
Returning <c>integer()</c> causes an incoming
CER to be answered with the specified Result-Code.
@@ -823,12 +805,14 @@ code causes the transport connection to be broken.</p>
Multiple <c>capabilities_cb</c> options can be specified, in which
case the corresponding callbacks are applied until either all return
<c>ok</c> or one does not.</p>
+
+<marker id="watchdog_timer"/>
</item>
<tag><c>{watchdog_timer, TwInit}</c></tag>
<item>
<code>
-TwInit = Unsigned32()
+TwInit = <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>
| {M,F,A}
</code>
@@ -844,17 +828,19 @@ the callback.</p>
<p>
An integer value must be at least 6000 as required by RFC 3539.
Defaults to 30000 if unspecified.</p>
+
+<marker id="reconnect_timer"/>
</item>
<tag><c>{reconnect_timer, Tc}</c></tag>
<item>
<code>
-Tc = Unsigned32()
+Tc = <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>
</code>
<p>
For a connecting transport, the RFC 3588 Tc timer, in milliseconds.
-Note that this timer determines the frequency with which the transport
+Note that this timer determines the frequency with which a transport
will attempt to establish a connection with its peer only <em>before</em>
an initial connection is established: once there is an initial
connection it's watchdog_timer that determines the frequency of
@@ -867,12 +853,13 @@ regarded as an initial connection rather than a reestablishment,
causing the RFC 3539 state machine to pass to state OPEN rather than
REOPEN.
Note that these semantics are not goverened by the RFC and
-that a listening transport's reconnect_timer should be greater than its
-peers's Tc plus jitter.</p>
+that a listening transport's <c>reconnect_timer</c> should be greater
+than its peer's Tw plus jitter.</p>
<p>
Defaults to 30000 for a connecting transport and 60000 for a listening
transport.</p>
+
</item>
</taglist>
@@ -884,6 +871,15 @@ marker="#service_info">service_info/2</seealso> and can be referred to
in predicate functions passed to <seealso
marker="#remove_transport">remove_transport/2</seealso>.</p>
+<marker id="transport_ref"/>
+</item>
+
+<tag><c>transport_ref() = reference()</c></tag>
+<item>
+<p>
+An reference returned by <seealso
+marker="#add_transport">add_transport/2</seealso> that
+identifies the configuration.</p>
</item>
</taglist>
@@ -896,13 +892,13 @@ marker="#remove_transport">remove_transport/2</seealso>.</p>
<!-- ===================================================================== -->
<func>
-<name>add_transport(SvcName, {connect|listen, Options})
+<name>add_transport(SvcName, {connect|listen, [Opt]})
-> {ok, Ref} | {error, Reason}</name>
<fsummary>Add transport capability to a service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Options = [transport_opt()]</v>
-<v>Ref = ref()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Opt = <seealso marker="#transport_opt">transport_opt()</seealso></v>
+<v>Ref = <seealso marker="#transport_ref">transport_ref()</seealso></v>
<v>Reason = term()</v>
</type>
<desc>
@@ -910,8 +906,8 @@ marker="#remove_transport">remove_transport/2</seealso>.</p>
Add transport capability to a service.</p>
<p>
-The service will start a transport process(es) in order to establish a
-connection with the peer, either by connecting to the peer
+The service will start transport processes as required in order to
+establish a connection with the peer, either by connecting to the peer
(<c>connect</c>) or by accepting incoming connection requests
(<c>listen</c>).
A connecting transport establishes transport connections with at most
@@ -926,16 +922,20 @@ marker="diameter_app#peer_up">peer_up/3</seealso> callback
after which the caller can exchange Diameter messages with the peer over
the transport.
In addition to CER/CEA, the service takes responsibility for the
-handling of DWR/DWA and required by RFC 3539 as well as for DPR/DPA.</p>
+handling of DWR/DWA and required by RFC 3539, as well as for DPR/DPA.</p>
<p>
The returned reference uniquely identifies the transport within the
scope of the service.
Note that the function returns before a transport connection has been
-established.
+established.</p>
+
+<note>
+<p>
It is not an error to add a transport to a service that has not yet
been configured: a service can be started after configuring
-transports.</p>
+its transports.</p>
+</note>
<marker id="call"/>
</desc>
@@ -944,48 +944,47 @@ transports.</p>
<!-- ===================================================================== -->
<func>
-<name>call(SvcName, App, Request, Options) -> ok | Answer | {error, Reason}</name>
+<name>call(SvcName, App, Request, [Opt]) -> Answer | ok | {error, Reason}</name>
<fsummary>Send a Diameter request message.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>App = application_alias()</v>
-<v>Request = diameter_app:message() | term()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>App = <seealso marker="#application_alias">application_alias()</seealso></v>
+<v>Request = <seealso marker="diameter_app#message">diameter_app:message()</seealso></v>
<v>Answer = term()</v>
-<v>Options = [call_opt()]</v>
+<v>Opt = <seealso marker="#call_opt">call_opt()</seealso></v>
</type>
<desc>
<p>
-Send a Diameter request message and possibly return the answer or error.</p>
+Send a Diameter request message.</p>
<p>
-<c>App</c> identifies the Diameter application in which the request is
+<c>App</c> specifies the Diameter application in which the request is
defined and callbacks to the corresponding callback module
will follow as described below and in <seealso
marker="diameter_app">diameter_app(3)</seealso>.
-Unless the <c>detach</c> option has been specified to cause an earlier
-return, the call returns either when an answer message is received
-from the peer or an error occurs.
-In the case of an answer, the return value is as returned by a
+Unless the <c>detach</c> option is specified, the call returns either
+when an answer message is received from the peer or an error occurs.
+In the answer case, the return value is as returned by a
<seealso
marker="diameter_app#handle_answer">handle_answer/4</seealso>
callback.
-In the case of an error, whether or not the error is returned directly
+In the error case, whether or not the error is returned directly
by diameter or from a <seealso
marker="diameter_app#handle_error">handle_error/4</seealso>
callback depends on whether or not the outgoing request is
-successfully encoded for transmission from the peer, the cases being
+successfully encoded for transmission to the peer, the cases being
documented below.</p>
<p>
If there are no suitable peers, or if
<seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
-rejects them by returning 'false', then <c>{error, no_connection}</c>
+rejects them by returning <c>false</c>, then <c>{error,no_connection}</c>
is returned.
Otherwise <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
is followed by a
<seealso
marker="diameter_app#prepare_request">prepare_request/3</seealso>
-callback, the message is encoded and sent.</p>
+callback, the message is encoded and then sent.</p>
<p>
There are several error cases which may prevent an
@@ -998,7 +997,7 @@ callback:</p>
<item>
<p>
If the initial encode of the outgoing request
-fails, then the request process fails and <c>{error, encode}</c>
+fails, then the request process fails and <c>{error,encode}</c>
is returned.</p>
</item>
@@ -1015,7 +1014,7 @@ callback takes place with <c>Reason = timeout</c>.</p>
If the request is successfully encoded and sent but the service in
question is stopped before an answer is received then a
<seealso marker="diameter_app#handle_error">handle_error/4</seealso>
-callback takes place <c>Reason = cancel</c>.</p>
+callback takes place with <c>Reason = cancel</c>.</p>
</item>
<item>
@@ -1042,7 +1041,7 @@ callback.</p>
<p>
If an encode error takes place during
retransmission then the request process fails and
-<c>{error, failure}</c> is returned.</p>
+<c>{error,failure}</c> is returned.</p>
</item>
<item>
@@ -1050,7 +1049,7 @@ retransmission then the request process fails and
If an application callback made in processing the request fails
(pick_peer, prepare_request, prepare_retransmit,
handle_answer or handle_error) then either
-<c>{error, encode}</c> or <c>{error, failure}</c>
+<c>{error,encode}</c> or <c>{error,failure}</c>
is returned depending on whether or not there has been an
attempt to send the request over the transport.</p>
</item>
@@ -1058,9 +1057,9 @@ attempt to send the request over the transport.</p>
</list>
<p>
-Note that <c>{error, encode}</c> is the only return value which
+Note that <c>{error,encode}</c> is the only return value which
guarantees that the request has <em>not</em> been sent over the
-transport.</p>
+transport connection.</p>
<marker id="origin_state_id"/>
</desc>
@@ -1069,7 +1068,7 @@ transport.</p>
<!-- ===================================================================== -->
<func>
-<name>origin_state_id() -> Unsigned32()</name>
+<name>origin_state_id() -> <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso></name>
<fsummary>Returns a reasonable Origin-State-Id.</fsummary>
<desc>
<p>
@@ -1078,7 +1077,7 @@ outgoing messages.</p>
<p>
The value returned is the number of seconds since 19680120T031408Z,
-the first value that can be encoded as a Diameter Time(),
+the first value that can be encoded as a Diameter <c><seealso marker="diameter_dict#DATA_TYPES">Time()</seealso></c>,
at the time the diameter application was started.</p>
<marker id="remove_transport"/>
@@ -1091,11 +1090,11 @@ at the time the diameter application was started.</p>
<name>remove_transport(SvcName, Pred) -> ok</name>
<fsummary>Remove previously added transports.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Pred = Fun | MFA | ref() | list() | true | false</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Pred = Fun | MFA | <seealso marker="#transport_ref">transport_ref()</seealso> | list() | true | false</v>
<v></v>
-<v>Fun = fun((reference(), connect|listen, list()) -> boolean())</v>
-<v>&nbsp;&nbsp;&nbsp; | fun((reference(), list()) -> boolean())</v>
+<v>Fun = fun((<seealso marker="#transport_ref">transport_ref()</seealso>, connect|listen, list()) -> boolean())</v>
+<v>&nbsp;&nbsp;&nbsp; | fun((<seealso marker="#transport_ref">transport_ref()</seealso>, list()) -> boolean())</v>
<v>&nbsp;&nbsp;&nbsp; | fun((list()) -> boolean())</v>
<v>MFA = {atom(), atom(), list()}</v>
</type>
@@ -1109,23 +1108,23 @@ An arity-3-valued <c>Pred</c> removes all transports for which
<c>Pred(Ref, Type, Opts)</c> returns <c>true</c>, where <c>Type</c> and
<c>Opts</c> are as passed to <seealso
marker="#add_transport">add_transport/2</seealso> and <c>Ref</c> is
-as returned by the corresponding call.
+as returned by it.
The remaining forms are equivalent to an arity-3 fun as follows.</p>
<code>
-Pred = fun(reference(), list()): fun(Ref, _, Opts) -> Pred(Ref, Opts) end
-Pred = fun(list()): fun(_, _, Opts) -> Pred(Opts) end
-Pred = reference(): fun(Ref, _, _) -> Pred == Ref end
-Pred = list(): fun(_, _, Opts) -> [] == Pred -- Opts end
-Pred = true: fun(_, _, _) -> true end
-Pred = false: fun(_, _, _) -> false end
+Pred = fun(transport_ref(), list()): fun(Ref, _, Opts) -> Pred(Ref, Opts) end
+Pred = fun(list()): fun(_, _, Opts) -> Pred(Opts) end
+Pred = transport_ref(): fun(Ref, _, _) -> Pred == Ref end
+Pred = list(): fun(_, _, Opts) -> [] == Pred -- Opts end
+Pred = true: fun(_, _, _) -> true end
+Pred = false: fun(_, _, _) -> false end
Pred = {M,F,A}: fun(Ref, Type, Opts) -> apply(M, F, [Ref, Type, Opts | A]) end
</code>
<p>
Removing a transport causes all associated transport connections to
be broken.
-A base application DPR message with
+A DPR message with
Disconnect-Cause <c>DO_NOT_WANT_TO_TALK_TO_YOU</c> will be sent
to each connected peer before disassociating the transport configuration
from the service and terminating the transport upon reception of
@@ -1140,15 +1139,293 @@ DPA or timeout.</p>
<!-- ===================================================================== -->
<func>
-<name>service_info(SvcName, Item) -> Value</name>
-<fsummary>Return specific information about a started service.</fsummary>
+<name>service_info(SvcName, Info) -> term()</name>
+<fsummary>Return information about a started service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Value = term()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Info = Item | [Info]</v>
+<v>Item = atom()</v>
</type>
<desc>
<p>
-Return information about a started service.</p>
+Return information about a started service.
+<c>Item</c> can be one of the following.</p>
+
+<taglist>
+
+<tag><c>'Origin-Host'</c></tag>
+<tag><c>'Origin-Realm'</c></tag>
+<tag><c>'Vendor-Id'</c></tag>
+<tag><c>'Product-Name'</c></tag>
+<tag><c>'Origin-State-Id'</c></tag>
+<tag><c>'Host-IP-Address'</c></tag>
+<tag><c>'Supported-Vendor'</c></tag>
+<tag><c>'Auth-Application-Id'</c></tag>
+<tag><c>'Inband-Security-Id'</c></tag>
+<tag><c>'Acct-Application-Id'</c></tag>
+<tag><c>'Vendor-Specific-Application-Id'</c></tag>
+<tag><c>'Firmware-Revision'</c></tag>
+<item>
+<p>
+Return a capability value as configured with <seealso
+marker="#start_service">start_service/2</seealso>.</p>
+</item>
+
+<tag><c>applications</c></tag>
+<item>
+<p>
+Return the list of applications as configured with <seealso
+marker="#start_service">start_service/2</seealso>.
+</p>
+</item>
+
+<tag><c>capabilities</c></tag>
+<item>
+<p>
+Return a tagged list of all capabilities values as configured with
+<seealso
+marker="#start_service">start_service/2</seealso>.</p>
+</item>
+
+<tag><c>transport</c></tag>
+<item>
+<p>
+Return a list containing one entry for each of the service's transport
+as configured with <seealso
+marker="#add_transport">add_transport/2</seealso>.
+Each entry is a tagged list containing both configuration and
+information about established peer connections.
+An example return value with for a client service with Origin-Host
+"client.example.com" configured with a single transport connected to
+"server.example.com" might look as follows.</p>
+
+<code>
+[[{ref,#Ref&lt;0.0.0.93>},
+ {type,connect},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{ip,{127,0,0,1}},
+ {raddr,{127,0,0,1}},
+ {rport,3868},
+ {reuseaddr,true}]}]},
+ {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
+ {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"client.example.com","server.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{0,193}},
+ {product_name,{"Client","Server"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[0]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.69.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},48758}},
+ {peer,{{127,0,0,1},3868}},
+ {statistics,[{recv_oct,656},
+ {recv_cnt,6},
+ {recv_max,148},
+ {recv_avg,109},
+ {recv_dvi,19},
+ {send_oct,836},
+ {send_cnt,6},
+ {send_max,184},
+ {send_avg,139},
+ {send_pend,0}]}]},
+ {statistics,[{{{0,258,0},recv},3},
+ {{{0,258,1},send},3},
+ {{{0,257,0},recv},1},
+ {{{0,257,1},send},1},
+ {{{0,258,0},recv,{'Result-Code',2001}},3},
+ {{{0,280,1},recv},2},
+ {{{0,280,0},send},2}]}]]
+</code>
+
+<p>
+Here <c>ref</c> is a <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c> and <c>options</c>
+the corresponding <c><seealso
+marker="#transport_opt">transport_opt()</seealso></c> list passed to <seealso
+marker="#add_transport">add_transport/2</seealso>.
+The <c>watchdog</c> entry shows the state of a connection's RFC 3539 watchdog
+state machine.
+The <c>peer</c> entry identifies the <c><seealso
+marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso></c> for
+which there will have been <seealso
+marker="diameter_app#peer_up">peer_up</seealso> callbacks for the
+Diameter applications identified by the <c>apps</c> entry,
+<c>common</c> being the <c><seealso
+marker="#application_alias">application_alias()</seealso></c>.
+The <c>caps</c> entry identifies the capabilities sent by the local
+node and received from the peer during capabilities exchange.
+The <c>port</c> entry displays socket-level information about the
+transport connection.
+The <c>statistics</c> entry presents Diameter-level counters,
+an entry like <c>{{{0,280,1},recv},2}</c> saying that the client has
+received 2 DWR messages: <c>{0,280,1} = {Application_Id, Command_Code,
+R_Flag}</c>.</p>
+
+<p>
+Note that <c>watchdog</c>, <c>peer</c>, <c>apps</c>, <c>caps</c>
+and <c>port</c> entries depend on connectivity
+with the peer and may not be present.
+Note also that the <c>statistics</c> entry presents values acuumulated
+during the lifetime of the transport configuration.</p>
+
+<p>
+A listening transport presents its information slightly differently
+since there may be multiple accepted connections for the same <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c>.
+The <c>transport</c> info returned by a server with a single client
+connection might look as follows.</p>
+
+<code>
+[[{ref,#Ref&lt;0.0.0.61>},
+ {type,listen},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]},
+ {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
+ {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"server.example.com","client.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{193,0}},
+ {product_name,{"Server","Client"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.62.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},3868}},
+ {peer,{{127,0,0,1},48758}},
+ {statistics,[{recv_oct,1576},
+ {recv_cnt,16},
+ {recv_max,184},
+ {recv_avg,98},
+ {recv_dvi,26},
+ {send_oct,1396},
+ {send_cnt,16},
+ {send_max,148},
+ {send_avg,87},
+ {send_pend,0}]}]}],
+ [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ {statistics,[{{{0,280,0},recv},7},
+ {{{0,280,1},send},7},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,258,1},recv},3},
+ {{{0,258,0},send},3},
+ {{{0,280,1},recv},5},
+ {{{0,280,0},send},5},
+ {{{0,257,1},recv},1},
+ {{{0,257,0},send},1}]}]]
+</code>
+
+<p>
+The information presented here is as in the <c>connect</c> case except
+that the client connections are grouped under an <c>accept</c> tuple.</p>
+
+</item>
+
+<tag><c>connections</c></tag>
+<item>
+<p>
+Return a list containing one entry for every established transport
+connection whose watchdog state machine is not in the <c>down</c>
+state.
+This is a flat view of <c>transport</c> info which lists only active
+connections and for which Diameter-level statistics are accumulated
+only for the lifetime of the transport connection.
+A return value for the server above might look as follows.</p>
+
+<code>
+[[{ref,#Ref&lt;0.0.0.61>},
+ {type,accept},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]},
+ {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
+ {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"server.example.com","client.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{193,0}},
+ {product_name,{"Server","Client"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.62.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},3868}},
+ {peer,{{127,0,0,1},48758}},
+ {statistics,[{recv_oct,10124},
+ {recv_cnt,132},
+ {recv_max,184},
+ {recv_avg,76},
+ {recv_dvi,9},
+ {send_oct,10016},
+ {send_cnt,132},
+ {send_max,148},
+ {send_avg,75},
+ {send_pend,0}]}]},
+ {statistics,[{{{0,280,0},recv},62},
+ {{{0,280,1},send},62},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,258,1},recv},3},
+ {{{0,258,0},send},3},
+ {{{0,280,1},recv},66},
+ {{{0,280,0},send},66},
+ {{{0,257,1},recv},1},
+ {{{0,257,0},send},1}]}]]
+</code>
+
+<p>
+Note that there may be multiple entries with the same <c>ref</c>, in
+contrast to <c>transport</c> info.</p>
+</item>
+
+<tag><c>statistics</c></tag>
+<item>
+<p>
+Return a <c>{{Counter, Ref}, non_neg_integer()}</c> list of counter values.
+<c>Ref</c> can be either a <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c>
+or a <c><seealso
+marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso></c>.
+Entries for the latter are folded into corresponding entries for the
+former as peer connections go down.
+Entries for both are removed at <seealso
+marker="#remove_transport">remove_transport/2</seealso>.
+The Diameter-level statistics returned by <c>transport</c> and
+<c>connections</c> info are based upon these entries.</p>
+</item>
+
+</taglist>
+
+<p>
+Requesting info for an unknown service causes <c>undefined</c> to be
+returned.
+Requesting a list of items causes a tagged list to be
+returned.</p>
<marker id="services"/>
</desc>
@@ -1160,7 +1437,7 @@ Return information about a started service.</p>
<name>services() -> [SvcName]</name>
<fsummary>Return the list of started services.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
</type>
<desc>
<p>
@@ -1173,10 +1450,10 @@ Return the list of started services.</p>
<!-- ===================================================================== -->
<func>
-<name>session_id(Ident) -> OctetString()</name>
+<name>session_id(Ident) -> <seealso marker="diameter_dict#DATA_TYPES">OctetString()</seealso></name>
<fsummary>Return a value for a Session-Id AVP.</fsummary>
<type>
-<v>Ident = DiameterIdentity()</v>
+<v>Ident = <seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso></v>
</type>
<desc>
<p>
@@ -1201,7 +1478,7 @@ Start the diameter application.</p>
<p>
The diameter application must be started before starting a service.
-In a production system this will typically be accomplished by a boot
+In a production system this is typically accomplished by a boot
file, not by calling <c>start/0</c> explicitly.</p>
<marker id="start_service"/>
@@ -1213,8 +1490,8 @@ file, not by calling <c>start/0</c> explicitly.</p>
<name>start_service(SvcName, Options) -> ok | {error, Reason}</name>
<fsummary>Start a Diameter service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Options = [service_opt()]</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Options = [<seealso marker="#service_opt">service_opt()</seealso>]</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -1222,11 +1499,19 @@ file, not by calling <c>start/0</c> explicitly.</p>
Start a diameter service.</p>
<p>
-A service defines a locally-implemented Diameter peer, specifying the
-capabilities of the peer to be used during capabilities exchange and
-the Diameter applications that it supports.
+A service defines a locally-implemented Diameter node, specifying the
+capabilities to be advertised during capabilities exchange.
Transports are added to a service using <seealso
-marker="#add_transport">add_transport/2</seealso>.</p>
+marker="#add_transport">add_transport/2</seealso>.
+</p>
+
+<note>
+<p>
+A transport can both override its service's
+capabilities and restrict its supported Diameter applications so
+"service = Diameter node as identified by Origin-Host" is not
+necessarily the case.</p>
+</note>
<marker id="stop_service"/>
</desc>
@@ -1252,13 +1537,26 @@ Stop the diameter application.</p>
<name>stop_service(SvcName) -> ok | {error, Reason}</name>
<fsummary>Stop a Diameter service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
<v>Reason = term()</v>
</type>
<desc>
<p>
Stop a diameter service.</p>
+<p>
+Stopping a service causes all associated transport connections to be
+broken.
+A DPR message with be sent as in the case of <seealso
+marker="#remove_transport">remove_transport/2</seealso>.</p>
+
+<note>
+<p>
+Stopping a transport does not remove any associated transports:
+<seealso marker="#remove_transport">remove_transport/2</seealso> must
+be called to remove transport configuration.</p>
+</note>
+
<marker id="subscribe"/>
</desc>
</func>
@@ -1269,15 +1567,19 @@ Stop a diameter service.</p>
<name>subscribe(SvcName) -> true</name>
<fsummary>Subscribe to event messages.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
</type>
<desc>
<p>
-Subscribe to <c>service_event()</c> messages from a service.</p>
+Subscribe to <c><seealso
+marker="#service_event">service_event()</seealso></c> messages from
+a service.</p>
<p>
It is not an error to subscribe to events from a service
-that does not yet exist.</p>
+that does not yet exist.
+Doing so before adding transports is required to guarantee the
+reception of all related events.</p>
<marker id="unsubscribe"/>
</desc>
@@ -1289,7 +1591,7 @@ that does not yet exist.</p>
<name>unsubscribe(SvcName) -> true</name>
<fsummary>Unsubscribe to event messages.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
</type>
<desc>
<p>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index a9ae0ebbec..4a4b212787 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -45,8 +45,8 @@ A diameter service as started by <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>
configures one of more Diameter applications, each of whose
configuration specifies a callback that handles messages specific to
-its application.
-The messages and AVPs of the Diameter application are defined in a
+the application.
+The messages and AVPs of the application are defined in a
dictionary file whose format is documented in
<seealso marker="diameter_dict">diameter_dict(4)</seealso>
while the callback module is documented here.
@@ -106,11 +106,13 @@ and, for the call-specific callbacks, any extra arguments passed to
<taglist>
+<marker id="capabilities"/>
+
<tag><c>capabilities() = #diameter_caps{}</c></tag>
<item>
<p>
A record containing the identities of
-the local and remote Diameter peers having an established transport
+the local Diameter node and the remote Diameter peer having an established transport
connection, as well as the capabilities as
determined by capabilities exchange.
Each field of the record is a 2-tuple consisting of
@@ -119,6 +121,8 @@ Optional or possibly multiple values are encoded as lists of values,
mandatory values as the bare value.</p>
</item>
+<marker id="message"/>
+
<tag><c>message() = record() | list()</c></tag>
<item>
<p>
@@ -136,8 +140,8 @@ list of <c>{FieldName, FieldValue}</c> pairs.</p>
<p>
A third representation allows a message to be specified as a list
-whose head is a <c>diameter_header</c> record and whose tail is a list
-of <c>diameter_avp</c> records.
+whose head is a <c>#diameter_header{}</c> record and whose tail is a list
+of <c>#diameter_avp{}</c> records.
This representation is used by diameter itself when relaying requests
as directed by the return value of a
<seealso marker="#handle_request">handle_request/3</seealso>
@@ -149,15 +153,18 @@ are sent exactly as specified.</p>
</item>
+<marker id="packet"/>
+
<tag><c>packet() = #diameter_packet{}</c></tag>
<item>
<p>
-A container for incoming and outgoing Diameters message that's passed
+A container for incoming and outgoing Diameter messages that's passed
through encode/decode and transport.
-Fields of a packet() record should not be set in return values except
-as documented.</p>
+Fields should not be set in return values except as documented.</p>
</item>
+<marker id="peer_ref"/>
+
<tag><c>peer_ref() = term()</c></tag>
<item>
<p>
@@ -165,20 +172,15 @@ A term identifying a transport connection with a Diameter peer.
Should be treated opaquely.</p>
</item>
-<tag><c>peer() = {peer_ref(), capabilities()}</c></tag>
+<marker id="peer"/>
+
+<tag><c>peer() = {<seealso marker="#peer_ref">peer_ref()</seealso>, <seealso marker="#capabilities">capabilities()</seealso>}</c></tag>
<item>
<p>
A tuple representing a Diameter peer connection.</p>
</item>
-<tag><c>service_name() = term()</c></tag>
-<item>
-<p>
-The service supporting the Diameter application.
-Specified to <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
-when starting the service.</p>
-</item>
+<marker id="state"/>
<tag><c>state() = term()</c></tag>
<item>
@@ -211,9 +213,9 @@ process.</p>
<name>Mod:peer_up(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been established</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>State = NewState = state()</v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>State = NewState = <seealso marker="#state">state()</seealso></v>
</type>
<desc>
<p>
@@ -230,9 +232,9 @@ the callback module in question has been configured.</p>
<name>Mod:peer_down(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been lost.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>State = NewState = state()</v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>State = NewState = <seealso marker="#state">state()</seealso></v>
</type>
<desc>
<p>
@@ -248,21 +250,17 @@ call to <seealso marker="#peer_up">peer_up/3</seealso>.</p>
-> {ok, Peer} | {Peer, NewState} | false</name>
<fsummary>Select a target peer for an outgoing request.</fsummary>
<type>
-<v>Candidates = [peer()]</v>
-<v>Peer = peer() | false</v>
-<v>SvcName = service_name()</v>
-<v>State = NewState = state()</v>
+<v>Candidates = [<seealso marker="#peer">peer()</seealso>]</v>
+<v>Peer = <seealso marker="#peer">peer()</seealso> | false</v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>State = NewState = <seealso marker="#state">state()</seealso></v>
</type>
<desc>
<p>
Invoked as a consequence of a call to <seealso
marker="diameter#call">diameter:call/4</seealso> to select a destination
-peer for an outgoing request, the return value indicating the selected peer.
-A new application state can also be returned but only if the Diameter
-application in question was
-configured with the option <c>call_mutates_state</c> set to
-<c>true</c>, as documented for <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
+peer for an outgoing request, the return value indicating the selected
+peer.</p>
<p>
The candidate peers list will only include those
@@ -282,7 +280,8 @@ equivalent when callback state is mutable, as are
<c>{ok, Peer}</c> and <c>{Peer, State}</c>.
Returning a peer as <c>false</c> causes <c>{error, no_connection}</c>
to be returned from <seealso marker="diameter#call">diameter:call/4</seealso>.
-Returning a peer() from an initial pick_peer/4 callback will result in a
+Returning a <seealso marker="#peer">peer()</seealso> from an initial
+pick_peer/4 callback will result in a
<seealso marker="#prepare_request">prepare_request/3</seealso> callback
followed by either <seealso
marker="#handle_answer">handle_answer/4</seealso>
@@ -301,6 +300,19 @@ marker="#pick_peer">pick_peer/4</seealso> will be, since a
retransmission to an alternate peer is abandoned if an answer is
received from a previously selected peer.</p>
+<note>
+<p>
+<c>{Peer, NewState}</c> and its equivalents can only be returned if
+the Diameter application in question was
+configured with the <seealso
+marker="diameter#application_opt">diameter:application_opt()</seealso>
+<c>{call_mutates_state, true}</c>.
+Otherwise, the <c>State</c> argument is always
+the intial value as configured on the application, not any subsequent
+value returned by a <seealso marker="#peer_up">peer_up/3</seealso>
+or <seealso marker="#peer_down">peer_down/3</seealso> callback.</p>
+</note>
+
<marker id="prepare_request"/>
</desc>
@@ -310,10 +322,10 @@ received from a previously selected peer.</p>
<name>Mod:prepare_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Return a request for encoding and transport.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>Action = {send, packet() | message()} | {discard, Reason} | discard</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>Action = {send, <seealso marker="#packet">packet()</seealso> | <seealso marker="#message">message()</seealso>} | {discard, Reason} | discard</v>
</type>
<desc>
<p>
@@ -325,14 +337,14 @@ callback need not be limited to this usage.
Many implementations may simply want to return <c>{send, Packet}</c></p>
<p>
-A returned packet() should set the request to be encoded in its
+A returned <seealso marker="#packet">packet()</seealso> should set the request to be encoded in its
<c>msg</c> field and can set the <c>transport_data</c> field in order
to pass information to the transport module.
Extra arguments passed to <seealso
marker="diameter#call">diameter:call/4</seealso> can be used to
communicate transport data to the callback.
-A returned packet() can also set the <c>header</c> field to a
-<c>diameter_header</c> record in order to specify values that should
+A returned <seealso marker="#packet">packet()</seealso> can also set the <c>header</c> field to a
+<c>#diameter_header{}</c> record in order to specify values that should
be preserved in the outgoing request, although this should typically
not be necessary and allows the callback to set header values
inappropriately.
@@ -355,10 +367,10 @@ discarded}</c>.</p>
<name>Mod:prepare_retransmit(Packet, SvcName, Peer) -> Result</name>
<fsummary>Return a request for encoding and retransmission.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>Result = {send, packet() | message()} | {discard, Reason} | discard</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>Result = {send, <seealso marker="#packet">packet()</seealso> | <seealso marker="#message">message()</seealso>} | {discard, Reason} | discard</v>
</type>
<desc>
<p>
@@ -366,7 +378,7 @@ Invoked to return a request for encoding and retransmission.
Has the same role as <seealso
marker="#prepare_request">prepare_request/3</seealso> in the case that
a peer connection is lost an an alternate peer selected but the
-argument packet() is as returned by the initial
+argument <seealso marker="#packet">packet()</seealso> is as returned by the initial
<c>prepare_request/3</c>.</p>
<p>
@@ -385,10 +397,10 @@ discarded}</c>.</p>
<name>Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result</name>
<fsummary>Receive an answer message from a peer.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>Request = message()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
+<v>Request = <seealso marker="#message">message()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
<v>Result = term()</v>
</type>
<desc>
@@ -401,7 +413,7 @@ specified.</p>
<p>
The decoded answer record is in the <c>msg</c> field of the argument
-packet(),
+<seealso marker="#packet">packet()</seealso>,
the undecoded binary in the <c>packet</c> field.
<c>Request</c> is the outgoing request message as was returned from
<seealso marker="#prepare_request">prepare_request/3</seealso> or
@@ -438,9 +450,9 @@ marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
<fsummary>Return an error from a outgoing request.</fsummary>
<type>
<v>Reason = timeout | failover | term()</v>
-<v>Request = message()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
+<v>Request = <seealso marker="#message">message()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
<v>Result = term()</v>
</type>
<desc>
@@ -469,14 +481,14 @@ callback returned false.</p>
<name>Mod:handle_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Receive an incoming request.</fsummary>
<type>
-<v>Packet = packet()</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
<v>SvcName = term()</v>
-<v>Peer = peer()</v>
-<v>Action = Reply | {relay, Opts} | discard | {eval, Action, PostF}</v>
-<v>Reply = {reply, message()}
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>Action = Reply | {relay, [Opt]} | discard | {eval, Action, PostF}</v>
+<v>Reply = {reply, <seealso marker="#message">message()</seealso>}
| {protocol_error, 3000..3999}</v>
-<v>Opts = diameter:call_opts()</v>
-<v>PostF = diameter:evaluable()</v>
+<v>Opt = <seealso marker="diameter#call_opt">diameter:call_opt()</seealso></v>
+<v>PostF = <seealso marker="diameter#evaluable">diameter:evaluable()</seealso></v>
</type>
<desc>
<p>
@@ -492,13 +504,13 @@ itself as defining either the application in question or the Relay
application.</p>
<p>
-The argument packet() has the following signature.</p>
+The argument <seealso marker="#packet">packet()</seealso> has the following signature.</p>
<code>
#diameter_packet{header = #diameter_header{},
avps = [#diameter_avp{}],
msg = record() | undefined,
- errors = ['Unsigned32'() | {'Unsigned32'(), #diameter_avp{}}],
+ errors = [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso> | {<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>, #diameter_avp{}}],
bin = binary(),
transport_data = term()}
</code>
@@ -515,8 +527,8 @@ The <c>errors</c> field specifies any Result-Code's identifying errors
that were encountered in decoding the request.
In this case diameter will set both Result-Code and
Failed-AVP AVP's in a returned
-answer message() before sending it to the peer:
-the returned message() need only set any other required AVP's.
+answer <seealso marker="#message">message()</seealso> before sending it to the peer:
+the returned <seealso marker="#message">message()</seealso> need only set any other required AVP's.
Note that the errors detected by diameter are all of the 5xxx series
(Permanent Failures).
The <c>errors</c> list is empty if the request has been received in
@@ -526,7 +538,7 @@ the relay application.</p>
The <c>transport_data</c> field contains an arbitrary term passed into
diameter from the transport module in question, or the atom
<c>undefined</c> if the transport specified no data.
-The term is preserved in the packet() containing any answer message
+The term is preserved in the <seealso marker="#packet">packet()</seealso> containing any answer message
sent back to the transport process unless another value is explicitly
specified.</p>
@@ -535,7 +547,7 @@ The semantics of each of the possible return values are as follows.</p>
<taglist>
-<tag><c>{reply, message()}</c></tag>
+<tag><c>{reply, <seealso marker="#message">message()</seealso>}</c></tag>
<item>
<p>
Send the specified answer message to the peer.</p>
@@ -579,7 +591,7 @@ header of the relayed request.</p>
The returned <c>Opts</c> should not specify <c>detach</c>.
A subsequent <seealso marker="#handle_answer">handle_answer/4</seealso>
callback for the relayed request must return its first
-argument, the <c>diameter_packet</c> record containing the answer
+argument, the <c>#diameter_packet{}</c> record containing the answer
message.
Note that the <c>extra</c> option can be specified to supply arguments
that can distinguish the relay case from others if so desired.
diff --git a/lib/diameter/doc/src/diameter_compile.xml b/lib/diameter/doc/src/diameter_compile.xml
index 60e08d41da..7a6ca48798 100644
--- a/lib/diameter/doc/src/diameter_compile.xml
+++ b/lib/diameter/doc/src/diameter_compile.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,7 @@ supplied.
<description>
<p>
-The diameterc utility is used to transform diameter
+The diameterc utility is used to compile diameter
<seealso marker="diameter_dict">dictionary files</seealso>
into Erlang source.
The resulting source implements the interface diameter requires
@@ -47,7 +47,7 @@ to encode and decode the dictionary's messages and AVP's.</p>
<tag><![CDATA[diameterc [<options>] <file>]]></tag>
<item>
<p>
-Transforms a single dictionary file. Valid options are as follows.</p>
+Compiles a single dictionary file. Valid options are as follows.</p>
<!-- Leave -h/d/v undocumented, except for the usage message from the
utility itself. -->
@@ -64,47 +64,36 @@ Defaults to the current working directory.</p>
<item>
<p>
Specifies a directory to add to the code path.
-Use to point at beam files corresponding to dictionaries
-inherited by the one being compiled using <c>@inherits</c> or
-<c>--inherits</c>.
-Inheritance is a beam dependency, not an erl/hrl dependency.</p>
+Use to point at beam files compiled from inherited dictionaries,
+<c>@inherits</c> in a dictionary file creating a beam dependency, not
+an erl/hrl dependency.</p>
<p>
Multiple <c>-i</c> options can be specified.</p>
</item>
<tag><![CDATA[-E]]></tag>
-<item>
-<p>
-Supresses erl generation.</p>
-</item>
-
<tag><![CDATA[-H]]></tag>
<item>
<p>
-Supresses hrl generation.</p>
+Supresses erl and hrl generation, respectively.</p>
</item>
<tag><![CDATA[--name <name>]]></tag>
-<item>
-<p>
-Set <c>@name</c> in the dictionary file.
-Overrides any setting in the file itself.</p>
-</item>
-
<tag><![CDATA[--prefix <prefix>]]></tag>
<item>
<p>
-Set <c>@prefix</c> in the dictionary file.
+Set <c>@name</c> and <c>@prefix</c> in the dictionary,
+respectively.
Overrides any setting in the file itself.</p>
</item>
<tag><![CDATA[--inherits <dict>]]></tag>
<item>
<p>
-Append an <c>@inherits</c> to the dictionary file.
-Specifying <c>'-'</c> as the dictionary has the effect of clearing
-any previous inherits, effectively ignoring previous inherits.</p>
+Append an <c>@inherits</c> to the dictionary before compiling.
+Specifying <c>'-'</c> as the dictionary has the effect of clearing any
+previous inherits, causing them to be ignored.</p>
<p>
Multiple <c>--inherits</c> options can be specified.</p>
@@ -130,16 +119,6 @@ Returns 0 on success, non-zero on failure.</p>
<!-- ===================================================================== -->
<section>
-<title>BUGS</title>
-
-<p>
-The identification of errors in the source file is poor.</p>
-
-</section>
-
-<!-- ===================================================================== -->
-
-<section>
<title>SEE ALSO</title>
<p>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index cc638dbc18..26d6ad4e56 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -275,10 +275,12 @@ Location-Information 350 Grouped MV
Requested-Information 353 Enumerated V
</code>
+<warning>
<p>
-Note that the P flag has been deprecated by the Diameter Maintenance
-and Extensions Working Group of the IETF: diameter will set the P flag
-to 0 as mandated by the current draft standard.</p>
+The P flag has been deprecated by the Diameter Maintenance
+and Extensions Working Group of the IETF and should be omitted
+to conform to the current draft standard.</p>
+</warning>
</item>
diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml
index 966d1f1eee..1fd7755695 100644
--- a/lib/diameter/doc/src/diameter_examples.xml
+++ b/lib/diameter/doc/src/diameter_examples.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -37,7 +37,6 @@ under the License.
<!-- ===================================================================== -->
<p>
-To be written.
Example code can be found in the diameter application's
<c>examples</c> subdirectory.</p>
diff --git a/lib/diameter/doc/src/diameter_intro.xml b/lib/diameter/doc/src/diameter_intro.xml
index ef08002a8b..bc2afbd453 100644
--- a/lib/diameter/doc/src/diameter_intro.xml
+++ b/lib/diameter/doc/src/diameter_intro.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -44,11 +44,11 @@ agent.
This chapter provides a short overview of the application.</p>
<p>
-A Diameter peer is implemented by configuring a <em>service</em> and
+A Diameter node is implemented by configuring a <em>service</em> and
one or more <em>transports</em> using the interface module
<seealso marker="diameter">diameter</seealso>.
The service configuration defines the Diameter applications to be
-supported by the peer and, typically, the capabilities that it should
+supported by the node and, typically, the capabilities that it should
send to remote peers at capabilities exchange upon the establishment
of transport connections.
A transport is configured on a service and provides protocol-specific
@@ -57,16 +57,17 @@ diameter and implemented by a transport module.
The diameter application provides two transport modules: <seealso
marker="diameter_tcp">diameter_tcp</seealso> and <seealso
marker="diameter_sctp">diameter_sctp</seealso> for transport over TCP
-(using <c>gen_tcp</c>) and SCTP (using <c>gen_sctp</c>) respectively.
+(using <seealso marker="kernel:gen_tcp">gen_tcp</seealso>) and SCTP
+(using <seealso marker="kernel:gen_sctp">gen_sctp</seealso>) respectively.
Other transports can be provided by any module that implements
diameter's <seealso marker="diameter_transport">transport
interface</seealso>.</p>
<p>
-While a service typically implements a single Diameter peer (as
+While a service typically implements a single Diameter node (as
identified by an Origin-Host AVP), transports can themselves be
-associated with capabilities AVP's so that a single service be used to
-implement more than one Diameter peer.</p>
+associated with capabilities AVP's so that a single service can be
+used to implement more than one Diameter node.</p>
<p>
Each Diameter application defined on a service is configured with a
@@ -76,19 +77,19 @@ diameter communicates the connectivity of remote peers, requests peer
selection for outgoing requests, and communicates the reception of
incoming Diameter request and answer messages.
An application using diameter implements these application callback
-modules to provide the functionality of the Diameter peer(s) it
+modules to provide the functionality of the Diameter node(s) it
implements.</p>
<p>
-Each Diameter application is also configured with one or more
-dictionary modules
+Each Diameter application is also configured with a
+dictionary module
that provide encode/decode functionality for outgoing/incoming
-Diameter messages.
-A module is generated from a <seealso
+Diameter messages belonging to the application.
+A dictionary module is generated from a <seealso
marker="diameter_dict">specification file</seealso> using the <seealso
marker="diameterc">diameterc</seealso> utility.
Dictionaries for the RFC 3588 Diameter Common Messages, Base
-Accounting and Relay applications are provided by the diameter
+Accounting and Relay applications are provided with the diameter
application.</p>
</chapter>
diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml
index c1e839b8e1..955169349c 100644
--- a/lib/diameter/doc/src/diameter_sctp.xml
+++ b/lib/diameter/doc/src/diameter_sctp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -58,11 +58,11 @@ and implements the behaviour documented in
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = <seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso></v>
<v>Svc = #diameter_service{}</v>
-<v>Opt = {raddr, ip_address()} | {rport, integer()} | term()</v>
+<v>Opt = {raddr, <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>} | {rport, integer()} | term()</v>
<v>Pid = pid()</v>
-<v>LAddr = ip_address()</v>
+<v>LAddr = <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso></v>
<v>Reason = term()</v>
</type>
<desc>
@@ -75,12 +75,13 @@ marker="diameter_transport#start">diameter_transport(3)</seealso>.</p>
The only diameter_sctp-specific argument is the options list.
Options <c>raddr</c> and <c>rport</c> specify the remote address
and port for a connecting transport and not valid for a listening
-transport.
-The former is required while latter defaults to 3868 if unspecified.
+transport: the former is required while latter defaults to 3868 if
+unspecified.
More than one <c>raddr</c> option can be specified, in which case the
connecting transport in question attempts each in sequence until
an association is established.
-Remaining options are any accepted by gen_sctp:open/1, with the exception
+Remaining options are any accepted by <seealso
+marker="kernel:gen_sctp#open-1">gen_sctp:open/1</seealso>, with the exception
of options <c>mode</c>, <c>binary</c>, <c>list</c>, <c>active</c>
and <c>sctp_events</c>.
Note that options <c>ip</c> and <c>port</c> specify the local address
@@ -88,30 +89,45 @@ and port respectively.</p>
<p>
Multiple <c>ip</c> options can be specified for a multihomed peer.
-If none are specified then the values of Host-IP-Address
-on the service are used. (In particular, one of these must be specified.)
+If none are specified then the values of <c>Host-IP-Address</c>
+in the <c>#diameter_service{}</c> record are used.
+(In particular, one of these must be specified.)
Option <c>port</c> defaults to 3868 for a listening transport and 0 for a
connecting transport.</p>
+<warning>
+<p>
+An insufficiently large receive buffer may result in a peer having to
+resend incoming messages: set the <seealso
+marker="kernel:inet">inet(3)</seealso> option <c>recbuf</c> to increase
+the buffer size.</p>
+
+<p>
+An insufficiently large send buffer may result in outgoing messages
+being discarded: set the <seealso
+marker="kernel:inet">inet(3)</seealso> option <c>sndbuf</c> to increase
+the buffer size.</p>
+</warning>
+
<p>
diameter_sctp uses the <c>transport_data</c> field of
-the <c>diameter_packet</c> record to communicate the stream on which an
+the <c>#diameter_packet{}</c> record to communicate the stream on which an
inbound message has been received, or on which an outbound message
should be sent: the value will be of the form <c>{stream, Id}</c>
on an inbound message passed to a <seealso
marker="diameter_app#handle_request">handle_request</seealso> or <seealso
marker="diameter_app#handle_answer">handle_answer</seealso> callback.
-For an outbound message, either <c>undefined</c> (explicitly of
-by specifying the outbound message as a <c>binary()</c>) or a tuple
+For an outbound message, either <c>undefined</c> (explicitly or
+by receiving the outbound message as a <c>binary()</c>) or a tuple
should be set in the return value of <seealso
marker="diameter_app#handle_request">handle_request</seealso>
(typically by retaining the value passed into this function)
or <seealso
marker="diameter_app#prepare_request">prepare_request</seealso>.
-The value <c>undefined</c> uses a "next outbound stream" id and then
-increments this modulo the total number outbound streams. That
-is, successive values of <c>undefined</c> cycle through all outbound
-streams.</p>
+The value <c>undefined</c> uses a "next outbound stream" id and
+increments this modulo the total number outbound streams.
+That is, successive values of <c>undefined</c> cycle through all
+outbound streams.</p>
<!-- TODO: Some way of getting at the number of available outbound -->
<!-- streams. -->
@@ -128,7 +144,9 @@ streams.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter_transport">diameter_transport(3)</seealso></p>
+<seealso marker="diameter_transport">diameter_transport(3)</seealso>,
+<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>,
+<seealso marker="kernel:inet">inet(3)</seealso></p>
</section>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index e6b53383c0..3ffcebfd90 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,8 +38,9 @@ under the License.
<description>
<p>
-This module implements diameter transport over TCP using gen_tcp.
-It can be specified as the value of a transport_module option to
+This module implements diameter transport over TCP using <seealso
+marker="kernel:gen_tcp">gen_tcp</seealso>.
+It can be specified as the value of a <c>transport_module</c> option to
<seealso
marker="diameter#add_transport">diameter:add_transport/2</seealso>
and implements the behaviour documented in
@@ -65,17 +66,17 @@ before configuring TLS capability on diameter transports.</p>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = <seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso></v>
<v>Svc = #diameter_service{}</v>
-<v>Opt = OwnOpt | SslOpt | OtherOpt</v>
+<v>Opt = OwnOpt | SslOpt | TcpOpt</v>
<v>Pid = pid()</v>
-<v>LAddr = ip_address()</v>
+<v>LAddr = <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso></v>
<v>Reason = term()</v>
-<v>OwnOpt = {raddr, ip_address()}
+<v>OwnOpt = {raddr, <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>}
| {rport, integer()}
| {port, integer()}</v>
<v>SslOpt = {ssl_options, true | list()}</v>
-<v>OtherOpt = term()</v>
+<v>TcpOpt = term()</v>
</type>
<desc>
@@ -89,25 +90,32 @@ Options <c>raddr</c> and <c>rport</c> specify the remote address
and port for a connecting transport and are not valid for a listening
transport.
Option <c>ssl_options</c> must be specified for a transport
-that must be able to support TLS: a value of <c>true</c> results in a
+that should support TLS: a value of <c>true</c> results in a
TLS handshake immediately upon connection establishment while
-list() specifies options to be passed to ssl:connect/2 of ssl:ssl_accept/2
+<c>list()</c> specifies options to be passed to <seealso
+marker="ssl:ssl#connect-2">ssl:connect/2</seealso> or
+<seealso marker="ssl:ssl#ssl_accept-2">ssl:ssl_accept/2</seealso>
after capabilities exchange if TLS is negotiated.
-Remaining options are any accepted by ssl:connect/3 or gen_tcp:connect/3 for
-a connecting transport, or ssl:listen/3 or gen_tcp:listen/2 for
+Remaining options are any accepted by <seealso
+marker="ssl:ssl#connect-3">ssl:connect/3</seealso> or <seealso
+marker="kernel:gen_tcp#connect-3">gen_tcp:connect/3</seealso> for
+a connecting transport, or <seealso
+marker="ssl:ssl#listen-2">ssl:listen/2</seealso> or <seealso
+marker="kernel:gen_tcp#listen-2">gen_tcp:listen/2</seealso> for
a listening transport, depending on whether or not <c>{ssl_options, true}</c>
has been specified.
-Options <c>binary</c>, <c>packet</c> and <c>active</c> cannot be specified.
+Options <c>binary</c>,
+<c>packet</c> and <c>active</c> cannot be specified.
Also, option <c>port</c> can be specified for a listening transport
to specify the local listening port, the default being the standardized
3868 if unspecified.
-Note that option <c>ip</c> specifies the local address.</p>
+Note that the option <c>ip</c> specifies the local address.</p>
<p>
An <c>ssl_options</c> list must be specified if and only if
-the transport in question has specified an Inband-Security-Id
-AVP with value TLS on the relevant call to
-<seealso
+the transport in question has set <c>Inband-Security-Id</c> to
+1 (<c>TLS</c>), as
+specified to either <seealso
marker="diameter#start_service">start_service/2</seealso> or
<seealso
marker="diameter#add_transport">add_transport/2</seealso>,
@@ -122,9 +130,9 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<p>
-If the service specifies more than one Host-IP-Address and
-option <c>ip</c> is unspecified then then the
-first of the service's addresses is used as the local address.</p>
+If the <c>#diameter_service{}</c> record has more than one
+<c>Host-IP-Address</c> and option <c>ip</c> is unspecified then the
+first of the these addresses is used as the local address.</p>
<p>
The returned local address list has length one.</p>
@@ -142,7 +150,10 @@ The returned local address list has length one.</p>
<p>
<seealso marker="diameter">diameter(3)</seealso>,
-<seealso marker="diameter_transport">diameter_transport(3)</seealso></p>
+<seealso marker="diameter_transport">diameter_transport(3)</seealso>,
+<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>,
+<seealso marker="kernel:inet">inet(3)</seealso>,
+<seealso marker="ssl:ssl">ssl(3)</seealso></p>
</section>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 087a90b099..1bd45d9ce8 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -59,11 +59,11 @@ parent).</p>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = <seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso></v>
<v>Svc = #diameter_service{}</v>
<v>Opts = term()</v>
<v>Pid = pid()</v>
-<v>LAddrs = [ip_address()]</v>
+<v>LAddrs = [<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>]</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -93,16 +93,19 @@ It should not link to the parent.
It should exit if its transport connection with its peer is lost.</p>
<p>
-Transport processes for a given service are started sequentially.</p>
-
-<p>
-The start function should use the 'Host-IP-Address' list on the service,
-namely <c>Svc#diameter_service.host_ip_address</c>, and/or
+The capabilities in the <c>#diameter_service{}</c> record are as
+passed to <seealso
+marker="diameter#start_service">diameter:start_service/2</seealso> and
+<seealso
+marker="diameter#add_transport">diameter:add_transport/2</seealso>,
+values passed to the latter overriding those passed to the former.
+The start function should use the <c>Host-IP-Address</c> list and/or
<c>Opts</c> to select an appropriate list of local IP addresses,
-and should return this list if different from the service addresses.
-The returned list is used to populate 'Host-IP-Address' AVPs in outgoing
-capabilities exchange messages, the service addresses being used
-otherwise.</p>
+and should return this list if different from the
+<c>#diameter_service{}</c> addresses.
+The returned list is used to populate <c>Host-IP-Address</c> AVPs in
+outgoing capabilities exchange messages, the
+<c>#diameter_service{}</c> addresses being used otherwise.</p>
<marker id="MESSAGES"/>
</desc>
@@ -129,9 +132,10 @@ diameter.</p>
<item>
<p>
An outbound Diameter message.
-Packet can be either <c>binary()</c> (the message to be sent)
-or a <c>#diameter_packet{}</c> whose <c>transport_data</c> field
-containes a value other than undefined.</p>
+<c>Packet</c> can be either binary() (the message to be sent)
+or a <c>#diameter_packet{}</c> record whose <c>transport_data</c>
+field contains a value other than undefined and whose <c>bin</c> field
+contains the binary to send.</p>
</item>
<tag><c>{diameter, {close, Pid}}</c></tag>
@@ -140,7 +144,7 @@ containes a value other than undefined.</p>
A request to close the transport connection.
The transport process should terminate after closing the
connection.
-Pid is the pid() of the parent process.</p>
+<c>Pid</c> is the pid() of the parent process.</p>
</item>
<tag><c>{diameter, {tls, Ref, Type, Bool}}</c></tag>
@@ -148,17 +152,17 @@ Pid is the pid() of the parent process.</p>
<p>
Indication of whether or not capabilities exchange has selected
inband security using TLS.
-Ref is a reference() that must be included in the
+<c>Ref</c> is a reference() that must be included in the
<c>{diameter, {tls, Ref}}</c> reply message to the transport's
parent process (see below).
-Type is either <c>connect</c> or <c>accept</c> depending on
+<c>Type</c> is either <c>connect</c> or <c>accept</c> depending on
whether the process has been started for a connecting or listening
transport respectively.
-Bool is a boolean() indicating whether or not the transport connection
-should be upgraded to TLS.</p>
+<c>Bool</c> is a boolean() indicating whether or not the transport
+connection should be upgraded to TLS.</p>
<p>
-If TLS is requested (Bool = true) then a connecting process should
+If TLS is requested (<c>Bool=true</c>) then a connecting process should
initiate a TLS handshake with the peer and an accepting process should
prepare to accept a handshake.
A successful handshake should be followed by a <c>{diameter, {tls, Ref}}</c>
@@ -182,18 +186,18 @@ to its parent.</p>
<tag><c>{diameter, {self(), connected}}</c></tag>
<item>
<p>
-Inform the parent that the transport process with Type = accept has
+Inform the parent that the transport process with <c>Type=accept</c> has
established a connection with the peer.
-Not sent if the transport process has Type = connect.</p>
+Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
<item>
<p>
-Inform the parent that the transport process with Type = connect
+Inform the parent that the transport process with <c>Type=connect</c>
has established a connection with a peer.
-Not sent if the transport process has Type = accept.
-Remote is an arbitrary term that uniquely identifies the remote
+Not sent if the transport process has <c>Type=accept</c>.
+<c>Remote</c> is an arbitrary term that uniquely identifies the remote
endpoint to which the transport has connected.</p>
</item>
@@ -201,14 +205,14 @@ endpoint to which the transport has connected.</p>
<item>
<p>
An inbound Diameter message.
-Packet can be either <c>binary()</c> (the message to be sent)
-or <c>#diameter_packet{}</c>
-whose <c>packet</c> field contains a <c>binary()</c>.
-Any value (other than undefined) set in
+<c>Packet</c> can be either binary() (the received message)
+or a <c>#diameter_packet{}</c> record
+whose <c>bin</c> field contains the received binary().
+Any value (other than <c>undefined</c>) set in
the <c>transport_data</c> field will be passed back with a
corresponding answer message in the case that the inbound message is a
request unless the sender sets another value.
-How the <c>transport_data</c> is used/interpreted is up to the
+How <c>transport_data</c> is used/interpreted is up to the
transport module.</p>
</item>
@@ -216,7 +220,7 @@ transport module.</p>
<item>
<p>
Acknowledgment of a successful TLS handshake.
-Ref is the reference() received in the
+<c>Ref</c> is the reference() received in the
<c>{diameter, {tls, Ref, Type, Bool}}</c> message in response
to which the reply is sent.
A transport must exit if a handshake is not successful.</p>
diff --git a/lib/diameter/doc/src/diameter_using.xml b/lib/diameter/doc/src/diameter_using.xml
index 809b76bdf3..7d9c0cd492 100644
--- a/lib/diameter/doc/src/diameter_using.xml
+++ b/lib/diameter/doc/src/diameter_using.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -23,7 +23,7 @@ under the License.
</legalnotice>
-<title>Using the stack</title>
+<title>Usage</title>
<prepared></prepared>
<responsible></responsible>
<docno></docno>
diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl
index b07cd32b98..8fdeba57bf 100644
--- a/lib/diameter/examples/code/peer.erl
+++ b/lib/diameter/examples/code/peer.erl
@@ -117,6 +117,11 @@ server(T) ->
%%
%% Return config for a connecting transport.
+client({all, LA, RA, RP}) ->
+ [[M,{K,C}], T]
+ = [client({P, LA, RA, RP}) || P <- [sctp,tcp]],
+ [M, {K,C,2000} | T];
+
client({T, LA, RA, RP}) ->
[{transport_module, tmod(T)},
{transport_config, [{ip, addr(LA)},
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index 4273262015..47f9d1240f 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -139,7 +139,6 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- answer_errors = report}). %% | callback | discard
- %% how to handle containing errors?
+ options = [{answer_errors, report}]}). %% | callback | discard
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/src/.gitignore b/lib/diameter/src/.gitignore
index feeb378fd8..cc06720fd1 100644
--- a/lib/diameter/src/.gitignore
+++ b/lib/diameter/src/.gitignore
@@ -1,2 +1,3 @@
/depend.mk
+/otp.plt
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index de2eca0279..99c343275b 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -181,6 +181,31 @@ clean:
rm -f $(TARGET_FILES) gen/*
rm -f depend.mk
+realclean: clean
+ rm -f ../ebin/*
+# Not $(EBIN) just to be a bit paranoid
+
+PLT = ./otp.plt
+
+plt:
+ dialyzer --build_plt \
+ --apps erts stdlib kernel \
+ xmerl ssl public_key crypto \
+ compiler syntax_tools runtime_tools \
+ --output_plt $(PLT) \
+ --verbose
+
+dialyze: opt $(PLT)
+ dialyzer --plt $(PLT) \
+ --verbose \
+ -Wno_improper_lists \
+ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \
+ $(patsubst %, $(EBIN)/%.$(EMULATOR), \
+ $(notdir $(RT_MODULES) $(CT_MODULES)))
+# Omit all but the common dictionary module since these
+# (diameter_gen_relay in particular) generate warning depending on how
+# much of the included diameter_gen.hrl they use.
+
# ----------------------------------------------------
# Release targets
# ----------------------------------------------------
@@ -245,10 +270,11 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
-include depend.mk
-.PHONY: app clean depend dict info release_subdir
+.PHONY: app clean realclean depend dict info release_subdir
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
.PHONY: $(EXAMPLE_DIRS:%/=release_examples_%)
+.PHONY: plt dialyze
# Keep intermediate files.
.SECONDARY: $(DICT_ERLS) $(DICT_HRLS) gen/$(DICT_YRL:%=%.erl)
diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src
index 2ebdad598f..9b2a7d18ab 100644
--- a/lib/diameter/src/base/diameter.appup.src
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -22,13 +22,28 @@
[
{"0.9", [{restart_application, diameter}]},
{"0.10", [{restart_application, diameter}]},
- {"1.0", [{update, diameter_service},
- {update, diameter_watchdog}]}
+ {"1.0", [{restart_application, diameter}]},
+ {"1.1", [%% new code
+ {add_module, diameter_transport},
+ %% modified code
+ {load, diameter_sctp},
+ {load, diameter_stats},
+ {load, diameter_service},
+ {load, diameter_config},
+ {load, diameter_codec},
+ {load, diameter_watchdog},
+ {load, diameter_peer},
+ {load, diameter_peer_fsm},
+ {load, diameter},
+ %% unmodified but including modified diameter.hrl
+ {load, diameter_callback},
+ {load, diameter_capx},
+ {load, diameter_types}]}
],
[
{"0.9", [{restart_application, diameter}]},
{"0.10", [{restart_application, diameter}]},
- {"1.0", [{update, diameter_watchdog},
- {update, diameter_service}]}
+ {"1.0", [{restart_application, diameter}]},
+ {"1.1", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 336f0c1f2d..4f90b741ae 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -312,6 +312,7 @@ call(SvcName, App, Message) ->
-type transport_opt()
:: {transport_module, atom()}
| {transport_config, any()}
+ | {transport_config, any(), non_neg_integer() | infinity}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
| {capabilities_cb, evaluable()}
@@ -322,10 +323,11 @@ call(SvcName, App, Message) ->
%% Predicate passed to remove_transport/2
-type transport_pred()
- :: fun((reference(), connect|listen, list()) -> boolean())
- | fun((reference(), list()) -> boolean())
+ :: fun((transport_ref(), connect|listen, list()) -> boolean())
+ | fun((transport_ref(), list()) -> boolean())
| fun((list()) -> boolean())
- | reference()
+ | transport_ref()
+ | boolean()
| list()
| {connect|listen, transport_pred()}
| {atom(), atom(), list()}.
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index fe1212b7e0..421e280422 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -63,9 +63,9 @@ encode(Mod, #diameter_packet{} = Pkt) ->
e(Mod, Pkt)
catch
error: Reason ->
- %% Be verbose rather than letting the emulator truncate the
- %% error report.
- X = {Reason, ?STACK},
+ %% Be verbose since a crash report may be truncated and
+ %% encode errors are self-inflicted.
+ X = {?MODULE, encode, {Reason, ?STACK}},
diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}),
exit(X)
end;
@@ -91,7 +91,8 @@ e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
Flags = make_flags(0, Hdr),
- Pkt#diameter_packet{bin = <<Vsn:8, Length:24,
+ Pkt#diameter_packet{header = Hdr,
+ bin = <<Vsn:8, Length:24,
Flags:8, Code:24,
Aid:32,
Hid:32,
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 9253af0de2..e47f63f814 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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,6 +519,7 @@ rm(SvcName, L) ->
Refs = lists:map(fun(#transport{ref = R}) -> R end, L),
case stop_transport(SvcName, Refs) of
ok ->
+ diameter_stats:flush(Refs),
lists:foreach(fun delete_object/1, L);
{error, _} = No ->
No
@@ -600,7 +601,7 @@ app_acc({application, Opts}, Acc) ->
module = init_mod(Mod),
init_state = ModS,
mutable = init_mutable(M),
- answer_errors = init_answers(A)}
+ options = [{answer_errors, init_answers(A)}]}
| Acc];
app_acc(_, Acc) ->
Acc.
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 3e78c4caef..74ba709aac 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -27,12 +27,15 @@
up/2]).
%% ... and the stack.
--export([start/3,
+-export([start/1,
send/2,
close/1,
abort/1,
notify/2]).
+%% Old interface only called from old code.
+-export([start/3]). %% < diameter-1.2 (R15B02)
+
%% Server start.
-export([start_link/0]).
@@ -57,6 +60,11 @@
%% Server state.
-record(state, {id = now()}).
+%% Default transport_module/config.
+-define(DEFAULT_TMOD, diameter_tcp).
+-define(DEFAULT_TCFG, []).
+-define(DEFAULT_TTMO, infinity).
+
%%% ---------------------------------------------------------------------------
%%% # notify/2
%%% ---------------------------------------------------------------------------
@@ -68,9 +76,119 @@ notify(SvcName, T) ->
%%% # start/3
%%% ---------------------------------------------------------------------------
-start(T, Opts, #diameter_service{} = Svc) ->
- {Mod, Cfg} = split_transport(Opts),
- apply(Mod, start, [T, Svc, Cfg]).
+%% From old code: make is restart.
+start(_T, _Opts, #diameter_service{}) ->
+ {error, restart}.
+
+%%% ---------------------------------------------------------------------------
+%%% # start/1
+%%% ---------------------------------------------------------------------------
+
+-spec start({T, [Opt], #diameter_service{}})
+ -> {TPid, [Addr], Tmo, Data}
+ | {error, [term()]}
+ when T :: {connect|accept, diameter:transport_ref()},
+ Opt :: diameter:transport_opt(),
+ TPid :: pid(),
+ Addr :: inet:ip_address(),
+ Tmo :: non_neg_integer() | infinity,
+ Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]},
+ Mod :: module(),
+ Cfg :: term(),
+ Err :: term()
+ ; ({#diameter_service{}, Tmo, Data})
+ -> {TPid, [Addr], Tmo, Data}
+ | {error, [term()]}
+ when TPid :: pid(),
+ Addr :: inet:ip_address(),
+ Tmo :: non_neg_integer() | infinity,
+ Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]},
+ T :: {connect|accept, diameter:transport_ref()},
+ Mod :: module(),
+ Cfg :: term(),
+ Err :: term().
+
+%% Initial start.
+start({T, Opts, #diameter_service{} = Svc}) ->
+ start(T, Svc, pair(Opts, [], []), []);
+
+%% Subsequent start.
+start({#diameter_service{} = Svc, Tmo, {{T, _, Cfg}, Ms, Rest, Errs}}) ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, Errs).
+
+%% pair/3
+%%
+%% Pair transport modules with config.
+
+%% Another transport_module: accumulate it.
+pair([{transport_module, M} | Rest], Mods, Acc) ->
+ pair(Rest, [M|Mods], Acc);
+
+%% Another transport_config: accumulate another tuple.
+pair([{transport_config = T, C} | Rest], Mods, Acc) ->
+ pair([{T, C, ?DEFAULT_TTMO} | Rest], Mods, Acc);
+pair([{transport_config, C, Tmo} | Rest], Mods, Acc) ->
+ pair(Rest, [], acc({Mods, C, Tmo}, Acc));
+
+pair([_ | Rest], Mods, Acc) ->
+ pair(Rest, Mods, Acc);
+
+%% No transport_module or transport_config: defaults.
+pair([], [], []) ->
+ [{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}];
+
+%% One transport_module, one transport_config.
+pair([], [M], [{[], Cfg, Tmo}]) ->
+ [{[M], Cfg, Tmo}];
+
+%% Trailing transport_module: default transport_config.
+pair([], [_|_] = Mods, Acc) ->
+ lists:reverse(acc({Mods, ?DEFAULT_TCFG, ?DEFAULT_TTMO}, Acc));
+
+pair([], [], Acc) ->
+ lists:reverse(def(Acc)).
+
+%% acc/2
+
+acc(T, Acc) ->
+ [T | def(Acc)].
+
+%% def/1
+%%
+%% Default module of previous pair if none were specified.
+
+def([{[], Cfg, Tmo} | Acc]) ->
+ [{[?DEFAULT_TMOD], Cfg, Tmo} | Acc];
+def(Acc) ->
+ Acc.
+
+%% start/4
+
+start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, Errs);
+
+start(_, _, [], Errs) ->
+ {error, Errs}.
+
+%% start/7
+
+start(T, [], _, Svc, _, Rest, Errs) ->
+ start(T, Svc, Rest, Errs);
+
+start(T, [M|Ms], Cfg, Svc, Tmo, Rest, Errs) ->
+ case start(M, [T, Svc, Cfg]) of
+ {ok, TPid} ->
+ {TPid, [], Tmo, {{T, M, Cfg}, Ms, Rest, Errs}};
+ {ok, TPid, [_|_] = Addrs} ->
+ {TPid, Addrs, Tmo, {{T, M, Cfg}, Ms, Rest, Errs}};
+ E ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, [E|Errs])
+ end.
+
+%% start/2
+
+start(Mod, Args) ->
+ apply(Mod, start, Args).
%%% ---------------------------------------------------------------------------
%%% # up/[12]
@@ -204,21 +322,6 @@ bang(undefined = 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) ->
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 99644814d2..302540e76b 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -54,6 +54,12 @@
-define(NO_INBAND_SECURITY, 0).
-define(TLS, 1).
+%% Keys in process dictionary.
+-define(CB_KEY, cb). %% capabilities callback
+-define(DWA_KEY, dwa). %% outgoing DWA
+-define(Q_KEY, q). %% transport start queue
+-define(START_KEY, start). %% start of connected transport
+
%% A 2xxx series Result-Code. Not necessarily 2001.
-define(IS_SUCCESS(N), 2 == (N) div 1000).
@@ -115,16 +121,20 @@
%%% Output: Pid
%%% ---------------------------------------------------------------------------
+-spec start(T, [Opt], #diameter_service{})
+ -> pid()
+ when T :: {connect|accept, diameter:transport_ref()},
+ Opt :: diameter:transport_opt().
+
%% 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) ->
+start({_,_} = 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) ->
@@ -143,18 +153,18 @@ init(T) ->
proc_lib:init_ack({ok, self()}),
gen_server:enter_loop(?MODULE, [], i(T)).
-i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
- putr(dwa, dwa(Caps)),
+i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc}) ->
+ putr(?DWA_KEY, dwa(Caps)),
{M, Ref} = T,
+ diameter_stats:reg(Ref),
{[Ts], Rest} = proplists:split(Opts, [capabilities_cb]),
- putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}),
- {ok, TPid, Svc} = start_transport(T, Rest, Svc0),
- erlang:monitor(process, TPid),
+ putr(?CB_KEY, {Ref, [F || {_,F} <- Ts]}),
erlang:monitor(process, WPid),
+ {TPid, Addrs} = start_transport(T, Rest, Svc),
#state{parent = WPid,
transport = TPid,
mode = M,
- service = Svc}.
+ service = svc(Svc, Addrs)}.
%% 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
@@ -164,18 +174,56 @@ i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
%% 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}};
+start_transport(T, Opts, #diameter_service{capabilities = Caps} = Svc) ->
+ Addrs0 = Caps#diameter_caps.host_ip_address,
+ start_transport(Addrs0, {T, Opts, Svc}).
+
+start_transport(Addrs0, T) ->
+ case diameter_peer:start(T) of
+ {TPid, Addrs, Tmo, Data} ->
+ erlang:monitor(process, TPid),
+ q_next(TPid, Addrs0, Tmo, Data),
+ {TPid, addrs(Addrs, Addrs0)};
No ->
exit({shutdown, No})
end.
+addrs([], Addrs0) ->
+ Addrs0;
+addrs(Addrs, _) ->
+ Addrs.
+
+svc(Svc, []) ->
+ Svc;
+svc(Svc, Addrs) ->
+ readdr(Svc, Addrs).
+
+readdr(#diameter_service{capabilities = Caps0} = Svc, Addrs) ->
+ Caps = Caps0#diameter_caps{host_ip_address = Addrs},
+ Svc#diameter_service{capabilities = Caps}.
+
+%% The 4-tuple Data returned from diameter_peer:start/1 identifies the
+%% transport module/config use to start the transport process in
+%% question as well as any alternates to try if a connection isn't
+%% established within Tmo.
+q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) ->
+ send_after(Tmo, {connection_timeout, TPid}),
+ putr(?Q_KEY, {Addrs0, Tmo, Data}).
+
+%% Connection has been established: retain the started
+%% pid/module/config in the process dictionary. This is a part of the
+%% interface defined by this module, so that the transport pid can be
+%% found when constructing service_info (in order to extract further
+%% information from it).
+keep_transport(TPid) ->
+ {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY),
+ putr(?START_KEY, {TPid, T}).
+
+send_after(infinity, _) ->
+ ok;
+send_after(Tmo, T) ->
+ erlang:send_after(Tmo, self(), T).
+
%% handle_call/3
handle_call(_, _, State) ->
@@ -202,14 +250,27 @@ handle_info(T, #state{} = State) ->
?LOG(stop, T),
x(T, State)
catch
+ exit: {diameter_codec, encode, _} = Reason ->
+ close_wd(Reason, State#state.parent),
+ ?LOG(stop, Reason),
+ %% diameter_codec:encode/2 emits an error report. Only
+ %% indicate the probable reason here.
+ diameter_lib:info_report(probable_configuration_error,
+ insufficient_capabilities),
+ {stop, {shutdown, Reason}, State};
{?MODULE, Tag, Reason} ->
?LOG(Tag, {Reason, T}),
{stop, {shutdown, Reason}, State}
end.
-%% The form of the exception caught here is historical. It's
+%% The form of the throw caught here is historical. It's
%% significant that it's not a 2-tuple, as in ?FAILURE(Reason),
%% since these are caught elsewhere.
+%% Note that there's no guarantee that the service and transport
+%% capabilities are good enough to build a CER/CEA that can be
+%% succesfully encoded. It's not checked at diameter:add_transport/2
+%% since this can be called before creating the service.
+
x(Reason, #state{} = S) ->
close_wd(Reason, S),
{stop, {shutdown, Reason}, S}.
@@ -240,25 +301,48 @@ eraser(Key) ->
%% Connection to peer.
transition({diameter, {TPid, connected, Remote}},
- #state{state = PS,
+ #state{transport = TPid,
+ state = PS,
mode = M}
= S) ->
'Wait-Conn-Ack' = PS, %% assert
connect = M, %%
- send_CER(S#state{mode = {M, Remote},
- transport = TPid});
+ keep_transport(TPid),
+ send_CER(S#state{mode = {M, Remote}});
%% Connection from peer.
transition({diameter, {TPid, connected}},
- #state{state = PS,
+ #state{transport = TPid,
+ state = PS,
mode = M,
parent = Pid}
= S) ->
'Wait-Conn-Ack' = PS, %% assert
accept = M, %%
+ keep_transport(TPid),
Pid ! {accepted, self()},
- start_timer(S#state{state = recv_CER,
- transport = TPid});
+ start_timer(S#state{state = recv_CER});
+
+%% Connection established after receiving a connection_timeout
+%% message. This may be followed by an incoming message which arrived
+%% before the transport was killed and this can't be distinguished
+%% from one from the transport that's been started to replace it.
+transition({diameter, {_, connected}}, _) ->
+ {stop, connection_timeout};
+transition({diameter, {_, connected, _}}, _) ->
+ {stop, connection_timeout};
+
+%% Connection has timed out: start an alternate.
+transition({connection_timeout = T, TPid},
+ #state{transport = TPid,
+ state = 'Wait-Conn-Ack'}
+ = S) ->
+ exit(TPid, {shutdown, T}),
+ start_next(S);
+
+%% Connect timeout after connection or alternate start: ignore.
+transition({connection_timeout, _}, _) ->
+ ok;
%% Incoming message from the transport.
transition({diameter, {recv, Pkt}}, S) ->
@@ -305,14 +389,21 @@ 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 ->
+%% Parent has died.
+transition({'DOWN', _, process, WPid, _},
+ #state{parent = WPid}) ->
stop;
+%% Transport has died before connection timeout.
+transition({'DOWN', _, process, TPid, _},
+ #state{transport = TPid}
+ = S) ->
+ start_next(S);
+
+%% Transport has died after connection timeout.
+transition({'DOWN', _, process, _, _}, _) ->
+ ok;
+
%% State query.
transition({state, Pid}, #state{state = S, transport = TPid}) ->
Pid ! {self(), [S, TPid]},
@@ -320,6 +411,19 @@ transition({state, Pid}, #state{state = S, transport = TPid}) ->
%% Crash on anything unexpected.
+%% start_next/1
+
+start_next(#state{service = Svc0} = S) ->
+ case getr(?Q_KEY) of
+ {Addrs0, Tmo, Data} ->
+ Svc = readdr(Svc0, Addrs0),
+ {TPid, Addrs} = start_transport(Addrs0, {Svc, Tmo, Data}),
+ S#state{transport = TPid,
+ service = svc(Svc, Addrs)};
+ undefined ->
+ stop
+ end.
+
%% send_CER/1
send_CER(#state{mode = {connect, Remote},
@@ -649,7 +753,7 @@ rc([RC|_]) ->
%% answer/2
answer('DWR', _) ->
- getr(dwa);
+ getr(?DWA_KEY);
answer(Name, #state{service = #diameter_service{capabilities = Caps}}) ->
a(Name, Caps).
@@ -749,15 +853,15 @@ caps(#state{service = Svc}) ->
%% caps_cb/1
caps_cb(Caps) ->
- {Ref, Ts} = eraser(capabilities_cb),
- ccb(Ts, [Ref, Caps]).
+ {Ref, Ts} = eraser(?CB_KEY),
+ caps_cb(Ts, [Ref, Caps]).
-ccb([], _) ->
+caps_cb([], _) ->
ok;
-ccb([F | Rest], T) ->
+caps_cb([F | Rest], T) ->
case diameter_lib:eval([F|T]) of
ok ->
- ccb(Rest, T);
+ caps_cb(Rest, T);
N when ?IS_SUCCESS(N) -> %% 2xxx result code: accept immediately
N;
Res ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 3dfdcee2b2..54594db292 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -43,8 +43,7 @@
subscriptions/0,
services/0,
services/1,
- whois/1,
- flush_stats/1]).
+ whois/1]).
%% test/debug
-export([call_module/3,
@@ -65,13 +64,33 @@
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
+%% The "old" states maintained in this module historically.
-define(STATE_UP, up).
-define(STATE_DOWN, down).
+-type op_state() :: ?STATE_UP
+ | ?STATE_DOWN.
+
+%% The RFC 3539 watchdog states that are now maintained, albeit
+%% along with the old up/down. okay = up, else down.
+-define(WD_INITIAL, initial).
+-define(WD_OKAY, okay).
+-define(WD_SUSPECT, suspect).
+-define(WD_DOWN, down).
+-define(WD_REOPEN, reopen).
+
+-type wd_state() :: ?WD_INITIAL
+ | ?WD_OKAY
+ | ?WD_SUSPECT
+ | ?WD_DOWN
+ | ?WD_REOPEN.
+
-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
+-define(RELAY, ?DIAMETER_DICT_RELAY).
+
%% 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
@@ -117,7 +136,8 @@
type :: match(connect | accept),
ref :: match(reference()), %% key into diameter_config
options :: match([diameter:transport_opt()]),%% from start_transport
- op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP),
+ op_state = {?STATE_DOWN, ?WD_INITIAL}
+ :: match(op_state() | {op_state(), wd_state()}),
started = now(), %% at process start
conn = false :: match(boolean() | pid())}).
%% true at accept, pid() at connection_up (connT key)
@@ -388,15 +408,6 @@ whois(SvcName) ->
undefined
end.
-%%% ---------------------------------------------------------------------------
-%%% # flush_stats/1
-%%%
-%%% Output: list of {{SvcName, Alias, Counter}, Value}
-%%% ---------------------------------------------------------------------------
-
-flush_stats(TPid) ->
- diameter_stats:flush(TPid).
-
%% ===========================================================================
%% ===========================================================================
@@ -516,6 +527,34 @@ transition({reconnect, Pid}, S) ->
reconnect(Pid, S),
ok;
+%% Watchdog is sending notification of a state transition. Note that
+%% the connection_up/down messages are pre-date this message and are
+%% still used. A 'watchdog' message will follow these and communicate
+%% the same state as was set in handling connection_up/down.
+transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName,
+ peerT = PeerT}) ->
+ #peer{ref = Ref, type = T, options = Opts, op_state = {OS,_}}
+ = P
+ = fetch(PeerT, Pid),
+ insert(PeerT, P#peer{op_state = {OS, To}}),
+ send_event(SvcName, {watchdog, Ref, TPid, {From, To}, {T, Opts}}),
+ ok;
+%% Death of a peer process results in the removal of it's peer and any
+%% associated conn record when 'DOWN' is received (after this) but the
+%% states will be {?STATE_UP, ?WD_DOWN} for a short time. (No real
+%% problem since ?WD_* is only used in service_info.) We set ?WD_OKAY
+%% as a consequence of connection_up since we know a watchdog is
+%% coming. We can't set anything at connection_down since we don't
+%% know if the subsequent watchdog message will be ?WD_DOWN or
+%% ?WD_SUSPECT. We don't (yet) set ?STATE_* as a consequence of a
+%% watchdog message since this requires changing some of the matching
+%% on ?STATE_*.
+%%
+%% Death of a conn process results in connection_down followed by
+%% watchdog ?WD_DOWN. The latter doesn't result in the conn record
+%% being deleted since 'DOWN' from death of its peer doesn't (yet)
+%% deal with the record having been removed.
+
%% 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.
@@ -879,7 +918,14 @@ accepted(Pid, _TPid, #state{peerT = PeerT} = S) ->
fetch(Tid, Key) ->
[T] = ets:lookup(Tid, Key),
- T.
+ case T of
+ #peer{op_state = ?STATE_UP} = P ->
+ P#peer{op_state = {?STATE_UP, ?WD_OKAY}};
+ #peer{op_state = ?STATE_DOWN} = P ->
+ P#peer{op_state = {?STATE_DOWN, ?WD_DOWN}};
+ _ ->
+ T
+ end.
%%% ---------------------------------------------------------------------------
%%% # connection_up/3
@@ -925,12 +971,12 @@ connection_up(T, P, C, #state{peerT = PeerT,
service
= #diameter_service{applications = Apps}}
= S) ->
- #peer{conn = TPid, op_state = ?STATE_DOWN}
+ #peer{conn = TPid, op_state = {?STATE_DOWN, _}}
= P,
#conn{apps = SApps, caps = Caps}
= C,
- insert(PeerT, P#peer{op_state = ?STATE_UP}),
+ insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}),
request_peer_up(TPid),
report_status(up, P, C, S, T),
@@ -945,27 +991,35 @@ ilp({Id, Alias}, {TC, SA}, LDict) ->
init_conn(Id, Alias, TC, SA),
?Dict:append(Alias, TC, LDict).
-init_conn(Id, Alias, TC, {SvcName, Apps}) ->
+init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) ->
#diameter_app{module = ModX,
id = Id} %% assert
= find_app(Alias, Apps),
- peer_cb({ModX, peer_up, [SvcName, TC]}, Alias).
+ peer_cb({ModX, peer_up, [SvcName, TC]}, Alias)
+ orelse exit(TPid, kill). %% fake transport failure
+
+%% find_app/2
find_app(Alias, Apps) ->
- lists:keyfind(Alias, #diameter_app.alias, Apps).
+ case lists:keyfind(Alias, #diameter_app.alias, Apps) of
+ #diameter_app{options = E} = A when is_atom(E) -> %% upgrade
+ A#diameter_app{options = [{answer_errors, E}]};
+ A ->
+ A
+ end.
-%% 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.
+%% Don't bring down the service (and all associated connections)
+%% regardless of what happens.
peer_cb(MFA, Alias) ->
try state_cb(MFA, Alias) of
ModS ->
- mod_state(Alias, ModS)
+ mod_state(Alias, ModS),
+ true
catch
- E: Reason ->
- ?ERROR({E, Reason, MFA, ?STACK})
+ E:R ->
+ diameter_lib:error_report({failure, {E, R, Alias, ?STACK}}, MFA),
+ false
end.
%%% ---------------------------------------------------------------------------
@@ -979,22 +1033,22 @@ peer_cb(MFA, Alias) ->
connection_down(Pid, #state{peerT = PeerT,
connT = ConnT}
= S) ->
- #peer{op_state = ?STATE_UP, %% assert
+ #peer{op_state = {?STATE_UP, WS}, %% assert
conn = TPid}
= P
= fetch(PeerT, Pid),
C = fetch(ConnT, TPid),
- insert(PeerT, P#peer{op_state = ?STATE_DOWN}),
+ insert(PeerT, P#peer{op_state = {?STATE_DOWN, WS}}),
connection_down(P,C,S).
%% connection_down/3
-connection_down(#peer{op_state = ?STATE_DOWN}, _, S) ->
+connection_down(#peer{op_state = {?STATE_DOWN, _}}, _, S) ->
S;
connection_down(#peer{conn = TPid,
- op_state = ?STATE_UP}
+ op_state = {?STATE_UP, _}}
= P,
#conn{caps = Caps,
apps = SApps}
@@ -1043,7 +1097,7 @@ peer_down(Pid, Reason, #state{peerT = PeerT} = S) ->
%% Send an event at connection establishment failure.
closed({shutdown, {close, _TPid, Reason}},
- #peer{op_state = ?STATE_DOWN,
+ #peer{op_state = {?STATE_DOWN, _},
ref = Ref,
type = Type,
options = Opts},
@@ -1352,7 +1406,7 @@ send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) ->
#diameter_app{alias = Alias,
dictionary = Dict,
module = ModX,
- answer_errors = AE}
+ options = [{answer_errors, AE} | _]}
= App,
EPkt = encode(Dict, Pkt),
@@ -1935,6 +1989,12 @@ is_loop(Code, Vid, OH, Avps) ->
%%
%% Send a locally originating reply.
+%% Skip the setting of Result-Code and Failed-AVP's below.
+reply([Msg], Dict, TPid, Pkt)
+ when is_list(Msg);
+ is_tuple(Msg) ->
+ reply(Msg, Dict, TPid, Pkt#diameter_packet{errors = []});
+
%% No errors or a diameter_header/avp list.
reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
transport_data = TD}
@@ -1942,7 +2002,7 @@ reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
when [] == Es;
is_record(hd(Msg), diameter_header) ->
Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)),
- incr(send, Pkt, TPid), %% count result codes in sent answers
+ 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.
@@ -1983,7 +2043,10 @@ rc(RC) ->
rc(Rec, RC, Failed, Dict)
when is_integer(RC) ->
- set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict).
+ set(Rec,
+ lists:append([rc(Rec, {'Result-Code', RC}, Dict),
+ failed_avp(Rec, Failed, Dict)]),
+ Dict).
%% Reply as name and tuple list ...
set([_|_] = Ans, Avps, _) ->
@@ -1993,6 +2056,22 @@ set([_|_] = Ans, Avps, _) ->
set(Rec, Avps, Dict) ->
Dict:'#set-'(Avps, Rec).
+%% rc/3
+%%
+%% Turn the result code into a list if its optional and only set it if
+%% the arity is 1 or {0,1}. In other cases (which probably shouldn't
+%% exist in practise) we can't know what's appropriate.
+
+rc([MsgName | _], {'Result-Code' = K, RC} = T, Dict) ->
+ case Dict:avp_arity(MsgName, 'Result-Code') of
+ 1 -> [T];
+ {0,1} -> [{K, [RC]}];
+ _ -> []
+ end;
+
+rc(Rec, T, Dict) ->
+ rc([Dict:rec2msg(element(1, Rec))], T, Dict).
+
%% failed_avp/3
failed_avp(_, [] = No, _) ->
@@ -2200,44 +2279,39 @@ handle_answer(SvcName, _, {error, Req, Reason}) ->
handle_answer(SvcName,
AnswerErrors,
{answer, #request{dictionary = Dict} = Req, Pkt}) ->
- a(examine(diameter_codec:decode(Dict, Pkt)),
- SvcName,
- AnswerErrors,
- Req).
+ answer(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,
- caps = Caps,
- packet = P}
- = Req) ->
+answer(Pkt, SvcName, AE, #request{transport = TPid,
+ dictionary = Dict}
+ = Req) ->
try
- incr(in, Pkt, TPid)
+ incr(recv, Pkt, Dict, TPid)
of
- _ ->
- cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}])
+ _ -> a(Pkt, SvcName, AE, Req)
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).
+ a(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
+ end.
-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) ->
+a(#diameter_packet{errors = Es} = Pkt, SvcName, AE, #request{transport = TPid,
+ caps = Caps,
+ packet = P}
+ = Req)
+ when [] == Es;
+ callback == AE ->
+ cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]);
+
+a(Pkt, SvcName, report, Req) ->
x(errors, handle_answer, [SvcName, Req, Pkt]);
-e(Pkt, SvcName, discard, Req) ->
+
+a(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
@@ -2249,17 +2323,19 @@ e(Pkt, SvcName, discard, Req) ->
%% Increment a stats counter for an incoming or outgoing message.
%% TODO: fix
-incr(_, #diameter_packet{msg = undefined}, _) ->
+incr(_, #diameter_packet{msg = undefined}, _, _) ->
ok;
-incr(Dir, Pkt, TPid)
- when is_pid(TPid) ->
+incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid) ->
+ incr(TPid, {diameter_codec:msg_id(H), D, error});
+
+incr(Dir, Pkt, Dict, TPid) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
msg = Rec}
= Pkt,
- RC = int(get_avp_value(?BASE, 'Result-Code', Rec)),
+ RC = int(get_avp_value(Dict, 'Result-Code', Rec)),
PE = is_protocol_error(RC),
%% Check that the E bit is set only for 3xxx result codes.
@@ -2267,15 +2343,21 @@ incr(Dir, Pkt, TPid)
orelse (E andalso PE)
orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
- Ctr = rc_counter(Rec, RC),
- is_tuple(Ctr)
- andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
+ irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)).
+
+irc(_, _, _, undefined) ->
+ false;
+
+irc(TPid, Hdr, Dir, Ctr) ->
+ incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
%% incr/2
incr(TPid, Counter) ->
diameter_stats:incr(Counter, TPid, 1).
+%% error_counter/2
+
%% RFC 3588, 7.6:
%%
%% All Diameter answer messages defined in vendor-specific
@@ -2285,26 +2367,27 @@ incr(TPid, Counter) ->
%% 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(Rec, _) ->
- rcc(get_avp_value(?BASE, 'Experimental-Result', Rec)).
+rc_counter(Dict, Rec, undefined) ->
+ er(get_avp_value(Dict, 'Experimental-Result', Rec));
+rc_counter(_, _, RC) ->
+ {'Result-Code', RC}.
%% 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) ->
+er([{_,_,N} = T | _])
+ when is_integer(N) ->
T;
-rcc({_,_,RC} = T)
- when is_integer(RC) ->
+er({_,_,N} = T)
+ when is_integer(N) ->
T;
-rcc(_) ->
+er(_) ->
undefined.
-int([N])
+%% Extract the first good looking integer. There's no guarantee
+%% that what we're looking for has arity 1.
+int([N|_])
when is_integer(N) ->
N;
int(N)
@@ -2349,8 +2432,11 @@ rt(#request{packet = #diameter_packet{msg = undefined}}, _) ->
false; %% TODO: Not what we should do.
%% ... or not.
-rt(#request{packet = #diameter_packet{msg = Msg}} = Req, S) ->
- find_transport(get_destination(Msg), Req, S).
+rt(#request{packet = #diameter_packet{msg = Msg},
+ dictionary = Dict}
+ = Req,
+ S) ->
+ find_transport(get_destination(Dict, Msg), Req, S).
%%% ---------------------------------------------------------------------------
%%% # report_status/5
@@ -2462,12 +2548,12 @@ find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) ->
find_transport(#diameter_app{} = App, Msg, Opts, S) ->
ft(App, Msg, Opts, S).
-ft(#diameter_app{module = Mod} = App, Msg, Opts, S) ->
+ft(#diameter_app{module = Mod, dictionary = Dict} = App, Msg, Opts, S) ->
#options{filter = Filter,
extra = Xtra}
= Opts,
pick_peer(App#diameter_app{module = Mod ++ Xtra},
- get_destination(Msg),
+ get_destination(Dict, Msg),
Filter,
S);
ft(false = No, _, _, _) ->
@@ -2503,11 +2589,11 @@ find_transport([_,_] = RH,
Filter,
S).
-%% get_destination/1
+%% get_destination/2
-get_destination(Msg) ->
- [str(get_avp_value(?BASE, 'Destination-Realm', Msg)),
- str(get_avp_value(?BASE, 'Destination-Host', Msg))].
+get_destination(Dict, Msg) ->
+ [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
@@ -2531,6 +2617,9 @@ str(T) ->
%% 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(?RELAY, Name, Msg) ->
+ get_avp_value(?BASE, Name, Msg);
+
get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
try
{Code, _, VId} = Dict:avp_header(Name),
@@ -2746,20 +2835,45 @@ transports(#state{peerT = PeerT}) ->
'Vendor-Specific-Application-Id',
'Firmware-Revision']).
+%% The config returned by diameter:service_info(SvcName, all).
-define(ALL_INFO, [capabilities,
applications,
transport,
- pending,
- statistics]).
+ pending]).
+
+%% The rest.
+-define(OTHER_INFO, [connections,
+ name,
+ peers,
+ 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).
+ case tagged_info(Item, S) of
+ {_, T} -> T;
+ undefined = No -> No
+ end;
-service_info(Item, #state{service = Svc} = S, Complete) ->
+service_info(Items, S) ->
+ tagged_info(Items, S).
+
+tagged_info(Item, S)
+ when is_atom(Item) ->
+ case complete(Item) of
+ {value, I} ->
+ {I, complete_info(I,S)};
+ false ->
+ undefined
+ end;
+
+tagged_info(Items, S)
+ when is_list(Items) ->
+ [T || I <- Items, T <- [tagged_info(I,S)], T /= undefined, T /= []];
+
+tagged_info(_, _) ->
+ undefined.
+
+complete_info(Item, #state{service = Svc} = S) ->
case Item of
name ->
S#state.service_name;
@@ -2803,84 +2917,160 @@ service_info(Item, #state{service = Svc} = S, Complete) ->
applications -> info_apps(S);
transport -> info_transport(S);
pending -> info_pending(S);
- statistics -> info_stats(S);
- keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test
+ keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO;
all -> service_info(?ALL_INFO, S);
- _ when Complete -> service_info(complete(Item), S, false);
- _ -> undefined
+ statistics -> info_stats(S);
+ connections -> info_connections(S);
+ peers -> info_peers(S)
end.
+complete(I)
+ when I == keys;
+ I == all ->
+ {value, I};
complete(Pre) ->
P = atom_to_list(Pre),
- case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO,
+ case [I || I <- ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO,
lists:prefix(P, atom_to_list(I))]
of
- [I] -> I;
- _ -> Pre
+ [I] -> {value, I};
+ _ -> false
end.
+%% info_stats/1
+
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,
+ MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'},
+ [{'is_pid', '$2'}],
+ [['$1', '$2']]}],
+ diameter_stats:read(lists:append(ets:select(PeerT, MatchSpec))).
+
+%% info_transport/1
+%%
+%% One entry per configured transport. Statistics for each entry are
+%% the accumulated values for the ref and associated peer pids.
+
+info_transport(S) ->
+ PeerD = peer_dict(S, config_dict(S)),
+ RefsD = dict:map(fun(_, Ls) -> [P || L <- Ls, {peer, {P,_}} <- L] end,
+ PeerD),
+ Refs = lists:append(dict:fold(fun(R, Ps, A) -> [[R|Ps] | A] end,
+ [],
+ RefsD)),
+ Stats = diameter_stats:read(Refs),
+ dict:fold(fun(R, Ls, A) ->
+ Ps = dict:fetch(R, RefsD),
+ [[{ref, R} | transport(Ls)] ++ [stats([R|Ps], Stats)]
+ | A]
+ end,
[],
- 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}) ->
+ PeerD).
+
+%% Only a config entry for a listening transport: use it.
+transport([[{type, listen}, _] = L]) ->
+ L ++ [{accept, []}];
+
+%% Only one config or peer entry for a connecting transport: use it.
+transport([[{type, connect} | _] = L]) ->
+ L;
+
+%% Peer entries: discard config. Note that the peer entries have
+%% length at least 3.
+transport([[_,_] | L]) ->
+ transport(L);
+
+%% Possibly many peer entries for a listening transport. Note that all
+%% have the same options by construction, which is not terribly space
+%% efficient. (TODO: all entries for the same Ref should share options.)
+transport([[{type, accept}, {options, Opts} | _] | _] = Ls) ->
+ [{type, listen},
+ {options, Opts},
+ {accept, [lists:nthtail(2,L) || L <- Ls]}].
+
+peer_dict(#state{peerT = PeerT, connT = ConnT}, Dict0) ->
+ ets:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, PeerT).
+
+peer_acc(ConnT, Acc, #peer{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts,
+ op_state = OS,
+ started = T,
+ conn = TPid}) ->
+ WS = wd_state(OS),
dict:append(Ref,
[{type, Type},
{options, Opts},
- {watchdog, {Pid, T, OS}}
- | info_conn(ConnT, TPid)],
+ {watchdog, {Pid, T, WS}}
+ | info_conn(ConnT, TPid, WS /= ?WD_DOWN)],
Acc).
-info_conn(ConnT, TPid) ->
- info_conn(ets:lookup(ConnT, TPid)).
+info_conn(ConnT, TPid, true)
+ when is_pid(TPid) ->
+ info_conn(ets:lookup(ConnT, TPid));
+info_conn(_, _, _) ->
+ [].
+
+%% The point of extracting the config here is so that 'transport' info
+%% has one entry for each transport ref, the peer table only
+%% containing entries that have a living watchdog.
+
+config_dict(#state{service_name = SvcName}) ->
+ lists:foldl(fun config_acc/2,
+ dict:new(),
+ diameter_config:lookup(SvcName)).
+
+config_acc({Ref, T, Opts}, Dict)
+ when T == listen;
+ T == connect ->
+ dict:store(Ref, [[{type, T}, {options, Opts}]], Dict);
+config_acc(_, Dict) ->
+ Dict.
+
+wd_state({_,S}) ->
+ S;
+wd_state(?STATE_UP) ->
+ ?WD_OKAY;
+wd_state(?STATE_DOWN) ->
+ ?WD_DOWN.
info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) ->
[{peer, {Pid, T}},
{apps, SApps},
- {caps, info_caps(Caps)}];
+ {caps, info_caps(Caps)}
+ | try [{port, info_port(Pid)}] catch _:_ -> [] end];
info_conn([] = No) ->
No.
+%% Extract information that the processes involved are expected to
+%% "publish" in their process dictionaries. Simple but backhanded.
+info_port(Pid) ->
+ {_, PD} = process_info(Pid, dictionary),
+ {_, T} = lists:keyfind({diameter_peer_fsm, start}, 1, PD),
+ {TPid, {_Type, TMod, _Cfg}} = T,
+ {_, TD} = process_info(TPid, dictionary),
+ {_, Data} = lists:keyfind({TMod, info}, 1, TD),
+ [{owner, TPid},
+ {module, TMod}
+ | try TMod:info(Data) catch _:_ -> [] end].
+
+%% Use the fields names from diameter_caps instead of
+%% diameter_base_CER to distinguish between the 2-tuple values
+%% compared to the single capabilities values. Note also that the
+%% returned list is tagged 'caps' rather than 'capabilities' to
+%% emphasize the difference.
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}].
+mk_app(#diameter_app{} = A) ->
+ lists:zip(record_info(fields, diameter_app), tl(tuple_to_list(A))).
+
+%% info_pending/1
+%%
+%% One entry for each outgoing request whose answer is outstanding.
info_pending(#state{} = S) ->
MatchSpec = [{{'$1',
@@ -2895,3 +3085,57 @@ info_pending(#state{} = S) ->
{{from, '$3'}}]}}]}],
ets:select(?REQUEST_TABLE, MatchSpec).
+
+%% info_connections/1
+%%
+%% One entry per transport connection. Statistics for each entry are
+%% for the peer pid only.
+
+info_connections(S) ->
+ ConnL = conn_list(S),
+ Stats = diameter_stats:read([P || L <- ConnL, {peer, {P,_}} <- L]),
+ [L ++ [stats([P], Stats)] || L <- ConnL, {peer, {P,_}} <- L].
+
+conn_list(S) ->
+ lists:append(dict:fold(fun conn_acc/3, [], peer_dict(S, dict:new()))).
+
+conn_acc(Ref, Peers, Acc) ->
+ [[[{ref, Ref} | L] || L <- Peers, lists:keymember(peer, 1, L)]
+ | Acc].
+
+stats(Refs, Stats) ->
+ {statistics, dict:to_list(lists:foldl(fun(R,D) ->
+ stats_acc(R, D, Stats)
+ end,
+ dict:new(),
+ Refs))}.
+
+stats_acc(Ref, Dict, Stats) ->
+ lists:foldl(fun({C,N}, D) -> dict:update_counter(C, N, D) end,
+ Dict,
+ proplists:get_value(Ref, Stats, [])).
+
+%% info_peers/1
+%%
+%% One entry per peer Origin-Host. Statistics for each entry are
+%% accumulated values for all peer pids.
+
+info_peers(S) ->
+ {PeerD, RefD} = lists:foldl(fun peer_acc/2,
+ {dict:new(), dict:new()},
+ conn_list(S)),
+ Refs = lists:append(dict:fold(fun(_, Rs, A) -> [Rs|A] end,
+ [],
+ RefD)),
+ Stats = diameter_stats:read(Refs),
+ dict:fold(fun(OH, Cs, A) ->
+ Rs = dict:fetch(OH, RefD),
+ [{OH, [{connections, Cs}, stats(Rs, Stats)]} | A]
+ end,
+ [],
+ PeerD).
+
+peer_acc(Peer, {PeerD, RefD}) ->
+ [{TPid, _}, [{origin_host, {_, OH}} | _]]
+ = [proplists:get_value(K, Peer) || K <- [peer, caps]],
+ {dict:append(OH, Peer, PeerD), dict:append(OH, TPid, RefD)}.
diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl
index 71479afa95..70727d068e 100644
--- a/lib/diameter/src/base/diameter_stats.erl
+++ b/lib/diameter/src/base/diameter_stats.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -22,14 +22,13 @@
%%
-module(diameter_stats).
--compile({no_auto_import, [monitor/2]}).
-behaviour(gen_server).
--export([reg/1, reg/2,
- incr/1, incr/2, incr/3,
+-export([reg/2, reg/1,
+ incr/3, incr/1,
read/1,
- flush/0, flush/1]).
+ flush/1]).
%% supervisor callback
-export([start_link/0]).
@@ -48,123 +47,105 @@
-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}.
+%% ets table containing 2-tuple 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.
+-type ref() :: any().
+
+%% ---------------------------------------------------------------------------
+%% # reg(Pid, Ref)
+%%
+%% Register a process as a contributor of statistics associated with a
+%% specified term. Statistics can be contributed by specifying either
+%% Pid or Ref as the second argument to incr/3. Statistics contributed
+%% by Pid are folded into the corresponding entry for Ref when the
+%% process dies.
+%% ---------------------------------------------------------------------------
+
+-spec reg(pid(), ref())
+ -> boolean().
-reg(Pid, Contrib)
+reg(Pid, Ref)
when is_pid(Pid) ->
- call({reg, Pid, Contrib}).
+ call({reg, Pid, Ref}).
--spec reg(contrib())
+-spec reg(ref())
-> 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(Counter, Ref, N)
+%%
+%% Increment a counter for the specified contributor.
+%%
+%% Ref will typically be an argument passed to reg/2 but there's
+%% nothing that requires this. Only registered pids can contribute
+%% counters however, otherwise incr/3 is a no-op.
+%% ---------------------------------------------------------------------------
-incr(Ctr, Contrib, N) ->
- update_counter({Ctr, Contrib}, N).
+-spec incr(counter(), ref(), integer())
+ -> integer() | false.
-incr(Ctr, N)
+incr(Ctr, Ref, N)
when is_integer(N) ->
- incr(Ctr, self(), N);
-
-incr(Ctr, Contrib) ->
- incr(Ctr, Contrib, 1).
+ update_counter({Ctr, Ref}, N).
incr(Ctr) ->
incr(Ctr, self(), 1).
-%%% ---------------------------------------------------------------------------
-%%% # read(Contribs)
-%%%
-%%% Description: Retrieve counters for the specified contributors.
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # read(Refs)
+%%
+%% Retrieve counters for the specified contributors.
+%% ---------------------------------------------------------------------------
--spec read([contrib()])
- -> [{contrib(), [{counter(), integer()}]}].
+-spec read([ref()])
+ -> [{ref(), [{counter(), integer()}]}].
-read(Contribs) ->
- lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end,
+read(Refs) ->
+ read(Refs, false).
+
+read(Refs, B) ->
+ MatchSpec = [{{{'_', '$1'}, '_'},
+ [?ORCOND([{'=:=', '$1', {const, R}}
+ || R <- Refs])],
+ ['$_']}],
+ L = ets:select(?TABLE, MatchSpec),
+ B andalso delete(L),
+ lists:foldl(fun({{C,R}, N}, D) -> orddict:append(R, {C,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()}].
+ L).
+
+%% ---------------------------------------------------------------------------
+%% # flush(Refs)
+%%
+%% Retrieve and delete statistics for the specified contributors.
+%% ---------------------------------------------------------------------------
+
+-spec flush([ref()])
+ -> [{ref(), {counter(), integer()}}].
-flush(Contrib) ->
+flush(Refs) ->
try
- call({flush, Contrib})
+ call({flush, Refs})
catch
exit: _ ->
[]
end.
-flush() ->
- flush(self()).
-
-%%% ---------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
+%% ===========================================================================
start_link() ->
ServerName = {local, ?SERVER},
@@ -179,18 +160,16 @@ state() ->
uptime() ->
call(uptime).
-%%% ----------------------------------------------------------
-%%% # init(_)
-%%%
-%%% Output: {ok, State}
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # init/1
+%% ----------------------------------------------------------
init([]) ->
ets:new(?TABLE, [named_table, ordered_set, public]),
{ok, #state{}}.
%% ----------------------------------------------------------
-%% handle_call(Request, From, State)
+%% # handle_call/3
%% ----------------------------------------------------------
handle_call(state, _, State) ->
@@ -199,31 +178,31 @@ handle_call(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({incr, T}, _, State) ->
+ {reply, update_counter(T), State};
-handle_call({flush, Contrib}, _From, State) ->
- {reply, fetch(Contrib), State};
+handle_call({reg, Pid, Ref}, _From, State) ->
+ B = ets:insert_new(?TABLE, {Pid, Ref}),
+ B andalso erlang:monitor(process, Pid),
+ {reply, B, State};
+
+handle_call({flush, Refs}, _From, State) ->
+ {reply, read(Refs, true), State};
handle_call(Req, From, State) ->
?UNEXPECTED([Req, From]),
{reply, nok, State}.
%% ----------------------------------------------------------
-%% handle_cast(Request, State)
+%% # handle_cast/2
%% ----------------------------------------------------------
-handle_cast({incr, Rec}, State) ->
- update_counter(Rec),
- {noreply, State};
-
handle_cast(Msg, State) ->
?UNEXPECTED([Msg]),
{noreply, State}.
%% ----------------------------------------------------------
-%% handle_info(Request, State)
+%% # handle_info/2
%% ----------------------------------------------------------
handle_info({'DOWN', _MRef, process, Pid, _}, State) ->
@@ -235,91 +214,62 @@ handle_info(Info, State) ->
{noreply, State}.
%% ----------------------------------------------------------
-%% terminate(Reason, State)
+%% # terminate/2
%% ----------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%% ----------------------------------------------------------
-%% code_change(OldVsn, State, Extra)
+%% # code_change/3
%% ----------------------------------------------------------
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),
+ down(lookup(Pid), ets:match_object(?TABLE, {{'_', Pid}, '_'})).
+
+down([{_, Ref} = T], L) ->
fold(Ref, L),
- delete_object(T),
+ delete([T|L]);
+down([], L) -> %% flushed
delete(L).
-%% Fold Pid-based entries into Ref-based ones.
+%% 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(_) ->
- [].
+ lists:foreach(fun({{K, _}, V}) -> update_counter({{K, Ref}, V}) end, L).
%% 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.
+%% From an arbitrary process. Call to the server process to insert a
+%% new element if the counter doesn't exists so that two processes
+%% don't insert simultaneously.
update_counter(Key, N) ->
try
ets:update_counter(?TABLE, Key, N)
catch
error: badarg ->
- cast({incr, ?REC(Key, N)})
+ call({incr, {Key, N}})
end.
%% update_counter/1
%%
-%% From the server process.
+%% From the server process, when update_counter/2 failed due to a
+%% non-existent entry.
-update_counter(?REC(Key, N) = T) ->
+update_counter({{_Ctr, Ref} = Key, N} = T) ->
try
ets:update_counter(?TABLE, Key, N)
catch
error: badarg ->
- insert(T)
+ (not is_pid(Ref) orelse ets:member(?TABLE, Ref))
+ andalso begin insert(T), N end
end.
insert(T) ->
@@ -328,13 +278,8 @@ insert(T) ->
lookup(Key) ->
ets:lookup(?TABLE, Key).
-delete_object(T) ->
- ets:delete_object(?TABLE, T).
-
-%% cast/1
-
-cast(Msg) ->
- gen_server:cast(?SERVER, Msg).
+delete(Objs) ->
+ lists:foreach(fun({K,_}) -> ets:delete(?TABLE, K) end, Objs).
%% call/1
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index fb22fd8275..d7474e5c56 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -54,7 +54,7 @@
%% number of DWAs received during reopen
%% end PCB
parent = self() :: pid(),
- transport :: pid(),
+ transport :: pid() | undefined,
tref :: reference(), %% reference for current watchdog timer
message_data}). %% term passed into diameter_service with message
@@ -64,6 +64,13 @@
%% that a failed capabilities exchange produces the desired exit
%% reason.
+-spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}})
+ -> {reference(), pid()}
+ when Type :: {connect|accept, diameter:transport_ref()},
+ RecvData :: term(),
+ Opt :: diameter:transport_opt(),
+ SvcName :: diameter:service_name().
+
start({_,_} = Type, T) ->
Ref = make_ref(),
{ok, Pid} = diameter_watchdog_sup:start_child({Ref, {Type, self(), T}}),
@@ -102,7 +109,7 @@ i({_, Pid, _} = T) -> %% from old code
erlang:monitor(process, Pid),
make_state(T).
-make_state({T, Pid, {ConnT,
+make_state({T, Pid, {RecvData,
Opts,
SvcName,
#diameter_service{applications = Apps,
@@ -116,7 +123,7 @@ make_state({T, Pid, {ConnT,
tw = proplists:get_value(watchdog_timer,
Opts,
?DEFAULT_TW_INIT),
- message_data = {ConnT, SvcName, Apps}}.
+ message_data = {RecvData, SvcName, Apps}}.
%% handle_call/3
@@ -134,14 +141,36 @@ handle_info(T, State) ->
case transition(T, State) of
ok ->
{noreply, State};
- #watchdog{status = X} = S ->
- ?LOGC(X =/= State#watchdog.status, transition, X),
+ #watchdog{} = S ->
+ event(State, S),
{noreply, S};
stop ->
?LOG(stop, T),
+ event(State, State#watchdog{status = down}),
{stop, {shutdown, T}, State}
end.
+event(#watchdog{status = T}, #watchdog{status = T}) ->
+ ok;
+
+event(#watchdog{transport = undefined}, #watchdog{transport = undefined}) ->
+ ok;
+
+event(#watchdog{status = From, transport = F, parent = Pid},
+ #watchdog{status = To, transport = T}) ->
+ E = {tpid(F,T), From, To},
+ notify(Pid, E),
+ ?LOG(transition, {self(), E}).
+
+tpid(_, Pid)
+ when is_pid(Pid) ->
+ Pid;
+tpid(Pid, _) ->
+ Pid.
+
+notify(Pid, E) ->
+ Pid ! {watchdog, self(), E}.
+
%% terminate/2
terminate(_, _) ->
@@ -251,8 +280,8 @@ transition({'DOWN', _, process, TPid, _},
status = initial}) ->
stop;
-transition({'DOWN', _, process, Pid, _},
- #watchdog{transport = Pid}
+transition({'DOWN', _, process, TPid, _},
+ #watchdog{transport = TPid}
= S) ->
failover(S),
close(S),
@@ -385,7 +414,7 @@ recv(Name, Pkt, S) ->
rcv(Name, Pkt, S),
NS
catch
- throw: {?MODULE, throwaway, #watchdog{} = NS} ->
+ {?MODULE, throwaway, #watchdog{} = NS} ->
NS
end.
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index 7a700a6d53..5d3c4157ae 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -58,6 +58,7 @@ RT_MODULES = \
transport/diameter_tcp_sup \
transport/diameter_sctp \
transport/diameter_sctp_sup \
+ transport/diameter_transport \
transport/diameter_transport_sup
# Handwritten (compile time) modules not included in the app file.
diff --git a/lib/diameter/src/transport/diameter_etcp.erl b/lib/diameter/src/transport/diameter_etcp.erl
index d925d62545..cd62cf34fa 100644
--- a/lib/diameter/src/transport/diameter_etcp.erl
+++ b/lib/diameter/src/transport/diameter_etcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -36,7 +36,9 @@
send/2,
close/1,
setopts/2,
- port/1]).
+ sockname/1,
+ peername/1,
+ getstat/1]).
%% child start
-export([start_link/1]).
@@ -113,10 +115,20 @@ close(Pid) ->
setopts(_, _) ->
ok.
-%% port/1
+%% sockname/1
-port(_) ->
- 3868. %% We have no local port: fake it.
+sockname(_) ->
+ {error, ?MODULE}.
+
+%% peername/1
+
+peername(_) ->
+ {error, ?MODULE}.
+
+%% getstat/1
+
+getstat(_) ->
+ {error, ?MODULE}.
%% start_link/1
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 68b0342cd5..79b8b851fb 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -37,12 +37,18 @@
code_change/3,
terminate/2]).
+-export([info/1]). %% service_info callback
+
-export([ports/0,
ports/1]).
-include_lib("kernel/include/inet_sctp.hrl").
-include_lib("diameter/include/diameter.hrl").
+%% Keys into process dictionary.
+-define(INFO_KEY, info).
+-define(REF_KEY, ref).
+
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
%% The default port for a listener.
@@ -62,6 +68,7 @@
-record(transport,
{parent :: pid(),
mode :: {accept, pid()}
+ | accept
| {connect, {list(inet:ip_address()), uint(), list()}}
%% {RAs, RP, Errors}
| connect,
@@ -134,6 +141,24 @@ start_link(T) ->
diameter_lib:spawn_opts(server, [])).
%% ---------------------------------------------------------------------------
+%% # info/1
+%% ---------------------------------------------------------------------------
+
+info({gen_sctp, Sock}) ->
+ lists:flatmap(fun(K) -> info(K, Sock) end,
+ [{socket, sockname},
+ {peer, peername},
+ {statistics, getstat}]).
+
+info({K,F}, Sock) ->
+ case inet:F(Sock) of
+ {ok, V} ->
+ [{K,V}];
+ _ ->
+ []
+ end.
+
+%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -157,7 +182,7 @@ i({connect, Pid, Opts, Addrs, Ref}) ->
RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As],
[RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps],
{LAs, Sock} = open(Addrs, Rest, 0),
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self(), LAs}),
erlang:monitor(process, Pid),
#transport{parent = Pid,
@@ -169,7 +194,7 @@ i({connect, _, _, _} = T) -> %% from old code
%% An accepting transport spawned by diameter.
i({accept, Pid, LPid, Sock, Ref})
when is_pid(Pid) ->
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
erlang:monitor(process, Pid),
erlang:monitor(process, LPid),
@@ -181,7 +206,7 @@ i({accept, _, _, _} = T) -> %% from old code
%% An accepting transport spawned at association establishment.
i({accept, Ref, LPid, Sock, Id}) ->
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
MRef = erlang:monitor(process, LPid),
%% Wait for a signal that the transport has been started before
@@ -325,6 +350,11 @@ terminate(_, #transport{assoc_id = undefined}) ->
ok;
terminate(_, #transport{socket = Sock,
+ mode = accept,
+ assoc_id = Id}) ->
+ close(Sock, Id);
+
+terminate(_, #transport{socket = Sock,
mode = {accept, _},
assoc_id = Id}) ->
close(Sock, Id);
@@ -356,13 +386,16 @@ start_timer(S) ->
%% Incoming message from SCTP.
l({sctp, Sock, _RA, _RP, Data} = Msg, #listener{socket = Sock} = S) ->
- setopts(Sock),
- case find(Data, S) of
+ Id = assoc_id(Data),
+
+ try find(Id, Data, S) of
{TPid, NewS} ->
- TPid ! Msg,
+ TPid ! {peeloff, peeloff(Sock, Id, TPid), Msg},
NewS;
false ->
S
+ after
+ setopts(Sock)
end;
%% Transport is asking message to be sent. See send/3 for why the send
@@ -430,15 +463,19 @@ t(T,S) ->
%% transition/2
-%% Incoming message.
-transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock,
- mode = {accept, _}}
- = S) ->
- recv(Data, S);
+%% Listening process is transfering ownership of an association.
+transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg},
+ #transport{mode = {accept, _},
+ socket = LSock}
+ = S) ->
+ transition(Msg, S#transport{socket = Sock});
-transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
+%% Incoming message.
+transition({sctp, _Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
setopts(Sock),
recv(Data, S);
+%% Don't match on Sock since in R15B01 it can be the listening socket
+%% in the (peeled-off) accept case, which is likely a bug.
%% Outgoing message.
transition({diameter, {send, Msg}}, S) ->
@@ -456,13 +493,18 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) ->
transition({diameter, {tls, _Ref, _Type, _Bool}}, _) ->
stop;
+%% Parent process has died.
+transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
+ stop;
+
%% Listener process has died.
transition({'DOWN', _, process, Pid, _}, #transport{mode = {accept, Pid}}) ->
stop;
-%% Parent process has died.
-transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
- stop;
+%% Ditto but we have ownership of the association. It might be that
+%% we'll go down anyway though.
+transition({'DOWN', _, process, _Pid, _}, #transport{mode = accept}) ->
+ ok;
%% Request for the local port number.
transition({resolve_port, Pid}, #transport{socket = Sock})
@@ -521,14 +563,6 @@ send(Bin, #transport{streams = {_, OS},
%% send/3
-%% Messages have to be sent from the controlling process, which is
-%% probably a bug. Sending from here causes an inet_reply, Sock,
-%% Status} message to be sent to the controlling process while
-%% gen_sctp:send/4 here hangs.
-send(StreamId, Bin, #transport{assoc_id = AId,
- mode = {accept, LPid}}) ->
- LPid ! {send, AId, StreamId, Bin};
-
send(StreamId, Bin, #transport{socket = Sock,
assoc_id = AId}) ->
send(Sock, AId, StreamId, Bin).
@@ -554,10 +588,9 @@ recv({_, #sctp_assoc_change{state = comm_up,
mode = {T, _},
socket = Sock}
= S) ->
- Ref = getr(ref),
+ Ref = getr(?REF_KEY),
is_reference(Ref) %% started in new code
- andalso
- (true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}})),
+ andalso publish(T, Ref, Id, Sock),
up(S#transport{assoc_id = Id,
streams = {IS, OS}});
@@ -599,6 +632,10 @@ recv({_, #sctp_paddr_change{}}, _) ->
recv({_, #sctp_pdapi_event{}}, _) ->
ok.
+publish(T, Ref, Id, Sock) ->
+ true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}),
+ putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1
+
%% up/1
up(#transport{parent = Pid,
@@ -608,21 +645,15 @@ up(#transport{parent = Pid,
S#transport{mode = C};
up(#transport{parent = Pid,
- mode = {accept, _}}
+ mode = {accept = A, _}}
= S) ->
diameter_peer:up(Pid),
- S.
+ S#transport{mode = A}.
-%% find/2
-
-find({[#sctp_sndrcvinfo{assoc_id = Id}], _}
- = Data,
- #listener{tmap = T}
- = S) ->
- f(ets:lookup(T, Id), Data, S);
+%% find/3
-find({_, Rec} = Data, #listener{tmap = T} = S) ->
- f(ets:lookup(T, assoc_id(Rec)), Data, S).
+find(Id, Data, #listener{tmap = T} = S) ->
+ f(ets:lookup(T, Id), Data, S).
%% New association and a transport waiting for one: use it.
f([],
@@ -663,17 +694,29 @@ f([], _, _) ->
%% assoc_id/1
-assoc_id(#sctp_shutdown_event{assoc_id = Id}) ->
+assoc_id({[#sctp_sndrcvinfo{assoc_id = Id}], _}) ->
Id;
-assoc_id(#sctp_assoc_change{assoc_id = Id}) ->
+assoc_id({_, Rec}) ->
+ id(Rec).
+
+id(#sctp_shutdown_event{assoc_id = Id}) ->
+ Id;
+id(#sctp_assoc_change{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_sndrcvinfo{assoc_id = Id}) ->
+id(#sctp_sndrcvinfo{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_paddr_change{assoc_id = Id}) ->
+id(#sctp_paddr_change{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_adaptation_event{assoc_id = Id}) ->
+id(#sctp_adaptation_event{assoc_id = Id}) ->
Id.
+%% peeloff/3
+
+peeloff(LSock, Id, TPid) ->
+ {ok, Sock} = gen_sctp:peeloff(LSock, Id),
+ ok = gen_sctp:controlling_process(Sock, TPid),
+ Sock.
+
%% connect/4
connect(_, [], _, Reasons) ->
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 78dbda6888..f3fbbee609 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -37,11 +37,17 @@
code_change/3,
terminate/2]).
+-export([info/1]). %% service_info callback
+
-export([ports/0,
ports/1]).
-include_lib("diameter/include/diameter.hrl").
+%% Keys into process dictionary.
+-define(INFO_KEY, info).
+-define(REF_KEY, ref).
+
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
-define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1
@@ -74,7 +80,7 @@
%% Accepting/connecting transport process state.
-record(transport,
- {socket :: inet:socket(), %% accept or connect socket
+ {socket :: inet:socket() | ssl:sslsock(), %% accept or connect socket
parent :: pid(), %% of process that started us
module :: module(), %% gen_tcp-like module
frag = <<>> :: binary() | {tref(), frag()}, %% message fragment
@@ -111,6 +117,33 @@ start_link(T) ->
diameter_lib:spawn_opts(server, [])).
%% ---------------------------------------------------------------------------
+%% # info/1
+%% ---------------------------------------------------------------------------
+
+info({Mod, Sock}) ->
+ lists:flatmap(fun(K) -> info(Mod, K, Sock) end,
+ [{socket, fun sockname/2},
+ {peer, fun peername/2},
+ {statistics, fun getstat/2}
+ | ssl_info(Mod, Sock)]).
+
+info(Mod, {K,F}, Sock) ->
+ case F(Mod, Sock) of
+ {ok, V} ->
+ [{K,V}];
+ _ ->
+ []
+ end.
+
+ssl_info(ssl = M, Sock) ->
+ [{M, ssl_info(Sock)}];
+ssl_info(_, _) ->
+ [].
+
+ssl_info(Sock) ->
+ [{peercert, C} || {ok, C} <- [ssl:peercert(Sock)]].
+
+%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -133,7 +166,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
setopts(M, Sock),
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
#transport{parent = Pid,
module = M,
socket = Sock,
@@ -191,7 +224,7 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
{LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
- true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ publish(Mod, T, Ref, Sock),
diameter_peer:up(Pid),
Sock;
@@ -202,10 +235,14 @@ i(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
RPort = get_port(RP),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))),
- true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ publish(Mod, T, Ref, Sock),
diameter_peer:up(Pid, {RAddr, RPort}),
Sock.
+publish(Mod, T, Ref, Sock) ->
+ true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ putr(?INFO_KEY, {Mod, Sock}). %% for info/1
+
ok({ok, T}) ->
T;
ok(No) ->
@@ -521,7 +558,7 @@ tls_handshake(Type, true, #transport{socket = Sock,
ssl = Opts}
= S) ->
{ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]),
- Ref = getr(ref),
+ Ref = getr(?REF_KEY),
is_reference(Ref) %% started in new code
andalso
(true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})),
@@ -696,12 +733,32 @@ setopts(M, Sock) ->
portnr(gen_tcp, Sock) ->
inet:port(Sock);
-portnr(ssl, Sock) ->
- case ssl:sockname(Sock) of
+portnr(M, Sock) ->
+ case M:sockname(Sock) of
{ok, {_Addr, PortNr}} ->
{ok, PortNr};
{error, _} = No ->
No
- end;
-portnr(M, Sock) ->
- M:port(Sock).
+ end.
+
+%% sockname/2
+
+sockname(gen_tcp, Sock) ->
+ inet:sockname(Sock);
+sockname(M, Sock) ->
+ M:sockname(Sock).
+
+%% peername/2
+
+peername(gen_tcp, Sock) ->
+ inet:peername(Sock);
+peername(M, Sock) ->
+ M:peername(Sock).
+
+%% getstat/2
+
+getstat(gen_tcp, Sock) ->
+ inet:getstat(Sock);
+getstat(M, Sock) ->
+ M:getstat(Sock).
+%% Note that ssl:getstat/1 doesn't yet exist in R15B01.
diff --git a/lib/diameter/src/transport/diameter_transport.erl b/lib/diameter/src/transport/diameter_transport.erl
new file mode 100644
index 0000000000..ff4b6bbc6d
--- /dev/null
+++ b/lib/diameter/src/transport/diameter_transport.erl
@@ -0,0 +1,55 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_transport).
+
+%%
+%% This module implements a transport start function that
+%% evaluates its config argument.
+%%
+
+%% Transport start functions
+-export([start/3,
+ select/3,
+ eval/3]).
+
+%% start/3
+
+%% Call a start function in this module ...
+start(T, Svc, {F,A}) ->
+ start(T, Svc, {?MODULE, F, [A]});
+
+%% ... or some other.
+start(T, Svc, F) ->
+ diameter_lib:eval([F, T, Svc]).
+
+%% select/3
+%%
+%% A start function that whose config argument is expected to return a
+%% new start function.
+
+select(T, Svc, F) ->
+ start(T, Svc, diameter_lib:eval([F, T, Svc])).
+
+%% eval/3
+%%
+%% A start function that simply evaluates its config argument.
+
+eval(_, _, F) ->
+ diameter_lib:eval(F).
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 1659330a91..616fcca0c0 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -67,8 +67,13 @@ ERL_COMPILE_FLAGS += +warn_export_vars \
# Targets
# ----------------------------------------------------
+# Require success ...
all: opt
+# ... or not.
+any: opt
+ $(MAKE) -i $(SUITES)
+
run: $(SUITES)
debug opt: $(TARGET_FILES)
@@ -113,7 +118,7 @@ help:
@echo " Echo some relevant variables."
@echo ========================================
-.PHONY: all run clean debug docs help info opt realclean
+.PHONY: all any run clean debug docs help info opt realclean
# ----------------------------------------------------
# Special Targets
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index 54a161d606..ae128b8203 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -93,12 +93,12 @@
-define(fail(T), erlang:error({T, process_info(self(), messages)})).
--define(TIMEOUT, 2000).
+-define(TIMEOUT, 10000).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() -> [start,
start_services,
diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl
index 3b4c9706e0..4b792b5426 100644
--- a/lib/diameter/test/diameter_compiler_SUITE.erl
+++ b/lib/diameter/test/diameter_compiler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -339,7 +339,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 5}}].
+ [{timetrap, {minutes, 2}}].
all() ->
[format,
diff --git a/lib/diameter/test/diameter_ct.erl b/lib/diameter/test/diameter_ct.erl
index f8ee3dc1d7..ded50bf6c5 100644
--- a/lib/diameter/test/diameter_ct.erl
+++ b/lib/diameter/test/diameter_ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -25,15 +25,14 @@
-export([run/1]).
-%% ct:run_test/1 is currently documented as returning a list of test
-%% results ... but no. Instead it returns 'ok' regardless of whether
-%% or not the suite in question has failed testcases.
+%% The makefile looks for signs of failure so ignore the ct:run_test/1
+%% return value.
run([Suite]) ->
Start = info(),
- ok = ct:run_test([{suite, Suite},
- {logdir, "./log"},
- {auto_compile, false}]),
+ ct:run_test([{suite, Suite},
+ {logdir, "./log"},
+ {auto_compile, false}]),
info(Start , info()).
info() ->
diff --git a/lib/diameter/test/diameter_dict_SUITE.erl b/lib/diameter/test/diameter_dict_SUITE.erl
index 5cf8506d3f..3cc65c0257 100644
--- a/lib/diameter/test/diameter_dict_SUITE.erl
+++ b/lib/diameter/test/diameter_dict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -48,7 +48,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl
index 53398dd93e..ed31670031 100644
--- a/lib/diameter/test/diameter_failover_SUITE.erl
+++ b/lib/diameter/test/diameter_failover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -101,7 +101,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start,
diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
index 7f435a6b7a..2fde7b9fdb 100644
--- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -175,7 +175,8 @@ send(Sock, Id) ->
send_from_multiple_clients(_) ->
{S, Rs} = T = send_from_multiple_clients(8, 1024),
- {false, [], _} = {?FOREVER < S,
+ Max = ?FOREVER*1000,
+ {false, [], _} = {Max < S,
Rs -- [OI || {O,_} = OI <- Rs, is_integer(O)],
T}.
@@ -223,6 +224,11 @@ send_from_multiple_clients(_) ->
%% {134,100},
%% {117,98},
%% {149,125}]}
+%%
+%% This turns out to have been due to SCTP resends as a consequence of
+%% the listener having an insufficient recbuf. Increasing the size
+%% solves the problem.
+%%
send_from_multiple_clients(N, Sz)
when is_integer(N), 0 < N, is_integer(Sz), 0 < Sz ->
@@ -341,8 +347,15 @@ receive_what_was_sent(_Config) ->
%% open/0
open() ->
- gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary]).
+ open([]).
+
+%% open/1
+open(Opts) ->
+ gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary,
+ {recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16}
+ | Opts]).
+
%% assoc/1
assoc(Sock) ->
diff --git a/lib/diameter/test/diameter_reg_SUITE.erl b/lib/diameter/test/diameter_reg_SUITE.erl
index ec6a0ca731..4939019f7a 100644
--- a/lib/diameter/test/diameter_reg_SUITE.erl
+++ b/lib/diameter/test/diameter_reg_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -43,7 +43,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 70e1866791..f10d82bdf8 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -48,6 +48,7 @@
send_loop/1,
send_timeout_1/1,
send_timeout_2/1,
+ info/1,
disconnect/1,
stop_services/1,
stop/1]).
@@ -112,7 +113,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start,
@@ -136,7 +137,8 @@ tc() ->
send4,
send_loop,
send_timeout_1,
- send_timeout_2].
+ send_timeout_2,
+ info].
%% ===========================================================================
%% start/stop testcases
@@ -224,6 +226,9 @@ send_timeout(Tmo) ->
{'Re-Auth-Request-Type', ?AUTHORIZE_ONLY}],
call(Req, [{filter, realm}, {timeout, Tmo}]).
+info(_Config) ->
+ [] = ?util:info().
+
%% ===========================================================================
realm(Host) ->
diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl
index e7807fd360..8b7d8cb1b6 100644
--- a/lib/diameter/test/diameter_stats_SUITE.erl
+++ b/lib/diameter/test/diameter_stats_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -30,16 +30,17 @@
end_per_suite/1]).
%% testcases
--export([an/1,
- twa/1]).
+-export([reg/1,
+ incr/1,
+ read/1,
+ flush/1]).
-define(stat, diameter_stats).
--define(util, diameter_util).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
@@ -49,8 +50,10 @@ groups() ->
[{all, [], tc()}].
tc() ->
- [an,
- twa].
+ [reg,
+ incr,
+ read,
+ flush].
init_per_suite(Config) ->
ok = diameter:start(),
@@ -61,25 +64,62 @@ end_per_suite(_Config) ->
%% ===========================================================================
-an(_) ->
- Ref = {'_', make_ref()},
+reg(_) ->
+ Ref = '$1',
true = ?stat:reg(Ref),
- true = ?stat:reg(Ref), %% duplicate
- ok = ?stat:incr(x),
- ok = ?stat:incr(x, Ref),
- ok = ?stat:incr(y, 2),
- ok = ?stat:incr(y, Ref),
- %% Flushing a pid flushes even stats on the registered reference.
- [{x,2},{y,3}] = lists:sort(?stat:flush()),
- [] = ?stat:flush(Ref),
- [] = ?stat:flush().
-
-twa(_) ->
+ false = ?stat:reg(Ref). %% duplicate
+
+incr(_) ->
+ Ref = '_',
+ Ctr = x,
+ false = ?stat:incr(Ctr), %% not registered,
+ 1 = ?stat:incr(Ctr, Ref, 1), %% only pids need register
+ true = ?stat:reg(Ref),
+ spawn(fun() ->
+ true = ?stat:reg(Ref),
+ 2 = ?stat:incr(Ctr, self(), 2)
+ end),
+ ok = fold(Ctr, Ref, 3), %% folded
+ ?stat:flush([self(), Ref]).
+
+read(_) ->
+ Ref = make_ref(),
+ C1 = {a,b},
+ C2 = {b,a},
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(C1),
+ 1 = ?stat:incr(C2),
+ 2 = ?stat:incr(C1),
+ 7 = ?stat:incr(C1, Ref, 7),
+ Self = self(),
+ [{Ref, [{C1,7}]}, {Self, [{C1,2}, {C2,1}]}]
+ = lists:sort(?stat:read([self(), Ref, make_ref()])),
+ [] = ?stat:read([]),
+ [] = ?stat:read([make_ref()]),
+ ?stat:flush([self(), Ref, make_ref()]).
+
+flush(_) ->
Ref = make_ref(),
- ok = ?stat:incr(x, 8),
- ok = ?stat:incr(x, Ref, 7),
- %% Flushing a reference doesn't affect registered pids.
- [{x,7}] = ?stat:flush(Ref),
- [] = ?stat:flush(Ref),
- [{x,8}] = ?stat:flush(),
- [] = ?stat:flush().
+ Ctr = '_',
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(Ctr),
+ 3 = ?stat:incr(Ctr, self(), 2),
+ 2 = ?stat:incr(Ctr, Ref, 2),
+ Self = self(),
+ [{Self, [{Ctr, 3}]}] = ?stat:flush([self()]),
+ 1 = ?stat:incr(Ctr),
+ [{Ref, [{Ctr, 2}]}] = ?stat:flush([Ref]),
+ [{Self, [{Ctr, 1}]}] = ?stat:flush([self()]),
+ [] = ?stat:flush([self(), Ref]).
+
+%% ===========================================================================
+
+%% Keep incremented until a fold results in the specified value.
+fold(Ctr, Ref, N) ->
+ case ?stat:incr(Ctr, Ref, 0) of
+ N ->
+ ok;
+ M when M < N ->
+ erlang:yield(),
+ fold(Ctr, Ref, N)
+ end.
diff --git a/lib/diameter/test/diameter_sync_SUITE.erl b/lib/diameter/test/diameter_sync_SUITE.erl
index ab629fb1c1..457efab8ae 100644
--- a/lib/diameter/test/diameter_sync_SUITE.erl
+++ b/lib/diameter/test/diameter_sync_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -43,7 +43,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 85b953dc1a..6cc34b20c5 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -127,7 +127,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start_ssl,
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6eed8d3b5d..99b4fc7f63 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -159,7 +159,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start, start_services, add_transports, result_codes]
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index 0c42f955ad..890d24f6f8 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -35,7 +35,8 @@
lport/3,
listen/2, listen/3,
connect/3, connect/4,
- disconnect/4]).
+ disconnect/4,
+ info/0]).
%% common_test-specific
-export([write_priv/3,
@@ -262,7 +263,10 @@ listen(SvcName, Prot) ->
listen(SvcName, Prot, []).
listen(SvcName, Prot, Opts) ->
- add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}).
+ SvcName = diameter:service_info(SvcName, name), %% assert
+ Ref = add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}),
+ true = transport(SvcName, Ref), %% assert
+ Ref.
%% ---------------------------------------------------------------------------
%% connect/2-3
@@ -275,15 +279,22 @@ connect(Client, Prot, LRef) ->
connect(Client, Prot, LRef, Opts) ->
[PortNr] = lport(Prot, LRef, 20),
- Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ Client = diameter:service_info(Client, name), %% assert
true = diameter:subscribe(Client),
+ Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ true = transport(Client, Ref), %% assert
+
ok = receive
{diameter_event, Client, {up, Ref, _, _, _}} -> ok
- after 2000 ->
+ after 10000 ->
{Client, Prot, PortNr, process_info(self(), messages)}
end,
Ref.
+transport(SvcName, Ref) ->
+ [Ref] == [R || [{ref, R} | _] <- diameter:service_info(SvcName, transport),
+ R == Ref].
+
%% ---------------------------------------------------------------------------
%% disconnect/4
%%
@@ -295,7 +306,7 @@ disconnect(Client, Ref, Server, LRef) ->
ok = diameter:remove_transport(Client, Ref),
ok = receive
{diameter_event, Server, {down, LRef, _, _}} -> ok
- after 2000 ->
+ after 10000 ->
{Client, Ref, Server, LRef, process_info(self(), messages)}
end.
@@ -320,3 +331,17 @@ opts(listen) ->
[];
opts(PortNr) ->
[{raddr, ?ADDR}, {rport, PortNr}].
+
+%% ---------------------------------------------------------------------------
+%% info/0
+
+info() ->
+ [_|_] = Svcs = diameter:services(), %% assert
+ run([[fun info/1, S] || S <- Svcs]).
+
+info(S) ->
+ [_|_] = Keys = diameter:service_info(S, keys),
+ [] = run([[fun info/2, K, S] || K <- Keys]).
+
+info(Key, SvcName) ->
+ [{Key, _}] = diameter:service_info(SvcName, [Key]).
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index f6dc786417..c69133a178 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.1
+DIAMETER_VSN = 1.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)"
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 1ef73da1be..fbb77b6a42 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1483,6 +1483,8 @@ type(erlang, statistics, 1, Xs) ->
t_tuple([t_non_neg_integer(), t_integer(0)]);
['wall_clock'] ->
t_tuple([t_non_neg_integer(), t_integer(0)]);
+ ['scheduler_wall_time'] ->
+ t_list(t_tuple([t_integer(), t_number(), t_number()]));
List when is_list(List) ->
T_statistics_1;
unknown ->
@@ -1532,6 +1534,8 @@ type(erlang, system_flag, 2, Xs) ->
t_sequential_tracer();
['trace_control_word'] ->
t_integer();
+ ['scheduler_wall_time'] ->
+ t_boolean();
List when is_list(List) ->
T_system_flag_2;
unknown ->
@@ -3901,6 +3905,7 @@ arg_types(erlang, statistics, 1) ->
t_atom('reductions'),
t_atom('run_queue'),
t_atom('runtime'),
+ t_atom('scheduler_wall_time'),
t_atom('wall_clock')])];
arg_types(erlang, subtract, 2) ->
arg_types(erlang, '--', 2);
@@ -3925,6 +3930,7 @@ arg_types(erlang, system_flag, 2) ->
t_atom('trace_control_word'),
%% 'internal_cpu_topology' is an undocumented internal feature.
t_atom('internal_cpu_topology'),
+ t_atom('scheduler_wall_time'),
t_integer()]),
t_sup([t_integer(),
%% 'cpu_topology'
@@ -3940,6 +3946,9 @@ arg_types(erlang, system_flag, 2) ->
%% The following two are for 'multi_scheduling'
t_atom('block'),
t_atom('unblock'),
+ %% For 'scheduler_wall_time'
+ t_atom('true'),
+ t_atom('false'),
%% The following is for 'internal_cpu_topology'
t_internal_cpu_topology()])];
arg_types(erlang, system_info, 1) ->
@@ -4267,7 +4276,7 @@ arg_types(hipe_bifs, ref_get, 1) ->
arg_types(hipe_bifs, ref_set, 2) ->
[t_hiperef(), t_immediate()];
arg_types(hipe_bifs, remove_refs_from, 1) ->
- [t_mfa()];
+ [t_sup([t_mfa(), t_atom('all')])];
arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 410e29d269..1579735773 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -275,8 +275,8 @@
-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
| ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag
- | ?opaque_tag | ?product_tag | ?tuple_tag | ?tuple_set_tag
- | ?union_tag | ?var_tag.
+ | ?opaque_tag | ?product_tag | ?remote_tag
+ | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
-define(float_qual, float).
-define(integer_qual, integer).
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index a44171b2f5..ecf1c77abc 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -72,12 +72,11 @@
end).
%%-----------------------------------------------------------------------
-%% Exported types
+%% Types
%%-----------------------------------------------------------------------
-type hipe_beam_to_icode_ret() :: [{mfa(),#icode{}}].
-
%%-----------------------------------------------------------------------
%% Internal data structures
%%-----------------------------------------------------------------------
@@ -315,19 +314,19 @@ trans_fun([{call_ext_last,_N,{extfunc,M,F,A},_}|Instructions], Env) ->
%%--- bif0 ---
trans_fun([{bif,BifName,nofail,[],Reg}|Instructions], Env) ->
BifInst = trans_bif0(BifName,Reg),
- [hipe_icode:mk_comment({bif0,BifName}),BifInst|trans_fun(Instructions,Env)];
+ [BifInst|trans_fun(Instructions,Env)];
%%--- bif1 ---
trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(1,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif1,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- bif2 ---
trans_fun([{bif,BifName,{f,Lbl},[_,_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(2,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif2,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- bif3 ---
trans_fun([{bif,BifName,{f,Lbl},[_,_,_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(3,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif3,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- allocate
trans_fun([{allocate,StackSlots,_}|Instructions], Env) ->
trans_allocate(StackSlots) ++ trans_fun(Instructions,Env);
@@ -816,7 +815,7 @@ trans_fun([{test,bs_test_tail2,{f,Lbl},[Ms,Numbits]}| Instructions], Env) ->
trans_op_call({hipe_bs_primop,{bs_test_tail,Numbits}},
Lbl, [MsVar], [], Env, Instructions);
%%--------------------------------------------------------------------
-%% New bit syntax instructions added in February 2004 (R10B).
+%% bit syntax instructions added in February 2004 (R10B).
%%--------------------------------------------------------------------
trans_fun([{bs_init2,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}|
Instructions], Env) ->
@@ -1031,7 +1030,7 @@ trans_fun([{arithfbif,fnegate,Lab,[SrcR],DestR}|Instructions], Env) ->
trans_fun([{arithbif,'-',Lab,[{float,0.0},SrcR],DestR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% New apply instructions added in April 2004 (R10B).
+%% apply instructions added in April 2004 (R10B).
%%--------------------------------------------------------------------
trans_fun([{apply,Arity}|Instructions], Env) ->
BeamArgs = extract_fun_args(Arity+2), %% +2 is for M and F
@@ -1047,21 +1046,21 @@ trans_fun([{apply_last,Arity,_N}|Instructions], Env) -> % N is StackAdjustment?
hipe_icode:mk_enter_primop(#apply_N{arity=Arity}, [M,F|Args])
| trans_fun(Instructions,Env)];
%%--------------------------------------------------------------------
-%% New test instruction added in April 2004 (R10B).
+%% test for boolean added in April 2004 (R10B).
%%--------------------------------------------------------------------
%%--- is_boolean ---
trans_fun([{test,is_boolean,{f,Lbl},[Arg]}|Instructions], Env) ->
{Code,Env1} = trans_type_test(boolean,Lbl,Arg,Env),
[Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
-%% New test instruction added in June 2005 for R11
+%% test for function with specific arity added in June 2005 (R11).
%%--------------------------------------------------------------------
%%--- is_function2 ---
trans_fun([{test,is_function2,{f,Lbl},[Arg,Arity]}|Instructions], Env) ->
{Code,Env1} = trans_type_test2(function2,Lbl,Arg,Arity,Env),
[Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
-%% New garbage-collecting BIFs added in January 2006 for R11B.
+%% garbage collecting BIFs added in January 2006 (R11B).
%%--------------------------------------------------------------------
trans_fun([{gc_bif,'-',Fail,_Live,[SrcR],DstR}|Instructions], Env) ->
%% Unary minus. Change this to binary minus.
@@ -1079,21 +1078,21 @@ trans_fun([{gc_bif,Name,Fail,_Live,SrcRs,DstR}|Instructions], Env) ->
trans_fun([{bif,Name,Fail,SrcRs,DstR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% New test instruction added in July 2007 for R12.
+%% test for bitstream added in July 2007 (R12).
%%--------------------------------------------------------------------
%%--- is_bitstr ---
trans_fun([{test,is_bitstr,{f,Lbl},[Arg]}|Instructions], Env) ->
{Code,Env1} = trans_type_test(bitstr, Lbl, Arg, Env),
[Code | trans_fun(Instructions, Env1)];
%%--------------------------------------------------------------------
-%% New stack triming instruction added in October 2007 for R12.
+%% stack triming instruction added in October 2007 (R12).
%%--------------------------------------------------------------------
trans_fun([{trim,N,NY}|Instructions], Env) ->
%% trim away N registers leaving NY registers
Moves = trans_trim(N, NY),
Moves ++ trans_fun(Instructions, Env);
%%--------------------------------------------------------------------
-%% New line/1 instruction in R15.
+%% line instruction added in Fall 2012 (R15).
%%--------------------------------------------------------------------
trans_fun([{line,_}|Instructions], Env) ->
trans_fun(Instructions,Env);
@@ -1297,7 +1296,7 @@ trans_bin([{bs_put_integer,{f,Lbl},Size,Unit,{field_flags,Flags0},Source}|
SrcInstrs ++ trans_bin_call({hipe_bs_primop, Name},
Lbl, [Src|Args], [Offset], Base, Offset, Env2, Instructions);
%%----------------------------------------------------------------
-%% New binary construction instructions added in R12B-5 (Fall 2008).
+%% binary construction instructions added in Fall 2008 (R12B-5).
%%----------------------------------------------------------------
trans_bin([{bs_put_utf8,{f,Lbl},_FF,A3}|Instructions], Base, Offset, Env) ->
Src = trans_arg(A3),
@@ -1348,7 +1347,7 @@ trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, X, Instructions, Env) ->
Lbl, [Dst,MsVar], [MsVar], Env1, Instructions).
%%-----------------------------------------------------------------------
-%% trans_arith(Op, SrcVars, Des, Lab, Env) -> { Icode, NewEnv }
+%% trans_arith(Op, SrcVars, Des, Lab, Env) -> {Icode, NewEnv}
%% A failure label of type {f,0} means in a body.
%% A failure label of type {f,L} where L>0 means in a guard.
%% Within a guard a failure should branch to the next guard and
@@ -1454,7 +1453,7 @@ clone_dst(Dest) ->
%%-----------------------------------------------------------------------
-%% trans_type_test(Test, Lbl, Arg, Env) -> { Icode, NewEnv }
+%% trans_type_test(Test, Lbl, Arg, Env) -> {Icode, NewEnv}
%% Handles all unary type tests like is_integer etc.
%%-----------------------------------------------------------------------
@@ -1466,7 +1465,7 @@ trans_type_test(Test, Lbl, Arg, Env) ->
{[Move,I,True],Env1}.
%%
-%% This handles binary type tests. Currently, the only such is the new
+%% This handles binary type tests. Currently, the only such is the
%% is_function/2 BIF.
%%
trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
@@ -1479,7 +1478,7 @@ trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
%%-----------------------------------------------------------------------
%% trans_puts(Code, Environment) ->
-%% { Movs, Code, Vars, NewEnv }
+%% {Movs, Code, Vars, NewEnv}
%%-----------------------------------------------------------------------
trans_puts(Code, Env) ->
diff --git a/lib/hipe/icode/hipe_icode.hrl b/lib/hipe/icode/hipe_icode.hrl
index 65deaf6d7c..3b21276209 100644
--- a/lib/hipe/icode/hipe_icode.hrl
+++ b/lib/hipe/icode/hipe_icode.hrl
@@ -64,9 +64,9 @@
-type icode_if_op() :: '>' | '<' | '>=' | '=<' | '=:=' | '=/=' | '==' | '/='
| 'fixnum_eq' | 'fixnum_neq' | 'fixnum_lt'
| 'fixnum_le' | 'fixnum_ge' | 'fixnum_gt'
- | 'suspend_msg_timeout'.
+ | 'op_exact_eqeq_2' | 'suspend_msg_timeout'.
--type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitrst' | 'boolean'
+-type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitstr' | 'boolean'
| 'cons' | 'constant' | 'fixnum' | 'float'
| 'function' | 'function2' | 'integer' | 'list' | 'nil'
| 'number' | 'pid' | 'port' | 'reference' | 'tuple'
@@ -88,7 +88,7 @@
-type icode_call_type() :: 'local' | 'primop' | 'remote'.
-type icode_exit_class() :: 'error' | 'exit' | 'rethrow' | 'throw'.
--type icode_comment_text() :: atom() | string() | {atom(), term()}.
+-type icode_comment_text() :: atom() | string().
-type icode_info() :: [{'arg_types', [erl_types:erl_type()]}].
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index f98d859822..046949d2f2 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -79,13 +79,13 @@
-import(erl_types, [number_min/1, number_max/1,
t_any/0, t_atom/1, t_atom/0, t_atom_vals/1,
t_binary/0, t_bitstr/0, t_bitstr_base/1, t_bitstr_unit/1,
- t_boolean/0, t_cons/0, t_constant/0,
+ t_boolean/0, t_cons/0,
t_float/0, t_from_term/1, t_from_range/2,
t_fun/0, t_fun/1, t_fun/2, t_fun_args/1, t_fun_arity/1,
t_inf/2, t_inf_lists/2, t_integer/0,
t_integer/1, t_is_atom/1, t_is_any/1,
t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1, t_is_boolean/1,
- t_is_fixnum/1, t_is_cons/1, t_is_constant/1,
+ t_is_fixnum/1, t_is_cons/1,
t_is_maybe_improper_list/1, t_is_equal/2, t_is_float/1,
t_is_fun/1, t_is_integer/1, t_is_non_neg_integer/1,
t_is_number/1, t_is_matchstate/1,
@@ -587,7 +587,6 @@ do_type(I, Info, Var) ->
TrueLab = hipe_icode:type_true_label(I),
FalseLab = hipe_icode:type_false_label(I),
None = t_none(),
-
case lookup(Var, Info) of
None ->
[{TrueLab, Info}, {FalseLab, Info}];
@@ -1699,7 +1698,6 @@ lookup_list0([H|T], Info, Acc) ->
lookup_list0([], _, Acc) ->
lists:reverse(Acc).
-
%% safe_lookup treats anything that is neither in the map nor a
%% constant as t_any(). Use this during transformations.
diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
index df5e2b0077..c93f603826 100644
--- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl
+++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
@@ -19,6 +19,7 @@
%%
-module(hipe_rtl_to_sparc).
+
-export([translate/1]).
-include("../rtl/hipe_rtl.hrl").
@@ -142,8 +143,7 @@ mk_fload_rr(Base1, Base2, Dst) ->
mk_fload_ii(Base1, Base2, Dst) ->
io:format("~w: RTL fload with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
- mk_set(Base1, Tmp,
- mk_fload_ri(Tmp, Base2, Dst)).
+ mk_set(Base1, Tmp, mk_fload_ri(Tmp, Base2, Dst)).
mk_fload_ri(Base, Disp, Dst) ->
hipe_sparc:mk_fload(Base, Disp, Dst, 'new').
@@ -239,8 +239,7 @@ mk_alu_ii(XAluOp, Src1, Src2, Dst) ->
io:format("~w: ALU with two immediates (~w ~w ~w ~w)\n",
[?MODULE, XAluOp, Src1, Src2, Dst]),
Tmp = new_untagged_temp(),
- mk_set(Src1, Tmp,
- mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
+ mk_set(Src1, Tmp, mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
case xaluop_commutes(XAluOp) of
@@ -249,8 +248,7 @@ mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
true};
_ ->
Tmp = new_untagged_temp(),
- {mk_set(Src1, Tmp,
- mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
+ {mk_set(Src1, Tmp, mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
false}
end.
@@ -274,8 +272,7 @@ mk_arith_ri(XAluOp, Src1, Src2, Dst) when is_integer(Src2) ->
mk_alu_rs(XAluOp, Src1, hipe_sparc:mk_simm13(Src2), Dst);
true ->
Tmp = new_untagged_temp(),
- mk_set(Src2, Tmp,
- mk_alu_rs(XAluOp, Src1, Tmp, Dst))
+ mk_set(Src2, Tmp, mk_alu_rs(XAluOp, Src1, Tmp, Dst))
end.
mk_alu_rs(XAluOp, Src1, Src2, Dst) ->
@@ -623,8 +620,7 @@ mk_move(Src, Dst, Tail) ->
conv_return(I, Map, Data) ->
%% TODO: multiple-value returns
{[Arg], Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map),
- I2 = mk_move(Arg, hipe_sparc:mk_rv(),
- [hipe_sparc:mk_pseudo_ret()]),
+ I2 = mk_move(Arg, hipe_sparc:mk_rv(), [hipe_sparc:mk_pseudo_ret()]),
{I2, Map0, Data}.
conv_store(I, Map, Data) ->
@@ -648,8 +644,7 @@ mk_store(StOp, Src, Base1, Base2) ->
mk_store2(StOp, Src, Base1, Base2);
_ ->
Tmp = new_untagged_temp(),
- mk_set(Src, Tmp,
- mk_store2(StOp, Tmp, Base1, Base2))
+ mk_set(Src, Tmp, mk_store2(StOp, Tmp, Base1, Base2))
end.
mk_store2(StOp, Src, Base1, Base2) ->
@@ -674,8 +669,7 @@ conv_switch(I, Map, Data) ->
[] ->
hipe_consttab:insert_block(Data, word, LMap);
SortOrder ->
- hipe_consttab:insert_sorted_block(
- Data, word, LMap, SortOrder)
+ hipe_consttab:insert_sorted_block(Data, word, LMap, SortOrder)
end,
%% no immediates allowed here
{IndexR, Map1} = conv_dst(hipe_rtl:switch_src(I), Map),
@@ -722,7 +716,7 @@ conv_aluop(RtlAluOp) ->
xaluop_commutes(XAluOp) ->
case XAluOp of
- 'cmp' -> true;
+ %% 'cmp' -> true;
'cmpcc' -> true;
'add' -> true;
'addcc' -> true;
@@ -739,16 +733,16 @@ xaluop_commutes(XAluOp) ->
'sll' -> false;
'srl' -> false;
'sra' -> false;
- 'sllx' -> false;
- 'srlx' -> false;
- 'srax' -> false;
+ %% 'sllx' -> false;
+ %% 'srlx' -> false;
+ %% 'srax' -> false;
'ldsb' -> true;
'ldsh' -> true;
- 'ldsw' -> true;
+ %% 'ldsw' -> true;
'ldub' -> true;
'lduh' -> true;
- 'lduw' -> true;
- 'ldx' -> true
+ 'lduw' -> true
+ %% 'ldx' -> true
end.
%%% Check if an extended SPARC AluOp is a shift.
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 6fe05dec80..923213d34d 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -55,7 +55,7 @@
status_line, % {Version, StatusCode, ReasonPharse}
headers, % #http_response_h{}
body, % binary()
- mfa, % {Moduel, Function, Args}
+ mfa, % {Module, Function, Args}
pipeline = queue:new(), % queue()
keep_alive = queue:new(), % queue()
status, % undefined | new | pipeline | keep_alive | close | ssl_tunnel
diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl
index 60ab326a20..a34435e0e8 100644
--- a/lib/inets/src/http_server/httpd_log.erl
+++ b/lib/inets/src/http_server/httpd_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -36,8 +36,8 @@
AuthUser :: string(),
Date :: string(),
StatusCode :: pos_integer(),
- Size :: pos_integer() | string()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ Size :: 0 | pos_integer() | string()) ->
+ {Log :: atom() | pid(), Entry :: string()} | term() .
access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr)
when is_list(SizeStr) ->
@@ -69,7 +69,7 @@ access_entry(Log, NoLog,
Info :: #mod{},
Date :: string(),
Reason :: term()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
error_entry(Log, NoLog,
#mod{config_db = ConfigDB,
@@ -87,7 +87,7 @@ error_entry(Log, NoLog,
ConfigDB :: term(),
Date :: string(),
ErrroStr :: string()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) ->
MakeEntry = fun() -> io_lib:format("[~s], ~s~n", [Date, ErrorStr]) end,
@@ -99,7 +99,7 @@ error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) ->
ConfigDB :: term(),
Date :: string(),
Reason :: term()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
security_entry(Log, NoLog, #mod{config_db = ConfigDB}, Date, Reason) ->
MakeEntry = fun() -> io_lib:format("[~s] ~s~n", [Date, Reason]) end,
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index ee687511a3..214e61cc00 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -91,7 +91,7 @@
of the additional library directories will override modules with
the same name in OTP, except for modules in Kernel and
STDLIB.</p>
- <p>The environment variable <c>ERL_LIBS</c> (if defined) shold contain
+ <p>The environment variable <c>ERL_LIBS</c> (if defined) should contain
a colon-separated (for Unix-like systems) or semicolon-separated
(for Windows) list of additional libraries.</p>
<p>Example: On an Unix-like system, <c>ERL_LIBS</c> could be set to
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 393064fbb5..928e3bea82 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -36,6 +36,78 @@
section is the version number of Megaco.</p>
+ <section><title>Megaco 3.16.0.2</title>
+
+ <p>Version 3.16.0.2 supports code replacement in runtime from/to
+ version 3.16.0.1, 3.16, 3.15.1.1, 3.15.1 and 3.15.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Allow whitespaces in installation path. </p>
+ <p>It is now possible to give configure and make an
+ installation/release path with whitespaces in it. </p>
+ <p>Own Id: OTP-10107</p>
+ </item>
+
+ <item>
+ <p>Fix parallel make for behaviours. </p>
+ </item>
+
+ <item>
+ <p>Removed use of deprecated system flag,
+ <c>global_haeps_size</c>, in the measurement tool
+ <c>mstone1</c>. </p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+
+ <p>-</p>
+
+ <!--
+ <list type="bulleted">
+ <item>
+ <p>Fixing miscellaneous things detected by dialyzer. </p>
+ <p>Own Id: OTP-9075</p>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Due to the change in the flex driver API,
+ we may no longer be able to build and/or use
+ the flex driver without reentrant support. </p>
+ <p>Own Id: OTP-9795</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ </section> <!-- 3.16.0.2 -->
+
+
<section><title>Megaco 3.16.0.1</title>
<p>Version 3.16.0.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 7f89fa8bc2..a7b38eb107 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -145,10 +145,17 @@
%% |
%% v
%% 3.16.0.1
+%% |
+%% v
+%% 3.16.0.2
%%
%%
{"%VSN%",
[
+ {"3.16.0.1",
+ [
+ ]
+ },
{"3.16",
[
]
@@ -170,6 +177,10 @@
}
],
[
+ {"3.16.0.1",
+ [
+ ]
+ },
{"3.16",
[
]
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 11a951a23e..5e72ade769 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.16.0.1
+MEGACO_VSN = 3.16.0.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 3b924d46cf..4077f8371a 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -19,7 +19,7 @@
-module(observer_lib).
-export([get_wx_parent/1,
- display_info_dialog/1, user_term/3,
+ display_info_dialog/1, user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, stop_timer/1,
display_info/2, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
@@ -347,6 +347,58 @@ user_term(Parent, Title, Default) ->
cancel
end.
+user_term_multiline(Parent, Title, Default) ->
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, Title,
+ [{style, ?wxDEFAULT_DIALOG_STYLE bor
+ ?wxRESIZE_BORDER}]),
+ Panel = wxPanel:new(Dialog),
+
+ TextCtrl = wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{value, Default},
+ {style, ?wxDEFAULT bor ?wxTE_MULTILINE}]),
+ Line = wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]),
+
+ Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+
+ InnerSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(InnerSizer, TextCtrl,
+ [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]),
+ wxSizer:add(InnerSizer, Line,
+ [{flag, ?wxEXPAND},{proportion, 0},{border, 5}]),
+ wxPanel:setSizer(Panel, InnerSizer),
+
+ TopSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(TopSizer, Panel,
+ [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]),
+ wxSizer:add(TopSizer, Buttons,
+ [{flag, ?wxEXPAND bor ?wxBOTTOM bor ?wxRIGHT},{border, 10}]),
+
+ % calculate the size of TopSizer when the whole user_term
+ % fits in the TextCtrl
+ DC = wxClientDC:new(Panel),
+ W = wxDC:getCharWidth(DC),
+ H = wxDC:getCharHeight(DC),
+ {EW, EH} = wxDC:getMultiLineTextExtent(DC, Default),
+ wxSizer:setItemMinSize(InnerSizer, 0, EW+2*W, EH+H),
+ TopSize = wxSizer:getMinSize(TopSizer),
+ % reset min size of TextCtrl to 40 chararacters * 4 lines
+ wxSizer:setItemMinSize(InnerSizer, 0, 40*W, 4*H),
+
+ wxWindow:setSizerAndFit(Dialog, TopSizer),
+ wxSizer:setSizeHints(TopSizer, Dialog),
+
+ wxWindow:setClientSize(Dialog, TopSize),
+
+ case wxDialog:showModal(Dialog) of
+ ?wxID_OK ->
+ Str = wxTextCtrl:getValue(TextCtrl),
+ wxDialog:destroy(Dialog),
+ parse_string(ensure_last_is_dot(Str));
+ ?wxID_CANCEL ->
+ wxDialog:destroy(Dialog),
+ cancel
+ end.
+
parse_string(Str) ->
try
Tokens = case erl_scan:string(Str) of
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 8eeffb7f91..c41f0f006a 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -220,8 +220,8 @@ search_area(Parent) ->
search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}.
edit(Index, #state{pid=Pid, frame=Frame}) ->
- Str = get_row(Pid, Index, all),
- case observer_lib:user_term(Frame, "Edit object:", Str) of
+ Str = get_row(Pid, Index, all_multiline),
+ case observer_lib:user_term_multiline(Frame, "Edit object:", Str) of
cancel -> ok;
{ok, Term} -> Pid ! {edit, Index, Term};
Err = {error, _} -> self() ! Err
@@ -597,7 +597,7 @@ keysort(Col, Table) ->
lists:sort(Sort, Table).
search([Str, Row, Dir0, CaseSens],
- S=#holder{parent=Parent, table=Table}) ->
+ S=#holder{parent=Parent, table=Table0}) ->
Opt = case CaseSens of
true -> [];
false -> [caseless]
@@ -608,29 +608,35 @@ search([Str, Row, Dir0, CaseSens],
end,
Res = case re:compile(Str, Opt) of
{ok, Re} ->
+ Table =
+ case Dir0 of
+ true ->
+ lists:nthtail(Row, Table0);
+ false ->
+ lists:reverse(lists:sublist(Table0, Row+1))
+ end,
search(Row, Dir, Re, Table);
{error, _} -> false
end,
Parent ! {self(), Res},
S#holder{search=Res}.
-search(Row, Dir, Re, Table) ->
- Res = try lists:nth(Row+1, Table) of
- [Term|_] ->
- Str = format(Term),
- re:run(Str, Re)
- catch _:_ -> no_more
- end,
+search(Row, Dir, Re, [ [Term|_] |Table]) ->
+ Str = format(Term),
+ Res = re:run(Str, Re),
case Res of
nomatch -> search(Row+Dir, Dir, Re, Table);
- no_more -> false;
{match,_} -> Row
- end.
+ end;
+search(_, _, _, []) ->
+ false.
get_row(From, Row, Col, Table) ->
case lists:nth(Row+1, Table) of
[Object|_] when Col =:= all ->
From ! {self(), format(Object)};
+ [Object|_] when Col =:= all_multiline ->
+ From ! {self(), io_lib:format("~p", [Object])};
[Object|_] when tuple_size(Object) >= Col ->
From ! {self(), format(element(Col, Object))};
_ ->
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 9a4e2d130e..2567a72999 100644
--- a/lib/reltool/doc/src/reltool.xml
+++ b/lib/reltool/doc/src/reltool.xml
@@ -144,7 +144,7 @@
value <c>include</c> implies that all applications and
escripts that do not have any explicit <c>incl_cond</c>
setting will be included. <c>exclude</c> implies that all
- applications and escripts) that do not have any explicit
+ applications and escripts that do not have any explicit
<c>incl_cond</c> setting will be excluded.</p>
</item>
@@ -221,6 +221,52 @@
system.</p>
</item>
+ <tag><c>excl_lib</c></tag>
+ <item>
+ <warning><p>This option is experimental.</p></warning>
+ <p>If the <c>excl_lib</c> option is set to <c>otp_root</c>
+ then reltool will not copy anything from the Erlang/OTP
+ installation ($OTP_ROOT) into the target structure. The goal
+ is to create a "slim" release which can be used together with
+ an existing Erlang/OTP installation. The target structure will
+ therefore only contain a <c>lib</c> directory with the
+ applications that were found outside of $OTP_ROOT (typically
+ your own applications), and a <c>releases</c> directory with
+ the generated <c>.rel,</c> <c>.script</c> and <c>.boot</c>
+ files.</p>
+
+ <p>When starting this release, three things must be specified:</p>
+ <taglist>
+ <tag><b>Which <c>releases</c> directory to use</b></tag>
+ <item>Tell the release handler to use the <c>releases</c>
+ directory in our target structure instead of
+ <c>$OTP_ROOT/releases</c>. This is done by setting the SASL
+ environment variable <c>releases_dir</c>, either from the
+ command line (<c>-sasl releases_dir
+ &lt;target-dir&gt;/releases</c>) or in
+ <c>sys.config</c>.</item>
+
+ <tag><b>Which boot file to use</b></tag>
+ <item>The default boot file is <c>$OTP_ROOT/bin/start</c>,
+ but in this case we need to specify a boot file from our
+ target structure, typically
+ <c>&lt;target-dir&gt;/releases/&lt;vsn&gt;/&lt;RelName&gt;</c>. This
+ is done with the <c>-boot</c> command line option to
+ <c>erl</c></item>
+
+ <tag><b>The location of our applications</b></tag>
+ <item>The generated .script (and .boot) file uses the
+ environment variable <c>$RELTOOL_EXT_LIB</c> as prefix for
+ the paths to all applications. The <c>-boot_var</c> option
+ to <c>erl</c> can be used for specifying the value of this
+ variable, typically <c>-boot_var RELTOOL_EXT_LIB
+ &lt;target-dir&gt;/lib</c>.</item>
+ </taglist>
+
+ <p>Example:</p>
+ <p><code>erl -sasl releases_dir \"mytarget/releases\" -boot mytarget/releases/1.0/myrel -boot_var RELTOOL_EXT_LIB mytarget/lib</code></p>
+ </item>
+
<tag><c>incl_sys_filters</c></tag>
<item>
<p>This parameter normally contains a list of regular
@@ -452,6 +498,7 @@ app() = {vsn, app_vsn()}
| {incl_cond, incl_cond()}
| {debug_info, debug_info()}
| {app_file, app_file()}
+ | {excl_lib, excl_lib()}
| {incl_sys_filters, incl_sys_filters()}
| {excl_sys_filters, excl_sys_filters()}
| {incl_app_filters, incl_app_filters()}
@@ -477,6 +524,7 @@ escript() = {incl_cond, incl_cond()}
escript_file() = file()
excl_app_filters() = regexps()
excl_archive_filters() = regexps()
+excl_lib() = otp_root
excl_sys_filters() = regexps()
file() = string()
incl_app() = app_name()
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 26dcd82447..f0d8b38519 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -79,6 +79,7 @@
| {debug_info, debug_info()}
| {app_file, app_file()}
| {profile, profile()}
+ | {excl_lib, excl_lib()}
| {incl_sys_filters, incl_sys_filters()}
| {excl_sys_filters, excl_sys_filters()}
| {incl_app_filters, incl_app_filters()}
@@ -123,6 +124,7 @@
-type incl_defaults() :: boolean().
-type incl_derived() :: boolean().
-type status() :: missing | ok.
+-type excl_lib() :: otp_root.
-record(common,
{
@@ -233,6 +235,7 @@
rels :: [#rel{}],
emu_name :: emu_name(),
profile :: profile(),
+ excl_lib :: excl_lib(),
incl_sys_filters :: [#regexp{}],
excl_sys_filters :: [#regexp{}],
incl_app_filters :: [#regexp{}],
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 034a42e1e2..3d1d7e54bf 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1408,6 +1408,8 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals]) ->
ExclApp,
Sys#sys.excl_app_filters),
embedded_app_type = AppType};
+ excl_lib when Val =:= otp_root ->
+ Sys#sys{excl_lib=Val};
incl_sys_filters ->
Sys#sys{incl_sys_filters =
dec_re(Key, Val, Sys#sys.incl_sys_filters)};
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index e3a7b02143..c39ed0ecd5 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -687,6 +687,8 @@ strip_name_ebin(Dir, Name, Vsn) ->
case lists:reverse(Dir) of
["ebin", Name | D] -> {ok, lists:reverse(D)};
["ebin", FullName | D] -> {ok, lists:reverse(D)};
+ [Name | D] -> {ok, lists:reverse(D)};
+ [FullName | D] -> {ok, lists:reverse(D)};
_ -> false
end.
@@ -733,8 +735,20 @@ do_spec_rel_files(#rel{name = RelName} = Rel, Sys) ->
BootFile = RelName ++ ".boot",
MergedApps = merge_apps(Rel, Sys),
GenRel = do_gen_rel(Rel, Sys, MergedApps),
+ Variables =
+ case Sys#sys.excl_lib of
+ otp_root ->
+ %% All applications that are fetched from somewhere
+ %% other than $OTP_ROOT/lib will get $RELTOOL_EXT_LIB
+ %% as path prefix in the .script file.
+ [{"RELTOOL_EXT_LIB",LibDir} || LibDir <- Sys#sys.lib_dirs] ++
+ [{"RELTOOL_EXT_LIB",filename:dirname(AppLibDir)} ||
+ #app{active_dir=AppLibDir,use_selected_vsn=dir}
+ <- MergedApps];
+ _ ->
+ []
+ end,
PathFlag = true,
- Variables = [],
{ok, Script} = do_gen_script(Rel, Sys, MergedApps, PathFlag, Variables),
{ok, BootBin} = gen_boot(Script),
Date = date(),
@@ -771,29 +785,34 @@ gen_spec(Sys) ->
end.
do_gen_spec(#sys{root_dir = RootDir,
+ excl_lib = ExclLib,
incl_sys_filters = InclRegexps,
excl_sys_filters = ExclRegexps,
relocatable = Relocatable,
apps = Apps} = Sys) ->
- {create_dir, _, SysFiles} = spec_dir(RootDir),
- {ExclRegexps2, SysFiles2} =
- strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps),
RelFiles = spec_rel_files(Sys),
- {InclRegexps2, BinFiles} =
- spec_bin_files(Sys, SysFiles, SysFiles2, RelFiles, InclRegexps),
+ {SysFiles, InclRegexps2, ExclRegexps2, Mandatory} =
+ case ExclLib of
+ otp_root ->
+ {[],InclRegexps,ExclRegexps,["lib"]};
+ _ ->
+ {create_dir, _, SF} = spec_dir(RootDir),
+ {ER2, SF2} = strip_sys_files(Relocatable, SF, Apps, ExclRegexps),
+ {IR2, BinFiles} =
+ spec_bin_files(Sys, SF, SF2, RelFiles, InclRegexps),
+ SF3 = [{create_dir, "bin", BinFiles}] ++ SF2,
+ {SF3,IR2,ER2,["bin","erts","lib"]}
+ end,
LibFiles = spec_lib_files(Sys),
{BootVsn, StartFile} = spec_start_file(Sys),
- SysFiles3 =
- [
- {create_dir, "releases",
+ SysFiles2 =
+ [{create_dir, "releases",
[StartFile,
- {create_dir,BootVsn, RelFiles}]},
- {create_dir, "bin", BinFiles}
- ] ++ SysFiles2,
- SysFiles4 = filter_spec(SysFiles3, InclRegexps2, ExclRegexps2),
- SysFiles5 = SysFiles4 ++ [{create_dir, "lib", LibFiles}],
- check_sys(["bin", "erts", "lib"], SysFiles5),
- SysFiles5.
+ {create_dir,BootVsn, RelFiles}]}] ++ SysFiles,
+ SysFiles3 = filter_spec(SysFiles2, InclRegexps2, ExclRegexps2),
+ SysFiles4 = SysFiles3 ++ [{create_dir, "lib", LibFiles}],
+ check_sys(Mandatory, SysFiles4),
+ SysFiles4.
strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) ->
ExclRegexps2 =
@@ -967,22 +986,35 @@ safe_lookup_spec(Prefix, Specs) ->
%% Specify applications
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec_lib_files(#sys{apps = Apps} = Sys) ->
+spec_lib_files(#sys{root_dir = RootDir,
+ apps = Apps,
+ excl_lib = ExclLib} = Sys) ->
Filter = fun(#app{is_escript = IsEscript, is_included = IsIncl,
- is_pre_included = IsPre, name = Name}) ->
+ is_pre_included = IsPre, name = Name,
+ active_dir = ActiveDir}) ->
if
Name =:= ?MISSING_APP_NAME ->
false;
IsEscript =/= false ->
false;
IsIncl; IsPre ->
- true;
+ case ExclLib of
+ otp_root ->
+ not lists:prefix(RootDir,ActiveDir);
+ _ ->
+ true
+ end;
true ->
false
end
end,
SelectedApps = lists:filter(Filter, Apps),
- check_apps([kernel, stdlib], SelectedApps),
+ case ExclLib of
+ otp_root ->
+ ok;
+ _ ->
+ check_apps([kernel, stdlib], SelectedApps)
+ end,
lists:flatten([spec_app(App, Sys) || App <- SelectedApps]).
check_apps([Mandatory | Names], Apps) ->
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index 8a98dc36cf..f29f6049a5 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -67,6 +67,7 @@ all() ->
create_standalone_app_clash,
create_multiple_standalone,
create_old_target,
+ create_slim,
eval_target_spec,
otp_9135,
otp_9229_dupl_mod_exclude_app,
@@ -985,6 +986,56 @@ create_old_target(_Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Generate target system
+
+create_slim(Config) ->
+ %% Configure the server
+ RelName = "slim",
+ RelVsn = "1.0",
+
+ DataDir = ?config(data_dir,Config),
+ LibDir = filename:join(DataDir,"slim"),
+
+ Sys =
+ {sys,
+ [
+ {root_dir, code:root_dir()},
+ {lib_dirs, []},
+ {boot_rel, RelName},
+ {rel, RelName, RelVsn, [sasl, stdlib, kernel, a]},
+ {app, sasl, [{incl_cond, include}]},
+ {app, a, [{incl_cond, include},
+ {lib_dir,filename:join(LibDir,"a-1.0")}]},
+ {excl_lib,otp_root}
+ ]},
+
+ %% Generate target file
+ TargetDir = filename:absname(filename:join([?WORK_DIR, "target_slim"])),
+ ?m(ok, reltool_utils:recursive_delete(TargetDir)),
+ ?m(ok, file:make_dir(TargetDir)),
+ ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]),
+ ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)),
+
+ TargetLibDir = filename:join(TargetDir,"lib"),
+ TargetRelDir = filename:join(TargetDir,"releases"),
+ TargetRelVsnDir = filename:join(TargetRelDir,RelVsn),
+
+ {ok,["a-1.0.ez"]} = file:list_dir(TargetLibDir),
+
+ RootDir = code:root_dir(),
+ Erl = filename:join([RootDir, "bin", "erl"]),
+ Args = "-boot_var RELTOOL_EXT_LIB " ++ TargetLibDir ++
+ " -boot " ++ filename:join(TargetRelVsnDir,RelName) ++
+ " -sasl releases_dir \\\"" ++ TargetRelDir ++ "\\\"",
+ {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl, Args)),
+ ?msym(RootDir, rpc:call(Node, code, root_dir, [])),
+ ?msym([{RelName,RelVsn,_,permanent}],
+ rpc:call(Node,release_handler,which_releases,[])),
+ ?msym(ok, stop_node(Node)),
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate target system with eval_target_spec/3
eval_target_spec(_Config) ->
@@ -2226,8 +2277,10 @@ mod_path(Node,Mod) ->
%% Node handling
start_node(Name, ErlPath) ->
+ start_node(Name, ErlPath, []).
+start_node(Name, ErlPath, Args) ->
FullName = full_node_name(Name),
- CmdLine = mk_node_cmdline(Name, ErlPath),
+ CmdLine = mk_node_cmdline(Name, ErlPath, Args),
io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
case open_port({spawn, CmdLine}, []) of
Port when is_port(Port) ->
@@ -2246,14 +2299,7 @@ stop_node(Node) ->
spawn(Node, fun () -> halt() end),
receive {nodedown, Node} -> ok end.
-mk_node_cmdline(Name) ->
- Prog = case catch init:get_argument(progname) of
- {ok,[[P]]} -> P;
- _ -> exit(no_progname_argument_found)
- end,
- mk_node_cmdline(Name, Prog).
-
-mk_node_cmdline(Name, Prog) ->
+mk_node_cmdline(Name, Prog, Args) ->
Static = "-detached -noinput",
Pa = filename:dirname(code:which(?MODULE)),
NameSw = case net_kernel:longnames() of
@@ -2268,7 +2314,8 @@ mk_node_cmdline(Name, Prog) ->
++ NameSw ++ " " ++ NameStr ++ " "
++ "-pa " ++ Pa ++ " "
++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " "
- ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()).
+ ++ "-setcookie " ++ atom_to_list(erlang:get_cookie())
+ ++ " " ++ Args.
full_node_name(PreName) ->
HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
index 21410ceaa9..02846f42b6 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
+++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
@@ -22,7 +22,11 @@ SEL_VSN= \
use_selected_vsn/b-3.0/ebin/b.@EMULATOR@ \
use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@
-all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN)
+SLIM= \
+ slim/a-1.0/ebin/a_sup.@EMULATOR@ \
+ slim/a-1.0/ebin/a.@EMULATOR@
+
+all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN) $(SLIM)
otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl
erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl
@@ -55,3 +59,8 @@ use_selected_vsn/b-3.0/ebin/b.@EMULATOR@: use_selected_vsn/b-3.0/src/b.erl
erlc $(EFLAGS) -ouse_selected_vsn/b-3.0/ebin use_selected_vsn/b-3.0/src/b.erl
use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@: use_selected_vsn/lib2/b-2.0/src/b.erl
erlc $(EFLAGS) -ouse_selected_vsn/lib2/b-2.0/ebin use_selected_vsn/lib2/b-2.0/src/b.erl
+
+slim/a-1.0/ebin/a_sup.@EMULATOR@: slim/a-1.0/src/a_sup.erl
+ erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a_sup.erl
+slim/a-1.0/ebin/a.@EMULATOR@: slim/a-1.0/src/a.erl
+ erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a.erl
diff --git a/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app
new file mode 100644
index 0000000000..3160e00da7
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app
@@ -0,0 +1,7 @@
+{application, a,
+ [{description, "A CXC 138 11"},
+ {vsn, "1.0"},
+ {modules, [a, a_sup]},
+ {registered, [a_sup]},
+ {applications, [kernel, stdlib]},
+ {mod, {a_sup, []}}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl
new file mode 100644
index 0000000000..bb500bed69
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl
@@ -0,0 +1,49 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(a).
+
+
+-behaviour(gen_server).
+
+-vsn(1).
+
+%% External exports
+-export([start_link/0, a/0]).
+%% Internal exports
+-export([init/1, handle_call/3, handle_info/2, terminate/2]).
+
+start_link() -> gen_server:start_link({local, aa}, a, [], []).
+
+a() -> gen_server:call(aa, a).
+
+%%-----------------------------------------------------------------
+%% Callback functions from gen_server
+%%-----------------------------------------------------------------
+init([]) ->
+ process_flag(trap_exit, true),
+ {ok, state}.
+
+handle_call(a, _From, State) ->
+ X = application:get_all_env(a),
+ {reply, X, State}.
+
+handle_info(_, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl
new file mode 100644
index 0000000000..a141c1767b
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl
@@ -0,0 +1,37 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(a_sup).
+
+
+-behaviour(supervisor).
+
+%% External exports
+-export([start/2]).
+
+%% Internal exports
+-export([init/1]).
+
+start(_, _) ->
+ supervisor:start_link({local, a_sup}, a_sup, []).
+
+init([]) ->
+ SupFlags = {one_for_one, 4, 3600},
+ Config = {a,
+ {a, start_link, []},
+ permanent, 2000, worker, [a]},
+ {ok, {SupFlags, [Config]}}.
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e30c6f1ccc..b84b3a3dcb 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -259,11 +259,17 @@
that identifies the host for ssh. The default is
<c><![CDATA[/etc/ssh]]></c>, note that SSH normally
requires the host files there to be readable only by
- root.</p>
+ root.</p>
+ </item>
+ <tag><c><![CDATA[{auth_methods, string()}]]></c></tag>
+ <item>
+ <p>Comma separated string that determines which authentication methodes that the server
+ should support and in what order they will be tried. Defaults to
+ <c><![CDATA["publickey,keyboard_interactive,password"]]></c></p>
</item>
- <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
- <item>
- <p>Provide passwords for password authentication.They will
+ <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
+ <item>
+ <p>Provide passwords for password authentication.They will
be used when someone tries to connect to the server and
public key user authentication fails. The option provides
a list of valid user names and the corresponding password.
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 0542054596..d08dbafc32 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -18,12 +18,26 @@
%%
{"%VSN%",
- [
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ [
+ {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []},
+ {load_module, ssh_connection, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []},
+ {load_module, ssh_auth, soft_purge, soft_purge, []},
+ {load_module, ssh_channel, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []}]},
+ {load_module, ssh, soft_purge, soft_purge, []}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
],
[
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []},
+ {load_module, ssh_connection, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []},
+ {load_module, ssh_auth, soft_purge, soft_purge, []},
+ {load_module, ssh_channel, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []}]},
+ {load_module, ssh, soft_purge, soft_purge, []}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index f4a40c81a4..3395f73884 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -91,10 +91,8 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{ok, ConnectionSup} ->
{ok, Manager} =
ssh_connection_sup:connection_manager(ConnectionSup),
- MRef = erlang:monitor(process, Manager),
receive
{Manager, is_connected} ->
- do_demonitor(MRef, Manager),
{ok, Manager};
%% When the connection fails
%% ssh_connection_sup:connection_manager
@@ -102,30 +100,13 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
%% could allready have terminated, so we will not
%% match the Manager in this case
{_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
- do_demonitor(MRef, Manager),
do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
SshOptions, Timeout, true);
{_, not_connected, {error, Reason}} ->
- do_demonitor(MRef, Manager),
{error, Reason};
{_, not_connected, Other} ->
- do_demonitor(MRef, Manager),
- {error, Other};
- {'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) ->
- error_logger:warning_report([{ssh, connect},
- {diagnose,
- "Connection was closed before properly set up."},
- {host, Host},
- {port, Port},
- {reason, Reason}]),
- receive %% Clear EXIT message from queue
- {'EXIT', Manager, _What} ->
- {error, channel_closed}
- after 0 ->
- {error, channel_closed}
- end
+ {error, Other}
after Timeout ->
- do_demonitor(MRef, Manager),
ssh_connection_manager:stop(Manager),
{error, timeout}
end
@@ -134,16 +115,6 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{error, ssh_not_started}
end.
-do_demonitor(MRef, Manager) ->
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, Manager, _} ->
- ok
- after 0 ->
- ok
- end.
-
-
%%--------------------------------------------------------------------
%% Function: close(ConnectionRef) -> ok
%%
@@ -369,6 +340,8 @@ handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -408,6 +381,8 @@ handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
+handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
+ Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index c46f799b6d..c2a7c63cbe 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -436,32 +436,32 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
#connection{channel_cache = Cache} = Connection,
ConnectionPid, _) ->
- #channel{send_window_size = Size} =
+ #channel{send_window_size = Size, remote_id = RemoteId} =
Channel0 = ssh_channel:cache_lookup(Cache, ChannelId),
-
+
{SendList, Channel} = %% TODO: Datatype 0 ?
update_send_window(Channel0#channel{send_window_size = Size + Add},
0, <<>>, Connection),
Replies = lists:map(fun({Type, Data}) ->
{connection_reply, ConnectionPid,
- channel_data_msg(ChannelId, Type, Data)}
+ channel_data_msg(RemoteId, Type, Data)}
end, SendList),
FlowCtrlMsgs = flow_control(Channel, Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
- sender_channel = ChannelId,
+ sender_channel = RemoteId,
initial_window_size = WindowSz,
maximum_packet_size = PacketSz}, Connection0,
ConnectionPid, server) ->
- try setup_session(Connection0, ConnectionPid, ChannelId,
+ try setup_session(Connection0, ConnectionPid, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
catch _:_ ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]},
@@ -532,9 +532,9 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
-handle_msg(#ssh_msg_channel_open{sender_channel = ChannelId}, Connection,
+handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection,
ConnectionPid, _) ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
"Not allowed", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index e3544af1c6..b620056310 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -48,8 +48,12 @@ start_manager_child(Sup, Args) ->
supervisor:start_child(Sup, Spec).
connection_manager(SupPid) ->
- Children = supervisor:which_children(SupPid),
- {ok, ssh_connection_manager(Children)}.
+ try supervisor:which_children(SupPid) of
+ Children ->
+ {ok, ssh_connection_manager(Children)}
+ catch exit:{noproc,_} ->
+ {ok, undefined}
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -107,6 +111,8 @@ handler_spec([Role, Socket, Opts]) ->
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+ssh_connection_manager([]) ->
+ undefined;
ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) ->
Child;
ssh_connection_manager([_ | Rest]) ->
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index d05fa8e09a..a6b82a7a13 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -232,7 +232,7 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, known_hosts) of
+ case ssh_decode_line(Line, known_hosts) of
[{Key, Attributes}] ->
handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
[] ->
@@ -240,6 +240,13 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
end
end.
+ssh_decode_line(Line, Type) ->
+ try
+ public_key:ssh_decode(Line, Type)
+ catch _:_ ->
+ []
+ end.
+
handle_host(Fd, Host, HostList, Key, KeyType) ->
Host1 = host_name(Host),
case lists:member(Host1, HostList) and key_match(Key, KeyType) of
@@ -285,7 +292,7 @@ lookup_user_key_fd(Fd, Key) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, auth_keys) of
+ case ssh_decode_line(Line, auth_keys) of
[{AuthKey, _}] ->
case is_auth_key(Key, AuthKey) of
true ->
diff --git a/lib/ssh/src/ssh_sftpd_file_api.erl b/lib/ssh/src/ssh_sftpd_file_api.erl
index 38371f809d..83d90907f5 100644
--- a/lib/ssh/src/ssh_sftpd_file_api.erl
+++ b/lib/ssh/src/ssh_sftpd_file_api.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -22,40 +22,40 @@
-module(ssh_sftpd_file_api).
%% To be further specified later
--callback close(IoDevice::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback delete(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback del_dir(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+-callback close(file:io_device(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback delete(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback del_dir(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback get_cwd(State::term()) ->
- {ok, Dir::term()} | {error, Reason::term()}.
--callback is_dir(AbsPath::term(), State::term()) ->
- boolean().
--callback list_dir(AbsPath::term(), State::term()) ->
- {ok, Filenames::term()} | {error, Reason::term()}.
+ {{ok, Dir::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback is_dir(file:name(), State::term()) ->
+ {boolean(), State::term()}.
+-callback list_dir(file:name(), State::term()) ->
+ {{ok, Filenames::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_dir(Dir::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, State::term()},State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_symlink(Path2::term(), Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback open(Path::term(), Flags::term(), State::term()) ->
- {ok, IoDevice::term()} | {error, Reason::term()}.
--callback position(IoDevice::term(), Offs::term(), State::term()) ->
- {ok, NewPosition::term()} | {error, Reason::term()}.
--callback read(IoDevice::term(), Len::term(), State::term()) ->
- {ok, Data::term()} | eof | {error, Reason::term()}.
--callback read_link(Path::term(), State::term()) ->
- {ok, FileName::term()} | {error, Reason::term()}.
--callback read_link_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback read_file_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback rename(Path::term(), Path2::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write(IoDevice::term(), Data::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write_file_info(Path::term(),Info::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, IoDevice::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback position(file:io_device(), Offs::term(), State::term()) ->
+ {{ok, NewPosition::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read(file:io_device(), Len::term(), State::term()) ->
+ {{ok, Data::term()},State::term()} | {eof, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link(file:name(), State::term()) ->
+ {{ok, FileName::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_file_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()},State::term()}.
+-callback rename(file:name(), file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write(file:io_device(), Data::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write_file_info(file:name(),Info::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index bff73a1b40..defa47f824 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 2.1
+SSH_VSN = 2.1.1
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
index baf5a9185e..133a1764bc 100644
--- a/lib/ssl/examples/src/client_server.erl
+++ b/lib/ssl/examples/src/client_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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,18 +21,14 @@
-module(client_server).
--export([start/0, start/1, init_connect/1]).
+-export([start/0, init_connect/1]).
start() ->
- start([ssl, subject]).
-
-start(CertOpts) ->
%% Start ssl application
+ application:start(crypto),
+ application:start(public_key),
application:start(ssl),
- %% Always seed
- ssl:seed("ellynatefttidppohjeh"),
-
%% Let the current process be the server that listens and accepts
%% Listen
{ok, LSock} = ssl:listen(0, mk_opts(listen)),
@@ -40,14 +36,14 @@ start(CertOpts) ->
io:fwrite("Listen: port = ~w.~n", [LPort]),
%% Spawn the client process that connects to the server
- spawn(?MODULE, init_connect, [{LPort, CertOpts}]),
+ spawn(?MODULE, init_connect, [LPort]),
%% Accept
{ok, ASock} = ssl:transport_accept(LSock),
ok = ssl:ssl_accept(ASock),
io:fwrite("Accept: accepted.~n"),
- {ok, Cert} = ssl:peercert(ASock, CertOpts),
- io:fwrite("Accept: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(ASock),
+ io:fwrite("Accept: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
io:fwrite("Accept: sending \"hello\".~n"),
ssl:send(ASock, "hello"),
{error, closed} = ssl:recv(ASock, 0),
@@ -59,12 +55,12 @@ start(CertOpts) ->
%% Client connect
-init_connect({LPort, CertOpts}) ->
+init_connect(LPort) ->
{ok, Host} = inet:gethostname(),
{ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)),
io:fwrite("Connect: connected.~n"),
- {ok, Cert} = ssl:peercert(CSock, CertOpts),
- io:fwrite("Connect: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(CSock),
+ io:fwrite("Connect: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
{ok, Data} = ssl:recv(CSock, 0),
io:fwrite("Connect: got data: ~p~n", [Data]),
io:fwrite("Connect: closing and terminating.~n"),
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index 1daf9640ab..41dc1bf0dc 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -81,7 +81,7 @@ certificate_verify(md5sha, _Version, Handshake) ->
<<MD5/binary, SHA/binary>>;
certificate_verify(HashAlgo, _Version, Handshake) ->
- Hash = crypto:hash(HashAlgo, Handshake).
+ crypto:hash(HashAlgo, Handshake).
-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(),
integer(), integer()) -> {binary(), binary(), binary(),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index b39c995552..63731ee25c 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -150,7 +150,7 @@ run_client(Opts) ->
{ok, Socket} ->
Pid ! { connected, Socket },
test_server:format("Client: connected~n", []),
- %% In specail cases we want to know the client port, it will
+ %% In special cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
send_selected_port(Pid, proplists:get_value(port, Options), Socket),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 76a6a6dc36..151f04b03b 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -5979,13 +5979,9 @@ is_literal(T) ->
revert(Node) ->
case is_tree(Node) of
false ->
- %% Just remove any wrapper and copy the position. `erl_parse'
- %% nodes never contain abstract syntax tree nodes as subtrees.
- case unwrap(Node) of
- {error, Info} -> {error, setelement(1,Info,get_pos(Node))};
- {warning, Info} -> {warning, setelement(1,Info,get_pos(Node))};
- Node1 -> setelement(2,Node1,get_pos(Node))
- end;
+ %% Just remove any wrapper. `erl_parse' nodes never contain
+ %% abstract syntax tree nodes as subtrees.
+ unwrap(Node);
true ->
case is_leaf(Node) of
true ->
diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml
index 9028a67ecb..6b33591701 100644
--- a/lib/test_server/doc/src/test_server_ctrl.xml
+++ b/lib/test_server/doc/src/test_server_ctrl.xml
@@ -769,11 +769,13 @@ Optional, if not given the test server controller node
constantly updated. The following can be reported:
</p>
<p><c>What = tests_start, Data = {Name,NumCases}</c><br></br>
+ <c>What = loginfo, Data = [{topdir,TestRootDir},{rundir,CurrLogDir}]</c><br></br>
<c>What = tests_done, Data = {Ok,Failed,{UserSkipped,AutoSkipped}}</c><br></br>
- <c>What = tc_start, Data = {Mod,Func}</c><br></br>
+ <c>What = tc_start, Data = {{Mod,Func},TCLogFile}</c><br></br>
<c>What = tc_done, Data = {Mod,Func,Result}</c><br></br>
<c>What = tc_user_skip, Data = {Mod,Func,Comment}</c><br></br>
- <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c></p>
+ <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c><br></br>
+ <c>What = framework_error, Data = {{FWMod,FWFunc},Error}</c></p>
</desc>
</func>
<func>
diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml
index 39c5545af9..169313cb9c 100644
--- a/lib/tools/doc/src/xref_chapter.xml
+++ b/lib/tools/doc/src/xref_chapter.xml
@@ -301,7 +301,7 @@
and <c>|||</c>) are the only operators that accept both
representations. This means that in order to analyze indirect
calls using restriction, the <c>closure</c> operator (which creates the
- <c>digraph</c> representation of graphs) has to been
+ <c>digraph</c> representation of graphs) has to be
applied explicitly.
</p>
<p>As an example of analyzing indirect calls, the following Erlang
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index e5ed56b412..69946be24a 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -86,3 +86,19 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
+
+EMACS ?= emacs
+
+test_indentation:
+ @rm -f test.erl
+ @rm -f test_indent.el
+ @echo '(load "erlang-start")' >> test_indent.el
+ @echo '(find-file "test.erl.orig")' >> test_indent.el
+ @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
+ @echo '(erlang-mode)' >> test_indent.el
+ @echo '(toggle-debug-on-error)' >> test_indent.el
+ @echo '(erlang-indent-current-buffer)' >> test_indent.el
+ @echo '(write-file "test.erl")' >> test_indent.el
+ $(EMACS) --batch -Q -L . -l test_indent.el
+ diff -u test.erl.indented test.erl
+ @echo "No differences between expected and actual indentation"