summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in11
-rw-r--r--OTP_VERSION2
-rw-r--r--bootstrap/lib/kernel/ebin/erl_compile_server.beambin0 -> 5052 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app1
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.beambin3644 -> 3764 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin6676 -> 6808 bytes
-rw-r--r--erts/configure.in24
-rw-r--r--erts/doc/src/crash_dump.xml25
-rw-r--r--erts/doc/src/erl_nif.xml51
-rw-r--r--erts/doc/src/erlc.xml63
-rw-r--r--erts/doc/src/notes.xml361
-rw-r--r--erts/doc/src/socket.xml36
-rw-r--r--erts/emulator/Makefile.in4
-rw-r--r--erts/emulator/beam/atom.names1
-rw-r--r--erts/emulator/beam/bif.tab10
-rw-r--r--erts/emulator/beam/erl_bif_info.c27
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c2
-rw-r--r--erts/emulator/beam/erl_binary.h1
-rw-r--r--erts/emulator/beam/erl_db.c92
-rw-r--r--erts/emulator/beam/erl_db_catree.c20
-rw-r--r--erts/emulator/beam/erl_db_hash.c142
-rw-r--r--erts/emulator/beam/erl_db_tree.c49
-rw-r--r--erts/emulator/beam/erl_db_tree_util.h4
-rw-r--r--erts/emulator/beam/erl_db_util.c12
-rw-r--r--erts/emulator/beam/erl_db_util.h37
-rw-r--r--erts/emulator/beam/erl_node_tables.c2
-rw-r--r--erts/emulator/beam/erl_process.c23
-rw-r--r--erts/emulator/beam/erl_process.h3
-rw-r--r--erts/emulator/beam/erl_unicode.c34
-rw-r--r--erts/emulator/beam/external.c4
-rw-r--r--erts/emulator/beam/global.h2
-rw-r--r--erts/emulator/beam/io.c100
-rw-r--r--erts/emulator/hipe/hipe_x86_signal.c14
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c42
-rw-r--r--erts/emulator/test/lcnt_SUITE.erl27
-rw-r--r--erts/emulator/test/lttng_SUITE.erl168
-rw-r--r--erts/emulator/test/socket_SUITE.erl28
-rw-r--r--erts/etc/common/Makefile.in12
-rw-r--r--erts/etc/common/erlc.c541
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin17760 -> 20156 bytes
-rw-r--r--erts/preloaded/src/erts_internal.erl71
-rw-r--r--erts/test/erlc_SUITE.erl23
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/common_test/doc/src/ct_netconfc.xml376
-rw-r--r--lib/common_test/doc/src/notes.xml58
-rw-r--r--lib/common_test/src/Makefile3
-rw-r--r--lib/common_test/src/common_test.app.src4
-rw-r--r--lib/common_test/src/ct_event.erl12
-rw-r--r--lib/common_test/src/ct_framework.erl3
-rw-r--r--lib/common_test/src/ct_netconfc.erl1564
-rw-r--r--lib/common_test/src/ct_run.erl91
-rw-r--r--lib/common_test/src/ct_util.erl7
-rw-r--r--lib/common_test/src/ct_webtool.erl1214
-rw-r--r--lib/common_test/src/ct_webtool_sup.erl76
-rw-r--r--lib/common_test/src/vts.erl927
-rw-r--r--lib/common_test/test/ct_test_support.erl13
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/notes.xml78
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/doc/src/crypto.xml18
-rw-r--r--lib/crypto/doc/src/notes.xml58
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/notes.xml17
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/notes.xml17
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl6
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/Makefile2
-rw-r--r--lib/erl_interface/doc/src/notes.xml35
-rw-r--r--lib/erl_interface/src/Makefile2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml15
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/ftp/doc/src/notes.xml18
-rw-r--r--lib/ftp/src/ftp.erl7
-rw-r--r--lib/ftp/vsn.mk2
-rw-r--r--lib/inets/doc/src/httpc.xml16
-rw-r--r--lib/inets/doc/src/notes.xml44
-rw-r--r--lib/inets/src/http_server/mod_esi.erl22
-rw-r--r--lib/inets/test/httpd_SUITE.erl28
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/notes.xml16
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/gen_udp.xml8
-rw-r--r--lib/kernel/doc/src/inet.xml12
-rw-r--r--lib/kernel/doc/src/notes.xml113
-rw-r--r--lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl17
-rw-r--r--lib/kernel/src/Makefile1
-rw-r--r--lib/kernel/src/erl_compile_server.erl253
-rw-r--r--lib/kernel/src/kernel.app.src1
-rw-r--r--lib/kernel/src/kernel.appup.src6
-rw-r--r--lib/kernel/src/kernel.erl17
-rw-r--r--lib/kernel/src/net_kernel.erl98
-rw-r--r--lib/kernel/src/raw_file_io_compressed.erl1
-rw-r--r--lib/kernel/src/raw_file_io_delayed.erl1
-rw-r--r--lib/kernel/test/file_SUITE.erl26
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl53
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/doc/src/notes.xml19
-rw-r--r--lib/megaco/src/engine/megaco_monitor.erl2
-rw-r--r--lib/megaco/test/megaco_SUITE.erl14
-rw-r--r--lib/megaco/test/megaco_mess_test.erl343
-rw-r--r--lib/megaco/test/megaco_test_lib.erl83
-rw-r--r--lib/megaco/test/megaco_timer_test.erl15
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/notes.xml31
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml28
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/os_mon/doc/src/notes.xml23
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/public_key/doc/src/notes.xml27
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/runtime_tools/doc/src/notes.xml16
-rw-r--r--lib/runtime_tools/src/dbg.erl2
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl46
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/notes.xml16
-rw-r--r--lib/sasl/src/sasl.appup.src8
-rw-r--r--lib/sasl/test/installer.erl2
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl2
-rw-r--r--lib/sasl/test/rh_test_lib.erl2
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/notes.xml57
-rw-r--r--lib/snmp/src/agent/snmp_generic.erl8
-rw-r--r--lib/snmp/src/agent/snmpa_general_db.erl12
-rw-r--r--lib/snmp/test/snmp_agent_test.erl71
-rw-r--r--lib/snmp/test/snmp_manager_test.erl1038
-rw-r--r--lib/snmp/test/snmp_test_lib.erl32
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl320
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/notes.xml96
-rw-r--r--lib/ssh/doc/src/ssh.xml46
-rw-r--r--lib/ssh/src/ssh.erl135
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl52
-rw-r--r--lib/ssh/src/ssh_options.erl282
-rw-r--r--lib/ssh/src/ssh_system_sup.erl18
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml69
-rw-r--r--lib/ssl/src/dtls_connection.erl56
-rw-r--r--lib/ssl/src/dtls_handshake.erl29
-rw-r--r--lib/ssl/src/dtls_record.erl20
-rw-r--r--lib/ssl/src/ssl.erl438
-rw-r--r--lib/ssl/src/ssl_config.erl26
-rw-r--r--lib/ssl/src/ssl_connection.erl174
-rw-r--r--lib/ssl/src/ssl_connection.hrl2
-rw-r--r--lib/ssl/src/ssl_handshake.erl70
-rw-r--r--lib/ssl/src/ssl_internal.hrl2
-rw-r--r--lib/ssl/src/ssl_session.erl16
-rw-r--r--lib/ssl/src/tls_connection.erl63
-rw-r--r--lib/ssl/src/tls_handshake.erl38
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl79
-rw-r--r--lib/ssl/src/tls_record.erl8
-rw-r--r--lib/ssl/src/tls_socket.erl4
-rw-r--r--lib/ssl/test/Makefile1
-rw-r--r--lib/ssl/test/openssl_alpn_SUITE.erl30
-rw-r--r--lib/ssl/test/openssl_npn_SUITE.erl27
-rw-r--r--lib/ssl/test/openssl_renegotiate_SUITE.erl22
-rw-r--r--lib/ssl/test/ssl_api_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_cert_SUITE.erl391
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl608
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl12
-rw-r--r--lib/ssl/test/ssl_test_lib.erl24
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/digraph.xml21
-rw-r--r--lib/stdlib/doc/src/ets.xml89
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml183
-rw-r--r--lib/stdlib/doc/src/notes.xml142
-rw-r--r--lib/stdlib/doc/src/sys.xml28
-rw-r--r--lib/stdlib/src/erl_compile.erl187
-rw-r--r--lib/stdlib/src/escript.erl29
-rw-r--r--lib/stdlib/src/ets.erl2
-rw-r--r--lib/stdlib/src/gen_statem.erl410
-rw-r--r--lib/stdlib/src/stdlib.app.src2
-rw-r--r--lib/stdlib/src/stdlib.appup.src6
-rw-r--r--lib/stdlib/src/sys.erl2
-rw-r--r--lib/stdlib/test/escript_SUITE.erl17
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/bad_io_server11
-rw-r--r--lib/stdlib/test/ets_SUITE.erl147
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl119
-rw-r--r--lib/stdlib/test/rand_SUITE.erl17
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl6
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml33
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tools/doc/src/notes.xml15
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/doc/src/notes.xml16
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/notes.xml17
-rw-r--r--lib/xmerl/vsn.mk2
-rw-r--r--make/otp.mk.in2
-rw-r--r--make/otp_patch_solve_forward_merge_version2
-rw-r--r--otp_versions.table2
-rwxr-xr-xscripts/build-otp1
-rw-r--r--system/doc/design_principles/statem.xml158
197 files changed, 7559 insertions, 6641 deletions
diff --git a/Makefile.in b/Makefile.in
index 25003f47a9..9be91a4b20 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -338,7 +338,8 @@ endif
else
# Cross compiling
-all: cross_check_erl depend emulator libs start_scripts check_dev_rt_dep
+all: cross_check_erl depend build_erl_interface \
+ emulator libs start_scripts check_dev_rt_dep
endif
@@ -376,11 +377,17 @@ endif
# With all bootstraps we mean all bootstrapping that is done when
# the system is delivered in open source, the primary
# bootstrap is not included, it requires a pre built emulator...
-all_bootstraps: emulator \
+all_bootstraps: build_erl_interface emulator \
bootstrap_setup \
secondary_bootstrap_build secondary_bootstrap_copy \
tertiary_bootstrap_build tertiary_bootstrap_copy
+.PHONY: build_erl_interface
+
+build_erl_interface:
+ $(make_verbose)cd lib/erl_interface && \
+ ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
+ $(MAKE) $(TYPE)
#
# Use these targets when you want to use the erl and erlc
# binaries in your PATH instead of those created from the
diff --git a/OTP_VERSION b/OTP_VERSION
index 84a941394a..70a91e23ec 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-22.0.7
+22.1
diff --git a/bootstrap/lib/kernel/ebin/erl_compile_server.beam b/bootstrap/lib/kernel/ebin/erl_compile_server.beam
new file mode 100644
index 0000000000..28d79c2efb
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/erl_compile_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index cb13db1a89..7483d1bae3 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -32,6 +32,7 @@
code_server,
dist_util,
erl_boot_server,
+ erl_compile_server,
erl_distribution,
erl_reply,
erl_signal_handler,
diff --git a/bootstrap/lib/kernel/ebin/kernel.beam b/bootstrap/lib/kernel/ebin/kernel.beam
index 2832938d95..5b93fe805b 100644
--- a/bootstrap/lib/kernel/ebin/kernel.beam
+++ b/bootstrap/lib/kernel/ebin/kernel.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index fa223657bf..5dd5691b3b 100644
--- a/bootstrap/lib/stdlib/ebin/erl_compile.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_compile.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index 3a043c940d..716dc7bd27 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -2723,9 +2723,25 @@ if test X${enable_hipe} != Xno; then
AC_MSG_ERROR([HiPE needs mprotect() on $ARCH])
else
enable_hipe=no
- AC_MSG_WARN([Disable HiPE due to lack of mprotect()])
+ AC_MSG_WARN([HiPE disabled due to lack of mprotect()])
fi
fi
+ AC_MSG_CHECKING([for safe signal delivery])
+ AC_TRY_COMPILE([#include <signal.h>],
+ [#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+ #define __DARWIN__ 1
+ #endif
+
+ #if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__))
+ /*
+ * Unknown libc -- assume musl, which does not allow safe signals
+ */
+ #error "HiPE does not work without a libc that can guarantee that sigaltstack works"
+ #endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */],
+ [AC_MSG_RESULT([yes])],
+ [enable_hipe=no
+ AC_MSG_RESULT([no, musl probably used. Need glibc to work properly])
+ AC_MSG_WARN([HiPE disabled due to lack of safe signal delivery])])
fi
dnl check to auto-enable hipe here...
@@ -3250,7 +3266,11 @@ AS_HELP_STRING([--without-javac], [don't use any Java compiler]))
dnl
dnl Then there are a number of apps which needs a java compiler...
dnl
-need_java="jinterface ic/java_src"
+need_java="jinterface"
+
+if test -d $ERL_TOP/lib/ic; then
+ need_java="$need_java ic/java_src"
+fi
# Remove all SKIP files from previous runs
for a in $need_java ; do
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index 876834307a..b517f014c1 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -395,9 +395,9 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Heap fragment data</em></tag>
<item>
- <p>Size of fragmented heap data. This is data either created by
- messages sent to the process or by the Erlang BIFs. This
- amount depends on so many things that this field is utterly
+ <p>Size of fragmented heap data, in words. This is data either
+ created by messages sent to the process or by the Erlang BIFs.
+ This amount depends on so many things that this field is usually
uninteresting.</p>
</item>
<tag><em>Link list</em></tag>
@@ -415,11 +415,11 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Stack+heap</em></tag>
<item>
- <p>The size of the stack and heap (they share memory segment).</p>
+ <p>The size of the stack and heap, in words (they share memory segment).</p>
</item>
<tag><em>OldHeap</em></tag>
<item>
- <p>The size of the "old heap". The Erlang virtual machine uses
+ <p>The size of the "old heap", in words. The Erlang virtual machine uses
generational garbage collection with two generations. There is
one heap for new data items and one for the data that has
survived two garbage collections. The assumption (which is
@@ -432,14 +432,14 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Heap unused, OldHeap unused</em></tag>
<item>
- <p>The amount of unused memory on each heap. This information is
+ <p>The amount of unused memory on each heap, in words. This information is
usually useless.</p>
</item>
<tag><em>Memory</em></tag>
<item>
- <p>The total memory used by this process. This includes call stack,
- heap, and internal structures. Same as
- <seealso marker="erlang#process_info-2">
+ <p>The total memory used by this process, in bytes.
+ This includes call stack, heap, and internal structures.
+ Same as <seealso marker="erlang#process_info-2">
<c>erlang:process_info(Pid,memory)</c></seealso>.</p>
</item>
<tag><em>Program counter</em></tag>
@@ -531,8 +531,7 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Words</em></tag>
<item>
- <p>The number of words (usually 4 bytes/word) allocated to data
- in the table.</p>
+ <p>The number of words allocated to data in the table.</p>
</item>
<tag><em>Type</em></tag>
<item>
@@ -672,8 +671,6 @@ Slogan: &lt;reason&gt;</pre>
</item>
</taglist>
- <p>The memory use is in bytes.</p>
-
<p>Then, all loaded modules are listed. The following fields exist:</p>
<taglist>
@@ -687,7 +684,7 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Old size</em></tag>
<item>
- <p>Memory use for the old code, if any.</p>
+ <p>Memory use for the old code, in bytes.</p>
</item>
<tag><em>Current attributes</em></tag>
<item>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index d74ae23a93..4eae346ead 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -38,13 +38,9 @@
<p>A NIF library contains native implementation of some functions
of an Erlang module. The native implemented functions (NIFs) are
called like any other functions without any difference to the
- caller. Each NIF must have an implementation in Erlang that
- is invoked if the function is called before the NIF library
- is successfully loaded. A typical such stub implementation
- is to throw an exception. But it can also be used as a fallback
- implementation if the NIF library is not implemented for some
- architecture.</p>
-
+ caller. A NIF library is built as a dynamically linked library file
+ and loaded in runtime by calling <seealso marker="erlang#load_nif-2">
+ <c>erlang:load_nif/2</c></seealso>.</p>
<warning>
<marker id="WARNING"/>
<p><em>Use this functionality with extreme care.</em></p>
@@ -56,7 +52,7 @@
VM will misbehave.</p>
<list type="bulleted">
<item>
- <p>A native function that crash will crash the whole VM.</p>
+ <p>A native function that crashes will crash the whole VM.</p>
</item>
<item>
<p>An erroneously implemented native function can cause a VM
@@ -75,6 +71,9 @@
</item>
</list>
</warning>
+ </description>
+ <section>
+ <title>Example</title>
<p>A minimal example of a NIF library can look as follows:</p>
@@ -101,11 +100,13 @@ ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)</code>
-export([init/0, hello/0]).
+-on_load(init/0).
+
init() ->
erlang:load_nif("./niftest", 0).
hello() ->
- "NIF library not loaded".</code>
+ erlang:nif_error("NIF library not loaded").</code>
<p>Compile and test can look as follows (on Linux):</p>
@@ -116,28 +117,30 @@ $> erl
1> c(niftest).
{ok,niftest}
2> niftest:hello().
-"NIF library not loaded"
-3> niftest:init().
-ok
-4> niftest:hello().
"Hello world!"</code>
- <p>A better solution for a real module is to take advantage of the new
- directive <c>on_load</c> (see section
- <seealso marker="doc/reference_manual:code_loading#on_load">Running a
- Function When a Module is Loaded</seealso> in the Erlang Reference
- Manual) to load the NIF library automatically when the module is
- loaded.</p>
-
+ <p>
+ In the example above the <seealso marker="doc/reference_manual:code_loading#on_load">
+ <em><c>on_load</c></em></seealso> directive is used get function <c>init</c> called
+ automatically when the module is loaded. Function <c>init</c> in turn
+ calls <seealso marker="erlang#load_nif-2"><c>erlang:load_nif/2</c></seealso>
+ which loads the NIF library and replaces the <c>hello</c> function with its
+ native implementation in C. Once loaded, a NIF library is persistent. It
+ will not be unloaded until the module code version that it belongs to is
+ purged.</p>
+ <p>
+ Each NIF must have an implementation in Erlang to be invoked if the
+ function is called before the NIF library is successfully loaded. A
+ typical such stub implementation is to call <seealso marker="erlang#nif_error-1">
+ <c>erlang:nif_error</c></seealso> which will raise an exception. The
+ Erlang function can also be used as a fallback implementation if the NIF
+ library lacks implementation for some OS or hardware architecture for example.</p>
<note>
<p>A NIF does not have to be exported, it can be local to the module.
However, unused local stub functions will be optimized
away by the compiler, causing loading of the NIF library to fail.</p>
</note>
-
- <p>Once loaded, a NIF library is persistent. It will not be unloaded
- until the module code version that it belongs to is purged.</p>
- </description>
+ </section>
<section>
<title>Functionality</title>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index 62957d6a50..9d221a69ee 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -138,6 +138,16 @@
for compiling native code, which must be compiled with the same
runtime system that it is to be run on.</p>
</item>
+ <tag><c>-no-server</c></tag>
+ <item>
+ <p>Do not use the
+ <seealso marker="#compile_server">compile server</seealso>.</p>
+ </item>
+ <tag><c>-server</c></tag>
+ <item>
+ <p>Use the
+ <seealso marker="#compile_server">compile server</seealso>.</p>
+ </item>
<tag><c>-M</c></tag>
<item>
<p>Produces a Makefile rule to track header dependencies. The
@@ -298,6 +308,52 @@ erlc +export_all file.erl</pre>
</section>
<section>
+ <marker id="compile_server"></marker>
+ <title>Compile Server</title>
+ <p>The compile server can be used to potentially speed up the
+ build of multi-file projects by avoiding to start an Erlang system
+ for each file to compile. Whether it will speed up the build
+ depends on the nature of the project and the build machine.</p>
+
+ <p>By default, the compile server is not used. It can be
+ enabled by giving <c>erlc</c> the option <c>-server</c> or by
+ setting the environment variable <c>ERLC_USE_SERVER</c> to
+ <c>yes</c> or <c>true</c>.</p>
+
+ <p>When the compile server is enabled, <c>erlc</c> will
+ automatically use the server if it is started and start the server
+ if has not already started. The server will terminate itself when
+ it has been idle for some number of seconds.</p>
+
+ <p><c>erlc</c> and the compile server communicate using the
+ Erlang distribution. The compile server is started as a hidden
+ node, with a name that includes the current user. Thus, each user
+ on a computer has their own compile server.</p>
+
+ <p>Using the compile server does not always speed up the build, as
+ the compile server sometimes must be restarted to ensure correctness.
+ Here are some examples of situtations that force a restart:</p>
+
+ <list type="bulleted">
+ <item><c>erlc</c> wants to use a different version of Erlang
+ than the compile server is using.</item>
+ <item><c>erlc</c> wants to use different options for <c>erl</c>
+ than the compile server was started with. (A change to code path
+ using the option <c>-pa</c> could cause different parse
+ transforms to be loaded. To be safe, the compile server will be
+ restarted when any <c>erl</c> option is changed.)</item>
+ <item>If the current working directory for <c>erlc</c> is
+ different from the working directory active when the compile
+ server was started, <strong>and</strong> if the compile server
+ has active jobs, it will be restarted as soon as those jobs have
+ finished. (Build systems that build files randomly across multiple
+ directories in parallel will probably not benefit from the
+ compile server.)</item>
+ </list>
+ </section>
+
+ <section>
+ <marker id="environment_variables"></marker>
<title>Environment Variables</title>
<taglist>
<tag><c>ERLC_EMULATOR</c></tag>
@@ -305,6 +361,13 @@ erlc +export_all file.erl</pre>
in the same directory as the <c>erlc</c> program itself,
or, if it does not exist, <c>erl</c> in any of the directories
specified in environment variable <c>PATH</c>.</item>
+ <tag><c>ERLC_USE_SERVER</c></tag>
+ <item>Allowed values are <c>yes</c> or <c>true</c> to use the
+ <seealso marker="#compile_server">compile
+ server</seealso>, and <c>no</c> or <c>false</c> to not use the
+ compile server. If other values are given, <c>erlc</c> will
+ print a warning message and continue.
+ </item>
</taglist>
</section>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 5ca387ffd8..6bc242c246 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,331 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If you set <c>{linger,{true,0}}</c> on a <c>gen_tcp</c>
+ listen socket, accept a connection on that socket, and
+ then close the accepted socket, now the linger zero
+ setting is transferred to the accepted socket. Before
+ this correction that information was lost and the close
+ behaviour on the accepted socket incorrect.</p>
+ <p>
+ Own Id: OTP-15370 Aux Id: ERIERL-353 </p>
+ </item>
+ <item>
+ <p>
+ Sending ancillary data implemented in OTP-15747
+ accidentally left behind test code that caused all UDP
+ sends to fail on Windows. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-15422 Aux Id: OTP-15747 </p>
+ </item>
+ <item>
+ <p>
+ In the socket nif, used invalid flags when if-def'ing for
+ supported TCP flags: TCP_MAXSEG and TCP_NODELAY (the
+ support function).</p>
+ <p>
+ Own Id: OTP-15827</p>
+ </item>
+ <item>
+ <p>
+ Fixed memory leaks in experimental socket module.</p>
+ <p>
+ Own Id: OTP-15830</p>
+ </item>
+ <item>
+ <p>
+ <c>re:run()</c> now yields when validating utf8 in a
+ large subject.</p>
+ <p>
+ Own Id: OTP-15836 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>seq_trace:set_token(label,Term)</c> which
+ could cause VM crash if <c>Term</c> was heap allocated
+ (not an atom, small integer, local pid or port). Bug
+ exists since OTP 21.0 when terms other than small
+ integers were first allowed as labels.</p>
+ <p>
+ Own Id: OTP-15849 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ Extra <c>-mode</c> flags given to <c>erl</c> are ignored
+ with a warning.</p>
+ <p>
+ Own Id: OTP-15852</p>
+ </item>
+ <item>
+ <p>
+ Don't loop indefinitely when <c>--enable-pgo</c> is given
+ to configure, but compiler does not support pgo.</p>
+ <p>
+ Own Id: OTP-15853 Aux Id: PR-2254 </p>
+ </item>
+ <item>
+ <p>
+ Fix <c>seq_trace:print/2</c> not to raise <c>badarg</c>
+ exception if label is not a small integer. Bug exists
+ since OTP 21.0.</p>
+ <p>
+ Own Id: OTP-15859 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ Fixed hipe_flush_icache_range for non-Linux OS on ARM.</p>
+ <p>
+ Own Id: OTP-15874 Aux Id: ERL-958, PR-2266 </p>
+ </item>
+ <item>
+ <p>The fix in OTP-15871 was too conservative and disabled
+ the offending load-time optimization in some cases where
+ it was safe.</p>
+ <p>
+ Own Id: OTP-15881</p>
+ </item>
+ <item>
+ <p>
+ Upgraded the ERTS internal PCRE library from version 8.42
+ to version 8.43. See <url
+ href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url>
+ for information about changes made to PCRE. This library
+ implements major parts of the <seealso
+ marker="stdlib:re"><c>re</c></seealso> regular
+ expressions module.</p>
+ <p>
+ Own Id: OTP-15889</p>
+ </item>
+ <item>
+ <p>
+ Fix race condition when closing a socket while using
+ <c>{active,N}</c> on Windows.</p>
+ <p>
+ Own Id: OTP-15901 Aux Id: ERL-960 PR-2272 </p>
+ </item>
+ <item>
+ <p>
+ Allow more than one <c>-config</c> command line option to
+ <c>erl</c> on Windows to conform with other OS.</p>
+ <p>
+ Own Id: OTP-15918 Aux Id: ERL-912 </p>
+ </item>
+ <item>
+ <p>
+ Fix so that ERL_FLAGS environment variable does not
+ interfere with command line arguments. Before this fix
+ you could write:</p>
+ <p>
+ <c>ERL_FLAGS="10" erl +S</c></p>
+ <p>
+ and erlang would start as if <c>+S</c> had been given the
+ argument <c>10</c>.</p>
+ <p>
+ Own Id: OTP-15931</p>
+ </item>
+ <item>
+ <p>
+ The bug with ID ERL-717 has been fixed. The functions
+ <c>io:columns()</c> and <c>io:rows()</c> only worked
+ correctly inside interactive erlang shells before this
+ fix. These functions returned <c>{error,enotsup}</c>
+ before this fix even if stdout and stdin were connected
+ to a terminal when they were invoked from an escript or a
+ program started with e.g., <c>erl -noshell</c>.</p>
+ <p>
+ Own Id: OTP-15959 Aux Id: ERL-717 </p>
+ </item>
+ <item>
+ <p>
+ Do not use named label in <c>ethread.c</c> inline
+ assemble. This allows erts to be compiled using gcc 9.1.0
+ with LTO enabled.</p>
+ <p>
+ Own Id: OTP-15971 Aux Id: PR-2333 </p>
+ </item>
+ <item>
+ <p><c>erlang:fun_to_list/1</c> will now escape the module
+ and function name when necessary.</p>
+ <p>
+ Own Id: OTP-15975 Aux Id: ERL-1009 </p>
+ </item>
+ <item>
+ <p><c>process_info(P,binary)</c> would neglect to look
+ through heap fragments, potentially missing a few
+ binaries associated with the process.</p>
+ <p>
+ Own Id: OTP-15978 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ HiPE is now automatically disabled on systems with
+ non-glibc implementation (for instance musl). This is
+ because musl does not provide the API's for guaranteeing
+ that signals are delivered on the correct native stack.</p>
+ <p>
+ Own Id: OTP-16037</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug triggered if a process is killed during call to
+ <c>persistent_term:put</c> or
+ <c>persistent_term:erase</c>.</p>
+ <p>
+ Own Id: OTP-16041</p>
+ </item>
+ <item>
+ <p>
+ Add units to all memory slogans in the crash dump
+ documentation.</p>
+ <p>
+ Own Id: OTP-16042</p>
+ </item>
+ <item>
+ <p>
+ Fix a bug in <c>binary_to_term</c> that would crash the
+ emulator if a term larger than 16GB was to be decoded.</p>
+ <p>
+ Own Id: OTP-16058 Aux Id: PR-2382 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug related to an exiting process sending EXIT and
+ DOWN signals to remote linked/monitored processes. Bugs
+ exists since OTP 22.0.</p>
+ <p>
+ Own Id: OTP-16060</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p><c>erlc</c> can now automatically use a compile server
+ to avoid starting an Erlang system for each file to be
+ compiled in a multi-file project. See the documentation
+ for how to enable it.</p>
+ <p>
+ Own Id: OTP-15738 Aux Id: PR-2361 </p>
+ </item>
+ <item>
+ <p>
+ The possibility to send ancillary data, in particular the
+ TOS field, has been added to <c>gen_udp:send/4,5</c>.</p>
+ <p>
+ Own Id: OTP-15747 Aux Id: ERIERL-294 </p>
+ </item>
+ <item>
+ <p>
+ The net module has been split into 'net' (kernel) and
+ prim_net (preloaded).</p>
+ <p>
+ Own Id: OTP-15765</p>
+ </item>
+ <item>
+ <p>
+ Socket counters now works as expected and can also be
+ extracted with the (new) info function.</p>
+ <p>
+ Own Id: OTP-15818</p>
+ </item>
+ <item>
+ <p>
+ <c>re:run()</c> now avoids validating utf8 in the subject
+ more than once in the same call. This validation could
+ previously be performed multiple times when the
+ <c>global</c> option was passed.</p>
+ <p>
+ Own Id: OTP-15831 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ The un-documented function <c>erlang:dist_get_stat/1</c>
+ now returns the real value of what the distribution queue
+ contains instead of a boolean.</p>
+ <p>
+ Own Id: OTP-15905 Aux Id: PR-2270 </p>
+ </item>
+ <item>
+ <p>
+ ETS <c>ordered_set</c> tables with
+ <c>write_concurrency</c> enabled has got a performance
+ issue fixed. There were no limits for the values of
+ internal statistics counters before this fix. This could
+ result in that the data structure sometimes reacted
+ slowly to a change in how many parallel processes were
+ using it.</p>
+ <p>
+ Own Id: OTP-15906</p>
+ </item>
+ <item>
+ <p>
+ Optimize the reception of large distribution messages.</p>
+ <p>
+ Own Id: OTP-15926 Aux Id: PR-2291 </p>
+ </item>
+ <item>
+ <p>Binary matching and functions like
+ <c>split_binary/2</c> will now create heap binaries when
+ the results are small enough, reducing the chances of
+ small sub-binaries keeping large binaries alive.</p>
+ <p>
+ Own Id: OTP-15977 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>Fixed rare emulator crash in
+ <c>instrument:allocations/0-1</c>.</p>
+ <p>
+ Own Id: OTP-15983</p>
+ </item>
+ <item>
+ <p>
+ Ports could pass very small binaries as reference counted
+ off heap binaries to processes. This could cause an
+ unnecessary large memory usage and an unnecessary load on
+ the binary allocator. Small binaries are now always
+ passed as heap binaries to processes.</p>
+ <p>
+ Own Id: OTP-16001 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ <c>unicode:characters_to_binary()</c> could return very
+ small binaries as reference counted off heap binaries.
+ This could cause an unnecessary large memory usage and an
+ unnecessary load on the binary allocator. Small binaries
+ are now always returned as heap binaries.</p>
+ <p>
+ Own Id: OTP-16002 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ Improved <c>erl_nif</c> documentation regarding
+ <c>on_load</c> and Erlang stub/fallback functions.</p>
+ <p>
+ Own Id: OTP-16028 Aux Id: PR-2362 </p>
+ </item>
+ <item>
+ <p>
+ New feature <c>ets:info(_, binary)</c> to get information
+ about all reference counted binaries kept by a table.
+ This is the same kind of debug information that
+ <c>process_info(_, binary)</c> returns for a process.</p>
+ <p>
+ Own Id: OTP-16035 Aux Id: ERIERL-366 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.4.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -640,6 +965,42 @@
</section>
+<section><title>Erts 10.3.5.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p><c>process_info(P,binary)</c> would neglect to look
+ through heap fragments, potentially missing a few
+ binaries associated with the process.</p>
+ <p>
+ Own Id: OTP-15978 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug triggered if a process is killed during call to
+ <c>persistent_term:put</c> or
+ <c>persistent_term:erase</c>.</p>
+ <p>
+ Own Id: OTP-16041</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Fixed rare emulator crash in
+ <c>instrument:allocations/0-1</c>.</p>
+ <p>
+ Own Id: OTP-15983</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.3.5.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 2f7ff2fc07..6b167e7203 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -327,7 +327,7 @@
</func>
<func>
- <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP @OTP-15731@"/>
+ <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP 22.1"/>
<fsummary>Accept a connection on a socket.</fsummary>
<desc>
<p>Accept a connection on a socket.</p>
@@ -362,7 +362,7 @@
</func>
<func>
- <name name="cancel" arity="2" since="OTP @OTP-15731@"/>
+ <name name="cancel" arity="2" since="OTP 22.1"/>
<fsummary>Cancel an asynchronous request.</fsummary>
<desc>
<p>Cancel an asynchronous request.</p>
@@ -403,7 +403,7 @@
</func>
<func>
- <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP @OTP-15731@"/>
+ <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP 22.1"/>
<fsummary>Initiate a connection on a socket.</fsummary>
<desc>
<p>This function connects the socket to the address
@@ -471,7 +471,7 @@
</func>
<func>
- <name name="info" arity="1" since="OTP @OTP-15818@"/>
+ <name name="info" arity="1" since="OTP 22.1"/>
<fsummary>Get miscellaneous socket info.</fsummary>
<desc>
<p>Get miscellaneous info about the socket.</p>
@@ -544,8 +544,8 @@
</func>
<func>
- <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP @OTP-15731@"/>
- <name name="recv" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP 22.1"/>
+ <name name="recv" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Receive a message from a socket.</fsummary>
<desc>
<p>Receive a message from a socket.</p>
@@ -596,9 +596,9 @@
</func>
<func>
- <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP @OTP-15731@"/>
- <name name="recvfrom" arity="3" clause_i="4" since="OTP @OTP-15731@"/>
- <name name="recvfrom" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP 22.1"/>
+ <name name="recvfrom" arity="3" clause_i="4" since="OTP 22.1"/>
+ <name name="recvfrom" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Receive a message from a socket.</fsummary>
<desc>
<p>Receive a message from a socket.</p>
@@ -663,9 +663,9 @@
</func>
<func>
- <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP @OTP-15731@"/>
- <name name="recvmsg" arity="3" clause_i="1" since="OTP @OTP-15731@"/>
- <name name="recvmsg" arity="5" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP 22.1"/>
+ <name name="recvmsg" arity="3" clause_i="1" since="OTP 22.1"/>
+ <name name="recvmsg" arity="5" clause_i="1" since="OTP 22.1"/>
<fsummary>Receive a message from a socket.</fsummary>
<desc>
<p>Receive a message from a socket.</p>
@@ -716,8 +716,8 @@
</func>
<func>
- <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP @OTP-15731@"/>
- <name name="send" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP 22.1"/>
+ <name name="send" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Send a message on a socket.</fsummary>
<desc>
<p>Send a message on a connected socket.</p>
@@ -764,8 +764,8 @@
</func>
<func>
- <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP @OTP-15731@"/>
- <name name="sendmsg" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP 22.1"/>
+ <name name="sendmsg" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Send a message on a socket.</fsummary>
<desc>
<p>Send a message on a socket. The destination, if needed
@@ -806,8 +806,8 @@
</func>
<func>
- <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP @OTP-15731@"/>
- <name name="sendto" arity="5" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP 22.1"/>
+ <name name="sendto" arity="5" clause_i="1" since="OTP 22.1"/>
<fsummary>Send a message on a socket.</fsummary>
<desc>
<p>Send a message on a socket, to the specified destination.</p>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index ba5ba8abef..7f50372532 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -741,8 +741,8 @@ $(OBJDIR)/PROFILE: $(BINDIR)/$(PROFILE_EXECUTABLE)
$(V_at)echo " PROFILE ${PROFILE_EXECUTABLE}"
$(V_at)rm -f $(OBJDIR)/erl*.profraw
$(V_at)set -e; LLVM_PROFILE_FILE="$(OBJDIR)/erlc-%m.profraw" \
- ERL_FLAGS="-emu_type prof${TYPEMARKER} +S 1" $(ERLC) -DPGO \
- -o $(OBJDIR) test/estone_SUITE.erl > $(OBJDIR)/PROFILE_LOG
+ ERL_FLAGS="-emu_type prof${TYPEMARKER} +S 1" $(ERLC) -no-server \
+ -DPGO -o $(OBJDIR) test/estone_SUITE.erl > $(OBJDIR)/PROFILE_LOG
$(V_at)set -e; LLVM_PROFILE_FILE="$(OBJDIR)/erl-%m.profraw" \
ERL_FLAGS="-emu_type prof${TYPEMARKER} +S 1" $(ERL) -pa $(OBJDIR) \
-noshell -s estone_SUITE pgo -s init stop >> $(OBJDIR)/PROFILE_LOG
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 412d689246..93ba56dccd 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -248,6 +248,7 @@ atom erts_debug
atom erts_dflags
atom erts_internal
atom ets
+atom ets_info_binary
atom ETS_TRANSFER='ETS-TRANSFER'
atom exact_reductions
atom exception_from
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 602db106b1..c9f5177bd3 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -195,6 +195,8 @@ bif erts_internal:dirty_process_handle_signals/1
bif erts_internal:create_dist_channel/4
+bif erts_internal:ets_super_user/1
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -742,3 +744,11 @@ bif erts_internal:spawn_system_process/3
bif erlang:integer_to_list/2
bif erlang:integer_to_binary/2
bif persistent_term:get/2
+
+#
+# New in 22.1
+#
+
+bif erts_internal:ets_lookup_binary_info/2
+bif erts_internal:ets_raw_first/1
+bif erts_internal:ets_raw_next/2
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 4d8c3eb9dd..b06b5fc1ab 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -161,23 +161,24 @@ current_function(Process* p, ErtsHeapFactory *hfact, Process* rp,
static Eterm current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
Uint reserve_size);
-static Eterm
-bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail)
+Eterm
+erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail)
{
- struct erl_off_heap_header* ohh;
+ union erl_off_heap_ptr u;
Eterm res = tail;
Eterm tuple;
+ struct erts_tmp_aligned_offheap tmp;
- for (ohh = oh->first; ohh; ohh = ohh->next) {
- if (ohh->thing_word == HEADER_PROC_BIN) {
- ProcBin* pb = (ProcBin*) ohh;
- Eterm val = erts_bld_uword(hpp, szp, (UWord) pb->val);
- Eterm orig_size = erts_bld_uint(hpp, szp, pb->val->orig_size);
+ for (u.hdr = oh->first; u.hdr; u.hdr = u.hdr->next) {
+ erts_align_offheap(&u, &tmp);
+ if (u.hdr->thing_word == HEADER_PROC_BIN) {
+ Eterm val = erts_bld_uword(hpp, szp, (UWord) u.pb->val);
+ Eterm orig_size = erts_bld_uint(hpp, szp, u.pb->val->orig_size);
if (szp)
*szp += 4+2;
if (hpp) {
- Uint refc = (Uint) erts_refc_read(&pb->val->intern.refc, 1);
+ Uint refc = (Uint) erts_refc_read(&u.pb->val->intern.refc, 1);
tuple = TUPLE3(*hpp, val, orig_size, make_small(refc));
res = CONS(*hpp + 4, tuple, res);
*hpp += 4+2;
@@ -1870,16 +1871,16 @@ process_info_aux(Process *c_p,
res = NIL;
sz = 0;
- (void)bld_bin_list(NULL, &sz, &MSO(rp), NIL);
+ (void)erts_bld_bin_list(NULL, &sz, &MSO(rp), NIL);
for (hfrag = rp->mbuf; hfrag != NULL; hfrag = hfrag->next) {
- (void)bld_bin_list(NULL, &sz, &hfrag->off_heap, NIL);
+ (void)erts_bld_bin_list(NULL, &sz, &hfrag->off_heap, NIL);
}
hp = erts_produce_heap(hfact, sz, reserve_size);
- res = bld_bin_list(&hp, NULL, &MSO(rp), NIL);
+ res = erts_bld_bin_list(&hp, NULL, &MSO(rp), NIL);
for (hfrag = rp->mbuf; hfrag != NULL; hfrag = hfrag->next) {
- res = bld_bin_list(&hp, NULL, &hfrag->off_heap, res);
+ res = erts_bld_bin_list(&hp, NULL, &hfrag->off_heap, res);
}
break;
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index 8662a9e33a..9dc5c66a0a 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -1167,6 +1167,7 @@ release_update_permission(int release_updater)
erts_resume(updater_process, ERTS_PROC_LOCK_STATUS);
}
erts_proc_unlock(updater_process, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(updater_process);
}
updater_process = NULL;
@@ -1193,6 +1194,7 @@ suspend_updater(Process* c_p)
ASSERT(updater_process == c_p);
erts_mtx_unlock(&update_table_permission_mtx);
#endif
+ erts_proc_inc_refc(c_p);
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
}
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index 66b59ef1a3..f5a35f2222 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -311,6 +311,7 @@ ERTS_GLB_INLINE void erts_free_aligned_binary_bytes(byte* buf);
ERTS_GLB_INLINE void erts_free_aligned_binary_bytes_extra(byte* buf, ErtsAlcType_t);
ERTS_GLB_INLINE Binary *erts_bin_drv_alloc_fnf(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_drv_alloc(Uint size);
+ERTS_GLB_INLINE Binary *erts_bin_nrml_alloc_fnf(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_nrml_alloc(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_realloc_fnf(Binary *bp, Uint size);
ERTS_GLB_INLINE Binary *erts_bin_realloc(Binary *bp, Uint size);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 3c74ef493b..285c23ea01 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -399,7 +399,9 @@ Export ets_select_continue_exp;
* Static traps
*/
static Export ets_delete_continue_exp;
-
+
+static Export *ets_info_binary_trap = NULL;
+
static void
free_dbtable(void *vtb)
{
@@ -675,7 +677,8 @@ static DbTable* handle_lacking_permission(Process* p, DbTable* tb,
tb = NULL;
*freason_p = TRAP;
}
- else if (p->common.id != tb->common.owner) {
+ else if (p->common.id != tb->common.owner
+ && !(p->flags & F_ETS_SUPER_USER)) {
db_unlock(tb, kind);
tb = NULL;
*freason_p = BADARG;
@@ -3411,6 +3414,10 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2)
}
BIF_RET(erts_make_integer(res, BIF_P));
}
+
+ if (BIF_ARG_2 == am_binary)
+ BIF_TRAP1(ets_info_binary_trap, BIF_P, BIF_ARG_1);
+
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) {
if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1)))
BIF_RET(am_undefined);
@@ -3509,6 +3516,81 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
BIF_RET2(ret,i);
}
+BIF_RETTYPE erts_internal_ets_lookup_binary_info_2(BIF_ALIST_2)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_erts_internal_ets_lookup_binary_info_2);
+
+ cret = tb->common.meth->db_get_binary_info(BIF_P, tb, BIF_ARG_2, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ switch (cret) {
+ case DB_ERROR_NONE:
+ BIF_RET(ret);
+ case DB_ERROR_SYSRES:
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ default:
+ BIF_ERROR(BIF_P, BADARG);
+ }
+}
+
+BIF_RETTYPE erts_internal_ets_raw_first_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_erts_internal_ets_raw_first_1);
+
+ cret = tb->common.meth->db_raw_first(BIF_P, tb, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
+BIF_RETTYPE erts_internal_ets_raw_next_2(BIF_ALIST_2)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_erts_internal_ets_raw_next_2);
+
+ cret = tb->common.meth->db_raw_next(BIF_P, tb, BIF_ARG_2, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
+BIF_RETTYPE
+erts_internal_ets_super_user_1(BIF_ALIST_1)
+{
+ if (BIF_ARG_1 == am_true)
+ BIF_P->flags |= F_ETS_SUPER_USER;
+ else if (BIF_ARG_1 == am_false)
+ BIF_P->flags &= ~F_ETS_SUPER_USER;
+ else
+ BIF_ERROR(BIF_P, BADARG);
+ BIF_RET(am_ok);
+}
/*
** External interface (NOT BIF's)
@@ -3628,6 +3710,12 @@ void init_db(ErtsDbSpinCount db_spin_count)
erts_init_trap_export(&ets_delete_continue_exp,
am_ets, ERTS_MAKE_AM("delete_trap"), 1,
&ets_delete_trap);
+
+ /* ets:info(Tab, binary) trap... */
+
+ ets_info_binary_trap = erts_export_put(am_erts_internal,
+ am_ets_info_binary,
+ 1);
}
void
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index 2d213baa25..176966edb5 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -159,6 +159,7 @@ static int
db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *);
+static int db_get_binary_info_catree(Process*, DbTable*, Eterm key, Eterm *ret);
static void split_catree(DbTableCATree *tb,
DbTableCATreeNode* ERTS_RESTRICT base,
@@ -211,8 +212,10 @@ DbTableMethod db_catree =
db_print_catree,
db_foreach_offheap_catree,
db_lookup_dbterm_catree,
- db_finalize_dbterm_catree
-
+ db_finalize_dbterm_catree,
+ db_get_binary_info_catree,
+ db_first_catree, /* raw_first same as first */
+ db_next_catree /* raw_next same as next */
};
/*
@@ -2293,6 +2296,19 @@ static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *handle)
return;
}
+static int db_get_binary_info_catree(Process *p, DbTable *tbl, Eterm key,
+ Eterm *ret)
+{
+ DbTableCATree *tb = &tbl->catree;
+ FindBaseNode fbn;
+ DbTableCATreeNode* node = find_wlock_valid_base_node(tb, key, &fbn);
+ TreeDbTerm* this = db_find_tree_node_common(&tbl->common, node->u.base.root,
+ key);
+ *ret = db_binary_info_tree_common(p, this);
+ wunlock_base_node(node);
+ return DB_ERROR_NONE;
+}
+
#ifdef ERTS_ENABLE_LOCK_COUNT
static void erts_lcnt_enable_db_catree_lock_count_helper(DbTableCATree *tb,
DbTableCATreeNode *node,
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 4904d3fa42..5937bd64ec 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -469,6 +469,11 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
static void
db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle);
+static int
+db_get_binary_info_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
+static int db_raw_first_hash(Process* p, DbTable *tbl, Eterm *ret);
+static int db_raw_next_hash(Process* p, DbTable *tbl, Eterm key, Eterm *ret);
+
static ERTS_INLINE void try_shrink(DbTableHash* tb)
{
int nactive = NACTIVE(tb);
@@ -569,7 +574,10 @@ DbTableMethod db_hash =
db_print_hash,
db_foreach_offheap_hash,
db_lookup_dbterm_hash,
- db_finalize_dbterm_hash
+ db_finalize_dbterm_hash,
+ db_get_binary_info_hash,
+ db_raw_first_hash,
+ db_raw_next_hash
};
#ifdef DEBUG
@@ -771,7 +779,7 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
b = next_live(tb, &ix, &lck, b->next);
if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) {
while (b != 0) {
- if (!has_live_key(tb, b, key, hval)) {
+ if (!has_key(tb, b, key, hval) && !is_pseudo_deleted(b)) {
break;
}
b = next_live(tb, &ix, &lck, b->next);
@@ -3208,6 +3216,12 @@ void db_foreach_offheap_hash(DbTable *tbl,
int i;
int nactive = NACTIVE(tb);
+ if (nactive > tb->nslots) {
+ /* Table is being emptied by delete/1 or delete_all_objects/1 */
+ ASSERT(!(tb->common.status & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC)));
+ nactive = tb->nslots;
+ }
+
for (i = 0; i < nactive; i++) {
list = BUCKET(tb,i);
while(list != 0) {
@@ -3256,6 +3270,130 @@ void db_calc_stats_hash(DbTableHash* tb, DbHashStats* stats)
stats->kept_items = kept_items;
}
+
+/*
+ * erts_internal:ets_lookup_binary_info/2
+ */
+static int db_get_binary_info_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashValue hval;
+ int ix;
+ HashDbTerm *b, *first, *end;
+ erts_rwmtx_t* lck;
+ Eterm *hp, *hp_end;
+ Uint hsz;
+ Eterm list;
+
+ hval = MAKE_HASH(key);
+ lck = RLOCK_HASH(tb,hval);
+ ix = hash_to_ix(tb, hval);
+ b = BUCKET(tb, ix);
+
+ while(b != 0) {
+ if (has_key(tb, b, key, hval)) {
+ goto found_key;
+ }
+ b = b->next;
+ }
+ RUNLOCK_HASH(lck);
+ *ret = NIL;
+ return DB_ERROR_NONE;
+
+found_key:
+
+ first = b;
+ hsz = 0;
+ do {
+ ErlOffHeap oh;
+ oh.first = b->dbterm.first_oh;
+ erts_bld_bin_list(NULL, &hsz, &oh, NIL);
+ b = b->next;
+ } while (b && has_key(tb, b, key, hval));
+ end = b;
+
+ hp = HAlloc(p, hsz);
+ hp_end = hp + hsz;
+ list = NIL;
+ for (b = first; b != end; b = b->next) {
+ ErlOffHeap oh;
+ oh.first = b->dbterm.first_oh;
+ list = erts_bld_bin_list(&hp, NULL, &oh, list);
+ }
+ ASSERT(hp == hp_end); (void)hp_end;
+
+ RUNLOCK_HASH(lck);
+ *ret = list;
+ return DB_ERROR_NONE;
+}
+
+static int raw_find_next(Process *p, DbTable* tbl, Uint ix,
+ erts_rwmtx_t* lck, Eterm *ret)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashDbTerm *b;
+
+ do {
+ b = BUCKET(tb,ix);
+ if (b) {
+ *ret = db_copy_key(p, tbl, &b->dbterm);
+ RUNLOCK_HASH(lck);
+ return DB_ERROR_NONE;
+ }
+ ix = next_slot(tb, ix, &lck);
+ } while (ix);
+
+ *ret = am_EOT;
+ return DB_ERROR_NONE;
+}
+
+static int db_raw_first_hash(Process *p, DbTable *tbl, Eterm *ret)
+{
+ const Uint ix = 0;
+ return raw_find_next(p, tbl, ix, RLOCK_HASH(&tbl->hash, ix), ret);
+}
+
+static int db_raw_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashValue hval;
+ Uint ix;
+ HashDbTerm* b;
+ erts_rwmtx_t* lck;
+
+ hval = MAKE_HASH(key);
+ lck = RLOCK_HASH(tb,hval);
+ ix = hash_to_ix(tb, hval);
+ b = BUCKET(tb, ix);
+
+ for (;;) {
+ if (b == NULL) {
+ RUNLOCK_HASH(lck);
+ return DB_ERROR_BADKEY;
+ }
+ if (has_key(tb, b, key, hval)) {
+ break;
+ }
+ b = b->next;
+ }
+ /* Key found */
+
+ for (b = b->next; b; b = b->next) {
+ if (!has_key(tb, b, key, hval)) {
+ *ret = db_copy_key(p, tbl, &b->dbterm);
+ RUNLOCK_HASH(lck);
+ return DB_ERROR_NONE;
+ }
+ }
+
+ ix = next_slot(tb, ix, &lck);
+ if (ix)
+ return raw_find_next(p, tbl, ix, lck, ret);
+
+ *ret = am_EOT;
+ return DB_ERROR_NONE;
+}
+
/* For testing only */
Eterm erts_ets_hash_sizeof_ext_segtab(void)
{
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 19b94b0634..723b3c5d29 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -459,6 +459,9 @@ db_lookup_dbterm_tree(Process *, DbTable *, Eterm key, Eterm obj,
static void
db_finalize_dbterm_tree(int cret, DbUpdateHandle *);
+static int db_get_binary_info_tree(Process*, DbTable*, Eterm key, Eterm *ret);
+
+
/*
** Static variables
*/
@@ -499,8 +502,10 @@ DbTableMethod db_tree =
db_print_tree,
db_foreach_offheap_tree,
db_lookup_dbterm_tree,
- db_finalize_dbterm_tree
-
+ db_finalize_dbterm_tree,
+ db_get_binary_info_tree,
+ db_first_tree, /* raw_first same as first */
+ db_next_tree /* raw_next same as next */
};
@@ -3151,6 +3156,14 @@ static TreeDbTerm *find_node(DbTableCommon *tb, TreeDbTerm *root,
return this;
}
+
+TreeDbTerm *db_find_tree_node_common(DbTableCommon *tb, TreeDbTerm *root,
+ Eterm key)
+{
+ return find_node(tb, root, key, NULL);
+}
+
+
/*
* Lookup a node and return the address of the node pointer in the tree
*/
@@ -3303,6 +3316,38 @@ db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle)
db_finalize_dbterm_tree_common(cret, handle, tb);
}
+static int db_get_binary_info_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ *ret = db_binary_info_tree_common(p, find_node(&tbl->common, tbl->tree.root,
+ key, &tbl->tree));
+ return DB_ERROR_NONE;
+}
+
+Eterm db_binary_info_tree_common(Process* p, TreeDbTerm* this)
+{
+ Eterm *hp, *hp_end;
+ Uint hsz;
+ Eterm ret;
+
+ if (this == NULL) {
+ ret = NIL;
+ } else {
+ ErlOffHeap oh;
+ hsz = 0;
+
+ oh.first = this->dbterm.first_oh;
+ erts_bld_bin_list(NULL, &hsz, &oh, NIL);
+
+ hp = HAlloc(p, hsz);
+ hp_end = hp + hsz;
+ oh.first = this->dbterm.first_oh;
+ ret = erts_bld_bin_list(&hp, NULL, &oh, NIL);
+ ASSERT(hp == hp_end); (void)hp_end;
+ }
+ return ret;
+}
+
+
/*
* Traverse the tree with a callback function, used by db_match_xxx
*/
diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h
index ba4a8f79e5..14afbd56f7 100644
--- a/erts/emulator/beam/erl_db_tree_util.h
+++ b/erts/emulator/beam/erl_db_tree_util.h
@@ -171,4 +171,8 @@ void db_finalize_dbterm_tree_common(int cret, DbUpdateHandle *handle,
DbTableTree *stack_container);
Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key);
+TreeDbTerm *db_find_tree_node_common(DbTableCommon*, TreeDbTerm *root,
+ Eterm key);
+Eterm db_binary_info_tree_common(Process*, TreeDbTerm*);
+
#endif /* _DB_TREE_UTIL_H */
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 1ea7074d21..ed09a34ae4 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -3285,22 +3285,15 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p,
void db_cleanup_offheap_comp(DbTerm* obj)
{
union erl_off_heap_ptr u;
- ProcBin tmp;
+ struct erts_tmp_aligned_offheap tmp;
for (u.hdr = obj->first_oh; u.hdr; u.hdr = u.hdr->next) {
- if ((UWord)u.voidp % sizeof(Uint) != 0) { /* unaligned ptr */
- sys_memcpy(&tmp, u.voidp, sizeof(tmp));
- /* Warning, must pass (void*)-variable to memcpy. Otherwise it will
- cause Bus error on Sparc due to false compile time assumptions
- about word aligned memory (type cast is not enough) */
- u.pb = &tmp;
- }
+ erts_align_offheap(&u, &tmp);
switch (thing_subtag(u.hdr->thing_word)) {
case REFC_BINARY_SUBTAG:
erts_bin_release(u.pb->val);
break;
case FUN_SUBTAG:
- ASSERT(u.pb != &tmp);
if (erts_refc_dectest(&u.fun->fe->refc, 0) == 0) {
erts_erase_fun_entry(u.fun->fe);
}
@@ -3311,7 +3304,6 @@ void db_cleanup_offheap_comp(DbTerm* obj)
break;
default:
ASSERT(is_external_header(u.hdr->thing_word));
- ASSERT(u.pb != &tmp);
erts_deref_node_entry(u.ext->node, make_boxed(u.ep));
break;
}
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 02d4dd6c9a..f038dba89a 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -235,6 +235,13 @@ typedef struct db_table_method
** not DB_ERROR_NONE, the object is removed from the table. */
void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle);
+ int (*db_get_binary_info)(Process*, DbTable* tb, Eterm key, Eterm* ret);
+
+ /* Raw first/next same as first/next but also return pseudo deleted keys.
+ Only internal use by ets:info(_,binary) */
+ int (*db_raw_first)(Process*, DbTable*, Eterm* ret);
+ int (*db_raw_next)(Process*, DbTable*, Eterm key, Eterm* ret);
+
} DbTableMethod;
typedef struct db_fixation {
@@ -536,6 +543,17 @@ ERTS_GLB_INLINE Eterm erts_db_make_match_prog_ref(Process *p, Binary *mp, Eterm
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary(Eterm term);
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary_unchecked(Eterm term);
+/* @brief Ensure off-heap header is word aligned, make a temporary copy if not.
+ * Needed when inspecting ETS off-heap lists that may contain unaligned
+ * ProcBins if table is 'compressed'.
+ */
+struct erts_tmp_aligned_offheap
+{
+ ProcBin proc_bin;
+};
+ERTS_GLB_INLINE void erts_align_offheap(union erl_off_heap_ptr*,
+ struct erts_tmp_aligned_offheap* tmp);
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
/*
@@ -568,6 +586,25 @@ erts_db_get_match_prog_binary(Eterm term)
return bp;
}
+ERTS_GLB_INLINE void
+erts_align_offheap(union erl_off_heap_ptr* ohp,
+ struct erts_tmp_aligned_offheap* tmp)
+{
+ if ((UWord)ohp->voidp % sizeof(UWord) != 0) {
+ /*
+ * ETS store word unaligned ProcBins in its compressed format.
+ * Make a temporary aligned copy.
+ *
+ * Warning, must pass (void*)-variable to memcpy. Otherwise it will
+ * cause Bus error on Sparc due to false compile time assumptions
+ * about word aligned memory (type cast is not enough).
+ */
+ sys_memcpy(tmp, ohp->voidp, sizeof(*tmp));
+ ASSERT(tmp->proc_bin.thing_word == HEADER_PROC_BIN);
+ ohp->pb = &tmp->proc_bin;
+ }
+}
+
#endif
/*
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 50e9812534..8863e219e2 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1509,10 +1509,12 @@ static void
insert_offheap(ErlOffHeap *oh, int type, Eterm id)
{
union erl_off_heap_ptr u;
+ struct erts_tmp_aligned_offheap tmp;
struct insert_offheap2_arg a;
a.type = BIN_REF;
for (u.hdr = oh->first; u.hdr; u.hdr = u.hdr->next) {
+ erts_align_offheap(&u, &tmp);
switch (thing_subtag(u.hdr->thing_word)) {
case REF_SUBTAG:
if (ErtsIsDistEntryBinary(u.mref->mb))
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 1c1ef1db84..ac0a4f8902 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -12072,6 +12072,8 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
ASSERT(c_p->flags & F_DISABLE_GC);
ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
+ ASSERT(ctxt->dist_state == NIL);
+ ASSERT(!ctxt->yield);
mdp = erts_monitor_to_data(mon);
@@ -12116,10 +12118,14 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
reason);
reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
- case ERTS_DSIG_SEND_CONTINUE:
case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
+ case ERTS_DSIG_SEND_CONTINUE:
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
break;
case ERTS_DSIG_SEND_OK:
break;
@@ -12330,6 +12336,9 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
ASSERT(c_p->flags & F_DISABLE_GC);
ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ ASSERT(ctxt->dist_state == NIL);
+ ASSERT(!ctxt->yield);
+
dlnk = erts_link_to_other(lnk, &ldp);
dist = ((ErtsLinkDataExtended *) ldp)->dist;
@@ -12367,9 +12376,13 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
case ERTS_DSIG_SEND_CONTINUE:
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
break;
case ERTS_DSIG_SEND_OK:
break;
@@ -12792,6 +12805,7 @@ restart:
trap_state->pectxt.dist_links = NULL;
trap_state->pectxt.dist_monitors = NULL;
trap_state->pectxt.dist_state = NIL;
+ trap_state->pectxt.yield = 0;
erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
@@ -12875,6 +12889,8 @@ restart:
erts_kill_dist_connection(ctx->dep, ctx->connection_id);
break;
case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/
+ trap_state->pectxt.dist_state = NIL;
+ goto yield;
case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/
goto yield;
}
@@ -12892,7 +12908,7 @@ restart:
(void *) &trap_state->pectxt,
&trap_state->yield_state,
reds);
- if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state))
+ if (reds <= 0 || trap_state->pectxt.yield)
goto yield;
trap_state->phase = ERTS_CONTINUE_EXIT_DIST_MONITORS;
}
@@ -12907,7 +12923,7 @@ restart:
(void *) &trap_state->pectxt,
&trap_state->yield_state,
reds);
- if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state))
+ if (reds <= 0 || trap_state->pectxt.yield)
goto yield;
trap_state->phase = ERTS_CONTINUE_EXIT_DONE;
@@ -12992,6 +13008,7 @@ restart:
sys_memcpy(trap_state, &static_state, sizeof(*trap_state));
p->u.terminate = trap_state;
}
+ trap_state->pectxt.yield = 0;
ASSERT(p->scheduler_data);
ASSERT(p->scheduler_data->current_process == p);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index c0d7cfd13d..5886b576e0 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1371,7 +1371,7 @@ extern int erts_system_profile_ts_type;
#define F_DISTRIBUTION (1 << 6) /* Process used in distribution */
#define F_USING_DDLL (1 << 7) /* Process has used the DDLL interface */
#define F_HAVE_BLCKD_MSCHED (1 << 8) /* Process has blocked multi-scheduling */
-#define F_UNUSED (1 << 9)
+#define F_ETS_SUPER_USER (1 << 9) /* Process is ETS super user */
#define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */
#define F_DISABLE_GC (1 << 11) /* Disable GC (see below) */
#define F_OFF_HEAP_MSGQ (1 << 12) /* Off heap msg queue */
@@ -1831,6 +1831,7 @@ typedef struct {
ErtsLink *dist_links;
ErtsMonitor *dist_monitors;
Eterm dist_state;
+ int yield;
} ErtsProcExitContext;
int erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds);
int erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds);
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 1d6869a7cd..9fd3012888 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -800,7 +800,29 @@ static int check_leftovers(byte *source, int size)
}
return -1;
}
-
+
+
+static Eterm
+mk_utf8_result_bin(Process *p, Eterm bin)
+{
+ /*
+ * Don't let small refc-binaries escape out in the system
+ * when done. That is, convert such to heap binaries.
+ */
+ Uint size = binary_size(bin);
+
+ ASSERT(*binary_val(bin) == HEADER_PROC_BIN);
+
+ if (size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb = (ErlHeapBin *) HAlloc(p, heap_bin_size(size));
+ hb->thing_word = header_heap_bin(size);
+ hb->size = size;
+ sys_memcpy(hb->data, binary_bytes(bin), size);
+ return make_binary(hb);
+ }
+
+ return bin;
+}
static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
@@ -822,15 +844,15 @@ static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
} else {
hp = HAlloc(p,4);
}
- ret = TUPLE3(hp,am_error,bin,rest_term);
+ ret = TUPLE3(hp,am_error,mk_utf8_result_bin(p,bin),rest_term);
} else if (rest_term == NIL && num_leftovers != 0) {
Eterm leftover_bin = new_binary(p, leftover, num_leftovers);
if (check_leftovers(leftover,num_leftovers) != 0) {
hp = HAlloc(p,4);
- ret = TUPLE3(hp,am_error,bin,leftover_bin);
+ ret = TUPLE3(hp,am_error,mk_utf8_result_bin(p,bin),leftover_bin);
} else {
hp = HAlloc(p,4);
- ret = TUPLE3(hp,am_incomplete,bin,leftover_bin);
+ ret = TUPLE3(hp,am_incomplete,mk_utf8_result_bin(p,bin),leftover_bin);
}
} else { /* All OK */
if (rest_term != NIL) { /* Trap */
@@ -843,8 +865,8 @@ static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
BIF_TRAP3(&characters_to_utf8_trap_exp, p, bin, rest_term, latin1);
} else { /* Success */
/*hp = HAlloc(p,5);
- ret = TUPLE4(hp,bin,rest_term,make_small(pos),make_small(err));*/
- ret = bin;
+ ret = TUPLE4(hp,mk_utf8_result_bin(p,bin),rest_term,make_small(pos),make_small(err));*/
+ ret = mk_utf8_result_bin(p,bin);
}
}
BIF_RET(ret);
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index ce61cdf040..39bbf62eae 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1414,7 +1414,7 @@ enum B2TState { /* order is somewhat significant */
};
typedef struct {
- int heap_size;
+ Sint heap_size;
int terms;
byte* ep;
int atom_extra_skip;
@@ -4633,7 +4633,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
static Sint
decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx)
{
- int heap_size;
+ Sint heap_size;
int terms;
int atom_extra_skip;
Uint n;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 0c2cf98033..40c65461bc 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -878,6 +878,8 @@ erts_bld_port_info(Eterm **hpp,
Uint *szp,
Port *prt,
Eterm item);
+Eterm erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail);
+
void erts_bif_info_init(void);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 45fef0c0e5..1622330aab 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -3243,7 +3243,10 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
need += 3;
}
if ((state & ERTS_PORT_SFLG_BINARY_IO) && buf != NULL) {
- need += PROC_BIN_SIZE;
+ if (len <= ERL_ONHEAP_BIN_LIMIT)
+ need += heap_bin_size(len);
+ else
+ need += PROC_BIN_SIZE;
} else {
need += 2*len;
}
@@ -3261,11 +3264,21 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) {
listp = buf_to_intlist(&hp, buf, len, listp);
} else if (buf != NULL) {
- Binary* bptr = erts_bin_nrml_alloc(len);
- sys_memcpy(bptr->orig_bytes, buf, len);
+ if (len <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin *hbin = (ErlHeapBin *) hp;
+ hbin->thing_word = header_heap_bin(len);
+ hbin->size = (Uint) len;
+ sys_memcpy(hbin->data, buf, len);
+ listp = make_binary(hp);
+ hp += heap_bin_size(len);
+ }
+ else {
+ Binary* bptr = erts_bin_nrml_alloc(len);
+ sys_memcpy(bptr->orig_bytes, buf, len);
- listp = erts_build_proc_bin(ohp, hp, bptr);
- hp += PROC_BIN_SIZE;
+ listp = erts_build_proc_bin(ohp, hp, bptr);
+ hp += PROC_BIN_SIZE;
+ }
}
/* Prepend the header */
@@ -3388,7 +3401,14 @@ deliver_vec_message(Port* prt, /* Port */
need = 3 + 3; /* Heap space for two tuples */
if (state & ERTS_PORT_SFLG_BINARY_IO) {
- need += (2+PROC_BIN_SIZE)*vsize - 2 + hlen*2;
+ Sint i;
+ for (i = 0; i < vsize; i++) {
+ if (iov[i].iov_len <= ERL_ONHEAP_BIN_LIMIT)
+ need += heap_bin_size(iov[i].iov_len);
+ else
+ need += PROC_BIN_SIZE;
+ }
+ need += (vsize - 1)*2 + hlen*2;
} else {
need += (hlen+csize)*2;
}
@@ -3408,36 +3428,52 @@ deliver_vec_message(Port* prt, /* Port */
} else {
binv += vsize;
while (vsize--) {
- ErlDrvBinary* b;
- ProcBin* pb = (ProcBin*) hp;
- byte* base;
-
- iov--;
- binv--;
- if ((b = *binv) == NULL) {
- b = driver_alloc_binary(iov->iov_len);
- sys_memcpy(b->orig_bytes, iov->iov_base, iov->iov_len);
- base = (byte*) b->orig_bytes;
- } else {
- /* Must increment reference count, caller calls free */
- driver_binary_inc_refc(b);
- base = iov->iov_base;
- }
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = iov->iov_len;
- pb->next = ohp->first;
- ohp->first = (struct erl_off_heap_header*)pb;
- pb->val = ErlDrvBinary2Binary(b);
- pb->bytes = base;
- pb->flags = 0;
- hp += PROC_BIN_SIZE;
+ Eterm bin;
+ Uint bin_size;
+ iov--;
+ binv--;
+ bin_size = (Uint) iov->iov_len;
+
+ if (bin_size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin *hbin = (ErlHeapBin *) hp;
+ hbin->thing_word = header_heap_bin(bin_size);
+ hbin->size = bin_size;
+ sys_memcpy(hbin->data, iov->iov_base, bin_size);
+ bin = make_binary(hp);
+ hp += heap_bin_size(bin_size);
+ }
+ else {
+ ErlDrvBinary* b;
+ ProcBin* pb = (ProcBin*) hp;
+ byte* base;
+
+ if ((b = *binv) == NULL) {
+ b = driver_alloc_binary(bin_size);
+ sys_memcpy(b->orig_bytes, iov->iov_base, bin_size);
+ base = (byte*) b->orig_bytes;
+ } else {
+ /* Must increment reference count, caller calls free */
+ driver_binary_inc_refc(b);
+ base = iov->iov_base;
+ }
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = bin_size;
+ pb->next = ohp->first;
+ ohp->first = (struct erl_off_heap_header*)pb;
+ pb->val = ErlDrvBinary2Binary(b);
+ pb->bytes = base;
+ pb->flags = 0;
+ hp += PROC_BIN_SIZE;
- OH_OVERHEAD(ohp, iov->iov_len / sizeof(Eterm));
+ OH_OVERHEAD(ohp, bin_size / sizeof(Eterm));
+
+ bin = make_binary(pb);
+ }
if (listp == NIL) { /* compatible with deliver_bin_message */
- listp = make_binary(pb);
+ listp = bin;
} else {
- listp = CONS(hp, make_binary(pb), listp);
+ listp = CONS(hp, bin, listp);
hp += 2;
}
}
diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c
index d3b6933155..df8e1825bc 100644
--- a/erts/emulator/hipe/hipe_x86_signal.c
+++ b/erts/emulator/hipe/hipe_x86_signal.c
@@ -159,19 +159,9 @@
#if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__))
/*
- * Unknown libc -- assume musl. Note: musl deliberately does not provide a musl-specific
- * feature test macro, so we cannot check for it.
- *
- * sigaction is a weak alias for __sigaction, which is a wrapper for __libc_sigaction.
- * There are libc-internal calls to __libc_sigaction which install handlers, so we must
- * override __libc_sigaction rather than __sigaction.
+ * Unknown libc -- assume musl, which does not allow safe signals
*/
-#define NEXT_SIGACTION "__libc_sigaction"
-#define LIBC_SIGACTION __libc_sigaction
-#define OVERRIDE_SIGACTION
-#ifndef _NSIG
-#define _NSIG NSIG
-#endif
+#error "HiPE does not work without a libc that can guarantee that sigaltstack works"
#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */
#if defined(NEXT_SIGACTION)
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
index 169b193993..fbf312a3b8 100644
--- a/erts/emulator/nifs/unix/unix_prim_file.c
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -559,18 +559,54 @@ int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) {
} while(ret < 0 && errno == EINTR);
#elif defined(F_PREALLOCATE)
/* Mac-specific */
+ off_t original_position, eof_offset;
fstore_t fs = {};
+ if(offset < 0 || length < 0 || (offset > ERTS_SINT64_MAX - length)) {
+ u->common.posix_errno = EINVAL;
+ return 0;
+ }
+
+ original_position = lseek(u->fd, 0, SEEK_CUR);
+
+ if(original_position < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ eof_offset = lseek(u->fd, 0, SEEK_END);
+
+ if(eof_offset < 0 || lseek(u->fd, original_position, SEEK_SET) < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+
+ if(offset + length <= eof_offset) {
+ /* File is already large enough. */
+ return 1;
+ }
+
fs.fst_flags = F_ALLOCATECONTIG;
- fs.fst_posmode = F_VOLPOSMODE;
- fs.fst_offset = offset;
- fs.fst_length = length;
+ fs.fst_posmode = F_PEOFPOSMODE;
+ fs.fst_offset = 0;
+ fs.fst_length = (offset + length) - eof_offset;
ret = fcntl(u->fd, F_PREALLOCATE, &fs);
if(ret < 0) {
fs.fst_flags = F_ALLOCATEALL;
ret = fcntl(u->fd, F_PREALLOCATE, &fs);
}
+
+ if(ret >= 0) {
+ /* We MUST truncate since F_PREALLOCATE works relative to end-of-file,
+ * otherwise we will expand the file on repeated calls to
+ * file:allocate/3 with the same arguments. */
+ ret = ftruncate(u->fd, offset + length);
+ if(ret < 0) {
+ u->common.posix_errno = errno;
+ return 0;
+ }
+ }
#elif !defined(HAVE_POSIX_FALLOCATE)
u->common.posix_errno = ENOTSUP;
return 0;
diff --git a/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl
index 2dbaec9942..7bee7cf1d4 100644
--- a/erts/emulator/test/lcnt_SUITE.erl
+++ b/erts/emulator/test/lcnt_SUITE.erl
@@ -24,8 +24,7 @@
-export(
[all/0, suite/0,
- init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, end_per_testcase/2]).
+ init_per_suite/1, end_per_suite/1]).
-export(
[toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1,
@@ -63,13 +62,6 @@ end_per_suite(Config) ->
erts_debug:lcnt_clear(),
ok.
-init_per_testcase(_Case, Config) ->
- disable_lock_counting(),
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
-
disable_lock_counting() ->
ok = erts_debug:lcnt_control(copy_save, false),
ok = erts_debug:lcnt_control(mask, []),
@@ -96,7 +88,10 @@ wait_for_empty_lock_list(Tries) when Tries > 0 ->
wait_for_empty_lock_list(Tries - 1)
end;
wait_for_empty_lock_list(0) ->
- ct:fail("Lock list failed to clear after disabling lock counting.").
+ [{duration, _}, {locks, Locks0}] = erts_debug:lcnt_collect(),
+ Locks = remove_untoggleable_locks(Locks0),
+ ct:fail("Lock list failed to clear after disabling lock counting.~n\t~p",
+ [Locks]).
%% Queue up a lot of thread progress cleanup ops in a vain attempt to
%% flush the lock list.
@@ -109,6 +104,8 @@ try_flush_cleanup_ops() ->
%%
toggle_lock_counting(Config) when is_list(Config) ->
+ ok = disable_lock_counting(),
+
Categories =
[allocator, db, debug, distribution, generic, io, process, scheduler],
lists:foreach(
@@ -131,6 +128,8 @@ get_lock_info_for(Category) when is_atom(Category) ->
get_lock_info_for([Category]).
preserve_locks(Config) when is_list(Config) ->
+ ok = disable_lock_counting(),
+
erts_debug:lcnt_control(mask, [process]),
erts_debug:lcnt_control(copy_save, true),
@@ -155,10 +154,14 @@ preserve_locks(Config) when is_list(Config) ->
end.
error_on_invalid_category(Config) when is_list(Config) ->
+ ok = disable_lock_counting(),
+
{error, badarg, q_invalid} = erts_debug:lcnt_control(mask, [q_invalid]),
ok.
registered_processes(Config) when is_list(Config) ->
+ ok = disable_lock_counting(),
+
%% There ought to be at least one registered process (init/code_server)
erts_debug:lcnt_control(mask, [process]),
[_, {locks, ProcLocks}] = erts_debug:lcnt_collect(),
@@ -170,6 +173,8 @@ registered_processes(Config) when is_list(Config) ->
ok.
registered_db_tables(Config) when is_list(Config) ->
+ ok = disable_lock_counting(),
+
%% There ought to be at least one registered table (code)
erts_debug:lcnt_control(mask, [db]),
[_, {locks, DbLocks}] = erts_debug:lcnt_collect(),
@@ -187,7 +192,7 @@ remove_untoggleable_locks([]) ->
[];
remove_untoggleable_locks([{resource_monitors, _, _, _} | T]) ->
remove_untoggleable_locks(T);
-remove_untoggleable_locks([{'socket[gcnt]', _, _, _} | T]) ->
+remove_untoggleable_locks([{'esock[gcnt]', _, _, _} | T]) ->
%% Global lock used by socket NIF
remove_untoggleable_locks(T);
remove_untoggleable_locks([H | T]) ->
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index 19c3844c40..f19047ba71 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -27,8 +27,7 @@
-export([t_lttng_list/1,
t_carrier_pool/1,
t_memory_carrier/1,
- t_async_io_pool/1,
- t_driver_control_ready_async/1,
+ t_driver_control/1,
t_driver_start_stop/1,
t_driver_ready_input_output/1,
t_driver_timeout/1,
@@ -36,6 +35,8 @@
t_driver_flush/1,
t_scheduler_poll/1]).
+-export([ets_load/0]).
+
-include_lib("common_test/include/ct.hrl").
suite() ->
@@ -46,10 +47,9 @@ all() ->
[t_lttng_list,
t_memory_carrier,
t_carrier_pool,
- t_async_io_pool,
t_driver_start_stop,
t_driver_ready_input_output,
- t_driver_control_ready_async,
+ t_driver_control,
t_driver_timeout,
t_driver_caller,
t_driver_flush,
@@ -124,7 +124,7 @@ t_carrier_pool(Config) ->
true ->
ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config),
- ok = ets_load(),
+ ok = ets_load(Config),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res),
@@ -141,7 +141,7 @@ t_memory_carrier(Config) ->
true ->
ok = lttng_start_event("org_erlang_otp:carrier_*", Config),
- ok = ets_load(),
+ ok = ets_load(Config),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res),
@@ -149,70 +149,49 @@ t_memory_carrier(Config) ->
ok
end.
-%% org_erlang_otp:aio_pool_put
-%% org_erlang_otp:aio_pool_get
-t_async_io_pool(Config) ->
- case have_async_threads() of
- false ->
- {skip, "No Async Threads configured on system."};
- true ->
- ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config),
-
- Path1 = proplists:get_value(priv_dir, Config),
- {ok, [[Path2]]} = init:get_argument(home),
- {ok, _} = file:list_dir(Path1),
- {ok, _} = file:list_dir(Path2),
- {ok, _} = file:list_dir(Path1),
- {ok, _} = file:list_dir(Path2),
-
- Res = lttng_stop_and_view(Config),
- ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res),
- ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res),
- ok
- end.
-
-
%% org_erlang_otp:driver_start
%% org_erlang_otp:driver_stop
t_driver_start_stop(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_*", Config),
timer:sleep(500),
- Path = proplists:get_value(priv_dir, Config),
- Name = filename:join(Path, "sometext.txt"),
- Bin = txt(),
- ok = file:write_file(Name, Bin),
- {ok, Bin} = file:read_file(Name),
+ os:cmd("echo hello"),
timer:sleep(500),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_start", Res),
ok = check_tracepoint("org_erlang_otp:driver_stop", Res),
- ok = check_tracepoint("org_erlang_otp:driver_control", Res),
- ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
- ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_output", Res),
ok.
%% org_erlang_otp:driver_control
%% org_erlang_otp:driver_outputv
-%% org_erlang_otp:driver_ready_async
-t_driver_control_ready_async(Config) ->
+t_driver_control(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_control", Config),
ok = lttng_start_event("org_erlang_otp:driver_outputv", Config),
- ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config),
- Path = proplists:get_value(priv_dir, Config),
- Name = filename:join(Path, "sometext.txt"),
- Bin = txt(),
- ok = file:write_file(Name, Bin),
- {ok, Bin} = file:read_file(Name),
+
+ timer:sleep(500),
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, active) end),
+ receive {Pid, accept} -> ok end,
+ Bin = txt(),
+ Sz = byte_size(Bin),
+
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
+ ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ ok = gen_tcp:close(Sock),
+ receive {Pid, done} -> ok end,
+
+ timer:sleep(500),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_control", Res),
ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
- ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
ok.
%% org_erlang_otp:driver_ready_input
%% org_erlang_otp:driver_ready_output
t_driver_ready_input_output(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config),
+
timer:sleep(500),
Me = self(),
Pid = spawn_link(fun() -> tcp_server(Me, active) end),
@@ -290,8 +269,27 @@ t_driver_caller(Config) ->
t_scheduler_poll(Config) ->
ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config),
+ N = 100,
+
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, {active, N*2}) end),
+ receive {Pid, accept} -> ok end,
+
+ %% We want to create a scenario where the fd is moved into a scheduler
+ %% pollset, this means we have to send many small packages to the
+ %% same socket, but not fast enough for them to all arrive at the
+ %% same time.
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
+ [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
+
ok = memory_load(),
+ [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
+
+ ok = gen_tcp:close(Sock),
+ Pid ! die,
+ receive {Pid, done} -> ok end,
+
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res),
ok.
@@ -335,8 +333,37 @@ chk_caller(Port, Callback, ExpectedCaller) ->
ExpectedCaller = Caller
end.
+memory_load() ->
+ Me = self(),
+ Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
+ timer:sleep(50),
+ Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
+ [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
+ timer:sleep(500),
+ ok.
+
+memory_loop(Parent, N, Bin) ->
+ memory_loop(Parent, N, Bin, []).
+
+memory_loop(Parent, 0, _Bin, _) ->
+ Parent ! {self(), done};
+memory_loop(Parent, N, Bin0, Ls) ->
+ Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
+ memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
+
+ets_load(Config) ->
+
+ %% Have to do on a fresh node to guarantee that carriers are created
+ {ok,Node} = start_node(Config),
+
+ Res = rpc:call(Node, ?MODULE, ets_load, []),
+
+ stop_node(Node),
+
+ Res.
ets_load() ->
+
Tid = ets:new(ets_load, [public,set]),
N = erlang:system_info(schedulers_online),
Pids = [spawn_link(fun() -> ets_shuffle(Tid) end) || _ <- lists:seq(1,N)],
@@ -368,27 +395,6 @@ ets_shuffle(Tid, I, N, Data, Data0) ->
ets_shuffle(Tid, I - 1, N, Data1, Data0)
end.
-
-
-
-memory_load() ->
- Me = self(),
- Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- timer:sleep(50),
- Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
- timer:sleep(500),
- ok.
-
-memory_loop(Parent, N, Bin) ->
- memory_loop(Parent, N, Bin, []).
-
-memory_loop(Parent, 0, _Bin, _) ->
- Parent ! {self(), done};
-memory_loop(Parent, N, Bin0, Ls) ->
- Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
- memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
-
tcp_server(Pid, Type) ->
{ok, LSock} = gen_tcp:listen(5679, [binary,
{reuseaddr, true},
@@ -399,14 +405,19 @@ tcp_server(Pid, Type) ->
passive_no_read ->
receive die -> ok end;
active ->
- inet:setopts(Sock, [{active, once}, {packet,2}]),
+ inet:setopts(Sock, [{active, 2}, {packet,2}]),
receive Msg1 -> io:format("msg1: ~p~n", [Msg1]) end,
- inet:setopts(Sock, [{active, once}, {packet,2}]),
receive Msg2 -> io:format("msg2: ~p~n", [Msg2]) end,
ok = gen_tcp:close(Sock);
timeout ->
Res = gen_tcp:recv(Sock, 2000, 1000),
- io:format("res ~p~n", [Res])
+ io:format("res ~p~n", [Res]);
+ {active, Number} when is_number(Number) ->
+ inet:setopts(Sock, [{active, Number}, {packet,2}]),
+ [begin
+ receive _Msg1 -> Pid ! ok, io:format("msg ~p~n", [I]) end
+ end || I <- lists:seq(1,Number)],
+ ok = gen_tcp:close(Sock)
end,
Pid ! {self(), done},
ok.
@@ -497,3 +508,20 @@ cmd(Cmd) ->
Res = os:cmd(Cmd),
io:format(">> ~ts~n", [Res]),
{ok,Res}.
+
+start_node(Config) ->
+ start_node(Config, "").
+
+start_node(Config, Args) when is_list(Config) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 4980ea2a82..124ae09951 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -1776,7 +1776,7 @@ quiet_mode(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% A simple test case that tests that the global debug can be channged.
+%% A simple test case that tests that the global debug can be changed.
%% At the same time, it will test the info function (since it uses it
%% for verification).
@@ -1794,7 +1794,8 @@ api_m_debug(_Config) when is_list(_Config) ->
%% For some reason this test case triggers a gcc bug, which causes
%% a segfault, on an ancient Fedora 16 VM. So, check the version of gcc...
-%% Not pretty, but the simplest way to skip (without actually testing for the host).
+%% Not pretty, but the simplest way to skip (without actually testing
+%% for the host).
has_bugfree_gcc() ->
has_bugfree_gcc(os:type()).
@@ -19470,6 +19471,7 @@ traffic_ping_pong_large_send_and_recv_tcp4(_Config) when is_list(_Config) ->
Msg = l2b(?TPP_LARGE),
Num = ?TPP_LARGE_NUM,
tc_try(traffic_ping_pong_large_send_and_recv_tcp4,
+ fun() -> is_old_fedora16() end,
fun() ->
InitState = #{domain => inet,
proto => tcp,
@@ -19496,7 +19498,8 @@ traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) ->
Msg = l2b(?TPP_LARGE),
Num = ?TPP_LARGE_NUM,
tc_try(traffic_ping_pong_large_send_and_recv_tcp6,
- fun() -> has_support_ipv6() end,
+ fun() -> is_old_fedora16(),
+ has_support_ipv6() end,
fun() ->
?TT(?SECS(45)),
InitState = #{domain => inet6,
@@ -19545,9 +19548,28 @@ traffic_ping_pong_large_host_cond() ->
traffic_ping_pong_large_host_cond({unix, sunos}, _) ->
skip("TC does not work on platform");
+traffic_ping_pong_large_host_cond({unix, linux}, _) ->
+ traffic_ping_pong_large_host_cond2(string:trim(os:cmd("cat /etc/issue")));
traffic_ping_pong_large_host_cond(_, _) ->
ok.
+traffic_ping_pong_large_host_cond2("Welcome to SUSE Linux Enterprise Server 10 SP1 (i586)" ++ _) ->
+ skip("TC does not work on platform");
+traffic_ping_pong_large_host_cond2("Fedora release 16 " ++ _) ->
+ skip("Very slow VM");
+traffic_ping_pong_large_host_cond2(_) ->
+ ok.
+
+
+is_old_fedora16() ->
+ is_old_fedora16(string:trim(os:cmd("cat /etc/issue"))).
+
+%% We actually only have one host running this, a slow VM.
+is_old_fedora16("Fedora release 16 " ++ _) ->
+ skip("Very slow VM");
+is_old_fedora16(_) ->
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendto and recvfrom
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 1f35cef669..3a03374fbf 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -54,6 +54,8 @@ ERTS_INCL = -I$(ERL_TOP)/erts/include \
-I$(ERL_TOP)/erts/include/internal \
-I$(ERL_TOP)/erts/include/internal/$(TARGET)
+EI_INCL = -I$(ERL_TOP)/lib/erl_interface/include
+
CC = @CC@
WFLAGS = @WFLAGS@
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSOSDIR) -I$(EMUDIR) -I. \
@@ -87,6 +89,10 @@ DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
UXETC = ../unix
WINETC = ../win32
+# Threads flags and libs
+THR_DEFS=@THR_DEFS@
+THR_LIBS=@THR_LIBS@
+
ifeq ($(TARGET), win32)
ETC = $(WINETC)
else
@@ -108,6 +114,8 @@ endif
ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
+EI_LIB = -L$(ERL_TOP)/lib/erl_interface/obj/$(TARGET) -lei $(THR_LIBS)
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -426,10 +434,10 @@ $(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c $(RC_GENERATED)
endif
$(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o $(ERTS_LIB)
- $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(EI_LIB)
$(OBJDIR)/erlc.o: erlc.c $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -o $@ -c erlc.c
+ $(V_CC) $(CFLAGS) $(THR_DEFS) $(EI_INCL) $(MT_FLAG) -o $@ -c erlc.c
$(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o $(ERTS_LIB)
$(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
index aa99c69100..56060497f3 100644
--- a/erts/etc/common/erlc.c
+++ b/erts/etc/common/erlc.c
@@ -20,14 +20,32 @@
/*
* Purpose: Common compiler front-end.
*/
+
+#ifdef __WIN32__
+# include <winsock2.h>
+#else
+# include <stdio.h>
+# include <limits.h>
+# include <stdlib.h>
+# include <signal.h>
+# include <sys/stat.h>
+#endif
+
#include "etc_common.h"
+#include "ei.h"
#define NO 0
#define YES 1
#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
-static int debug = 0; /* Bit flags for debug printouts. */
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+static int debug = 0; /* Debug level. */
+static int use_server = 0; /* Use compile server. */
+static char* source_file = "<no source>"; /* Source file (last argument). */
static char** eargv_base; /* Base of vector. */
static char** eargv; /* First argument for erl. */
@@ -57,6 +75,7 @@ static int pause_after_execution = 0;
* Local functions.
*/
+static void get_env_compile_server(void);
static char* process_opt(int* pArgc, char*** pArgv, int offset);
static void error(char* format, ...);
static void* emalloc(size_t size);
@@ -66,7 +85,17 @@ static void efree(void *p);
static char* strsave(char* string);
static void push_words(char* src);
static int run_erlang(char* name, char** argv);
+static void call_compile_server(char** argv);
+static void encode_env(ei_x_buff* buf);
+#ifndef __WIN32__
+static char* find_executable(char* progname);
+static char* safe_realpath(char* file);
+#endif
+static char* get_encoding(void);
+static char* decode_binary(const char* buf, int* dec_index, int* dec_size);
+static void start_compile_server(char* node_name, char** argv);
static char* get_default_emulator(char* progname);
+static char* possibly_unquote(char* arg);
#ifdef __WIN32__
static char* possibly_quote(char* arg);
static void* erealloc(void *p, size_t size);
@@ -194,6 +223,10 @@ int main(int argc, char** argv)
argv[argc] = NULL;
#endif
+ get_env_compile_server();
+
+ ei_init();
+
env = get_env("ERLC_EMULATOR");
emulator = env ? env : get_default_emulator(argv[0]);
@@ -203,9 +236,29 @@ int main(int argc, char** argv)
/*
* Add scriptname to env
*/
+
set_env("ESCRIPT_NAME", argv[0]);
/*
+ * Save a piece of configuration in an environment variable. The
+ * point is that the compile server needs to know that the same
+ * Erlang/OTP system would be started. On Unix, we save the full
+ * path to the Erlang emulator. On Windows, we save the value of
+ * the environment variable PATH. If the compile server finds that
+ * another Erlang/OTP system would be started, it will terminate
+ * itself.
+ */
+
+#ifdef __WIN32__
+ set_env("ERLC_CONFIGURATION", get_env("PATH"));
+#else
+ {
+ char* full_path_emulator = find_executable(emulator);
+ set_env("ERLC_CONFIGURATION", full_path_emulator);
+ }
+#endif
+
+ /*
* Allocate the argv vector to be used for arguments to Erlang.
* Arrange for starting to pushing information in the middle of
* the array, to allow easy adding of emulator options (like -pa)
@@ -252,6 +305,7 @@ int main(int argc, char** argv)
* Options starting with '+' are passed on to Erlang.
*/
+ source_file = "<no source>";
switch (argv[1][0]) {
case '+':
PUSH(argv[1]);
@@ -260,11 +314,18 @@ int main(int argc, char** argv)
switch (argv[1][1]) {
case 'd':
if (argv[1][2] == '\0') {
- debug = 1;
+ debug++;
} else {
PUSH(argv[1]);
}
break;
+ case 'n':
+ if (strcmp(argv[1], "-no-server") == 0) {
+ use_server = 0;
+ } else {
+ PUSH(argv[1]);
+ }
+ break;
case 'p':
{
int c = argv[1][2];
@@ -290,6 +351,8 @@ int main(int argc, char** argv)
case 's':
if (strcmp(argv[1], "-smp") == 0) {
UNSHIFT(argv[1]);
+ } else if (strcmp(argv[1], "-server") == 0) {
+ use_server = 1;
} else {
PUSH(argv[1]);
}
@@ -300,6 +363,7 @@ int main(int argc, char** argv)
}
break;
default:
+ source_file = argv[1];
PUSH(argv[1]);
break;
}
@@ -320,6 +384,9 @@ int main(int argc, char** argv)
*/
PUSH(NULL);
+ if (use_server) {
+ call_compile_server(eargv);
+ }
return run_erlang(eargv[0], eargv);
}
@@ -350,6 +417,45 @@ process_opt(int* pArgc, char*** pArgv, int offset)
}
static void
+get_env_compile_server(void)
+{
+ char* us = get_env("ERLC_USE_SERVER");
+
+ if (us == NULL) {
+ return; /* Keep default */
+ }
+
+ switch (us[0]) {
+ case 'f':
+ if (strcmp(us+1, "alse") == 0) {
+ use_server = 0;
+ return;
+ }
+ break;
+ case 'n':
+ if (strcmp(us+1, "o") == 0) {
+ use_server = 0;
+ return;
+ }
+ break;
+ case 't':
+ if (strcmp(us+1, "rue") == 0) {
+ use_server = 1;
+ return;
+ }
+ break;
+ case 'y':
+ if (strcmp(us+1, "es") == 0) {
+ use_server = 1;
+ return;
+ }
+ break;
+ }
+ fprintf(stderr, "erlc: Warning: Ignoring unrecognized value '%s' "
+ "for environment value ERLC_USE_SERVER\n", us);
+}
+
+static void
push_words(char* src)
{
char sbuf[MAXPATHLEN];
@@ -369,11 +475,12 @@ push_words(char* src)
if (sbuf[0])
PUSH(strsave(sbuf));
}
+
#ifdef __WIN32__
wchar_t *make_commandline(char **argv)
{
- static wchar_t *buff = NULL;
- static int siz = 0;
+ wchar_t *buff = NULL;
+ int siz = 0;
int num = 0, len;
char **arg;
wchar_t *p;
@@ -400,17 +507,17 @@ wchar_t *make_commandline(char **argv)
}
*(--p) = L'\0';
- if (debug) {
- printf("Processed command line:%S\n",buff);
+ if (debug > 1) {
+ fprintf(stderr, "Processed command line: %S\n", buff);
}
return buff;
}
-int my_spawnvp(char **argv)
+int my_spawnvp(int wait, char **argv)
{
STARTUPINFOW siStartInfo;
PROCESS_INFORMATION piProcInfo;
- DWORD ec;
+ DWORD ec = 0;
memset(&siStartInfo,0,sizeof(STARTUPINFOW));
siStartInfo.cb = sizeof(STARTUPINFOW);
@@ -436,12 +543,14 @@ int my_spawnvp(char **argv)
}
CloseHandle(piProcInfo.hThread);
- WaitForSingleObject(piProcInfo.hProcess,INFINITE);
- if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
- return 0;
+ if (wait) {
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
}
return (int) ec;
-}
+}
#endif /* __WIN32__ */
@@ -452,11 +561,18 @@ run_erlang(char* progname, char** argv)
int status;
#endif
- if (debug) {
+ if (debug > 0) {
+ fprintf(stderr, "spawning erl for %s", source_file);
+ }
+ if (debug > 1) {
int i = 0;
- while (argv[i] != NULL)
- printf(" %s", argv[i++]);
- printf("\n");
+ fprintf(stderr, ":\n ");
+ while (argv[i] != NULL) {
+ fprintf(stderr, "%s ", argv[i++]);
+ }
+ }
+ if (debug) {
+ putc('\n', stderr);
}
#ifdef __WIN32__
@@ -466,7 +582,7 @@ run_erlang(char* progname, char** argv)
* we are finished and print a prompt and read keyboard input.
*/
- status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ status = my_spawnvp(1, argv);
if (status == -1) {
fprintf(stderr, "erlc: Error executing '%s': %d", progname,
GetLastError());
@@ -485,6 +601,360 @@ run_erlang(char* progname, char** argv)
}
static void
+call_compile_server(char** argv)
+{
+ ei_cnode ec;
+ char* user;
+ char node_name[MAXNODELEN+1];
+ char remote[MAXNODELEN+1];
+ short creation = 1;
+ int fd;
+ char cwd[MAXPATHLEN+1];
+ ei_x_buff args;
+ ei_x_buff reply;
+ int reply_size;
+ int dec_size, dec_index;
+ char atom[MAXATOMLEN];
+ int argc;
+
+#ifdef __WIN32__
+ if (_getcwd(cwd, sizeof(cwd)) == 0) {
+ fprintf(stderr, "erlc: failed to get current working directory\n");
+ exit(2);
+ }
+#else
+ if (getcwd(cwd, sizeof(cwd)) == 0) {
+ fprintf(stderr, "erlc: failed to get current working directory\n");
+ exit(2);
+ }
+#endif
+
+#ifndef __WIN32__
+ {
+ struct sigaction act;
+
+ /*
+ * If the node is terminating when ei_rpc() is executed, the process
+ * may receive a SIGPIPE signal. Make sure it does not kill this process.
+ */
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGPIPE, &act, NULL);
+ }
+#endif
+
+ /* Get user name */
+ user = get_env("USERNAME"); /* Windows */
+ if (!user) {
+ user = get_env("LOGNAME"); /* Unix */
+ }
+ if (!user) {
+ user = get_env("USER"); /* Unix */
+ }
+ if (!user) {
+ user = "nouser";
+ }
+
+ /* Create my own node name. */
+#ifdef __WIN32__
+ sprintf(node_name, "erlc_client_%s_%lu", user, (unsigned long) GetCurrentProcessId());
+#else
+ sprintf(node_name, "erlc_client_%s_%d", user, getpid());
+#endif
+
+ if (ei_connect_init(&ec, node_name, NULL, creation) < 0) {
+ /*
+ * There is probably no .erlang.cookie file.
+ */
+ if (debug > 1) {
+ fprintf(stderr, "\ncan't create C node %s: %s\n",
+ node_name, strerror(erl_errno));
+ }
+ sprintf(remote, "erl_compile_server_%s@host", user);
+ goto start_compile_server;
+ }
+
+ /* Create node name for compile server. */
+
+ sprintf(remote, "erl_compile_server_%s@%s", user, ei_thishostname(&ec));
+
+ if ((fd = ei_connect(&ec, remote)) < 0) {
+ if (debug > 1) {
+ fprintf(stderr, "failed to connect to compile server %s: %s\n",
+ remote, strerror(erl_errno));
+ }
+ goto start_compile_server;
+ }
+
+ /*
+ * Encode the request to the compile server.
+ */
+
+ ei_x_new_with_version(&args);
+ ei_x_encode_list_header(&args, 1);
+ ei_x_encode_map_header(&args, 4);
+ ei_x_encode_atom(&args, "encoding");
+ ei_x_encode_atom(&args, get_encoding());
+ ei_x_encode_atom(&args, "cwd");
+ ei_x_encode_string(&args, cwd);
+ ei_x_encode_atom(&args, "env");
+ encode_env(&args);
+ ei_x_encode_atom(&args, "command_line");
+ argc = 0;
+ while (argv[argc]) {
+ ei_x_encode_list_header(&args, 1);
+ ei_x_encode_string(&args, possibly_unquote(argv[argc]));
+ argc++;
+ }
+ ei_x_encode_empty_list(&args); /* End of command_line */
+ ei_x_encode_empty_list(&args); /* End of argument list for apply */
+
+ /*
+ * Do a RPC to the compile server.
+ */
+
+ ei_x_new_with_version(&reply);
+ reply_size = ei_rpc(&ec, fd, "erl_compile_server", "compile",
+ args.buff+1, args.index-1, &reply);
+ if (reply_size < 0) {
+ if (debug > 1) {
+ fprintf(stderr, "failed to rpc to node %s: %s\n",
+ remote, strerror(erl_errno));
+ }
+ goto start_compile_server;
+ }
+
+ /*
+ * Decode the answer.
+ */
+
+ dec_index = 0;
+ if (ei_decode_atom(reply.buff, &dec_index, atom) == 0 &&
+ strcmp(atom, "wrong_config") == 0) {
+ if (debug > 1) {
+ fprintf(stderr, "wrong configuration\n");
+ }
+ goto start_compile_server;
+ } else if (ei_decode_tuple_header(reply.buff, &dec_index, &dec_size) == 0) {
+ atom[0] = '\0';
+ if (dec_size >= 2) {
+ ei_decode_atom(reply.buff, &dec_index, atom);
+ }
+ if (dec_size == 2) {
+ if (strcmp(atom, "ok") == 0) {
+ char* output = decode_binary(reply.buff, &dec_index, &dec_size);
+ if (debug) {
+ fprintf(stderr, "called server for %s => ok\n", source_file);
+ }
+ if (output) {
+ fwrite(output, dec_size, 1, stdout);
+ exit(0);
+ }
+ }
+ } else if (dec_size == 3 && strcmp(atom, "error") == 0) {
+ int std_size, err_size;
+ char* std;
+ char* err;
+
+ if (debug) {
+ fprintf(stderr, "called server for %s => error\n", source_file);
+ }
+ std = decode_binary(reply.buff, &dec_index, &std_size);
+ err = decode_binary(reply.buff, &dec_index, &err_size);
+ if (std && err) {
+ fwrite(err, err_size, 1, stderr);
+ fwrite(std, std_size, 1, stdout);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Unrecognized term, probably because the node was shutting down.
+ */
+
+ if (debug > 1) {
+ fprintf(stderr, "unrecognized term returned by compilation server:\n");
+ dec_index = 0;
+ ei_print_term(stderr, reply.buff, &dec_index);
+ putc('\n', stderr);
+ }
+
+ start_compile_server:
+ *strchr(remote, '@') = '\0';
+ start_compile_server(remote, argv);
+}
+
+static void
+encode_env(ei_x_buff* buf)
+{
+ char* env_names[] = {"ERL_AFLAGS",
+ "ERL_FLAGS",
+ "ERL_ZFLAGS",
+ "ERL_COMPILER_OPTIONS",
+ "ERL_LIBS",
+ "ERLC_CONFIGURATION",
+ 0};
+ char** p = env_names;
+ while (p[0]) {
+ char* val;
+
+ if ((val = get_env(p[0])) != 0) {
+ ei_x_encode_list_header(buf, 1);
+ ei_x_encode_tuple_header(buf, 2);
+ ei_x_encode_string(buf, p[0]);
+ ei_x_encode_string(buf, val);
+ }
+ p++;
+ }
+ ei_x_encode_empty_list(buf);
+}
+
+#ifndef __WIN32__
+static char*
+find_executable(char* progname)
+{
+ char* path;
+ char* start_path;
+ char* real_name;
+ char buf[PATH_MAX];
+ size_t len_component;
+ size_t len_prog;
+
+ if (strchr(progname, '/')) {
+ return progname;
+ }
+
+ len_prog = strlen(progname);
+
+ if (!(path = getenv("PATH"))) {
+ path = "/bin:/usr/bin";
+ }
+
+ do {
+ for (start_path = path; *path != '\0' && *path != ':'; path++) {
+ ;
+ }
+ if (start_path == path) {
+ start_path = ".";
+ len_component = 1;
+ } else {
+ len_component = path - start_path;
+ }
+ memcpy(buf, start_path, len_component);
+ buf[len_component] = '/';
+ memcpy(buf + len_component + 1, progname, len_prog);
+ buf[len_component + len_prog + 1] = '\0';
+ if ((real_name = safe_realpath(buf)) != 0) {
+ struct stat s;
+ if (stat(real_name, &s) == 0 && s.st_mode & S_IFREG) {
+ return real_name;
+ }
+ }
+ } while (*path++ == ':');
+ return progname;
+}
+
+static char*
+safe_realpath(char* file)
+{
+ /*
+ * Always allocate a buffer for the result of realpath().
+ * realpath() on old versions of MacOS X will crash if the buffer
+ * argument is NULL, and realpath() will fail on old versions of
+ * Solaris.
+ */
+ char* real_name = emalloc(PATH_MAX + 1);
+ return realpath(file, real_name);
+}
+#endif
+
+static char*
+get_encoding(void)
+{
+#ifdef __WIN32__
+ return "latin1";
+#else
+ char* p;
+ p = get_env("LC_ALL");
+ if (!p) {
+ p = get_env("LC_CTYPE");
+ }
+ if (!p) {
+ p = get_env("LANG");
+ }
+ if (!p) {
+ return "latin1";
+ } else {
+ return strstr(p, "UTF-8") ? "utf8" : "latin1";
+ }
+#endif
+}
+
+static char*
+decode_binary(const char* buf, int* dec_index, int* dec_size)
+{
+ int dec_type;
+ char* bin;
+
+ ei_get_type(buf, dec_index, &dec_type, dec_size);
+ bin = emalloc(*dec_size);
+ if (ei_decode_binary(buf, dec_index, bin, NULL) < 0) {
+ return NULL;
+ }
+ return bin;
+}
+
+static void
+start_compile_server(char* node_name, char** argv)
+{
+ char* eargv[100];
+ int eargc = 0;
+ char* progname = argv[0];
+
+ while (strcmp(argv[0], "-mode") != 0) {
+ eargv[eargc++] = *argv++;
+ }
+ PUSH2("-boot", "no_dot_erlang");
+ PUSH2("-sname", node_name);
+ PUSH("-hidden");
+ PUSH("-detached");
+ PUSH3("-kernel", "start_compile_server", "true");
+
+ /*
+ * If this is an older Erlang system (before 22.1) that does not
+ * support the compile server, terminate immediately.
+ */
+ PUSH2("-eval", "is_pid(whereis(erl_compile_server)) orelse halt(1)");
+
+ PUSH(NULL);
+
+ if (debug == 1) {
+ fprintf(stderr, "starting compile server %s\n", node_name);
+ } else if (debug > 1) {
+ int i = 0;
+ fprintf(stderr, "starting compile server %s:\n", node_name);
+ while (eargv[i] != NULL) {
+ fprintf(stderr, "%s ", eargv[i++]);
+ }
+ putc('\n', stderr);
+ }
+
+#ifdef __WIN32__
+ if (my_spawnvp(0, eargv) == -1) {
+ fprintf(stderr, "erlc: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+#else
+ if (fork() == 0) {
+ execvp(eargv[0], eargv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ }
+#endif
+}
+
+static void
error(char* format, ...)
{
char sbuf[1024];
@@ -565,6 +1035,42 @@ get_default_emulator(char* progname)
return ERL_NAME;
}
+
+static char*
+possibly_unquote(char* arg)
+{
+#ifndef __WIN32__
+ /* Nothing to do if not Windows. */
+ return arg;
+#else
+ char* unquoted;
+ char* dstp;
+
+ if (arg[0] != '"') {
+ /* Not quoted. Nothing to do. */
+ return arg;
+ }
+
+ /*
+ * Remove the quotes and remove backslashes before quotes.
+ */
+
+ unquoted = emalloc(strlen(arg) + 1);
+ arg++;
+ dstp = unquoted;
+ while (*arg) {
+ if (arg[0] == '\\' && arg[1] == '"') {
+ *dstp++ = '"';
+ arg += 2;
+ } else {
+ *dstp++ = *arg++;
+ }
+ }
+ *--dstp = 0;
+ return unquoted;
+#endif
+}
+
#ifdef __WIN32__
static char*
possibly_quote(char* arg)
@@ -623,4 +1129,5 @@ possibly_quote(char* arg)
*s = '\0';
return narg;
}
+
#endif /* __WIN32__ */
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index dd08111aad..d5f6c16f09 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 305b524438..33363a3c82 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -99,6 +99,9 @@
-export([spawn_system_process/3]).
+-export([ets_lookup_binary_info/2, ets_super_user/1, ets_info_binary/1,
+ ets_raw_first/1, ets_raw_next/2]).
+
%%
%% Await result of send to port
%%
@@ -735,3 +738,71 @@ counters_info(_Ref) ->
Args :: list().
spawn_system_process(_Mod, _Func, _Args) ->
erlang:nif_error(undefined).
+
+
+%%
+%% ETS info internals...
+%%
+
+-spec ets_lookup_binary_info(Tab, Key) -> BinInfo when
+ Tab :: ets:tab(),
+ Key :: term(),
+ BinInfo :: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}].
+
+ets_lookup_binary_info(_Tab, _Key) ->
+ erlang:nif_error(undef).
+
+-spec ets_super_user(Bool) -> 'ok' when
+ Bool :: boolean().
+
+ets_super_user(_Bool) ->
+ erlang:nif_error(undef).
+
+-spec ets_raw_first(Tab) -> term() when
+ Tab :: ets:tab().
+
+ets_raw_first(_Tab) ->
+ erlang:nif_error(undef).
+
+-spec ets_raw_next(Tab, Key) -> term() when
+ Tab :: ets:tab(),
+ Key :: term().
+
+ets_raw_next(_Tab, _Key) ->
+ erlang:nif_error(undef).
+
+-spec ets_info_binary(Tab) -> BinInfo when
+ Tab :: ets:tab(),
+ BinInfo :: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}].
+
+ets_info_binary(Tab) ->
+ try
+ erts_internal:ets_super_user(true),
+ ets:safe_fixtable(Tab, true),
+ ets_info_binary_iter(Tab, erts_internal:ets_raw_first(Tab), [])
+ catch
+ C:R:S ->
+ ets_info_binary_error(Tab, C, R, S)
+ after
+ ets:safe_fixtable(Tab, false),
+ erts_internal:ets_super_user(false)
+ end.
+
+ets_info_binary_error(Tab, C, R, []) ->
+ erlang:raise(C, R, [{ets, info, [Tab, binary], []}]);
+ets_info_binary_error(Tab, C, R, [SF|SFs]) when
+ element(1, SF) == erts_internal,
+ element(2, SF) == ets_info_binary ->
+ erlang:raise(C, R, [{ets, info, [Tab, binary], []}|SFs]);
+ets_info_binary_error(Tab, C, R, [_SF|SFs]) ->
+ ets_info_binary_error(Tab, C, R, SFs).
+
+ets_info_binary_iter(_Tab, '$end_of_table', Acc) ->
+ Acc;
+ets_info_binary_iter(Tab, Key, Acc) ->
+ NewAcc = case erts_internal:ets_lookup_binary_info(Tab, Key) of
+ [] -> Acc;
+ [BI] -> [BI|Acc];
+ [_|_] = BIL -> BIL ++ Acc
+ end,
+ ets_info_binary_iter(Tab, erts_internal:ets_raw_next(Tab, Key), NewAcc).
diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl
index 0c5b9f8358..c01506b1cd 100644
--- a/erts/test/erlc_SUITE.erl
+++ b/erts/test/erlc_SUITE.erl
@@ -32,22 +32,34 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ [{group,with_server},{group,without_server}].
+
+groups() ->
+ Tests = tests(),
+ [{with_server,[],Tests},
+ {without_server,[],Tests}].
+
+tests() ->
[compile_erl, compile_yecc, compile_script, compile_mib,
good_citizen, deep_cwd, arg_overflow, make_dep_options].
-groups() ->
- [].
-
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
ok.
-init_per_group(_GroupName, Config) ->
+init_per_group(with_server, Config) ->
+ os:putenv("ERLC_USE_SERVER", "yes"),
+ Config;
+init_per_group(without_server, Config) ->
+ os:putenv("ERLC_USE_SERVER", "no"),
+ Config;
+init_per_group(_, Config) ->
Config.
end_per_group(_GroupName, Config) ->
+ os:unsetenv("ERLC_USE_SERVER"),
Config.
%% Copy from erlc_SUITE_data/include/erl_test.hrl.
@@ -199,8 +211,7 @@ deep_cwd(Config) when is_list(Config) ->
deep_cwd_1(PrivDir) ->
DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)),
DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)),
- ok = file:make_dir(DeepDir0),
- ok = file:make_dir(DeepDir),
+ ok = filelib:ensure_dir(filename:join(DeepDir,"any_file")),
ok = file:set_cwd(DeepDir),
ok = file:write_file("test.erl", "-module(test).\n\n"),
io:format("~s\n", [os:cmd("erlc test.erl")]),
diff --git a/erts/vsn.mk b/erts/vsn.mk
index f06fd08540..d1db2ceceb 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.4.4
+VSN = 10.5
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/common_test/doc/src/ct_netconfc.xml b/lib/common_test/doc/src/ct_netconfc.xml
index 8fbe5f3df6..f4d98b611b 100644
--- a/lib/common_test/doc/src/ct_netconfc.xml
+++ b/lib/common_test/doc/src/ct_netconfc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2017</year>
+ <year>2010</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,51 +37,64 @@
<description>
- <p>NETCONF client module.</p>
-
- <p>The NETCONF client is compliant with RFC 4741 NETCONF Configuration
- Protocol and RFC 4742 Using the NETCONF Configuration Protocol over
- Secure SHell (SSH).</p>
+ <p>NETCONF client module compliant with RFC 6241, NETCONF Configuration
+ Protocol, and RFC 6242, Using the NETCONF Configuration Protocol over
+ Secure SHell (SSH), and with support for RFC 5277, NETCONF Event
+ Notifications.</p>
<marker id="Connecting"/>
<p><em>Connecting to a NETCONF server</em></p>
- <p>NETCONF sessions can either be opened by a single call
- to <seealso marker="#open-1"><c>open/1,2</c></seealso> or by a call
- to <seealso marker="#connect-1"><c>connect/1,2</c></seealso> followed
- by one or more calls to
- <seealso marker="#session-1"><c>session/1,2,3</c></seealso>.</p>
-
- <p>The properties of the sessions will be exactly the same, except
- that when
- using <seealso marker="#connect-1"><c>connect/1,2</c></seealso>, you
- may start multiple sessions over the same SSH connection. Each
- session is implemented as an SSH channel.</p>
-
- <p><seealso marker="#open-1"><c>open/1,2</c></seealso> will establish one
- SSH connection with one SSH channel implementing one NETCONF
- session. You may start mutiple sessions by
- calling <seealso marker="#open-1"><c>open/1,2</c></seealso> multiple
- times, but then a new SSH connection will be established for each
- session.</p>
-
- <p>For each server to test against, the following entry can be added to a
- configuration file:</p>
+ <p>Call <seealso marker="#connect-1"><c>connect/1,2</c></seealso>
+ to establish a connection to a server, then pass the returned
+ handle to <seealso marker="#session-1"><c>session/1-3</c></seealso> to
+ establish a NETCONF session on a new SSH channel.
+ Each call to
+ <seealso marker="#session-1"><c>session/1-3</c></seealso> establishes a
+ new session on the same connection, and results in a hello message
+ to the server.</p>
+
+ <p>Alternately,
+ <seealso marker="#open-1"><c>open/1,2</c></seealso> can be used to
+ establish a single session on a dedicated connection.
+ (Or, equivalently,
+ <seealso marker="#only_open-1"><c>only_open/1,2</c></seealso>
+ followed by <seealso marker="#hello-1"><c>hello/1-3</c></seealso>.)</p>
+
+ <p>Connect/session options can be specified in a configuration
+ file with entries like the following.</p>
<pre>
- {server_id(),options()}.</pre>
+ {server_id(), [option()]}.</pre>
<p>The <seealso marker="#type-server_id"><c>server_id()</c></seealso>
- or an associated
- <seealso marker="ct#type-target_name"><c>ct:target_name()</c></seealso>
- must then be used in calls to
- <seealso marker="#connect-2"><c>connect/2</c></seealso>
- or <seealso marker="#open-2"><c>open/2</c></seealso>.</p>
-
- <p>If no configuration exists for a server,
- use <seealso marker="#connect-1"><c>connect/1</c></seealso>
- or <seealso marker="#open-1"><c>open/1</c></seealso> instead,
- and specify all necessary options in the <c>Options</c> parameter.</p>
+ or an associated
+ <seealso marker="ct#type-target_name"><c>ct:target_name()</c></seealso>
+ can then be passed to the aforementioned functions to use the
+ referenced configuration.</p>
+
+ <marker id="Signaling"/>
+ <p><em>Signaling</em></p>
+
+ <p>Protocol operations in the NETCONF protocol are realized as remote
+ procedure calls (RPCs) from client to server and a corresponding
+ reply from server to client.
+ RPCs are sent using like-named functions (eg.
+ <seealso marker="#edit_config-3"><c>edit_config/3-5</c></seealso>
+ to send an edit-config RPC), with the server reply
+ as return value.
+ There are functions for each RPC defined in RFC 6241 and
+ the create-subscription RPC from RFC 5277, all of which are
+ wrappers on <seealso marker="#send_rpc-2"><c>send_rpc/2,3</c></seealso>,
+ that can be used to send an arbitrary RPC
+ not defined in RFC 6241 or RFC 5277.</p>
+
+ <p>All of the signaling functions have one variant with a
+ <c>Timeout</c> argument and one without, corresponding to an
+ infinite timeout.
+ The latter is inappropriate in most cases since a non-response by
+ the server or a missing message-id causes the call to hang
+ indefinitely.</p>
<marker id="Logging"/>
<p><em>Logging</em></p>
@@ -93,7 +106,7 @@
<pre>
suite() -&gt;
- [{ct_hooks, [{cth_conn_log, [{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>,<seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}]}].</pre>
+ [{ct_hooks, [{cth_conn_log, [{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>, <seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}]}].</pre>
<p><c>conn_log_mod()</c> is the name of the <c>Common Test</c> module
implementing the connection protocol, for example, <c>ct_netconfc</c>.</p>
@@ -133,7 +146,7 @@
configuration variable <c>ct_conn_log</c>:</p>
<pre>
- {ct_conn_log,[{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>,<seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}.</pre>
+ {ct_conn_log,[{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>, <seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}.</pre>
<p>For example:</p>
@@ -185,100 +198,111 @@
would cause HTML logging of all NETCONF connections in to the test
case HTML log.</p>
- <marker id="Notifications"/>
- <p><em>Notifications</em></p>
-
- <p>The NETCONF client is also compliant with RFC 5277 NETCONF Event
- Notifications, which defines a mechanism for an asynchronous message
- notification delivery service for the NETCONF protocol.</p>
-
- <p>Specific functions to support this are
- <seealso marker="#create_subscription-1"><c>create_subscription/1-6</c></seealso>
- and
- <seealso marker="#get_event_streams-1"><c>get_event_streams/1-3</c></seealso>.</p>
-
- <marker id="Default_timeout"/>
- <p><em>Default Timeout</em></p>
-
- <p>Most of the functions in this module have one variant with
- a <c>Timeout</c> parameter, and one without. If nothing else is
- specified, the default value <c>infinity</c> is used when
- the <c>Timeout</c> parameter is not given.</p>
-
</description>
+ <!-- ====================================================================== -->
+
<datatypes>
<datatype>
<name name="client"/>
- </datatype>
- <datatype>
- <name name="error_reason"/>
- </datatype>
- <datatype>
- <name name="event_time"/>
+ <desc>
+ <p>Handle to a NETCONF session, as required by signaling
+ functions.</p>
+ </desc>
</datatype>
<datatype>
<name name="handle"/>
<desc>
- <p>Opaque reference for a connection to a NETCONF server or a
- NETCONF session.</p>
+ <p>Handle to a connection to a NETCONF server as
+ returned by
+ <seealso marker="#connect-1"><c>connect/1,2</c></seealso>,
+ or to a session as returned by
+ <seealso marker="#session-1"><c>session/1-3</c></seealso>,
+ <seealso marker="#open-1"><c>open/1,2</c></seealso>,
+ or <seealso marker="#only_open-1"><c>only_open/1,2</c></seealso>.</p>
</desc>
</datatype>
<datatype>
- <name name="host"/>
- </datatype>
- <datatype>
- <name name="netconf_db"/>
+ <name name="xs_datetime"/>
+ <desc>
+ <p>Date and time of a startTime/stopTime element in an RFC
+ 5277 create-subscription request. Of XML primitive type
+ <c>dateTime</c>, which has the (informal) form</p>
+ <pre>
+ [-]YYYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]</pre>
+ <p>where <c>T</c> and <c>Z</c> are literal and <c>.s</c> is
+ one or more fractional seconds.</p>
+ </desc>
</datatype>
<datatype>
- <name name="notification"/>
+ <name name="event_time"/>
</datatype>
<datatype>
<name name="notification_content"/>
</datatype>
<datatype>
- <name name="option"/>
+ <name name="notification"/>
<desc>
- <p><c>SshConnectOption</c> is any valid option to
- <seealso marker="ssh:ssh#connect-3"><c>ssh:connect/3,4</c></seealso>.
- Common options used are <c>user</c>, <c>password</c>
- and <c>user_dir</c>. The <c>SshConnectOptions</c> are
- verfied by the SSH application.</p>
+ <p>Event notification messages sent as a result of calls to
+ <seealso marker="#create_subscription-2"><c>create_subscription/2,3</c></seealso>.</p>
</desc>
</datatype>
<datatype>
- <name name="options"/>
+ <name name="option"/>
<desc>
- <p>Options used for setting up an SSH connection to a NETCONF
- server.</p>
+ <p>Options <c>host</c> and <c>port</c> specify the
+ server endpoint to which to connect, and are passed directly
+ to <seealso
+ marker="ssh:ssh#connect-3"><c>ssh:connect/4</c></seealso>,
+ as are arbitrary ssh options. Common options are <c>user</c>,
+ <c>password</c> and <c>user_dir</c>.</p>
+
+ <p>Option <c>timeout</c> specifies the number of
+ milliseconds to allow for connection establishment and, if the
+ function in question results in an outgoing hello message,
+ reception of the server hello. The timeout applies to
+ connection and hello independently;
+ one timeout for connection establishment, another for hello
+ reception.</p>
+
+ <p>Option <c>capability</c> specifies the content of a
+ corresponding element in an outgoing hello message, each
+ option specifying the content of a single element.
+ If no base NETCONF capability is configured then the RFC 4741
+ 1.0 capability, "urn:ietf:params:netconf:base:1.0", is added,
+ otherwise not.
+ In particular, the RFC 6241 1.1 capability must be explicitly
+ configured.
+ NETCONF capabilities can be specified using the shorthand notation
+ defined in RFC 6241, any capability string starting with a
+ colon being prefixed by either "urn:ietf:params:netconf" or
+ "urn:ietf:params:netconf:capability", as appropriate.</p>
+
+ <p>Capability options are ignored by connect/1-3 and only_open/1-2,
+ which don't result in an outgoing hello message.</p>
</desc>
</datatype>
<datatype>
<name name="server_id"/>
<desc>
- <p>The identity of a server, specified in a configuration
- file.</p>
- </desc>
- </datatype>
- <datatype>
- <name name="simple_xml"/>
- <desc>
- <p>This type is further described in application
- <seealso marker="xmerl:index"><c>xmerl</c></seealso>.</p>
+ <p>Identity of connection or session configuration in a
+ configuration file.</p>
</desc>
</datatype>
<datatype>
<name name="stream_data"/>
- <desc>
- <p>For details about the data format for the string values, see
- "XML Schema for Event Notifications" in RFC 5277.</p>
- </desc>
</datatype>
<datatype>
<name name="stream_name"/>
</datatype>
<datatype>
<name name="streams"/>
+ <desc>
+ <p>Stream information as returned by
+ <seealso marker="#get_event_streams-1"><c>get_event_streams/1-3</c></seealso>.
+ See RFC 5277, "XML Schema for Event Notifications", for detail
+ on the format of the string values.</p>
+ </desc>
</datatype>
<datatype>
<name name="xml_attribute_tag"/>
@@ -296,20 +320,28 @@
<name name="xml_tag"/>
</datatype>
<datatype>
+ <name name="simple_xml"/>
+ <desc>
+ <p>Representation of XML, as described in application
+ <seealso marker="xmerl:index"><c>xmerl</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="xpath"/>
</datatype>
<datatype>
- <name name="xs_datetime"/>
- <desc>
- <p>This date and time identifier has the same format as the XML type
- <c>dateTime</c> and is compliant with RFC 3339 Date and Time on
- the Internet Timestamps. The format is as follows:</p>
- <pre>
- [-]CCYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]</pre>
- </desc>
+ <name name="error_reason"/>
+ </datatype>
+ <datatype>
+ <name name="host"/>
+ </datatype>
+ <datatype>
+ <name name="netconf_db"/>
</datatype>
</datatypes>
+ <!-- ====================================================================== -->
+
<funcs>
<func>
<name name="action" arity="2" since="OTP R15B02"/>
@@ -352,11 +384,7 @@
reference returned from this
function is required as connection identifier when opening
sessions over this connection, see
- <seealso marker="#session-1"><c>session/1,2,3</c></seealso>.</p>
-
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection. It is not used for any other purposes during the
- lifetime of the connection.</p>
+ <seealso marker="#session-1"><c>session/1-3</c></seealso>.</p>
</desc>
</func>
@@ -371,10 +399,9 @@
<c>target_name()</c> associated with such an Id, then the options
for this server are fetched from the configuration file.</p>
- <p>Argument <c><anno>ExtraOptions</anno></c> is added to the
- options found in the configuration file. If the same options
- are specified, the values from the configuration file
- overwrite <c><anno>ExtraOptions</anno></c>.</p>
+ <p>The options list is added to those of the
+ configuration file. If an option is specified in both lists,
+ the configuration file takes precedence.</p>
<p>If the server is not specified in a configuration file, use
<seealso marker="#connect-1"><c>connect/1</c></seealso>
@@ -384,17 +411,13 @@
reference returned from this
function can be used as connection identifier when opening
sessions over this connection, see
- <seealso marker="#session-1"><c>session/1,2,3</c></seealso>.
+ <seealso marker="#session-1"><c>session/1-3</c></seealso>.
However, if <c><anno>KeyOrName</anno></c> is a
<c>target_name()</c>, that is, if the server is named through a
call to <seealso marker="ct#require-2"><c>ct:require/2</c></seealso>
or a <c>require</c> statement in the test suite, then this name can
be used instead of
<seealso marker="#type-handle"><c>handle()</c></seealso>.</p>
-
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection. It is not used for any other purposes during the
- lifetime of the connection.</p>
</desc>
</func>
@@ -412,80 +435,61 @@
</func>
<func>
- <name since="OTP R15B02">create_subscription(Client) -> Result</name>
- <name since="OTP R15B02">create_subscription(Client, Stream) -> Result</name>
- <name since="OTP R15B02">create_subscription(Client, Stream, Filter) -> Result</name>
- <name since="OTP R15B02">create_subscription(Client, Stream, Filter, Timeout) -> Result</name>
- <name name="create_subscription" arity="5" clause_i="2" since="OTP R15B02"/>
- <name name="create_subscription" arity="6" since="OTP R15B02"/>
+ <name name="create_subscription" arity="2" clause_i="1" since="OTP 22.1"/>
+ <name name="create_subscription" arity="3" clause_i="1" since="OTP 22.1"/>
<fsummary>Creates a subscription for event notifications.</fsummary>
<desc>
- <p>Creates a subscription for event notifications.</p>
-
- <p>This function sets up a subscription for NETCONF event
- notifications of the specified stream type, matching the specified
- filter. The calling process receives notifications as messages of
- type <seealso marker="#type-notification"><c>notification()</c></seealso>.</p>
-
- <p>Only a subset of the function clauses are show above. The
- full set of valid combinations of input parameters is as
- follows:</p>
-
-<pre>create_subscription(Client)
+ <p>Creates a subscription for event notifications by sending
+ an RFC 5277 create-subscription RPC to the server.
+ The calling process receives events as messages of
+ type <seealso marker="#type-notification"><c>notification()</c></seealso>.</p>
-create_subscription(Client, Timeout)
-create_subscription(Client, Stream)
-create_subscription(Client, Filter)
-
-create_subscription(Client, Stream, Timeout)
-create_subscription(Client, Filter, Timeout)
-create_subscription(Client, Stream, Filter)
-create_subscription(Client, StartTime, StopTime)
-
-create_subscription(Client, Stream, Filter, Timeout)
-create_subscription(Client, StartTime, StopTime, Timeout)
-create_subscription(Client, Stream, StartTime, StopTime)
-create_subscription(Client, Filter, StartTime, StopTime)
-
-create_subscription(Client, Stream, StartTime, StopTime, Timeout)
-create_subscription(Client, Stream, Filter, StartTime, StopTime)
-create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
+ <p>From RFC 5722, 2.1 Subscribing to Receive Event Notifications:</p>
<taglist>
<tag><c><anno>Stream</anno></c></tag>
- <item><p>Optional parameter that indicates which stream of event
+ <item><p>Indicates which stream of event
is of interest. If not present, events in the default NETCONF
stream are sent.</p></item>
<tag><c><anno>Filter</anno></c></tag>
- <item><p>Optional parameter that indicates which subset of all
+ <item><p>Indicates which subset of all
possible events is of interest. The parameter format is the
same as that of the filter parameter in the NETCONF protocol
operations. If not present, all events not precluded by other
parameters are sent.</p></item>
<tag><c><anno>StartTime</anno></c></tag>
- <item><p>Optional parameter used to trigger the replay feature and
+ <item><p>Used to trigger the replay feature and
indicate that the replay is to start at the time specified.
If <c><anno>StartTime</anno></c> is not present, this is not a
- replay subscription.</p>
- <p>It is not valid to specify start times that are later than
+ replay subscription.
+ It is not valid to specify start times that are later than
the current time. If <c><anno>StartTime</anno></c> is specified
earlier than the log can support, the replay begins with the
- earliest available notification.</p>
- <p>This parameter is of type <c>dateTime</c> and compliant to
+ earliest available notification.
+ This parameter is of type <c>dateTime</c> and compliant to
RFC 3339. Implementations must support time zones.</p></item>
<tag><c><anno>StopTime</anno></c></tag>
- <item><p>Optional parameter used with the optional replay feature
+ <item><p>Used with the optional replay feature
to indicate the newest notifications of interest. If
<c><anno>StopTime</anno></c> is not present, the notifications
- continues until the subscription is terminated.</p>
- <p>Must be used with and be later than <c>StartTime</c>. Values
+ continues until the subscription is terminated.
+ Must be used with and be later than <c>StartTime</c>. Values
of <c><anno>StopTime</anno></c> in the future are valid. This
parameter is of type <c>dateTime</c> and compliant to RFC 3339.
Implementations must support time zones.</p></item>
</taglist>
- <p>For more details about the event notification mechanism, see
- RFC 5277.</p>
+ <p>See RFC 5277 for more details. The requirement that
+ <c>StopTime</c> must only be used with <c>StartTime</c> is not
+ enforced, to allow an invalid request to be sent to the
+ server.</p>
+
+ <p>Prior to OTP 22.1, this function was documented as having
+ 15 variants in 6 arities. These are still exported for
+ backwards compatibility, but no longer documented.
+ The map-based variants documented above provide the same
+ functionality with simpler arguments.</p>
+
</desc>
</func>
@@ -561,23 +565,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<name name="get_capabilities" arity="2" since="OTP R15B02"/>
<fsummary>Returns the server side capabilities.</fsummary>
<desc>
- <p>Returns the server side capabilities.</p>
-
- <p>The following capability identifiers, defined in RFC 4741 NETCONF
- Configuration Protocol, can be returned:</p>
-
- <list>
- <item><p><c>"urn:ietf:params:netconf:base:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:writable-running:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:candidate:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:confirmed-commit:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:rollback-on-error:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:startup:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:url:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:xpath:1.0"</c></p></item>
- </list>
-
- <p>More identifiers can exist, for example, server-side namespace.</p>
+ <p>Returns the server capabilities as received in its hello message.</p>
</desc>
</func>
@@ -652,10 +640,12 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<name name="hello" arity="3" since="OTP 17.5.3"/>
<fsummary>Exchanges hello messages with the server.</fsummary>
<desc>
- <p>Exchanges <c>hello</c> messages with the server.</p>
+ <p>Exchanges <c>hello</c> messages with the server. Returns
+ when the server hello has been received or after the
+ specified timeout.</p>
- <p>Adds optional capabilities and sends a <c>hello</c> message to the
- server and waits for the return.</p>
+ <p>Note that capabilities for an outgoing hello can be passed
+ directly to <seealso marker="#open-2"><c>open/2</c></seealso>.</p>
</desc>
</func>
@@ -740,11 +730,6 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
reference returned from this
function is required as client identifier when calling any other
function in this module.</p>
-
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection and when waiting for the <c>hello</c> message from
- the server. It is not used for any other purposes during the
- lifetime of the connection.</p>
</desc>
</func>
@@ -761,10 +746,9 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<c>target_name()</c> associated with such an Id, then the options
for this server are fetched from the configuration file.</p>
- <p>Argument <c><anno>ExtraOptions</anno></c> is added to the
- options found in the configuration file. If the same
- options are specified, the values from the configuration
- file overwrite <c><anno>ExtraOptions</anno></c>.</p>
+ <p>The options list is added to those of the
+ configuration file. If an option is specified in both lists,
+ the configuration file take precedence.</p>
<p>If the server is not specified in a configuration file, use
<seealso marker="#open-1"><c>open/1</c></seealso>
@@ -780,11 +764,6 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
be used instead of
<seealso marker="#type-handle"><c>handle()</c></seealso>.</p>
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection and when waiting for the <c>hello</c> message from
- the server. It is not used for any other purposes during the
- lifetime of the connection.</p>
-
<p>See also
<seealso marker="ct#require-2"><c>ct:require/2</c></seealso>.</p>
</desc>
@@ -827,7 +806,6 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<fsummary>Opens a NETCONF session as a channel on the given SSH
connection, and exchanges hello messages with the
server.</fsummary>
- <type name="session_options"/>
<type name="session_option"/>
<desc>
<p>Opens a NETCONF session as a channel on the given SSH
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index c454608bbe..39388fd5ed 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,64 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.18</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a ct hook is installed in the <c>suite/0</c> function
+ in a test suite, then the hook's <c>terminate/1</c>
+ function would be called several times without it's
+ <c>init/2</c> function being called first. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15863 Aux Id: ERIERL-370 </p>
+ </item>
+ <item>
+ <p>
+ If <c>init_per_testcase</c> fails, the test itself is
+ skipped. According to the documentation, it should be
+ possible to change the result to failed in a hook
+ function. The only available hook function in this case
+ is <c>post_init_per_testcase</c>, but changing the return
+ value there did not affect the test case result. This is
+ now corrected.</p>
+ <p>
+ Own Id: OTP-15869 Aux Id: ERIERL-350 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add ct_netconfc support for NETCONF 1.1 (RFC 6241). The
+ 1.1 base capability can be sent in hello, and RFC 6242
+ chunk framing is applied when both client and server
+ advertise 1.1 support.</p>
+ <p>
+ Own Id: OTP-15789</p>
+ </item>
+ <item>
+ <p>
+ Correct lib_dir paths in common_tests opaque data
+ structure that is passed to ct_release_test callback
+ modules in functions upgrade_init/2, upgrade_upgraded/2
+ and upgrade_downgraded/2. The incorrect paths may cause
+ confusion when debugging although it will not cause any
+ incorrect behavior on the part of common_test as it is
+ currently not used.</p>
+ <p>
+ Own Id: OTP-15934</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.17.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 80eaed70bd..76689dab8c 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -62,9 +62,6 @@ MODULES= \
ct_repeat \
ct_telnet_client \
ct_make \
- vts \
- ct_webtool \
- ct_webtool_sup \
unix_telnet \
ct_config \
ct_config_plain \
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index efebea896c..271bb2335b 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -49,10 +49,7 @@
ct_telnet,
ct_testspec,
ct_util,
- ct_webtool,
- ct_webtool_sup,
unix_telnet,
- vts,
ct_config,
ct_config_plain,
ct_config_xml,
@@ -72,7 +69,6 @@
ct_util_server,
ct_config_server,
ct_make_ref,
- vts,
ct_master,
ct_master_logs,
test_server_ctrl,
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 3689c6bc45..7c6ba28180 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -221,17 +221,7 @@ handle_event(Event,State=#state{receivers=RecvPids}) ->
%% report to master
report_event({master,Master},E=#event{name=_Name,node=_Node,data=_Data}) ->
- ct_master:status(Master,E);
-
-%% report to VTS
-report_event({vts,VTS},#event{name=Name,node=_Node,data=Data}) ->
- if Name == start_info ;
- Name == test_stats ;
- Name == test_done ->
- vts:test_info(VTS,Name,Data);
- true ->
- ok
- end.
+ ct_master:status(Master,E).
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index bce6420042..367b5f5fdc 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1550,8 +1550,7 @@ report(What,Data) ->
end;
_ ->
ok
- end,
- catch vts:report(What,Data).
+ end.
add_to_stats(Result) ->
Update = fun({Ok,Failed,Skipped={UserSkipped,AutoSkipped}}) ->
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 6a758c4ea3..7ad6fa46e8 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -1,7 +1,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
%% Netconf servers can be configured by adding the following statement
%% to a configuration file:
%%
-%% {server_id(),options()}.
+%% {server_id(), [option()]}.
%%
%% The server_id() or an associated ct:target_name() shall then be
%% used in calls to open/2 connect/2.
@@ -55,13 +55,14 @@
%% The netconf client is also compliant with RFC5277 NETCONF Event
%% Notifications, which defines a mechanism for an asynchronous
%% message notification delivery service for the netconf protocol.
-%%
-%% Specific functions to support this are create_subscription/6
-%% get_event_streams/3. (The functions also exist with other arities.)
+%% Functions supporting this are create_subscription/3
+%% get_event_streams/3.
%%
%%----------------------------------------------------------------------
-module(ct_netconfc).
+-dialyzer(no_improper_lists).
+
-include("ct_netconfc.hrl").
-include("ct_util.hrl").
-include_lib("xmerl/include/xmerl.hrl").
@@ -107,12 +108,8 @@
copy_config/4,
action/2,
action/3,
- create_subscription/1,
create_subscription/2,
create_subscription/3,
- create_subscription/4,
- create_subscription/5,
- create_subscription/6,
get_event_streams/1,
get_event_streams/2,
get_event_streams/3,
@@ -121,6 +118,12 @@
get_session_id/1,
get_session_id/2]).
+%% historic, no longer documented
+-export([create_subscription/1,
+ create_subscription/4,
+ create_subscription/5,
+ create_subscription/6]).
+
%%----------------------------------------------------------------------
%% Exported types
%%----------------------------------------------------------------------
@@ -163,6 +166,9 @@
(is_atom(Xml) orelse (is_tuple(Xml) andalso is_atom(element(1,Xml))))).
-define(is_string(S), (is_list(S) andalso is_integer(hd(S)))).
+%% Keys into the process dictionary.
+-define(KEY(T), {?MODULE, T}).
+
%%----------------------------------------------------------------------
%% Records
%%----------------------------------------------------------------------
@@ -173,9 +179,9 @@
capabilities,
session_id,
msg_id = 1,
- hello_status,
- no_end_tag_buff = <<>>,
- buff = <<>>,
+ hello_status, % undefined | received | #pending{}
+ % string() | {error, Reason}
+ buf = false, % binary() | list() | boolean()
pending = [], % [#pending]
event_receiver}).% pid
@@ -195,10 +201,9 @@
type}).
%% Pending replies from server
--record(pending, {tref, % timer ref (returned from timer:xxx)
- ref, % pending ref
+-record(pending, {tref :: false | reference(), % timer reference
msg_id,
- op,
+ op,
caller}).% pid which sent the request
%%----------------------------------------------------------------------
@@ -207,13 +212,14 @@
-type client() :: handle() | server_id() | ct:target_name().
-opaque handle() :: pid().
--type options() :: [option()].
--type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} |
- {password,string()} | {user_dir,string()} |
- {timeout,timeout()}.
+-type option() :: {host | ssh, host()}
+ | {port, inet:port_number()}
+ | {timeout, timeout()}
+ | {capability, string() | [string()]}
+ | ssh:client_option().
--type session_options() :: [session_option()].
--type session_option() :: {timeout,timeout()}.
+-type session_option() :: {timeout,timeout()}
+ | {capability, string() | [string()]}.
-type host() :: inet:hostname() | inet:ip_address().
@@ -258,33 +264,44 @@
%% Open an SSH connection to a Netconf server
%% If the server options are specified in a configuration file, use
%% open/2.
+
+%% connect/1
+
-spec connect(Options) -> Result when
- Options :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Options :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
connect(Options) ->
- do_connect(Options, #options{type=connection},[]).
+ connect(Options, #options{type = connection}, []).
+
+%% connect/2
--spec connect(KeyOrName,ExtraOptions) -> Result when
+-spec connect(KeyOrName, ExtraOptions) -> Result when
KeyOrName :: ct:key_or_name(),
- ExtraOptions :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ ExtraOptions :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
connect(KeyOrName, ExtraOptions) ->
- SortedExtra = lists:keysort(1,ExtraOptions),
- SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
- AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
- do_connect(AllOpts,#options{name=KeyOrName,type=connection},[{name,KeyOrName}]).
-
-do_connect(OptList,InitOptRec,NameOpt) ->
- case check_options(OptList,InitOptRec) of
- {Host,Port,Options} ->
- ct_gen_conn:start({Host,Port},Options,?MODULE,
- NameOpt ++ [{reconnect,false},
- {use_existing_connection,false},
- {forward_messages,false}]);
- Error ->
- Error
+ connect(make_opts(KeyOrName, ExtraOptions),
+ #options{name = KeyOrName, type = connection},
+ [{name, KeyOrName}]).
+
+%% connect/3
+
+connect(Opts, InitRec, NameOpt) ->
+ case make_options(Opts, InitRec) of
+ #options{} = Rec ->
+ start(Rec, NameOpt, false);
+ {error, _} = No ->
+ No
end.
+%% make_opts/2
+
+make_opts(KeyOrName, ExtraOptions) ->
+ SortedExtra = lists:keysort(1, ExtraOptions),
+ SortedConfig = lists:keysort(1, ct:get_config(KeyOrName, [])),
+ lists:ukeymerge(1, SortedConfig, SortedExtra).
+
%%----------------------------------------------------------------------
%% Close the given SSH connection.
-spec disconnect(Conn) -> ok | {error,error_reason()} when
@@ -300,146 +317,185 @@ disconnect(Conn) ->
%%----------------------------------------------------------------------
%% Open a netconf session as a channel on the given SSH connection,
%% and exchange `hello' messages.
+
+%% session/1
+
-spec session(Conn) -> Result when
Conn :: handle(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Result :: {ok, handle()} | {error, error_reason()}.
+
session(Conn) ->
- do_session(Conn,[],#options{type=channel},[]).
+ session(Conn, [], #options{type = channel}, []).
--spec session(Conn,Options) -> Result when
+%% session/2
+
+-spec session(Conn, Options) -> Result when
Conn :: handle(),
- Options :: session_options(),
- Result :: {ok,handle()} | {error,error_reason()};
- (KeyOrName,Conn) -> Result when
+ Options :: [session_option()],
+ Result :: {ok, handle()} | {error, error_reason()};
+ (KeyOrName, Conn) -> Result when
KeyOrName :: ct:key_or_name(),
Conn :: handle(),
- Result :: {ok,handle()} | {error,error_reason()}.
-session(Conn,Options) when is_list(Options) ->
- do_session(Conn,Options,#options{type=channel},[]);
-session(KeyOrName,Conn) ->
- do_session(Conn,[],#options{name=KeyOrName,type=channel},[{name,KeyOrName}]).
+ Result :: {ok, handle()} | {error, error_reason()}.
+
+session(Conn, Options) when is_list(Options) ->
+ session(Conn, Options, #options{type = channel}, []);
--spec session(KeyOrName,Conn,Options) -> Result when
+session(KeyOrName, Conn) ->
+ session(Conn,
+ [],
+ #options{name = KeyOrName, type = channel},
+ [{name, KeyOrName}]).
+
+%% session/3
+
+-spec session(KeyOrName, Conn, Options) -> Result when
Conn :: handle(),
- Options :: session_options(),
+ Options :: [session_option()],
KeyOrName :: ct:key_or_name(),
- Result :: {ok,handle()} | {error,error_reason()}.
-session(KeyOrName,Conn,ExtraOptions) ->
- SortedExtra = lists:keysort(1,ExtraOptions),
- SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
- AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
- do_session(Conn,AllOpts,#options{name=KeyOrName,type=channel},
- [{name,KeyOrName}]).
-
-do_session(Conn,OptList,InitOptRec,NameOpt) ->
- case call(Conn,get_ssh_connection) of
- {ok,SshConn} ->
- case check_session_options(OptList,InitOptRec) of
- {ok,Options} ->
- case ct_gen_conn:start(SshConn,Options,?MODULE,
- NameOpt ++
- [{reconnect,false},
- {use_existing_connection,false},
- {forward_messages,true}]) of
- {ok,Client} ->
- case hello(Client,Options#options.timeout) of
- ok ->
- {ok,Client};
- Error ->
- Error
- end;
- Error ->
- Error
- end;
- Error ->
- Error
- end;
- Error ->
- Error
+ Result :: {ok, handle()} | {error, error_reason()}.
+
+session(KeyOrName, Conn, ExtraOptions) ->
+ session(Conn,
+ make_opts(KeyOrName, ExtraOptions),
+ #options{name = KeyOrName, type = channel},
+ [{name, KeyOrName}]).
+
+%% session/4
+
+session(Conn, Opts, InitRec, NameOpt) ->
+ T = make_ref(),
+ try
+ [_ | {ok, SshConn}] = [T | call(Conn, get_ssh_connection)],
+ [_ | #options{} = Rec] = [T | make_session_options(Opts, InitRec)],
+ [_ | {ok, Client} = Ok] = [T | start(SshConn, Rec, NameOpt, true)],
+ [_ | ok] = [T | hello(Client, caps(Opts), Rec#options.timeout)],
+ Ok
+ catch
+ error: {badmatch, [T | Error]} ->
+ Error
end.
+%% caps/1
+
+caps(Opts) ->
+ [T || {capability, _} = T <- Opts].
+
%%----------------------------------------------------------------------
%% Open a netconf session and exchange 'hello' messages.
%% If the server options are specified in a configuration file, use
%% open/2.
+
+%% open/1
+
-spec open(Options) -> Result when
- Options :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Options :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
open(Options) ->
- open(Options,#options{type=connection_and_channel},[],true).
+ open(Options,
+ #options{type = connection_and_channel},
+ [],
+ true).
--spec open(KeyOrName, ExtraOptions) -> Result when
+-spec open(KeyOrName, ExtraOption) -> Result when
KeyOrName :: ct:key_or_name(),
- ExtraOptions :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ ExtraOption :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
open(KeyOrName, ExtraOpts) ->
open(KeyOrName, ExtraOpts, true).
-open(KeyOrName, ExtraOpts, Hello) ->
- SortedExtra = lists:keysort(1,ExtraOpts),
- SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
- AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
- open(AllOpts,#options{name=KeyOrName,type=connection_and_channel},
- [{name,KeyOrName}],Hello).
-
-open(OptList,InitOptRec,NameOpt,Hello) ->
- case check_options(OptList,InitOptRec) of
- {Host,Port,Options} ->
- case ct_gen_conn:start({Host,Port},Options,?MODULE,
- NameOpt ++ [{reconnect,false},
- {use_existing_connection,false},
- {forward_messages,true}]) of
- {ok,Client} when Hello==true ->
- case hello(Client,Options#options.timeout) of
- ok ->
- {ok,Client};
- Error ->
- Error
- end;
- Other ->
- Other
- end;
- Error ->
- Error
+%% open/3
+
+open(KeyOrName, ExtraOptions, Hello) ->
+ open(make_opts(KeyOrName, ExtraOptions),
+ #options{name = KeyOrName, type = connection_and_channel},
+ [{name, KeyOrName}],
+ Hello).
+
+%% open/4
+
+open(Opts, InitRec, NameOpt, Hello) ->
+ T = make_ref(),
+ try
+ [_, #options{} = Rec] = [T, make_options(Opts, InitRec)],
+ [_, {ok, Client} = Ok | true] = [T, start(Rec, NameOpt, true) | Hello],
+ [_, ok] = [T, hello(Client, caps(Opts), Rec#options.timeout)],
+ Ok
+ catch
+ error: {badmatch, [T, Res | _]} ->
+ Res
end.
+%% start/3
+
+start(#options{host = undefined}, _, _) ->
+ {error, no_host_address};
+
+start(#options{port = undefined}, _, _) ->
+ {error, no_port};
+
+start(#options{host = Host, port = Port} = Opts, NameOpt, Fwd) ->
+ start({Host, Port}, Opts, NameOpt, Fwd).
+
+%% start/4
+
+start(Ep, Opts, NameOpt, Fwd) ->
+ ct_gen_conn:start(Ep, Opts, ?MODULE, [{reconnect, false},
+ {use_existing_connection, false},
+ {forward_messages, Fwd}
+ | NameOpt]).
%%----------------------------------------------------------------------
-%% As open/1,2, except no 'hello' message is sent.
+%% Like open/1,2, but no 'hello' message is sent.
+
-spec only_open(Options) -> Result when
- Options :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Options :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
only_open(Options) ->
- open(Options,#options{type=connection_and_channel},[],false).
+ open(Options, #options{type = connection_and_channel}, [], false).
--spec only_open(KeyOrName,ExtraOptions) -> Result when
+-spec only_open(KeyOrName, ExtraOptions) -> Result when
KeyOrName :: ct:key_or_name(),
- ExtraOptions :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ ExtraOptions :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
only_open(KeyOrName, ExtraOpts) ->
open(KeyOrName, ExtraOpts, false).
%%----------------------------------------------------------------------
%% Send a 'hello' message.
+
+%% hello/1
+
-spec hello(Client) -> Result when
Client :: handle(),
- Result :: ok | {error,error_reason()}.
+ Result :: ok | {error, error_reason()}.
+
hello(Client) ->
- hello(Client,[],?DEFAULT_TIMEOUT).
+ hello(Client, [], ?DEFAULT_TIMEOUT).
+
+%% hello/2
--spec hello(Client,Timeout) -> Result when
+-spec hello(Client, Timeout) -> Result when
Client :: handle(),
Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
-hello(Client,Timeout) ->
- hello(Client,[],Timeout).
+ Result :: ok | {error, error_reason()}.
+
+hello(Client, Timeout) ->
+ hello(Client, [], Timeout).
--spec hello(Client,Options,Timeout) -> Result when
+%% hello/3
+
+-spec hello(Client, Options, Timeout) -> Result when
Client :: handle(),
Options :: [{capability, [string()]}],
Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
-hello(Client,Options,Timeout) ->
+ Result :: ok | {error, error_reason()}.
+
+hello(Client, Options, Timeout) ->
call(Client, {hello, Options, Timeout}).
@@ -675,117 +731,122 @@ action(Client,Action,Timeout) ->
%%----------------------------------------------------------------------
%% Send a 'create-subscription' request
%% See RFC5277, NETCONF Event Notifications
--spec create_subscription(Client) -> Result when
- Client :: client(),
- Result :: ok | {error,error_reason()}.
-create_subscription(Client) ->
- create_subscription(Client,?DEFAULT_STREAM,?DEFAULT_TIMEOUT).
--spec create_subscription(Client, Stream | Filter | Timeout) -> Result when
+%% create_subscription/2
+
+-spec create_subscription(Client, Values) -> Result when
Client :: client(),
+ Values :: #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime},
Stream :: stream_name(),
Filter :: simple_xml() | [simple_xml()],
- Timeout :: timeout(),
+ StartTime :: xs_datetime(),
+ StopTime :: xs_datetime(),
+ Result :: ok | {error,error_reason()};
+ %% historic, no longer documented
+ (Client, list() | timeout()) -> Result when
+ Client :: client(),
Result :: ok | {error,error_reason()}.
-create_subscription(Client,Timeout)
+
+create_subscription(Client, #{} = Values) ->
+ create_subscription(Client, Values, ?DEFAULT_TIMEOUT);
+
+%% historic clauses
+create_subscription(Client, Timeout)
when ?is_timeout(Timeout) ->
- create_subscription(Client,?DEFAULT_STREAM,Timeout);
-create_subscription(Client,Stream)
+ create_subscription(Client, #{}, Timeout);
+create_subscription(Client, Stream)
when ?is_string(Stream) ->
- create_subscription(Client,Stream,?DEFAULT_TIMEOUT);
-create_subscription(Client,Filter)
+ create_subscription(Client, #{stream => Stream});
+create_subscription(Client, Filter)
when ?is_filter(Filter) ->
- create_subscription(Client,?DEFAULT_STREAM,Filter,
- ?DEFAULT_TIMEOUT).
+ create_subscription(Client, #{filter => Filter}).
-create_subscription(Client,Stream,Timeout)
- when ?is_string(Stream) andalso
- ?is_timeout(Timeout) ->
- call(Client,{send_rpc_op,{create_subscription,self()},
- [Stream,undefined,undefined,undefined],
- Timeout});
-create_subscription(Client,StartTime,StopTime)
- when ?is_string(StartTime) andalso
- ?is_string(StopTime) ->
- create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,
- ?DEFAULT_TIMEOUT);
-create_subscription(Client,Filter,Timeout)
- when ?is_filter(Filter) andalso
- ?is_timeout(Timeout) ->
- create_subscription(Client,?DEFAULT_STREAM,Filter,Timeout);
-create_subscription(Client,Stream,Filter)
- when ?is_string(Stream) andalso
- ?is_filter(Filter) ->
- create_subscription(Client,Stream,Filter,?DEFAULT_TIMEOUT).
-
-create_subscription(Client,StartTime,StopTime,Timeout)
- when ?is_string(StartTime) andalso
- ?is_string(StopTime) andalso
- ?is_timeout(Timeout) ->
- create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,Timeout);
-create_subscription(Client,Stream,StartTime,StopTime)
- when ?is_string(Stream) andalso
- ?is_string(StartTime) andalso
- ?is_string(StopTime) ->
- create_subscription(Client,Stream,StartTime,StopTime,?DEFAULT_TIMEOUT);
-create_subscription(Client,Filter,StartTime,StopTime)
- when ?is_filter(Filter) andalso
- ?is_string(StartTime) andalso
- ?is_string(StopTime) ->
- create_subscription(Client,?DEFAULT_STREAM,Filter,
- StartTime,StopTime,?DEFAULT_TIMEOUT);
-create_subscription(Client,Stream,Filter,Timeout)
- when ?is_string(Stream) andalso
- ?is_filter(Filter) andalso
- ?is_timeout(Timeout) ->
- call(Client,{send_rpc_op,{create_subscription,self()},
- [Stream,Filter,undefined,undefined],
- Timeout}).
-
--spec create_subscription(Client, Stream, StartTime, StopTime, Timeout) ->
- Result when
+-spec create_subscription(Client, Values, Timeout) -> Result when
Client :: client(),
+ Values :: #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime},
Stream :: stream_name(),
+ Filter :: simple_xml() | [simple_xml()],
StartTime :: xs_datetime(),
StopTime :: xs_datetime(),
Timeout :: timeout(),
Result :: ok | {error,error_reason()};
- (Client, Stream, Filter,StartTime, StopTime) ->
- Result when
+ %% historic, no longer documented
+ (Client, list(), list() | timeout()) -> Result when
Client :: client(),
- Stream :: stream_name(),
- Filter :: simple_xml() | [simple_xml()],
- StartTime :: xs_datetime(),
- StopTime :: xs_datetime(),
Result :: ok | {error,error_reason()}.
-create_subscription(Client,Stream,StartTime,StopTime,Timeout)
- when ?is_string(Stream) andalso
- ?is_string(StartTime) andalso
- ?is_string(StopTime) andalso
+
+create_subscription(Client, #{} = Values, Timeout) ->
+ Keys = [{stream, ?DEFAULT_STREAM},
+ {filter, undefined},
+ {start, undefined},
+ {stop, undefined}],
+ call(Client, {send_rpc_op, {create_subscription, self()},
+ [maps:get(K, Values, D) || {K,D} <- Keys],
+ Timeout});
+
+%% historic clauses, arity 3
+create_subscription(Client, Stream, Timeout)
+ when ?is_string(Stream), ?is_timeout(Timeout) ->
+ create_subscription(Client, #{stream => Stream}, Timeout);
+create_subscription(Client, StartTime, StopTime)
+ when ?is_string(StartTime), ?is_string(StopTime) ->
+ create_subscription(Client, #{start => StartTime, stop => StopTime});
+create_subscription(Client, Filter, Timeout)
+ when ?is_filter(Filter), ?is_timeout(Timeout) ->
+ create_subscription(Client, #{filter => Filter}, Timeout);
+create_subscription(Client, Stream, Filter)
+ when ?is_string(Stream), ?is_filter(Filter) ->
+ create_subscription(Client, #{stream => Stream, filter => Filter}).
+
+%% historic clauses, arity 1,4-5
+create_subscription(Client) ->
+ create_subscription(Client, #{}).
+create_subscription(Client, StartTime, StopTime, Timeout)
+ when ?is_string(StartTime), ?is_string(StopTime), ?is_timeout(Timeout) ->
+ Values = #{start => StartTime,
+ stop => StopTime},
+ create_subscription(Client, Values, Timeout);
+create_subscription(Client, Stream, StartTime, StopTime)
+ when ?is_string(Stream), ?is_string(StartTime), ?is_string(StopTime) ->
+ create_subscription(Client, #{stream => Stream,
+ start => StartTime,
+ stop => StopTime});
+create_subscription(Client, Filter, StartTime, StopTime)
+ when ?is_filter(Filter), ?is_string(StartTime), ?is_string(StopTime) ->
+ create_subscription(Client, #{filter => Filter,
+ start => StartTime,
+ stop => StopTime});
+create_subscription(Client, Stream, Filter, Timeout)
+ when ?is_string(Stream), ?is_filter(Filter), ?is_timeout(Timeout) ->
+ Values = #{stream => Stream,
+ filter => Filter},
+ create_subscription(Client, Values, Timeout).
+create_subscription(Client, Stream, StartTime, StopTime, Timeout)
+ when ?is_string(Stream), ?is_string(StartTime), ?is_string(StopTime),
?is_timeout(Timeout) ->
- call(Client,{send_rpc_op,{create_subscription,self()},
- [Stream,undefined,StartTime,StopTime],
- Timeout});
-create_subscription(Client,Stream,Filter,StartTime,StopTime)
- when ?is_string(Stream) andalso
- ?is_filter(Filter) andalso
- ?is_string(StartTime) andalso
+ Values = #{stream => Stream,
+ start => StartTime,
+ stop => StopTime},
+ create_subscription(Client, Values, Timeout);
+create_subscription(Client, Stream, Filter, StartTime, StopTime)
+ when ?is_string(Stream), ?is_filter(Filter), ?is_string(StartTime),
?is_string(StopTime) ->
- create_subscription(Client,Stream,Filter,StartTime,StopTime,?DEFAULT_TIMEOUT).
-
--spec create_subscription(Client, Stream, Filter,StartTime, StopTime, Timeout) ->
- Result when
- Client :: client(),
- Stream :: stream_name(),
- Filter :: simple_xml() | [simple_xml()],
- StartTime :: xs_datetime(),
- StopTime :: xs_datetime(),
- Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
-create_subscription(Client,Stream,Filter,StartTime,StopTime,Timeout) ->
- call(Client,{send_rpc_op,{create_subscription, self()},
- [Stream,Filter,StartTime,StopTime],
- Timeout}).
+ create_subscription(Client, #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime}).
+create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout) ->
+ Values = #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime},
+ create_subscription(Client, Values, Timeout).
%%----------------------------------------------------------------------
%% Send a request to get the given event streams
@@ -859,6 +920,8 @@ kill_session(Client, SessionId, Timeout) ->
%% Callback functions
%%----------------------------------------------------------------------
+%% init/3
+
init(_KeyOrName,{CM,{Host,Port}},Options) ->
case ssh_channel(#connection{reference=CM,host=Host,port=Port},Options) of
{ok,Connection} ->
@@ -883,29 +946,32 @@ init(_KeyOrName,{_Host,_Port},Options) ->
{error,Reason}
end.
+%% terminate/2
terminate(_, #state{connection=Connection}) ->
ssh_close(Connection),
ok.
-handle_msg({hello, Options, Timeout}, From,
- #state{connection=Connection,hello_status=HelloStatus} = State) ->
+%% handle_msg/3
+
+%% Send hello and return to the caller only after reception of the
+%% server's hello.
+handle_msg({hello, Options, Timeout},
+ From,
+ #state{connection = Connection,
+ hello_status = HelloStatus}
+ = State) ->
case do_send(Connection, client_hello(Options)) of
- ok ->
- case HelloStatus of
- undefined ->
- {Ref,TRef} = set_request_timer(Timeout),
- {noreply, State#state{hello_status=#pending{tref=TRef,
- ref=Ref,
- caller=From}}};
- received ->
- {reply, ok, State#state{hello_status=done}};
- {error,Reason} ->
- {stop, {error,Reason}, State}
- end;
- Error ->
+ ok when HelloStatus == undefined -> %% server hello not yet received
+ TRef = set_request_timer(Timeout, hello),
+ {noreply, State#state{hello_status = #pending{tref = TRef,
+ caller = From}}};
+ ok -> %% or yes: negotiate version
+ handle_capx(State);
+ Error ->
{stop, Error, State}
end;
+
handle_msg(get_ssh_connection, _From, #state{connection=Connection}=State) ->
Reply =
case Connection#connection.reference of
@@ -914,29 +980,40 @@ handle_msg(get_ssh_connection, _From, #state{connection=Connection}=State) ->
Connection#connection.port}}}
end,
{reply, Reply, State};
-handle_msg(_, _From, #state{session_id=undefined} = State) ->
- %% Hello is not yet excanged - this shall never happen
- {reply,{error,waiting_for_hello},State};
+
+%% Request before server hello. Possible with only_open, since a
+%% handle is then returned without waiting for the server.
+handle_msg(_, _From, #state{session_id = undefined} = State) ->
+ {reply, {error, waiting_for_hello}, State};
+
handle_msg(get_capabilities, _From, #state{capabilities = Caps} = State) ->
{reply, Caps, State};
+
handle_msg(get_session_id, _From, #state{session_id = Id} = State) ->
{reply, Id, State};
-handle_msg({send, Timeout, SimpleXml}, From,
- #state{connection=Connection,pending=Pending} = State) ->
+
+handle_msg({send, Timeout, SimpleXml},
+ From,
+ #state{connection = Connection,
+ pending = Pending}
+ = State) ->
case do_send(Connection, SimpleXml) of
- ok ->
- {Ref,TRef} = set_request_timer(Timeout),
- {noreply, State#state{pending=[#pending{tref=TRef,
- ref=Ref,
- caller=From} | Pending]}};
- Error ->
- {reply, Error, State}
+ ok ->
+ TRef = set_request_timer(Timeout, send),
+ {noreply, State#state{pending = [#pending{tref = TRef,
+ caller = From}
+ | Pending]}};
+ Error ->
+ {reply, Error, State}
end;
+
handle_msg({send_rpc, SimpleXml, Timeout}, From, State) ->
do_send_rpc(undefined, SimpleXml, Timeout, From, State);
+
handle_msg({send_rpc_op, Op, Data, Timeout}, From, State) ->
SimpleXml = encode_rpc_operation(Op,Data),
do_send_rpc(Op, SimpleXml, Timeout, From, State);
+
handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
Filter = {netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,
[{streams,[{stream,[{name,[Name]}]} || Name <- Streams]}]},
@@ -945,7 +1022,9 @@ handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
handle_msg({ssh_cm, CM, {data, Ch, _Type, Data}}, State) ->
ssh_connection:adjust_window(CM,Ch,size(Data)),
+ log(State#state.connection, recv, Data),
handle_data(Data, State);
+
handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
%% _SshCloseMsg can probably be one of
%% {eof,Ch}
@@ -962,21 +1041,29 @@ handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
%%! connection - due to terminate/2
{stop, State};
-handle_msg({Ref,timeout},
- #state{hello_status=#pending{ref=Ref,caller=Caller}} = State) ->
- ct_gen_conn:return(Caller,{error,{hello_session_failed,timeout}}),
- {stop,State#state{hello_status={error,timeout}}};
-handle_msg({Ref,timeout},#state{pending=Pending} = State) ->
- {value,#pending{op=Op,caller=Caller},Pending1} =
- lists:keytake(Ref,#pending.ref,Pending),
- ct_gen_conn:return(Caller,{error,timeout}),
- R = case Op of
- close_session -> stop;
- _ -> noreply
- end,
- %% Halfhearted try to get in correct state, this matches
- %% the implementation before this patch
- {R,State#state{pending=Pending1, no_end_tag_buff= <<>>, buff= <<>>}}.
+
+handle_msg({timeout, TRef, hello},
+ #state{hello_status = #pending{tref = TRef,
+ caller = From}}
+ = State) ->
+ ct_gen_conn:return(From, {error, {hello_session_failed, timeout}}),
+ {stop, State#state{hello_status = {error,timeout}}};
+
+handle_msg({timeout, TRef, Op}, #state{pending = Pending} = State) ->
+ case lists:keytake(TRef, #pending.tref, Pending) of
+ {value, #pending{caller = From}, Rest} ->
+ ct_gen_conn:return(From, {error, timeout}),
+ %% Discard received bytes in hope that the server has sent
+ %% an incomplete message. Otherwise this is doomed to
+ %% leave the connection in an unusable state.
+ {if Op == close_session -> stop; true -> noreply end,
+ State#state{pending = Rest,
+ buf = is_binary(State#state.buf)}};
+ false ->
+ {noreply, State}
+ end.
+
+%% close/1
%% Called by ct_util_server to close registered connections before terminate.
close(Client) ->
@@ -1048,63 +1135,163 @@ get_handle(Client) ->
Error
end.
-check_options(OptList,Options) ->
- check_options(OptList,undefined,undefined,Options).
+%% make_options/2
-check_options([], undefined, _Port, _Options) ->
- {error, no_host_address};
-check_options([], _Host, undefined, _Options) ->
- {error, no_port};
-check_options([], Host, Port, Options) ->
- {Host,Port,Options};
-check_options([{ssh, Host}|T], _, Port, Options) ->
- check_options(T, Host, Port, Options#options{host=Host});
-check_options([{port,Port}|T], Host, _, Options) ->
- check_options(T, Host, Port, Options#options{port=Port});
-check_options([{timeout, Timeout}|T], Host, Port, Options)
- when is_integer(Timeout); Timeout==infinity ->
- check_options(T, Host, Port, Options#options{timeout = Timeout});
-check_options([{timeout, _} = Opt|_T], _Host, _Port, _Options) ->
- {error, {invalid_option, Opt}};
-check_options([Opt|T], Host, Port, #options{ssh=SshOpts}=Options) ->
- %% Option verified by ssh
- check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]}).
-
-check_session_options([],Options) ->
- {ok,Options};
-check_session_options([{timeout, Timeout}|T], Options)
- when is_integer(Timeout); Timeout==infinity ->
- check_session_options(T, Options#options{timeout = Timeout});
-check_session_options([Opt|_T], _Options) ->
- {error, {invalid_option, Opt}}.
+make_options(Opts, Rec) ->
+ make_options(Opts, Rec#options{port = undefined}, fun opt/2).
+
+opt({T, Host}, Rec)
+ when T == ssh;
+ T == host ->
+ Rec#options{host = Host};
+
+opt({port, Port}, Rec) ->
+ Rec#options{port = Port};
+
+opt({timeout, Tmo}, Rec)
+ when is_integer(Tmo);
+ Tmo == infinity ->
+ Rec#options{timeout = Tmo};
+
+opt({timeout, _} = T, _) ->
+ throw(T);
+
+opt({capability, _}, Rec) ->
+ Rec;
+
+opt(Opt, #options{ssh = Opts} = Rec) -> %% option verified by ssh
+ Rec#options{ssh = [Opt | Opts]}.
+
+%% make_session_options/2
+
+make_session_options(Opts, Rec) ->
+ make_options(Opts, Rec, fun session_opt/2).
+
+session_opt({capability, _}, Rec) ->
+ Rec;
+
+session_opt({timeout, Tmo}, Rec)
+ when is_integer(Tmo);
+ Tmo == infinity ->
+ Rec#options{timeout = Tmo};
+session_opt(T, _Rec) ->
+ throw(T).
+
+%% make_options/3
+
+make_options(Opts, Rec, F) ->
+ try
+ #options{} = lists:foldl(F, Rec, Opts)
+ catch
+ T ->
+ {error, {invalid_option, T}}
+ end.
%%%-----------------------------------------------------------------
-set_request_timer(infinity) ->
- {undefined,undefined};
-set_request_timer(T) ->
- Ref = make_ref(),
- {ok,TRef} = timer:send_after(T,{Ref,timeout}),
- {Ref,TRef}.
+
+set_request_timer(infinity, _) ->
+ false;
+
+set_request_timer(Tmo, Op) ->
+ erlang:start_timer(Tmo, self(), Op).
%%%-----------------------------------------------------------------
-cancel_request_timer(undefined,undefined) ->
+
+cancel_request_timer(false) ->
ok;
-cancel_request_timer(Ref,TRef) ->
- _ = timer:cancel(TRef),
- receive {Ref,timeout} -> ok
- after 0 -> ok
- end.
+
+cancel_request_timer(TRef) ->
+ erlang:cancel_timer(TRef).
%%%-----------------------------------------------------------------
-client_hello(Options) when is_list(Options) ->
- UserCaps = [{capability, UserCap} ||
- {capability, UserCap} <- Options,
- is_list(hd(UserCap))],
- {hello, ?NETCONF_NAMESPACE_ATTR,
- [{capabilities,
- [{capability,[?NETCONF_BASE_CAP++?NETCONF_BASE_CAP_VSN]}|
- UserCaps]}]}.
+
+%% client_hello/1
+%%
+%% Prepend the 1.0 base capability only if none is specified by the
+%% user. Store the versions in the process dictionary until they're
+%% examined upon reception of server capabilities in handle_capx/1.
+
+client_hello(Opts)
+ when is_list(Opts) ->
+ UserCaps = [{T, cap(lists:flatten(Cs))} || {capability = T, Cs} <- Opts],
+ Vsns = versions(UserCaps),
+ put(?KEY(protocol_vsn), Vsns),
+ {hello,
+ ?NETCONF_NAMESPACE_ATTR,
+ [{capabilities, [{capability, [?NETCONF_BASE_CAP, ?NETCONF_BASE_CAP_VSN]}
+ || [] == Vsns]
+ ++ UserCaps}]}.
+
+%% cap/1
+%%
+%% Let NETCONF capabilities be specified in the shorthand documented in
+%% RFC 6241.
+
+%% This shorthand is documented in RFC 6241 10.4 NETCONF Capabilities
+%% URNS, but not in 8 Capabilities.
+cap(":base:" ++ _ = Str) ->
+ ["urn:ietf:params:netconf", Str];
+
+cap([$:|_] = Str) ->
+ ["urn:ietf:params:netconf:capability", Str];
+
+cap(Str) ->
+ [Str].
+
+%% versions/1
+%%
+%% Extract base protocol versions from capability options.
+
+versions(Opts) ->
+ [V || {capability, L} <- Opts,
+ S <- L,
+ ?NETCONF_BASE_CAP ++ X <- [lists:flatten(S)],
+ V <- [lists:takewhile(fun(C) -> C /= $? end, X)]].
+
+%% handle_capx/1
+%%
+%% Ignore parameters as RFC 6241 (NETCONF 1.1) requires in 8.1
+%% Capabilities Exchange. Be overly lenient with whitespace since RFC
+%% 6241 gives examples with significant trailing whitespace.
+
+handle_capx(#state{hello_status = received, capabilities = Caps} = S) ->
+ Remote = [V || ?NETCONF_BASE_CAP ++ X <- Caps,
+ [V|_] <- [string:lexemes(X, "? \t\r\n")]],
+ Local = erase(?KEY(protocol_vsn)),
+ case protocol_vsn(Local, Remote) of
+ false when Remote == [] ->
+ Reason = {incorrect_hello, no_base_capability_found},
+ {stop, {error, Reason}, S};
+ false ->
+ Reason = {incompatible_base_capability_vsn, lists:min(Remote)},
+ {stop, {error, Reason}, S};
+ Vsn ->
+ put(?KEY(chunk), Vsn /= "1.0"),
+ {reply, ok, rebuf(Vsn, S#state{hello_status = Vsn})}
+ end;
+
+handle_capx(#state{hello_status = {error, _} = No} = S) ->
+ {stop, No, S}.
+
+%% rebuf/2
+%%
+%% Turn the message buffer into a list for 1.1 chunking if the
+%% negotiated protocol version is > 1.0.
+
+rebuf("1.0", S) ->
+ S;
+
+rebuf(_, #state{buf = Bin} = S) ->
+ S#state{buf = [Bin, 3]}.
+
+%% protocol_vsn/2
+
+protocol_vsn([], Vsns) ->
+ protocol_vsn(["1.0"], Vsns);
+
+protocol_vsn(Local, Remote) ->
+ lists:max([false | [V || V <- Remote, lists:member(V, Local)]]).
%%%-----------------------------------------------------------------
@@ -1150,111 +1337,130 @@ maybe_element(Tag,Value) ->
%%%-----------------------------------------------------------------
%%% Send XML data to server
-do_send_rpc(PendingOp,SimpleXml,Timeout,Caller,
- #state{connection=Connection,msg_id=MsgId,pending=Pending} = State) ->
- case do_send_rpc(Connection, MsgId, SimpleXml) of
- ok ->
- {Ref,TRef} = set_request_timer(Timeout),
- {noreply, State#state{msg_id=MsgId+1,
- pending=[#pending{tref=TRef,
- ref=Ref,
- msg_id=MsgId,
- op=PendingOp,
- caller=Caller} | Pending]}};
- Error ->
- {reply, Error, State#state{msg_id=MsgId+1}}
+do_send_rpc(Op, SimpleXml, Timeout, Caller, #state{connection = Connection,
+ msg_id = MsgId,
+ pending = Pending}
+ = State) ->
+ Msg = {rpc,
+ [{'message-id', MsgId} | ?NETCONF_NAMESPACE_ATTR],
+ [SimpleXml]},
+ Next = MsgId + 1,
+ case do_send(Connection, Msg) of
+ ok ->
+ TRef = set_request_timer(Timeout, Op),
+ Rec = #pending{tref = TRef,
+ msg_id = MsgId,
+ op = Op,
+ caller = Caller},
+ {noreply, State#state{msg_id = Next,
+ pending = [Rec | Pending]}};
+ Error ->
+ {reply, Error, State#state{msg_id = Next}}
end.
-do_send_rpc(Connection, MsgId, SimpleXml) ->
- do_send(Connection,
- {rpc,
- [{'message-id',MsgId} | ?NETCONF_NAMESPACE_ATTR],
- [SimpleXml]}).
+do_send(Connection, Simple) ->
+ ssh_send(Connection, frame(to_xml(Simple))).
-do_send(Connection, SimpleXml) ->
- Xml=to_xml_doc(SimpleXml),
- ssh_send(Connection, Xml).
-
-to_xml_doc(Simple) ->
+to_xml(Simple) ->
Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
- Xml = unicode:characters_to_binary(
- xmerl:export_simple([Simple],
- xmerl_xml,
- [#xmlAttribute{name=prolog,
- value=Prolog}])),
- <<Xml/binary,?END_TAG/binary>>.
+ Chars = xmerl:export_simple([Simple],
+ xmerl_xml,
+ [#xmlAttribute{name = prolog,
+ value = Prolog}]),
+ unicode:characters_to_binary(Chars).
+
+%% frame/1
+
+frame(Bin) ->
+ case get(?KEY(chunk)) of
+ true -> %% 1.1 chunking
+ [chunk(Bin) | "\n##\n"];
+ _ -> %% 1.0 framing
+ [Bin | ?END_TAG]
+ end.
+
+%% chunk/1
+%%
+%% Chunk randomly to exercise the server.
+
+chunk(<<>>) ->
+ [];
+
+chunk(Bin) ->
+ Sz = min(rand:uniform(1024), size(Bin)),
+ <<B:Sz/binary, Rest/binary>> = Bin,
+ ["\n#", integer_to_list(Sz), $\n, B | chunk(Rest)].
%%%-----------------------------------------------------------------
%%% Parse and handle received XML data
-%%% Two buffers are used:
-%%% * 'no_end_tag_buff' contains data that is checked and does not
-%%% contain any (part of an) end tag.
-%%% * 'buff' contains all other saved data - it may or may not
-%%% include (a part of) an end tag.
-%%% The reason for this is to avoid running binary:split/3 multiple
-%%% times on the same data when it does not contain an end tag. This
-%%% can be a considerable optimation in the case when a lot of data is
-%%% received (e.g. when fetching all data from a node) and the data is
-%%% sent in multiple ssh packages.
-handle_data(NewData,#state{connection=Connection} = State0) ->
- log(Connection,recv,NewData),
- NoEndTag0 = State0#state.no_end_tag_buff,
- Buff0 = State0#state.buff,
- Data = <<Buff0/binary, NewData/binary>>,
- case binary:split(Data,?END_TAG,[]) of
- [_NoEndTagFound] ->
- NoEndTagSize = case byte_size(Data) of
- Sz when Sz<5 -> 0;
- Sz -> Sz-5
- end,
- <<NoEndTag1:NoEndTagSize/binary,Buff/binary>> = Data,
- NoEndTag = <<NoEndTag0/binary,NoEndTag1/binary>>,
- {noreply, State0#state{no_end_tag_buff=NoEndTag, buff=Buff}};
- [FirstMsg0,Buff1] ->
- FirstMsg = remove_initial_nl(<<NoEndTag0/binary,FirstMsg0/binary>>),
- SaxArgs = [{event_fun,fun sax_event/3}, {event_state,[]}],
- case xmerl_sax_parser:stream(FirstMsg, SaxArgs) of
- {ok, Simple, _Thrash} ->
- case decode(Simple, State0#state{no_end_tag_buff= <<>>,
- buff=Buff1}) of
- {noreply, #state{buff=Buff} = State} when Buff =/= <<>> ->
- %% Recurse if we have more data in buffer
- handle_data(<<>>, State);
- Other ->
- Other
- end;
- {fatal_error,_Loc,Reason,_EndTags,_EventState} ->
- ?error(Connection#connection.name,
- [{parse_error,Reason},
- {buffer, Buff0},
- {new_data,NewData}]),
- handle_error(Reason, State0#state{no_end_tag_buff= <<>>,
- buff= <<>>})
- end
+
+handle_data(Bin, #state{buf = Head} = S) ->
+ case recv(Bin, Head) of
+ {error, Reason} ->
+ Conn = S#state.connection,
+ ?error(Conn#connection.name, [{receive_error, Reason},
+ {buffer, Head},
+ {bytes, Bin}]),
+ {stop, S};
+ {Bytes, Rest} ->
+ handle_more(Rest, handle_xml(Bytes, S));
+ Buf ->
+ {noreply, S#state{buf = Buf}}
end.
+%% handle_more/2
+
+handle_more(_, {stop, _} = No) ->
+ No;
+
+handle_more(Bin, {noreply, State}) ->
+ handle_data(Bin, State#state{buf = true == get(?KEY(chunk))}).
+
+%% handle_xml/2
+
+handle_xml(Bytes, State) ->
+ case parse(Bytes) of
+ {ok, Simple, _Rest} -> %% ignore trailing bytes
+ decode(Simple, State);
+ {fatal_error,_Loc,Reason,_EndTags,_EventState} ->
+ Conn = State#state.connection,
+ ?error(Conn#connection.name, [{parse_error, Reason},
+ {message, Bytes}]),
+ {noreply, handle_error(Reason, State)}
+ end.
+
+%% parse/1
+
+parse(Bytes) ->
+ xmerl_sax_parser:stream(<<>>, [{event_fun, fun sax_event/3},
+ {event_state, []},
+ {continuation_fun, fun cont/1},
+ {continuation_state, Bytes}]).
+
+%% cont/1
-%% xml does not accept a leading nl and some netconf server add a nl after
-%% each ?END_TAG, ignore them
-remove_initial_nl(<<"\n", Data/binary>>) ->
- remove_initial_nl(Data);
-remove_initial_nl(Data) ->
- Data.
-
-handle_error(Reason, State) ->
- Pending1 = case State#state.pending of
- [] -> [];
- Pending ->
- %% Assuming the first request gets the
- %% first answer
- P=#pending{tref=TRef,ref=Ref,caller=Caller} =
- lists:last(Pending),
- cancel_request_timer(Ref,TRef),
- Reason1 = {failed_to_parse_received_data,Reason},
- ct_gen_conn:return(Caller,{error,Reason1}),
- lists:delete(P,Pending)
- end,
- {noreply, State#state{pending=Pending1}}.
+cont([] = No) ->
+ {<<>>, No};
+
+cont([Bin | Rest]) ->
+ {Bin, Rest};
+
+cont(Bin) ->
+ {Bin, <<>>}.
+
+%% handle_error/2
+
+handle_error(_Reason, #state{pending = []} = State) ->
+ State;
+
+handle_error(Reason, #state{pending = Pending} = State) ->
+ %% Assuming the first request gets the first answer.
+ Rec = #pending{tref = TRef,
+ caller = Caller}
+ = lists:last(Pending),
+ cancel_request_timer(TRef),
+ ct_gen_conn:return(Caller,{error, {failed_to_parse_received_data, Reason}}),
+ State#state{pending = lists:delete(Rec, Pending)}.
%% Event function for the sax parser. It builds a simple XML structure.
%% Care is taken to keep namespace attributes and prefixes as in the original XML.
@@ -1305,136 +1511,180 @@ parse_attrs([]) ->
%%%-----------------------------------------------------------------
-%%% Decoding of parsed XML data
-decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
- ConnName = Connection#connection.name,
- case get_local_name_atom(Tag) of
- 'rpc-reply' ->
- case get_msg_id(Attrs) of
- undefined ->
- case Pending of
- [#pending{msg_id=MsgId}] ->
- ?error(ConnName,[{warning,rpc_reply_missing_msg_id},
- {assuming,MsgId}]),
- decode_rpc_reply(MsgId,E,State);
- _ ->
- ?error(ConnName,[{error,rpc_reply_missing_msg_id}]),
- {noreply,State}
- end;
- MsgId ->
- decode_rpc_reply(MsgId,E,State)
- end;
- hello ->
- case State#state.hello_status of
- undefined ->
- case decode_hello(E) of
- {ok,SessionId,Capabilities} ->
- {noreply,State#state{session_id = SessionId,
- capabilities = Capabilities,
- hello_status = received}};
- {error,Reason} ->
- {noreply,State#state{hello_status = {error,Reason}}}
- end;
- #pending{tref=TRef,ref=Ref,caller=Caller} ->
- cancel_request_timer(Ref,TRef),
- case decode_hello(E) of
- {ok,SessionId,Capabilities} ->
- ct_gen_conn:return(Caller,ok),
- {noreply,State#state{session_id = SessionId,
- capabilities = Capabilities,
- hello_status = done}};
- {error,Reason} ->
- ct_gen_conn:return(Caller,{error,Reason}),
- {stop,State#state{hello_status={error,Reason}}}
- end;
- Other ->
- ?error(ConnName,[{got_unexpected_hello,E},
- {hello_status,Other}]),
- {noreply,State}
- end;
- notification ->
- EventReceiver = State#state.event_receiver,
- EventReceiver ! E,
- {noreply,State};
- Other ->
- %% Result of send/2, when not sending an rpc request - or
- %% if netconf server sends noise. Can handle this only if
- %% there is just one pending that matches (i.e. has
- %% undefined msg_id and op)
- case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
- [#pending{tref=TRef,ref=Ref,caller=Caller}] ->
- cancel_request_timer(Ref,TRef),
- ct_gen_conn:return(Caller,E),
- {noreply,State#state{pending=[]}};
- _ ->
- ?error(ConnName,[{got_unexpected_msg,Other},
- {expecting,Pending}]),
- {noreply,State}
- end
+%% decode/2
+%%
+%% Decode parsed (incoming) XML.
+
+decode({Tag, _, _} = E, #state{} = State) ->
+ case decode(get_local_name_atom(Tag), E, State) of
+ #state{} = S ->
+ {noreply, S};
+ {stop, #state{}} = T ->
+ T
+ end.
+
+%% decode/3
+
+decode('rpc-reply', {_, Attrs, _} = E, State) ->
+ decode_rpc_reply(get_msg_id(Attrs), E, State);
+
+%% Incoming hello, outgoing not yet sent.
+decode(hello, E, #state{hello_status = undefined} = State) ->
+ case decode_hello(E) of
+ {ok, SessionId, Capabilities} ->
+ State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = received};
+ {error, _Reason} = No ->
+ State#state{hello_status = No}
+ end;
+
+%% Incoming hello, outgoing already sent: negotiate protocol version.
+decode(hello, E, #state{hello_status = #pending{tref = TRef,
+ caller = From}}
+ = State) ->
+ cancel_request_timer(TRef),
+ case decode_hello(E) of
+ {ok, SessionId, Capabilities} ->
+ reply(From, handle_capx(State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = received}));
+ {error, _Reason} = No ->
+ ct_gen_conn:return(From, No),
+ {stop, State#state{hello_status = No}}
+ end;
+
+%% Duplicate hello: ignore.
+decode(hello, E, #state{hello_status = Other} = State) ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName, [{got_unexpected_hello, E},
+ {hello_status, Other}]),
+ State;
+
+decode(notification, E, State) ->
+ State#state.event_receiver ! E,
+ State;
+
+decode(Other, E, State) ->
+ decode_send({got_unexpected_msg, Other}, E, State).
+
+%% reply/2
+%%
+%% Explicitly send a reply that can't be returned.
+
+reply(From, {T, Res, State}) ->
+ ct_gen_conn:return(From, Res),
+ case T of
+ reply ->
+ State;
+ stop ->
+ {T, State}
end.
+%% get_msg_id/1
+
get_msg_id(Attrs) ->
- case lists:keyfind('message-id',1,Attrs) of
- {_,Str} ->
- list_to_integer(Str);
- false ->
- undefined
+ case find('message-id', Attrs) of
+ {_,Str} ->
+ list_to_integer(Str);
+ false ->
+ undefined
end.
-decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
- case lists:keytake(MsgId,#pending.msg_id,Pending) of
- {value, #pending{tref=TRef,ref=Ref,op=Op,caller=Caller}, Pending1} ->
- cancel_request_timer(Ref,TRef),
- Content = forward_xmlns_attr(Attrs,Content0),
- {CallerReply,{ServerReply,State2}} =
- do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}),
- ct_gen_conn:return(Caller,CallerReply),
- {ServerReply,State2};
- false ->
- %% Result of send/2, when receiving a correct
- %% rpc-reply. Can handle this only if there is just one
- %% pending that matches (i.e. has undefined msg_id and op)
- case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
- [#pending{tref=TRef,
- ref=Ref,
- msg_id=undefined,
- op=undefined,
- caller=Caller}] ->
- cancel_request_timer(Ref,TRef),
- ct_gen_conn:return(Caller,E),
- {noreply,State#state{pending=[]}};
- _ ->
- ConnName = (State#state.connection)#connection.name,
- ?error(ConnName,[{got_unexpected_msg_id,MsgId},
- {expecting,Pending}]),
- {noreply,State}
- end
+%% recode_rpc_reply/3
+
+decode_rpc_reply(undefined, E, #state{pending = [#pending{msg_id = MsgId}]}
+ = State)
+ when MsgId /= undefined ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName, [{warning, rpc_reply_missing_msg_id},
+ {assuming, MsgId}]),
+ decode_rpc_reply(MsgId, E, State);
+
+decode_rpc_reply(undefined, _, State) ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName, [{error, rpc_reply_missing_msg_id}]),
+ State;
+
+decode_rpc_reply(MsgId,
+ {_, Attrs, Content0}
+ = E,
+ #state{pending = Pending}
+ = State) ->
+ case lists:keytake(MsgId, #pending.msg_id, Pending) of
+ {value, Rec, Rest} ->
+ #pending{tref = TRef, op = Op, caller = From}
+ = Rec,
+ cancel_request_timer(TRef),
+ Content = forward_xmlns_attr(Attrs, Content0),
+ {Reply, T} = do_decode_rpc_reply(Op,
+ Content,
+ State#state{pending = Rest}),
+ ct_gen_conn:return(From, Reply),
+ T;
+ false -> %% not a send_rcp or server has sent wrong id
+ decode_send({got_unexpected_msg_id, MsgId}, E, State)
+ end.
+
+%% decode_send/2
+%%
+%% Result of send/2,3. Only handle one at a time there since all
+%% pendings have msg_id = undefined.
+
+decode_send(ErrorT, Elem, #state{pending = Pending} = State) ->
+ case [P || #pending{msg_id = undefined} = P <- Pending] of
+ [Rec] ->
+ #pending{tref = TRef,
+ caller = From}
+ = Rec,
+ cancel_request_timer(TRef),
+ ct_gen_conn:return(From, Elem),
+ State#state{pending = lists:delete(Rec, Pending)};
+ _ ->
+ Conn = State#state.connection,
+ ?error(Conn#connection.name, [ErrorT, {expecting, Pending}]),
+ State
end.
-do_decode_rpc_reply(Op,Result,State)
- when Op==lock; Op==unlock; Op==edit_config; Op==delete_config;
- Op==copy_config; Op==kill_session ->
- {decode_ok(Result),{noreply,State}};
-do_decode_rpc_reply(Op,Result,State)
- when Op==get; Op==get_config; Op==action ->
- {decode_data(Result),{noreply,State}};
-do_decode_rpc_reply(close_session,Result,State) ->
+%% do_decode_rpc_reply/3
+
+do_decode_rpc_reply(Op, Result, State)
+ when Op == lock;
+ Op == unlock;
+ Op == edit_config;
+ Op == delete_config;
+ Op == copy_config;
+ Op == kill_session ->
+ {decode_ok(Result), State};
+
+do_decode_rpc_reply(Op, Result, State)
+ when Op == get;
+ Op == get_config;
+ Op == action ->
+ {decode_data(Result), State};
+
+do_decode_rpc_reply(close_session, Result, State) ->
case decode_ok(Result) of
- ok -> {ok,{stop,State}};
- Other -> {Other,{noreply,State}}
+ ok ->
+ {ok, {stop, State}};
+ Other ->
+ {Other, State}
end;
-do_decode_rpc_reply({create_subscription,Caller},Result,State) ->
+
+do_decode_rpc_reply({create_subscription, From}, Result, State) ->
case decode_ok(Result) of
- ok ->
- {ok,{noreply,State#state{event_receiver=Caller}}};
- Other ->
- {Other,{noreply,State}}
+ ok ->
+ {ok, State#state{event_receiver = From}};
+ Other ->
+ {Other, State}
end;
-do_decode_rpc_reply(get_event_streams,Result,State) ->
- {decode_streams(decode_data(Result)),{noreply,State}};
-do_decode_rpc_reply(undefined,Result,State) ->
- {Result,{noreply,State}}.
+
+do_decode_rpc_reply(get_event_streams, Result, State) ->
+ {decode_streams(decode_data(Result)), State};
+
+do_decode_rpc_reply(undefined, Result, State) ->
+ {Result, State}.
@@ -1454,7 +1704,7 @@ decode_data([{Tag,Attrs,Content}]) ->
case get_local_name_atom(Tag) of
ok ->
%% when action has return type void
- ok;
+ ok;
data ->
%% Since content of data has nothing from the netconf
%% namespace, we remove the parent's xmlns attribute here
@@ -1525,41 +1775,43 @@ get_all_xmlns_attrs([{Key,_}=Attr|Attrs],XmlnsAttrs) ->
get_all_xmlns_attrs([],XmlnsAttrs) ->
XmlnsAttrs.
-
%% Decode server hello to pick out session id and capabilities
-decode_hello({hello,_Attrs,Hello}) ->
- case lists:keyfind('session-id',1,Hello) of
- {'session-id',_,[SessionId]} ->
- case lists:keyfind(capabilities,1,Hello) of
- {capabilities,_,Capabilities} ->
- case decode_caps(Capabilities,[],false) of
- {ok,Caps} ->
- {ok,list_to_integer(SessionId),Caps};
- Error ->
- Error
- end;
- false ->
- {error,{incorrect_hello,capabilities_not_found}}
- end;
- false ->
- {error,{incorrect_hello,no_session_id_found}}
+decode_hello({hello, _Attrs, Hello}) ->
+ U = make_ref(),
+ try
+ [{'session-id', _, [SessionId]}, _ | _]
+ = [find('session-id', Hello), no_session_id_found | U],
+ [{ok, Id}, _ | _]
+ = [catch {ok, list_to_integer(SessionId)}, invalid_session_id | U],
+ [true, _ | _]
+ = [0 < Id, invalid_session_id | U],
+ [{capabilities, _, Capabilities}, _ | _]
+ = [find(capabilities, Hello), capabilities_not_found | U],
+ [{ok, Caps}, _ | _]
+ = [decode_caps(Capabilities, [], false), false | U],
+ {ok, Id, Caps}
+ catch
+ error: {badmatch, [Error, false | U]} ->
+ Error;
+ error: {badmatch, [_, Reason | U]} ->
+ {error, {incorrect_hello, Reason}}
end.
-decode_caps([{capability,[],[?NETCONF_BASE_CAP++Vsn=Cap]} |Caps], Acc, _) ->
- case Vsn of
- ?NETCONF_BASE_CAP_VSN ->
- decode_caps(Caps, [Cap|Acc], true);
- _ ->
- {error,{incompatible_base_capability_vsn,Vsn}}
- end;
-decode_caps([{capability,[],[Cap]}|Caps],Acc,Base) ->
- decode_caps(Caps,[Cap|Acc],Base);
-decode_caps([H|_T],_,_) ->
- {error,{unexpected_capability_element,H}};
-decode_caps([],_,false) ->
- {error,{incorrect_hello,no_base_capability_found}};
-decode_caps([],Acc,true) ->
- {ok,lists:reverse(Acc)}.
+find(Key, List) ->
+ lists:keyfind(Key, 1, List).
+
+decode_caps([{capability, [], [?NETCONF_BASE_CAP ++ _ = Cap]} | Caps],
+ Acc,
+ _) ->
+ decode_caps(Caps, [Cap|Acc], true);
+decode_caps([{capability, [], [Cap]} | Caps], Acc, Base) ->
+ decode_caps(Caps, [Cap|Acc], Base);
+decode_caps([H|_], _, _) ->
+ {error, {unexpected_capability_element, H}};
+decode_caps([], _, false) ->
+ {error, {incorrect_hello, no_base_capability_found}};
+decode_caps([], Acc, true) ->
+ {ok, lists:reverse(Acc)}.
%% Return a list of {Name,Data}, where data is a {Tag,Value} list for each stream
@@ -1570,7 +1822,7 @@ decode_streams({ok,[{netconf,_,Streams}]}) ->
decode_streams([{streams,_,Streams}]) ->
decode_streams(Streams);
decode_streams([{stream,_,Stream} | Streams]) ->
- {name,_,[Name]} = lists:keyfind(name,1,Stream),
+ {name,_,[Name]} = find(name, Stream),
[{Name,[{Tag,Value} || {Tag,_,[Value]} <- Stream, Tag /= name]}
| decode_streams(Streams)];
decode_streams([]) ->
@@ -1814,6 +2066,190 @@ ssh_close(Connection=#connection{reference = CM}) ->
log(Connection,disconnect),
ok.
+%% ===========================================================================
+
+%% recv/1
+%%
+%% Extract incoming messages using either NETCONF 1.0 framing or
+%% NETCONF 1.1 chunking.
+
+recv(Bin, true) ->
+ recv(Bin, [<<>>, 3]);
+recv(Bin, false) ->
+ recv(Bin, <<>>);
+
+recv(Bin, [Head, Len | Chunks]) -> %% 1.1 chunking
+ chunk(<<Head/binary, Bin/binary>>, Chunks, Len);
+
+%% Start looking for the terminating end-of-message sequence ]]>]]>
+%% 5 characters from the end of the buffered head, since this binary
+%% has already been scanned.
+recv(Bin, Head) when is_binary(Head) -> %% 1.0 framing
+ frame(<<Head/binary, Bin/binary>>, max(0, size(Head) - 5)).
+
+%% frame/2
+%%
+%% Extract a message terminated by the ]]>]]> end-of-message sequence.
+%% Don't need to extract characters as UTF-8 since matching byte-wise
+%% is unambiguous: the high-order bit of every byte of a multi-byte
+%% UTF character is 1, while the end-of-message sequence is ASCII.
+
+frame(Bin, Start) ->
+ Sz = size(Bin),
+ Scope = {Start, Sz - Start},
+ case binary:match(Bin, pattern(), [{scope, Scope}]) of
+ {Len, 6} ->
+ <<Msg:Len/binary, _:6/binary, Rest/binary>> = Bin,
+ {trim(Msg), Rest};
+ nomatch ->
+ Bin
+ end.
+
+%% pattern/0
+
+pattern() ->
+ Key = ?KEY(pattern),
+ case get(Key) of
+ undefined ->
+ CP = binary:compile_pattern(<<"]]>]]>">>),
+ put(Key, CP),
+ CP;
+ CP ->
+ CP
+ end.
+
+%% trim/1
+%%
+%% Whitespace before an XML declaration is an error, but be somewhat
+%% lenient and strip line breaks since the RFC's are unclear on what's
+%% allowed following a ]]>]]> delimiter. Typical seems to be a single
+%% $\n, but strip any of " \t\r\n", and regardless of NETCONF version.
+
+trim(<<C, Bin/binary>>)
+ when C == $\n;
+ C == $\r;
+ C == $\t;
+ C == $ ->
+ trim(Bin);
+
+trim(Bin) ->
+ Bin.
+
+%% chunk/3
+%%
+%% The final argument is either 0 to indicate that a specified number
+%% of bytes of chunk data should be consumed, or at least 3 to
+%% indicate an offset at which to look for a newline following a chunk
+%% size.
+
+%% Accumulating chunk-data ...
+chunk(Bin, [Sz | Chunks] = L, 0) ->
+ case Bin of
+ <<Chunk:Sz/binary, Rest/binary>> ->
+ chunk(Rest, acc(Chunk, Chunks), 3); %% complete chunk ...
+ _ ->
+ [Bin, 0 | L] %% ... or not
+ end;
+
+%% ... or a header.
+
+chunk(Bin, Chunks, Len)
+ when size(Bin) < 4 ->
+ [Bin, 3 = Len | Chunks];
+
+%% End of chunks.
+chunk(<<"\n##\n", Rest/binary>>, Chunks, _) ->
+ case Chunks of
+ [] ->
+ {error, "end-of-chunks unexpected"}; %% must be at least one
+ Bins ->
+ {lists:reverse(Bins), Rest}
+ end;
+
+%% Matching each of the 10 newline possibilities is faster than
+%% searching.
+chunk(<<"\n#", Head:1/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:2/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:3/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:4/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:5/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:6/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:7/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:8/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:9/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:10/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+
+chunk(<<"\n#", Bin:11/binary, _/binary>>, _, _) ->
+ {error, {"chunk-size too long", Bin}}; %% 32-bits = max 10 digits
+
+chunk(<<"\n#", _/binary>> = Bin, Chunks, _) ->
+ [Bin, size(Bin) | Chunks];
+
+chunk(Bin, Chunks, 3 = Len) ->
+ case drop(Bin) of
+ <<>> ->
+ [Bin, Len | Chunks];
+ <<"\n#", _/binary>> = B ->
+ chunk(B, Chunks, Len);
+ _ ->
+ {error, {"not a chunk", Bin}}
+ end.
+
+%% drop/1
+
+drop(<<"\n#", _/binary>> = Bin) ->
+ Bin;
+
+drop(<<C, Bin/binary>>)
+ when C == $\n;
+ C == $\r;
+ C == $\t;
+ C == $ ->
+ drop(Bin);
+
+drop(Bin) ->
+ Bin.
+
+%% acc/2
+
+acc(Chunk, []) ->
+ [B || B <- [trim(Chunk)], <<>> /= B];
+
+acc(Chunk, Chunks) ->
+ [Chunk | Chunks].
+
+%% acc/3
+
+acc(Head, Rest, Chunks) ->
+ case chunk_size(Head) of
+ {error, _Reason} = No ->
+ No;
+ Sz ->
+ chunk(Rest, [Sz | Chunks], 0)
+ end.
+
+%% chunk_size/1
+
+chunk_size(<<C, _/binary>> = Bin) ->
+ try true = $0 < C, binary_to_integer(Bin) of
+ Sz when 0 < Sz bsr 32 ->
+ {error, {"chunk-size too large", Sz}};
+ Sz ->
+ Sz
+ catch
+ error: _ ->
+ {error, {"chunk-size invalid", Bin}}
+ end.
%%----------------------------------------------------------------------
%% END OF MODULE
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 960252a6fe..b028078332 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -27,13 +27,8 @@
-export([install/1,install/2,run/1,run/2,run/3,run_test/1,
run_testspec/1,step/3,step/4,refresh_logs/1]).
-
-%% Exported for VTS
--export([run_make/3,do_run/4,tests/1,tests/2,tests/3]).
-
-
-%% Misc internal functions
--export([variables_file_name/1,script_start1/2,run_test2/1]).
+%% Misc internal API functions
+-export([variables_file_name/1,script_start1/2,run_test2/1, run_make/3]).
-include("ct.hrl").
-include("ct_event.hrl").
@@ -51,7 +46,6 @@
-record(opts, {label,
profile,
- vts,
shell,
cover,
cover_stop,
@@ -212,25 +206,19 @@ finish(Tracing, ExitStatus, Args) ->
if ExitStatus == interactive_mode ->
interactive_mode;
true ->
- case get_start_opt(vts, true, Args) of
- true ->
- %% VTS mode, don't halt the node
- ok;
- _ ->
- %% it's possible to tell CT to finish execution with a call
- %% to a different function than the normal halt/1 BIF
- %% (meant to be used mainly for reading the CT exit status)
- case get_start_opt(halt_with,
- fun([HaltMod,HaltFunc]) ->
- {list_to_atom(HaltMod),
- list_to_atom(HaltFunc)} end,
- Args) of
- undefined ->
- halt(ExitStatus);
- {M,F} ->
- apply(M, F, [ExitStatus])
- end
- end
+ %% it's possible to tell CT to finish execution with a call
+ %% to a different function than the normal halt/1 BIF
+ %% (meant to be used mainly for reading the CT exit status)
+ case get_start_opt(halt_with,
+ fun([HaltMod,HaltFunc]) ->
+ {list_to_atom(HaltMod),
+ list_to_atom(HaltFunc)} end,
+ Args) of
+ undefined ->
+ halt(ExitStatus);
+ {M,F} ->
+ apply(M, F, [ExitStatus])
+ end
end.
script_start1(Parent, Args) ->
@@ -239,7 +227,6 @@ script_start1(Parent, Args) ->
%% read general start flags
Label = get_start_opt(label, fun([Lbl]) -> Lbl end, Args),
Profile = get_start_opt(profile, fun([Prof]) -> Prof end, Args),
- Vts = get_start_opt(vts, true, undefined, Args),
Shell = get_start_opt(shell, true, Args),
Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args),
CoverStop = get_start_opt(cover_stop,
@@ -325,8 +312,8 @@ script_start1(Parent, Args) ->
Stylesheet = get_start_opt(stylesheet,
fun([SS]) -> ?abs(SS) end, Args),
%% basic_html - used by ct_logs
- BasicHtml = case {Vts,proplists:get_value(basic_html, Args)} of
- {undefined,undefined} ->
+ BasicHtml = case proplists:get_value(basic_html, Args) of
+ undefined ->
application:set_env(common_test, basic_html, false),
undefined;
_ ->
@@ -357,7 +344,7 @@ script_start1(Parent, Args) ->
application:set_env(common_test, keep_logs, KeepLogs),
Opts = #opts{label = Label, profile = Profile,
- vts = Vts, shell = Shell,
+ shell = Shell,
cover = Cover, cover_stop = CoverStop,
logdir = LogDir, logopts = LogOpts,
basic_html = BasicHtml,
@@ -415,8 +402,7 @@ run_or_refresh(Opts = #opts{logdir = LogDir}, Args) ->
end
end.
-script_start2(Opts = #opts{vts = undefined,
- shell = undefined}, Args) ->
+script_start2(Opts = #opts{shell = undefined}, Args) ->
case proplists:get_value(spec, Args) of
Specs when Specs =/= [], Specs =/= undefined ->
Specs1 = get_start_opt(join_specs, [Specs], Specs, Args),
@@ -702,7 +688,7 @@ script_start3(Opts, Args) ->
{error,incorrect_start_options};
{undefined,undefined,_} ->
- if Opts#opts.vts ; Opts#opts.shell ->
+ if Opts#opts.shell ->
script_start4(Opts#opts{tests = []}, Args);
true ->
%% no start options, use default "-dir ./"
@@ -712,20 +698,6 @@ script_start3(Opts, Args) ->
end
end.
-script_start4(#opts{vts = true, config = Config, event_handlers = EvHandlers,
- tests = Tests, logdir = LogDir, logopts = LogOpts}, _Args) ->
- ConfigFiles =
- lists:foldl(fun({ct_config_plain,CfgFiles}, AllFiles) when
- is_list(hd(CfgFiles)) ->
- AllFiles ++ CfgFiles;
- ({ct_config_plain,CfgFile}, AllFiles) when
- is_integer(hd(CfgFile)) ->
- AllFiles ++ [CfgFile];
- (_, AllFiles) ->
- AllFiles
- end, [], Config),
- vts:init_data(ConfigFiles, EvHandlers, ?abs(LogDir), LogOpts, Tests);
-
script_start4(#opts{label = Label, profile = Profile,
shell = true, config = Config,
event_handlers = EvHandlers,
@@ -759,27 +731,6 @@ script_start4(#opts{label = Label, profile = Profile,
Error ->
Error
end;
-
-script_start4(#opts{vts = true, cover = Cover}, _) ->
- case Cover of
- undefined ->
- script_usage();
- _ ->
- %% Add support later (maybe).
- io:format("\nCan't run cover in vts mode.\n\n", [])
- end,
- {error,no_cover_in_vts_mode};
-
-script_start4(#opts{shell = true, cover = Cover}, _) ->
- case Cover of
- undefined ->
- script_usage();
- _ ->
- %% Add support later (maybe).
- io:format("\nCan't run cover in interactive mode.\n\n", [])
- end,
- {error,no_cover_in_interactive_mode};
-
script_start4(Opts = #opts{tests = Tests}, Args) ->
do_run(Tests, [], Opts, Args).
@@ -850,7 +801,6 @@ script_usage() ->
"\n\t [-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t [-decrypt_key Key] | [-decrypt_file KeyFile]\n\n"),
io:format("Run tests in web based GUI:\n\n"
- "\tct_run -vts [-browser Browser]"
"\n\t [-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t [-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t [-dir TestDir1 TestDir2 .. TestDirN] |"
@@ -2674,7 +2624,6 @@ get_name(Dir) ->
TopDir ++ "." ++ Base
end.
-
run_make(TestDir, Mod, UserInclude) ->
run_make(suites, TestDir, Mod, UserInclude, [nowarn_export_all]).
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 9f489e9bfb..470a938c53 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -156,12 +156,7 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
{error,{already_started,_}} ->
ok;
_ ->
- case whereis(vts) of
- undefined ->
- ct_event:add_handler();
- VtsPid ->
- ct_event:add_handler([{vts,VtsPid}])
- end
+ ct_event:add_handler()
end,
%% start ct_config server
diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl
deleted file mode 100644
index 32d4255217..0000000000
--- a/lib/common_test/src/ct_webtool.erl
+++ /dev/null
@@ -1,1214 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(ct_webtool).
--behaviour(gen_server).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% The general idea is: %%
-%% %%
-%% %%
-%% 1. Scan through the path for *.tool files and find all the web %%
-%% based tools. Query each tool for configuration data. %%
-%% 2. Add Alias for Erlscript and html for each tool to %%
-%% the webserver configuration data. %%
-%% 3. Start the webserver. %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% API functions
--export([start/0, start/2, stop/0]).
-
-%% Starting Webtool from a shell script
--export([script_start/0, script_start/1]).
-
-%% Web api
--export([started_tools/2, toolbar/2, start_tools/2, stop_tools/2]).
-
-%% API against other tools
--export([is_localhost/0]).
-
-%% Debug export s
--export([get_tools1/1]).
--export([debug/1, stop_debug/0, debug_app/1]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--include_lib("kernel/include/file.hrl").
--include_lib("stdlib/include/ms_transform.hrl").
-
--record(state,{priv_dir,app_data,supvis,web_data,started=[]}).
-
--define(MAX_NUMBER_OF_WEBTOOLS,256).
--define(DEFAULT_PORT,8888).% must be >1024 or the user must be root on unix
--define(DEFAULT_ADDR,{127,0,0,1}).
-
--define(WEBTOOL_ALIAS,{ct_webtool,[{alias,{erl_alias,"/ct_webtool",[ct_webtool]}}]}).
--define(HEADER,"Pragma:no-cache\r\n Content-type: text/html\r\n\r\n").
--define(HTML_HEADER,"<HTML>\r\n<HEAD>\r\n<TITLE>WebTool</TITLE>\r\n</HEAD>\r\n<BODY BGCOLOR=\"#FFFFFF\">\r\n").
--define(HTML_HEADER_RELOAD,"<HTML>\r\n<HEAD>\r\n<TITLE>WebTool
- </TITLE>\r\n</HEAD>\r\n
- <BODY BGCOLOR=\"#FFFFFF\" onLoad=reloadCompiledList()>\r\n").
-
--define(HTML_END,"</BODY></HTML>").
-
--define(SEND_URL_TIMEOUT,5000).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% For debugging only. %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Start tracing with
-%% debug(Functions).
-%% Functions = local | global | FunctionList
-%% FunctionList = [Function]
-%% Function = {FunctionName,Arity} | FunctionName |
-%% {Module, FunctionName, Arity} | {Module,FunctionName}
-debug(F) ->
- {ok, _} = ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes
- {ok, _} = ttb:p(all,[call,timestamp]),
- MS = [{'_',[],[{return_trace},{message,{caller}}]}],
- _ = tp(F,MS),
- {ok, _} = ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func
- ok.
-tp(local,MS) -> % all functions
- ttb:tpl(?MODULE,MS);
-tp(global,MS) -> % all exported functions
- ttb:tp(?MODULE,MS);
-tp([{M,F,A}|T],MS) -> % Other module
- {ok, _} = ttb:tpl(M,F,A,MS),
- tp(T,MS);
-tp([{M,F}|T],MS) when is_atom(F) -> % Other module
- {ok, _} = ttb:tpl(M,F,MS),
- tp(T,MS);
-tp([{F,A}|T],MS) -> % function/arity
- {ok, _} = ttb:tpl(?MODULE,F,A,MS),
- tp(T,MS);
-tp([F|T],MS) -> % function
- {ok, _} = ttb:tpl(?MODULE,F,MS),
- tp(T,MS);
-tp([],_MS) ->
- ok.
-stop_debug() ->
- ttb:stop([format]).
-
-debug_app(Mod) ->
- {ok, _} = ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]),
- {ok, _} = ttb:p(all,[call,timestamp]),
- MS = [{'_',[],[{return_trace},{message,{caller}}]}],
- {ok, _} = ttb:tp(Mod,MS),
- ok.
-
-out(_,{trace_ts,Pid,call,MFA={M,F,A},{W,_,_},TS},_,S)
- when W==webtool;W==mod_esi->
- io:format("~w: (~p)~ncall ~ts~n", [TS,Pid,ffunc(MFA)]),
- [{M,F,length(A)}|S];
-out(_,{trace_ts,Pid,return_from,MFA,R,TS},_,[MFA|S]) ->
- io:format("~w: (~p)~nreturned from ~ts -> ~tp~n", [TS,Pid,ffunc(MFA),R]),
- S;
-out(_,_,_,_) ->
- ok.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Functions called via script. %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-script_start() ->
- usage(),
- halt().
-script_start([App]) ->
- DefaultBrowser =
- case os:type() of
- {win32,_} -> iexplore;
- _ -> firefox
- end,
- script_start([App,DefaultBrowser]);
-script_start([App,Browser]) ->
- io:format("Starting webtool...\n"),
- {ok, _} = start(),
- AvailableApps = get_applications(),
- {OSType,_} = os:type(),
- case lists:keysearch(App,1,AvailableApps) of
- {value,{App,StartPage}} ->
- io:format("Starting ~w...\n",[App]),
- start_tools([],"app=" ++ atom_to_list(App)),
- PortStr = integer_to_list(get_port()),
- Url = case StartPage of
- "/" ++ Page ->
- "http://localhost:" ++ PortStr ++ "/" ++ Page;
- _ ->
- "http://localhost:" ++ PortStr ++ "/" ++ StartPage
- end,
- _ = case Browser of
- none ->
- ok;
- iexplore when OSType == win32->
- io:format("Starting internet explorer...\n"),
- {ok,R} = win32reg:open(""),
- Key="\\local_machine\\SOFTWARE\\Microsoft\\IE Setup\\Setup",
- ok = win32reg:change_key(R,Key),
- {ok,Val} = win32reg:value(R,"Path"),
- IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"),
- os:cmd("\"" ++ IExplore ++ "\" " ++ Url);
- _ when OSType == win32 ->
- io:format("Starting ~tw...\n",[Browser]),
- os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url);
- B when B==firefox; B==mozilla ->
- io:format("Sending URL to ~w...",[Browser]),
- BStr = atom_to_list(Browser),
- SendCmd = BStr ++ " -raise -remote \'openUrl(" ++
- Url ++ ")\'",
- Port = open_port({spawn,SendCmd},[exit_status]),
- receive
- {Port,{exit_status,0}} ->
- io:format("done\n"),
- ok;
- {Port,{exit_status,_Error}} ->
- io:format(" not running, starting ~w...\n",
- [Browser]),
- _ = os:cmd(BStr ++ " " ++ Url),
- ok
- after ?SEND_URL_TIMEOUT ->
- io:format(" failed, starting ~w...\n",[Browser]),
- erlang:port_close(Port),
- os:cmd(BStr ++ " " ++ Url)
- end;
- _ ->
- io:format("Starting ~tw...\n",[Browser]),
- os:cmd(atom_to_list(Browser) ++ " " ++ Url)
- end,
- ok;
- false ->
- stop(),
- io:format("\n{error,{unknown_app,~p}}\n",[App]),
- halt()
- end.
-
-usage() ->
- io:format("Starting webtool...\n"),
- {ok, _} = start(),
- Apps = lists:map(fun({A,_}) -> A end,get_applications()),
- io:format(
- "\nUsage: start_webtool application [ browser ]\n"
- "\nAvailable applications are: ~p\n"
- "Default browser is \'iexplore\' (Internet Explorer) on Windows "
- "or else \'firefox\'\n",
- [Apps]),
- stop().
-
-
-get_applications() ->
- gen_server:call(ct_web_tool,get_applications).
-
-get_port() ->
- gen_server:call(ct_web_tool,get_port).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Api functions to the genserver. %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%
-%----------------------------------------------------------------------
-
-start()->
- start(standard_path,standard_data).
-
-start(Path,standard_data)->
- case get_standard_data() of
- {error,Reason} ->
- {error,Reason};
- Data ->
- start(Path,Data)
- end;
-
-start(standard_path,Data)->
- Path=get_path(),
- start(Path,Data);
-
-start(Path,Port) when is_integer(Port)->
- Data = get_standard_data(Port),
- start(Path,Data);
-
-start(Path,Data0)->
- Data = Data0 ++ rest_of_standard_data(),
- case gen_server:start({local,ct_web_tool},ct_webtool,{Path,Data},[]) of
- {error, {already_started, Pid}} ->
- {ok, Pid};
- Else ->
- Else
- end.
-
-stop()->
- gen_server:call(ct_web_tool,stoppit).
-
-%----------------------------------------------------------------------
-%Web Api functions called by the web
-%----------------------------------------------------------------------
-started_tools(Env,Input)->
- gen_server:call(ct_web_tool,{started_tools,Env,Input}).
-
-toolbar(Env,Input)->
- gen_server:call(ct_web_tool,{toolbar,Env,Input}).
-
-start_tools(Env,Input)->
- gen_server:call(ct_web_tool,{start_tools,Env,Input}).
-
-stop_tools(Env,Input)->
- gen_server:call(ct_web_tool,{stop_tools,Env,Input}).
-%----------------------------------------------------------------------
-%Support API for other tools
-%----------------------------------------------------------------------
-
-is_localhost()->
- gen_server:call(ct_web_tool,is_localhost).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%%The gen_server callback functions that builds the webbpages %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_call(get_applications,_,State)->
- MS = ets:fun2ms(fun({Tool,{web_data,{_,Start}}}) -> {Tool,Start} end),
- Tools = ets:select(State#state.app_data,MS),
- {reply,Tools,State};
-
-handle_call(get_port,_,State)->
- {value,{port,Port}}=lists:keysearch(port,1,State#state.web_data),
- {reply,Port,State};
-
-handle_call({started_tools,_Env,_Input},_,State)->
- {reply,started_tools_page(State),State};
-
-handle_call({toolbar,_Env,_Input},_,State)->
- {reply,toolbar(),State};
-
-handle_call({start_tools,Env,Input},_,State)->
- {NewState,Page}=start_tools_page(Env,Input,State),
- {reply,Page,NewState};
-
-handle_call({stop_tools,Env,Input},_,State)->
- {NewState,Page}=stop_tools_page(Env,Input,State),
- {reply,Page,NewState};
-
-handle_call(stoppit,_From,Data)->
- {stop,normal,ok,Data};
-
-handle_call(is_localhost,_From,Data)->
- Result=case proplists:get_value(bind_address, Data#state.web_data) of
- ?DEFAULT_ADDR ->
- true;
- _IpNumber ->
- false
- end,
- {reply,Result,Data}.
-
-
-handle_info(_Message,State)->
- {noreply,State}.
-
-handle_cast(_Request,State)->
- {noreply,State}.
-
-code_change(_,State,_)->
- {ok,State}.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% The other functions needed by the gen_server behaviour
-%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-% Start the gen_server
-%----------------------------------------------------------------------
-init({Path,Config})->
- ct_util:mark_process(),
- case filelib:is_dir(Path) of
- true ->
- {ok, Table} = get_tool_files_data(),
- insert_app(?WEBTOOL_ALIAS, Table),
- case ct_webtool_sup:start_link() of
- {ok, Pid} ->
- case start_webserver(Table, Path, Config) of
- {ok, _} ->
- print_url(Config),
- {ok,#state{priv_dir=Path,
- app_data=Table,
- supvis=Pid,
- web_data=Config}};
- {error, Error} ->
- {stop, {error, Error}}
- end;
- Error ->
- {stop,Error}
- end;
- false ->
- {stop, {error, error_dir}}
- end.
-
-terminate(_Reason,Data)->
- %%shut down the webbserver
- shutdown_server(Data),
- %%Shutdown the different tools that are started with application:start
- shutdown_apps(Data),
- %%Shutdown the supervisor and its children will die
- shutdown_supervisor(Data),
- ok.
-
-print_url(ConfigData)->
- Server=proplists:get_value(server_name,ConfigData,"undefined"),
- Port=proplists:get_value(port,ConfigData,"undefined"),
- {A,B,C,D}=proplists:get_value(bind_address,ConfigData,"undefined"),
- io:format("WebTool is available at http://~ts:~w/~n",[Server,Port]),
- io:format("Or http://~w.~w.~w.~w:~w/~n",[A,B,C,D,Port]).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% begin build the pages
-%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-%The page that shows the started tools
-%----------------------------------------------------------------------
-started_tools_page(State)->
- [?HEADER,?HTML_HEADER,started_tools(State),?HTML_END].
-
-toolbar()->
- [?HEADER,?HTML_HEADER,toolbar_page(),?HTML_END].
-
-
-start_tools_page(_Env,Input,State)->
- %%io:format("~n======= ~n ~p ~n============~n",[Input]),
- case get_tools(Input) of
- {tools,Tools}->
- %%io:format("~n======= ~n ~p ~n============~n",[Tools]),
- {ok,NewState}=handle_apps(Tools,State,start),
- {NewState,[?HEADER,?HTML_HEADER_RELOAD,reload_started_apps(),
- show_unstarted_apps(NewState),?HTML_END]};
- _ ->
- {State,[?HEADER,?HTML_HEADER,show_unstarted_apps(State),?HTML_END]}
- end.
-
-stop_tools_page(_Env,Input,State)->
- case get_tools(Input) of
- {tools,Tools}->
- {ok,NewState}=handle_apps(Tools,State,stop),
- {NewState,[?HEADER,?HTML_HEADER_RELOAD,reload_started_apps(),
- show_started_apps(NewState),?HTML_END]};
- _ ->
- {State,[?HEADER,?HTML_HEADER,show_started_apps(State),?HTML_END]}
- end.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% Functions that start and config the webserver
-%% 1. Collect the config data
-%% 2. Start webserver
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% Start the webserver
-%----------------------------------------------------------------------
-start_webserver(Data,Path,Config)->
- case get_conf_data(Data,Path,Config) of
- {ok,Conf_data}->
- %%io:format("Conf_data: ~p~n",[Conf_data]),
- start_server(Conf_data);
- {error,Error} ->
- {error,{error_server_conf_file,Error}}
- end.
-
-start_server(Conf_data)->
- case inets:start(httpd, Conf_data, stand_alone) of
- {ok,Pid}->
- {ok,Pid};
- Error->
- {error,{server_error,Error}}
- end.
-
-%----------------------------------------------------------------------
-% Create config data for the webserver
-%----------------------------------------------------------------------
-get_conf_data(Data,Path,Config)->
- Aliases=get_aliases(Data),
- ServerRoot = filename:join([Path,"root"]),
- MimeTypesFile = filename:join([ServerRoot,"conf","mime.types"]),
- case httpd_conf:load_mime_types(MimeTypesFile) of
- {ok,MimeTypes} ->
- Config1 = Config ++ Aliases,
- Config2 = [{server_root,ServerRoot},
- {document_root,filename:join([Path,"root/doc"])},
- {mime_types,MimeTypes} |
- Config1],
- {ok,Config2};
- Error ->
- Error
- end.
-
-%----------------------------------------------------------------------
-% Control the path for *.tools files
-%----------------------------------------------------------------------
-get_tool_files_data()->
- Tools=get_tools1(code:get_path()),
- %%io:format("Data : ~p ~n",[Tools]),
- get_file_content(Tools).
-
-%----------------------------------------------------------------------
-%Control that the data in the file really is erlang terms
-%----------------------------------------------------------------------
-get_file_content(Tools)->
- Get_data=fun({tool,ToolData}) ->
- %%io:format("Data : ~p ~n",[ToolData]),
- case proplists:get_value(config_func,ToolData) of
- {M,F,A}->
- case catch apply(M,F,A) of
- {'EXIT',_} ->
- bad_data;
- Data when is_tuple(Data) ->
- Data;
- _->
- bad_data
- end;
- _ ->
- bad_data
- end
- end,
- insert_file_content([X ||X<-lists:map(Get_data,Tools),X/=bad_data]).
-
-%----------------------------------------------------------------------
-%Insert the data from the file in to the ets:table
-%----------------------------------------------------------------------
-insert_file_content(Content)->
- Table=ets:new(app_data,[bag]),
- lists:foreach(fun(X)->
- insert_app(X,Table)
- end,Content),
- {ok,Table}.
-
-%----------------------------------------------------------------------
-%Control that we got a a tuple of a atom and a list if so add the
-%elements in the list to the ets:table
-%----------------------------------------------------------------------
-insert_app({Name,Key_val_list},Table) when is_list(Key_val_list),is_atom(Name)->
- %%io:format("ToolData: ~p: ~p~n",[Name,Key_val_list]),
- lists:foreach(
- fun({alias,{erl_alias,Alias,Mods}}) ->
- Key_val = {erl_script_alias,{Alias,Mods}},
- %%io:format("Insert: ~p~n",[Key_val]),
- ets:insert(Table,{Name,Key_val});
- (Key_val_pair)->
- %%io:format("Insert: ~p~n",[Key_val_pair]),
- ets:insert(Table,{Name,Key_val_pair})
- end,
- Key_val_list);
-
-insert_app(_,_)->
- ok.
-
-%----------------------------------------------------------------------
-% Select all the alias in the database
-%----------------------------------------------------------------------
-get_aliases(Data)->
- MS = ets:fun2ms(fun({_,{erl_script_alias,Alias}}) ->
- {erl_script_alias,Alias};
- ({_,{alias,Alias}}) ->
- {alias,Alias}
- end),
- ets:select(Data,MS).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Helper functions %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-get_standard_data(Port)->
- [
- {port,Port},
- {bind_address,?DEFAULT_ADDR},
- {server_name,"localhost"}
- ].
-
-get_standard_data()->
- case get_free_port(?DEFAULT_PORT,?MAX_NUMBER_OF_WEBTOOLS) of
- {error,Reason} -> {error,Reason};
- Port ->
- [
- {port,Port},
- {bind_address,?DEFAULT_ADDR},
- {server_name,"localhost"}
- ]
- end.
-
-get_free_port(_Port,0) ->
- {error,no_free_port_found};
-get_free_port(Port,N) ->
- case gen_tcp:connect("localhost",Port,[]) of
- {error, _Reason} ->
- Port;
- {ok,Sock} ->
- gen_tcp:close(Sock),
- get_free_port(Port+1,N-1)
- end.
-
-rest_of_standard_data() ->
- [
- %% Do not allow the server to be crashed by malformed http-request
- {max_header_siz,1024},
- {max_header_action,reply414},
- %% Go on a straight ip-socket
- {com_type,ip_comm},
- %% Do not change the order of these module names!!
- {modules,[mod_alias,
- mod_auth,
- mod_esi,
- mod_actions,
- mod_cgi,
- mod_include,
- mod_dir,
- mod_get,
- mod_head,
- mod_log,
- mod_disk_log]},
- {directory_index,["index.html"]},
- {default_type,"text/plain"}
- ].
-
-
-get_path()->
- code:priv_dir(webtool).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% These functions is used to shutdown the webserver
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% Shut down the webbserver
-%----------------------------------------------------------------------
-shutdown_server(State)->
- {Addr,Port} = get_addr_and_port(State#state.web_data),
- inets:stop(httpd,{Addr,Port}).
-
-get_addr_and_port(Config) ->
- Addr = proplists:get_value(bind_address,Config,?DEFAULT_ADDR),
- Port = proplists:get_value(port,Config,?DEFAULT_PORT),
- {Addr,Port}.
-
-%----------------------------------------------------------------------
-% Select all apps in the table and close them
-%----------------------------------------------------------------------
-shutdown_apps(State)->
- Data=State#state.app_data,
- MS = ets:fun2ms(fun({_,{start,HowToStart}}) -> HowToStart end),
- lists:foreach(fun(Start_app)->
- stop_app(Start_app)
- end,
- ets:select(Data,MS)).
-
-%----------------------------------------------------------------------
-%Shuts down the supervisor that supervises tools that is not
-%Designed as applications
-%----------------------------------------------------------------------
-shutdown_supervisor(State)->
- %io:format("~n==================~n"),
- ct_webtool_sup:stop(State#state.supvis).
- %io:format("~n==================~n").
-
-%----------------------------------------------------------------------
-%close the individual apps.
-%----------------------------------------------------------------------
-stop_app({child,_Real_name})->
- ok;
-
-stop_app({app,Real_name})->
- application:stop(Real_name);
-
-stop_app({func,_Start,Stop})->
- case Stop of
- {M,F,A} ->
- catch apply(M,F,A);
- _NoStop ->
- ok
- end.
-
-
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% These functions creates the webpage where the user can select if
-%% to start apps or to stop apps
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-toolbar_page()->
- "<TABLE>
- <TR>
- <TD>
- <B>Select Action</B>
- </TD>
- </TR>
- <TR>
- <TD>
- <A HREF=\"./start_tools\" TARGET=right> Start Tools</A>
- </TD>
- </TR>
- <TR>
- <TD>
- <A HREF=\"./stop_tools\" TARGET=right> Stop Tools</A>
- </TD>
- </TR>
- </TABLE>".
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% These functions creates the webbpage that shows the started apps
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% started_tools(State)->String (html table)
-% State is a record of type state
-%----------------------------------------------------------------------
-started_tools(State)->
- Names=get_started_apps(State#state.app_data,State#state.started),
- "<TABLE BORDER=1 WIDTH=100%>
- "++ make_rows(Names,[],0) ++"
- </TABLE>".
-%----------------------------------------------------------------------
-%get_started_apps(Data,Started)-> [{web_name,link}]
-%selects the started apps from the ets table of apps.
-%----------------------------------------------------------------------
-
-get_started_apps(Data,Started)->
- SelectData=fun({Name,Link}) ->
- {Name,Link}
- end,
- MS = lists:map(fun(A) -> {{A,{web_data,'$1'}},[],['$1']} end,Started),
-
- [{"WebTool","/tool_management.html"} |
- [SelectData(X) || X <- ets:select(Data,MS)]].
-
-%----------------------------------------------------------------------
-% make_rows(List,Result,Fields)-> String (The rows of a htmltable
-% List a list of tupler discibed above
-% Result an accumulator for the result
-% Field, counter that counts the number of cols in each row.
-%----------------------------------------------------------------------
-make_rows([],Result,Fields)->
- Result ++ fill_out(Fields);
-make_rows([Data|Paths],Result,Field)when Field==0->
- make_rows(Paths,Result ++ "<TR>" ++ make_field(Data),Field+1);
-
-make_rows([Path|Paths],Result,Field)when Field==4->
- make_rows(Paths,Result ++ make_field(Path) ++ "</TR>",0);
-
-make_rows([Path|Paths],Result,Field)->
- make_rows(Paths,Result ++ make_field(Path),Field+1).
-
-%----------------------------------------------------------------------
-% make_fields(Path)-> String that is a field i a html table
-% Path is a name url tuple {Name,url}
-%----------------------------------------------------------------------
-make_field(Path)->
- "<TD WIDTH=20%>" ++ get_name(Path) ++ "</TD>".
-
-
-%----------------------------------------------------------------------
-%get_name({Nae,Url})->String that represents a <A> tag in html.
-%----------------------------------------------------------------------
-get_name({Name,Url})->
- "<A HREF=\"" ++ Url ++ "\" TARGET=app_frame>" ++ Name ++ "</A>".
-
-
-%----------------------------------------------------------------------
-% fill_out(Nr)-> String, that represent Nr fields in a html-table.
-%----------------------------------------------------------------------
-fill_out(Nr)when Nr==0->
- [];
-fill_out(Nr)when Nr==4->
- "<TD WIDTH=\"20%\" >&nbsp</TD></TR>";
-
-fill_out(Nr)->
- "<TD WIDTH=\"20%\">&nbsp</TD>" ++ fill_out(Nr+1).
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%%These functions starts applicatons and builds the page showing tools
-%%to start
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%Controls whether the user selected a tool to start
-%----------------------------------------------------------------------
-get_tools(Input)->
- case uri_string:dissect_query(Input) of
- []->
- no_tools;
- Tools->
- FormatData=fun({_Name,Data}) -> list_to_atom(Data) end,
- SelectData=
- fun({Name,_Data}) -> string:equal(Name,"app") end,
- {tools,[FormatData(X)||X<-Tools,SelectData(X)]}
- end.
-
-%----------------------------------------------------------------------
-% Selects the data to start the applications the user has ordered
-% starting of
-%----------------------------------------------------------------------
-handle_apps([],State,_Cmd)->
- {ok,State};
-
-handle_apps([Tool|Tools],State,Cmd)->
- case ets:match_object(State#state.app_data,{Tool,{start,'_'}}) of
- []->
- Started = case Cmd of
- start ->
- [Tool|State#state.started];
- stop ->
- lists:delete(Tool,State#state.started)
- end,
- {ok,#state{priv_dir=State#state.priv_dir,
- app_data=State#state.app_data,
- supvis=State#state.supvis,
- web_data=State#state.web_data,
- started=Started}};
- ToStart ->
- case handle_apps2(ToStart,State,Cmd) of
- {ok,NewState}->
- handle_apps(Tools,NewState,Cmd);
- _->
- handle_apps(Tools,State,Cmd)
- end
- end.
-
-%----------------------------------------------------------------------
-%execute every start or stop data about a tool.
-%----------------------------------------------------------------------
-handle_apps2([{Name,Start_data}],State,Cmd)->
- case handle_app({Name,Start_data},State#state.app_data,State#state.supvis,Cmd) of
- ok->
- Started = case Cmd of
- start ->
- [Name|State#state.started];
- stop ->
-
- lists:delete(Name,State#state.started)
- end,
- {ok,#state{priv_dir=State#state.priv_dir,
- app_data=State#state.app_data,
- supvis=State#state.supvis,
- web_data=State#state.web_data,
- started=Started}};
- _->
- error
- end;
-
-handle_apps2([{Name,Start_data}|Rest],State,Cmd)->
- case handle_app({Name,Start_data},State#state.app_data,State#state.supvis,Cmd)of
- ok->
- handle_apps2(Rest,State,Cmd);
- _->
- error
- end.
-
-
-%----------------------------------------------------------------------
-% Handle start and stop of applications
-%----------------------------------------------------------------------
-
-handle_app({Name,{start,{func,Start,Stop}}},Data,_Pid,Cmd)->
- Action = case Cmd of
- start ->
- Start;
- _ ->
- Stop
- end,
- case Action of
- {M,F,A} ->
- case catch apply(M,F,A) of
- {'EXIT',_} = Exit->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "~w:~tw(~ts) ->\n"
- "~tp\n\n",
- [?LINE,Name,M,F,format_args(A),Exit]),
- ets:delete(Data,Name);
- _OK->
- ok
- end;
- _NoStart ->
- ok
- end;
-
-
-handle_app({Name,{start,{child,ChildSpec}}},Data,Pid,Cmd)->
- case Cmd of
- start ->
- case catch supervisor:start_child(Pid,ChildSpec) of
- {ok,_}->
- ok;
- {ok,_,_}->
- ok;
- {error,Reason}->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "supervisor:start_child(~p,~tp) ->\n"
- "~tp\n\n",
- [?LINE,Name,Pid,ChildSpec,{error,Reason}]),
- ets:delete(Data,Name);
- Error ->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "supervisor:start_child(~p,~tp) ->\n"
- "~tp\n\n",
- [?LINE,Name,Pid,ChildSpec,Error]),
- ets:delete(Data,Name)
- end;
- stop ->
- case catch supervisor:terminate_child(websup,element(1,ChildSpec)) of
- ok ->
- supervisor:delete_child(websup,element(1,ChildSpec));
- _ ->
- error
- end
- end;
-
-
-
-handle_app({Name,{start,{app,Real_name}}},Data,_Pid,Cmd)->
- case Cmd of
- start ->
- case application:start(Real_name,temporary) of
- ok->
- io:write(Name),
- ok;
- {error,{already_started,_}}->
- %% Remove it from the database so we dont start
- %% anything already started
- ets:match_delete(Data,{Name,{start,{app,Real_name}}}),
- ok;
- {error,_Reason}=Error->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "application:start(~p,~p) ->\n"
- "~tp\n\n",
- [?LINE,Name,Real_name,temporary,Error]),
- ets:delete(Data,Name)
- end;
-
- stop ->
- application:stop(Real_name)
- end;
-
-%----------------------------------------------------------------------
-% If the data is incorrect delete the app
-%----------------------------------------------------------------------
-handle_app({Name,Incorrect},Data,_Pid,Cmd)->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not ~w application \'~p\'\n\n"
- "Incorrect data: ~tp\n\n",
- [?LINE,Cmd,Name,Incorrect]),
- ets:delete(Data,Name).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% this functions creates the page that shows the unstarted tools %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-reload_started_apps()->
- "<script>
- function reloadCompiledList()
- {
- parent.parent.top1.document.location.href=\"/webtool/webtool/started_tools\";
- }
- </script>".
-
-show_unstarted_apps(State)->
- "<TABLE HEIGHT=100% WIDTH=100% BORDER=0>
- <TR HEIGHT=80%><TD ALIGN=\"center\" VALIGN=\"middle\">
- <FORM NAME=\"stop_apps\" ACTION=\"/webtool/webtool/start_tools\" >
- <TABLE BORDER=1 WIDTH=60%>
- <TR BGCOLOR=\"#8899AA\">
- <TD ALIGN=CENTER COLSPAN=2><FONT SIZE=4>Available Tools<FONT></TD>
- </TR>
- <TR>
- <TD WIDTH=50%>
- <TABLE BORDER=0>
- "++ list_available_apps(State)++"
- <TR><TD COLSPAN=2>&nbsp;</TD></TR>
- <TR>
- <TD COLSPAN=2 ALIGN=\"center\">
- <INPUT TYPE=submit VALUE=\"Start\">
- </TD>
- </TR>
- </TABLE>
- </TD>
- <TD>
- To Start a Tool:
- <UL>
- <LI>Select the
- checkbox for each tool to
- start.</LI>
- <LI>Click on the
- button marked <EM>Start</EM>.</LI></UL>
- </TD>
- </TR>
- </TABLE>
- </FORM>
- </TD></TR>
- <TR><TD>&nbsp;</TD></TR>
- </TABLE>".
-
-
-
-list_available_apps(State)->
- MS = ets:fun2ms(fun({Tool,{web_data,{Name,_}}}) -> {Tool,Name} end),
- Unstarted_apps=
- lists:filter(
- fun({Tool,_})->
- false==lists:member(Tool,State#state.started)
- end,
- ets:select(State#state.app_data,MS)),
- case Unstarted_apps of
- []->
- "<TR><TD>All tools are started</TD></TR>";
- _->
- list_apps(Unstarted_apps)
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% these functions creates the page that shows the started apps %%
-%% the user can select to shutdown %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-show_started_apps(State)->
- "<TABLE HEIGHT=100% WIDTH=100% BORDER=0>
- <TR HEIGHT=80%><TD ALIGN=\"center\" VALIGN=\"middle\">
- <FORM NAME=\"stop_apps\" ACTION=\"/webtool/webtool/stop_tools\" >
- <TABLE BORDER=1 WIDTH=60%>
- <TR BGCOLOR=\"#8899AA\">
- <TD ALIGN=CENTER COLSPAN=2><FONT SIZE=4>Started Tools<FONT></TD>
- </TR>
- <TR>
- <TD WIDTH=50%>
- <TABLE BORDER=0>
- "++ list_started_apps(State)++"
- <TR><TD COLSPAN=2>&nbsp;</TD></TR>
- <TR>
- <TD COLSPAN=2 ALIGN=\"center\">
- <INPUT TYPE=submit VALUE=\"Stop\">
- </TD>
- </TR>
- </TABLE>
- </TD>
- <TD>
- Stop a Tool:
- <UL>
- <LI>Select the
- checkbox for each tool to
- stop.</LI>
- <LI>Click on the
- button marked <EM>Stop</EM>.</LI></UL>
- </TD>
- </TR>
- </TABLE>
- </FORM>
- </TD></TR>
- <TR><TD>&nbsp;</TD></TR>
- </TABLE>".
-
-list_started_apps(State)->
- MS = lists:map(fun(A) -> {{A,{web_data,{'$1','_'}}},[],[{{A,'$1'}}]} end,
- State#state.started),
- Started_apps= ets:select(State#state.app_data,MS),
- case Started_apps of
- []->
- "<TR><TD>No tool is started yet.</TD></TR>";
- _->
- list_apps(Started_apps)
- end.
-
-
-list_apps(Apps) ->
- lists:map(fun({Tool,Name})->
- "<TR><TD>
- <INPUT TYPE=\"checkbox\" NAME=\"app\" VALUE=\""
- ++ atom_to_list(Tool) ++ "\">
- " ++ Name ++ "
- </TD></TR>"
- end,
- Apps).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Collecting the data from the *.tool files %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------
-% get_tools(Dirs) => [{M,F,A},{M,F,A}...{M,F,A}]
-% Dirs - [string()] Directory names
-% Calls get_tools2/2 recursively for a number of directories
-% to retireve the configuration data for the web based tools.
-%----------------------------------------
-get_tools1(Dirs)->
- get_tools1(Dirs,[]).
-
-get_tools1([Dir|Rest],Data) when is_list(Dir) ->
- Tools=case filename:basename(Dir) of
- %% Dir is an 'ebin' directory, check in '../priv' as well
- "ebin" ->
- [get_tools2(filename:join(filename:dirname(Dir),"priv")) |
- get_tools2(Dir)];
- _ ->
- get_tools2(Dir)
- end,
- get_tools1(Rest,[Tools|Data]);
-
-get_tools1([],Data) ->
- lists:flatten(Data).
-
-%----------------------------------------
-% get_tools2(Directory) => DataList
-% DataList : [WebTuple]|[]
-% WebTuple: {tool,[{web,M,F,A}]}
-%
-%----------------------------------------
-get_tools2(Dir)->
- get_tools2(tool_files(Dir),[]).
-
-get_tools2([ToolFile|Rest],Data) ->
- case get_tools3(ToolFile) of
- {tool,WebData} ->
- get_tools2(Rest,[{tool,WebData}|Data]);
- {error,_Reason} ->
- get_tools2(Rest,Data);
- nodata ->
- get_tools2(Rest,Data)
- end;
-
-get_tools2([],Data) ->
- Data.
-
-%----------------------------------------
-% get_tools3(ToolFile) => {ok,Tool}|{error,Reason}|nodata
-% Tool: {tool,[KeyValTuple]}
-% ToolFile - string() A .tool file
-% Now we have the file get the data and sort it out
-%----------------------------------------
-get_tools3(ToolFile) ->
- case file:consult(ToolFile) of
- {error,open} ->
- {error,nofile};
- {error,read} ->
- {error,format};
- {ok,[{version,"1.2"},ToolInfo]} when is_list(ToolInfo)->
- webdata(ToolInfo);
- {ok,[{version,_Vsn},_Info]} ->
- {error,old_version};
- {ok,_Other} ->
- {error,format}
- end.
-
-
-%----------------------------------------------------------------------
-% webdata(TupleList)-> ToolTuple| nodata
-% ToolTuple: {tool,[{config_func,{M,F,A}}]}
-%
-% There are a little unneccesary work in this format but it is extendable
-%----------------------------------------------------------------------
-webdata(TupleList)->
- case proplists:get_value(config_func,TupleList,nodata) of
- {M,F,A} ->
- {tool,[{config_func,{M,F,A}}]};
- _ ->
- nodata
- end.
-
-
-%=============================================================================
-% Functions for getting *.tool configuration files
-%=============================================================================
-
-%----------------------------------------
-% tool_files(Dir) => ToolFiles
-% Dir - string() Directory name
-% ToolFiles - [string()]
-% Return the list of all files in Dir ending with .tool (appended to Dir)
-%----------------------------------------
-tool_files(Dir) ->
- case file:list_dir(Dir) of
- {ok,Files} ->
- filter_tool_files(Dir,Files);
- {error,_Reason} ->
- []
- end.
-
-%----------------------------------------
-% filter_tool_files(Dir,Files) => ToolFiles
-% Dir - string() Directory name
-% Files, ToolFiles - [string()] File names
-% Filters out the files in Files ending with .tool and append them to Dir
-%----------------------------------------
-filter_tool_files(_Dir,[]) ->
- [];
-filter_tool_files(Dir,[File|Rest]) ->
- case filename:extension(File) of
- ".tool" ->
- [filename:join(Dir,File)|filter_tool_files(Dir,Rest)];
- _ ->
- filter_tool_files(Dir,Rest)
- end.
-
-
-%%%-----------------------------------------------------------------
-%%% format functions
-ffunc({M,F,A}) when is_list(A) ->
- io_lib:format("~w:~tw(~ts)\n",[M,F,format_args(A)]);
-ffunc({M,F,A}) when is_integer(A) ->
- io_lib:format("~w:~tw/~w\n",[M,F,A]).
-
-format_args([]) ->
- "";
-format_args(Args) ->
- Str = lists:append(["~tp"|lists:duplicate(length(Args)-1,",~tp")]),
- io_lib:format(Str,Args).
diff --git a/lib/common_test/src/ct_webtool_sup.erl b/lib/common_test/src/ct_webtool_sup.erl
deleted file mode 100644
index 04fbbf8745..0000000000
--- a/lib/common_test/src/ct_webtool_sup.erl
+++ /dev/null
@@ -1,76 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(ct_webtool_sup).
-
--behaviour(supervisor).
-
-%% External exports
--export([start_link/0,stop/1]).
-
-%% supervisor callbacks
--export([init/1]).
-
-%%%----------------------------------------------------------------------
-%%% API
-%%%----------------------------------------------------------------------
-start_link() ->
- supervisor:start_link({local,ct_websup},ct_webtool_sup, []).
-
-stop(Pid)->
- exit(Pid,normal).
-%%%----------------------------------------------------------------------
-%%% Callback functions from supervisor
-%%%----------------------------------------------------------------------
-
-%%----------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, {SupFlags, [ChildSpec]}} |
-%% ignore |
-%% {error, Reason}
-%%----------------------------------------------------------------------
-init(_StartArgs) ->
- ct_util:mark_process(),
- %%Child1 =
- %%Child2 ={webcover_backend,{webcover_backend,start_link,[]},permanent,2000,worker,[webcover_backend]},
- %%{ok,{{simple_one_for_one,5,10},[Child1]}}.
- {ok,{{one_for_one,100,10},[]}}.
-
-%%%----------------------------------------------------------------------
-%%% Internal functions
-%%%----------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
deleted file mode 100644
index 38e549d2d6..0000000000
--- a/lib/common_test/src/vts.erl
+++ /dev/null
@@ -1,927 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(vts).
-
--export([start/0,
- init_data/5,
- stop/0,
- report/2]).
-
--export([config_data/0,
- start_link/0]).
-
--export([start_page/2,
- title_frame/2,
- menu_frame/2,
- welcome_frame/2,
- config_frame/2,
- browse_config_file/2,
- add_config_file/2,
- remove_config_file/2,
- run_frame/2,
- add_test_dir/2,
- remove_test_dir/2,
- select_case/2,
- select_suite/2,
- run_test/2,
- result_frameset/2,
- result_summary_frame/2,
- no_result_log_frame/2,
- redirect_to_result_log_frame/2]).
-
--export([test_info/3]).
-
--define(START_PAGE,"/vts_erl/vts/start_page").
-
--define(tests,vts_tests).
-
-%% Colors
--define(INFO_BG_COLOR,"#C0C0EA").
-
--record(state,{tests=[],config=[],event_handler=[],test_runner,
- running=0,reload_results=false,start_dir,current_log_dir,
- logopts=[],total=0,ok=0,fail=0,skip=0,testruns=[]}).
-
-
-%%%-----------------------------------------------------------------
-%%% User API
-start() ->
- {ok, _} = ct_webtool:start(),
- ct_webtool:start_tools([],"app=vts").
-
-init_data(ConfigFiles,EvHandlers,LogDir,LogOpts,Tests) ->
- call({init_data,ConfigFiles,EvHandlers,LogDir,LogOpts,Tests}).
-
-stop() ->
- ct_webtool:stop_tools([],"app=vts"),
- ct_webtool:stop().
-
-report(What,Data) ->
- call({report,What,Data}).
-
-%%%-----------------------------------------------------------------
-%%% Return config data used by ct_webtool
-config_data() ->
- {ok,LogDir} =
- case lists:keysearch(logdir,1,init:get_arguments()) of
- {value,{logdir,[LogD]}} -> {ok,filename:absname(LogD)};
- false -> file:get_cwd()
- end,
- {vts,
- [{web_data,{"VisualTestServer",?START_PAGE}},
- {alias,{erl_alias,"/vts_erl",[?MODULE]}},
- {alias,{"/log_dir",LogDir}},
- {start,{child,{{local,?MODULE},
- {?MODULE,start_link,[]},
- permanent,100,worker,[?MODULE]}}}
- ]}.
-
-start_link() ->
- case whereis(?MODULE) of
- undefined ->
- Self = self(),
- Pid = spawn_link(fun() -> init(Self) end),
- MRef = erlang:monitor(process,Pid),
- receive
- {Pid,started} ->
- erlang:demonitor(MRef, [flush]),
- {ok,Pid};
- {'DOWN',MRef,process,_,Reason} ->
- {error,{vts,died,Reason}}
- end;
- Pid ->
- {ok,Pid}
- end.
-
-start_page(_Env,_Input) ->
- call(start_page).
-title_frame(_Env,_Input) ->
- call(title_frame).
-welcome_frame(_Env,_Input) ->
- call(welcome_frame).
-menu_frame(_Env,_Input) ->
- call(menu_frame).
-config_frame(_Env,_Input) ->
- call(config_frame).
-browse_config_file(_Env,Input) ->
- call({browse_config_file,Input}).
-add_config_file(_Env,Input) ->
- call({add_config_file,Input}).
-remove_config_file(_Env,Input) ->
- call({remove_config_file,Input}).
-run_frame(_Env,_Input) ->
- call(run_frame).
-add_test_dir(_Env,Input) ->
- call({add_test_dir,Input}).
-remove_test_dir(_Env,Input) ->
- call({remove_test_dir,Input}).
-select_suite(_Env,Input) ->
- call({select_suite,Input}).
-select_case(_Env,Input) ->
- call({select_case,Input}).
-run_test(_Env,_Input) ->
- call(run_test).
-result_frameset(_Env,_Input) ->
- call(result_frameset).
-redirect_to_result_log_frame(_Env,_Input) ->
- call(redirect_to_result_log_frame).
-result_summary_frame(_Env,_Input) ->
- call(result_summary_frame).
-no_result_log_frame(_Env,_Input) ->
- call(no_result_log_frame).
-
-aborted() ->
- call(aborted).
-
-test_info(_VtsPid,Type,Data) ->
- call({test_info,Type,Data}).
-
-init(Parent) ->
- register(?MODULE,self()),
- process_flag(trap_exit,true),
- ct_util:mark_process(),
- Parent ! {self(),started},
- {ok,Cwd} = file:get_cwd(),
- InitState = #state{start_dir=Cwd},
- loop(InitState).
-
-loop(State) ->
- receive
- {{init_data,Config,EvHandlers,LogDir,LogOpts,Tests},From} ->
- %% ct:pal("State#state.current_log_dir=~p", [State#state.current_log_dir]),
- NewState = State#state{config=Config,event_handler=EvHandlers,
- current_log_dir=LogDir,
- logopts=LogOpts,tests=Tests},
- _ = ct_install(NewState),
- return(From,ok),
- loop(NewState);
- {start_page,From} ->
- return(From,start_page1()),
- loop(State);
- {title_frame,From} ->
- return(From,title_frame1()),
- loop(State);
- {welcome_frame,From} ->
- return(From,welcome_frame1()),
- loop(State);
- {menu_frame,From} ->
- return(From,menu_frame1()),
- loop(State);
- {config_frame,From} ->
- return(From,config_frame1(State)),
- loop(State);
- {{browse_config_file,_Input},From} ->
- return(From,ok),
- loop(State);
- {{add_config_file,Input},From} ->
- {Return,State1} = add_config_file1(Input,State),
- _ = ct_install(State1),
- return(From,Return),
- loop(State1);
- {{remove_config_file,Input},From} ->
- {Return,State1} = remove_config_file1(Input,State),
- _ = ct_install(State1),
- return(From,Return),
- loop(State1);
- {run_frame,From} ->
- return(From,run_frame1(State)),
- loop(State);
- {{add_test_dir,Input},From} ->
- {Return,State1} = add_test_dir1(Input,State),
- return(From,Return),
- loop(State1);
- {{remove_test_dir,Input},From} ->
- {Return,State1} = remove_test_dir1(Input,State),
- return(From,Return),
- loop(State1);
- {{select_suite,Input},From} ->
- {Return,State1} = select_suite1(Input,State),
- return(From,Return),
- loop(State1);
- {{select_case,Input},From} ->
- {Return,State1} = select_case1(Input,State),
- return(From,Return),
- loop(State1);
- {run_test,From} ->
- State1 = run_test1(State),
- return(From,redirect_to_result_frameset1()),
- loop(State1);
- {result_frameset,From} ->
- return(From,result_frameset1(State)),
- loop(State);
- {redirect_to_result_log_frame,From} ->
- return(From,redirect_to_result_log_frame1(State)),
- loop(State);
- {result_summary_frame,From} ->
- return(From,result_summary_frame1(State)),
- loop(State);
- stop_reload_results ->
- ok = file:set_cwd(State#state.start_dir),
- loop(State#state{reload_results=false});
- {no_result_log_frame,From} ->
- return(From,no_result_log_frame1()),
- loop(State);
- {aborted,From} ->
- return(From,ok),
- loop(State#state{test_runner=undefined,running=0});
- {{report,What,Data},From} ->
- State1 = report1(What,Data,State),
- return(From,ok),
- loop(State1);
- {stop,From} ->
- return(From,ok);
- {'EXIT',Pid,Reason} ->
- case State#state.test_runner of
- Pid ->
- io:format("Test run error: ~tp\n",[Reason]),
- loop(State);
- _ ->
- loop(State)
- end;
- {{test_info,_Type,_Data},From} ->
- return(From,ok),
- loop(State)
- end.
-
-call(Msg) ->
- case whereis(?MODULE) of
- undefined -> {error,no_proc};
- Pid ->
- MRef = erlang:monitor(process,Pid),
- Ref = make_ref(),
- Pid ! {Msg,{self(),Ref}},
- receive
- {Ref, Result} ->
- erlang:demonitor(MRef, [flush]),
- Result;
- {'DOWN',MRef,process,_,Reason} ->
- {error,{process_down,Pid,Reason}}
- end
- end.
-
-return({To,Ref},Result) ->
- To ! {Ref, Result},
- ok.
-
-run_test1(State=#state{tests=Tests,current_log_dir=LogDir,
- logopts=LogOpts}) ->
- Self=self(),
- RunTest = fun() ->
- ct_util:mark_process(),
- case ct_run:do_run(Tests,[],LogDir,LogOpts) of
- {error,_Reason} ->
- aborted();
- _ ->
- ok
- end,
- unlink(Self)
- end,
- Pid = spawn_link(RunTest),
- {Total,Tests1} =
- receive
- {{test_info,start_info,{_,_,Cases}},From} ->
- return(From,ok),
- {Cases,Tests};
- EXIT = {'EXIT',_,_} ->
- self() ! EXIT,
- {0,[]}
- after 30000 ->
- {0,[]}
- end,
- State#state{test_runner=Pid,running=length(Tests1),
- total=Total,ok=0,fail=0,skip=0,testruns=[]}.
-
-
-ct_install(#state{config=Config,event_handler=EvHandlers,
- current_log_dir=LogDir}) ->
- ct_run:install([{config,Config},{event_handler,EvHandlers}],LogDir).
-%%%-----------------------------------------------------------------
-%%% HTML
-start_page1() ->
- header("Visual Test Server Start Page",start_page_frameset()).
-
-start_page_frameset() ->
- frameset(
- "ROWS=\"60,*\"",
- [frame(["NAME=\"title\" SRC=\"./title_frame\""]),
- frameset(
- "COLS=\"150,*\"",
- [frame(["NAME=\"menu\" SRC=\"./menu_frame\""]),
- frame(["NAME=\"main\" SRC=\"./welcome_frame\""])])]).
-
-
-title_frame1() ->
- header(body("BGCOLOR=lightgrey TEXT=darkgreen",title_body())).
-
-title_body() ->
- p("ALIGN=center",font("SIZE=\"+3\"",b("Visual Test Server"))).
-
-welcome_frame1() ->
- header(body(welcome_body())).
-
-welcome_body() ->
- table(
- "WIDTH=100% HEIGHT=60%",
- [tr("VALIGN=middle",
- td("ALIGN=center",
- font("SIZE=6",
- ["Welcome to the",br(),
- "Visual Test Server"])))]).
-
-menu_frame1() ->
- header(body(menu_body())).
-
-menu_body() ->
- [h2("Content"),
- ul([
- li(href(["TARGET=\"main\""],"./config_frame","Config")),
- li(href(["TARGET=\"main\""],"./run_frame","Run")),
- li(href(["TARGET=\"main\""],"./result_frameset","Result"))
- ]),
- h2("Logs"),
- ul([
- li(href(["TARGET=\"new\""],"/log_dir/index.html","Last Runs")),
- li(href(["TARGET=\"new\""],"/log_dir/all_runs.html","All Runs"))
- ])
- ].
-
-config_frame1(State) ->
- header("Config",body(config_body(State))).
-
-config_body(State) ->
- Entry = [input("TYPE=file NAME=browse SIZE=40"),
- input("TYPE=hidden NAME=file")],
- BrowseForm =
- form(
- "NAME=read_file_form METHOD=post ACTION=\"./browse_config_file\"",
- table(
- "BORDER=0",
- [tr(td("1. Locate config file")),
- tr(td(Entry))])),
- AddForm =
- form(
- "NAME=add_file_form METHOD=post ACTION=\"./add_config_file\"",
- table(
- "BORDER=0",
- [tr(td("2. Paste full config file name here")),
- tr(
- [td(input("TYPE=text NAME=file SIZE=40")),
- td("ALIGN=center",
- input("TYPE=submit onClick=\"file.value=browse.value;\""
- " VALUE=\"Add\""))])])),
-
- {Text,RemoveForm} =
- case State#state.config of
- [] ->
- T = "Before running the tests, one or more configuration "
- "files may be added. Locate the config file, copy its "
- "full name, paste this into the text field below, then "
- "click the \"Add\" button.",
- R = "",
- {T,R};
- Files ->
- T = "The currently known configuration files are listed below. "
- "To add a file, type the filename in the entry and "
- "click the \"Add\" button. "
- "To remove a file, select it and click the \"Remove\" "
- "button.",
- ConfigFiles = [option(File) || File <- Files],
- Select = select("NAME=file TITLE=\"Select Config File\""
- " MULTIPLE=true",
- ConfigFiles),
- R =
- form(["NAME=remove_config METHOD=post ",
- "ACTION=\"./remove_config_file\""],
- table(
- "BORDER=0",
- [tr(td("ALIGN=center",Select)),
- tr(td("ALIGN=center",
- input("TYPE=submit VALUE=\"Remove\"")))])),
- {T,R}
- end,
-
- [h1("ALIGN=center","Config"),
- table(
- "WIDTH=450 ALIGN=center CELLPADDING=5",
- [tr(td(["BGCOLOR=",?INFO_BG_COLOR],Text)),
- tr(td("")),
- tr(td("")),
- tr(td("ALIGN=left",BrowseForm)),
- tr(td("ALIGN=left",AddForm)),
- tr(td("ALIGN=left",RemoveForm))])].
-
-add_config_file1(Input,State) ->
- State1 =
- case get_input_data(Input,"file") of
- "" ->
- State;
- File ->
- State#state{config=[File|State#state.config]}
- end,
- Return = config_frame1(State1),
- {Return,State1}.
-
-remove_config_file1(Input,State) ->
- Files = get_all_input_data(Input,"file"),
- State1 = State#state{config=State#state.config--Files},
- Return = config_frame1(State1),
- {Return,State1}.
-
-
-
-run_frame1(State) ->
- header("Run Test",body(run_body(State))).
-
-run_body(#state{running=Running}) when Running>0 ->
- [h1("ALIGN=center","Run Test"),
- p(["Test are ongoing: ",href("./result_frameset","Results")])];
-run_body(State) ->
- ConfigList =
- case State#state.config of
- [] ->
- ul(["none"]);
- CfgFiles ->
- ul([li(File) || File <- CfgFiles])
- end,
- ConfigFiles = [h3("Config Files"),
- ConfigList],
- {ok,CWD} = file:get_cwd(),
- CurrWD = [h3("Current Working Directory"), ul(CWD)],
- AddDirForm =
- form(
- "NAME=add_dir_form METHOD=post ACTION=\"./add_test_dir\"",
- table(
- "BORDER=0",
- [tr(td("COLSPAN=2","Enter test directory")),
- tr(
- [td(input("TYPE=text NAME=dir SIZE=40")),
- td("ALIGN=center",
- input("TYPE=submit onClick=\"dir.value=browse.value;\""
- " VALUE=\"Add Test Dir\""))])])),
- {LoadedTestsTable,Submit} =
- case create_testdir_entries(State#state.tests,1) of
- [] -> {"",""};
- TestDirs ->
- Heading = tr([th(""),
- th("ALIGN=left","Directory"),
- th("ALIGN=left","Suite"),
- th("ALIGN=left","Case")]),
- {table("CELLPADDING=5",[Heading,TestDirs]),
- submit_button()}
- end,
- Body =
- table(
- "WIDTH=450 ALIGN=center",
- [tr(td("")),
- tr(td("")),
- tr(td(ConfigFiles)),
- tr(td("")),
- tr(td(CurrWD)),
- tr(td("")),
- tr(td(AddDirForm)),
- tr(td("")),
- tr(td(LoadedTestsTable)),
- tr(td(Submit))
- ]),
- [h1("ALIGN=center","Run Test"), Body].
-
-create_testdir_entries([{Dir,Suite,Case}|Tests],N) ->
- [testdir_entry(Dir,Suite,Case,N)|create_testdir_entries(Tests,N+1)];
-create_testdir_entries([],_N) ->
- [].
-
-testdir_entry(Dir,Suite,Case,N) ->
- NStr = vts_integer_to_list(N),
- tr([td(delete_button(NStr)),
- td(Dir),
- td(suite_select(Dir,Suite,NStr)),
- td(case_select(Dir,Suite,Case,NStr))]).
-
-delete_button(N) ->
- form(["NAME=remove_dir_form METHOD=post ACTION=\"./remove_test_dir\""],
- [input(["TYPE=hidden NAME=dir VALUE=\'",N,"\'"]),
- input(["TYPE=submit VALUE=X"])]).
-
-suite_select(Dir,Suite,N) ->
- case filelib:wildcard(filename:join(Dir,"*_SUITE.erl")) of
- [] ->
- select("NAME=suite TITLE=\"Select suite\"","");
- Suites0 ->
- Suites = [filename:basename(filename:rootname(S)) || S <- Suites0],
- select("NAME=suite TITLE=\"Select suite\"",
- options(["all"|Suites],atom_to_list(Suite),N,"select_suite"))
- end.
-
-case_select(_Dir,all,_,N) ->
- select("NAME=case TITLE=\"Select case\"",
- options(["all"],"all",N,"select_case"));
-case_select(Dir,Suite,Case,N) ->
- MakeResult =
- case application:get_env(common_test, auto_compile) of
- {ok,false} ->
- ok;
- _ ->
- UserInclude =
- case application:get_env(common_test, include) of
- {ok,UserInclDirs} when length(UserInclDirs) > 0 ->
- [{i,UserInclDir} || UserInclDir <- UserInclDirs];
- _ ->
- []
- end,
- ct_run:run_make(Dir,Suite,UserInclude)
- end,
- case MakeResult of
- ok ->
- true = code:add_pathz(Dir),
- case catch apply(Suite,all,[]) of
- {'EXIT',Reason} ->
- io:format("\n~tp\n",[Reason]),
- red(["COULD NOT READ TESTCASES!!",br(),
- "See erlang shell for info"]);
- {skip,_Reason} ->
- select("NAME=case TITLE=\"Select case\"",
- options(["all"],"all",N,"select_case"));
- AllCasesAtoms ->
- AllCases = [atom_to_list(C) || C <- AllCasesAtoms,
- is_atom(C)],
- select("NAME=case TITLE=\"Select case\"",
- options(["all"|AllCases],atom_to_list(Case),
- N,"select_case"))
- end;
- _Error ->
- red(["COMPILATION ERROR!!",br(),
- "See erlang shell for info",br(),
- "Reload this page when errors are fixed"])
- end.
-
-
-options([Selected|Elements],Selected,N,Func) ->
- [option(["SELECTED ",
- "onClick=\"document.location.href=\'./",Func,"?n=",N,
- "&selected=",Selected,"\';\""],
- Selected)|
- options(Elements,Selected,N,Func)];
-options([Element|Elements],Selected,N,Func) ->
- [option(["onClick=\"document.location.href=\'./",Func,"?n=",N,
- "&selected=",Element,"\';\""],
- Element)|
- options(Elements,Selected,N,Func)];
-options([],_Selected,_N,_Func) ->
- [].
-
-add_test_dir1(Input, State) ->
- State1 =
- case get_input_data(Input,"dir") of
- "" -> State;
- Dir0 ->
- Dir = case ct_util:is_test_dir(Dir0) of
- true -> Dir0;
- false -> ct_util:get_testdir(Dir0, all)
- end,
- case filelib:is_dir(Dir) of
- true ->
- Test = ct_run:tests(Dir),
- State#state{tests=State#state.tests++Test};
- false ->
- State
- end
- end,
- Return = run_frame1(State1),
- {Return,State1}.
-
-remove_test_dir1(Input,State) ->
- N = list_to_integer(get_input_data(Input,"dir")),
- State1 = State#state{tests=delete_test(N,State#state.tests)},
- Return = run_frame1(State1),
- {Return,State1}.
-
-delete_test(1,[_|T]) ->
- T;
-delete_test(N,[H|T]) ->
- [H|delete_test(N-1,T)].
-
-select_suite1(Input,State) ->
- N = list_to_integer(get_input_data(Input,"n")),
- Suite = list_to_atom(get_input_data(Input,"selected")),
- Tests1 = replace_suite(N,Suite,State#state.tests),
- State1 = State#state{tests=Tests1},
- Return = run_frame1(State1),
- {Return,State1}.
-
-replace_suite(1,Suite,[{Dir,_,_}|T]) ->
- [Test] = ct_run:tests(Dir,Suite),
- [Test|T];
-replace_suite(N,Suite,[H|T]) ->
- [H|replace_suite(N-1,Suite,T)].
-
-select_case1(Input,State) ->
- N = list_to_integer(get_input_data(Input,"n")),
- Case = list_to_atom(get_input_data(Input,"selected")),
- Tests1 = replace_case(N,Case,State#state.tests),
- State1 = State#state{tests=Tests1},
- Return = run_frame1(State1),
- {Return,State1}.
-
-replace_case(1,Case,[{Dir,Suite,_}|T]) ->
- [Test] = ct_run:tests(Dir,Suite,Case),
- [Test|T];
-replace_case(N,Case,[H|T]) ->
- [H|replace_case(N-1,Case,T)].
-
-
-submit_button() ->
- form(["NAME=run_test_form METHOD=post ACTION=\"./run_test\""],
- [input("TYPE=submit VALUE=\"Run Test\"")]).
-
-
-redirect_to_result_frameset1() ->
- Head =
- ["<META HTTP-EQUIV=\"refresh\" CONTENT=\"1; URL=./result_frameset\">"],
- [header("",Head,body("Please wait..."))].
-
-result_frameset1(State) ->
- header("Results",result_frameset2(State)).
-
-result_frameset2(State) ->
- ResultLog =
- case {State#state.current_log_dir,State#state.running} of
- {undefined,0} ->
- "./no_result_log_frame";
- {undefined,_} ->
- "./redirect_to_result_log_frame";
- {_Dir,0} ->
- filename:join(["/log_dir","index.html"]);
- {_Dir,_} when State#state.testruns == [] ->
- %% crash before first test
- "./no_result_log_frame";
- {_Dir,_} ->
- {_,CurrentLog} = hd(State#state.testruns),
- CurrentLog
- end,
- frameset(
- "COLS=\"200,*\"",
- [frame(["NAME=\"result_summary\" SRC=\"./result_summary_frame\""]),
- frame(["NAME=\"result_log\" SRC=\"",ResultLog,"\""])]).
-
-redirect_to_result_log_frame1(State) ->
- ResultLog =
- case {State#state.testruns,State#state.running} of
- {[],0} ->
- "./no_result_log_frame";
- {[],_} ->
- "./redirect_to_result_log_frame";
- {[{_,CurrentLog}|_],_} ->
- CurrentLog
- end,
- Head = ["<META HTTP-EQUIV=\"refresh\" CONTENT=\"1; URL=",ResultLog,"\">"],
- [header("",Head,body("Please wait..."))].
-
-result_summary_frame1(State) ->
- case {State#state.running,State#state.reload_results} of
- {0,false} ->
- header("Result Summary",body(result_summary_body(State)));
- _ ->
- Head =
- "<SCRIPT LANGUAGE=\"JavaScript1.2\">\n"
- "\n"
- "function startReloadInterval() {\n"
- " intervalId = setInterval(\"reloadPage()\",5000)\n"
- "}\n"
- "\n"
- "function reloadPage() {\n"
- " location.reload()\n"
- " parent.result_log.location.reload()\n"
-% " parent.result_log.scrollBy(0, window.innerHeight)\n"
- "}\n"
- "</SCRIPT>\n",
- header("Result Summary",Head,
- body("onLoad=\"startReloadInterval()\" BGCOLOR=\"#FFFFFF\"",
- result_summary_body(State)))
- end.
-
-result_summary_body(State) ->
- N = State#state.ok + State#state.fail + State#state.skip,
- [h2("Result Summary"),
- p([b(vts_integer_to_list(N))," cases executed (of ",
- b(vts_integer_to_list(State#state.total)),")"]),
- p([green([b(vts_integer_to_list(State#state.ok))," successful"]),br(),
- red([b(vts_integer_to_list(State#state.fail))," failed"]),br(),
- orange([b(vts_integer_to_list(State#state.skip))," skipped"])]),
- executed_test_list(State)].
-
-executed_test_list(#state{testruns=[]}) ->
- [];
-executed_test_list(#state{testruns=TestRuns}) ->
- [h2("Executed Tests"),
- table(
- "",
- [tr(td(href("TARGET=\"result_log\"",Log,Name))) ||
- {Name,Log} <- lists:reverse(TestRuns)])].
-
-
-no_result_log_frame1() ->
- header("Test Results",body(no_result_log_body())).
-
-no_result_log_body() ->
- [h1("ALIGN=center","Test Results"),
- p(["There are currently no test results available. ",
- br(),href("TARGET=\"main\"","./run_frame","You can run tests here")])].
-
-report1(tests_start,{TestName,_N},State) ->
- {ok,LogDir} = ct_logs:get_log_dir(),
- TestRuns =
- case State#state.testruns of
- [{TestName,_}|_]=TR ->
- TR;
- TR ->
- [{TestName,get_test_log(TestName,LogDir)}|TR]
- end,
- State#state{testruns=TestRuns};
-report1(tests_done,{_Ok,_Fail,_Skip},State) ->
- {ok, _} = timer:send_after(5000, self(),stop_reload_results),
- State#state{running=State#state.running-1,reload_results=true};
-report1(tc_start,{_Suite,_Case},State) ->
- State;
-report1(tc_done,{_Suite,init_per_suite,_},State) ->
- State;
-report1(tc_done,{_Suite,end_per_suite,_},State) ->
- State;
-report1(tc_done,{_Suite,init_per_group,_},State) ->
- State;
-report1(tc_done,{_Suite,end_per_group,_},State) ->
- State;
-report1(tc_done,{_Suite,_Case,ok},State) ->
- State#state{ok=State#state.ok+1};
-report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) ->
- State#state{fail=State#state.fail+1};
-report1(tc_done,{_Suite,_Case,{skipped,_Reason}},State) ->
- State#state{skip=State#state.skip+1};
-report1(tc_user_skip,{_Suite,_Case,_Reason},State) ->
- State#state{skip=State#state.skip+1};
-report1(tc_auto_skip,{_Suite,_Case,_Reason},State) ->
- State#state{skip=State#state.skip+1};
-report1(loginfo,_,State) ->
- State.
-
-get_test_log(TestName,LogDir) ->
- [Log] =
- filelib:wildcard(
- filename:join([TestName++".logs","run*","suite.log.html"])),
- filename:join(["/log_dir",LogDir,Log]).
-
-
-
-%get_description(Suite,Case) ->
-% case erlang:function_exported(Suite,Case,0) of
-% true ->
-% case catch apply(Suite,Case,[]) of
-% {'EXIT',_Reason} ->
-% "-";
-% Info ->
-% case lists:keysearch(doc,1,Info) of
-% {value,{doc,Doc}} when is_list(Doc) ->
-% Doc;
-% _ ->
-% "-"
-% end
-% end;
-% false ->
-% "-"
-% end.
-
-%%%-----------------------------------------------------------------
-%%% Internal library
-header(Body) ->
- header("","",Body).
-header(Title,Body) ->
- header(Title,"",Body).
-header(Title,Head,Body) ->
- ["Pragma:no-cache\r\n",
- "Content-type: text/html\r\n\r\n",
- html_header(Title,Head,Body)].
-
-html_header(Title,Head,Body) ->
- ["<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Title, "</TITLE>\n",
- Head,
- "</HEAD>\n",
- Body,
- "</HTML>"].
-
-body(Text) ->
- ["<BODY BGCOLOR=\"#FFFFFF\">\n",Text,"<\BODY>\n"].
-body(Args,Text) ->
- ["<BODY ", Args, ">\n", Text,"<\BODY>\n"].
-
-
-frameset(Args,Frames) ->
- ["<FRAMESET ",Args,">\n", Frames, "\n</FRAMESET>\n"].
-frame(Args) ->
- ["<FRAME ",Args, ">\n"].
-
-table(Args,Text) ->
- ["<TABLE ", Args, ">\n", Text, "\n</TABLE>\n"].
-tr(Text) ->
- ["<TR>\n", Text, "\n</TR>\n"].
-tr(Args,Text) ->
- ["<TR ", Args, ">\n", Text, "\n</TR>\n"].
-th(Text) ->
- ["<TH>", Text, "</TH>"].
-th(Args,Text) ->
- ["<TH ", Args, ">\n", Text, "\n</TH>\n"].
-td(Text) ->
- ["<TD>", Text, "</TD>"].
-td(Args,Text) ->
- ["<TD ", Args, ">", Text, "</TD>"].
-
-b(Text) ->
- ["<B>",Text,"</B>"].
-%em(Text) ->
-% ["<EM>",Text,"</EM>\n"].
-%pre(Text) ->
-% ["<PRE>",Text,"</PRE>"].
-href(Link,Text) ->
- ["<A HREF=\"",Link,"\">",Text,"</A>"].
-href(Args,Link,Text) ->
- ["<A HREF=\"",Link,"\" ",Args,">",Text,"</A>"].
-form(Args,Text) ->
- ["<FORM ",Args,">\n",Text,"\n</FORM>\n"].
-input(Args) ->
- ["<INPUT ", Args, ">\n"].
-select(Args,Text) ->
- ["<SELECT ", Args, ">\n", Text, "\n</SELECT>\n"].
-option(Text) ->
- ["<OPTION>\n", Text, "\n</OPTION>\n"].
-option(Args,Text) ->
- ["<OPTION ", Args, ">\n", Text, "\n</OPTION>\n"].
-h1(Args,Text) ->
- ["<H1 ", Args, ">",Text,"</H1>\n"].
-h2(Text) ->
- ["<H2>",Text,"</H2>\n"].
-h3(Text) ->
- ["<H3>",Text,"</H3>\n"].
-%%h4(Text) ->
-%% ["<H4>",Text,"</H4>\n"].
-font(Args,Text) ->
- ["<FONT ",Args,">\n",Text,"\n</FONT>\n"].
-p(Text) ->
- ["<P>",Text,"</P>\n"].
-p(Args, Text) ->
- ["<P ", Args, ">",Text,"</P>\n"].
-ul(Text) ->
- ["<UL>", Text, "</UL>\n"].
-li(Text) ->
- ["<LI>", Text, "</LI>\n"].
-br() ->
- "<BR>\n".
-
-red(Text) -> color(red,Text).
-green(Text) -> color(green,Text).
-orange(Text) -> color(orange,Text).
-color(Color,Text) when is_atom(Color) ->
- font(["COLOR=",atom_to_list(Color)],Text).
-
-get_all_input_data(Input,Key)->
- List = parse(Input),
- get_all_input_data(List,Key,[]).
-get_all_input_data([{Key,Value}|List],Key,Acc) ->
- get_all_input_data(List,Key,[Value|Acc]);
-get_all_input_data([{_OtherKey,_Value}|List],Key,Acc) ->
- get_all_input_data(List,Key,Acc);
-get_all_input_data([],_Key,Acc) ->
- Acc.
-
-get_input_data(Input,Key)->
- case lists:keysearch(Key,1,parse(Input)) of
- {value,{Key,Value}} ->
- Value;
- false ->
- undefined
- end.
-
-parse(Input) ->
- uri_string:dissect_query(Input).
-
-vts_integer_to_list(X) when is_atom(X) ->
- atom_to_list(X);
-vts_integer_to_list(X) when is_integer(X) ->
- integer_to_list(X).
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 388d5d46c6..1d50f4f97f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -219,17 +219,8 @@ get_opts(Config) ->
end,
LogDir =
case os:getenv("CT_USE_TMP_DIR") of
- false ->
- case os:type() of
- {win32,_} ->
- if TempDir == undefined -> PrivDir;
- true -> TempDir
- end;
- _ ->
- PrivDir
- end;
- _ ->
- TempDir
+ false -> PrivDir;
+ _ -> TempDir
end,
%% Copy test variables to app environment on new node
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 8dcb69c1c6..ddc518f474 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.17.3
+COMMON_TEST_VSN = 1.18
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index f11444137d..4db09e8059 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,84 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.4.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Code such as the following would crash the compiler in
+ OTP 22: <c>[some_atom = fun some_function/1]</c></p>
+ <p>
+ Own Id: OTP-15833</p>
+ </item>
+ <item>
+ <p>Compilation could get really slow (in the order of
+ minutes instead of seconds) when compiling huge
+ functions. (Thanks to Kostis Sagonas for reporting this
+ bug.)</p>
+ <p>
+ Own Id: OTP-15923</p>
+ </item>
+ <item>
+ <p>Fixed a bug in the validator that could reject valid
+ code.</p>
+ <p>
+ Own Id: OTP-15954 Aux Id: ERL-995 </p>
+ </item>
+ <item>
+ <p>In rare circumstances, when two clauses had identical
+ bodies and guard tests that tested a single boolean
+ variable, the guard test for the second clause could be
+ discarded, executing the second clause unconditionally if
+ the first clause was not executed.</p>
+ <p>
+ Own Id: OTP-15963</p>
+ </item>
+ <item>
+ <p>Fixed extremely slow compilation for huge functions
+ doing predominantly pattern matching.</p>
+ <p>
+ Own Id: OTP-15966 Aux Id: ERL-1014 </p>
+ </item>
+ <item>
+ <p>The compiler could generate unsafe code (that would
+ crash the runtime system) for map pattern matching. The
+ code could be unsafe if the matched key was not present
+ in the map at runtime. </p>
+ <p>
+ Own Id: OTP-15968 Aux Id: ERL-1017 </p>
+ </item>
+ <item>
+ <p>Correct code using try/after could fail to compile
+ when using the option '<c>no_type_opt</c>'.</p>
+ <p>
+ Own Id: OTP-15969 Aux Id: ERL-997 </p>
+ </item>
+ <item>
+ <p>The compiler could crash when compiling code that
+ called '<c>length/1</c>' on a binary extracted using the
+ binary syntax.</p>
+ <p>
+ Own Id: OTP-15970 Aux Id: ERL-1013 </p>
+ </item>
+ <item>
+ <p>Fixed a bug where the compiler could fail with an
+ internal consistency failure error when compiling receive
+ statements.</p>
+ <p>
+ Own Id: OTP-15982 Aux Id: ERL-1022 </p>
+ </item>
+ <item>
+ <p>Fixed a problem where the compiler would crash when
+ compiling binary matching in a function head.</p>
+ <p>
+ Own Id: OTP-15985 Aux Id: ERL-1026 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.4.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 7192ddca15..5434fdf90a 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.4.4
+COMPILER_VSN = 7.4.5
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 3973cf3f9f..5c1f6af016 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -796,7 +796,7 @@
</func>
<func>
- <name name="mac" arity="3" since="OTP @OTP-13872@"/>
+ <name name="mac" arity="3" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Short for <seealso marker="#mac-4">mac(Type, undefined, Key, Data)</seealso>.
@@ -805,7 +805,7 @@
</func>
<func>
- <name name="mac" arity="4" since="OTP @OTP-13872@"/>
+ <name name="mac" arity="4" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Computes a MAC (Message Authentication Code) of type <c>Type</c> from <c>Data</c>.
@@ -846,7 +846,7 @@
</func>
<func>
- <name name="macN" arity="4" since="OTP @OTP-13872@"/>
+ <name name="macN" arity="4" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Short for <seealso marker="#macN-5">macN(Type, undefined, Key, Data, MacLength)</seealso>.
@@ -855,7 +855,7 @@
</func>
<func>
- <name name="macN" arity="5" since="OTP @OTP-13872@"/>
+ <name name="macN" arity="5" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Computes a MAC (Message Authentication Code)
@@ -874,7 +874,7 @@
</func>
<func>
- <name name="mac_init" arity="2" since="OTP @OTP-13872@"/>
+ <name name="mac_init" arity="2" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Short for <seealso marker="#mac_init-3">mac_init(Type, undefined, Key)</seealso>.
@@ -883,7 +883,7 @@
</func>
<func>
- <name name="mac_init" arity="3" since="OTP @OTP-13872@"/>
+ <name name="mac_init" arity="3" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Initializes the context for streaming MAC operations.
@@ -929,7 +929,7 @@
</func>
<func>
- <name name="mac_update" arity="2" since="OTP @OTP-13872@"/>
+ <name name="mac_update" arity="2" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Updates the MAC represented by <c>State0</c> using the given <c>Data</c> which
@@ -945,7 +945,7 @@
</func>
<func>
- <name name="mac_final" arity="1" since="OTP @OTP-13872@"/>
+ <name name="mac_final" arity="1" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Finalizes the MAC operation referenced by <c>State</c>. The <c>Mac</c> result will have
@@ -960,7 +960,7 @@
</func>
<func>
- <name name="mac_finalN" arity="2" since="OTP @OTP-13872@"/>
+ <name name="mac_finalN" arity="2" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Finalizes the MAC operation referenced by <c>State</c>.
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 5f47981855..b35dddf4ff 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,64 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 4.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The implementation of <c>crypto_one_time/4</c> is
+ adjusted to match the type specification. The spec and
+ the black-box behaviour of the function are unchanged.</p>
+ <p>
+ Some details: Both the spec and the implementation were
+ correct seen separately. But with both of them combined
+ simultaneously with <c>crypto_one_time/5</c> which was
+ called by the implementation of <c>crypto_one_time/4</c>,
+ an (obvious) error was detected by a Dialyzer with more
+ thorough checking than usual.</p>
+ <p>
+ Own Id: OTP-15884 Aux Id: ERL-974 </p>
+ </item>
+ <item>
+ <p>
+ When using crypto with FIPS mode enabled, the digests
+ were not correctly handled.</p>
+ <p>
+ Own Id: OTP-15911</p>
+ </item>
+ <item>
+ <p>
+ A memory leak in error handling code in
+ <c>ng_crypto_init_nif</c> is fixed.</p>
+ <p>
+ Own Id: OTP-15924</p>
+ </item>
+ <item>
+ <p>
+ Fixed the broken static build of the crypto nifs</p>
+ <p>
+ Own Id: OTP-15928 Aux Id: PR-2296 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The Message Authentication Codes (MAC) CMAC, HMAC and
+ Poly1305 are unified into common functions in the New
+ Crypto API. See the manual for CRYPTO.</p>
+ <p>
+ Own Id: OTP-13872</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 4.5.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 2315cb3c48..9a5b9397f7 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 4.5.1
+CRYPTO_VSN = 4.6
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index dad9fd18c7..f6cd2ec585 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 4.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Allow native compilation when using Dialyzer from
+ Erlang. The options <c>native</c> (defaults to
+ <c>false</c>) and <c>native_cache</c> have been added.
+ </p>
+ <p>
+ Own Id: OTP-15880 Aux Id: PR-2283 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 4.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index a77c74c717..03155e2d24 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 4.0.3
+DIALYZER_VSN = 4.1
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index f25361a202..f51cd26a7f 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -31,7 +31,22 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.9.1</title>
+ <section><title>Erl_Docgen 0.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Update the documentation build support to handle FOP
+ 2.1 . </p>
+ <p>
+ Own Id: OTP-16051</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.9.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index 1b91d768e3..7080394298 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -662,7 +662,7 @@
<fo:flow flow-name="xsl-region-body">
<fo:block xsl:use-attribute-sets="cover.logo">
- <fo:external-graphic src="{$logo}"/>
+ <fo:external-graphic src="url('{$logo}')"/>
</fo:block>
<fo:block xsl:use-attribute-sets="cover.title" id="cover-page">
<xsl:apply-templates/>
@@ -1658,10 +1658,10 @@
<fo:block xsl:use-attribute-sets="image">
<xsl:choose>
<xsl:when test="@width">
- <fo:external-graphic content-width="scale-to-fit" width="{@width}" inline-progression-dimension.maximum="100%" src="{@file}"/>
+ <fo:external-graphic content-width="scale-to-fit" width="{@width}" inline-progression-dimension.maximum="100%" src="url('{@file}')"/>
</xsl:when>
<xsl:otherwise>
- <fo:external-graphic content-width="scale-down-to-fit" inline-progression-dimension.maximum="100%" src="{@file}"/>
+ <fo:external-graphic content-width="scale-down-to-fit" inline-progression-dimension.maximum="100%" src="url('{@file}')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates>
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index fece2456c1..2ac4acaf09 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.9.1
+ERL_DOCGEN_VSN = 0.10
diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile
index 507a84a453..03044a0ddd 100644
--- a/lib/erl_interface/doc/src/Makefile
+++ b/lib/erl_interface/doc/src/Makefile
@@ -96,7 +96,7 @@ man: $(MAN1_FILES) $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt lcnt:
clean clean_docs clean_tex:
rm -rf $(HTMLDIR)/*
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index c47f0d2bd1..08f5a40687 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bugs in <c>ei_print_term</c> for binaries and bit
+ strings causing incorrect output.</p>
+ <p>
+ Own Id: OTP-15917</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>ei_decode_fun</c> for very old fun
+ encoding format. Bug exist since OTP 22.0.</p>
+ <p>
+ Own Id: OTP-15996</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p><c>ei_print_term()</c> now supports printing of maps
+ and funs.</p>
+ <p>
+ Own Id: OTP-15814</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/src/Makefile b/lib/erl_interface/src/Makefile
index 00c49f1622..6f728a0a28 100644
--- a/lib/erl_interface/src/Makefile
+++ b/lib/erl_interface/src/Makefile
@@ -33,3 +33,5 @@ endif
clean depend docs release release_docs tests release_tests check xmllint:
$(make_verbose)$(MAKE) -f $(TARGET)/Makefile $@
+
+lcnt:
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index cc72ed639a..2bf84bf18f 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.12
+EI_VSN = 3.13
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 67a9ae5fcb..397a4657d3 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -33,6 +33,21 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.3.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Handle <c>get_until</c> request with explicit
+ encoding in the implementation of the I/O protocol. </p>
+ <p>
+ Own Id: OTP-16000</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.3.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 46ef5eea3c..52d23698fa 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.3.7
+EUNIT_VSN = 2.3.8
diff --git a/lib/ftp/doc/src/notes.xml b/lib/ftp/doc/src/notes.xml
index 61da079900..d71f795c09 100644
--- a/lib/ftp/doc/src/notes.xml
+++ b/lib/ftp/doc/src/notes.xml
@@ -33,7 +33,23 @@
<file>notes.xml</file>
</header>
- <section><title>Ftp 1.0.2</title>
+ <section><title>Ftp 1.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A possibly infinite loop when receiving messages divided
+ in parts is removed.</p>
+ <p>
+ Own Id: OTP-16056</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ftp 1.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ftp/src/ftp.erl b/lib/ftp/src/ftp.erl
index 18cd8c7524..e9be7b8ff7 100644
--- a/lib/ftp/src/ftp.erl
+++ b/lib/ftp/src/ftp.erl
@@ -1399,10 +1399,13 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
ctrl_data =
{NextMsgData, [], start}})
end;
- {continue, NewCtrlData} ->
+ {continue, NewCtrlData} when NewCtrlData =/= CtrlData ->
?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
State = activate_ctrl_connection(State0),
- {noreply, State#state{ctrl_data = NewCtrlData}}
+ {noreply, State#state{ctrl_data = NewCtrlData}};
+ {continue, NewCtrlData} when NewCtrlData == CtrlData ->
+ ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
+ {noreply, State0}
end;
%% If the server closes the control channel it is
diff --git a/lib/ftp/vsn.mk b/lib/ftp/vsn.mk
index 9f14658099..397328ce27 100644
--- a/lib/ftp/vsn.mk
+++ b/lib/ftp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = ftp
-FTP_VSN = 1.0.2
+FTP_VSN = 1.0.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(FTP_VSN)$(PRE_VSN)"
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 7451b314ec..72ac79a0b0 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -701,15 +701,17 @@
<type>
<v>Profile = profile() | pid()</v>
<d>When started <c>stand_alone</c> only the pid can be used.</d>
- <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v>
- <v>GoodSessions = session()</v>
- <v>BadSessions = tuple()</v>
- <v>NonSessions = term()</v>
+ <v>session_info() = {[session()], [term()], [term()]}</v>
+ <v>session() = term() - Internal representation of a session</v>
</type>
<desc>
- <p>Produces a slightly processed dump of the session
- database. It is intended for debugging.
- If no profile is specified, the default profile is used.</p>
+ <p> This function is intended for debugging only. It produces
+ a slightly processed dump of the session database. The first
+ list of the session information tuple will contain session
+ information on an internal format. The last two lists of the
+ session information tuple should always be empty if the code
+ is working as intended. If no profile is specified, the default
+ profile is used.</p>
</desc>
</func>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 45533c4f4b..da670bb1c9 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,30 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 7.0.9</title>
+ <section><title>Inets 7.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpd - Accept singel LF as line terminator</p>
+ <p>
+ Own Id: OTP-15893 Aux Id: PR-2206 </p>
+ </item>
+ <item>
+ <p>
+ mod_esi will now always propagate the actual HTTP status
+ code that it answered with, to later mod-modules, and not
+ in some cases hardcode 200.</p>
+ <p>
+ Own Id: OTP-16049 Aux Id: ERIERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 7.0.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -66,6 +89,23 @@
</section>
+ <section><title>Inets 7.0.7.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ mod_esi will now always propagate the actual HTTP status
+ code that it anwsered with, to later mod-modules, and not
+ in some cases hardcode 200.</p>
+ <p>
+ Own Id: OTP-16049 Aux Id: ERIERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Inets 7.0.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -3346,5 +3386,3 @@
-->
</chapter>
-
-
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 8cbd9798e6..bcf392d55c 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -345,12 +345,12 @@ erl_scheme_webpage_whole(Mod, Func, Env, Input, ModData) ->
integer_to_list(Length)}| NewHeaders]),
case ModData#mod.method of
"HEAD" ->
- {proceed, [{response, {already_sent, 200, 0}} |
+ {proceed, [{response, {already_sent, StatusCode, 0}} |
ModData#mod.data]};
_ ->
httpd_response:send_body(ModData,
StatusCode, Body),
- {proceed, [{response, {already_sent, 200,
+ {proceed, [{response, {already_sent, StatusCode,
Length}} |
ModData#mod.data]}
end
@@ -415,12 +415,12 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
[{"transfer-encoding",
"chunked"} | NewHeaders])
end,
- handle_body(Pid, ModData, Body, Timeout, length(Body),
+ handle_body(Pid, ModData, Body, Timeout, length(Body), StatusCode,
IsDisableChunkedSend);
timeout ->
send_headers(ModData, 504, [{"connection", "close"}]),
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket),
- {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]}
+ {proceed,[{response, {already_sent, 504, 0}} | ModData#mod.data]}
end.
receive_headers(Timeout) ->
@@ -444,24 +444,24 @@ send_headers(ModData, StatusCode, HTTPHeaders) ->
httpd_response:send_header(ModData, StatusCode,
ExtraHeaders ++ HTTPHeaders).
-handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
- {proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
+handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, StatusCode, _) ->
+ {proceed, [{response, {already_sent, StatusCode, Size}} | ModData#mod.data]};
-handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
+handle_body(Pid, ModData, Body, Timeout, Size, StatusCode, IsDisableChunkedSend) ->
httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend),
receive
{esi_data, Data} when is_binary(Data) ->
- handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data),
+ handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), StatusCode,
IsDisableChunkedSend);
{esi_data, Data} ->
- handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
+ handle_body(Pid, ModData, Data, Timeout, Size + length(Data), StatusCode,
IsDisableChunkedSend);
{ok, Data} ->
- handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
+ handle_body(Pid, ModData, Data, Timeout, Size + length(Data), StatusCode,
IsDisableChunkedSend);
{'EXIT', Pid, normal} when is_pid(Pid) ->
httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
- {proceed, [{response, {already_sent, 200, Size}} |
+ {proceed, [{response, {already_sent, StatusCode, Size}} |
ModData#mod.data]};
{'EXIT', Pid, Reason} when is_pid(Pid) ->
Error = lists:flatten(io_lib:format("mod_esi process failed with reason ~p", [Reason])),
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index fc5ca14dcd..bf926ec9c1 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -28,6 +28,7 @@
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include_lib("inets/include/httpd.hrl").
-include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
@@ -58,6 +59,7 @@ all() ->
{group, https_limit},
{group, http_custom},
{group, https_custom},
+ {group, https_custom},
{group, http_basic_auth},
{group, https_basic_auth},
{group, http_auth_api},
@@ -139,7 +141,7 @@ groups() ->
{http_1_1, [],
[host, chunked, expect, cgi, cgi_chunked_encoding_test,
trace, range, if_modified_since, mod_esi_chunk_timeout,
- esi_put, esi_post] ++ http_head() ++ http_get() ++ load()},
+ esi_put, esi_post, esi_proagate] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()},
{http_rel_path_script_alias, [], [cgi]},
@@ -1053,6 +1055,17 @@ mod_esi_chunk_timeout(Config) when is_list(Config) ->
proplists:get_value(port, Config),
proplists:get_value(host, Config),
proplists:get_value(node, Config)).
+%%-------------------------------------------------------------------------
+esi_proagate(Config) when is_list(Config) ->
+ register(propagate_test, self()),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:new_status_and_location ",
+ Config, [{statuscode, 201}]),
+ receive
+ {status, 201} ->
+ ok;
+ Err ->
+ ct:fail(Err)
+ end.
%%-------------------------------------------------------------------------
cgi() ->
@@ -2246,8 +2259,17 @@ head_status(_) ->
basic_conf() ->
[{modules, [mod_alias, mod_range, mod_responsecontrol,
- mod_trace, mod_esi, mod_cgi, mod_get, mod_head]}].
-
+ mod_trace, mod_esi, ?MODULE, mod_cgi, mod_get, mod_head]}].
+do(ModData) ->
+ case whereis(propagate_test) of
+ undefined ->
+ ok;
+ _ ->
+ {already_sent, Status, _Size} = proplists:get_value(response, ModData#mod.data),
+ propagate_test ! {status, Status}
+ end,
+ {proceed, ModData#mod.data}.
+
not_sup_conf() ->
[{modules, [mod_get]}].
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index d948204618..afc02f2038 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 7.0.9
+INETS_VSN = 7.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index e79ada47f1..433534207a 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Replaced deprecated &lt;tt&gt; with &lt;code&gt; in
+ documentation.</p>
+ <p>
+ Own Id: OTP-16050</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 95c7c95726..f15a3f323b 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.10
+JINTERFACE_VSN = 1.10.1
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 6c0d072fed..14819aa938 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -213,7 +213,7 @@
</func>
<func>
- <name name="send" arity="3" since="OTP @OTP-15747@"/>
+ <name name="send" arity="3" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -242,7 +242,7 @@
</func>
<func>
- <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP @OTP-15747@"/>
+ <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -265,7 +265,7 @@
</func>
<func>
- <name name="send" arity="4" clause_i="3" since="OTP @OTP-15747@"/>
+ <name name="send" arity="4" clause_i="3" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -284,7 +284,7 @@
</func>
<func>
- <name name="send" arity="5" since="OTP @OTP-15747@"/>
+ <name name="send" arity="5" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 1011befca0..e1a8ae567a 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -118,7 +118,7 @@ fe80::204:acff:fe17:bf38
<name name="port_number"/>
</datatype>
<datatype>
- <name name="family_address" since="OTP @OTP-15747@"/>
+ <name name="family_address" since="OTP 22.1"/>
<desc>
<p>
A general address format on the form <c>{Family, Destination}</c>
@@ -130,7 +130,7 @@ fe80::204:acff:fe17:bf38
</desc>
</datatype>
<datatype>
- <name name="inet_address" since="OTP @OTP-15747@"/>
+ <name name="inet_address" since="OTP 22.1"/>
<desc>
<warning>
<p>
@@ -142,7 +142,7 @@ fe80::204:acff:fe17:bf38
</desc>
</datatype>
<datatype>
- <name name="inet6_address" since="OTP @OTP-15747@"/>
+ <name name="inet6_address" since="OTP 22.1"/>
<desc>
<warning>
<p>
@@ -582,7 +582,7 @@ get_tcpi_sacked(Sock) ->
<p>Gets one or more statistic options for a socket.</p>
<p><c>getstat(<anno>Socket</anno>)</c> is equivalent to
<c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi,
- recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max,
+ recv_max, recv_oct, send_avg, send_cnt, send_pend, send_max,
send_oct])</c>.</p>
<p>The following options are available:</p>
<taglist>
@@ -614,9 +614,9 @@ get_tcpi_sacked(Sock) ->
<item>
<p>Number of packets sent from the socket.</p>
</item>
- <tag><c>send_dvi</c></tag>
+ <tag><c>send_pend</c></tag>
<item>
- <p>Average packet size deviation, in bytes, sent from the socket.</p>
+ <p>Number of bytes waiting to be sent by the socket.</p>
</item>
<tag><c>send_max</c></tag>
<item>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 4d31eeea3d..aea3787115 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,101 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The type specification for <c>gen_sctp:connect/4,5</c>
+ has been corrected.</p>
+ <p>
+ Own Id: OTP-15344 Aux Id: ERL-947 </p>
+ </item>
+ <item>
+ <p>
+ Extra <c>-mode</c> flags given to <c>erl</c> are ignored
+ with a warning.</p>
+ <p>
+ Own Id: OTP-15852</p>
+ </item>
+ <item>
+ <p>
+ Fix type spec for <c>seq_trace:set_token/2</c>.</p>
+ <p>
+ Own Id: OTP-15858 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ <c>logger:compare_levels/2</c> would fail with a
+ <c>badarg</c> exception if given the values <c>all</c> or
+ <c>none</c> as any of the parameters. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15942 Aux Id: PR-2301 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug where the log file in <c>logger_std_h</c> would
+ not be closed when the inode of the file changed. This
+ would in turn cause a file descriptor leak when tools
+ like logrotate are used.</p>
+ <p>
+ Own Id: OTP-15997 Aux Id: PR-2331 </p>
+ </item>
+ <item>
+ <p>
+ Fix a race condition in the debugging function
+ <c>net_kernel:nodes_info/0</c>.</p>
+ <p>
+ Own Id: OTP-16022</p>
+ </item>
+ <item>
+ <p>
+ Fix race condition when closing a file opened in
+ <c>compressed</c> or <c>delayed_write</c> mode.</p>
+ <p>
+ Own Id: OTP-16023</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The possibility to send ancillary data, in particular the
+ TOS field, has been added to <c>gen_udp:send/4,5</c>.</p>
+ <p>
+ Own Id: OTP-15747 Aux Id: ERIERL-294 </p>
+ </item>
+ <item>
+ <p>
+ If the log file was given with relative path, the
+ standard logger handler (<c>logger_std_h</c>) would store
+ the file name with relative path. If the current
+ directory of the node was later changed, a new file would
+ be created relative the new current directory,
+ potentially failing with an <c>enoent</c> if the new
+ directory did not exist. This is now corrected and
+ <c>logger_std_h</c> always stores the log file name as an
+ absolute path, calculated from the current directory at
+ the time of the handler startup.</p>
+ <p>
+ Own Id: OTP-15850</p>
+ </item>
+ <item>
+ <p>
+ Support local sockets with inet:i/0.</p>
+ <p>
+ Own Id: OTP-15935 Aux Id: PR-2299 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 6.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -147,6 +242,24 @@
</section>
+<section><title>Kernel 6.3.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug where the log file in <c>logger_std_h</c> would
+ not be closed when the inode of the file changed. This
+ would in turn cause a file descriptor leak when tools
+ like logrotate are used.</p>
+ <p>
+ Own Id: OTP-15997 Aux Id: PR-2331 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 6.3.1.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
index cda4c470f9..bc2e07ddf5 100644
--- a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
+++ b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
@@ -1,8 +1,8 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2017-2019. All Rights Reserved.
+%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,7 +14,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_tcp_dist).
@@ -30,12 +30,11 @@
%% VM.
%%
%% This code is a rewrite of the lib/kernel/src/inet_tcp_dist.erl
-%% distribution impementation for TCP used by default. That
-%% implementation use distribution ports instead of distribution
+%% distribution implementation for TCP used by default. The default
+%% implementation uses distribution ports instead of distribution
%% processes and is more efficient compared to this implementation.
-%% This since this implementation more or less gets the
-%% distribution processes in between the VM and the ports without
-%% any gain specific gain.
+%% This example more or less gets the distribution processes
+%% in between the VM and the ports without any specific gain.
%%
-export([listen/1, accept/1, accept_connection/5,
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index 88752431eb..2d2b84c206 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -67,6 +67,7 @@ MODULES = \
dist_ac \
dist_util \
erl_boot_server \
+ erl_compile_server \
erl_ddll \
erl_distribution \
erl_epmd \
diff --git a/lib/kernel/src/erl_compile_server.erl b/lib/kernel/src/erl_compile_server.erl
new file mode 100644
index 0000000000..f4b719068e
--- /dev/null
+++ b/lib/kernel/src/erl_compile_server.erl
@@ -0,0 +1,253 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erl_compile_server).
+-behaviour(gen_server).
+-export([start_link/0, compile/1]).
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
+
+-define(COMPILE_SERVER, erl_compile_server).
+-define(IDLE_TIMEOUT, (10*1000)).
+-define(WRONG_TIMEOUT, 1).
+
+-type client() :: {pid(), term()}.
+-type job_map() :: #{{pid(),reference()} := client()}.
+
+-record(st, {
+ cwd=[] :: file:filename(),
+ config :: term(),
+ timeout=?IDLE_TIMEOUT :: non_neg_integer(),
+ jobs=#{} :: job_map()
+ }).
+
+-type state() :: #st{}.
+
+-spec start_link() -> {'ok', pid()} | {'error', term()}.
+
+start_link() ->
+ gen_server:start_link({local, ?COMPILE_SERVER}, ?MODULE, [], []).
+
+-spec init(Arg :: []) -> {'ok', any(), timeout()}.
+
+init([]) ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ Config = init_config(),
+ {ok, #st{config=Config}, ?IDLE_TIMEOUT}.
+
+-spec compile(term()) -> term().
+
+compile(Parameters) ->
+ gen_server:call(?COMPILE_SERVER, {compile, Parameters}, infinity).
+
+-spec handle_call(any(), _, state()) -> {'noreply', state()}.
+
+handle_call({compile, Parameters}, From, #st{jobs=Jobs}=St0) ->
+ {ErlcArgs, PathArgs} = parse_command_line(Parameters),
+ case verify_context(PathArgs, Parameters, St0) of
+ {ok, St1} ->
+ #{cwd := Cwd, encoding := Enc} = Parameters,
+ PidRef = spawn_monitor(fun() -> exit(do_compile(ErlcArgs, Cwd, Enc)) end),
+ St = St1#st{jobs=Jobs#{PidRef => From}},
+ {noreply, St#st{timeout=?IDLE_TIMEOUT}};
+ wrong_config ->
+ case map_size(Jobs) of
+ 0 ->
+ %% Wrong configuration and no outstanding jobs.
+ %% Terminate immediately.
+ halt();
+ _ ->
+ {reply, wrong_config, St0#st{timeout=?WRONG_TIMEOUT}, ?WRONG_TIMEOUT}
+ end
+ end.
+
+-spec handle_cast(term(), state()) -> {'noreply', state()}.
+
+handle_cast(_, St) ->
+ {noreply, St}.
+
+-spec handle_info(term(), state()) -> {'noreply', state(), timeout()}.
+
+handle_info({'DOWN',Ref,process,Pid,Reason}, #st{jobs=Jobs0}=St0) ->
+ Key = {Pid, Ref},
+ Client = map_get(Key, Jobs0),
+ Jobs = maps:remove(Key, Jobs0),
+ St = St0#st{jobs=Jobs},
+ gen_server:reply(Client, Reason),
+ case map_size(Jobs) =:= 0 of
+ true ->
+ {noreply, St, St#st.timeout};
+ false ->
+ {noreply, St}
+ end;
+handle_info(timeout, #st{jobs=Jobs}) when map_size(Jobs) =:= 0 ->
+ halt();
+handle_info(_, #st{timeout=Timeout}=St) ->
+ %% There are still outstanding jobs.
+ {noreply, St, Timeout}.
+
+%%%
+%%% Local functions.
+%%%
+
+verify_context(PathArgs, #{env := Env}=Parameters, St0) ->
+ case ensure_cwd(Parameters, St0) of
+ {ok, #st{config=Config}=St} ->
+ case make_config(PathArgs, Env) of
+ Config ->
+ {ok, St};
+ _ ->
+ wrong_config
+ end;
+ wrong_config ->
+ wrong_config
+ end.
+
+ensure_cwd(#{cwd := Cwd}, #st{cwd=Cwd}=St) ->
+ {ok, St};
+ensure_cwd(#{cwd := NewCwd}, #st{jobs=Jobs}=St) when map_size(Jobs) =:= 0 ->
+ ok = file:set_cwd(NewCwd),
+ {ok, St#st{cwd=NewCwd}};
+ensure_cwd(#{}, #st{}) ->
+ wrong_config.
+
+do_compile(ErlcArgs, Cwd, Enc) ->
+ GL = create_gl(),
+ group_leader(GL, self()),
+ Result = erl_compile:compile(ErlcArgs, Cwd),
+ StdOutput = ensure_enc(gl_get_output(GL), Enc),
+ case Result of
+ ok ->
+ {ok, StdOutput};
+ {error, StdErrorOutput0} ->
+ StdErrorOutput = ensure_enc(StdErrorOutput0, Enc),
+ {error, StdOutput, StdErrorOutput}
+ end.
+
+parse_command_line(#{command_line := CmdLine, cwd := Cwd}) ->
+ parse_command_line_1(CmdLine, Cwd, [], []).
+
+parse_command_line_1(["-pa", Pa|T], Cwd, PaAcc, PzAcc) ->
+ parse_command_line_1(T, Cwd, [Pa|PaAcc], PzAcc);
+parse_command_line_1(["-pz", Pz|T], Cwd, PaAcc, PzAcc) ->
+ parse_command_line_1(T, Cwd, PaAcc, [Pz|PzAcc]);
+parse_command_line_1(["-extra"|ErlcArgs], Cwd, PaAcc, PzAcc) ->
+ PaArgs = clean_path_args(lists:reverse(PaAcc), Cwd),
+ PzArgs = clean_path_args(lists:reverse(PzAcc), Cwd),
+ {ErlcArgs, [{pa, PaArgs}, {pz, PzArgs}]};
+parse_command_line_1([_|T], Cwd, PaAcc, PzAcc) ->
+ parse_command_line_1(T, Cwd, PaAcc, PzAcc).
+
+ensure_enc(Chars, latin1) ->
+ L = unicode:characters_to_list(Chars, unicode),
+ unicode:characters_to_binary(
+ [ case X of
+ High when High > 255 ->
+ ["\\x{", erlang:integer_to_list(X, 16), $}];
+ Low ->
+ Low
+ end || X <- L ], unicode, latin1);
+ensure_enc(Chars, _Enc) -> Chars.
+
+init_config() ->
+ EnvVars = ["ERL_AFLAGS", "ERL_FLAGS", "ERL_ZFLAGS",
+ "ERL_COMPILER_OPTIONS",
+ "ERL_LIBS",
+ "ERLC_CONFIGURATION"],
+ Env0 = [{Name, os:getenv(Name)} || Name <- EnvVars],
+ Env = [P || {_, Val}=P <- Env0, Val =/= false],
+ {ok, Cwd} = file:get_cwd(),
+ make_config([get_path_arg(pa, Cwd), get_path_arg(pz, Cwd)], Env).
+
+get_path_arg(PathArg, Cwd) ->
+ case init:get_argument(PathArg) of
+ error ->
+ {PathArg, []};
+ {ok, Paths0} ->
+ Paths1 = lists:append(Paths0),
+ Paths = clean_path_args(Paths1, Cwd),
+ {PathArg, Paths}
+ end.
+
+clean_path_args(PathArgs, Cwd) ->
+ [filename:absname(P, Cwd) || P <- PathArgs].
+
+make_config(PathArgs, Env0) ->
+ Env = lists:sort(Env0),
+ PathArgs ++ [iolist_to_binary([[Name,$=,Val,$\n] || {Name,Val} <- Env])].
+
+%%%
+%%% A group leader that will capture all output to the group leader.
+%%%
+
+create_gl() ->
+ spawn_link(fun() -> gl_loop([]) end).
+
+gl_get_output(GL) ->
+ GL ! {self(), get_output},
+ receive
+ {GL, Output} -> Output
+ end.
+
+gl_loop(State0) ->
+ receive
+ {io_request, From, ReplyAs, Request} ->
+ {_Tag, Reply, State} = gl_request(Request, State0),
+ gl_reply(From, ReplyAs, Reply),
+ gl_loop(State);
+ {From, get_output} ->
+ Output = iolist_to_binary(State0),
+ From ! {self(), Output},
+ gl_loop(State0);
+ _Unknown ->
+ gl_loop(State0)
+ end.
+
+gl_reply(From, ReplyAs, Reply) ->
+ From ! {io_reply, ReplyAs, Reply},
+ ok.
+
+gl_request({put_chars, Encoding, Chars}, State) ->
+ gl_put_chars(unicode:characters_to_binary(Chars, Encoding), State);
+gl_request({put_chars, Encoding, Module, Function, Args}, State) ->
+ try
+ gl_request({put_chars, Encoding, apply(Module, Function, Args)}, State)
+ catch
+ _:_ ->
+ {{error,Function}, State}
+ end;
+gl_request({requests, Reqs}, State) ->
+ gl_multi_request(Reqs, {ok, State});
+gl_request(_Other, State) ->
+ {error, {error, request}, State}.
+
+gl_multi_request([R|Rs], {ok, State}) ->
+ gl_multi_request(Rs, gl_request(R, State));
+gl_multi_request([_|_], Error) ->
+ Error;
+gl_multi_request([], Result) ->
+ Result.
+
+gl_put_chars(Chars, Output) ->
+ {ok, ok, [Output,Chars]}.
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index c2ff6b63e9..4f5e6d782f 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -32,6 +32,7 @@
code_server,
dist_util,
erl_boot_server,
+ erl_compile_server,
erl_distribution,
erl_reply,
erl_signal_handler,
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 95853a7a8f..d862b5491f 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -40,7 +40,8 @@
{<<"^6\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^6\\.4$">>,[restart_new_emulator]},
- {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
+ {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
[{<<"^6\\.0$">>,[restart_new_emulator]},
{<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -54,4 +55,5 @@
{<<"^6\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^6\\.4$">>,[restart_new_emulator]},
- {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
+ {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index c8c631ab23..8877ceea8e 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -166,10 +166,12 @@ init([]) ->
modules => dynamic},
Timer = start_timer(),
+ CompileServer = start_compile_server(),
{ok, {SupFlags,
[Code, InetDb | DistChildren] ++
- [File, SigSrv, StdError, User, Config, RefC, SafeSup, LoggerSup] ++ Timer}}
+ [File, SigSrv, StdError, User, Config, RefC, SafeSup, LoggerSup] ++
+ Timer ++ CompileServer}}
end;
init(safe) ->
SupFlags = #{strategy => one_for_one,
@@ -303,6 +305,19 @@ start_timer() ->
[]
end.
+start_compile_server() ->
+ case application:get_env(kernel, start_compile_server) of
+ {ok, true} ->
+ [#{id => erl_compile_server,
+ start => {erl_compile_server, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [erl_compile_server]}];
+ _ ->
+ []
+ end.
+
%%-----------------------------------------------------------------
%% The change of the distributed parameter is taken care of here
%%-----------------------------------------------------------------
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 83d3b4b5e1..e333bcd307 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -135,7 +135,6 @@
node %% remote node name
}).
-
-record(tick, {ticker, %% ticker : pid()
time %% Ticktime in milli seconds : integer()
}).
@@ -213,7 +212,6 @@ set_net_ticktime(T) when is_integer(T) ->
get_net_ticktime() ->
ticktime_res(request(ticktime)).
-
%% The monitor_nodes() feature has been moved into the emulator.
%% The feature is reached via (intentionally) undocumented process
%% flags (we may want to move it elsewhere later). In order to easily
@@ -274,7 +272,6 @@ connect_node(Node) when is_atom(Node) ->
hidden_connect_node(Node) when is_atom(Node) ->
request({connect, hidden, Node}).
-
passive_connect_monitor(From, Node) ->
ok = monitor_nodes(true,[{node_type,all}]),
Reply = case lists:member(Node,nodes([connected])) of
@@ -292,7 +289,6 @@ passive_connect_monitor(From, Node) ->
{Pid, Tag} = From,
erlang:send(Pid, {Tag, Reply}).
-
%% If the net_kernel isn't running we ignore all requests to the
%% kernel, thus basically accepting them :-)
request(Req) ->
@@ -367,7 +363,7 @@ do_auto_connect_1(Node, ConnId, From, State) ->
ConnLookup ->
do_auto_connect_2(Node, ConnId, From, State, ConnLookup)
end.
-
+
do_auto_connect_2(Node, passive_cnct, From, State, ConnLookup) ->
try erts_internal:new_connection(Node) of
ConnId ->
@@ -393,7 +389,7 @@ do_auto_connect_2(Node, ConnId, From, State, ConnLookup) ->
case application:get_env(kernel, dist_auto_connect) of
{ok, never} ->
?connect_failure(Node,{dist_auto_connect,never}),
- erts_internal:abort_connection(Node, ConnId),
+ erts_internal:abort_connection(Node, ConnId),
{reply, false, State};
%% This might happen due to connection close
@@ -418,14 +414,13 @@ do_auto_connect_2(Node, ConnId, From, State, ConnLookup) ->
end
end.
-
do_explicit_connect([#connection{conn_id = ConnId, state = up}], _, _, ConnId, _From, State) ->
{reply, true, State};
do_explicit_connect([#connection{conn_id = ConnId}=Conn], _, _, ConnId, From, State)
when Conn#connection.state =:= pending;
Conn#connection.state =:= up_pending ->
Waiting = Conn#connection.waiting,
- ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}),
+ ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}),
{noreply, State};
do_explicit_connect([#barred_connection{}], Type, Node, ConnId, From , State) ->
%% Barred connection only affects auto_connect, ignore it.
@@ -440,7 +435,6 @@ do_explicit_connect(_ConnLookup, Type, Node, ConnId, From , State) ->
{reply, false, State}
end.
-
%% ------------------------------------------------------------
%% handle_call.
%% ------------------------------------------------------------
@@ -477,15 +471,13 @@ handle_call({connect, Type, Node}, From, State) ->
erts_internal:abort_connection(Node, ConnId)
end,
R1
-
catch
_:_ ->
error_logger:error_msg("~n** Cannot get connection id for node ~w~n",
[Node]),
- {reply, false, State}
+ {reply, false, State}
end,
return_call(R, From);
-
%%
%% Close the connection to Node.
@@ -570,7 +562,6 @@ handle_call({publish_on_node, Node}, From, State) ->
end,
async_reply({reply, Publish, NewState}, From);
-
handle_call({verbose, Level}, From, State) ->
async_reply({reply, State#state.verbose, State#state{verbose = Level}},
From);
@@ -670,25 +661,16 @@ code_change(_OldVsn, State, _Extra) ->
terminate(no_network, State) ->
lists:foreach(
- fun({Node, Type}) ->
- case Type of
- normal -> ?nodedown(Node, State);
- _ -> ok
- end
- end, get_up_nodes() ++ [{node(), normal}]);
+ fun(Node) -> ?nodedown(Node, State)
+ end, get_nodes_up_normal() ++ [node()]);
terminate(_Reason, State) ->
lists:foreach(
fun(#listen {listen = Listen,module = Mod}) ->
Mod:close(Listen)
end, State#state.listen),
lists:foreach(
- fun({Node, Type}) ->
- case Type of
- normal -> ?nodedown(Node, State);
- _ -> ok
- end
- end, get_up_nodes() ++ [{node(), normal}]).
-
+ fun(Node) -> ?nodedown(Node, State)
+ end, get_nodes_up_normal() ++ [node()]).
%% ------------------------------------------------------------
%% handle_info.
@@ -818,7 +800,6 @@ handle_info({SetupPid, {is_pending, Node}}, State) ->
SetupPid ! {self(), {is_pending, Reply}},
{noreply, State};
-
%%
%% Handle different types of process terminations.
%%
@@ -1020,7 +1001,6 @@ up_pending_nodedown(Conn, Node, _Reason, _Type, State) ->
AcceptPid ! {self(), pending},
State#state{conn_owners = [{AcceptPid,Node}|Owners], pend_owners = Pend}.
-
up_nodedown(Conn, Node, _Reason, Type, State) ->
mark_sys_dist_nodedown(Conn, Node),
case Type of
@@ -1042,7 +1022,6 @@ mark_sys_dist_nodedown(Conn, Node) ->
%% End handle_exit/2 !!
%% -----------------------------------------------------------
-
%% -----------------------------------------------------------
%% monitor_nodes/[1,2] errors
%% -----------------------------------------------------------
@@ -1147,35 +1126,10 @@ disconnect_pid(Pid, State) ->
%%
%%
%%
-get_nodes(Which) ->
- get_nodes(ets:first(sys_dist), Which).
-get_nodes('$end_of_table', _) ->
- [];
-get_nodes(Key, Which) ->
- case ets:lookup(sys_dist, Key) of
- [Conn = #connection{state = up}] ->
- [Conn#connection.node | get_nodes(ets:next(sys_dist, Key),
- Which)];
- [Conn = #connection{}] when Which =:= all ->
- [Conn#connection.node | get_nodes(ets:next(sys_dist, Key),
- Which)];
- _ ->
- get_nodes(ets:next(sys_dist, Key), Which)
- end.
-
-%% Return a list of all nodes that are 'up'.
-get_up_nodes() ->
- get_up_nodes(ets:first(sys_dist)).
-
-get_up_nodes('$end_of_table') -> [];
-get_up_nodes(Key) ->
- case ets:lookup(sys_dist, Key) of
- [#connection{state=up,node=Node,type=Type}] ->
- [{Node,Type}|get_up_nodes(ets:next(sys_dist, Key))];
- _ ->
- get_up_nodes(ets:next(sys_dist, Key))
- end.
+%% Return a list of all nodes that are 'up' and not hidden.
+get_nodes_up_normal() ->
+ ets:select(sys_dist, [{#connection{node = '$1', state = up, type = normal, _ = '_'}, [], ['$1']}]).
ticker(Kernel, Tick) when is_integer(Tick) ->
process_flag(priority, max),
@@ -1312,7 +1266,7 @@ setup(Node, ConnId, Type, From, State) ->
end.
setup_check(Node, State) ->
- Allowed = State#state.allowed,
+ Allowed = State#state.allowed,
case lists:member(Node, Allowed) of
false when Allowed =/= [] ->
error_msg("** Connection attempt with "
@@ -1323,9 +1277,7 @@ setup_check(Node, State) ->
{ok, _L}=OK -> OK;
Error -> Error
end
- end.
-
-
+ end.
%%
%% Find a module that is willing to handle connection setup to Node
@@ -1339,7 +1291,6 @@ select_mod(Node, [L|Ls]) ->
select_mod(Node, []) ->
{error, {unsupported_address_type, Node}}.
-
get_proto_mod(Family,Protocol,[L|Ls]) ->
A = L#listen.address,
if A#net_address.family =:= Family,
@@ -1365,7 +1316,7 @@ init_node(Name, LongOrShortNames, CleanHalt) ->
Error
end;
Error ->
- Error
+ Error
end.
%% Create the node name
@@ -1474,7 +1425,6 @@ protocol_childspecs([H|T]) ->
protocol_childspecs(T)
end.
-
%%
%% epmd_module() -> module_name of erl_epmd or similar gen_server_module.
%%
@@ -1640,15 +1590,14 @@ get_node_info(Node, Key) ->
end.
get_nodes_info() ->
- get_nodes_info(get_nodes(all), []).
-
-get_nodes_info([Node|Nodes], InfoList) ->
- case get_node_info(Node) of
- {ok, Info} -> get_nodes_info(Nodes, [{Node, Info}|InfoList]);
- _ -> get_nodes_info(Nodes, InfoList)
- end;
-get_nodes_info([], InfoList) ->
- {ok, InfoList}.
+ Nodes = ets:select(sys_dist, [{#connection{node = '$1', _ = '_'}, [], ['$1']}]),
+ {ok, lists:filtermap(
+ fun(Node) ->
+ case get_node_info(Node) of
+ {ok, Info} -> {true, {Node, Info}};
+ _ -> false
+ end
+ end, Nodes)}.
%% ------------------------------------------------------------
%% Misc. functions
@@ -1669,7 +1618,6 @@ reply_waiting1([From|W], Rep) ->
reply_waiting1([], _) ->
ok.
-
-ifdef(UNUSED).
delete_all(From, [From |Tail]) -> delete_all(From, Tail);
@@ -1730,7 +1678,6 @@ fmt_address(A) ->
lists:flatten(io_lib:format("~p", [A#net_address.address]))
end.
-
fetch(Key, Info) ->
case lists:keysearch(Key, 1, Info) of
{value, {_, Val}} -> Val;
@@ -1802,7 +1749,6 @@ call_owner(Owner, Msg) ->
error
end.
-
-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when
Node :: node() | new,
Options :: [inet:socket_setopt()],
diff --git a/lib/kernel/src/raw_file_io_compressed.erl b/lib/kernel/src/raw_file_io_compressed.erl
index d5ab042d25..f6ac6eaffc 100644
--- a/lib/kernel/src/raw_file_io_compressed.erl
+++ b/lib/kernel/src/raw_file_io_compressed.erl
@@ -123,6 +123,7 @@ wrap_call(Fd, Command) ->
try gen_statem:call(Pid, Command, infinity) of
Result -> Result
catch
+ exit:{normal, _StackTrace} -> {error, einval};
exit:{noproc, _StackTrace} -> {error, einval}
end.
diff --git a/lib/kernel/src/raw_file_io_delayed.erl b/lib/kernel/src/raw_file_io_delayed.erl
index d2ad7550a1..5644717aaa 100644
--- a/lib/kernel/src/raw_file_io_delayed.erl
+++ b/lib/kernel/src/raw_file_io_delayed.erl
@@ -309,6 +309,7 @@ wrap_call(Fd, Command) ->
try gen_statem:call(Pid, Command, infinity) of
Result -> Result
catch
+ exit:{normal, _StackTrace} -> {error, einval};
exit:{noproc, _StackTrace} -> {error, einval}
end.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 3bc8e6e828..1c1b35abc1 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -94,6 +94,8 @@
-export([allocate/1]).
+-export([allocate_file_size/1]).
+
-export([standard_io/1,mini_server/1]).
-export([old_io_protocol/1]).
@@ -145,7 +147,7 @@ groups() ->
{files, [],
[{group, open}, {group, pos}, {group, file_info},
{group, consult}, {group, eval}, {group, script},
- truncate, sync, datasync, advise, allocate]},
+ truncate, sync, datasync, advise, allocate, allocate_file_size]},
{open, [],
[open1, old_modes, new_modes, path_open, close, access,
read_write, pread_write, append, open_errors,
@@ -2036,6 +2038,28 @@ allocate_and_assert(Fd, Offset, Length) ->
_ = Result
end.
+%% Tests that asserts that file:allocate/3 changes file size
+allocate_file_size(Config) when is_list(Config) ->
+ case os:type() of
+ {unix, darwin} ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"),
+
+ {ok, Fd} = ?FILE_MODULE:open(Allocate, [write]),
+ ok = ?FILE_MODULE:allocate(Fd, 0, 1024),
+ {ok, 1024} = ?FILE_MODULE:position(Fd, eof),
+ ok = ?FILE_MODULE:close(Fd),
+
+ [] = flush(),
+ ok;
+ {unix, linux} ->
+ {skip, "file:allocate/3 on Linux does not change file size"};
+ {win32, _} ->
+ {skip, "Windows does not support file:allocate/3"};
+ _ ->
+ {skip, "Support for allocate/3 is spotty in our test platform at the moment."}
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
delete(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index de87bd9472..cd1bc2e0d1 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -3545,28 +3545,33 @@ wait(Mref) ->
%% OTP-15536
%% Test that send error works correctly for delay_send
delay_send_error(_Config) ->
- {ok, LS} = gen_tcp:listen(0, [{reuseaddr, true}, {packet, 1}, {active, false}]),
- {ok,{{0,0,0,0},PortNum}}=inet:sockname(LS),
- P = spawn_link(
- fun() ->
- {ok, S} = gen_tcp:accept(LS),
- receive die -> gen_tcp:close(S) end
- end),
- erlang:monitor(process, P),
- {ok, S} = gen_tcp:connect("localhost", PortNum,
- [{packet, 1}, {active, false}, {delay_send, true}]),
-
+ {ok, L} =
+ gen_tcp:listen(
+ 0, [{reuseaddr, true}, {packet, 1}, {active, false}]),
+ {ok,{{0,0,0,0},PortNum}}=inet:sockname(L),
+ {ok, C} =
+ gen_tcp:connect(
+ "localhost", PortNum,
+ [{packet, 1}, {active, false}, {delay_send, true}]),
+ {ok, S} = gen_tcp:accept(L),
%% Do a couple of sends first to see that it works
- ok = gen_tcp:send(S, "hello"),
- ok = gen_tcp:send(S, "hello"),
- ok = gen_tcp:send(S, "hello"),
-
- %% Make the receiver close
- P ! die,
- receive _Down -> ok end,
-
- ok = gen_tcp:send(S, "hello"),
- timer:sleep(500), %% Sleep in order for delay_send to have time to trigger
-
- %% This used to result in a double free
- {error, closed} = gen_tcp:send(S, "hello").
+ ok = gen_tcp:send(C, "hello"),
+ ok = gen_tcp:send(C, "hello"),
+ ok = gen_tcp:send(C, "hello"),
+ %% Close the receiver
+ ok = gen_tcp:close(S),
+ %%
+ case gen_tcp:send(C, "hello") of
+ ok ->
+ case gen_tcp:send(C, "hello") of
+ ok ->
+ timer:sleep(1000), %% Sleep in order for delay_send to have time to trigger
+ %% This used to result in a double free
+ {error, closed} = gen_tcp:send(C, "hello");
+ {error, closed} ->
+ ok
+ end;
+ {error, closed} ->
+ ok
+ end,
+ ok = gen_tcp:close(C).
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e5188aa9b5..508e54237b 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 6.4.1
+KERNEL_VSN = 6.5
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 6f33ae390c..62d0aad77d 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -37,7 +37,24 @@
section is the version number of Megaco.</p>
- <section><title>Megaco 3.18.5</title>
+ <section><title>Megaco 3.18.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix various minor issues related to Dialyzer. Mostly
+ these are dialyzer warnings, but there was also some
+ minor bugs detected by Dialyzer.</p>
+ <p>
+ Own Id: OTP-15882</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Megaco 3.18.5</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/megaco/src/engine/megaco_monitor.erl b/lib/megaco/src/engine/megaco_monitor.erl
index 877509a776..8f4741b202 100644
--- a/lib/megaco/src/engine/megaco_monitor.erl
+++ b/lib/megaco/src/engine/megaco_monitor.erl
@@ -193,7 +193,7 @@ cancel_apply_after({apply_after, Ref}) ->
TimeLeft when is_integer(TimeLeft) ->
{ok, TimeLeft};
_ ->
- {ok, 0}
+ {error, {already_expired, Ref}}
end;
cancel_apply_after(apply_after_infinity) ->
ok;
diff --git a/lib/megaco/test/megaco_SUITE.erl b/lib/megaco/test/megaco_SUITE.erl
index f7b8ffe032..b55dc68143 100644
--- a/lib/megaco/test/megaco_SUITE.erl
+++ b/lib/megaco/test/megaco_SUITE.erl
@@ -130,7 +130,19 @@ end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
- Config.
+ Skippable = [{unix, [{darwin, fun(V) when (V > {9, 8, 0}) ->
+ %% This version is OK: No Skip
+ false;
+ (_V) ->
+ %% This version is *not* ok: Skip
+ true
+ end}]}],
+ case ?OS_BASED_SKIP(Skippable) of
+ true ->
+ {skip, "***OLD*** Darwin"};
+ false ->
+ Config
+ end.
end_per_group(_GroupName, Config) ->
Config.
diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl
index 3fd39a9e58..46aeabd679 100644
--- a/lib/megaco/test/megaco_mess_test.erl
+++ b/lib/megaco/test/megaco_mess_test.erl
@@ -790,8 +790,15 @@ request_and_reply_pending_ack_no_pending(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -844,6 +851,7 @@ request_and_reply_pending_ack_no_pending(Config) when is_list(Config) ->
-endif.
rarpanp_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -855,10 +863,6 @@ rarpanp_mgc_event_sequence(text, tcp) ->
ScrVerify = ?rarpanp_mgc_verify_service_change_req_fun(Mid),
NrVerify = ?rarpanp_mgc_verify_notify_req_fun(),
DiscoVerify = ?rarpanp_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarpanp_mgc_verify_handle_connect/1,
-%% ScrVerify = rarpanp_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarpanp_mgc_verify_notify_request_fun(),
-%% DiscoVerify = fun rarpanp_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -868,6 +872,10 @@ rarpanp_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -1381,8 +1389,15 @@ request_and_reply_pending_ack_one_pending(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -1453,6 +1468,7 @@ rarpaop_mgc_event_sequence(binary, tcp) ->
rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf).
rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, Port},
@@ -1465,11 +1481,6 @@ rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf) ->
NrVerify = ?rarpaop_mgc_verify_notify_req_fun(),
AckVerify = ?rarpaop_mgc_verify_reply_ack_fun(),
DiscoVerify = ?rarpaop_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarpaop_mgc_verify_handle_connect/1,
-%% ScrVerify = rarpaop_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarpaop_mgc_verify_notify_request_fun(),
-%% AckVerify = rarpaop_mgc_verify_reply_ack_fun(),
-%% DiscoVerify = fun rarpaop_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -1479,6 +1490,10 @@ rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -2066,8 +2081,15 @@ single_trans_req_and_reply(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -2120,6 +2142,7 @@ single_trans_req_and_reply(Config) when is_list(Config) ->
-endif.
strar_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -2127,18 +2150,10 @@ strar_mgc_event_sequence(text, tcp) ->
{encoding_config, []},
{transport_module, megaco_tcp}
],
- %% Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]},
-%% ReqTmr = #megaco_incr_timer{wait_for = 500,
-%% factor = 1,
-%% max_retries = 1},
ConnectVerify = ?strar_mgc_verify_handle_connect_fun(),
ServiceChangeReqVerify = ?strar_mgc_verify_service_change_req_fun(Mid),
NotifyReqVerify = ?strar_mgc_verify_notify_req_fun(),
DiscoVerify = ?strar_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun strar_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = strar_mgc_verify_service_change_req_fun(Mid),
-%% NotifyReqVerify = strar_mgc_verify_notify_request_fun(),
-%% DiscoVerify = fun strar_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -2146,6 +2161,10 @@ strar_mgc_event_sequence(text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify},
@@ -2578,8 +2597,15 @@ single_trans_req_and_reply_sendopts(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -2632,6 +2658,7 @@ single_trans_req_and_reply_sendopts(Config) when is_list(Config) ->
-endif.
straro_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -2643,10 +2670,6 @@ straro_mgc_event_sequence(text, tcp) ->
ServiceChangeReqVerify = ?straro_mgc_verify_service_change_req_fun(Mid),
NotifyReqVerify = ?straro_mgc_verify_notify_req_fun(),
TransAckVerify = ?straro_mgc_verify_handle_trans_ack_fun(),
-%% ConnectVerify = fun straro_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = straro_mgc_verify_service_change_req_fun(Mid),
-%% NotifyReqVerify = straro_mgc_verify_notify_request_fun(),
-%% TransAckVerify = straro_mgc_verify_handle_trans_ack_fun(),
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -2654,6 +2677,10 @@ straro_mgc_event_sequence(text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify},
@@ -3125,8 +3152,15 @@ request_and_reply_and_ack(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -3184,6 +3218,7 @@ request_and_reply_and_ack(Config) when is_list(Config) ->
-endif.
raraa_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -3196,11 +3231,6 @@ raraa_mgc_event_sequence(text, tcp) ->
NrVerify = ?raraa_mgc_verify_notify_req_fun(),
AckVerify = ?raraa_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?raraa_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun raraa_mgc_verify_handle_connect/1,
-%% ScrVerify = raraa_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = raraa_mgc_verify_notify_request_fun(),
-%% AckVerify = raraa_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun raraa_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -3210,6 +3240,10 @@ raraa_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -3739,8 +3773,15 @@ request_and_reply_and_no_ack(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -3798,6 +3839,7 @@ request_and_reply_and_no_ack(Config) when is_list(Config) ->
-endif.
rarana_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -3810,11 +3852,6 @@ rarana_mgc_event_sequence(text, tcp) ->
NrVerify = ?rarana_mgc_verify_notify_req_fun(),
AckVerify = ?rarana_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?rarana_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarana_mgc_verify_handle_connect/1,
-%% ScrVerify = rarana_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarana_mgc_verify_notify_request_fun(),
-%% AckVerify = rarana_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun rarana_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -3825,6 +3862,10 @@ rarana_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, reply_timer, 9000},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -4339,8 +4380,15 @@ request_and_reply_and_late_ack(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -4398,6 +4446,7 @@ request_and_reply_and_late_ack(Config) when is_list(Config) ->
-endif.
rarala_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -4415,11 +4464,6 @@ rarala_mgc_event_sequence(text, tcp) ->
NrVerify = ?rarala_mgc_verify_notify_req_fun(),
AckVerify = ?rarala_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?rarala_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarala_mgc_verify_handle_connect/1,
-%% ScrVerify = rarala_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarala_mgc_verify_notify_request_fun(),
-%% AckVerify = rarala_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun rarala_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -4430,6 +4474,10 @@ rarala_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, reply_timer, RepTmr},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -4970,8 +5018,15 @@ trans_req_and_reply_and_req(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -5024,6 +5079,7 @@ trans_req_and_reply_and_req(Config) when is_list(Config) ->
-endif.
trarar_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -5031,10 +5087,6 @@ trarar_mgc_event_sequence(text, tcp) ->
{encoding_config, []},
{transport_module, megaco_tcp}
],
-%% Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]},
-%% ReqTmr = #megaco_incr_timer{wait_for = 500,
-%% factor = 1,
-%% max_retries = 1},
ConnectVerify = ?trarar_mgc_verify_handle_connect_fun(),
ServiceChangeReqVerify = ?trarar_mgc_verify_service_change_req_fun(Mid),
NotifyReqVerify1 = ?trarar_mgc_verify_notify_req_fun(1),
@@ -5042,13 +5094,6 @@ trarar_mgc_event_sequence(text, tcp) ->
NotifyReqVerify3 = ?trarar_mgc_verify_notify_req_fun(3),
NotifyReqVerify4 = ?trarar_mgc_verify_notify_req_fun(4),
DiscoVerify = ?trarar_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun trarar_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = trarar_mgc_verify_service_change_req_fun(Mid),
-%% NotifyReqVerify1 = trarar_mgc_verify_notify_request_fun(1),
-%% NotifyReqVerify2 = trarar_mgc_verify_notify_request_fun(2),
-%% NotifyReqVerify3 = trarar_mgc_verify_notify_request_fun(3),
-%% NotifyReqVerify4 = trarar_mgc_verify_notify_request_fun(4),
-%% DiscoVerify = fun trarar_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -5057,6 +5102,10 @@ trarar_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, reply_timer, 2000},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify1},
@@ -5625,8 +5674,15 @@ pending_ack_plain(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -5688,6 +5744,7 @@ pending_ack_plain(Config) when is_list(Config) ->
-endif.
pap_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -5701,12 +5758,6 @@ pap_mgc_event_sequence(text, tcp) ->
NrVerify2 = ?pap_mgc_verify_notify_req_long_fun(),
AckVerify = ?pap_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?pap_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun pap_mgc_verify_handle_connect/1,
-%% ScrVerify = pap_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify1 = pap_mgc_verify_notify_request_fun(),
-%% NrVerify2 = pap_mgc_verify_notify_request_long_fun(),
-%% AckVerify = pap_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun pap_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -5716,6 +5767,10 @@ pap_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -6312,8 +6367,15 @@ request_and_pending_and_late_reply(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -6371,6 +6433,7 @@ request_and_pending_and_late_reply(Config) when is_list(Config) ->
-endif.
rapalr_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?rapalr_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?rapalr_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"mgc"},
@@ -6391,6 +6454,10 @@ rapalr_mgc_event_sequence(text, tcp) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ScrVerifyFun, 5000}},
{send, "service-change-reply", ServiceChangeRep},
@@ -7143,6 +7210,13 @@ otp_4836(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
i("[MG] start"),
MgMid = {deviceName, "mg"},
ReqTmr = #megaco_incr_timer{wait_for = 3000,
@@ -7207,6 +7281,7 @@ otp_4836(Config) when is_list(Config) ->
-endif.
otp_4836_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_4836_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_4836_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -7220,6 +7295,10 @@ otp_4836_mgc_event_sequence(text, tcp) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ServiceChangeVerifyFun, 10000}},
{send, "service-change-reply", ServiceChangeReply},
@@ -7388,8 +7467,15 @@ otp_5805(Config) when is_list(Config) ->
{ok, MgcId} =
megaco_test_megaco_generator:exec(Mgc, MgcEvSeq, timer:minutes(1)),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("start the MG simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -7652,6 +7738,7 @@ Transaction = 2 {
-endif.
otp_5805_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -7664,11 +7751,6 @@ otp_5805_mgc_event_sequence(text, tcp) ->
SyntaxErrorVerify1 = ?otp_5805_mgc_verify_handle_syntax_error_fun(),
SyntaxErrorVerify2 = ?otp_5805_mgc_verify_handle_syntax_error_fun(),
DiscoVerify = ?otp_5805_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun otp_5805_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = otp_5805_mgc_verify_service_change_req_fun(Mid),
-%% SyntaxErrorVerify1 = fun otp_5805_mgc_verify_handle_syntax_error/1,
-%% SyntaxErrorVerify2 = fun otp_5805_mgc_verify_handle_syntax_error/1,
-%% DiscoVerify = fun otp_5805_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -7677,6 +7759,10 @@ otp_5805_mgc_event_sequence(text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request_sc, ServiceChangeReqVerify},
@@ -7849,6 +7935,13 @@ otp_5881(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
i("[MG] start"),
MgMid = {deviceName, "mg"},
ReqTmr = #megaco_incr_timer{wait_for = 3000,
@@ -7930,6 +8023,7 @@ otp_5881_verify_trans_id(Mg, Expected) ->
-endif.
otp_5881_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_5881_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_5881_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -7943,6 +8037,10 @@ otp_5881_mgc_event_sequence(text, tcp) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ServiceChangeVerifyFun, 10000}},
{send, "service-change-reply", ServiceChangeReply},
@@ -8090,6 +8188,13 @@ otp_5887(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
i("[MG] start"),
MgMid = {deviceName, "mg"},
ReqTmr = #megaco_incr_timer{wait_for = 3000,
@@ -8201,6 +8306,7 @@ otp_5887_verify_trans_id(F, Expected) ->
-endif.
otp_5887_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_5887_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_5887_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -8213,6 +8319,10 @@ otp_5887_mgc_event_sequence(text, tcp) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ServiceChangeVerifyFun, 10000}},
{send, "service-change-reply", ServiceChangeReply},
@@ -8421,6 +8531,13 @@ otp_6275(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -8483,9 +8600,6 @@ otp_6275_mg_event_sequence2(Mid, RI) ->
ConnectVerify = ?otp_6275_mg_verify_handle_connect_fun(),
NotifyReqVerify = ?otp_6275_mg_verify_notify_req_fun(),
TransReplyVerify = ?otp_6275_mg_verify_handle_trans_rep_fun(),
-%% ConnectVerify = otp_6275_mg_verify_handle_connect_fun(),
-%% NotifyReqVerify = otp_6275_mg_verify_notify_request_fun(),
-%% TransReplyVerify = otp_6275_mg_verify_trans_reply_fun(),
EvSeq = [
{debug, true},
megaco_start,
@@ -8657,6 +8771,7 @@ otp_6275_mg_service_change_request_ar(_Mid, Cid) ->
-endif.
otp_6275_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_6275_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_6275_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -8670,6 +8785,9 @@ otp_6275_mgc_event_sequence(text, tcp) ->
{encode, EncodeFun},
{listen, 2944},
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {SCRVerifyFun, 5000}},
{sleep, 1000}, %% Do _not_ send SC reply
@@ -8912,8 +9030,15 @@ otp_6276(Config) when is_list(Config) ->
d("[MGC] start the tcp-simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -9106,6 +9231,7 @@ otp_6276_mgc_event_sequence(text, tcp, Ctrl) ->
otp_6276_mgc_event_sequence2(Mid, EM, EC, Ctrl).
otp_6276_mgc_event_sequence2(Mid, EM, EC, Ctrl) ->
+ CTRL = self(),
DecodeFun = otp_6276_mgc_decode_msg_fun(EM, EC),
EncodeFun = otp_6276_mgc_encode_msg_fun(EM, EC),
AnnounceMe = otp_6276_mgc_announce_fun(Ctrl),
@@ -9125,6 +9251,10 @@ otp_6276_mgc_event_sequence2(Mid, EM, EC, Ctrl) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-req",
{ServiceChangeReqVerify, 10000}},
@@ -11127,8 +11257,12 @@ otp_6865_request_and_reply_plain_extra2(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -11192,13 +11326,14 @@ otp_6865_request_and_reply_plain_extra2(Config) when is_list(Config) ->
-endif.
otp6865e2_mgc_event_sequence(ExtraInfo, text, tcp) ->
- Mid = {deviceName,"ctrl"},
- RI = [
- {port, 2944},
- {encoding_module, megaco_pretty_text_encoder},
- {encoding_config, []},
- {transport_module, megaco_tcp}
- ],
+ Mid = {deviceName, "ctrl"},
+ CTRL = self(),
+ RI = [
+ {port, 2944},
+ {encoding_module, megaco_pretty_text_encoder},
+ {encoding_config, []},
+ {transport_module, megaco_tcp}
+ ],
ConnectVerify =
?otp6865e2_mgc_verify_handle_connect_fun(ExtraInfo),
ServiceChangeReqVerify =
@@ -11220,6 +11355,10 @@ otp6865e2_mgc_event_sequence(ExtraInfo, text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify1},
@@ -11992,8 +12131,15 @@ otp_7189(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -12046,6 +12192,7 @@ otp_7189(Config) when is_list(Config) ->
-endif.
otp_7189_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -12099,6 +12246,10 @@ otp_7189_mgc_event_sequence(text, tcp) ->
{megaco_user_info, all},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl
index 7c8c287e7c..3d26a8585f 100644
--- a/lib/megaco/test/megaco_test_lib.erl
+++ b/lib/megaco/test/megaco_test_lib.erl
@@ -127,31 +127,72 @@ non_pc_tc_maybe_skip(Config, Condition, File, Line)
end.
+%% The type and spec'ing is just to increase readability
+-type os_family() :: win32 | unix.
+-type os_name() :: atom().
+-type os_version() :: string() | {non_neg_integer(),
+ non_neg_integer(),
+ non_neg_integer()}.
+-type os_skip_check() :: fun(() -> boolean()) |
+ fun((os_version()) -> boolean()).
+-type skippable() :: any | [os_family() |
+ {os_family(), os_name() |
+ [os_name() | {os_name(),
+ os_skip_check()}]}].
+
+-spec os_based_skip(skippable()) -> boolean().
+
os_based_skip(any) ->
true;
os_based_skip(Skippable) when is_list(Skippable) ->
- {OsFam, OsName} =
- case os:type() of
- {_Fam, _Name} = FamAndName ->
- FamAndName;
- Fam ->
- {Fam, undefined}
- end,
- case lists:member(OsFam, Skippable) of
- true ->
- true;
- false ->
- case lists:keysearch(OsFam, 1, Skippable) of
- {value, {OsFam, OsName}} ->
- true;
- {value, {OsFam, OsNames}} when is_list(OsNames) ->
- lists:member(OsName, OsNames);
- _ ->
- false
- end
- end;
-os_based_skip(_) ->
+ os_base_skip(Skippable, os:type());
+os_based_skip(_Crap) ->
false.
+
+os_base_skip(Skippable, {OsFam, OsName}) ->
+ os_base_skip(Skippable, OsFam, OsName);
+os_base_skip(Skippable, OsFam) ->
+ os_base_skip(Skippable, OsFam, undefined).
+
+os_base_skip(Skippable, OsFam, OsName) ->
+ %% Check if the entire family is to be skipped
+ %% Example: [win32, unix]
+ case lists:member(OsFam, Skippable) of
+ true ->
+ true;
+ false ->
+ %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}]
+ case lists:keysearch(OsFam, 1, Skippable) of
+ {value, {OsFam, OsName}} ->
+ true;
+ {value, {OsFam, OsNames}} when is_list(OsNames) ->
+ %% OsNames is a list of:
+ %% [atom()|{atom(), function/0 | function/1}]
+ case lists:member(OsName, OsNames) of
+ true ->
+ true;
+ false ->
+ os_based_skip_check(OsName, OsNames)
+ end;
+ _ ->
+ false
+ end
+ end.
+
+
+
+%% Performs a check via a provided fun with arity 0 or 1.
+%% The argument is the result of os:version().
+os_based_skip_check(OsName, OsNames) ->
+ case lists:keysearch(OsName, 1, OsNames) of
+ {value, {OsName, Check}} when is_function(Check, 0) ->
+ Check();
+ {value, {OsName, Check}} when is_function(Check, 1) ->
+ Check(os:version());
+ _ ->
+ false
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_timer_test.erl b/lib/megaco/test/megaco_timer_test.erl
index 84c314d8ed..87aa51b8de 100644
--- a/lib/megaco/test/megaco_timer_test.erl
+++ b/lib/megaco/test/megaco_timer_test.erl
@@ -354,18 +354,23 @@ integer_timer_start_and_stop(Config) when is_list(Config) ->
i("starting"),
Timeout = 5000,
- Ref = tmr_start(Timeout),
+ i("try start (~w msec) timer", [Timeout]),
+ Ref = tmr_start(Timeout),
+ i("timer started "),
receive
{timeout, Timeout} ->
+ i("unexpected premature timer expire"),
error(bad_timeout)
after Timeout - 100 ->
+ i("try stop timer"),
case tmr_stop(Ref) of
- ok ->
- ok;
- {ok, _} ->
+ {ok, Rem} ->
+ i("timer stopped with ~w msec remaining", [Rem]),
ok;
CancelRes ->
- ?SKIP({cancel_failed, CancelRes})
+ i("failed stop timer: "
+ "~n ~p", [CancelRes]),
+ ?SKIP({cancel_failed, CancelRes}) % Race - not our problem
end
end,
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 843a3dccc5..381def97d7 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.18.5
+MEGACO_VSN = 3.18.6
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 2d38e4d01c..e918338c8a 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -39,7 +39,36 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.16</title>
+ <section><title>Mnesia 4.16.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>mnesia:add_table_copy/3</c> could cause a deadlock if
+ called when a new node was starting.</p>
+ <p>
+ Own Id: OTP-15933 Aux Id: ERL-872 </p>
+ </item>
+ <item>
+ <p>Transactions with sticky locks could with async_asym
+ transactions be committed in the wrong order, since asym
+ transaction are spawned on the remote nodes.</p> <p>To
+ fix this bug the communication protocol between mnesia
+ nodes had to be updated, thus mnesia will no longer be
+ able to connect to nodes earlier than mnesia-4.14 ,
+ OTP-19.0.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15979 Aux Id: ERL-768 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.16</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index aa5d9adb6d..f1a6333c18 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.16
+MNESIA_VSN = 4.16.1
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index f05e58dc21..f66ab95893 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,34 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug after a user followed link on a pid from an
+ expanded term window.</p>
+ <p>
+ Own Id: OTP-15980 Aux Id: PR-2201 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved dark mode colors on Linux.</p>
+ <p>
+ Own Id: OTP-15916 Aux Id: ERL-921 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.9.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index c16c43f942..3bb316a5e2 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.9.1
+OBSERVER_VSN = 2.9.2
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 1f169263e9..63efa96e2f 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -31,6 +31,29 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>disk_sup</c> to ignore squashfs on Linux when
+ determining if a mounted filesystem is full or not.</p>
+ <p>
+ Own Id: OTP-15778</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where <c>cpu_sup:util()</c> always returned 100%
+ on systems not using gnu libc, for example Alpine OS.</p>
+ <p>
+ Own Id: OTP-15974 Aux Id: ERL-1012 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index 845443d329..6081e181ff 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.5
+OS_MON_VSN = 2.5.1
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 57d9898661..f47988d6d8 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -35,6 +35,33 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 1.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Support Password based encryption with AES</p>
+ <p>
+ Own Id: OTP-15870 Aux Id: ERL-952 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Change dialyzer spec to avoid confusion</p>
+ <p>
+ Own Id: OTP-15843 Aux Id: ERL-915 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 1.6.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index a5e4ec8d5a..1982218574 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 1.6.7
+PUBLIC_KEY_VSN = 1.7
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 1b94c3e6d9..210d63687c 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>dbg:stop_clear/0</c> to also clear trace events
+ (<c>send</c> and <c>'receive'</c>).</p>
+ <p>
+ Own Id: OTP-16044</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.13.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 92938ed5c1..21e180e854 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -604,6 +604,8 @@ stop() ->
stop_clear() ->
{ok, _} = ctp(),
+ {ok, _} = ctpe('receive'),
+ {ok, _} = ctpe('send'),
stop().
%%% Calling the server.
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index 98cbc0360f..4d319c29c1 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -20,7 +20,7 @@
-module(dbg_SUITE).
%% Test functions
--export([all/0, suite/0,
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
big/1, tiny/1, simple/1, message/1, distributed/1, port/1,
send/1, recv/1,
ip_port/1, file_port/1, file_port2/1,
@@ -46,6 +46,12 @@ all() ->
local_trace, saved_patterns, tracer_exit_on_stop,
erl_tracer, distributed_erl_tracer].
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ dbg:stop_clear(),
+ ok.
%% Rudimentary interface test
big(Config) when is_list(Config) ->
@@ -83,7 +89,7 @@ big(Config) when is_list(Config) ->
ok=file:set_cwd(OldCurDir)
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -114,7 +120,7 @@ tiny(Config) when is_list(Config) ->
failure
end
after
- ok = dbg:stop(),
+ dbg:stop_clear(),
ok = file:set_cwd(OldCurDir)
end,
ok.
@@ -130,7 +136,7 @@ simple(Config) when is_list(Config) ->
S = self(),
[{trace,S,call,{dbg,ltp,[]}}] = flush()
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -146,7 +152,7 @@ message(Config) when is_list(Config) ->
ok = dbg:ltp(),
ok = dbg:ln()
after
- stop()
+ dbg:stop_clear()
end,
S = self(),
[{trace,S,call,{dbg,ltp,[]},S},
@@ -229,7 +235,7 @@ send(Config) when is_list(Config) ->
ok
after
- stop()
+ dbg:stop_clear()
end.
send_test(Pid, Pattern, Msg, TraceEvent) ->
@@ -340,7 +346,7 @@ recv(Config) when is_list(Config) ->
ok
after
- stop()
+ dbg:stop_clear()
end.
recv_test(Pid, Pattern, Msg, TraceEvent) ->
@@ -404,8 +410,8 @@ distributed(Config) when is_list(Config) ->
%%
stop()
after
- stop_slave(Node),
- stop()
+ dbg:stop_clear(),
+ stop_slave(Node)
end,
ok.
@@ -455,7 +461,7 @@ local_trace(Config) when is_list(Config) ->
{dbg_SUITE,not_exported,1},
{error,badarith}}] = flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -480,7 +486,7 @@ port(Config) when is_list(Config) ->
{trace,Port,getting_linked,S},
{trace,Port,closed,normal}] = flush()
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -506,7 +512,7 @@ saved_patterns(Config) when is_list(Config) ->
S = self(),
[{trace,S,call,{dbg,ltp,[]},blahonga}] = flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -538,7 +544,7 @@ ip_port(Config) when is_list(Config) ->
[{trace,S,call,{dbg,ltp,[]},S},
{trace,S,call,{dbg,ln,[]},hej}] = flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -554,7 +560,7 @@ ip_port_busy(Config) when is_list(Config) ->
io:format("Error reason = ~p~n", [Reason]),
true = port_close(Port)
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -584,7 +590,7 @@ file_port(Config) when is_list(Config) ->
{trace,S,call,{dbg,ln,[]},hej},
end_of_trace] = flush()
after
- stop(),
+ dbg:stop_clear(),
file:delete(FName)
end,
ok.
@@ -617,7 +623,7 @@ file_port2(Config) when is_list(Config) ->
stop(),
[] = flush()
after
- stop(),
+ dbg:stop_clear(),
file:delete(FName)
end,
ok.
@@ -688,7 +694,7 @@ wrap_port(Config) when is_list(Config) ->
%%
lists:map(fun(F) -> file:delete(F) end, Files)
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -763,7 +769,7 @@ wrap_port_time(Config) when is_list(Config) ->
end_of_trace] = flush(),
lists:map(fun(F) -> file:delete(F) end, Files)
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -789,7 +795,7 @@ with_seq_trace(Config) when is_list(Config) ->
{seq_trace,0,{send,_,Server,S,{dbg,{ok,Tracer}}}}] =
flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -800,7 +806,7 @@ dead_suspend(Config) when is_list(Config) ->
try
survived = run_dead_suspend()
after
- stop()
+ dbg:stop_clear()
end.
run_dead_suspend() ->
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index 3f38574be4..c01dd60009 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.13.3
+RUNTIME_TOOLS_VSN = 1.14
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index a1ae404776..245542242b 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 3.4.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The net module has been split into 'net' (kernel) and
+ prim_net (preloaded).</p>
+ <p>
+ Own Id: OTP-15765</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 3.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index 22a9027b7c..aabe1a904b 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -31,9 +31,13 @@
{<<"^3\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.3$">>,[restart_new_emulator]},
- {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
+ {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.4$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
[{<<"^3\\.2$">>,[restart_new_emulator]},
{<<"^3\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.3$">>,[restart_new_emulator]},
- {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
+ {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.4$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl
index 5429008a5f..d763fad11b 100644
--- a/lib/sasl/test/installer.erl
+++ b/lib/sasl/test/installer.erl
@@ -903,7 +903,7 @@ start_client(TestNode,Client,Sname) ->
receive
{nodeup, Node} ->
wait_started(TestNode,Node)
- after 30000 ->
+ after 60000 ->
?print([{start_client,failed,Node},net_adm:ping(Node)]),
?fail({"cannot start", Node})
end.
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 2ff2f29591..98f96b7170 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -2331,7 +2331,7 @@ reg_print_proc() ->
rh_print() ->
receive
{print, {Module,Line}, [H|T]} ->
- ?t:format("=== ~p:~p - ~p",[Module,Line,H]),
+ ?t:format("=== ~p:~p - ~tp",[Module,Line,H]),
lists:foreach(fun(Term) -> ?t:format(" ~tp",[Term]) end, T),
?t:format("",[]),
rh_print();
diff --git a/lib/sasl/test/rh_test_lib.erl b/lib/sasl/test/rh_test_lib.erl
index dacd8b6b9f..6425fce7a7 100644
--- a/lib/sasl/test/rh_test_lib.erl
+++ b/lib/sasl/test/rh_test_lib.erl
@@ -29,7 +29,7 @@ erlsrv(Erlsrv,Action,Name) ->
erlsrv(Erlsrv,Action,Name,Rest) ->
Cmd = "\"" ++ Erlsrv ++ "\" " ++ atom_to_list(Action) ++ " " ++
Name ++ " " ++ Rest,
- io:format("erlsrv cmd: ~p~n",[Cmd]),
+ io:format("erlsrv cmd: ~tp~n",[Cmd]),
Port = open_port({spawn, Cmd}, [stream, {line, 100}, eof, in]),
Res = recv_prog_output(Port),
case Res of
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index 8838b514da..b13d03dc76 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 3.4
+SASL_VSN = 3.4.1
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 780e0cae76..dd499d1069 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,7 +34,62 @@
</header>
- <section><title>SNMP 5.3</title>
+ <section><title>SNMP 5.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix various minor issues related to Dialyzer. Mostly
+ these are dialyzer warnings, but there was also some
+ minor bugs detected by Dialyzer.</p>
+ <p>
+ Own Id: OTP-15932</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed a dets usage problem detected by dialyzer.</p>
+ <p>
+ Own Id: OTP-10400 Aux Id: kunagi-253 [164] </p>
+ </item>
+ <item>
+ <p>
+ The function snmp:print_version_info() prints various
+ version info. For each module a number of items are
+ printed, such as app vsn and md5 digest. And an attempt
+ was also made to print "compile time". This used to be
+ available in the module_info for each module, but has now
+ been removed.</p>
+ <p>
+ Own Id: OTP-15330</p>
+ </item>
+ <item>
+ <p>
+ The use of the deprecated random module has been replaced
+ the with rand module.</p>
+ <p>
+ Own Id: OTP-15331</p>
+ </item>
+ <item>
+ <p>
+ Removed use of the deprecated function
+ erlang:get_stacktrace(). Instead make use of the 'catch
+ Class:Error:Stacktrace' feature.</p>
+ <p>
+ Own Id: OTP-15332</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl
index 26a0dd0648..429f78abd0 100644
--- a/lib/snmp/src/agent/snmp_generic.erl
+++ b/lib/snmp/src/agent/snmp_generic.erl
@@ -424,8 +424,8 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndGo', RowIndex, Cols) ->
_:_E:_S ->
?vtrace(
"failed construct row (createAndGo): "
- " n Error: ~p"
- " n Stack: ~p",
+ "~n Error: ~p"
+ "~n Stack: ~p",
[_E, _S]),
{noCreation, Col} % Bad RowIndex
end;
@@ -444,8 +444,8 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndWait', RowIndex, Cols) ->
_:_E:_S ->
?vtrace(
"failed construct row (createAndWait): "
- " n Error: ~p"
- " n Stack: ~p",
+ "~n Error: ~p"
+ "~n Stack: ~p",
[_E, _S]),
{noCreation, Col} % Bad RowIndex
end;
diff --git a/lib/snmp/src/agent/snmpa_general_db.erl b/lib/snmp/src/agent/snmpa_general_db.erl
index 431302b6cc..0d4e39f56d 100644
--- a/lib/snmp/src/agent/snmpa_general_db.erl
+++ b/lib/snmp/src/agent/snmpa_general_db.erl
@@ -534,16 +534,16 @@ dets_backup(close, _Cont, _Name, B) ->
ok;
dets_backup(read, Cont1, Name, B) ->
case dets:bchunk(Name, Cont1) of
+ {error, _} = ERROR ->
+ ERROR;
+ '$end_of_table' ->
+ dets:close(B),
+ end_of_input;
{Cont2, Data} ->
F = fun(Arg) ->
dets_backup(Arg, Cont2, Name, B)
end,
- {Data, F};
- '$end_of_table' ->
- dets:close(B),
- end_of_input;
- Error ->
- Error
+ {Data, F}
end.
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index a45cfa9e98..f6416ee2f9 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -3550,7 +3550,8 @@ table_test() ->
?line ?expect1([{NewKeyc5, ?destroy}]),
s([{NewKeyc3, 3}]),
?line ?expect3(?v1_2(noSuchName, inconsistentName), 1, [{NewKeyc3, 3}]),
- otp_1128_test().
+ otp_1128_test(),
+ ok.
%% Req. system group
simple_standard_test() ->
@@ -5030,7 +5031,8 @@ snmpv2_mib_2(Config) when is_list(Config) ->
"then disable auth traps",[]),
try_test(snmpv2_mib_test_finish, [], [{community, "bad community"}]),
- ?LOG("snmpv2_mib_2 -> done",[]).
+ ?LOG("snmpv2_mib_2 -> done", []),
+ ok.
standard_mibs3_cases() ->
@@ -5126,7 +5128,8 @@ snmpv2_mib_a() ->
?line ?expect3(inconsistentValue, 2,
[{[sysLocation, 0], "val3"},
{[snmpSetSerialNo,0], SetSerial}]),
- ?line ["val2"] = get_req(5, [[sysLocation,0]]).
+ ?line ["val2"] = get_req(5, [[sysLocation,0]]),
+ ok.
%%-----------------------------------------------------------------
@@ -5149,6 +5152,7 @@ snmp_community_mib_test() ->
?INF("NOT YET IMPLEMENTED", []),
nyi.
+
%%-----------------------------------------------------------------
%% o Test engine boots / time
%%-----------------------------------------------------------------
@@ -5277,7 +5281,8 @@ snmp_mpd_mib_b() ->
snmp_mpd_mib_c(UnknownPDUHs) ->
?line [UnknownPDUHs2] = get_req(1, [[snmpUnknownPDUHandlers, 0]]),
- ?line UnknownPDUHs2 = UnknownPDUHs + 1.
+ ?line UnknownPDUHs2 = UnknownPDUHs + 1,
+ ok.
snmp_target_mib(suite) -> [];
@@ -5314,6 +5319,7 @@ snmp_notification_mib_test() ->
?INF("NOT YET IMPLEMENTED", []),
nyi.
+
%%-----------------------------------------------------------------
%% o add/delete views and try them
%% o try boundaries
@@ -5661,7 +5667,8 @@ usm_key_change3(OldShaKey, OldDesKey, ShaKey, DesKey) ->
Vbs3 = [{[usmUserAuthKeyChange, NewRowIndex], ShaKeyChange},
{[usmUserPrivKeyChange, NewRowIndex], DesKeyChange}],
s(Vbs3),
- ?line ?expect1(Vbs3).
+ ?line ?expect1(Vbs3),
+ ok.
usm_read() ->
NewRowIndex = [11,"agentEngine", 7, "newUser"],
@@ -5779,7 +5786,8 @@ loop_mib_1(Config) when is_list(Config) ->
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
%% snmpa:verbosity(master_agent,log),
%% snmpa:verbosity(mib_server,silence),
- ?LOG("loop_mib_1 -> done",[]).
+ ?LOG("loop_mib_1 -> done",[]),
+ ok.
loop_mib_2(suite) -> [];
@@ -5808,7 +5816,8 @@ loop_mib_2(Config) when is_list(Config) ->
?line unload_master("SNMP-NOTIFICATION-MIB"),
?line unload_master("SNMP-FRAMEWORK-MIB"),
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
- ?LOG("loop_mib_2 -> done",[]).
+ ?LOG("loop_mib_2 -> done",[]),
+ ok.
loop_mib_3(suite) -> [];
@@ -5833,7 +5842,8 @@ loop_mib_3(Config) when is_list(Config) ->
?line unload_master("SNMP-NOTIFICATION-MIB"),
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
?line unload_master("SNMP-USER-BASED-SM-MIB"),
- ?LOG("loop_mib_3 -> done",[]).
+ ?LOG("loop_mib_3 -> done",[]),
+ ok.
%% Req. As many mibs all possible
@@ -6043,7 +6053,8 @@ otp_1128(Config) when is_list(Config) ->
?line load_master("OLD-SNMPEA-MIB"),
?line init_old(),
try_test(otp_1128_test),
- ?line unload_master("OLD-SNMPEA-MIB").
+ ?line unload_master("OLD-SNMPEA-MIB"),
+ ok.
otp_1128_2(X) -> ?P(otp_1128_2), otp_1128(X).
@@ -6065,7 +6076,8 @@ otp_1128_test() ->
g([NewKeyc5]),
?line ?expect1([{NewKeyc5, ?active}]),
s([{NewKeyc5, ?destroy}]),
- ?line ?expect1([{NewKeyc5, ?destroy}]).
+ ?line ?expect1([{NewKeyc5, ?destroy}]),
+ ok.
%%-----------------------------------------------------------------
@@ -6078,7 +6090,8 @@ otp_1129(Config) when is_list(Config) ->
init_case(Config),
?line load_master("Klas3"),
try_test(otp_1129_i, [node()]),
- ?line unload_master("Klas3").
+ ?line unload_master("Klas3"),
+ ok.
otp_1129_2(X) -> ?P(otp_1129_2), otp_1129(X).
@@ -6149,7 +6162,8 @@ otp_1131_test() ->
io:format("Testing bug reported in ticket OTP-1131...~n"),
s([{[friendsEntry, [2, 3, 1]], s, "kompis3"},
{[friendsEntry, [3, 3, 1]], i, ?createAndGo}]),
- ?line ?expect3(?v1_2(noSuchName, noCreation), 2, any).
+ ?line ?expect3(?v1_2(noSuchName, noCreation), 2, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6170,7 +6184,8 @@ otp_1162_3(X) -> ?P(otp_1162_3), otp_1162(X).
otp_1162_test() ->
s([{[sa, [2,0]], 6}]), % wrongValue (i is_set_ok)
- ?line ?expect3(?v1_2(badValue, wrongValue), 1, any).
+ ?line ?expect3(?v1_2(badValue, wrongValue), 1, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6185,7 +6200,8 @@ otp_1222(Config) when is_list(Config) ->
?line load_master("Klas4"),
try_test(otp_1222_test),
?line unload_master("Klas3"),
- ?line unload_master("Klas4").
+ ?line unload_master("Klas4"),
+ ok.
otp_1222_2(X) -> ?P(otp_1222_2), otp_1222(X).
@@ -6196,7 +6212,8 @@ otp_1222_test() ->
s([{[fStatus4,1], 4}, {[fName4,1], 1}]),
?line ?expect3(genErr, 0, any),
s([{[fStatus4,2], 4}, {[fName4,2], 1}]),
- ?line ?expect3(genErr, 0, any).
+ ?line ?expect3(genErr, 0, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6210,7 +6227,8 @@ otp_1298(Config) when is_list(Config) ->
?line load_master("Klas2"),
try_test(otp_1298_test),
- ?line unload_master("Klas2").
+ ?line unload_master("Klas2"),
+ ok.
otp_1298_2(X) -> ?P(otp_1298_2), otp_1298(X).
@@ -6219,7 +6237,8 @@ otp_1298_3(X) -> ?P(otp_1298_3), otp_1298(X).
otp_1298_test() ->
io:format("Testing bug reported in ticket OTP-1298...~n"),
s([{[fint,0], -1}]),
- ?line ?expect1([{[fint,0], -1}]).
+ ?line ?expect1([{[fint,0], -1}]),
+ ok.
%%-----------------------------------------------------------------
@@ -6233,7 +6252,8 @@ otp_1331(Config) when is_list(Config) ->
?line load_master("OLD-SNMPEA-MIB"),
?line init_old(),
try_test(otp_1331_test),
- ?line unload_master("OLD-SNMPEA-MIB").
+ ?line unload_master("OLD-SNMPEA-MIB"),
+ ok.
otp_1331_2(X) -> ?P(otp_1331_2), otp_1331(X).
@@ -6242,7 +6262,8 @@ otp_1331_3(X) -> ?P(otp_1331_3), otp_1331(X).
otp_1331_test() ->
NewKeyc5 = [intCommunityStatus,[127,32,0,0],is("test")],
s([{NewKeyc5, ?destroy}]),
- ?line ?expect1([{NewKeyc5, ?destroy}]).
+ ?line ?expect1([{NewKeyc5, ?destroy}]),
+ ok.
%%-----------------------------------------------------------------
@@ -6280,7 +6301,8 @@ otp_1342(Config) when is_list(Config) ->
init_case(Config),
?line load_master("Klas4"),
try_test(otp_1342_test),
- ?line unload_master("Klas4").
+ ?line unload_master("Klas4"),
+ ok.
otp_1342_2(X) -> ?P(otp_1342_2), otp_1342(X).
@@ -6290,7 +6312,8 @@ otp_1342_test() ->
s([{[fIndex5, 1], i, 1},
{[fName5, 1], i, 3},
{[fStatus5, 1], i, ?createAndGo}]),
- ?line ?expect3(?v1_2(noSuchName, noCreation), 3, any).
+ ?line ?expect3(?v1_2(noSuchName, noCreation), 3, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6306,7 +6329,8 @@ otp_1366(Config) when is_list(Config) ->
?line load_master("OLD-SNMPEA-MIB"),
?line init_old(),
try_test(otp_1366_test),
- ?line unload_master("OLD-SNMPEA-MIB").
+ ?line unload_master("OLD-SNMPEA-MIB"),
+ ok.
otp_1366_2(X) -> ?P(otp_1366_2), otp_1366(X).
@@ -6404,7 +6428,8 @@ otp_2979_3(X) -> ?P(otp_2979_3), otp_2979(X).
otp_2979_test() ->
gn([[sparseDescr], [sparseStatus]]),
?line ?expect1([{[sparseStr,0], "slut"},
- {[sparseStr,0], "slut"}]).
+ {[sparseStr,0], "slut"}]),
+ ok.
%%-----------------------------------------------------------------
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index c31bb92e1f..e2356bd70a 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -68,35 +68,26 @@
info/1,
- simple_sync_get1/1,
simple_sync_get2/1,
simple_sync_get3/1,
- simple_async_get1/1,
simple_async_get2/1,
simple_async_get3/1,
- simple_sync_get_next1/1,
simple_sync_get_next2/1,
simple_sync_get_next3/1,
- simple_async_get_next1/1,
simple_async_get_next2/1,
simple_async_get_next3/1,
- simple_sync_set1/1,
simple_sync_set2/1,
simple_sync_set3/1,
- simple_async_set1/1,
simple_async_set2/1,
simple_async_set3/1,
- simple_sync_get_bulk1/1,
simple_sync_get_bulk2/1,
simple_sync_get_bulk3/1,
- simple_async_get_bulk1/1,
simple_async_get_bulk2/1,
simple_async_get_bulk3/1,
- misc_async1/1,
misc_async2/1,
discovery/1,
@@ -163,6 +154,10 @@ init_per_suite(Config0) when is_list(Config0) ->
%% until we have a more important reason to fix this.
case snmp_test_lib:crypto_start() of
ok ->
+
+ snmp_test_global_sys_monitor:start(),
+ snmp_test_sys_monitor:start(), % We need one on this node also
+
Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0),
Config2 = snmp_test_lib:fix_data_dir(Config1),
%% Mib-dirs
@@ -181,6 +176,9 @@ end_per_suite(Config) when is_list(Config) ->
?DBG("end_per_suite -> entry with"
"~n Config: ~p", [Config]),
+ snmp_test_sys_monitor:stop(),
+ snmp_test_global_sys_monitor:stop(),
+
Config.
@@ -190,15 +188,6 @@ init_per_testcase(Case, Config) when is_list(Config) ->
%% This version of the API, based on Addr and Port, has been deprecated
DeprecatedApiCases =
[
- simple_sync_get1,
- simple_async_get1,
- simple_sync_get_next1,
- simple_async_get_next1,
- simple_sync_set1,
- simple_async_set1,
- simple_sync_get_bulk1,
- simple_async_get_bulk1,
- misc_async1
],
Result =
case lists:member(Case, DeprecatedApiCases) of
@@ -505,47 +494,38 @@ groups() ->
},
{get_tests, [],
[
- simple_sync_get1,
simple_sync_get2,
simple_sync_get3,
- simple_async_get1,
simple_async_get2,
simple_async_get3
]
},
{get_next_tests, [],
[
- simple_sync_get_next1,
simple_sync_get_next2,
simple_sync_get_next3,
- simple_async_get_next1,
simple_async_get_next2,
simple_async_get_next3
]
},
{set_tests, [],
[
- simple_sync_set1,
simple_sync_set2,
simple_sync_set3,
- simple_async_set1,
simple_async_set2,
simple_async_set3
]
},
{bulk_tests, [],
[
- simple_sync_get_bulk1,
simple_sync_get_bulk2,
simple_sync_get_bulk3,
- simple_async_get_bulk1,
simple_async_get_bulk2,
simple_async_get_bulk3
]
},
{misc_request_tests, [],
[
- misc_async1,
misc_async2
]
},
@@ -1806,68 +1786,6 @@ do_register_agent3(Config) ->
%%======================================================================
-simple_sync_get1(doc) -> ["Simple sync get-request - Old style (Addr & Port)"];
-simple_sync_get1(suite) -> [];
-simple_sync_get1(Config) when is_list(Config) ->
- ?TC_TRY(simple_sync_get1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_sync_get1(Config) end).
-
-do_simple_sync_get1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- Node = ?config(manager_node, Config),
- Addr = ?config(manager_agent_target_name, Config),
- Port = ?AGENT_PORT,
-
- p("issue get-request without loading the mib"),
- Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance],
- ?line ok = do_simple_sync_get(Node, Addr, Port, Oids1),
-
- p("issue get-request after first loading the mibs"),
- ?line ok = mgr_user_load_mib(Node, std_mib()),
- Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]],
- ?line ok = do_simple_sync_get(Node, Addr, Port, Oids2),
-
- p("Display log"),
- display_log(Config),
-
- p("done"),
- ok.
-
-do_simple_sync_get(Node, Addr, Port, Oids) ->
- ?line {ok, Reply, _Rem} = mgr_user_sync_get(Node, Addr, Port, Oids),
-
- ?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
-
- %% verify that the operation actually worked:
- %% The order should be the same, so no need to seach
- ?line ok = case Reply of
- {noError, 0, [#varbind{oid = ?sysObjectID_instance,
- value = SysObjectID},
- #varbind{oid = ?sysDescr_instance,
- value = SysDescr},
- #varbind{oid = ?sysUpTime_instance,
- value = SysUpTime}]} ->
- p("expected result from get: "
- "~n SysObjectID: ~p"
- "~n SysDescr: ~s"
- "~n SysUpTime: ~w",
- [SysObjectID, SysDescr, SysUpTime]),
- ok;
- {noError, 0, Vbs} ->
- p("unexpected varbinds: ~n~p", [Vbs]),
- {error, {unexpected_vbs, Vbs}};
- Else ->
- p("unexpected reply: ~n~p", [Else]),
- {error, {unexpected_response, Else}}
- end,
- ok.
-
-
-%%======================================================================
-
simple_sync_get2(doc) ->
["Simple sync get-request - Version 2 API (TargetName)"];
simple_sync_get2(suite) -> [];
@@ -1967,79 +1885,9 @@ do_simple_sync_get3(Config) ->
ok.
-%%======================================================================
-
-simple_async_get1(doc) ->
- ["Simple (async) get-request - Old style (Addr & Port)"];
-simple_async_get1(suite) -> [];
-simple_async_get1(Config) when is_list(Config) ->
- ?TC_TRY(simple_async_get1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_async_get1(Config) end).
-
-do_simple_async_get1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(Data) ->
- async_g_exec1(MgrNode, Addr, Port, Data)
- end,
-
- Requests =
- [
- { 1,
- [?sysObjectID_instance],
- Exec,
- fun(X) -> sag_verify(X, [?sysObjectID_instance]) end },
- { 2,
- [?sysDescr_instance, ?sysUpTime_instance],
- Exec,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysUpTime_instance])
- end },
- { 3,
- [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]],
- Exec,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance])
- end },
- { 4,
- [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance],
- Exec,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance])
- end }
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
- display_log(Config),
- ok.
-async_g_exec1(Node, Addr, Port, Oids) ->
- mgr_user_async_get(Node, Addr, Port, Oids).
+%%======================================================================
sag_verify({noError, 0, _Vbs}, any) ->
p("verified [any]"),
@@ -2191,114 +2039,6 @@ async_g_exec3(Node, TargetName, Oids, SendOpts) ->
%%======================================================================
-simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - "
- "Old style (Addr & Port)"];
-simple_sync_get_next1(suite) -> [];
-simple_sync_get_next1(Config) when is_list(Config) ->
- ?TC_TRY(simple_sync_get_next1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_sync_get_next1(Config) end).
-
-do_simple_sync_get_next1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- %% -- 1 --
- Oids01 = [[1,3,7,1]],
- VF01 = fun(X) -> verify_ssgn_reply1(X, [{[1,3,7,1],endOfMibView}]) end,
- ?line ok = do_simple_get_next(1,
- MgrNode, Addr, Port, Oids01, VF01),
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
-
- %% -- 2 --
- Oids02 = [[sysDescr], [1,3,7,1]],
- VF02 = fun(X) ->
- verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_next(2,
- MgrNode, Addr, Port, Oids02, VF02),
-
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- %% -- 3 --
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- Oids03 = [[TCnt2, 1]],
- VF03 = fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}])
- end,
- ?line ok = do_simple_get_next(3,
- MgrNode, Addr, Port, Oids03, VF03),
-
- %% -- 4 --
- Oids04 = [[TCnt2, 2]],
- VF04 = fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}])
- end,
- ?line ok = do_simple_get_next(4,
- MgrNode, Addr, Port, Oids04, VF04),
-
- %% -- 5 --
- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1),
- Oids05 = [TGenErr1],
- VF05 = fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})
- end,
- ?line ok = do_simple_get_next(5,
- MgrNode, Addr, Port, Oids05, VF05),
-
- %% -- 6 --
- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2),
- Oids06 = [TGenErr2],
- VF06 = fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})
- end,
- ?line ok = do_simple_get_next(6,
- MgrNode, Addr, Port, Oids06, VF06),
-
- %% -- 7 --
- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3),
- Oids07 = [[sysDescr], TGenErr3],
- VF07 = fun(X) ->
- verify_ssgn_reply2(X, {genErr, 2,
- [?sysDescr, TGenErr3]})
- end,
- ?line ok = do_simple_get_next(7,
- MgrNode, Addr, Port, Oids07, VF07),
-
- %% -- 8 --
- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig),
- Oids08 = [TTooBig],
- VF08 = fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end,
- ?line ok = do_simple_get_next(8,
- MgrNode, Addr, Port, Oids08, VF08),
-
- display_log(Config),
- ok.
-
-
-do_simple_get_next(N, Node, Addr, Port, Oids, Verify) ->
- p("issue get-next command ~w", [N]),
- case mgr_user_sync_get_next(Node, Addr, Port, Oids) of
- {ok, Reply, _Rem} ->
- ?DBG("get-next ok:"
- "~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
- Verify(Reply);
-
- Error ->
- {error, {unexpected_reply, Error}}
- end.
-
-
verify_ssgn_reply1({noError, 0, _Vbs}, any) ->
ok;
verify_ssgn_reply1({noError, 0, Vbs}, Expected) ->
@@ -2488,107 +2228,6 @@ simple_sync_get_next3(Config) when is_list(Config) ->
%%======================================================================
-simple_async_get_next1(doc) -> ["Simple (async) get_next-request - "
- "Old style (Addr & Port)"];
-simple_async_get_next1(suite) -> [];
-simple_async_get_next1(Config) when is_list(Config) ->
- ?TC_TRY(simple_async_get_next1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_async_get_next1(Config) end).
-
-do_simple_async_get_next1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(X) ->
- async_gn_exec1(MgrNode, Addr, Port, X)
- end,
-
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1),
- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2),
- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3),
- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig),
-
- Requests =
- [
- {1,
- [[1,3,7,1]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [{[1,3,7,1], endOfMibView}])
- end},
- {2,
- [[sysDescr], [1,3,7,1]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])
- end},
- {3,
- [[TCnt2, 1]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}])
- end},
- {4,
- [[TCnt2, 2]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}])
- end},
- {5,
- [TGenErr1],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})
- end},
- {6,
- [TGenErr2],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})
- end},
- {7,
- [[sysDescr], TGenErr3],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]})
- end},
- {8,
- [TTooBig],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-async_gn_exec1(Node, Addr, Port, Oids) ->
- mgr_user_async_get_next(Node, Addr, Port, Oids).
-
-
-%%======================================================================
-
simple_async_get_next2(doc) ->
["Simple (async) get_next-request - Version 2 API (TargetName)"];
simple_async_get_next2(suite) -> [];
@@ -2747,67 +2386,6 @@ async_gn_exec3(Node, TargetName, Oids, SendOpts) ->
%%======================================================================
-simple_sync_set1(doc) -> ["Simple (sync) set-request - "
- "Old style (Addr & Port)"];
-simple_sync_set1(suite) -> [];
-simple_sync_set1(Config) when is_list(Config) ->
- ?TC_TRY(simple_sync_set1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_sync_set1(Config) end).
-
-do_simple_sync_set1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- Node = ?config(manager_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- p("issue set-request without loading the mib"),
- Val11 = "Arne Anka",
- Val12 = "Stockholm",
- VAVs1 = [
- {?sysName_instance, s, Val11},
- {?sysLocation_instance, s, Val12}
- ],
- ?line ok = do_simple_set1(Node, Addr, Port, VAVs1),
-
- p("issue set-request after first loading the mibs"),
- ?line ok = mgr_user_load_mib(Node, std_mib()),
- Val21 = "Sune Anka",
- Val22 = "Gothenburg",
- VAVs2 = [
- {[sysName, 0], Val21},
- {[sysLocation, 0], Val22}
- ],
- ?line ok = do_simple_set1(Node, Addr, Port, VAVs2),
-
- display_log(Config),
- ok.
-
-do_simple_set1(Node, Addr, Port, VAVs) ->
- [SysName, SysLoc] = value_of_vavs(VAVs),
- ?line {ok, Reply, _Rem} = mgr_user_sync_set(Node, Addr, Port, VAVs),
-
- ?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
-
- %% verify that the operation actually worked:
- %% The order should be the same, so no need to seach
- %% The value we get should be exactly the same as we sent
- ?line ok = case Reply of
- {noError, 0, [#varbind{oid = ?sysName_instance,
- value = SysName},
- #varbind{oid = ?sysLocation_instance,
- value = SysLoc}]} ->
- ok;
- {noError, 0, Vbs} ->
- {error, {unexpected_vbs, Vbs}};
- Else ->
- p("unexpected reply: ~n~p", [Else]),
- {error, {unexpected_response, Else}}
- end,
- ok.
-
value_of_vavs(VAVs) ->
value_of_vavs(VAVs, []).
@@ -2924,70 +2502,6 @@ do_simple_sync_set3(Config) ->
%%======================================================================
-simple_async_set1(doc) -> ["Simple (async) set-request - "
- "Old style (Addr & Port)"];
-simple_async_set1(suite) -> [];
-simple_async_set1(Config) when is_list(Config) ->
- ?TC_TRY(simple_async_set1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_async_set1(Config) end).
-
-do_simple_async_set1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(X) ->
- async_s_exec1(MgrNode, Addr, Port, X)
- end,
-
- Requests =
- [
- {1,
- [{?sysName_instance, s, "Arne Anka"}],
- Exec,
- fun(X) ->
- sas_verify(X, [?sysName_instance])
- end},
- {2,
- [{?sysLocation_instance, s, "Stockholm"},
- {?sysName_instance, s, "Arne Anka"}],
- Exec,
- fun(X) ->
- sas_verify(X, [?sysLocation_instance, ?sysName_instance])
- end},
- {3,
- [{[sysName, 0], "Gothenburg"},
- {[sysLocation, 0], "Sune Anka"}],
- Exec,
- fun(X) ->
- sas_verify(X, [?sysName_instance, ?sysLocation_instance])
- end}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-async_s_exec1(Node, Addr, Port, VAVs) ->
- mgr_user_async_set(Node, Addr, Port, VAVs).
-
sas_verify({noError, 0, _Vbs}, any) ->
p("verified [any]"),
ok;
@@ -3141,146 +2655,9 @@ async_s_exec3(Node, TargetName, VAVs, SendOpts) ->
%%======================================================================
-simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - "
- "Old style (Addr & Port)"];
-simple_sync_get_bulk1(suite) -> [];
-simple_sync_get_bulk1(Config) when is_list(Config) ->
- ?TC_TRY(simple_sync_get_bulk1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_sync_get_bulk1(Config) end).
-
-do_simple_sync_get_bulk1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- %% -- 1 --
- ?line ok = do_simple_get_bulk1(1,
- MgrNode, Addr, Port, 1, 1, [],
- fun verify_ssgb_reply1/1),
-
- %% -- 2 --
- ?line ok = do_simple_get_bulk1(2,
- MgrNode, Addr, Port, -1, 1, [],
- fun verify_ssgb_reply1/1),
-
- %% -- 3 --
- ?line ok = do_simple_get_bulk1(3,
- MgrNode, Addr, Port, -1, -1, [],
- fun verify_ssgb_reply1/1),
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- %% -- 4 --
- VF04 = fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_bulk1(4,
- MgrNode, Addr, Port,
- 2, 0, [[sysDescr],[1,3,7,1]], VF04),
-
- %% -- 5 --
- ?line ok = do_simple_get_bulk1(5,
- MgrNode, Addr, Port,
- 1, 2, [[sysDescr],[1,3,7,1]], VF04),
-
- %% -- 6 --
- VF06 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_bulk1(6,
- MgrNode, Addr, Port,
- 0, 2, [[sysDescr],[1,3,7,1]], VF06),
-
- %% -- 7 --
- VF07 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_bulk1(7,
- MgrNode, Addr, Port,
- 2, 2,
- [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]],
- VF07),
-
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- %% -- 8 --
- VF08 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance,
- ?sysDescr_instance])
- end,
- ?line ok = do_simple_get_bulk1(8,
- MgrNode, Addr, Port,
- 1, 2,
- [[sysDescr],[sysDescr],[tTooBig]],
- VF08),
-
- %% -- 9 --
- ?line ok = do_simple_get_bulk1(9,
- MgrNode, Addr, Port,
- 1, 12,
- [[tDescr2], [sysDescr]],
- fun verify_ssgb_reply1/1),
-
- %% -- 10 --
- VF10 = fun(X) ->
- verify_ssgb_reply3(X,
- [{?sysDescr, 'NULL'},
- {?sysObjectID, 'NULL'},
- {?tGenErr1, 'NULL'},
- {?sysDescr, 'NULL'}])
- end,
- ?line ok = do_simple_get_bulk1(10,
- MgrNode, Addr, Port,
- 2, 2,
- [[sysDescr],
- [sysObjectID],
- [tGenErr1],
- [sysDescr]],
- VF10),
-
- %% -- 11 --
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- p("TCnt2: ~p", [TCnt2]),
- VF11 = fun(X) ->
- verify_ssgb_reply2(X,
- [{fl([TCnt2,2]), 100},
- {fl([TCnt2,2]), endOfMibView}])
- end,
- ?line ok = do_simple_get_bulk1(11,
- MgrNode, Addr, Port,
- 0, 2,
- [[TCnt2, 1]], VF11),
-
- display_log(Config),
- ok.
-
fl(L) ->
lists:flatten(L).
-do_simple_get_bulk1(N, Node, Addr, Port, NonRep, MaxRep, Oids, Verify) ->
- p("issue get-bulk command ~w", [N]),
- case mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) of
- {ok, Reply, _Rem} ->
- ?DBG("get-bulk ok:"
- "~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
- Verify(Reply);
-
- Error ->
- {error, {unexpected_reply, Error}}
- end.
-
verify_ssgb_reply1({noError, 0, []}) ->
ok;
verify_ssgb_reply1(X) ->
@@ -3313,7 +2690,6 @@ check_ssgb_vbs([#varbind{oid = Oid, value = Value}|R],
check_ssgb_vbs([R|_], [E|_]) ->
{error, {unexpected_vb, R, E}}.
-
%%======================================================================
simple_sync_get_bulk2(doc) ->
@@ -3513,153 +2889,6 @@ do_simple_sync_get_bulk3(Config) ->
%%======================================================================
-simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - "
- "Old style (Addr & Port)"];
-simple_async_get_bulk1(suite) -> [];
-simple_async_get_bulk1(Config) when is_list(Config) ->
- ?TC_TRY(simple_async_get_bulk1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_simple_async_get_bulk1(Config) end).
-
-do_simple_async_get_bulk1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(Data) ->
- async_gb_exec1(MgrNode, Addr, Port, Data)
- end,
-
- %% We re-use the verification functions from the ssgb test-case
- VF04 = fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end,
- VF06 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- VF07 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- VF08 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance,
- ?sysDescr_instance])
- end,
- VF10 = fun(X) ->
- verify_ssgb_reply3(X,
- [{?sysDescr, 'NULL'},
- {?sysObjectID, 'NULL'},
- {?tGenErr1, 'NULL'},
- {?sysDescr, 'NULL'}])
- end,
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- VF11 = fun(X) ->
- verify_ssgb_reply2(X,
- [{fl([TCnt2,2]), 100},
- {fl([TCnt2,2]), endOfMibView}])
- end,
- Requests = [
- { 1,
- {1, 1, []},
- Exec,
- fun verify_ssgb_reply1/1},
- { 2,
- {-1, 1, []},
- Exec,
- fun verify_ssgb_reply1/1},
- { 3,
- {-1, -1, []},
- Exec,
- fun verify_ssgb_reply1/1},
- { 4,
- {2, 0, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF04},
- { 5,
- {1, 2, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF04},
- { 6,
- {0, 2, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF06},
- { 7,
- {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]},
- Exec,
- VF07},
- { 8,
- {1, 2, [[sysDescr],[sysDescr],[tTooBig]]},
- Exec,
- VF08},
- { 9,
- {1, 12, [[tDescr2], [sysDescr]]},
- Exec,
- fun verify_ssgb_reply1/1},
- {10,
- {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]},
- Exec,
- VF10},
- {11,
- {0, 2, [[TCnt2, 1]]},
- Exec,
- VF11},
- {12,
- {2, 0, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF04},
- {13,
- {1, 12, [[tDescr2], [sysDescr]]},
- Exec,
- fun verify_ssgb_reply1/1},
- {14,
- {2, 2, [[sysDescr],[sysObjectID],[tGenErr1],[sysDescr]]},
- Exec,
- VF10},
- {15,
- {0, 2, [[TCnt2, 1]]},
- Exec,
- VF11},
- {16,
- {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]},
- Exec,
- VF07},
- {17,
- {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]},
- Exec,
- VF10}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-async_gb_exec1(Node, Addr, Port, {NR, MR, Oids}) ->
- mgr_user_async_get_bulk(Node, Addr, Port, NR, MR, Oids).
-
-
-%%======================================================================
-
simple_async_get_bulk2(doc) ->
["Simple (async) get_bulk-request - Version 2 API (TargetName)"];
simple_async_get_bulk2(suite) -> [];
@@ -3864,199 +3093,6 @@ async_gb_exec3(Node, TargetName, {NR, MR, Oids}, SendOpts) ->
%%======================================================================
-misc_async1(doc) -> ["Misc (async) request(s) - "
- "Old style (Addr & Port)"];
-misc_async1(suite) -> [];
-misc_async1(Config) when is_list(Config) ->
- ?TC_TRY(misc_async1,
- fun() -> {skip, "API no longer supported"} end,
- fun() -> do_misc_async1(Config) end).
-
-do_misc_async1(Config) ->
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- ExecG = fun(Data) ->
- async_g_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ExecGN = fun(Data) ->
- async_gn_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ExecS = fun(Data) ->
- async_s_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ExecGB = fun(Data) ->
- async_gb_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1),
- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2),
- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3),
- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig),
-
- Requests =
- [
- { 1,
- [?sysObjectID_instance],
- ExecG,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance])
- end
- },
- { 2,
- {1, 1, []},
- ExecGB,
- fun verify_ssgb_reply1/1},
- { 3,
- {-1, 1, []},
- ExecGB,
- fun verify_ssgb_reply1/1},
- { 4,
- [{?sysLocation_instance, s, "Stockholm"},
- {?sysName_instance, s, "Arne Anka"}],
- ExecS,
- fun(X) ->
- sas_verify(X, [?sysLocation_instance, ?sysName_instance])
- end},
- { 5,
- [[sysDescr], [1,3,7,1]],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])
- end},
- { 6,
- [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]],
- ExecG,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance])
- end},
- { 7,
- [TGenErr2],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})
- end},
- { 8,
- {2, 0, [[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end},
- { 9,
- {1, 2, [[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end},
- {10,
- [TGenErr1],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})
- end},
- {11,
- {0, 2, [[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end},
- {12,
- [{[sysName, 0], "Gothenburg"},
- {[sysLocation, 0], "Sune Anka"}],
- ExecS,
- fun(X) ->
- sas_verify(X, [?sysName_instance, ?sysLocation_instance])
- end},
- {13,
- {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end},
- {14,
- {1, 2, [[sysDescr],[sysDescr],[tTooBig]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance,
- ?sysDescr_instance])
- end},
- {15,
- {1, 12, [[tDescr2], [sysDescr]]},
- ExecGB,
- fun verify_ssgb_reply1/1},
- {16,
- {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply3(X,
- [{?sysDescr, 'NULL'},
- {?sysObjectID, 'NULL'},
- {?tGenErr1, 'NULL'},
- {?sysDescr, 'NULL'}])
- end},
- {17,
- [[sysDescr], TGenErr3],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]})
- end},
- {18,
- {0, 2, [[TCnt2, 1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [{fl([TCnt2,2]), 100},
- {fl([TCnt2,2]), endOfMibView}])
- end},
- {19,
- [TTooBig],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end},
- {20,
- [TTooBig],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-%%======================================================================
-
misc_async2(doc) ->
["Misc (async) request(s) - Version 2 API (TargetName)"];
misc_async2(suite) -> [];
@@ -6114,40 +5150,21 @@ mgr_user_stop(Node) ->
mgr_user_register_agent(Node, TargetName, Conf)
when is_list(TargetName) andalso is_list(Conf) ->
rcall(Node, snmp_manager_user, register_agent, [TargetName, Conf]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_register_agent(Node, Addr, Port) ->
-%% mgr_user_register_agent(Node, Addr, Port, []).
-%% mgr_user_register_agent(Node, Addr, Port, Conf) ->
-%% rcall(Node, snmp_manager_user, register_agent, [Addr, Port, Conf]).
-%% </REMOVED-IN-R16B>
%% mgr_user_unregister_agent(Node) ->
%% mgr_user_unregister_agent(Node, ?LOCALHOST(), ?AGENT_PORT).
mgr_user_unregister_agent(Node, TargetName) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, unregister_agent, [TargetName]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_unregister_agent(Node, Addr, Port) ->
-%% rcall(Node, snmp_manager_user, unregister_agent, [Addr, Port]).
-%% </REMOVED-IN-R16B>
mgr_user_agent_info(Node, TargetName, Item)
when is_list(TargetName) andalso is_atom(Item) ->
rcall(Node, snmp_manager_user, agent_info, [TargetName, Item]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_agent_info(Node, Addr, Port, Item) when is_atom(Item) ->
-%% rcall(Node, snmp_manager_user, agent_info, [Addr, Port, Item]).
-%% </REMOVED-IN-R16B>
%% mgr_user_update_agent_info(Node, Item, Val) when atom(Item) ->
%% mgr_user_update_agent_info(Node, ?LOCALHOST(), ?AGENT_PORT, Item, Val).
mgr_user_update_agent_info(Node, TargetName, Item, Val)
when is_list(TargetName) andalso is_atom(Item) ->
rcall(Node, snmp_manager_user, update_agent_info, [TargetName, Item, Val]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_update_agent_info(Node, Addr, Port, Item, Val) when is_atom(Item) ->
-%% rcall(Node, snmp_manager_user, update_agent_info,
-%% [Addr, Port, Item, Val]).
-%% </REMOVED-IN-R16B>
%% mgr_user_which_all_agents(Node) ->
%% rcall(Node, snmp_manager_user, which_all_agents, []).
@@ -6162,10 +5179,6 @@ mgr_user_load_mib(Node, Mib) ->
%% mgr_user_sync_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_sync_get(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_get(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, sync_get, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get2, [TargetName, Oids, SendOpts]).
@@ -6174,10 +5187,6 @@ mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) when is_list(TargetName) ->
%% mgr_user_async_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_async_get(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_get(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, async_get, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_async_get2(Node, TargetName, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6187,10 +5196,6 @@ mgr_user_async_get2(Node, TargetName, Oids, SendOpts)
%% mgr_user_sync_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_sync_get_next(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_next, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_get_next(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, sync_get_next, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6200,10 +5205,6 @@ mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts)
%% mgr_user_async_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_async_get_next(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_next, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_get_next(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, async_get_next, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6213,10 +5214,6 @@ mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts)
%% mgr_user_sync_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV).
mgr_user_sync_set(Node, TargetName, VAV) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_set, [TargetName, VAV]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_set(Node, Addr, Port, VAV) ->
- rcall(Node, snmp_manager_user, sync_set, [Addr, Port, VAV]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_set2, [TargetName, VAV, SendOpts]).
@@ -6225,10 +5222,6 @@ mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
%% mgr_user_async_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV).
mgr_user_async_set(Node, TargetName, VAV) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_set, [TargetName, VAV]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_set(Node, Addr, Port, VAV) ->
- rcall(Node, snmp_manager_user, async_set, [Addr, Port, VAV]).
-%% </REMOVED-IN-R16B>
mgr_user_async_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_set2, [TargetName, VAV, SendOpts]).
@@ -6240,11 +5233,6 @@ mgr_user_sync_get_bulk(Node, TargetName, NonRep, MaxRep, Oids)
when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_bulk,
[TargetName, NonRep, MaxRep, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) ->
- rcall(Node, snmp_manager_user, sync_get_bulk,
- [Addr, Port, NonRep, MaxRep, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6258,11 +5246,6 @@ mgr_user_async_get_bulk(Node, TargetName, NonRep, MaxRep, Oids)
when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_bulk,
[TargetName, NonRep, MaxRep, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) ->
- rcall(Node, snmp_manager_user, async_get_bulk,
- [Addr, Port, NonRep, MaxRep, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6436,9 +5419,12 @@ start_node(Name, Retry) ->
error ->
""
end,
- A = Args ++ " -pa " ++ Pa,
+ A = Args ++ " -pa " ++ Pa ++
+ " -s " ++ atom_to_list(snmp_test_sys_monitor) ++ " start" ++
+ " -s global sync",
try ?START_NODE(Name, A) of
{ok, Node} ->
+ global:sync(),
Node;
{error, timeout} ->
e("Failed starting node ~p: timeout", [Name]),
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 26b68501e2..a040c4f39c 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -80,22 +80,42 @@ tc_try(Case, TCCondFun, TCFun)
tc_end( f("skipping(catched,~w,tc)", [C]) ),
SKIP;
C:E:S ->
- tc_end( f("failed(catched,~w,tc)", [C]) ),
- erlang:raise(C, E, S)
+ %% We always check the system events before we accept a failure
+ case snmp_test_global_sys_monitor:events() of
+ [] ->
+ tc_end( f("failed(catched,~w,tc)", [C]) ),
+ erlang:raise(C, E, S);
+ SysEvs ->
+ tc_print("System Events received: "
+ "~n ~p", [SysEvs], "", ""),
+ tc_end( f("skipping(catched-sysevs,~w,tc)", [C]) ),
+ SKIP = {skip, "TC failure with system events"},
+ SKIP
+ end
end;
{skip, _} = SKIP ->
- tc_end("skipping(tc)"),
+ tc_end("skipping(cond)"),
SKIP;
{error, Reason} ->
- tc_end("failed(tc)"),
+ tc_end("failed(cond)"),
exit({tc_cond_failed, Reason})
catch
C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) ->
tc_end( f("skipping(catched,~w,cond)", [C]) ),
SKIP;
C:E:S ->
- tc_end( f("failed(catched,~w,cond)", [C]) ),
- erlang:raise(C, E, S)
+ %% We always check the system events before we accept a failure
+ case snmp_test_global_sys_monitor:events() of
+ [] ->
+ tc_end( f("failed(catched,~w,cond)", [C]) ),
+ erlang:raise(C, E, S);
+ SysEvs ->
+ tc_print("System Events received: "
+ "~n ~p", [SysEvs], "", ""),
+ tc_end( f("skipping(catched-sysevs,~w,cond)", [C]) ),
+ SKIP = {skip, "TC cond failure with system events"},
+ SKIP
+ end
end.
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 9d6be65088..f50147a852 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -700,18 +700,30 @@ echo_errors({error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}})->
echo_errors(ok) -> ok;
echo_errors({ok, Val}) -> {ok, Val}.
-get_response_impl(Id, Vars) ->
+get_response_impl(Id, ExpVars) ->
+ ?PRINT2("await response ~w with"
+ "~n Expected Varbinds: ~p",
+ [Id, ExpVars]),
+ PureVars = find_pure_oids2(ExpVars),
case receive_response() of
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- match_vars(Id, find_pure_oids2(Vars), VBs, []);
+ ?PRINT2("received expected response pdu (~w) - match vars"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVars, VBs]),
+ match_vars(Id, PureVars, VBs, []);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
+ ?EPRINT2("received unexpected response pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error,
Id,
{"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
@@ -720,6 +732,8 @@ get_response_impl(Id, Vars) ->
[Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT2("unexpected receive pdu error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
@@ -729,171 +743,208 @@ get_response_impl(Id, Vars) ->
%% Returns: ok | {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}}
%%----------------------------------------------------------------------
expect_impl(Id, any) ->
- io:format("expect_impl(~w, any) -> entry ~n", [Id]),
+ ?PRINT2("await ~w pdu (any)", [Id]),
case receive_response() of
- PDU when is_record(PDU, pdu) -> ok;
- {error, Reason} -> format_reason(Id, Reason)
+ PDU when is_record(PDU, pdu) ->
+ ?PRINT2("received expected pdu (~w)", [Id]),
+ ok;
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
+ format_reason(Id, Reason)
end;
expect_impl(Id, return) ->
- io:format("expect_impl(~w, return) -> entry ~n", [Id]),
+ ?PRINT2("await ~w pdu", [Id]),
case receive_response() of
- PDU when is_record(PDU, pdu) -> {ok, PDU};
- {error, Reason} -> format_reason(Id, Reason)
+ PDU when is_record(PDU, pdu) ->
+ ?PRINT2("received expected pdu (~w)", [Id]),
+ {ok, PDU};
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
+ format_reason(Id, Reason)
end;
expect_impl(Id, trap) ->
- io:format("expect_impl(~w, trap) -> entry ~n", [Id]),
+ ?PRINT2("await ~w trap", [Id]),
case receive_trap(3500) of
- PDU when is_record(PDU, trappdu) -> ok;
- {error, Reason} -> format_reason(Id, Reason)
+ PDU when is_record(PDU, trappdu) ->
+ ?PRINT2("received expected trap (~w)", [Id]),
+ ok;
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
+ format_reason(Id, Reason)
end;
expect_impl(Id, timeout) ->
- io:format("expect_impl(~w, timeout) -> entry ~n", [Id]),
+ ?PRINT2("await ~w nothing", [Id]),
receive
X ->
- io:format("expect_impl(~w, timeout) -> "
- "received unexpected message: ~n~p~n", [Id, X]),
+ ?EPRINT1("received unexpected message: ~w"
+ "~n ~p",
+ [Id, X]),
{error, Id, {"Timeout", []}, {"Message ~w", [X]}}
after 3500 ->
ok
end;
expect_impl(Id, Err) when is_atom(Err) ->
- io:format("expect_impl(~w, ~w) -> entry ~n", [Id, Err]),
+ ?PRINT2("await ~w with"
+ "~n Err: ~p",
+ [Id, Err]),
case receive_response() of
#pdu{error_status = Err} ->
+ ?PRINT2("received pdu with expected error status (~w, ~w)",
+ [Id, Err]),
ok;
- #pdu{request_id = ReqId,
- error_status = OtherErr} ->
- io:format("expect_impl(~w, ~w) -> "
- "received pdu (~w) with unexpected error-status: "
- "~n~p~n", [Id, Err, ReqId, OtherErr]),
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2} ->
+ ?EPRINT1("received pdu with unexpected error status: ~w, ~w, ~w"
+ "~n Expected Error: ~p"
+ "~n Received Error: ~p",
+ [Type2, Id, ReqId, Err, Err2]),
{error, Id, {"ErrorStatus: ~w, RequestId: ~w", [Err,ReqId]},
- {"ErrorStatus: ~w", [OtherErr]}};
+ {"ErrorStatus: ~w", [Err2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end;
expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w) -> entry with"
- "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ ?PRINT2("await ~w with"
+ "~n ExpectedVarbinds: ~p",
+ [Id, ExpectedVarbinds]),
+ PureVars = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected response pdu (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVars, VBs]),
+ check_vars(Id, PureVars, VBs);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['get-response', noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w, v2trap) -> entry with"
- "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ ?PRINT2("await v2 trap ~w with"
+ "~n ExpectedVarbinds: ~p",
+ [Id, ExpectedVarbinds]),
+ PureVars = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'snmpv2-trap',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w, v2trap) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected v2 trap (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVars, VBs]),
+ check_vars(Id, PureVars, VBs);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, v2trap) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['snmpv2-trap', noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end;
expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w, report) -> entry with"
- "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ ?PRINT2("await report ~w with"
+ "~n ExpectedVarbinds: ~p",
+ [Id, ExpectedVarbinds]),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'report',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w, report) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected report (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVBs, VBs]),
+ check_vars(Id, PureVBs, VBs);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, report) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
[report, noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end;
expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w, inform) -> entry with"
- "~n Reply: ~p"
- "~n ExpectedVarbinds: ~p"
- "~n", [Id, Reply, ExpectedVarbinds]),
- Resp = receive_response(),
+ ?PRINT2("await inform ~w with"
+ "~n Reply: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Reply, ExpectedVarbinds]),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
+ Resp = receive_response(),
case Resp of
#pdu{type = 'inform-request',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w, inform) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- case check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs) of
+ ?PRINT2("received inform (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVBs, VBs]),
+ case check_vars(Id, PureVBs, VBs) of
ok when (Reply == true) ->
- io:format("expect_impl(~w, inform) -> send ok response"
- "~n", [Id]),
+ ?PRINT2("varbinds ok (~w) - send ok inform response", [Id]),
RespPDU = Resp#pdu{type = 'get-response',
error_status = noError,
error_index = 0},
?MODULE:rpl(RespPDU),
ok;
ok when (element(1, Reply) == error) ->
- io:format("expect_impl(~w, inform) -> send error response"
- "~n", [Id]),
+ ?PRINT2("varbinds ok (~w) - send error inform response", [Id]),
{error, Status, Index} = Reply,
RespPDU = Resp#pdu{type = 'get-response',
error_status = Status,
@@ -901,10 +952,10 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
?MODULE:rpl(RespPDU),
ok;
ok when (Reply == false) ->
- io:format("expect_impl(~w, inform) -> no response sent"
- "~n", [Id]),
+ ?PRINT2("varbinds ok (~w) - don't send inform response", [Id]),
ok;
Else ->
+ ?EPRINT1("unexpected varbinds (~w)", [Id]),
io:format("expect_impl(~w, inform) -> "
"~n Else: ~p"
"~n", [Id, Else]),
@@ -915,54 +966,54 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, inform) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['inform-request', noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
- io:format("expect_impl(~w, inform) -> receive failed"
- "~n Reason: ~p"
- "~n", [Id, Reason]),
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
-expect_impl(Id, Err, Index, any) ->
- io:format("expect_impl(~w, any) -> entry with"
- "~n Err: ~p"
- "~n Index: ~p"
- "~n", [Id, Err, Index]),
+expect_impl(Id, Err, Index, any = _ExpectedVarbinds) ->
+ ?PRINT2("await response ~w with"
+ "~n Err: ~p"
+ "~n Index: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Err, Index, _ExpectedVarbinds]),
case receive_response() of
#pdu{type = 'get-response',
error_status = Err,
error_index = Index} ->
- io:format("expect_impl(~w, any) -> received expected pdu"
- "~n", [Id]),
+ ?PRINT2("received expected response pdu (~w, ~w, ~w)",
+ [Id, Err, Index]),
ok;
- #pdu{type = 'get-response', error_status = Err} when (Index == any) ->
- io:format("expect_impl(~w, any) -> received expected pdu (any)"
- "~n", [Id]),
+ #pdu{type = 'get-response',
+ error_status = Err} when (Index == any) ->
+ ?PRINT2("received expected response pdu (~w, ~w)",
+ [Id, Err]),
ok;
#pdu{type = 'get-response',
request_id = ReqId,
error_status = Err,
error_index = Idx} when is_list(Index) ->
- io:format("expect_impl(~w, any) -> received pdu: "
- "~n ReqId: ~p"
- "~n Err: ~p"
- "~n Idx: ~p"
- "~n", [Id, ReqId, Err, Idx]),
case lists:member(Idx, Index) of
true ->
+ ?PRINT2("received expected response pdu (~w, ~w, ~w)",
+ [Id, Err, Idx]),
ok;
false ->
+ ?EPRINT1("received response pdu with unexpected index (~w, ~w):"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p",
+ [Id, Err, Index, Idx]),
{error, Id, {"ErrStat: ~w, Idx: ~w, RequestId: ~w",
[Err, Index, ReqId]},
{"ErrStat: ~w, Idx: ~w", [Err, Idx]}}
@@ -972,12 +1023,12 @@ expect_impl(Id, Err, Index, any) ->
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, any) -> received unexpected pdu: "
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected response pdu: ~w, ~w, ~w"
+ "~n Expected Error: ~p"
+ "~n Received Error: ~p"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err, Err2, Index, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['get-response', Err, Index, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
@@ -987,22 +1038,30 @@ expect_impl(Id, Err, Index, any) ->
end;
expect_impl(Id, Err, Index, ExpectedVarbinds) ->
- io:format("expect_impl(~w) -> entry with"
- "~n Err: ~p"
- "~n Index: ~p"
- "~n ExpectedVarbinds: ~p"
- "~n", [Id, Err, Index, ExpectedVarbinds]),
+ ?PRINT2("await response ~w with"
+ "~n Err: ~p"
+ "~n Index: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Err, Index, ExpectedVarbinds]),
PureVBs = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'get-response',
error_status = Err,
error_index = Index,
varbinds = VBs} ->
+ ?PRINT2("received expected response pdu (~w, ~w, ~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, Index, PureVBs, VBs]),
check_vars(Id, PureVBs, VBs);
#pdu{type = 'get-response',
error_status = Err,
varbinds = VBs} when (Index == any) ->
+ ?PRINT2("received expected response pdu (~w, ~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, PureVBs, VBs]),
check_vars(Id, PureVBs, VBs);
#pdu{type = 'get-response',
@@ -1012,8 +1071,18 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) ->
varbinds = VBs} when is_list(Index) ->
case lists:member(Idx, Index) of
true ->
+ ?PRINT2("received expected pdu (~w, ~w, ~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, Idx, PureVBs, VBs]),
check_vars(Id, PureVBs, VBs);
false ->
+ ?EPRINT1("received response pdu with unexpected index (~w, ~w):"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, Index, Idx, PureVBs, VBs]),
{error,Id,
{"ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
[Err,Index,PureVBs,ReqId]},
@@ -1026,29 +1095,65 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) ->
error_status = Err2,
error_index = Index2,
varbinds = VBs} ->
+ ?EPRINT1("received unexpected response pdu: ~w, ~w, ~w"
+ "~n Expected Error: ~p"
+ "~n Received Error: ~p"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Type2, Id, ReqId,
+ Err, Err2, Index, Index2, PureVBs, VBs]),
{error,Id,
{"Type: ~w, ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
['get-response',Err,Index,PureVBs,ReqId]},
{"Type: ~w, ErrStat: ~w Idx: ~w Varbinds: ~w",
[Type2,Err2,Index2,VBs]}};
- {error, Reason} ->
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive pdu error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
- PureE = find_pure_oid(Enterp),
+ ?PRINT2("await trap pdu ~w with"
+ "~n Enterprise: ~p"
+ "~n Generic: ~p"
+ "~n Specific: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Enterp, Generic, Specific, ExpectedVarbinds]),
+ PureE = find_pure_oid(Enterp),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
case receive_trap(3500) of
#trappdu{enterprise = PureE,
generic_trap = Generic,
specific_trap = Specific,
varbinds = VBs} ->
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected trap pdu - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [PureVBs, VBs]),
+ check_vars(Id, PureVBs, VBs);
#trappdu{enterprise = Ent2,
generic_trap = G2,
specific_trap = Spec2,
varbinds = VBs} ->
+ ?EPRINT1("received unexpected trap pdu: ~w"
+ "~n Expected Enterprise: ~p"
+ "~n Received Enterprise: ~p"
+ "~n Expected Generic: ~p"
+ "~n Received Generic: ~p"
+ "~n Expected Specific: ~p"
+ "~n Received Specific: ~p"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id,
+ PureE, Ent2,
+ Generic, G2,
+ Specific, Spec2,
+ PureVBs, VBs]),
{error, Id,
{"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
[PureE, Generic, Specific, ExpectedVarbinds]},
@@ -1056,12 +1161,15 @@ expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
[Ent2, G2, Spec2, VBs]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive trap pdu error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
format_reason(Id, Reason) ->
{error, Id, {"?", []}, {"~w", [Reason]}}.
+
%%----------------------------------------------------------------------
%% Args: Id, ExpectedVarbinds, GotVarbinds
%% Returns: ok
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index f305497cd3..2450b771f7 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.3
+SNMP_VSN = 5.4
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 60f20c7c3f..b9cb80806e 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,86 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed wrong type definition for the daemon option
+ <c>subsystems</c>.</p>
+ <p>
+ Own Id: OTP-15820</p>
+ </item>
+ <item>
+ <p>
+ Fixed a possible SSH logging crash if there was a problem
+ in an early stage of session setup.</p>
+ <p>
+ Own Id: OTP-15962 Aux Id: ERL-990 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation for the modules ssh_connection,
+ ssh_sftp and ssh_sftpd are now generated from the
+ -spec:s.</p>
+ <p>
+ Own Id: OTP-15395</p>
+ </item>
+ <item>
+ <p>
+ Internal cleanup including removal of the internal file
+ <c>ssh_userauth.hrl</c>.</p>
+ <p>
+ Own Id: OTP-15876 Aux Id: PR-2255, PR-2256 </p>
+ </item>
+ <item>
+ <p>
+ Removed unused definitions in <c>ssh.hrl</c>.</p>
+ <p>
+ Own Id: OTP-15929 Aux Id: PR-2297 </p>
+ </item>
+ <item>
+ <p>
+ Removed unused fields in the internal
+ <c>#connection{}</c> record.</p>
+ <p>
+ Own Id: OTP-15984</p>
+ </item>
+ <item>
+ <p>
+ To get information of a <c>connection_ref()</c> from for
+ example <c>ssh:connect/3</c>, there was previously one
+ function available namely <c>ssh:connection_info/2</c>.
+ This ticket adds <c>ssh:connection_info/1</c> which
+ returns all information.</p>
+ <p>
+ For daemons (servers) started with for example
+ <c>ssh:daemon/2</c> the function <c>ssh:daemon_info/1</c>
+ returning all information was available. This ticket adds
+ <c>ssh:daemon_info/2</c> which returns only the
+ information specified in the second argument.</p>
+ <p>
+ The info of connections and of daemons now also includes
+ the item '<c>options</c>'. Only those options that does
+ not have their default values are returned.</p>
+ <p>
+ For a connection also the items '<c>algorithms</c>' and
+ '<c>channels</c>' are added.</p>
+ <p>
+ Own Id: OTP-16040</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.7</title>
<section><title>Improvements and New Features</title>
@@ -45,6 +125,22 @@
</section>
+<section><title>Ssh 4.7.6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a possible SSH logging crash if there was a problem
+ in an early stage of session setup.</p>
+ <p>
+ Own Id: OTP-15962 Aux Id: ERL-990 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.6</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 8b7cb4dcd4..afcae71418 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -1036,6 +1036,34 @@
</desc>
</datatype>
+ <datatype>
+ <name name="connection_info_tuple"/>
+ <name name="version"/>
+ <name name="protocol_version"/>
+ <name name="software_version"/>
+ <name name="conn_info_algs"/>
+ <name name="conn_info_channels"/>
+ <desc>
+ <p>Return values from the
+ <seealso marker="#connection_info/1">connection_info/1</seealso> and
+ <seealso marker="#connection_info/2">connection_info/2</seealso> functions.
+ </p>
+ <p>In the <c>option</c> info tuple are only the options included that differs from the default values.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="daemon_info_tuple"/>
+ <desc>
+ <p>Return values from the
+ <seealso marker="#daemon_info/1">daemon_info/1</seealso> and
+ <seealso marker="#daemon_info/2">daemon_info/2</seealso> functions.
+ </p>
+ <p>In the <c>option</c> info tuple are only the options included that differs from the default values.
+ </p>
+ </desc>
+ </datatype>
<datatype>
<name>opaque_client_options</name>
@@ -1099,12 +1127,15 @@
<!-- CONNECTION_INFO/1, CONNECTION_INFO/2 -->
<func>
+ <name name="connection_info" arity="1" since=""/>
<name name="connection_info" arity="2" since=""/>
<fsummary>Retrieves information about a connection.</fsummary>
<desc>
- <p>Retrieves information about a connection. The list <c>Keys</c> defines which information that
- is returned.</p>
- </desc>
+ <p>Returns information about a connection intended for e.g debugging or logging.
+ </p>
+ <p>When the <c>Key</c> is a single <c>Item</c>, the result is a single <c>InfoTuple</c>
+ </p>
+ </desc>
</func>
<!-- DEAMON/1,2,3 -->
@@ -1156,9 +1187,16 @@
<!-- DAEMON_INFO/1 -->
<func>
<name name="daemon_info" arity="1" since="OTP 19.0"/>
+ <name name="daemon_info" arity="2" since=""/>
<fsummary>Get info about a daemon</fsummary>
<desc>
- <p>Returns a key-value list with information about the daemon.</p>
+ <p>Returns information about a daemon intended for e.g debugging or logging.
+ </p>
+ <p>When the <c>Key</c> is a single <c>Item</c>, the result is a single <c>InfoTuple</c>
+ </p>
+ <p>Note that <c>daemon_info/1</c> and <c>daemon_info/2</c> returns different types
+ due to compatibility reasons.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 32f10c797d..28f8e0ac18 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -31,9 +31,10 @@
-export([start/0, start/1, stop/0,
connect/2, connect/3, connect/4,
close/1, connection_info/2,
+ connection_info/1,
channel_info/3,
daemon/1, daemon/2, daemon/3,
- daemon_info/1,
+ daemon_info/1, daemon_info/2,
default_algorithms/0,
chk_algos_opts/1,
stop_listener/1, stop_listener/2, stop_listener/3,
@@ -185,21 +186,50 @@ close(ConnectionRef) ->
%%--------------------------------------------------------------------
%% Description: Retrieves information about a connection.
-%%--------------------------------------------------------------------
--spec connection_info(ConnectionRef, Keys) -> ConnectionInfo when
+%%---------------------------------------------------------------------
+-type version() :: {protocol_version(), software_version()}.
+-type protocol_version() :: {Major::pos_integer(), Minor::non_neg_integer()}.
+-type software_version() :: string().
+-type conn_info_algs() :: [{kex, kex_alg()}
+ | {hkey, pubkey_alg()}
+ | {encrypt, cipher_alg()}
+ | {decrypt, cipher_alg()}
+ | {send_mac, mac_alg()}
+ | {recv_mac, mac_alg()}
+ | {compress, compression_alg()}
+ | {decompress, compression_alg()}
+ | {send_ext_info, boolean()}
+ | {recv_ext_info, boolean()}
+ ].
+-type conn_info_channels() :: [proplists:proplist()].
+
+-type connection_info_tuple() ::
+ {client_version, version()}
+ | {server_version, version()}
+ | {user, string()}
+ | {peer, {inet:hostname(), ip_port()}}
+ | {sockname, ip_port()}
+ | {options, client_options()}
+ | {algorithms, conn_info_algs()}
+ | {channels, conn_info_channels()}.
+
+-spec connection_info(ConnectionRef) -> InfoTupleList when
+ ConnectionRef :: connection_ref(),
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: connection_info_tuple().
+
+connection_info(ConnectionRef) ->
+ connection_info(ConnectionRef, []).
+
+-spec connection_info(ConnectionRef, ItemList|Item) -> InfoTupleList|InfoTuple when
ConnectionRef :: connection_ref(),
- Keys :: [client_version | server_version | user | peer | sockname],
- ConnectionInfo :: [{client_version, Version}
- | {server_version, Version}
- | {user,string()}
- | {peer, {inet:hostname(), ip_port()}}
- | {sockname, ip_port()}
- ],
- Version :: {ProtocolVersion, VersionString::string()},
- ProtocolVersion :: {Major::pos_integer(), Minor::non_neg_integer()} .
-
-connection_info(Connection, Options) ->
- ssh_connection_handler:connection_info(Connection, Options).
+ ItemList :: [Item],
+ Item :: client_version | server_version | user | peer | sockname | options | algorithms | sockname,
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: connection_info_tuple().
+
+connection_info(ConnectionRef, Key) ->
+ ssh_connection_handler:connection_info(ConnectionRef, Key).
%%--------------------------------------------------------------------
-spec channel_info(connection_ref(), channel_id(), [atom()]) -> proplists:proplist().
@@ -321,33 +351,72 @@ daemon(_, _, _) ->
{error, badarg}.
%%--------------------------------------------------------------------
--spec daemon_info(Daemon) -> {ok, DaemonInfo} | {error,term()} when
- Daemon :: daemon_ref(),
- DaemonInfo :: [ {ip, inet:ip_address()}
- | {port, inet:port_number()}
- | {profile, term()}
- ].
-
-daemon_info(Pid) ->
- case catch ssh_system_sup:acceptor_supervisor(Pid) of
+-type daemon_info_tuple() ::
+ {port, inet:port_number()}
+ | {ip, inet:ip_address()}
+ | {profile, atom()}
+ | {options, daemon_options()}.
+
+-spec daemon_info(DaemonRef) -> {ok,InfoTupleList} | {error,bad_daemon_ref} when
+ DaemonRef :: daemon_ref(),
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: daemon_info_tuple().
+
+daemon_info(DaemonRef) ->
+ case catch ssh_system_sup:acceptor_supervisor(DaemonRef) of
AsupPid when is_pid(AsupPid) ->
- [{IP,Port,Profile}] =
- [{IP,Prt,Prf}
+ [{Host,Port,Profile}] =
+ [{Hst,Prt,Prf}
|| {{ssh_acceptor_sup,Hst,Prt,Prf},_Pid,worker,[ssh_acceptor]}
- <- supervisor:which_children(AsupPid),
- IP <- [case inet:parse_strict_address(Hst) of
- {ok,IP} -> IP;
- _ -> Hst
- end]
- ],
+ <- supervisor:which_children(AsupPid)],
+ IP =
+ case inet:parse_strict_address(Host) of
+ {ok,IP0} -> IP0;
+ _ -> Host
+ end,
+
+ Opts =
+ case ssh_system_sup:get_options(DaemonRef, Host, Port, Profile) of
+ {ok, OptMap} ->
+ lists:sort(
+ maps:to_list(
+ ssh_options:keep_set_options(
+ server,
+ ssh_options:keep_user_options(server,OptMap))));
+ _ ->
+ []
+ end,
+
{ok, [{port,Port},
{ip,IP},
- {profile,Profile}
+ {profile,Profile},
+ {options,Opts}
]};
_ ->
{error,bad_daemon_ref}
end.
+-spec daemon_info(DaemonRef, ItemList|Item) -> InfoTupleList|InfoTuple | {error,bad_daemon_ref} when
+ DaemonRef :: daemon_ref(),
+ ItemList :: [Item],
+ Item :: ip | port | profile | options,
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: daemon_info_tuple().
+
+daemon_info(DaemonRef, Key) when is_atom(Key) ->
+ case daemon_info(DaemonRef, [Key]) of
+ [{Key,Val}] -> {Key,Val};
+ Other -> Other
+ end;
+daemon_info(DaemonRef, Keys) ->
+ case daemon_info(DaemonRef) of
+ {ok,KVs} ->
+ [{Key,proplists:get_value(Key,KVs)} || Key <- Keys,
+ lists:keymember(Key,1,KVs)];
+ _ ->
+ []
+ end.
+
%%--------------------------------------------------------------------
%% Description: Stops the listener, but leaves
%% existing connections started by the listener up and running.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index e984cbb21b..ad5a54a2e2 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -279,10 +279,13 @@ get_print_info(ConnectionHandler) ->
call(ConnectionHandler, get_print_info, 1000).
%%--------------------------------------------------------------------
--spec connection_info(connection_ref(),
- [atom()]
- ) -> proplists:proplist().
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+connection_info(ConnectionHandler, []) ->
+ connection_info(ConnectionHandler, conn_info_keys());
+connection_info(ConnectionHandler, Key) when is_atom(Key) ->
+ case connection_info(ConnectionHandler, [Key]) of
+ [{Key,Val}] -> {Key,Val};
+ Other -> Other
+ end;
connection_info(ConnectionHandler, Options) ->
call(ConnectionHandler, {connection_info, Options}).
@@ -1993,18 +1996,57 @@ counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}.
%%%----------------------------------------------------------------
+conn_info_keys() ->
+ [client_version,
+ server_version,
+ peer,
+ user,
+ sockname,
+ options,
+ algorithms,
+ channels
+ ].
+
conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version};
conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version};
conn_info(peer, #data{ssh_params=S}) -> S#ssh.peer;
conn_info(user, D) -> D#data.auth_user;
conn_info(sockname, #data{ssh_params=S}) -> S#ssh.local;
+conn_info(options, #data{ssh_params=#ssh{opts=Opts}}) -> lists:sort(
+ maps:to_list(
+ ssh_options:keep_set_options(
+ client,
+ ssh_options:keep_user_options(client,Opts))));
+conn_info(algorithms, #data{ssh_params=#ssh{algorithms=A}}) -> conn_info_alg(A);
+conn_info(channels, D) -> try conn_info_chans(ets:tab2list(cache(D)))
+ catch _:_ -> undefined
+ end;
%% dbg options ( = not documented):
-conn_info(socket, D) -> D#data.socket;
+conn_info(socket, D) -> D#data.socket;
conn_info(chan_ids, D) ->
ssh_client_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) ->
[Id | Acc]
end, [], cache(D)).
+conn_info_chans(Chs) ->
+ Fs = record_info(fields, channel),
+ [lists:zip(Fs, tl(tuple_to_list(Ch))) || Ch=#channel{} <- Chs].
+
+conn_info_alg(AlgTup) ->
+ [alg|Vs] = tuple_to_list(AlgTup),
+ Fs = record_info(fields, alg),
+ [{K,V} || {K,V} <- lists:zip(Fs,Vs),
+ lists:member(K,[kex,
+ hkey,
+ encrypt,
+ decrypt,
+ send_mac,
+ recv_mac,
+ compress,
+ decompress,
+ send_ext_info,
+ recv_ext_info])].
+
%%%----------------------------------------------------------------
chann_info(recv_window, C) ->
{{win_size, C#channel.recv_window_size},
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 1010c9be55..39f23a8b8a 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -29,7 +29,9 @@
get_value/5, get_value/6,
put_value/5,
delete_key/5,
- handle_options/2
+ handle_options/2,
+ keep_user_options/2,
+ keep_set_options/2
]).
-export_type([private_options/0
@@ -42,14 +44,14 @@
-type option_class() :: internal_options | socket_options | user_options .
--type option_declaration() :: #{class := user_options,
- chk := fun((any) -> boolean() | {true,any()}),
+-type option_declaration() :: #{class := user_option | undoc_user_option,
+ chk := fun((any()) -> boolean() | {true,any()}),
default => any()
}.
-type option_key() :: atom().
--type option_declarations() :: #{ {option_key(),def} := option_declaration() }.
+-type option_declarations() :: #{ option_key() := option_declaration() }.
-type error() :: {error,{eoptions,any()}} .
@@ -166,7 +168,7 @@ handle_options(Role, PropList0, Opts0) when is_map(Opts0),
OptionDefinitions = default(Role),
InitialMap =
maps:fold(
- fun({K,def}, #{default:=V}, M) -> M#{K=>V};
+ fun(K, #{default:=V}, M) -> M#{K=>V};
(_,_,M) -> M
end,
Opts0#{user_options =>
@@ -192,7 +194,7 @@ handle_options(Role, PropList0, Opts0) when is_map(Opts0),
check_fun(Key, Defs) ->
- #{chk := Fun} = maps:get({Key,def}, Defs),
+ #{chk := Fun} = maps:get(Key, Defs),
Fun.
%%%================================================================
@@ -232,10 +234,10 @@ save({Key,Value}, Defs, OptMap) when is_map(OptMap) ->
catch
%% An unknown Key (= not in the definition map) is
%% regarded as an inet option:
- error:{badkey,{inet,def}} ->
+ error:{badkey,inet} ->
%% atomic (= non-tuple) options 'inet' and 'inet6':
OptMap#{socket_options := [Value | maps:get(socket_options,OptMap)]};
- error:{badkey,{Key,def}} ->
+ error:{badkey,Key} ->
OptMap#{socket_options := [{Key,Value} | maps:get(socket_options,OptMap)]};
%% But a Key that is known but the value does not validate
@@ -249,6 +251,35 @@ save(Opt, _Defs, OptMap) when is_map(OptMap) ->
%%%================================================================
%%%
+-spec keep_user_options(client|server, #{}) -> #{}.
+
+keep_user_options(Type, Opts) ->
+ Defs = default(Type),
+ maps:filter(fun(Key, _Value) ->
+ try
+ #{class := Class} = maps:get(Key,Defs),
+ Class == user_option
+ catch
+ _:_ -> false
+ end
+ end, Opts).
+
+
+-spec keep_set_options(client|server, #{}) -> #{}.
+
+keep_set_options(Type, Opts) ->
+ Defs = default(Type),
+ maps:filter(fun(Key, Value) ->
+ try
+ #{default := DefVal} = maps:get(Key,Defs),
+ DefVal =/= Value
+ catch
+ _:_ -> false
+ end
+ end, Opts).
+
+%%%================================================================
+%%%
%%% Default options
%%%
@@ -257,7 +288,7 @@ save(Opt, _Defs, OptMap) when is_map(OptMap) ->
default(server) ->
(default(common))
#{
- {subsystems, def} =>
+ subsystems =>
#{default => [ssh_sftpd:subsystem_spec([])],
chk => fun(L) ->
is_list(L) andalso
@@ -269,42 +300,42 @@ default(server) ->
false
end, L)
end,
- class => user_options
+ class => user_option
},
- {shell, def} =>
+ shell =>
#{default => ?DEFAULT_SHELL,
chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V)
end,
- class => user_options
+ class => user_option
},
- {exec, def} =>
+ exec =>
#{default => undefined,
chk => fun({direct, V}) -> check_function1(V) orelse check_function2(V) orelse check_function3(V);
%% Compatibility (undocumented):
({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V) orelse check_function3(V)
end,
- class => user_options
+ class => user_option
},
- {ssh_cli, def} =>
+ ssh_cli =>
#{default => undefined,
chk => fun({Cb, As}) -> is_atom(Cb) andalso is_list(As);
(V) -> V == no_cli
end,
- class => user_options
+ class => user_option
},
- {system_dir, def} =>
+ system_dir =>
#{default => "/etc/ssh",
chk => fun(V) -> check_string(V) andalso check_dir(V) end,
- class => user_options
+ class => user_option
},
- {auth_method_kb_interactive_data, def} =>
+ auth_method_kb_interactive_data =>
#{default => undefined, % Default value can be constructed when User is known
chk => fun({S1,S2,S3,B}) ->
check_string(S1) andalso
@@ -314,10 +345,10 @@ default(server) ->
(F) ->
check_function3(F)
end,
- class => user_options
+ class => user_option
},
- {user_passwords, def} =>
+ user_passwords =>
#{default => [],
chk => fun(V) ->
is_list(V) andalso
@@ -326,22 +357,22 @@ default(server) ->
check_string(S2)
end, V)
end,
- class => user_options
+ class => user_option
},
- {password, def} =>
+ password =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {dh_gex_groups, def} =>
+ dh_gex_groups =>
#{default => undefined,
chk => fun check_dh_gex_groups/1,
- class => user_options
+ class => user_option
},
- {dh_gex_limits, def} =>
+ dh_gex_limits =>
#{default => {0, infinity},
chk => fun({I1,I2}) ->
check_pos_integer(I1) andalso
@@ -350,137 +381,137 @@ default(server) ->
(_) ->
false
end,
- class => user_options
+ class => user_option
},
- {pwdfun, def} =>
+ pwdfun =>
#{default => undefined,
chk => fun(V) -> check_function4(V) orelse check_function2(V) end,
- class => user_options
+ class => user_option
},
- {negotiation_timeout, def} =>
+ negotiation_timeout =>
#{default => 2*60*1000,
chk => fun check_timeout/1,
- class => user_options
+ class => user_option
},
- {max_sessions, def} =>
+ max_sessions =>
#{default => infinity,
chk => fun check_pos_integer/1,
- class => user_options
+ class => user_option
},
- {max_channels, def} =>
+ max_channels =>
#{default => infinity,
chk => fun check_pos_integer/1,
- class => user_options
+ class => user_option
},
- {parallel_login, def} =>
+ parallel_login =>
#{default => false,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
- {minimal_remote_max_packet_size, def} =>
+ minimal_remote_max_packet_size =>
#{default => 0,
chk => fun check_pos_integer/1,
- class => user_options
+ class => user_option
},
- {failfun, def} =>
+ failfun =>
#{default => fun(_,_,_) -> void end,
chk => fun(V) -> check_function3(V) orelse
check_function2(V) % Backwards compatibility
end,
- class => user_options
+ class => user_option
},
- {connectfun, def} =>
+ connectfun =>
#{default => fun(_,_,_) -> void end,
chk => fun check_function3/1,
- class => user_options
+ class => user_option
},
%%%%% Undocumented
- {infofun, def} =>
+ infofun =>
#{default => fun(_,_,_) -> void end,
chk => fun(V) -> check_function3(V) orelse
check_function2(V) % Backwards compatibility
end,
- class => user_options
+ class => undoc_user_option
}
};
default(client) ->
(default(common))
#{
- {dsa_pass_phrase, def} =>
+ dsa_pass_phrase =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {rsa_pass_phrase, def} =>
+ rsa_pass_phrase =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {ecdsa_pass_phrase, def} =>
+ ecdsa_pass_phrase =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
-%%% Not yet implemented {ed25519_pass_phrase, def} =>
+%%% Not yet implemented ed25519_pass_phrase =>
%%% Not yet implemented #{default => undefined,
%%% Not yet implemented chk => fun check_string/1,
-%%% Not yet implemented class => user_options
+%%% Not yet implemented class => user_option
%%% Not yet implemented },
%%% Not yet implemented
-%%% Not yet implemented {ed448_pass_phrase, def} =>
+%%% Not yet implemented ed448_pass_phrase =>
%%% Not yet implemented #{default => undefined,
%%% Not yet implemented chk => fun check_string/1,
-%%% Not yet implemented class => user_options
+%%% Not yet implemented class => user_option
%%% Not yet implemented },
%%% Not yet implemented
- {silently_accept_hosts, def} =>
+ silently_accept_hosts =>
#{default => false,
chk => fun check_silently_accept_hosts/1,
- class => user_options
+ class => user_option
},
- {user_interaction, def} =>
+ user_interaction =>
#{default => true,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
- {save_accepted_host, def} =>
+ save_accepted_host =>
#{default => true,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
- {dh_gex_limits, def} =>
+ dh_gex_limits =>
#{default => {1024, 6144, 8192}, % FIXME: Is this true nowadays?
chk => fun({Min,I,Max}) ->
lists:all(fun check_pos_integer/1,
[Min,I,Max]);
(_) -> false
end,
- class => user_options
+ class => user_option
},
- {connect_timeout, def} =>
+ connect_timeout =>
#{default => infinity,
chk => fun check_timeout/1,
- class => user_options
+ class => user_option
},
- {user, def} =>
+ user =>
#{default =>
begin
Env = case os:type() of
@@ -498,59 +529,59 @@ default(client) ->
end
end,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {password, def} =>
+ password =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {quiet_mode, def} =>
+ quiet_mode =>
#{default => false,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
%%%%% Undocumented
- {keyboard_interact_fun, def} =>
+ keyboard_interact_fun =>
#{default => undefined,
chk => fun check_function3/1,
- class => user_options
+ class => undoc_user_option
}
};
default(common) ->
#{
- {user_dir, def} =>
+ user_dir =>
#{default => false, % FIXME: TBD ~/.ssh at time of call when user is known
chk => fun(V) -> check_string(V) andalso check_dir(V) end,
- class => user_options
+ class => user_option
},
- {pref_public_key_algs, def} =>
+ pref_public_key_algs =>
#{default => ssh_transport:default_algorithms(public_key),
chk => fun check_pref_public_key_algs/1,
- class => user_options
+ class => user_option
},
- {preferred_algorithms, def} =>
+ preferred_algorithms =>
#{default => ssh:default_algorithms(),
chk => fun check_preferred_algorithms/1,
- class => user_options
+ class => user_option
},
%% NOTE: This option is supposed to be used only in this very module (?MODULE). There is
%% a final stage in handle_options that "merges" the preferred_algorithms option and this one.
%% The preferred_algorithms is the one to use in the rest of the ssh application!
- {modify_algorithms, def} =>
+ modify_algorithms =>
#{default => undefined, % signals error if unsupported algo in preferred_algorithms :(
chk => fun check_modify_algorithms/1,
- class => user_options
+ class => user_option
},
- {id_string, def} =>
+ id_string =>
#{default => undefined, % FIXME: see ssh_transport:ssh_vsn/0
chk => fun(random) ->
{true, {random,2,5}}; % 2 - 5 random characters
@@ -562,56 +593,49 @@ default(common) ->
(V) ->
check_string(V)
end,
- class => user_options
+ class => user_option
},
- {key_cb, def} =>
+ key_cb =>
#{default => {ssh_file, []},
chk => fun({Mod,Opts}) -> is_atom(Mod) andalso is_list(Opts);
(Mod) when is_atom(Mod) -> {true, {Mod,[]}};
(_) -> false
end,
- class => user_options
+ class => user_option
},
- {profile, def} =>
+ profile =>
#{default => ?DEFAULT_PROFILE,
chk => fun erlang:is_atom/1,
- class => user_options
+ class => user_option
},
- {idle_time, def} =>
+ idle_time =>
#{default => infinity,
chk => fun check_timeout/1,
- class => user_options
+ class => user_option
},
- %% This is a "SocketOption"...
- %% {fd, def} =>
- %% #{default => undefined,
- %% chk => fun erlang:is_integer/1,
- %% class => user_options
- %% },
-
- {disconnectfun, def} =>
+ disconnectfun =>
#{default => fun(_) -> void end,
chk => fun check_function1/1,
- class => user_options
+ class => user_option
},
- {unexpectedfun, def} =>
+ unexpectedfun =>
#{default => fun(_,_) -> report end,
chk => fun check_function2/1,
- class => user_options
+ class => user_option
},
- {ssh_msg_debug_fun, def} =>
+ ssh_msg_debug_fun =>
#{default => fun(_,_,_,_) -> void end,
chk => fun check_function4/1,
- class => user_options
+ class => user_option
},
- {rekey_limit, def} =>
+ rekey_limit =>
#{default => {3600000, 1024000000}, % {1 hour, 1 GB}
chk => fun({infinity, infinity}) ->
true;
@@ -629,10 +653,10 @@ default(common) ->
(_) ->
false
end,
- class => user_options
+ class => user_option
},
- {auth_methods, def} =>
+ auth_methods =>
#{default => ?SUPPORTED_AUTH_METHODS,
chk => fun(As) ->
try
@@ -644,54 +668,54 @@ default(common) ->
_:_ -> false
end
end,
- class => user_options
+ class => user_option
},
+ send_ext_info =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_option
+ },
+
+ recv_ext_info =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_option
+ },
+
%%%%% Undocumented
- {transport, def} =>
+ transport =>
#{default => ?DEFAULT_TRANSPORT,
chk => fun({A,B,C}) ->
is_atom(A) andalso is_atom(B) andalso is_atom(C)
end,
- class => user_options
+ class => undoc_user_option
},
- {vsn, def} =>
+ vsn =>
#{default => {2,0},
chk => fun({Maj,Min}) -> check_non_neg_integer(Maj) andalso check_non_neg_integer(Min);
(_) -> false
end,
- class => user_options
+ class => undoc_user_option
},
- {tstflg, def} =>
+ tstflg =>
#{default => [],
chk => fun erlang:is_list/1,
- class => user_options
+ class => undoc_user_option
},
- {user_dir_fun, def} =>
+ user_dir_fun =>
#{default => undefined,
chk => fun check_function1/1,
- class => user_options
+ class => undoc_user_option
},
- {max_random_length_padding, def} =>
+ max_random_length_padding =>
#{default => ?MAX_RND_PADDING_LEN,
chk => fun check_non_neg_integer/1,
- class => user_options
- },
-
- {send_ext_info, def} =>
- #{default => true,
- chk => fun erlang:is_boolean/1,
- class => user_options
- },
-
- {recv_ext_info, def} =>
- #{default => true,
- chk => fun erlang:is_boolean/1,
- class => user_options
+ class => undoc_user_option
}
}.
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index ed7c0c2bd5..80406a6c47 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -37,11 +37,16 @@
subsystem_supervisor/1, channel_supervisor/1,
connection_supervisor/1,
acceptor_supervisor/1, start_subsystem/6,
- stop_subsystem/2]).
+ stop_subsystem/2,
+ get_options/4
+ ]).
%% Supervisor callback
-export([init/1]).
+-define(START(Address, Port, Profile, Options),
+ {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]}).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -61,7 +66,7 @@ init([Address, Port, Profile, Options]) ->
case ?GET_INTERNAL_OPT(connected_socket,Options,undefined) of
undefined ->
[#{id => id(ssh_acceptor_sup, Address, Port, Profile),
- start => {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]},
+ start => ?START(Address,Port,Profile,Options),
restart => transient,
type => supervisor
}];
@@ -96,6 +101,15 @@ stop_system(Address, Port, Profile) ->
ok.
+get_options(Sup, Address, Port, Profile) ->
+ try
+ {ok, #{start:=?START(Address,Port,Profile,Options)}} =
+ supervisor:get_childspec(Sup, id(ssh_acceptor_sup,Address,Port,Profile)),
+ {ok, Options}
+ catch
+ _:_ -> {error,not_found}
+ end.
+
system_supervisor(Address, Port, Profile) ->
Name = make_name(Address, Port, Profile),
whereis(Name).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index bb87dd388c..fcf97177d8 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.7.7
+SSH_VSN = 4.8
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 335896c60a..2f675560d6 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,6 +27,75 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 9.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handling of zero size fragments in TLS could cause an
+ infinite loop. This has now been corrected.</p>
+ <p>
+ Own Id: OTP-15328 Aux Id: ERIERL-379 </p>
+ </item>
+ <item>
+ <p>
+ DTLS record check needs to consider that a resent hello
+ message can have a different version than the negotiated.</p>
+ <p>
+ Own Id: OTP-15807 Aux Id: ERL-920 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Basic support for TLS 1.3 Client for experimental use.
+ For more information see the Standards Compliance chapter
+ of the User's Guide.</p>
+ <p>
+ Own Id: OTP-15431</p>
+ </item>
+ <item>
+ <p>
+ Correct solution for retaining tcp flow control OTP-15802
+ (ERL-934) as to not break ssl:recv as reported in
+ (ERL-938)</p>
+ <p>
+ Own Id: OTP-15823 Aux Id: ERL-934, ERL-938 </p>
+ </item>
+ <item>
+ <p>
+ Enhance dialyzer specs to reflect implementation better
+ and avoid dialyzer warnings for the user that wants to
+ use TLS with unix domain sockets.</p>
+ <p>
+ Own Id: OTP-15851 Aux Id: PR-2235 </p>
+ </item>
+ <item>
+ <p>
+ Add support for ECDSA signature algorithms in TLS 1.3.</p>
+ <p>
+ Own Id: OTP-15854</p>
+ </item>
+ <item>
+ <p>
+ Correct error handling of TLS downgrade, possible return
+ values form ssl:close/2 when downgrading is {ok, Port} or
+ {error, Reason}, it could happen that only ok was
+ returned instead of {error, closed} when downgrade failed
+ due to that the peer closed the TCP connection.</p>
+ <p>
+ Own Id: OTP-16027</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 9.3.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index b220691e79..f8fd42e36d 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -66,7 +66,7 @@
%%====================================================================
%% Setup
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
+start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts,
User, {CbModule, _, _, _, _} = CbInfo,
Timeout) ->
try
@@ -316,10 +316,10 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_
flight_buffer = #{handshakes := HsBuffer0,
change_cipher_spec := undefined,
next_sequence := Seq} = Flight0,
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
Hist = update_handshake_history(Handshake0, Handshake, Hist0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0),
State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0],
next_sequence => Seq +1},
@@ -329,10 +329,10 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_
connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0,
next_sequence := Seq} = Flight0,
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
Hist = update_handshake_history(Handshake0, Handshake, Hist0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0),
State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0],
next_sequence => Seq +1},
@@ -384,11 +384,11 @@ send_alert(Alert, #state{static_env = #static_env{socket = Socket,
connection_env = #connection_env{negotiated_version = Version},
connection_states = ConnectionStates0,
- ssl_options = SslOpts} = State0) ->
+ ssl_options = #{log_level := LogLevel}} = State0) ->
{BinMsg, ConnectionStates} =
encode_alert(Alert, Version, ConnectionStates0),
send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
State0#state{connection_states = ConnectionStates}.
send_alert_in_connection(Alert, State) ->
@@ -440,7 +440,7 @@ init({call, From}, {start, Timeout},
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
connection_env = CEnv,
- ssl_options = SslOpts,
+ ssl_options = #{versions := Versions} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
@@ -448,7 +448,7 @@ init({call, From}, {start, Timeout},
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
- HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
+ HelloVersion = dtls_record:hello_version(Version, Versions),
State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}),
{State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}),
State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion
@@ -547,14 +547,14 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #sta
Hello#client_hello.session_id}},
next_event(?FUNCTION_NAME, no_record, State, Actions);
hello(internal, #client_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
hello(internal, #server_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
@@ -705,7 +705,7 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho
handshake_env = #handshake_env{ renegotiation = {Renegotiation, _}},
connection_env = CEnv,
session = #session{own_certificate = Cert} = Session0,
- ssl_options = SslOpts,
+ ssl_options = #{versions := Versions} = SslOpts,
connection_states = ConnectionStates0,
protocol_specific = PS
} = State0) ->
@@ -713,7 +713,7 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
- HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
+ HelloVersion = dtls_record:hello_version(Version, Versions),
State1 = prepare_flight(State0),
{State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}),
State = State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)},
@@ -774,9 +774,10 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
+initial_state(Role, Host, Port, Socket,
+ {#{client_renegotiation := ClientRenegotiation} = SSLOptions, SocketOptions, _}, User,
{CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) ->
- #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
+ #{beast_mitigation := BeastMitigation} = SSLOptions,
ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
@@ -810,13 +811,13 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
handshake_env = #handshake_env{
tls_handshake_history = ssl_handshake:init_handshake_history(),
renegotiation = {false, first},
- allow_renegotiate = SSLOptions#ssl_options.client_renegotiation
+ allow_renegotiate = ClientRenegotiation
},
connection_env = #connection_env{user_application = {Monitor, User}},
socket_options = SocketOptions,
%% We do not want to save the password in the state so that
%% could be written in the clear into error logs.
- ssl_options = SSLOptions#ssl_options{password = undefined},
+ ssl_options = SSLOptions#{password => undefined},
session = #session{is_resumable = new},
connection_states = ConnectionStates,
protocol_buffers = #protocol_buffers{},
@@ -989,12 +990,13 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
-handle_own_alert(Alert, Version, StateName, #state{static_env = #static_env{data_tag = udp,
- role = Role},
- ssl_options = Options} = State0) ->
+handle_own_alert(Alert, Version, StateName,
+ #state{static_env = #static_env{data_tag = udp,
+ role = Role},
+ ssl_options = #{log_level := LogLevel}} = State0) ->
case ignore_alert(Alert, State0) of
{true, State} ->
- log_ignore_alert(Options#ssl_options.log_level, StateName, Alert, Role),
+ log_ignore_alert(LogLevel, StateName, Alert, Role),
{next_state, StateName, State};
{false, State} ->
ssl_connection:handle_own_alert(Alert, Version, StateName, State)
@@ -1105,7 +1107,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
flight_buffer = #{handshakes := Flight,
change_cipher_spec := undefined},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
%% TODO remove hardcoded Max size
{Encoded, ConnectionStates} =
@@ -1121,7 +1123,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := []},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0),
@@ -1139,7 +1141,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := Flight1},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0),
@@ -1160,7 +1162,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := Flight1},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
{EncChangeCipher, ConnectionStates1} =
encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
@@ -1220,8 +1222,8 @@ send_application_data(Data, From, _StateName,
connection_env = #connection_env{negotiated_version = Version},
handshake_env = HsEnv,
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{renegotiate_at = RenegotiateAt,
- log_level = LogLevel}} = State0) ->
+ ssl_options = #{renegotiate_at := RenegotiateAt,
+ log_level := LogLevel}} = State0) ->
case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
true ->
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 4a381745d4..b2a5fcfa66 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -47,7 +47,7 @@
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
- #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
+ ssl_options(), integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
%% Description: Creates a client hello message.
@@ -60,16 +60,15 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
%%--------------------------------------------------------------------
-spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(),
- #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
+ ssl_options(), integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, Cookie, ConnectionStates,
- #ssl_options{versions = Versions,
- ciphers = UserSuites,
- fallback = Fallback
- } = SslOpts,
+ #{versions := Versions,
+ ciphers := UserSuites,
+ fallback := Fallback} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
@@ -97,7 +96,7 @@ hello(#server_hello{server_version = Version, random = Random,
cipher_suite = CipherSuite,
compression_method = Compression,
session_id = SessionId, extensions = HelloExt},
- #ssl_options{versions = SupportedVersions} = SslOpt,
+ #{versions := SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
case dtls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
@@ -108,7 +107,7 @@ hello(#server_hello{server_version = Version, random = Random,
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
hello(#client_hello{client_version = ClientVersion} = Hello,
- #ssl_options{versions = Versions} = SslOpts,
+ #{versions := Versions} = SslOpts,
Info, Renegotiation) ->
Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions),
handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation).
@@ -151,7 +150,7 @@ encode_handshake(Handshake, Version, Seq) ->
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
--spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, #ssl_options{}) ->
+-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, ssl_options()) ->
{[dtls_handshake()], #protocol_buffers{}}.
%%
%% Description: Given buffered and new data from dtls_record, collects
@@ -170,10 +169,10 @@ handle_client_hello(Version,
compression_methods = Compressions,
random = Random,
extensions = HelloExt},
- #ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns,
- eccs = SupportedECCs,
- honor_ecc_order = ECCOrder} = SslOpts,
+ #{versions := Versions,
+ signature_algs := SupportedHashSigns,
+ eccs := SupportedECCs,
+ honor_ecc_order := ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
Renegotiation) ->
case dtls_record:is_acceptable_version(Version, Versions) of
@@ -317,14 +316,14 @@ handle_fragments(Version, FragmentData, Buffers0, Options, Acc) ->
do_handle_fragments(_, [], Buffers, _Options, Acc) ->
{lists:reverse(Acc), Buffers};
-do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Options, Acc) ->
+do_handle_fragments(Version, [Fragment | Fragments], Buffers0, #{log_level := LogLevel} = Options, Acc) ->
case reassemble(Version, Fragment, Buffers0) of
{more_data, Buffers} when Fragments == [] ->
{lists:reverse(Acc), Buffers};
{more_data, Buffers} ->
do_handle_fragments(Version, Fragments, Buffers, Options, Acc);
{{Handshake, _} = HsPacket, Buffers} ->
- ssl_logger:debug(Options#ssl_options.log_level, inbound, 'handshake', Handshake),
+ ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake),
do_handle_fragments(Version, Fragments, Buffers, Options, [HsPacket | Acc])
end.
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 8b8db7b2de..ee0ce2d22a 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -163,7 +163,7 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}},
%%--------------------------------------------------------------------
-spec get_dtls_records(binary(), {atom(), atom(), ssl_record:ssl_version(), [ssl_record:ssl_version()]}, binary(),
- #ssl_options{}) -> {[binary()], binary()} | #alert{}.
+ ssl_options()) -> {[binary()], binary()} | #alert{}.
%%
%% Description: Given old buffer and new data from UDP/SCTP, packs up a records
%% and returns it as a list of tls_compressed binaries also returns leftover
@@ -399,13 +399,13 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord,
- Acc, SslOpts) when ((StateName == hello) orelse
- ((StateName == certify) andalso (DataTag == udp)) orelse
- ((StateName == abbreviated) andalso(DataTag == udp)))
- andalso
- ((Type == ?HANDSHAKE) orelse
- (Type == ?ALERT)) ->
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]),
+ Acc, #{log_level := LogLevel} = SslOpts)
+ when ((StateName == hello)
+ orelse ((StateName == certify) andalso (DataTag == udp))
+ orelse ((StateName == abbreviated) andalso (DataTag == udp))) andalso ((Type == ?HANDSHAKE)
+ orelse
+ (Type == ?ALERT)) ->
+ ssl_logger:debug(LogLevel, inbound, 'record', [RawDTLSRecord]),
case is_acceptable_version({MajVer, MinVer}, Versions) of
true ->
get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type,
@@ -418,11 +418,11 @@ get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?B
get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord,
- Acc, SslOpts) when (Type == ?APPLICATION_DATA) orelse
+ Acc, #{log_level := LogLevel} = SslOpts) when (Type == ?APPLICATION_DATA) orelse
(Type == ?HANDSHAKE) orelse
(Type == ?ALERT) orelse
(Type == ?CHANGE_CIPHER_SPEC) ->
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]),
+ ssl_logger:debug(LogLevel, inbound, 'record', [RawDTLSRecord]),
case {MajVer, MinVer} of
Version ->
get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type,
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 7ff9aed8ea..0d6501b5ee 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -97,7 +97,8 @@
new_ssl_options/3,
suite_to_str/1,
suite_to_openssl_str/1,
- str_to_suite/1]).
+ str_to_suite/1,
+ options_to_map/1]).
-deprecated({ssl_accept, 1, eventually}).
-deprecated({ssl_accept, 2, eventually}).
@@ -438,6 +439,7 @@
elliptic_curves => [public_key:oid()],
sni => hostname()}. % exported
%% -------------------------------------------------------------------------------------------------------
+-define(SSL_OPTIONS, record_info(fields, ssl_options)).
%%%--------------------------------------------------------------------
%%% API
@@ -784,7 +786,13 @@ close(#sslsocket{pid = [TLSPid|_]},
{Pid, Timeout} = DownGrade) when is_pid(TLSPid),
is_pid(Pid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
- ssl_connection:close(TLSPid, {close, DownGrade});
+ case ssl_connection:close(TLSPid, {close, DownGrade}) of
+ ok -> %% In normal close {error, closed} is regarded as ok, as it is not interesting which side
+ %% that got to do the actual close. But in the downgrade case only {ok, Port} is a sucess.
+ {error, closed};
+ Other ->
+ Other
+ end;
close(#sslsocket{pid = [TLSPid|_]}, Timeout) when is_pid(TLSPid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
ssl_connection:close(TLSPid, {close, Timeout});
@@ -1452,15 +1460,18 @@ do_listen(Port, #config{transport_info = {Transport, _, _, _,_}} = Config, tls_c
do_listen(Port, Config, dtls_connection) ->
dtls_socket:listen(Port, Config).
-%% Handle extra ssl options given to ssl_accept
--spec handle_options([any()], #ssl_options{}) -> #ssl_options{}
- ; ([any()], client | server) -> {ok, #config{}}.
+
+-spec handle_options([any()], client | server) -> {ok, #config{}};
+ ([any()], ssl_options()) -> ssl_options().
+
handle_options(Opts, Role) ->
handle_options(Opts, Role, undefined).
-handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
- cacertfile = CaCertFile0} = InheritedSslOpts, _) ->
+%% Handle ssl options at handshake_continue
+handle_options(Opts0, #{protocol := Protocol,
+ cacerts := CaCerts0,
+ cacertfile := CaCertFile0} = InheritedSslOpts, _) ->
RecordCB = record_cb(Protocol),
CaCerts = handle_option(cacerts, Opts0, CaCerts0),
{Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder,
@@ -1472,13 +1483,13 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
CAFile
end,
- NewVerifyOpts = InheritedSslOpts#ssl_options{cacerts = CaCerts,
- cacertfile = CaCertFile,
- verify = Verify,
- verify_fun = VerifyFun,
- partial_chain = PartialChainHanlder,
- fail_if_no_peer_cert = FailIfNoPeerCert,
- verify_client_once = VerifyClientOnce},
+ NewVerifyOpts = InheritedSslOpts#{cacerts => CaCerts,
+ cacertfile => CaCertFile,
+ verify => Verify,
+ verify_fun => VerifyFun,
+ partial_chain => PartialChainHanlder,
+ fail_if_no_peer_cert => FailIfNoPeerCert,
+ verify_client_once => VerifyClientOnce},
SslOpts1 = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts0, [cacerts, cacertfile, verify, verify_fun, partial_chain,
@@ -1490,10 +1501,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
Versions0 = [RecordCB:protocol_version(Vsn) || Vsn <- Value],
Versions1 = lists:sort(fun RecordCB:is_higher/2, Versions0),
new_ssl_options(proplists:delete(versions, SslOpts1),
- NewVerifyOpts#ssl_options{versions = Versions1}, record_cb(Protocol))
+ NewVerifyOpts#{versions => Versions1}, record_cb(Protocol))
end;
-%% Handle all options in listen and connect
+%% Handle all options in listen, connect and handshake
handle_options(Opts0, Role, Host) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
@@ -1544,36 +1555,21 @@ handle_options(Opts0, Role, Host) ->
user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined),
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
- ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
- HighestVersion),
- eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()),
- HighestVersion),
- supported_groups = handle_supported_groups_option(
- proplists:get_value(supported_groups, Opts, groups(default)),
- HighestVersion),
- signature_algs =
- handle_hashsigns_option(
- proplists:get_value(
- signature_algs,
- Opts,
- default_option_role_sign_algs(server,
- tls_v1:default_signature_algs(HighestVersion),
- Role,
- HighestVersion)),
- tls_version(HighestVersion)),
- signature_algs_cert =
- handle_signature_algorithms_option(
- proplists:get_value(
- signature_algs_cert,
- Opts,
- undefined), %% Do not send by default
- tls_version(HighestVersion)),
- reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role),
- reuse_session = handle_reuse_session_option(reuse_session, Opts, Role),
+ ciphers = handle_option(ciphers, Opts, undefined, undefined, undefined, HighestVersion),
+
+ eccs = handle_option(eccs, Opts, undefined, undefined, undefined, HighestVersion),
+
+ supported_groups = handle_option(supported_groups, Opts, undefined, undefined, undefined,
+ HighestVersion),
+ signature_algs = handle_option(signature_algs, Opts, undefined, Role, undefined, HighestVersion),
+ signature_algs_cert = handle_option(signature_algs_cert, Opts, undefined, undefined, undefined,
+ HighestVersion),
+ reuse_sessions = handle_option(reuse_sessions, Opts, undefined, Role),
+
+ reuse_session = handle_option(reuse_session, Opts, undefined, Role),
+
secure_renegotiate = handle_option(secure_renegotiate, Opts, true),
- client_renegotiation = handle_option(client_renegotiation, Opts,
- default_option_role(server, true, Role),
- server, Role),
+ client_renegotiation = handle_option(client_renegotiation, Opts, undefined, Role),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
hibernate_after = handle_option(hibernate_after, Opts, infinity),
erl_dist = handle_option(erl_dist, Opts, false),
@@ -1584,71 +1580,61 @@ handle_options(Opts0, Role, Host) ->
next_protocols_advertised =
handle_option(next_protocols_advertised, Opts, undefined),
next_protocol_selector =
- make_next_protocol_selector(
- handle_option(client_preferred_next_protocols, Opts, undefined)),
- server_name_indication = handle_option(server_name_indication, Opts,
- default_option_role(client,
- server_name_indication_default(Host), Role)),
+ handle_option(next_protocol_selector, Opts, undefined),
+ server_name_indication =
+ handle_option(server_name_indication, Opts, undefined, Role, Host),
sni_hosts = handle_option(sni_hosts, Opts, []),
sni_fun = handle_option(sni_fun, Opts, undefined),
- honor_cipher_order = handle_option(honor_cipher_order, Opts,
- default_option_role(server, false, Role),
- server, Role),
- honor_ecc_order = handle_option(honor_ecc_order, Opts,
- default_option_role(server, false, Role),
- server, Role),
+ honor_cipher_order = handle_option(honor_cipher_order, Opts, undefined, Role),
+ honor_ecc_order = handle_option(honor_ecc_order, Opts, undefined, Role),
protocol = Protocol,
padding_check = proplists:get_value(padding_check, Opts, true),
- beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
- fallback = handle_option(fallback, Opts,
- proplists:get_value(fallback, Opts,
- default_option_role(client,
- false, Role)),
- client, Role),
+ beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
+ fallback = handle_option(fallback, Opts, undefined, Role),
+
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}),
max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE),
handshake = handle_option(handshake, Opts, full),
- customize_hostname_check = handle_option(customize_hostname_check, Opts, [])
+ customize_hostname_check = handle_option(customize_hostname_check, Opts, [])
},
LogLevel = handle_option(log_alert, Opts, true),
- SSLOptions = SSLOptions0#ssl_options{
+ SSLOptions1 = SSLOptions0#ssl_options{
log_level = handle_option(log_level, Opts, LogLevel)
},
CbInfo = handle_option(cb_info, Opts, default_cb_info(Protocol)),
- SslOptions = [protocol, versions, verify, verify_fun, partial_chain,
- fail_if_no_peer_cert, verify_client_once,
- depth, cert, certfile, key, keyfile,
- password, cacerts, cacertfile, dh, dhfile,
- user_lookup_fun, psk_identity, srp_identity, ciphers,
- reuse_session, reuse_sessions, ssl_imp, client_renegotiation,
- cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
- erl_dist, alpn_advertised_protocols, sni_hosts, sni_fun,
- alpn_preferred_protocols, next_protocols_advertised,
- client_preferred_next_protocols, log_alert, log_level,
- server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, signature_algs_cert, eccs, honor_ecc_order,
- beast_mitigation, max_handshake_size, handshake, customize_hostname_check,
- supported_groups],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
- end, Opts, SslOptions),
+ end, Opts, ?SSL_OPTIONS ++
+ [ssl_imp, %% TODO: remove ssl_imp
+ client_preferred_next_protocols, %% next_protocol_selector
+ log_alert,
+ cb_info]),
{Sock, Emulated} = emulated_options(Protocol, SockOpts),
ConnetionCb = connection_cb(Opts),
-
+ SSLOptions = options_to_map(SSLOptions1),
{ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = Sock,
inet_user = Sock, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
-handle_option(OptionName, Opts, Default, Role, Role) ->
- handle_option(OptionName, Opts, Default);
-handle_option(_, _, undefined = Value, _, _) ->
- Value.
+%% handle_option(OptionName, Opts, Default, Role, Role) ->
+%% handle_option(OptionName, Opts, Default);
+%% handle_option(_, _, undefined = Value, _, _) ->
+%% Value.
-handle_option(sni_fun, Opts, Default) ->
+handle_option(OptionName, Opts, Default) ->
+ handle_option(OptionName, Opts, Default, undefined, undefined, undefined).
+%%
+handle_option(OptionName, Opts, Default, Role) ->
+ handle_option(OptionName, Opts, Default, Role, undefined, undefined).
+%%
+handle_option(OptionName, Opts, Default, Role, Host) ->
+ handle_option(OptionName, Opts, Default, Role, Host, undefined).
+%%
+handle_option(sni_fun, Opts, Default, _Role, _Host, _Version) ->
OptFun = validate_option(sni_fun,
proplists:get_value(sni_fun, Opts, Default)),
OptHosts = proplists:get_value(sni_hosts, Opts, undefined),
@@ -1660,14 +1646,91 @@ handle_option(sni_fun, Opts, Default) ->
_ ->
throw({error, {conflict_options, [sni_fun, sni_hosts]}})
end;
-handle_option(cb_info, Opts, Default) ->
+handle_option(cb_info, Opts, Default, _Role, _Host, _Version) ->
CbInfo = proplists:get_value(cb_info, Opts, Default),
true = validate_option(cb_info, CbInfo),
handle_cb_info(CbInfo, Default);
-handle_option(OptionName, Opts, Default) ->
+handle_option(ciphers = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_cipher_option(proplists:get_value(Key, Opts, []),
+ HighestVersion);
+handle_option(eccs = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_eccs_option(proplists:get_value(Key, Opts, eccs()),
+ HighestVersion);
+handle_option(supported_groups = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_supported_groups_option(proplists:get_value(Key, Opts, groups(default)),
+ HighestVersion);
+handle_option(signature_algs = Key, Opts, _Default, Role, _Host, HighestVersion) ->
+ handle_hashsigns_option(
+ proplists:get_value(Key,
+ Opts,
+ default_option_role_sign_algs(server,
+ tls_v1:default_signature_algs(HighestVersion),
+ Role,
+ HighestVersion)),
+ tls_version(HighestVersion));
+handle_option(signature_algs_cert = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_signature_algorithms_option(
+ proplists:get_value(Key,
+ Opts,
+ undefined), %% Do not send by default
+ tls_version(HighestVersion));
+handle_option(reuse_sessions = Key, Opts, _Default, client, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, true),
+ validate_option(Key, Value),
+ Value;
+handle_option(reuse_sessions = Key, Opts0, _Default, server, _Host, _Version) ->
+ Opts = proplists:delete({Key, save}, Opts0),
+ Value = proplists:get_value(Key, Opts, true),
+ validate_option(Key, Value),
+ Value;
+handle_option(reuse_session = Key, Opts, _Default, client, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, undefined),
+ validate_option(Key, Value),
+ Value;
+handle_option(reuse_session = Key, Opts, _Default, server, _Host, _Version) ->
+ ReuseSessionFun = fun(_, _, _, _) -> true end,
+ Value = proplists:get_value(Key, Opts, ReuseSessionFun),
+ validate_option(Key, Value),
+ Value;
+handle_option(fallback = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(client, false, Role)),
+ assert_role(client_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(client_renegotiation = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(server, true, Role)),
+ assert_role(server_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(honor_cipher_order = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(server, false, Role)),
+ assert_role(server_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(honor_ecc_order = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(server, false, Role)),
+ assert_role(server_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(next_protocol_selector = _Key, Opts, _Default, _Role, _Host, _Version) ->
+ make_next_protocol_selector(
+ handle_option(client_preferred_next_protocols, Opts, undefined, undefined, undefined, undefined));
+handle_option(server_name_indication = Key, Opts, _Default, Role, Host, _Version) ->
+ Default = default_option_role(client, server_name_indication_default(Host), Role),
+ validate_option(Key, proplists:get_value(Key, Opts, Default));
+handle_option(OptionName, Opts, Default, _Role, _Host, _Version) ->
validate_option(OptionName,
proplists:get_value(OptionName, Opts, Default)).
+
+assert_role(client_only, client, _, _) ->
+ ok;
+assert_role(server_only, server, _, _) ->
+ ok;
+assert_role(client_only, _, _, undefined) ->
+ ok;
+assert_role(server_only, _, _, undefined) ->
+ ok;
+assert_role(Type, _, Key, _) ->
+ throw({error, {option, Type, Key}}).
+
+
validate_option(versions, Versions) ->
validate_versions(Versions, Versions);
validate_option(verify, Value)
@@ -1913,6 +1976,13 @@ validate_option(cb_info, {V1, V2, V3, V4, V5}) when is_atom(V1),
true;
validate_option(cb_info, _) ->
false;
+validate_option(Opt, undefined = Value) ->
+ case lists:member(Opt, ?SSL_OPTIONS) of
+ true ->
+ Value;
+ false ->
+ throw({error, {options, {Opt, Value}}})
+ end;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
@@ -1957,26 +2027,6 @@ handle_signature_algorithms_option(Value, Version) when is_list(Value)
handle_signature_algorithms_option(_, _Version) ->
undefined.
-handle_reuse_sessions_option(Key, Opts, client) ->
- Value = proplists:get_value(Key, Opts, true),
- validate_option(Key, Value),
- Value;
-handle_reuse_sessions_option(Key, Opts0, server) ->
- Opts = proplists:delete({Key, save}, Opts0),
- Value = proplists:get_value(Key, Opts, true),
- validate_option(Key, Value),
- Value.
-
-handle_reuse_session_option(Key, Opts, client) ->
- Value = proplists:get_value(Key, Opts, undefined),
- validate_option(Key, Value),
- Value;
-handle_reuse_session_option(Key, Opts, server) ->
- ReuseSessionFun = fun(_, _, _, _) -> true end,
- Value = proplists:get_value(Key, Opts, ReuseSessionFun),
- validate_option(Key, Value),
- Value.
-
validate_options([]) ->
[];
validate_options([{Opt, Value} | Tail]) ->
@@ -2225,101 +2275,104 @@ assert_proplist([inet6 | Rest]) ->
assert_proplist([Value | _]) ->
throw({option_not_a_key_value_tuple, Value}).
-new_ssl_options([], #ssl_options{} = Opts, _) ->
+new_ssl_options([], #{} = Opts, _) ->
Opts;
-new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{verify_client_once =
- validate_option(verify_client_once, Value)}, RecordCB);
-new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB);
-new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{cert = validate_option(cert, Value)}, RecordCB);
-new_ssl_options([{certfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{certfile = validate_option(certfile, Value)}, RecordCB);
-new_ssl_options([{key, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{key = validate_option(key, Value)}, RecordCB);
-new_ssl_options([{keyfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{keyfile = validate_option(keyfile, Value)}, RecordCB);
-new_ssl_options([{password, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{password = validate_option(password, Value)}, RecordCB);
-new_ssl_options([{dh, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{dh = validate_option(dh, Value)}, RecordCB);
-new_ssl_options([{dhfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{dhfile = validate_option(dhfile, Value)}, RecordCB);
-new_ssl_options([{user_lookup_fun, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{user_lookup_fun = validate_option(user_lookup_fun, Value)}, RecordCB);
-new_ssl_options([{psk_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{psk_identity = validate_option(psk_identity, Value)}, RecordCB);
-new_ssl_options([{srp_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{srp_identity = validate_option(srp_identity, Value)}, RecordCB);
-new_ssl_options([{ciphers, Value} | Rest], #ssl_options{versions = Versions} = Opts, RecordCB) ->
+new_ssl_options([{verify_client_once, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{verify_client_once =>
+ validate_option(verify_client_once, Value)}, RecordCB);
+new_ssl_options([{depth, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{depth => validate_option(depth, Value)}, RecordCB);
+new_ssl_options([{cert, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{cert => validate_option(cert, Value)}, RecordCB);
+new_ssl_options([{certfile, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{certfile => validate_option(certfile, Value)}, RecordCB);
+new_ssl_options([{key, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{key => validate_option(key, Value)}, RecordCB);
+new_ssl_options([{keyfile, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{keyfile => validate_option(keyfile, Value)}, RecordCB);
+new_ssl_options([{password, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{password => validate_option(password, Value)}, RecordCB);
+new_ssl_options([{dh, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{dh => validate_option(dh, Value)}, RecordCB);
+new_ssl_options([{dhfile, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{dhfile => validate_option(dhfile, Value)}, RecordCB);
+new_ssl_options([{user_lookup_fun, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{user_lookup_fun => validate_option(user_lookup_fun, Value)}, RecordCB);
+new_ssl_options([{psk_identity, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{psk_identity => validate_option(psk_identity, Value)}, RecordCB);
+new_ssl_options([{srp_identity, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{srp_identity => validate_option(srp_identity, Value)}, RecordCB);
+new_ssl_options([{ciphers, Value} | Rest], #{versions := Versions} = Opts, RecordCB) ->
Ciphers = handle_cipher_option(Value, RecordCB:highest_protocol_version(Versions)),
- new_ssl_options(Rest,
- Opts#ssl_options{ciphers = Ciphers}, RecordCB);
-new_ssl_options([{reuse_session, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{reuse_session = validate_option(reuse_session, Value)}, RecordCB);
-new_ssl_options([{reuse_sessions, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{reuse_sessions = validate_option(reuse_sessions, Value)}, RecordCB);
-new_ssl_options([{ssl_imp, _Value} | Rest], #ssl_options{} = Opts, RecordCB) -> %% Not used backwards compatibility
+ new_ssl_options(Rest, Opts#{ciphers => Ciphers}, RecordCB);
+new_ssl_options([{reuse_session, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{reuse_session => validate_option(reuse_session, Value)}, RecordCB);
+new_ssl_options([{reuse_sessions, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{reuse_sessions => validate_option(reuse_sessions, Value)}, RecordCB);
+new_ssl_options([{ssl_imp, _Value} | Rest], #{} = Opts, RecordCB) -> %% Not used backwards compatibility
new_ssl_options(Rest, Opts, RecordCB);
-new_ssl_options([{renegotiate_at, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{ renegotiate_at = validate_option(renegotiate_at, Value)}, RecordCB);
-new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB);
-new_ssl_options([{client_renegotiation, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{client_renegotiation = validate_option(client_renegotiation, Value)}, RecordCB);
-new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB);
-new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{alpn_advertised_protocols = validate_option(alpn_advertised_protocols, Value)}, RecordCB);
-new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{alpn_preferred_protocols = validate_option(alpn_preferred_protocols, Value)}, RecordCB);
-new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{next_protocols_advertised = validate_option(next_protocols_advertised, Value)}, RecordCB);
-new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{next_protocol_selector =
- make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, RecordCB);
-new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_alert, Value)}, RecordCB);
-new_ssl_options([{log_level, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_level, Value)}, RecordCB);
-new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB);
-new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB);
-new_ssl_options([{honor_ecc_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{honor_ecc_order = validate_option(honor_ecc_order, Value)}, RecordCB);
-new_ssl_options([{eccs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+new_ssl_options([{renegotiate_at, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{renegotiate_at => validate_option(renegotiate_at, Value)}, RecordCB);
+new_ssl_options([{secure_renegotiate, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{secure_renegotiate => validate_option(secure_renegotiate, Value)}, RecordCB);
+new_ssl_options([{client_renegotiation, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{client_renegotiation => validate_option(client_renegotiation, Value)}, RecordCB);
+new_ssl_options([{hibernate_after, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{hibernate_after => validate_option(hibernate_after, Value)}, RecordCB);
+new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{alpn_advertised_protocols => validate_option(alpn_advertised_protocols, Value)},
+ RecordCB);
+new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{alpn_preferred_protocols => validate_option(alpn_preferred_protocols, Value)},
+ RecordCB);
+new_ssl_options([{next_protocols_advertised, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{next_protocols_advertised => validate_option(next_protocols_advertised, Value)},
+ RecordCB);
+new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #{} = Opts, RecordCB) ->
new_ssl_options(Rest,
- Opts#ssl_options{eccs =
- handle_eccs_option(Value, RecordCB:highest_protocol_version())
+ Opts#{next_protocol_selector =>
+ make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))},
+ RecordCB);
+new_ssl_options([{log_alert, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{log_level => validate_option(log_alert, Value)}, RecordCB);
+new_ssl_options([{log_level, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{log_level => validate_option(log_level, Value)}, RecordCB);
+new_ssl_options([{server_name_indication, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{server_name_indication => validate_option(server_name_indication, Value)}, RecordCB);
+new_ssl_options([{honor_cipher_order, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{honor_cipher_order => validate_option(honor_cipher_order, Value)}, RecordCB);
+new_ssl_options([{honor_ecc_order, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{honor_ecc_order => validate_option(honor_ecc_order, Value)}, RecordCB);
+new_ssl_options([{eccs, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#{eccs => handle_eccs_option(Value, RecordCB:highest_protocol_version())
},
RecordCB);
-new_ssl_options([{supported_groups, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+new_ssl_options([{supported_groups, Value} | Rest], #{} = Opts, RecordCB) ->
new_ssl_options(Rest,
- Opts#ssl_options{supported_groups =
- handle_supported_groups_option(Value, RecordCB:highest_protocol_version())
+ Opts#{supported_groups =>
+ handle_supported_groups_option(Value, RecordCB:highest_protocol_version())
},
RecordCB);
-new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest,
- Opts#ssl_options{signature_algs =
- handle_hashsigns_option(Value,
- tls_version(RecordCB:highest_protocol_version()))},
+new_ssl_options([{signature_algs, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#{signature_algs =>
+ handle_hashsigns_option(Value,
+ tls_version(RecordCB:highest_protocol_version()))},
RecordCB);
-new_ssl_options([{signature_algs_cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+new_ssl_options([{signature_algs_cert, Value} | Rest], #{} = Opts, RecordCB) ->
new_ssl_options(
Rest,
- Opts#ssl_options{signature_algs_cert =
- handle_signature_algorithms_option(
- Value,
- tls_version(RecordCB:highest_protocol_version()))},
+ Opts#{signature_algs_cert =>
+ handle_signature_algorithms_option(
+ Value,
+ tls_version(RecordCB:highest_protocol_version()))},
RecordCB);
-new_ssl_options([{protocol, dtls = Value} | Rest], #ssl_options{} = Opts, dtls_record = RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
-new_ssl_options([{protocol, tls = Value} | Rest], #ssl_options{} = Opts, tls_record = RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
-new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) ->
+new_ssl_options([{protocol, dtls = Value} | Rest], #{} = Opts, dtls_record = RecordCB) ->
+ new_ssl_options(Rest, Opts#{protocol => Value}, RecordCB);
+new_ssl_options([{protocol, tls = Value} | Rest], #{} = Opts, tls_record = RecordCB) ->
+ new_ssl_options(Rest, Opts#{protocol => Value}, RecordCB);
+new_ssl_options([{Key, Value} | _Rest], #{}, _) ->
throw({error, {options, {Key, Value}}}).
@@ -2433,3 +2486,10 @@ add_filter(undefined, Filters) ->
Filters;
add_filter(Filter, Filters) ->
[Filter | Filters].
+
+%% Convert the record #ssl_options{} into a map for internal usage
+options_to_map(Options) ->
+ Fields = record_info(fields, ssl_options),
+ [_Tag| Values] = tuple_to_list(Options),
+ L = lists:zip(Fields, Values),
+ maps:from_list(L).
diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl
index 1e6dab9276..7d1e80c1cc 100644
--- a/lib/ssl/src/ssl_config.erl
+++ b/lib/ssl/src/ssl_config.erl
@@ -28,16 +28,20 @@
-export([init/2]).
-init(SslOpts, Role) ->
+init(#{erl_dist := ErlDist,
+ key := Key,
+ keyfile := KeyFile,
+ password := Password,
+ dh := DH,
+ dhfile := DHFile} = SslOpts, Role) ->
- init_manager_name(SslOpts#ssl_options.erl_dist),
+ init_manager_name(ErlDist),
{ok, #{pem_cache := PemCache} = Config}
= init_certificates(SslOpts, Role),
PrivateKey =
- init_private_key(PemCache, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
- SslOpts#ssl_options.password, Role),
- DHParams = init_diffie_hellman(PemCache, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
+ init_private_key(PemCache, Key, KeyFile, Password, Role),
+ DHParams = init_diffie_hellman(PemCache, DH, DHFile, Role),
{ok, Config#{private_key => PrivateKey, dh_params => DHParams}}.
init_manager_name(false) ->
@@ -47,12 +51,12 @@ init_manager_name(true) ->
put(ssl_manager, ssl_manager:name(dist)),
put(ssl_pem_cache, ssl_pem_cache:name(dist)).
-init_certificates(#ssl_options{cacerts = CaCerts,
- cacertfile = CACertFile,
- certfile = CertFile,
- cert = Cert,
- crl_cache = CRLCache
- }, Role) ->
+init_certificates(#{cacerts := CaCerts,
+ cacertfile := CACertFile,
+ certfile := CertFile,
+ cert := Cert,
+ crl_cache := CRLCache
+ }, Role) ->
{ok, Config} =
try
Certs = case CaCerts of
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 2483509228..6789c2c23f 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -80,7 +80,7 @@
-spec connect(tls_connection | dtls_connection,
ssl:host(), inet:port_number(),
port() | {tuple(), port()}, %% TLS | DTLS
- {#ssl_options{}, #socket_options{},
+ {ssl_options(), #socket_options{},
%% Tracker only needed on server side
undefined},
pid(), tuple(), timeout()) ->
@@ -98,7 +98,7 @@ connect(Connection, Host, Port, Socket, Options, User, CbInfo, Timeout) ->
%%--------------------------------------------------------------------
-spec handshake(tls_connection | dtls_connection,
inet:port_number(), port(),
- {#ssl_options{}, #socket_options{}, undefined | pid()},
+ {ssl_options(), #socket_options{}, undefined | pid()},
pid(), tuple(), timeout()) ->
{ok, #sslsocket{}} | {error, reason()}.
%%
@@ -130,7 +130,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) ->
end.
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}}, timeout()) ->
+-spec handshake(#sslsocket{}, {ssl_options(),#socket_options{}}, timeout()) ->
{ok, #sslsocket{}} | {ok, #sslsocket{}, map()} | {error, reason()}.
%%
%% Description: Starts ssl handshake with some new options
@@ -331,7 +331,7 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
handle_own_alert(Alert0, _, StateName,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
try %% Try to tell the other side
send_alert(Alert0, StateName, State)
catch _:_ -> %% Can crash if we are in a uninitialized state
@@ -339,7 +339,7 @@ handle_own_alert(Alert0, _, StateName,
end,
try %% Try to tell the local user
Alert = Alert0#alert{role = Role},
- log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert),
+ log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
@@ -376,13 +376,13 @@ handle_alert(#alert{level = ?FATAL} = Alert0, StateName,
transport_cb = Transport,
protocol_cb = Connection},
connection_env = #connection_env{user_application = {_Mon, Pid}},
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
start_or_recv_from = From,
session = Session,
socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
Alert = Alert0#alert{role = opposite_role(Role)},
- log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(),
+ log_alert(LogLevel, Role, Connection:protocol_name(),
StateName, Alert),
Pids = Connection:pids(State),
alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection),
@@ -399,9 +399,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert0,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
handshake_env = #handshake_env{renegotiation = {true, internal}},
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
Alert = Alert0#alert{role = opposite_role(Role)},
- log_alert(SslOpts#ssl_options.log_level, Role,
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
{stop,{shutdown, peer_close}, State};
@@ -410,9 +410,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv,
- ssl_options = SslOpts
+ ssl_options = #{log_level := LogLevel}
} = State0) ->
- log_alert(SslOpts#ssl_options.log_level, Role,
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
State = Connection:reinit_handshake_data(State0),
@@ -422,9 +422,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv,
- ssl_options = SslOpts
+ ssl_options = #{log_level := LogLevel}
} = State0) ->
- log_alert(SslOpts#ssl_options.log_level, Role,
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
%% Go back to connection!
@@ -435,8 +435,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
- ssl_options = SslOpts} = State) ->
- log_alert(SslOpts#ssl_options.log_level, Role,
+ ssl_options = #{log_level := LogLevel}} = State) ->
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName,
Alert#alert{role = opposite_role(Role)}),
Connection:next_event(StateName, no_record, State).
@@ -773,7 +773,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
end.
%%--------------------------------------------------------------------
--spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}.
+-spec ssl_config(ssl_options(), client | server, #state{}) -> #state{}.
%%--------------------------------------------------------------------
ssl_config(Opts, Role, #state{static_env = InitStatEnv0,
handshake_env = HsEnv,
@@ -870,7 +870,7 @@ user_hello({call, From}, {handshake_continue, NewOptions, Timeout},
#state{static_env = #static_env{role = Role},
handshake_env = #handshake_env{hello = Hello},
ssl_options = Options0} = State0, _Connection) ->
- Options = ssl:handle_options(NewOptions, Options0#ssl_options{handshake = full}),
+ Options = ssl:handle_options(NewOptions, Options0#{handshake => full}),
State = ssl_config(Options, Role, State0),
{next_state, hello, State#state{start_or_recv_from = From},
[{next_event, internal, Hello}, {{timeout, handshake}, Timeout, close}]};
@@ -962,21 +962,21 @@ certify(info, Msg, State, _) ->
certify(internal, #certificate{asn1_certificates = []},
#state{static_env = #static_env{role = server},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = #ssl_options{verify = verify_peer,
- fail_if_no_peer_cert = true}} =
+ ssl_options = #{verify := verify_peer,
+ fail_if_no_peer_cert := true}} =
State, _) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
certify(internal, #certificate{asn1_certificates = []},
#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{verify = verify_peer,
- fail_if_no_peer_cert = false}} =
+ ssl_options = #{verify := verify_peer,
+ fail_if_no_peer_cert := false}} =
State0, Connection) ->
Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_requested = false});
certify(internal, #certificate{},
#state{static_env = #static_env{role = server},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = #ssl_options{verify = verify_none}} =
+ ssl_options = #{verify := verify_none}} =
State, _) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate),
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
@@ -1067,7 +1067,7 @@ certify(internal, #certificate_request{} = CertRequest,
handshake_env = HsEnv,
connection_env = #connection_env{negotiated_version = Version},
session = #session{own_certificate = Cert},
- ssl_options = #ssl_options{signature_algs = SupportedHashSigns}} = State, Connection) ->
+ ssl_options = #{signature_algs := SupportedHashSigns}} = State, Connection) ->
case ssl_handshake:select_hashsign(CertRequest, Cert,
SupportedHashSigns, ssl:tls_version(Version)) of
#alert {} = Alert ->
@@ -1085,7 +1085,7 @@ certify(internal, #server_hello_done{},
handshake_env = #handshake_env{kex_algorithm = KexAlg,
premaster_secret = undefined,
server_psk_identity = PSKIdentity} = HsEnv,
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection)
+ ssl_options = #{user_lookup_fun := PSKLookup}} = State0, Connection)
when KexAlg == psk ->
case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup) of
#alert{} = Alert ->
@@ -1103,7 +1103,7 @@ certify(internal, #server_hello_done{},
premaster_secret = undefined,
server_psk_identity = PSKIdentity} = HsEnv,
session = #session{master_secret = undefined},
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection)
+ ssl_options = #{user_lookup_fun := PSKLookup}} = State0, Connection)
when KexAlg == rsa_psk ->
Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
RSAPremasterSecret = <<?BYTE(Major), ?BYTE(Minor), Rand/binary>>,
@@ -1152,7 +1152,7 @@ certify(internal, #server_hello_done{},
certify(internal = Type, #client_key_exchange{} = Msg,
#state{static_env = #static_env{role = server},
client_certificate_requested = true,
- ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State,
+ ssl_options = #{fail_if_no_peer_cert := true}} = State,
Connection) ->
%% We expect a certificate here
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection);
@@ -1297,7 +1297,7 @@ connection(cast, {internal_renegotiate, WriteState}, #state{static_env = #static
Connection:renegotiate(State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, internal}},
connection_states = ConnectionStates#{current_write => WriteState}}, []);
connection(cast, {dist_handshake_complete, DHandle},
- #state{ssl_options = #ssl_options{erl_dist = true},
+ #state{ssl_options = #{erl_dist := true},
connection_env = CEnv,
socket_options = SockOpts} = State0, Connection) ->
process_flag(priority, normal),
@@ -1506,7 +1506,7 @@ handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_e
handle_info({'DOWN', MonitorRef, _, _, Reason}, _,
#state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}},
- ssl_options = #ssl_options{erl_dist = true}}) ->
+ ssl_options = #{erl_dist := true}}) ->
{stop, {shutdown, Reason}};
handle_info({'DOWN', MonitorRef, _, _, _}, _,
#state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}}) ->
@@ -1572,7 +1572,7 @@ terminate(Reason, connection, #state{static_env = #static_env{
transport_cb = Transport,
socket = Socket},
connection_states = ConnectionStates,
- ssl_options = #ssl_options{padding_check = Check}
+ ssl_options = #{padding_check := Check}
} = State) ->
handle_trusted_certs_db(State),
Alert = terminate_alert(Reason),
@@ -1590,13 +1590,13 @@ format_status(normal, [_, StateName, State]) ->
[{data, [{"State", {StateName, State}}]}];
format_status(terminate, [_, StateName, State]) ->
SslOptions = (State#state.ssl_options),
- NewOptions = SslOptions#ssl_options{password = ?SECRET_PRINTOUT,
- cert = ?SECRET_PRINTOUT,
- cacerts = ?SECRET_PRINTOUT,
- key = ?SECRET_PRINTOUT,
- dh = ?SECRET_PRINTOUT,
- psk_identity = ?SECRET_PRINTOUT,
- srp_identity = ?SECRET_PRINTOUT},
+ NewOptions = SslOptions#{password => ?SECRET_PRINTOUT,
+ cert => ?SECRET_PRINTOUT,
+ cacerts => ?SECRET_PRINTOUT,
+ key => ?SECRET_PRINTOUT,
+ dh => ?SECRET_PRINTOUT,
+ psk_identity => ?SECRET_PRINTOUT,
+ srp_identity => ?SECRET_PRINTOUT},
[{data, [{"State", {StateName, State#state{connection_states = ?SECRET_PRINTOUT,
protocol_buffers = ?SECRET_PRINTOUT,
user_data_buffer = ?SECRET_PRINTOUT,
@@ -1651,7 +1651,7 @@ do_server_hello(Type, #{next_protocol_negotiation := NextProtocols} =
handshake_env = HsEnv,
session = #session{session_id = SessId},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{versions = [HighestVersion|_]}}
+ ssl_options = #{versions := [HighestVersion|_]}}
= State0, Connection) when is_atom(Type) ->
%% TLS 1.3 - Section 4.1.3
%% Override server random values for TLS 1.3 downgrade protection mechanism.
@@ -1885,7 +1885,7 @@ certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientP
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_psk_identity{} = ClientKey,
#state{ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ #{user_lookup_fun := PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
@@ -1893,7 +1893,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
#state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params,
kex_keys = {_, ServerDhPrivateKey}},
ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ #{user_lookup_fun := PSKLookup}} = State0,
Connection) ->
PremasterSecret =
ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),
@@ -1901,7 +1901,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
#state{handshake_env = #handshake_env{kex_keys = ServerEcDhPrivateKey},
ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State,
+ #{user_lookup_fun := PSKLookup}} = State,
Connection) ->
PremasterSecret =
ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup),
@@ -1909,7 +1909,7 @@ certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,
#state{connection_env = #connection_env{private_key = Key},
ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ #{user_lookup_fun := PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
@@ -1995,10 +1995,10 @@ key_exchange(#state{static_env = #static_env{role = server},
State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
handshake_env = #handshake_env{kex_algorithm = psk},
- ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
+ ssl_options = #{psk_identity := undefined}} = State, _) ->
State;
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = psk,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2015,7 +2015,7 @@ key_exchange(#state{static_env = #static_env{role = server},
PrivateKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = dhe_psk,
diffie_hellman_params = #'DHParameter'{} = Params,
hashsign_algorithm = HashSignAlgo},
@@ -2037,7 +2037,7 @@ key_exchange(#state{static_env = #static_env{role = server},
#state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = ecdhe_psk,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2060,10 +2060,10 @@ key_exchange(#state{static_env = #static_env{role = server},
State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
handshake_env = #handshake_env{kex_algorithm = rsa_psk},
- ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
+ ssl_options = #{psk_identity := undefined}} = State, _) ->
State;
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = rsa_psk,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2081,7 +2081,7 @@ key_exchange(#state{static_env = #static_env{role = server},
PrivateKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{user_lookup_fun = LookupFun},
+ ssl_options = #{user_lookup_fun := LookupFun},
handshake_env = #handshake_env{kex_algorithm = KexAlg,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2146,28 +2146,28 @@ key_exchange(#state{static_env = #static_env{role = client},
key_exchange(#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{kex_algorithm = psk},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts} = State0, Connection) ->
+ ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
- {psk, SslOpts#ssl_options.psk_identity}),
+ {psk, PSKIdentity}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{kex_algorithm = dhe_psk,
kex_keys = {DhPubKey, _}},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts} = State0, Connection) ->
+ ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{dhe_psk,
- SslOpts#ssl_options.psk_identity, DhPubKey}),
+ PSKIdentity, DhPubKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{kex_algorithm = ecdhe_psk,
kex_keys = ECDHKeys},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts} = State0, Connection) ->
+ ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{ecdhe_psk,
- SslOpts#ssl_options.psk_identity, ECDHKeys}),
+ PSKIdentity, ECDHKeys}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
@@ -2175,9 +2175,9 @@ key_exchange(#state{static_env = #static_env{role = client},
public_key_info = PublicKeyInfo,
premaster_secret = PremasterSecret},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts}
+ ssl_options = #{psk_identity := PSKIdentity}}
= State0, Connection) ->
- Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity,
+ Msg = rsa_psk_key_exchange(ssl:tls_version(Version), PSKIdentity,
PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
@@ -2239,8 +2239,8 @@ request_client_cert(#state{handshake_env = #handshake_env{kex_algorithm = Alg}}
request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle,
cert_db_ref = CertDbRef},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = #ssl_options{verify = verify_peer,
- signature_algs = SupportedHashSigns},
+ ssl_options = #{verify := verify_peer,
+ signature_algs := SupportedHashSigns},
connection_states = ConnectionStates0} = State0, Connection) ->
#{security_parameters :=
#security_parameters{cipher_suite = CipherSuite}} =
@@ -2253,7 +2253,7 @@ request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle,
State = Connection:queue_handshake(Msg, State0),
State#state{client_certificate_requested = true};
-request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
+request_client_cert(#state{ssl_options = #{verify := verify_none}} =
State, _) ->
State.
@@ -2353,7 +2353,7 @@ calculate_secret(#server_psk_params{
calculate_secret(#server_dhe_psk_params{
dh_params = #server_dh_params{dh_p = Prime, dh_g = Base}} = ServerKey,
#state{handshake_env = HsEnv,
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
+ ssl_options = #{user_lookup_fun := PSKLookup}} =
State, Connection) ->
Keys = {_, PrivateDhKey} =
crypto:generate_key(dh, [Prime, Base]),
@@ -2363,7 +2363,7 @@ calculate_secret(#server_dhe_psk_params{
calculate_secret(#server_ecdhe_psk_params{
dh_params = #server_ecdh_params{curve = ECCurve}} = ServerKey,
- #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
+ #state{ssl_options = #{user_lookup_fun := PSKLookup}} =
#state{handshake_env = HsEnv,
session = Session} = State, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
@@ -2376,7 +2376,7 @@ calculate_secret(#server_ecdhe_psk_params{
calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey,
#state{handshake_env = HsEnv,
- ssl_options = #ssl_options{srp_identity = SRPId}} = State,
+ ssl_options = #{srp_identity := SRPId}} = State,
Connection) ->
Keys = generate_srp_client_keys(Generator, Prime, 0),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, Keys, SRPId),
@@ -2610,7 +2610,7 @@ set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other)
hibernate_after(connection = StateName,
- #state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}} = State,
+ #state{ssl_options= #{hibernate_after := HibernateAfter}} = State,
Actions) ->
{next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]};
hibernate_after(StateName, State, Actions) ->
@@ -2626,12 +2626,12 @@ terminate_alert(_) ->
?ALERT_REC(?FATAL, ?INTERNAL_ERROR).
handle_trusted_certs_db(#state{ssl_options =
- #ssl_options{cacertfile = <<>>, cacerts = []}}) ->
+ #{cacertfile := <<>>, cacerts := []}}) ->
%% No trusted certs specified
ok;
handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref,
cert_db = CertDb},
- ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined ->
+ ssl_options = #{cacertfile := <<>>}}) when CertDb =/= undefined ->
%% Certs provided as DER directly can not be shared
%% with other connections and it is safe to delete them when the connection ends.
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
@@ -2641,7 +2641,7 @@ handle_trusted_certs_db(#state{static_env = #static_env{file_ref_db = undefined}
ok;
handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref,
file_ref_db = RefDb},
- ssl_options = #ssl_options{cacertfile = File}}) ->
+ ssl_options = #{cacertfile := File}}) ->
case ssl_pkix_db:ref_count(Ref, RefDb, -1) of
0 ->
ssl_manager:clean_cert_db(Ref, File);
@@ -2678,11 +2678,11 @@ session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) ->
session_handle_params(_, Session) ->
Session.
-handle_session(Role = server, #ssl_options{reuse_sessions = true} = SslOpts,
+handle_session(Role = server, #{reuse_sessions := true} = SslOpts,
Host, Port, Session0) ->
register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, true);
-handle_session(Role = client, #ssl_options{verify = verify_peer,
- reuse_sessions = Reuse} = SslOpts,
+handle_session(Role = client, #{verify := verify_peer,
+ reuse_sessions := Reuse} = SslOpts,
Host, Port, Session0) when Reuse =/= false ->
register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, reg_type(Reuse));
handle_session(server, _, Host, Port, Session) ->
@@ -2709,7 +2709,7 @@ register_session(server, _, Port, #session{is_resumable = new} = Session0, _) ->
register_session(_, _, _, Session, _) ->
Session. %% Already registered
-host_id(client, _Host, #ssl_options{server_name_indication = Hostname}) when is_list(Hostname) ->
+host_id(client, _Host, #{server_name_indication := Hostname}) when is_list(Hostname) ->
Hostname;
host_id(_, Host, _) ->
Host.
@@ -2760,28 +2760,27 @@ negotiated_hashsign(HashSign = {_, _}, _, _, _) ->
HashSign.
ssl_options_list(SslOptions) ->
- Fileds = record_info(fields, ssl_options),
- Values = tl(tuple_to_list(SslOptions)),
- ssl_options_list(Fileds, Values, []).
+ L = maps:to_list(SslOptions),
+ ssl_options_list(L, []).
-ssl_options_list([],[], Acc) ->
+ssl_options_list([], Acc) ->
lists:reverse(Acc);
%% Skip internal options, only return user options
-ssl_options_list([protocol | Keys], [_ | Values], Acc) ->
- ssl_options_list(Keys, Values, Acc);
-ssl_options_list([erl_dist | Keys], [_ | Values], Acc) ->
- ssl_options_list(Keys, Values, Acc);
-ssl_options_list([renegotiate_at | Keys], [_ | Values], Acc) ->
- ssl_options_list(Keys, Values, Acc);
-ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) ->
- ssl_options_list(Keys, Values,
+ssl_options_list([{protocol, _}| T], Acc) ->
+ ssl_options_list(T, Acc);
+ssl_options_list([{erl_dist, _}|T], Acc) ->
+ ssl_options_list(T, Acc);
+ssl_options_list([{renegotiate_at, _}|T], Acc) ->
+ ssl_options_list(T, Acc);
+ssl_options_list([{ciphers = Key, Value}|T], Acc) ->
+ ssl_options_list(T,
[{Key, lists:map(
fun(Suite) ->
ssl_cipher_format:suite_bin_to_map(Suite)
end, Value)}
| Acc]);
-ssl_options_list([Key | Keys], [Value | Values], Acc) ->
- ssl_options_list(Keys, Values, [{Key, Value} | Acc]).
+ssl_options_list([{Key, Value}|T], Acc) ->
+ ssl_options_list(T, [{Key, Value} | Acc]).
handle_active_option(false, connection = StateName, To, Reply, State) ->
hibernate_after(StateName, State, [{reply, To, Reply}]);
@@ -3029,12 +3028,13 @@ handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{
}
end.
-update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
+update_ssl_options_from_sni(#{sni_fun := SNIFun,
+ sni_hosts := SNIHosts} = OrigSSLOptions, SNIHostname) ->
SSLOption =
- case OrigSSLOptions#ssl_options.sni_fun of
+ case SNIFun of
undefined ->
proplists:get_value(SNIHostname,
- OrigSSLOptions#ssl_options.sni_hosts);
+ SNIHosts);
SNIFun ->
SNIFun(SNIHostname)
end,
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 844368c761..be94fd05bb 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -92,7 +92,7 @@
-record(state, {
static_env :: #static_env{},
connection_env :: #connection_env{} | secret_printout(),
- ssl_options :: #ssl_options{},
+ ssl_options :: ssl_options(),
socket_options :: #socket_options{},
%% Hanshake %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index bd2efa9fbb..1c8a2ca452 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -336,25 +336,30 @@ next_protocol(SelectedProtocol) ->
%% Handle handshake messages
%%====================================================================
%%--------------------------------------------------------------------
--spec certify(#certificate{}, db_handle(), certdb_ref(), #ssl_options{}, term(),
+-spec certify(#certificate{}, db_handle(), certdb_ref(), ssl_options(), term(),
client | server, inet:hostname() | inet:ip_address()) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- Opts, CRLDbHandle, Role, Host) ->
-
- ServerName = server_name(Opts#ssl_options.server_name_indication, Host, Role),
+ #{server_name_indication := ServerNameIndication,
+ partial_chain := PartialChain,
+ verify_fun := VerifyFun,
+ customize_hostname_check := CustomizeHostnameCheck,
+ crl_check := CrlCheck,
+ depth := Depth} = Opts, CRLDbHandle, Role, Host) ->
+
+ ServerName = server_name(ServerNameIndication, Host, Role),
[PeerCert | ChainCerts ] = ASN1Certs,
try
{TrustedCert, CertPath} =
ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef,
- Opts#ssl_options.partial_chain),
- ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role,
+ PartialChain),
+ ValidationFunAndState = validation_fun_and_state(VerifyFun, Role,
CertDbHandle, CertDbRef, ServerName,
- Opts#ssl_options.customize_hostname_check,
- Opts#ssl_options.crl_check, CRLDbHandle, CertPath),
- Options = [{max_path_length, Opts#ssl_options.depth},
+ CustomizeHostnameCheck,
+ CrlCheck, CRLDbHandle, CertPath),
+ Options = [{max_path_length, Depth},
{verify_fun, ValidationFunAndState}],
case public_key:pkix_path_validation(TrustedCert, CertPath, Options) of
{ok, {PublicKeyInfo,_}} ->
@@ -945,7 +950,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port, #session{ecc = ECCCurve0} =
Session, Version,
- #ssl_options{ciphers = UserSuites, honor_cipher_order = HonorCipherOrder} = SslOpts,
+ #{ciphers := UserSuites, honor_cipher_order := HonorCipherOrder} = SslOpts,
Cache, CacheCb, Cert) ->
{SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId,
SslOpts, Cert,
@@ -1071,27 +1076,29 @@ client_hello_extensions(Version, CipherSuites, SslOpts, ConnectionStates, Renego
add_tls12_extensions(_Version,
- SslOpts,
+ #{alpn_advertised_protocols := AlpnAdvertisedProtocols,
+ next_protocol_selector := NextProtocolSelector,
+ server_name_indication := ServerNameIndication} = SslOpts,
ConnectionStates,
Renegotiation) ->
SRP = srp_user(SslOpts),
#{renegotiation_info => renegotiation_info(tls_record, client,
ConnectionStates, Renegotiation),
srp => SRP,
- alpn => encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation),
+ alpn => encode_alpn(AlpnAdvertisedProtocols, Renegotiation),
next_protocol_negotiation =>
- encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
+ encode_client_protocol_negotiation(NextProtocolSelector,
Renegotiation),
- sni => sni(SslOpts#ssl_options.server_name_indication)
+ sni => sni(ServerNameIndication)
}.
add_common_extensions({3,4},
HelloExtensions,
_CipherSuites,
- #ssl_options{eccs = SupportedECCs,
- supported_groups = Groups,
- signature_algs = SignatureSchemes}) ->
+ #{eccs := SupportedECCs,
+ supported_groups := Groups,
+ signature_algs := SignatureSchemes}) ->
{EcPointFormats, _} =
client_ecc_extensions(SupportedECCs),
HelloExtensions#{ec_point_formats => EcPointFormats,
@@ -1101,8 +1108,8 @@ add_common_extensions({3,4},
add_common_extensions(Version,
HelloExtensions,
CipherSuites,
- #ssl_options{eccs = SupportedECCs,
- signature_algs = SupportedHashSigns}) ->
+ #{eccs := SupportedECCs,
+ signature_algs := SupportedHashSigns}) ->
{EcPointFormats, EllipticCurves} =
case advertises_ec_ciphers(
@@ -1120,8 +1127,8 @@ add_common_extensions(Version,
maybe_add_tls13_extensions({3,4},
HelloExtensions0,
- #ssl_options{signature_algs_cert = SignatureSchemes,
- versions = SupportedVersions},
+ #{signature_algs_cert := SignatureSchemes,
+ versions := SupportedVersions},
KeyShare) ->
HelloExtensions =
HelloExtensions0#{client_hello_versions =>
@@ -1223,8 +1230,8 @@ signature_algs_cert(SignatureSchemes) ->
handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
Exts, Version,
- #ssl_options{secure_renegotiate = SecureRenegotation,
- alpn_preferred_protocols = ALPNPreferredProtocols} = Opts,
+ #{secure_renegotiate := SecureRenegotation,
+ alpn_preferred_protocols := ALPNPreferredProtocols} = Opts,
#session{cipher_suite = NegotiatedCipherSuite,
compression_method = Compression} = Session0,
ConnectionStates0, Renegotiation) ->
@@ -1259,8 +1266,8 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
Exts, Version,
- #ssl_options{secure_renegotiate = SecureRenegotation,
- next_protocol_selector = NextProtoSelector},
+ #{secure_renegotiate := SecureRenegotation,
+ next_protocol_selector := NextProtoSelector},
ConnectionStates0, Renegotiation) ->
ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version,
maps:get(renegotiation_info, Exts, undefined), Random,
@@ -1477,7 +1484,7 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) ->
select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
-srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
+srp_user(#{srp_identity := {UserName, _}}) ->
#srp{username = UserName};
srp_user(_) ->
undefined.
@@ -1644,12 +1651,12 @@ handle_path_validation_error({bad_cert, unknown_ca} = Reason, PeerCert, Chain,
Opts, Options, CertDbHandle, CertsDbRef) ->
handle_incomplete_chain(PeerCert, Chain, Opts, Options, CertDbHandle, CertsDbRef, Reason);
handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chain0,
- Opts, Options, CertDbHandle, CertsDbRef) ->
+ #{partial_chain := PartialChain} = Opts, Options, CertDbHandle, CertsDbRef) ->
case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef, Chain0) of
{ok, _, [PeerCert | Chain] = OrdedChain} when Chain =/= Chain0 -> %% Chain appaears to be unorded
{Trusted, Path} = ssl_certificate:trusted_cert_and_path(OrdedChain,
CertDbHandle, CertsDbRef,
- Opts#ssl_options.partial_chain),
+ PartialChain),
case public_key:pkix_path_validation(Trusted, Path, Options) of
{ok, {PublicKeyInfo,_}} ->
{PeerCert, PublicKeyInfo};
@@ -1663,12 +1670,13 @@ handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chai
handle_path_validation_error(Reason, _, _, _, _,_, _) ->
path_validation_alert(Reason).
-handle_incomplete_chain(PeerCert, Chain0, Opts, Options, CertDbHandle, CertsDbRef, PathError0) ->
+handle_incomplete_chain(PeerCert, Chain0,
+ #{partial_chain := PartialChain}, Options, CertDbHandle, CertsDbRef, PathError0) ->
case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef) of
{ok, _, [PeerCert | _] = Chain} when Chain =/= Chain0 -> %% Chain candidate found
{Trusted, Path} = ssl_certificate:trusted_cert_and_path(Chain,
CertDbHandle, CertsDbRef,
- Opts#ssl_options.partial_chain),
+ PartialChain),
case public_key:pkix_path_validation(Trusted, Path, Options) of
{ok, {PublicKeyInfo,_}} ->
{PeerCert, PublicKeyInfo};
@@ -2834,7 +2842,7 @@ handle_next_protocol_on_server(undefined, _Renegotiation, _SslOpts) ->
undefined;
handle_next_protocol_on_server(#next_protocol_negotiation{extension_data = <<>>},
- false, #ssl_options{next_protocols_advertised = Protocols}) ->
+ false, #{next_protocols_advertised := Protocols}) ->
Protocols;
handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 06c3ccae45..fc9f16a189 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -202,7 +202,7 @@
-type gen_fsm_state_return() :: {next_state, state_name(), any()} |
{next_state, state_name(), any(), timeout()} |
{stop, any(), any()}.
--type ssl_options() :: #ssl_options{}.
+-type ssl_options() :: map().
-endif. % -ifdef(ssl_internal).
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 44305c65fe..fd012acd5e 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
--spec client_id({ssl:host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(),
+-spec client_id({ssl:host(), inet:port_number(), ssl_options()}, db_handle(), atom(),
undefined | binary()) -> binary().
%%
%% Description: Should be called by the client side to get an id
%% for the client hello message.
%%--------------------------------------------------------------------
-client_id({Host, Port, #ssl_options{reuse_session = SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)->
+client_id({Host, Port, #{reuse_session := SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)->
case CacheCb:lookup(Cache, {{Host, Port}, SessionId}) of
undefined ->
<<>>;
@@ -99,7 +99,7 @@ server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-select_session({_, _, #ssl_options{reuse_sessions = Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true ->
+select_session({_, _, #{reuse_sessions := Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true ->
%% If reuse_sessions == true | save a new session should be created
no_session;
select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
@@ -108,7 +108,7 @@ select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
select_session([], _, _) ->
no_session;
-select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
+select_session(Sessions, #{ciphers := Ciphers}, OwnCert) ->
IsNotResumable =
fun(Session) ->
not (resumable(Session#session.is_resumable) andalso
@@ -120,9 +120,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
[Session | _] -> Session#session.session_id
end.
-is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) ->
+is_resumable(_, _, #{reuse_sessions := false}, _, _, _, _) ->
{false, undefined};
-is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache,
+is_resumable(SuggestedSessionId, Port, #{reuse_session := ReuseFun} = Options, Cache,
CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
@@ -149,8 +149,8 @@ resumable(new) ->
resumable(IsResumable) ->
IsResumable.
-reusable_options(#ssl_options{fail_if_no_peer_cert = true,
- verify = verify_peer}, Session) ->
+reusable_options(#{fail_if_no_peer_cert := true,
+ verify := verify_peer}, Session) ->
(Session#session.peer_certificate =/= undefined);
reusable_options(_,_) ->
true.
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 3998f03519..cf104cd805 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -98,7 +98,7 @@
%%====================================================================
%% Setup
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
+start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts,
User, {CbModule, _,_, _, _} = CbInfo,
Timeout) ->
try
@@ -112,7 +112,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
Error
end;
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts,
+start_fsm(Role, Host, Port, Socket, {#{erl_dist := true},_, Tracker} = Opts,
User, {CbModule, _,_, _, _} = CbInfo,
Timeout) ->
try
@@ -136,10 +136,10 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
start_link(Role, Sender, Host, Port, Socket, Options, User, CbInfo) ->
{ok, proc_lib:spawn_link(?MODULE, init, [[Role, Sender, Host, Port, Socket, Options, User, CbInfo]])}.
-init([Role, Sender, Host, Port, Socket, {SslOpts, _, _} = Options, User, CbInfo]) ->
+init([Role, Sender, Host, Port, Socket, {#{erl_dist := ErlDist}, _, _} = Options, User, CbInfo]) ->
process_flag(trap_exit, true),
link(Sender),
- case SslOpts#ssl_options.erl_dist of
+ case ErlDist of
true ->
process_flag(priority, max);
_ ->
@@ -170,7 +170,7 @@ next_record(_, #state{handshake_env =
next_record(_, #state{protocol_buffers =
#protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts},
connection_states = ConnectionStates,
- ssl_options = #ssl_options{padding_check = Check}} = State) ->
+ ssl_options = #{padding_check := Check}} = State) ->
next_record(State, CipherTexts, ConnectionStates, Check);
next_record(connection, #state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []},
protocol_specific = #{active_n_toggle := true}
@@ -385,12 +385,12 @@ send_handshake(Handshake, State) ->
queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
connection_env = #connection_env{negotiated_version = Version},
flight_buffer = Flight0,
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinHandshake),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinHandshake),
State0#state{connection_states = ConnectionStates,
handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist},
@@ -406,11 +406,11 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_version = Version},
flight_buffer = Flight0,
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
connection_states = ConnectionStates0} = State0) ->
{BinChangeCipher, ConnectionStates} =
encode_change_cipher(Msg, Version, ConnectionStates0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinChangeCipher),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinChangeCipher),
State0#state{connection_states = ConnectionStates,
flight_buffer = Flight0 ++ [BinChangeCipher]}.
@@ -454,12 +454,12 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
send_alert(Alert, #state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
connection_states = ConnectionStates0} = StateData0) ->
{BinMsg, ConnectionStates} =
encode_alert(Alert, Version, ConnectionStates0),
tls_socket:send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
StateData0#state{connection_states = ConnectionStates}.
%% If an ALERT sent in the connection state, should cause the TLS
@@ -542,7 +542,8 @@ init({call, From}, {start, Timeout},
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
connection_env = CEnv,
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel,
+ versions := Versions} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
@@ -550,13 +551,13 @@ init({call, From}, {start, Timeout},
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert, KeyShare),
- HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions),
+ HelloVersion = tls_record:hello_version(Versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
tls_socket:send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Hello),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
State = State0#state{connection_states = ConnectionStates,
connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version
@@ -592,14 +593,14 @@ error(_, _, _) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
hello(internal, #client_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
hello(internal, #server_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
@@ -982,8 +983,9 @@ code_change(_OldVsn, StateName, State, _) ->
%%--------------------------------------------------------------------
initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) ->
- #ssl_options{beast_mitigation = BeastMitigation,
- erl_dist = IsErlDist} = SSLOptions,
+ #{beast_mitigation := BeastMitigation,
+ erl_dist := IsErlDist,
+ client_renegotiation := ClientRenegotiation} = SSLOptions,
ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -1017,7 +1019,7 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
handshake_env = #handshake_env{
tls_handshake_history = ssl_handshake:init_handshake_history(),
renegotiation = {false, first},
- allow_renegotiate = SSLOptions#ssl_options.client_renegotiation
+ allow_renegotiate = ClientRenegotiation
},
connection_env = #connection_env{user_application = {UserMonitor, User}},
socket_options = SocketOptions,
@@ -1042,8 +1044,8 @@ initialize_tls_sender(#state{static_env = #static_env{
},
connection_env = #connection_env{negotiated_version = Version},
socket_options = SockOpts,
- ssl_options = #ssl_options{renegotiate_at = RenegotiateAt,
- log_level = LogLevel},
+ ssl_options = #{renegotiate_at := RenegotiateAt,
+ log_level := LogLevel},
connection_states = #{current_write := ConnectionWriteState},
protocol_specific = #{sender := Sender}}) ->
Init = #{current_write => ConnectionWriteState,
@@ -1261,7 +1263,7 @@ unprocessed_events(Events) ->
assert_buffer_sanity(<<?BYTE(_Type), ?UINT24(Length), Rest/binary>>,
- #ssl_options{max_handshake_size = Max}) when
+ #{max_handshake_size := Max}) when
Length =< Max ->
case size(Rest) of
N when N < Length ->
@@ -1295,18 +1297,17 @@ ensure_sender_terminate(_, #state{protocol_specific = #{sender := Sender}}) ->
end,
spawn(Kill).
-maybe_generate_client_shares(#ssl_options{
- versions = [Version|_],
- supported_groups =
- #supported_groups{
- supported_groups = [Group|_]}})
+maybe_generate_client_shares(#{versions := [Version|_],
+ supported_groups :=
+ #supported_groups{
+ supported_groups = [Group|_]}})
when Version =:= {3,4} ->
%% Generate only key_share entry for the most preferred group
ssl_cipher:generate_client_shares([Group]);
maybe_generate_client_shares(_) ->
undefined.
-choose_tls_version(#ssl_options{versions = Versions},
+choose_tls_version(#{versions := Versions},
#client_hello{
extensions = #{client_hello_versions :=
#client_hello_versions{versions = ClientVersions}
@@ -1322,7 +1323,7 @@ choose_tls_version(_, _) ->
'tls_v1.2'.
-effective_version(undefined, #ssl_options{versions = [Version|_]}) ->
+effective_version(undefined, #{versions := [Version|_]}) ->
Version;
effective_version(Version, _) ->
Version.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 37265e0759..203f89a0b8 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -51,17 +51,17 @@
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
- #ssl_options{}, integer(), atom(), boolean(), der_cert(),
+ ssl_options(), integer(), atom(), boolean(), der_cert(),
#key_share_client_hello{} | undefined) ->
#client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates,
- #ssl_options{versions = Versions,
- ciphers = UserSuites,
- fallback = Fallback
- } = SslOpts,
+ #{versions := Versions,
+ ciphers := UserSuites,
+ fallback := Fallback
+ } = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert, KeyShare) ->
Version = tls_record:highest_protocol_version(Versions),
@@ -95,7 +95,7 @@ client_hello(Host, Port, ConnectionStates,
}.
%%--------------------------------------------------------------------
--spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
+-spec hello(#server_hello{} | #client_hello{}, ssl_options(),
ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(),
atom(), ssl_record:connection_states(),
binary() | undefined, ssl:kex_algo()},
@@ -117,7 +117,7 @@ client_hello(Host, Port, ConnectionStates,
%% values.
hello(#server_hello{server_version = {Major, Minor},
random = <<_:24/binary,Down:8/binary>>},
- #ssl_options{versions = [{M,N}|_]}, _, _)
+ #{versions := [{M,N}|_]}, _, _)
when (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client
(Major =:= 3 andalso Minor =:= 3 andalso %% Negotiating TLS 1.2
Down =:= ?RANDOM_OVERRIDE_TLS12) orelse
@@ -131,7 +131,7 @@ hello(#server_hello{server_version = {Major, Minor},
%% equal to the second value if the ServerHello indicates TLS 1.1 or below.
hello(#server_hello{server_version = {Major, Minor},
random = <<_:24/binary,Down:8/binary>>},
- #ssl_options{versions = [{M,N}|_]}, _, _)
+ #{versions := [{M,N}|_]}, _, _)
when (M =:= 3 andalso N =:= 3) andalso %% TLS 1.2 client
(Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior
Down =:= ?RANDOM_OVERRIDE_TLS11) ->
@@ -156,7 +156,7 @@ hello(#server_hello{server_version = LegacyVersion,
extensions = #{server_hello_selected_version :=
#server_hello_selected_version{selected_version = Version} = HelloExt}
},
- #ssl_options{versions = SupportedVersions} = SslOpt,
+ #{versions := SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
%% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension
%% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version
@@ -190,7 +190,7 @@ hello(#server_hello{server_version = Version,
compression_method = Compression,
session_id = SessionId,
extensions = HelloExt},
- #ssl_options{versions = SupportedVersions} = SslOpt,
+ #{versions := SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
case tls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
@@ -221,7 +221,7 @@ hello(#client_hello{client_version = _ClientVersion,
extensions = #{client_hello_versions :=
#client_hello_versions{versions = ClientVersions}
}} = Hello,
- #ssl_options{versions = Versions} = SslOpts,
+ #{versions := Versions} = SslOpts,
Info, Renegotiation) ->
try
Version = ssl_handshake:select_supported_version(ClientVersions, Versions),
@@ -233,7 +233,7 @@ hello(#client_hello{client_version = _ClientVersion,
hello(#client_hello{client_version = ClientVersion,
cipher_suites = CipherSuites} = Hello,
- #ssl_options{versions = Versions} = SslOpts,
+ #{versions := Versions} = SslOpts,
Info, Renegotiation) ->
try
Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
@@ -269,7 +269,7 @@ encode_handshake(Package, Version) ->
%%--------------------------------------------------------------------
-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(),
- #ssl_options{}) ->
+ ssl_options()) ->
{[{tls_handshake(), binary()}], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
@@ -290,10 +290,10 @@ handle_client_hello(Version,
compression_methods = Compressions,
random = Random,
extensions = HelloExt},
- #ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns,
- eccs = SupportedECCs,
- honor_ecc_order = ECCOrder} = SslOpts,
+ #{versions := Versions,
+ signature_algs := SupportedHashSigns,
+ eccs := SupportedECCs,
+ honor_ecc_order := ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
@@ -404,11 +404,11 @@ enc_handshake(HandshakeMsg, Version) ->
%%--------------------------------------------------------------------
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>,
- Opts, Acc) ->
+ #{log_level := LogLevel} = Opts, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
try decode_handshake(Version, Type, Body) of
Handshake ->
- ssl_logger:debug(Opts#ssl_options.log_level, inbound, 'handshake', Handshake),
+ ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake),
get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc])
catch
_:_ ->
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index c29366e717..5f4c0b9a4a 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -336,8 +336,9 @@ decode_handshake(?ENCRYPTED_EXTENSIONS, <<?UINT16(Size), EncExts:Size/binary>>)
extensions = decode_extensions(EncExts, encrypted_extensions)
};
decode_handshake(?NEW_SESSION_TICKET, <<?UINT32(LifeTime), ?UINT32(Age),
- ?BYTE(Nonce), ?UINT16(TicketSize), Ticket:TicketSize/binary,
- BinExts/binary>>) ->
+ ?BYTE(NonceSize), Nonce:NonceSize/binary,
+ ?UINT16(TicketSize), Ticket:TicketSize/binary,
+ ?UINT16(BinExtSize), BinExts:BinExtSize/binary>>) ->
Exts = decode_extensions(BinExts, encrypted_extensions),
#new_session_ticket{ticket_lifetime = LifeTime,
ticket_age_add = Age,
@@ -501,11 +502,11 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
session_id = SessionId,
extensions = Extensions} = _Hello,
#state{connection_states = _ConnectionStates0,
- ssl_options = #ssl_options{ciphers = ServerCiphers,
- signature_algs = ServerSignAlgs,
- supported_groups = ServerGroups0,
- alpn_preferred_protocols = ALPNPreferredProtocols,
- honor_cipher_order = HonorCipherOrder},
+ ssl_options = #{ciphers := ServerCiphers,
+ signature_algs := ServerSignAlgs,
+ supported_groups := ServerGroups0,
+ alpn_preferred_protocols := ALPNPreferredProtocols,
+ honor_cipher_order := HonorCipherOrder},
session = #session{own_certificate = Cert}} = State0) ->
ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined),
ClientGroups = get_supported_groups(ClientGroups0),
@@ -600,8 +601,10 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
handshake_env = #handshake_env{renegotiation = {Renegotiation, _},
tls_handshake_history = _HHistory} = HsEnv,
connection_env = CEnv,
- ssl_options = #ssl_options{ciphers = ClientCiphers,
- supported_groups = ClientGroups0} = SslOpts,
+ ssl_options = #{ciphers := ClientCiphers,
+ supported_groups := ClientGroups0,
+ versions := Versions,
+ log_level := LogLevel} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
@@ -632,7 +635,7 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert, ClientKeyShare),
- HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions),
+ HelloVersion = tls_record:hello_version(Versions),
%% Update state
State1 = update_start_state(State0,
@@ -648,8 +651,8 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
{BinMsg, ConnectionStates, Handshake} =
tls_connection:encode_handshake(Hello, HelloVersion, ConnectionStates0, HHistory),
tls_socket:send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Hello),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
State = State2#state{
connection_states = ConnectionStates,
@@ -673,7 +676,7 @@ do_negotiated(start_handshake,
ecc = SelectedGroup,
sign_alg = SignatureScheme,
dh_public_value = ClientPublicKey},
- ssl_options = #ssl_options{} = SslOpts,
+ ssl_options = #{} = SslOpts,
key_share = KeyShare,
handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
connection_env = #connection_env{private_key = CertPrivateKey},
@@ -831,8 +834,8 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite,
session_id = SessionId,
extensions = Extensions} = ServerHello,
#state{key_share = ClientKeyShare0,
- ssl_options = #ssl_options{ciphers = ClientCiphers,
- supported_groups = ClientGroups0}} = State0) ->
+ ssl_options = #{ciphers := ClientCiphers,
+ supported_groups := ClientGroups0}} = State0) ->
ClientGroups = get_supported_groups(ClientGroups0),
ServerKeyShare0 = maps:get(key_share, Extensions, undefined),
ClientKeyShare = get_key_shares(ClientKeyShare0),
@@ -976,7 +979,7 @@ maybe_queue_cert_cert_cv(#state{client_certificate_requested = false} = State) -
maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0,
session = #session{session_id = _SessionId,
own_certificate = OwnCert},
- ssl_options = #ssl_options{} = _SslOpts,
+ ssl_options = #{} = _SslOpts,
key_share = _KeyShare,
handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
static_env = #static_env{
@@ -1063,12 +1066,11 @@ send_hello_retry_request(State0, _, _, _) ->
{ok, {State0, negotiated}}.
-maybe_send_certificate_request(State, #ssl_options{verify = verify_none}) ->
+maybe_send_certificate_request(State, #{verify := verify_none}) ->
{State, wait_finished};
-maybe_send_certificate_request(State, #ssl_options{
- verify = verify_peer,
- signature_algs = SignAlgs,
- signature_algs_cert = SignAlgsCert}) ->
+maybe_send_certificate_request(State, #{verify := verify_peer,
+ signature_algs := SignAlgs,
+ signature_algs_cert := SignAlgsCert}) ->
CertificateRequest = certificate_request(SignAlgs, SignAlgsCert),
{tls_connection:queue_handshake(CertificateRequest, State), wait_cert}.
@@ -1102,15 +1104,13 @@ process_certificate(#certificate_1_3{
certificate_request_context = <<>>,
certificate_list = []},
#state{ssl_options =
- #ssl_options{
- fail_if_no_peer_cert = false}} = State) ->
+ #{fail_if_no_peer_cert := false}} = State) ->
{ok, {State, wait_finished}};
process_certificate(#certificate_1_3{
certificate_request_context = <<>>,
certificate_list = []},
#state{ssl_options =
- #ssl_options{
- fail_if_no_peer_cert = true}} = State0) ->
+ #{fail_if_no_peer_cert := true}} = State0) ->
%% At this point the client believes that the connection is up and starts using
%% its traffic secrets. In order to be able send an proper Alert to the client
@@ -1121,8 +1121,8 @@ process_certificate(#certificate_1_3{
{error, {certificate_required, State}};
process_certificate(#certificate_1_3{certificate_list = Certs0},
#state{ssl_options =
- #ssl_options{signature_algs = SignAlgs,
- signature_algs_cert = SignAlgsCert} = SslOptions,
+ #{signature_algs := SignAlgs,
+ signature_algs_cert := SignAlgsCert} = SslOptions,
static_env =
#static_env{
role = Role,
@@ -1180,19 +1180,26 @@ update_encryption_state(client, State) ->
State.
-validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) ->
- ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role),
+validate_certificate_chain(Certs, CertDbHandle, CertDbRef,
+ #{server_name_indication := ServerNameIndication,
+ partial_chain := PartialChain,
+ verify_fun := VerifyFun,
+ customize_hostname_check := CustomizeHostnameCheck,
+ crl_check := CrlCheck,
+ depth := Depth} = SslOptions,
+ CRLDbHandle, Role, Host) ->
+ ServerName = ssl_handshake:server_name(ServerNameIndication, Host, Role),
[PeerCert | ChainCerts ] = Certs,
try
{TrustedCert, CertPath} =
ssl_certificate:trusted_cert_and_path(Certs, CertDbHandle, CertDbRef,
- SslOptions#ssl_options.partial_chain),
+ PartialChain),
ValidationFunAndState =
- ssl_handshake:validation_fun_and_state(SslOptions#ssl_options.verify_fun, Role,
+ ssl_handshake:validation_fun_and_state(VerifyFun, Role,
CertDbHandle, CertDbRef, ServerName,
- SslOptions#ssl_options.customize_hostname_check,
- SslOptions#ssl_options.crl_check, CRLDbHandle, CertPath),
- Options = [{max_path_length, SslOptions#ssl_options.depth},
+ CustomizeHostnameCheck,
+ CrlCheck, CRLDbHandle, CertPath),
+ Options = [{max_path_length, Depth},
{verify_fun, ValidationFunAndState}],
%% TODO: Validate if Certificate is using a supported signature algorithm
%% (signature_algs_cert)!
@@ -1531,9 +1538,7 @@ get_handshake_context_client(L) ->
%% CertificateRequest message.
verify_signature_algorithm(#state{
static_env = #static_env{role = Role},
- ssl_options =
- #ssl_options{
- signature_algs = LocalSignAlgs}} = State0,
+ ssl_options = #{signature_algs := LocalSignAlgs}} = State0,
#certificate_verify_1_3{algorithm = PeerSignAlg}) ->
case lists:member(PeerSignAlg, LocalSignAlgs) of
true ->
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 2aeab98929..cfd75076b3 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -83,7 +83,7 @@ init_connection_states(Role, BeastMitigation) ->
binary(),
[tls_version()] | tls_version(),
Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}},
- #ssl_options{}) ->
+ ssl_options()) ->
{Records :: [#ssl_tls{}],
Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} |
#alert{}.
@@ -495,7 +495,9 @@ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) ->
{lists:reverse(Acc),
{#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}};
-validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Version, Length) ->
+validate_tls_record_length(Versions, {_,Size0,_} = Q0,
+ #{log_level := LogLevel} = SslOpts,
+ Acc, Type, Version, Length) ->
if
Length =< ?MAX_CIPHER_TEXT_LENGTH ->
if
@@ -503,7 +505,7 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Versi
%% Complete record
{Fragment, Q} = binary_from_front(Length, Q0),
Record = #ssl_tls{type = Type, version = Version, fragment = Fragment},
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', Record),
+ ssl_logger:debug(LogLevel, inbound, 'record', Record),
decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined);
true ->
{lists:reverse(Acc),
diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl
index 6c32e6fa04..acd907905c 100644
--- a/lib/ssl/src/tls_socket.erl
+++ b/lib/ssl/src/tls_socket.erl
@@ -210,9 +210,9 @@ internal_inet_values() ->
default_inet_values() ->
[{packet_size, 0}, {packet,0}, {header, 0}, {active, true}, {mode, list}].
-inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = false} = SslOpts) ->
+inherit_tracker(ListenSocket, EmOpts, #{erl_dist := false} = SslOpts) ->
ssl_listen_tracker_sup:start_child([ListenSocket, EmOpts, SslOpts]);
-inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = true} = SslOpts) ->
+inherit_tracker(ListenSocket, EmOpts, #{erl_dist := true} = SslOpts) ->
ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]).
get_emulated_opts(TrackerPid) ->
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 0925c0facc..ec0addac59 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -60,7 +60,6 @@ MODULES = \
ssl_cert_SUITE\
openssl_server_cert_SUITE\
openssl_client_cert_SUITE\
- ssl_certificate_verify_SUITE\
ssl_crl_SUITE\
ssl_dist_SUITE \
ssl_dist_bench_SUITE \
diff --git a/lib/ssl/test/openssl_alpn_SUITE.erl b/lib/ssl/test/openssl_alpn_SUITE.erl
index 5008dba922..a54286c2cd 100644
--- a/lib/ssl/test/openssl_alpn_SUITE.erl
+++ b/lib/ssl/test/openssl_alpn_SUITE.erl
@@ -85,13 +85,11 @@ alpn_npn_coexist() ->
erlang_server_alpn_npn_openssl_client_alpn_npn
].
rengotiation_tests() ->
- case ssl_test_lib:sane_openssl_alpn_npn_renegotiate() of
- true ->
- [erlang_client_alpn_openssl_server_alpn_renegotiate,
- erlang_server_alpn_openssl_client_alpn_renegotiate];
- false ->
- []
- end.
+ [
+ erlang_client_alpn_openssl_server_alpn_renegotiate,
+ erlang_server_alpn_openssl_client_alpn_renegotiate
+ ].
+
init_per_suite(Config0) ->
case os:find_executable("openssl") of
false ->
@@ -148,15 +146,17 @@ init_per_testcase(TestCase, Config) ->
ct:timetrap({seconds, 10}),
special_init(TestCase, Config).
-special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate;
- TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate ->
- {ok, Version} = application:get_env(ssl, protocol_version),
+special_init(erlang_client_alpn_openssl_server_alpn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
ssl_test_lib:check_sane_openssl_renegotaite(Config, Version);
-special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn;
- TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn ->
- ssl_test_lib:check_openssl_npn_support(Config);
+special_init(erlang_server_alpn_openssl_client_alpn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ case ssl_test_lib:check_sane_openssl_renegotaite(Config, Version) of
+ Config ->
+ ssl_test_lib:openssl_allows_client_renegotaite(Config);
+ Skip ->
+ Skip
+ end;
special_init(_, Config) ->
Config.
diff --git a/lib/ssl/test/openssl_npn_SUITE.erl b/lib/ssl/test/openssl_npn_SUITE.erl
index 0294f4997f..ed3d81eba7 100644
--- a/lib/ssl/test/openssl_npn_SUITE.erl
+++ b/lib/ssl/test/openssl_npn_SUITE.erl
@@ -55,13 +55,10 @@ npn_tests() ->
erlang_client_openssl_server_npn_only_server].
npn_renegotiate_tests() ->
- case ssl_test_lib:sane_openssl_alpn_npn_renegotiate() of
- true ->
- [erlang_server_openssl_client_npn_renegotiate,
- erlang_client_openssl_server_npn_renegotiate];
- false ->
- []
- end.
+ [
+ erlang_server_openssl_client_npn_renegotiate,
+ erlang_client_openssl_server_npn_renegotiate
+ ].
init_per_suite(Config0) ->
case os:find_executable("openssl") of
@@ -119,13 +116,19 @@ init_per_testcase(TestCase, Config) ->
ct:timetrap({seconds, 10}),
special_init(TestCase, Config).
-special_init(TestCase, Config)
- when TestCase == erlang_client_npn_openssl_server_npn_renegotiate;
- TestCase == erlang_server_npn_openssl_client_npn_renegotiate ->
- {ok, Version} = application:get_env(ssl, protocol_version),
+special_init(erlang_client_openssl_server_npn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
ssl_test_lib:check_sane_openssl_renegotaite(Config, Version);
+special_init(erlang_server_openssl_client_npn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ case ssl_test_lib:check_sane_openssl_renegotaite(Config, Version) of
+ Config ->
+ ssl_test_lib:openssl_allows_client_renegotaite(Config);
+ Skip ->
+ Skip
+ end;
special_init(_, Config) ->
- Config.
+ Config.
end_per_testcase(_, Config) ->
Config.
diff --git a/lib/ssl/test/openssl_renegotiate_SUITE.erl b/lib/ssl/test/openssl_renegotiate_SUITE.erl
index 787b5208b8..a5c6056d63 100644
--- a/lib/ssl/test/openssl_renegotiate_SUITE.erl
+++ b/lib/ssl/test/openssl_renegotiate_SUITE.erl
@@ -104,7 +104,8 @@ init_per_group(GroupName, Config) ->
true ->
case ssl_test_lib:check_sane_openssl_version(GroupName) of
true ->
- ssl_test_lib:check_sane_openssl_renegotaite(ssl_test_lib:init_tls_version(GroupName, Config),
+ ssl_test_lib:check_sane_openssl_renegotaite(ssl_test_lib:init_tls_version(GroupName,
+ Config),
GroupName);
false ->
{skip, openssl_does_not_support_version}
@@ -123,21 +124,12 @@ end_per_group(GroupName, Config) ->
false ->
Config
end.
-
+init_per_testcase(erlang_client_openssl_server_nowrap_seqnum, Config) ->
+ ct:timetrap({seconds, 10}),
+ ssl_test_lib:openssl_allows_client_renegotaite(Config);
init_per_testcase(TestCase, Config) ->
ct:timetrap({seconds, 10}),
- special_init(TestCase, Config).
-
-special_init(TestCase, Config)
- when TestCase == erlang_client_openssl_server_renegotiate;
- TestCase == erlang_client_openssl_server_nowrap_seqnum;
- TestCase == erlang_server_openssl_client_nowrap_seqnum;
- TestCase == erlang_client_openssl_server_renegotiate_after_client_data
- ->
- {ok, Version} = application:get_env(ssl, protocol_version),
- ssl_test_lib:check_sane_openssl_renegotaite(Config, Version);
-special_init(_, Config) ->
- Config.
+ Config.
end_per_testcase(_, Config) ->
Config.
@@ -287,7 +279,7 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
erlang_server_openssl_client_nowrap_seqnum() ->
- [{doc, "Test that erlang client will renegotiate session when",
+ [{doc, "Test that erlang server will renegotiate session when",
"max sequence number celing is about to be reached. Although"
"in the testcase we use the test option renegotiate_at"
" to lower treashold substantially."}].
diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl
index 14e5024b91..c0f23cce58 100644
--- a/lib/ssl/test/ssl_api_SUITE.erl
+++ b/lib/ssl/test/ssl_api_SUITE.erl
@@ -1946,7 +1946,7 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) ->
{host, Hostname},
{from, self()},
{mfa, {?MODULE, connection_info_result, []}},
- {options, [{ciphers, ClientCiphers}, {honor_cipher_order, Honor}
+ {options, [{ciphers, ClientCiphers}
| ClientOpts]}]),
Version = ssl_test_lib:protocol_version(Config),
diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl
index fb1695f38a..d5ca9bcf02 100644
--- a/lib/ssl/test/ssl_cert_SUITE.erl
+++ b/lib/ssl/test/ssl_cert_SUITE.erl
@@ -44,17 +44,16 @@ all() ->
groups() ->
[
{'tlsv1.3', [], tls_1_3_protocol_groups()},
- {'tlsv1.2', [], pre_tls_1_3_protocol_groups()},
- {'tlsv1.1', [], pre_tls_1_3_protocol_groups()},
- {'tlsv1', [], pre_tls_1_3_protocol_groups()},
+ {'tlsv1.2', [], tls_1_2_protocol_groups()},
+ {'tlsv1.1', [], ssl_protocol_groups()},
+ {'tlsv1', [], ssl_protocol_groups()},
{'sslv3', [], ssl_protocol_groups()},
- {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()},
- {'dtlsv1', [], pre_tls_1_3_protocol_groups()},
- {rsa, [], all_version_tests()},
+ {'dtlsv1.2', [], tls_1_2_protocol_groups()},
+ {'dtlsv1', [], ssl_protocol_groups()},
+ {rsa, [], all_version_tests() ++ rsa_tests() ++ pre_tls_1_3_rsa_tests()},
{ecdsa, [], all_version_tests()},
{dsa, [], all_version_tests()},
- {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests() ++ [unsupported_sign_algo_client_auth,
- unsupported_sign_algo_cert_client_auth]},
+ {rsa_1_3, [], all_version_tests() ++ rsa_tests() ++ tls_1_3_tests() ++ tls_1_3_rsa_tests()},
{ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()}
].
@@ -62,7 +61,7 @@ ssl_protocol_groups() ->
[{group, rsa},
{group, dsa}].
-pre_tls_1_3_protocol_groups() ->
+tls_1_2_protocol_groups() ->
[{group, rsa},
{group, ecdsa},
{group, dsa}].
@@ -80,6 +79,22 @@ tls_1_3_tests() ->
hello_retry_client_auth_empty_cert_rejected
].
+pre_tls_1_3_rsa_tests() ->
+ [
+ key_auth_ext_sign_only
+ ].
+
+rsa_tests() ->
+ [
+ longer_chain
+ ].
+
+tls_1_3_rsa_tests() ->
+ [
+ unsupported_sign_algo_client_auth,
+ unsupported_sign_algo_cert_client_auth
+ ].
+
all_version_tests() ->
[
no_auth,
@@ -96,8 +111,18 @@ all_version_tests() ->
missing_root_cert_auth_user_verify_fun_reject,
verify_fun_always_run_client,
verify_fun_always_run_server,
- incomplete_chain_auth
- %%invalid_signature_client
+ incomplete_chain_auth,
+ invalid_signature_client,
+ invalid_signature_server,
+ critical_extension_auth,
+ critical_extension_client_auth,
+ critical_extension_no_auth,
+ extended_key_usage_auth,
+ extended_key_usage_client_auth,
+ cert_expired,
+ client_auth_once,
+ no_auth_key_identifier_ext,
+ no_auth_key_identifier_ext_keyEncipherment
].
init_per_suite(Config) ->
@@ -352,7 +377,8 @@ incomplete_chain_auth(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
verify_fun_always_run_client() ->
- [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
+ [{doc,"Verify that user verify_fun is always run (for valid and "
+ "valid_peer not only unknown_extension)"}].
verify_fun_always_run_client(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
@@ -392,7 +418,8 @@ verify_fun_always_run_client(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
verify_fun_always_run_server() ->
- [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
+ [{doc,"Verify that user verify_fun is always run (for valid and "
+ "valid_peer not only unknown_extension)"}].
verify_fun_always_run_server(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
@@ -434,12 +461,318 @@ verify_fun_always_run_server(Config) when is_list(Config) ->
invalid_signature_client() ->
ssl_cert_tests:invalid_signature_client().
invalid_signature_client(Config) when is_list(Config) ->
+ ssl:clear_pem_cache(),
ssl_cert_tests:invalid_signature_client(Config).
%%--------------------------------------------------------------------
invalid_signature_server() ->
- ssl_cert_tests:invalid_signature_client().
+ ssl_cert_tests:invalid_signature_server().
invalid_signature_server(Config) when is_list(Config) ->
- ssl_cert_tests:invalid_signature_client(Config).
+ ssl:clear_pem_cache(),
+ ssl_cert_tests:invalid_signature_server(Config).
+
+%%--------------------------------------------------------------------
+critical_extension_auth() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],[],[{extensions, Ext}]]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_none} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate).
+
+%%--------------------------------------------------------------------
+critical_extension_client_auth() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_client_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[],[],[{extensions, Ext}]]},
+ {server_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_none} | ClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. Therefore, verification should fail.
+ ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate).
+
+%%--------------------------------------------------------------------
+critical_extension_no_auth() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
+
+critical_extension_no_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],[], [{extensions, Ext}]]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+
+%%--------------------------------------------------------------------
+extended_key_usage_auth() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in server cert"}].
+
+extended_key_usage_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{?'id-ce-extKeyUsage',
+ [?'id-kp-serverAuth'], true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],[], [{extensions, Ext}]]},
+ {client_chain, DefaultCertConf}
+ ]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_none} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+extended_key_usage_client_auth() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in client and server cert"}].
+
+extended_key_usage_client_auth(Config) when is_list(Config) ->
+ ServerExt = x509_test:extensions([{?'id-ce-extKeyUsage',
+ [?'id-kp-serverAuth'], true}]),
+ ClientExt = x509_test:extensions([{?'id-ce-extKeyUsage',
+ [?'id-kp-clientAuth'], true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain, [[],[],[{extensions, ClientExt}]]},
+ {server_chain, [[],[],[{extensions, ServerExt}]]}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+cert_expired() ->
+ [{doc,"Test server with expired certificate"}].
+
+cert_expired(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],
+ [{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ []
+ ]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_client_alert(Server, Client, certificate_expired).
+
+%%--------------------------------------------------------------------
+client_auth_once() ->
+ [{doc,"Test server option verify_client_once"}].
+
+client_auth_once(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer},
+ {verify_client_once, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client0, ok),
+ Server ! {listen, {mfa, {ssl_test_lib, send_recv_result_active, []}}},
+ ssl_test_lib:close(Client0),
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client1, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+no_auth_key_identifier_ext() ->
+ [{doc, "Test cert that does not have authorityKeyIdentifier extension"}].
+
+no_auth_key_identifier_ext(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} =
+ ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain, DefaultCertConf},
+ {server_chain, DefaultCertConf}]),
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+no_auth_key_identifier_ext_keyEncipherment() ->
+ [{doc, "Test cert with keyEncipherment key_usage an no"
+ " authorityKeyIdentifier extension"}].
+
+no_auth_key_identifier_ext_keyEncipherment(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} =
+ ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[],[],[{extensions, ClientExt}]]},
+ {server_chain, DefaultCertConf}
+ ]),
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+key_auth_ext_sign_only() ->
+ [{doc, "Test that client with a certificate without keyEncipherment usage "
+ " extension can connect to a server with restricted cipher suites "}].
+key_auth_ext_sign_only(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ ClientExt = x509_test:extensions([{key_usage, [digitalSignature]}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} =
+ ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[],[],[{extensions, ClientExt}]]},
+ {server_chain, DefaultCertConf}
+ ]),
+ Version = proplists:get_value(version, Config),
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_peer}, {ciphers,
+ ssl_test_lib:rsa_non_signed_suites(n_version(Version))}
+ | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+longer_chain() ->
+ [{doc,"Test depth option"}].
+longer_chain(Config) when is_list(Config) ->
+ #{server_config := ServerOpts0,
+ client_config := ClientOpts0} =
+ public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}],
+ [{key, ssl_test_lib:hardcode_rsa_key(3)}],
+ [{key, ssl_test_lib:hardcode_rsa_key(4)}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]},
+ client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}),
+ [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerOpts0),
+ ClientCas = proplists:get_value(cacerts, ClientOpts0),
+
+ ServerOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, {cacerts, [ServerRoot]} |
+ proplists:delete(cacerts, ServerOpts0)], Config),
+ ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {depth, 5},
+ {cacerts, ServerCas ++ ClientCas} |
+ proplists:delete(cacerts, ClientOpts0)], Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
%%--------------------------------------------------------------------
%% TLS 1.3 Test cases -----------------------------------------------
@@ -454,7 +787,7 @@ hello_retry_request(Config) ->
ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
{supported_groups, [x448, x25519]}|ServerOpts0],
ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [secp256r1, x25519]}|ClientOpts0],
+ {supported_groups, [secp256r1, x25519]} | ClientOpts0],
ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
%%--------------------------------------------------------------------
custom_groups() ->
@@ -561,3 +894,29 @@ hello_retry_client_auth_empty_cert_rejected(Config) ->
{supported_groups, [secp256r1, x25519]}|ClientOpts2],
ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required).
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+two_digits_str(N) when N < 10 ->
+ lists:flatten(io_lib:format("0~p", [N]));
+two_digits_str(N) ->
+ lists:flatten(io_lib:format("~p", [N])).
+
+delete_authority_key_extension([], Acc) ->
+ lists:reverse(Acc);
+delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest],
+ Acc) ->
+ delete_authority_key_extension(Rest, Acc);
+delete_authority_key_extension([Head | Rest], Acc) ->
+ delete_authority_key_extension(Rest, [Head | Acc]).
+
+n_version(Version) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == 'tlsv1';
+ Version == 'sslv3'
+ ->
+ tls_record:protocol_version(Version);
+n_version(Version) when Version == 'dtlsv1.2';
+ Version == 'dtlsv1' ->
+ dtls_record:protocol_version(Version).
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
deleted file mode 100644
index f38858e0bf..0000000000
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ /dev/null
@@ -1,608 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2012-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
--module(ssl_certificate_verify_SUITE).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("public_key/include/public_key.hrl").
-
--include("ssl_internal.hrl").
--include("ssl_alert.hrl").
--include("ssl_internal.hrl").
--include("tls_record.hrl").
--include("tls_handshake.hrl").
-
--define(LONG_TIMEOUT, 600000).
-
-%%--------------------------------------------------------------------
-%% Common Test interface functions -----------------------------------
-%%--------------------------------------------------------------------
-all() ->
- [
- {group, 'tlsv1.3'},
- {group, 'tlsv1.2'},
- {group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'},
- {group, 'dtlsv1.2'},
- {group, 'dtlsv1'}
- ].
-
-groups() ->
- [
- {'tlsv1.3', [], all_protocol_groups()},
- {'tlsv1.2', [], all_protocol_groups()},
- {'tlsv1.1', [], all_protocol_groups()},
- {'tlsv1', [], all_protocol_groups()},
- {'sslv3', [], all_protocol_groups()},
- {'dtlsv1.2', [], all_protocol_groups()},
- {'dtlsv1', [], all_protocol_groups()},
- {active, [], tests()},
- {active_once, [], tests()},
- {passive, [], tests()},
- {error_handling, [],error_handling_tests()}
- ].
-
-all_protocol_groups() ->
- [{group, active},
- {group, passive},
- {group, active_once},
- {group, error_handling}].
-
-tests() ->
- [cert_expired,
- %invalid_signature_client,
- %%invalid_signature_server,
- extended_key_usage_verify_both,
- extended_key_usage_verify_server,
- critical_extension_verify_client,
- critical_extension_verify_server,
- critical_extension_verify_none,
- long_chain
- ].
-
-error_handling_tests()->
- [client_with_cert_cipher_suites_handshake,
- %%unknown_server_ca_accept_backwardscompatibility,
- no_authority_key_identifier,
- no_authority_key_identifier_keyEncipherment].
-
-init_per_suite(Config) ->
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- ssl_test_lib:clean_start(),
- ssl_test_lib:make_rsa_cert(Config)
- catch _:_ ->
- {skip, "Crypto did not start"}
- end.
-
-end_per_suite(_Config) ->
- ssl:stop(),
- application:stop(crypto).
-
-init_per_group(active, Config) ->
- [{active, true}, {receive_function, send_recv_result_active} | Config];
-init_per_group(active_once, Config) ->
- [{active, once}, {receive_function, send_recv_result_active_once} | Config];
-init_per_group(passive, Config) ->
- [{active, false}, {receive_function, send_recv_result} | Config];
-init_per_group(error_handling, Config) ->
- [{active, false}, {receive_function, send_recv_result} | Config];
-init_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- case ssl_test_lib:sufficient_crypto_support(GroupName) of
- true ->
- [{version, GroupName} | ssl_test_lib:init_tls_version(GroupName, Config)];
- false ->
- {skip, "Missing crypto support"}
- end
- end.
-
-end_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- ssl_test_lib:clean_tls_version(Config);
- false ->
- Config
- end.
-
-init_per_testcase(_TestCase, Config) ->
- ssl:stop(),
- ssl:start(),
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:pal(" ~p", [ dtls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 10}),
- Config.
-
-end_per_testcase(_TestCase, Config) ->
- Config.
-
-%%--------------------------------------------------------------------
-%% Test Cases --------------------------------------------------------
-%%--------------------------------------------------------------------
-server_verify_client_once() ->
- [{doc,"Test server option verify_client_once"}].
-
-server_verify_client_once(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, []),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer},
- {verify_client_once, true}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client0, ok),
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
- ssl_test_lib:close(Client0),
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, result_ok, []}},
- {options, [{active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client1, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-
-cert_expired() ->
- [{doc,"Test server with expired certificate"}].
-
-cert_expired(Config) when is_list(Config) ->
- {Year, Month, Day} = date(),
- Active = proplists:get_value(active, Config),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],
- [{validity, {{Year-2, Month, Day},
- {Year-1, Month, Day}}}],
- []
- ]}],
- Config, "_expired"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{active, Active}| ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_client_alert(Server, Client, certificate_expired).
-
-two_digits_str(N) when N < 10 ->
- lists:flatten(io_lib:format("0~p", [N]));
-two_digits_str(N) ->
- lists:flatten(io_lib:format("~p", [N])).
-
-%%--------------------------------------------------------------------
-extended_key_usage_verify_server() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in server cert"}].
-
-extended_key_usage_verify_server(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{?'id-ce-extKeyUsage',
- [?'id-kp-serverAuth'], true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],[], [{extensions, Ext}]]}], Config,
- "_keyusage_server"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-extended_key_usage_verify_both() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}].
-
-extended_key_usage_verify_both(Config) when is_list(Config) ->
- ServerExt = x509_test:extensions([{?'id-ce-extKeyUsage',
- [?'id-kp-serverAuth'], true}]),
- ClientExt = x509_test:extensions([{?'id-ce-extKeyUsage',
- [?'id-kp-clientAuth'], true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain, [[],[],[{extensions, ClientExt}]]},
- {server_chain, [[],[],[{extensions, ServerExt}]]}],
- Config, "_keyusage_both"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-critical_extension_verify_server() ->
- [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
-
-critical_extension_verify_server(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain,
- [[],[], [{extensions, Ext}]]}],
- Config, "_client_unknown_extension"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error(
- [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error(
- [{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]),
-
- %% This certificate has a critical extension that we don't
- %% understand. Therefore, verification should fail.
- ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate).
-%%--------------------------------------------------------------------
-
-critical_extension_verify_client() ->
- [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
-
-critical_extension_verify_client(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],[],[{extensions, Ext}]]}],
- Config, "_server_unknown_extensions"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error(
- [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error(
- [{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate).
-
-%%--------------------------------------------------------------------
-critical_extension_verify_none() ->
- [{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
-
-critical_extension_verify_none(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],[], [{extensions, Ext}]]}],
- Config, "_unknown_extensions"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server(
- [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client(
- [{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]),
-
- %% This certificate has a critical extension that we don't
- %% understand. But we're using `verify_none', so verification
- %% shouldn't fail.
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-no_authority_key_identifier() ->
- [{doc, "Test cert that does not have authorityKeyIdentifier extension"
- " but are present in trusted certs db."}].
-
-no_authority_key_identifier(Config) when is_list(Config) ->
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([], Config, "_peer_no_auth_key_id"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{verify, verify_peer} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-delete_authority_key_extension([], Acc) ->
- lists:reverse(Acc);
-delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest],
- Acc) ->
- delete_authority_key_extension(Rest, Acc);
-delete_authority_key_extension([Head | Rest], Acc) ->
- delete_authority_key_extension(Rest, [Head | Acc]).
-
-%%--------------------------------------------------------------------
-
-no_authority_key_identifier_keyEncipherment() ->
- [{doc, "Test cert with keyEncipherment key_usage an no"
- " authorityKeyIdentifier extension, but are present in trusted certs db."}].
-
-no_authority_key_identifier_keyEncipherment(Config) when is_list(Config) ->
- ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain,
- [[],[],[{extensions, ClientExt}]]}],
- Config, "_peer_keyEncipherment"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, [{active, true} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, [{verify, verify_peer} | ClientOpts]}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-
-invalid_signature_server() ->
- [{doc,"Test client with invalid signature"}].
-
-invalid_signature_server(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
-
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
- Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
-
- ServerCertFile = proplists:get_value(certfile, ServerOpts),
- NewServerCertFile = filename:join(PrivDir, "server_invalid_cert.pem"),
- [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
- ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp),
- ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate,
- NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key),
- ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
- NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, NewServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{verify, verify_peer} | ClientOpts]}]),
- ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-
-invalid_signature_client() ->
- [{doc,"Test server with invalid signature"}].
-
-invalid_signature_client(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
-
- KeyFile = proplists:get_value(keyfile, ClientOpts),
- [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
- Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
-
- ClientCertFile = proplists:get_value(certfile, ClientOpts),
- NewClientCertFile = filename:join(PrivDir, "client_invalid_cert.pem"),
- [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
- ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
- ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
- NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key),
- ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
- NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{verify, verify_peer} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, NewClientOpts}]),
-
- ssl_test_lib:check_client_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-
-client_with_cert_cipher_suites_handshake() ->
- [{doc, "Test that client with a certificate without keyEncipherment usage "
- " extension can connect to a server with restricted cipher suites "}].
-client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{key_usage, [digitalSignature]}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain,
- [[], [], [{extensions, Ext}]]}],
- Config, "_sign_only_extensions"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- TLSVersion = ssl_test_lib:protocol_version(Config, tuple),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, [{active, true},
- {ciphers,
- ssl_test_lib:rsa_non_signed_suites(TLSVersion)}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, [{active, true}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-
-long_chain() ->
- [{doc,"Test option verify_peer"}].
-long_chain(Config) when is_list(Config) ->
- #{server_config := ServerConf,
- client_config := ClientConf} = public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}],
- intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}],
- [{key, ssl_test_lib:hardcode_rsa_key(3)}],
- [{key, ssl_test_lib:hardcode_rsa_key(4)}]],
- peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]},
- client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}],
- intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]],
- peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}),
- [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf),
- ClientCas = proplists:get_value(cacerts, ClientConf),
-
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer},
- {cacerts, [ServerRoot]} |
- proplists:delete(cacerts, ServerConf)]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active},
- {verify, verify_peer},
- {depth, 5},
- {cacerts, ServerCas ++ ClientCas} |
- proplists:delete(cacerts, ClientConf)]}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
-%%--------------------------------------------------------------------
-
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 2750a4a9dc..e4a2ebb583 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -106,7 +106,7 @@ decode_hello_handshake(_Config) ->
Version = {3, 0},
{Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>,
- #ssl_options{}),
+ ssl:options_to_map(#ssl_options{})),
{Hello, _Data} = hd(Records),
Extensions = Hello#server_hello.extensions,
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 7dbc0c5134..e098190ece 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -71,7 +71,8 @@ encode_and_decode_client_hello_test(Config) ->
HandShakeData = create_client_handshake(undefined),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#client_hello.extensions,
#{next_protocol_negotiation := undefined} = Extensions.
%%--------------------------------------------------------------------
@@ -79,7 +80,8 @@ encode_and_decode_npn_client_hello_test(Config) ->
HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#client_hello.extensions,
#{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<>>}} = Extensions.
%%--------------------------------------------------------------------
@@ -87,7 +89,8 @@ encode_and_decode_server_hello_test(Config) ->
HandShakeData = create_server_handshake(undefined),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#server_hello.extensions,
#{next_protocol_negotiation := undefined} = Extensions.
@@ -96,7 +99,8 @@ encode_and_decode_npn_server_hello_test(Config) ->
HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#server_hello.extensions,
ct:log("~p ~n", [Extensions]),
#{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}} = Extensions.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index c4f294771a..cb528f185a 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -2208,10 +2208,23 @@ check_sane_openssl_renegotaite(Config) ->
{skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 0.9.7" ++ _ ->
{skip, "Known renegotiation bug in OpenSSL"};
+ "LibreSSL 2." ++ _ ->
+ {skip, "Known renegotiation bug in LibreSSL"};
+
_ ->
Config
end.
+openssl_allows_client_renegotaite(Config) ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1.1" ++ _ ->
+ {skip, "OpenSSL does not allow client renegotiation"};
+ "LibreSSL 2" ++ _ ->
+ {skip, "LibreSSL does not allow client renegotiation"};
+ _ ->
+ Config
+ end.
+
workaround_openssl_s_clinent() ->
%% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159
%% https://bugs.archlinux.org/task/33919
@@ -2777,17 +2790,6 @@ new_config(PrivDir, ServerOpts0) ->
[{cacertfile, NewCaCertFile}, {certfile, NewCertFile},
{keyfile, NewKeyFile} | ServerOpts].
-sane_openssl_alpn_npn_renegotiate() ->
- case os:cmd("openssl version") of
- "LibreSSL 2.9.1" ++ _ ->
- false;
- "LibreSSL 2.6.4" ++ _ ->
- false;
- "OpenSSL 1.1.1a-freebsd" ++ _ ->
- false;
- _ ->
- true
- end.
openssl_sane_dtls_alpn() ->
case os:cmd("openssl version") of
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index c9547cae36..c7404c0169 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 9.3.5
+SSL_VSN = 9.4
diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml
index cf2c0844c9..fa6d2b81fe 100644
--- a/lib/stdlib/doc/src/digraph.xml
+++ b/lib/stdlib/doc/src/digraph.xml
@@ -36,10 +36,23 @@
<modulesummary>Directed graphs.</modulesummary>
<description>
<p>This module provides a version of labeled
- directed graphs. What makes the graphs provided here
- non-proper directed graphs is that multiple edges between
- vertices are allowed. However, the customary definition of
- directed graphs is used here.</p>
+ directed graphs ("digraphs").</p>
+
+ <p>The digraphs managed by this module are stored in
+ <seealso marker="ets">ETS tables</seealso>.
+ That implies the following:</p>
+
+ <list type="bulleted">
+ <item><p>Only the process that created the digraph is allowed to update it.</p></item>
+ <item><p>Digraphs will not be garbage collected. The ETS tables used for a
+ digraph will only be deleted when <seealso marker="#delete/1"><c>delete/1</c></seealso>
+ is called or the process that created the digraph terminates.</p></item>
+ <item><p>A digraph is a mutable data structure.</p></item>
+ </list>
+
+ <p>What makes the graphs provided here non-proper directed graphs
+ is that multiple edges between vertices are allowed. However, the
+ customary definition of directed graphs is used here.</p>
<list type="bulleted">
<item>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 2cb677785d..2d0a88fc6e 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -635,6 +635,14 @@ Error: fun containing local Erlang function calls
the following items are allowed:</p>
<list type="bulleted">
<item>
+ <p><c>Item=binary, Value=BinInfo</c></p>
+ <p><c>BinInfo</c> is a list containing miscellaneous
+ information about binaries kept by the table.
+ This <c><anno>Item</anno></c> can be changed or removed without
+ prior notice. In the current implementation <c>BinInfo</c> is a
+ list of tuples <c>{BinaryId,BinarySize,BinaryRefcCount}</c>.</p>
+ </item>
+ <item>
<p><c>Item=fixed, Value=boolean()</c></p>
<p>Indicates if the table is fixed by any process.</p>
</item>
@@ -782,29 +790,25 @@ Error: fun containing local Erlang function calls
<fsummary>Check if an Erlang term is the result of
<c>match_spec_compile</c>.</fsummary>
<desc>
- <p>Checks if a term is a valid
- compiled <seealso marker="#match_spec">match specification</seealso>.
- The compiled match specification is an opaque datatype that
- <em>cannot</em> be sent between Erlang nodes or be stored on
- disk. Any attempt to create an external representation of a
- compiled match specification results in an empty binary
- (<c><![CDATA[<<>>]]></c>).</p>
- <p><em>Examples:</em></p>
- <p>The following expression yields <c>true</c>::</p>
- <code type="none">
-ets:is_compiled_ms(ets:match_spec_compile([{'_',[],[true]}])).</code>
- <p>The following expressions yield <c>false</c>, as variable
- <c>Broken</c> contains a compiled match specification that has
- passed through external representation:</p>
- <code type="none">
-MS = ets:match_spec_compile([{'_',[],[true]}]),
-Broken = binary_to_term(term_to_binary(MS)),
-ets:is_compiled_ms(Broken).</code>
+ <p>Checks if a term represent a valid compiled
+ <seealso marker="#match_spec">match specification</seealso>.
+ A compiled match specifications is only valid on the Erlang node where
+ it was compiled by calling <seealso marker="#match_spec_compile/1">
+ <c>match_spec_compile/1</c></seealso>.</p>
<note>
- <p>The reason for not having an external representation of
- compiled match specifications is performance. It can be
- subject to change in future releases, while this interface
- remains for backward compatibility.</p>
+ <p>
+ Before STDLIB 3.4 (OTP 20.0) compiled match specifications did
+ not have an external representation. If passed through
+ <c>binary_to_term(term_to_binary(CMS))</c> or sent to another node
+ and back, the result was always an empty binary <c>&lt;&lt;>></c>.</p>
+ <p>
+ After STDLIB 3.4 (OTP 20.0) compiled match specifications have an
+ external representation as a node specific reference to the original
+ compiled match specification. If passed through
+ <c>binary_to_term(term_to_binary(CMS))</c> or sent to another node
+ and back, the result <em>may or may not</em> be a valid compiled match
+ specification depending on if the original compiled match
+ specification was still alive.</p>
</note>
</desc>
</func>
@@ -1033,11 +1037,7 @@ ets:is_compiled_ms(Broken).</code>
<seealso marker="#match_spec">match specification</seealso> into an
internal representation that can be used in subsequent calls to
<seealso marker="#match_spec_run/2"><c>match_spec_run/2</c></seealso>.
- The internal representation is
- opaque and cannot be converted to external term format and
- then back again without losing its properties (that is, it cannot
- be sent to a process on another node and still remain a
- valid compiled match specification, nor can it be stored on disk).
+ The internal representation is opaque.
To check the validity of a compiled match specification, use
<seealso marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seealso>.
</p>
@@ -1330,46 +1330,41 @@ ets:select(Table, MatchSpec),</code>
continuation has passed through external term format (been
sent between nodes or stored on disk).</p>
<p>The reason for this function is that continuation terms
- contain compiled match specifications and therefore are
+ contain compiled match specifications and may therefore be
invalidated if converted to external term format. Given that the
original match specification is kept intact, the continuation can
be restored, meaning it can once again be used in subsequent
<c>select/1</c> calls even though it has been stored on
disk or on another node.</p>
<p><em>Examples:</em></p>
- <p>The following sequence of calls fails:</p>
+ <p>The following sequence of calls may fail:</p>
<code type="none">
T=ets:new(x,[]),
...
-{_,C} = ets:select(T,ets:fun2ms(fun({N,_}=A)
-when (N rem 10) =:= 0 ->
-A
-end),10),
-Broken = binary_to_term(term_to_binary(C)),
-ets:select(Broken).</code>
+MS = ets:fun2ms(fun({N,_}=A) when (N rem 10) =:= 0 -> A end),
+{_,C} = ets:select(T, MS, 10),
+MaybeBroken = binary_to_term(term_to_binary(C)),
+ets:select(MaybeBroken).</code>
<p>The following sequence works, as the call to
- <c>repair_continuation/2</c> reestablishes the (deliberately)
- invalidated continuation <c>Broken</c>.</p>
+ <c>repair_continuation/2</c> reestablishes the
+ <c>MaybeBroken</c> continuation.</p>
<code type="none">
T=ets:new(x,[]),
...
-MS = ets:fun2ms(fun({N,_}=A)
-when (N rem 10) =:= 0 ->
-A
-end),
+MS = ets:fun2ms(fun({N,_}=A) when (N rem 10) =:= 0 -> A end),
{_,C} = ets:select(T,MS,10),
-Broken = binary_to_term(term_to_binary(C)),
-ets:select(ets:repair_continuation(Broken,MS)).</code>
+MaybeBroken = binary_to_term(term_to_binary(C)),
+ets:select(ets:repair_continuation(MaybeBroken,MS)).</code>
<note>
<p>This function is rarely needed in application code. It is used
by Mnesia to provide distributed <c>select/3</c>
and <c>select/1</c> sequences. A normal application would
either use Mnesia or keep the continuation from being
converted to external format.</p>
- <p>The reason for not having an external representation of a
- compiled match specification is performance. It can be subject to
- change in future releases, while this interface remains
- for backward compatibility.</p>
+ <p>The actual behavior of compiled match specifications when recreated
+ from external format has changed and may change in future releases,
+ but this interface remains for backward compatibility.
+ See <seealso marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seealso>.</p>
</note>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index ef548ad643..dabce02b3d 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -33,43 +33,73 @@
<description>
<p>
<c>gen_statem</c> provides a generic state machine behaviour
- and replaces its predecessor
+ that for new code replaces its predecessor
<seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
- since Erlang/OTP 20.0.
+ since Erlang/OTP 20.0. The <c>gen_fsm</c> behaviour remains
+ in OTP "as is".
</p>
+ <note>
+ <p>
+ If you are new to <c>gen_statem</c> and want an overview
+ of concepts and operation the section
+ <seealso marker="doc/design_principles:statem">
+ <c>gen_statem</c>&nbsp;Behaviour
+ </seealso>
+ located in the User's Guide
+ <seealso marker="doc/design_principles:users_guide">
+ OTP Design Principles
+ </seealso>
+ is recommended to read before this reference manual,
+ possibly after the Description section you are reading here.
+ </p>
+ </note>
<p>
- This reference manual describes types generated from the types
- in the <c>gen_statem</c> source code, so they are correct.
+ This reference manual contains type descriptions generated from
+ types in the <c>gen_statem</c> source code, so they are correct.
However, the generated descriptions also reflect the type hierarchy,
- which makes them kind of hard to read.
- </p>
- <p>
- To get an overview of the concepts and operation of <c>gen_statem</c>,
- do read the
+ which sometimes makes it hard to get a good overview.
+ If so, see the section
<seealso marker="doc/design_principles:statem">
<c>gen_statem</c>&nbsp;Behaviour
</seealso>
- in
+ in the
<seealso marker="doc/design_principles:users_guide">
OTP Design Principles
</seealso>
- which frequently links back to this reference manual to avoid
- containing detailed facts that may rot by age.
+ User's Guide.
</p>
<note>
- <p>
- This behavior appeared in Erlang/OTP 19.0.
- In OTP 19.1 a backwards incompatible change of
- the return tuple from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
- was made and the mandatory callback function
- <seealso marker="#Module:callback_mode/0">
- <c>Module:callback_mode/0</c>
- </seealso>
- was introduced. In OTP 20.0 the
- <seealso marker="#type-generic_timeout"><c>generic timeouts</c></seealso>
- were added.
- </p>
+ <list type="bulleted">
+ <item>This behavior appeared in Erlang/OTP 19.0.</item>
+ <item>
+ In OTP 19.1 a backwards incompatible change of
+ the return tuple from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ was made and the mandatory callback function
+ <seealso marker="#Module:callback_mode/0">
+ <c>Module:callback_mode/0</c>
+ </seealso>
+ was introduced.
+ </item>
+ <item>
+ In OTP 20.0
+ <seealso marker="#type-generic_timeout">
+ generic time-outs
+ </seealso>
+ were added.
+ </item>
+ <item>
+ In OTP 22.1 time-out content
+ <seealso marker="#type-timeout_update_action">
+ <c>update</c>
+ </seealso>
+ and explicit time-out
+ <seealso marker="#type-timeout_cancel_action">
+ <c>cancel</c>
+ </seealso>
+ were added.
+ </item>
+ </list>
</note>
<p>
<c>gen_statem</c> has got the same features that
@@ -653,7 +683,7 @@ handle_event(_, _, State, Data) ->
<name name="timeout_event_type"/>
<desc>
<p>
- There are 3 types of timeout events that the state machine
+ There are 3 types of time-out events that the state machine
can generate for itself with the corresponding
<seealso marker="#type-timeout_action">timeout_action()</seealso>s.
</p>
@@ -720,7 +750,9 @@ handle_event(_, _, State, Data) ->
the <c>gen_statem</c> engine will, at every <em>state change</em>,
call the
<seealso marker="#state callback">state callback</seealso>
- with arguments <c>(enter, OldState, Data)</c>.
+ with arguments <c>(enter, OldState, Data)</c> or
+ <c>(enter, OldState, State, Data)</c>, depending on the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>.
This may look like an event but is really a call
performed after the previous
<seealso marker="#state callback"><em>state callback</em></seealso>
@@ -776,44 +808,53 @@ handle_event(_, _, State, Data) ->
</p>
<list type="ordered">
<item>
+ <p>
+ All returned
+ <seealso marker="#type-action">actions</seealso>
+ are processed in order of appearance.
+ In this step all replies generated by any
+ <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>
+ are sent. Other actions set <c>transition_option()</c>s
+ that come into play in subsequent steps.
+ </p>
+ </item>
+ <item>
<p>
If
<seealso marker="#type-state_enter">
<em>state enter calls</em>
</seealso>
- are used, and either:
- the state changes, it is the initial state,
- or one of the callback results
+ are used, and either
+ it is the initial state or one of the callback results
<seealso marker="#type-state_callback_result">
- <c>repeat_state</c>
+ <c>repeat_state_and_data</c>
</seealso>
or
<seealso marker="#type-state_callback_result">
<c>repeat_state_and_data</c>
</seealso>
- is used; the <c>gen_statem</c> calls
- the new state callback with arguments
- <seealso marker="#type-state_enter"><c>(enter, OldState, Data)</c></seealso>.
+ is used the <c>gen_statem</c> engine calls
+ the current state callback with arguments
+ <seealso marker="#type-state_enter"><c>(enter, State, Data)</c></seealso>
+ or
+ <seealso marker="#type-state_enter"><c>(enter, State, State, Data)</c></seealso>
+ (depending on <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>)
+ and when it returns starts again from the top of this sequence.
</p>
- <p>
- Any
- <seealso marker="#type-enter_action">actions</seealso>
- returned from this call are handled as if they were
- appended to the actions
- returned by the state callback that caused the state entry.
+ <p>
+ If
+ <seealso marker="#type-state_enter">
+ <em>state enter calls</em>
+ </seealso>
+ are used, and the state changes
+ the <c>gen_statem</c> engine calls
+ the new state callback with arguments
+ <seealso marker="#type-state_enter"><c>(enter, OldState, Data)</c></seealso>
+ or
+ <seealso marker="#type-state_enter"><c>(enter, OldState, State, Data)</c></seealso>
+ (depending on <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>)
+ and when it returns starts again from the top of this sequence.
</p>
- <p>
- Should this <em>state enter call</em> return any of
- the mentioned <c>repeat_*</c> callback results
- it is repeated again, with the updated <c>Data</c>.
- </p>
- </item>
- <item>
- <p>
- All
- <seealso marker="#type-action">actions</seealso>
- are processed in order of appearance.
- </p>
</item>
<item>
<p>
@@ -863,7 +904,9 @@ handle_event(_, _, State, Data) ->
A <em>state change</em> cancels a
<seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
and any new transition option of this type
- belongs to the new state.
+ belongs to the new state, that is; a
+ <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ applies to the state the state machine enters.
</p>
</item>
<item>
@@ -872,7 +915,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#state callback"><em>state callback</em></seealso>
for the possibly new state
is called with the oldest enqueued event,
- and we start again from the top of this list.
+ and we start again from the top of this sequence.
</p>
</item>
<item>
@@ -1176,7 +1219,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
</p>
<p>
- These timeout actions sets timeout
+ These time-out actions sets time-out
<seealso marker="#type-transition_option">transition options</seealso>.
</p>
<taglist>
@@ -1229,6 +1272,36 @@ handle_event(_, _, State, Data) ->
</desc>
</datatype>
<datatype>
+ <name name="timeout_cancel_action" since="OTP 22.1"/>
+ <desc>
+ <p>
+ This is a shorter and clearer form of
+ <seealso marker="#type-timeout_action">
+ timeout_action()
+ </seealso>
+ with <c>Time = infinity</c> which cancels a time-out.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="timeout_update_action" since="OTP 22.1"/>
+ <desc>
+ <p>
+ Updates a time-out with a new <c>EventContent</c>.
+ See
+ <seealso marker="#type-timeout_action">
+ timeout_action()
+ </seealso>
+ for how to start a time-out.
+ </p>
+ <p>
+ If no time-out of the same type is active instead
+ insert the time-out event just like when starting
+ a time-out with relative <c>Time = 0</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="reply_action"/>
<desc>
<p>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 66624c43be..e2badecffa 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,148 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>re:run()</c> now yields when validating utf8 in a
+ large subject.</p>
+ <p>
+ Own Id: OTP-15836 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ Upgraded the ERTS internal PCRE library from version 8.42
+ to version 8.43. See <url
+ href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url>
+ for information about changes made to PCRE. This library
+ implements major parts of the <seealso
+ marker="stdlib:re"><c>re</c></seealso> regular
+ expressions module.</p>
+ <p>
+ Own Id: OTP-15889</p>
+ </item>
+ <item>
+ <p>
+ The bug with ID ERL-717 has been fixed. The functions
+ <c>io:columns()</c> and <c>io:rows()</c> only worked
+ correctly inside interactive erlang shells before this
+ fix. These functions returned <c>{error,enotsup}</c>
+ before this fix even if stdout and stdin were connected
+ to a terminal when they were invoked from an escript or a
+ program started with e.g., <c>erl -noshell</c>.</p>
+ <p>
+ Own Id: OTP-15959 Aux Id: ERL-717 </p>
+ </item>
+ <item>
+ <p>Fixed handling of ".." and "@" in wildcards. ".."
+ would only work when preceded by a literal pattern such
+ as in "a/..", not when preceded by wildcard characters
+ such as in "*/..". The combination "@/.." was also
+ broken, and in addition "@" in a pattern could degrade
+ performance of the wildcard matching.</p>
+ <p>
+ Own Id: OTP-15987 Aux Id: ERL-1029 </p>
+ </item>
+ <item>
+ <p> Make sure <c>ets:fun2ms()</c> can handle <c>++/2</c>
+ in the head of functions when called from the shell. </p>
+ <p>
+ Own Id: OTP-15992 Aux Id: PR-2322 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Debugging of time-outs in <c>gen_statem</c> has been
+ improved. Starting a time-out is now logged in
+ <c>sys:log</c> and <c>sys:trace</c>. Running time-outs
+ are visible in server crash logs, and with
+ <c>sys:get_status</c>. Due to this system events
+ <c>{start_timer, Action, State}</c> and
+ <c>{insert_timout, Event, State}</c> have been added,
+ which may surprise tools that rely on the format of these
+ events. </p> <p>New features: The <c>EventContent</c> of
+ a running time-out can be updated with <c>{TimeoutType,
+ update, NewEventContent}</c>. Running time-outs can be
+ cancelled with <c>{TimeoutType, cancel}</c> which is more
+ readable than using <c>Time = infinity</c>. </p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15510</p>
+ </item>
+ <item>
+ <p>
+ <c>re:run()</c> now avoids validating utf8 in the subject
+ more than once in the same call. This validation could
+ previously be performed multiple times when the
+ <c>global</c> option was passed.</p>
+ <p>
+ Own Id: OTP-15831 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ ETS <c>ordered_set</c> tables with
+ <c>write_concurrency</c> enabled has got a performance
+ issue fixed. There were no limits for the values of
+ internal statistics counters before this fix. This could
+ result in that the data structure sometimes reacted
+ slowly to a change in how many parallel processes were
+ using it.</p>
+ <p>
+ Own Id: OTP-15906</p>
+ </item>
+ <item>
+ <p>The <c>ordsets:union/1</c> is now faster when passed a
+ long list of ordsets.</p>
+ <p>
+ Own Id: OTP-15927</p>
+ </item>
+ <item>
+ <p>
+ <c>unicode:characters_to_binary()</c> could return very
+ small binaries as reference counted off heap binaries.
+ This could cause an unnecessary large memory usage and an
+ unnecessary load on the binary allocator. Small binaries
+ are now always returned as heap binaries.</p>
+ <p>
+ Own Id: OTP-16002 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p> Display a more meaningful error message when a bad
+ I/O server is used in a script written in Erlang
+ (<c>escript</c>). </p>
+ <p>
+ Own Id: OTP-16006 Aux Id: ERL-992 </p>
+ </item>
+ <item>
+ <p>
+ New feature <c>ets:info(_, binary)</c> to get information
+ about all reference counted binaries kept by a table.
+ This is the same kind of debug information that
+ <c>process_info(_, binary)</c> returns for a process.</p>
+ <p>
+ Own Id: OTP-16035 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ Corrected ETS documentation about the behavior of
+ compiled match specifications when serialized through
+ external format.</p>
+ <p>
+ Own Id: OTP-16038 Aux Id: PR-2366 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.9.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index e22ca89ef5..ebea054fff 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -239,6 +239,34 @@
</item>
<tag>
<c>
+ {start_timer,<anno>Action</anno>,<anno>State</anno>}
+ </c>
+ </tag>
+ <item>
+ <p>
+ Is produced by <c>gen_statem</c>
+ when the action <c><anno>Action</anno></c>
+ starts a timer in state <c><anno>State</anno></c>.
+ </p>
+ </item>
+ <tag>
+ <c>
+ {insert_timeout,<anno>Event</anno>,<anno>State</anno>}
+ </c>
+ </tag>
+ <item>
+ <p>
+ Is produced by <c>gen_statem</c> when a timeout zero action
+ inserts event <c><anno>Event</anno></c>
+ in state <c><anno>State</anno></c>.
+ </p>
+ <p>
+ <c><anno>Event</anno></c> is
+ an <c>{EventType,EventContent}</c> tuple.
+ </p>
+ </item>
+ <tag>
+ <c>
{enter,<anno>State</anno>}
</c>
</tag>
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index f781312ca2..2a063fede1 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -22,12 +22,10 @@
-include("erl_compile.hrl").
-include("file.hrl").
--export([compile_cmdline/0]).
+-export([compile_cmdline/0, compile/2]).
-export_type([cmd_line_arg/0]).
--define(STDERR, standard_error). %Macro to avoid misspellings.
-
%% Mapping from extension to {M,F} to run the correct compiler.
compiler(".erl") -> {compile, compile};
@@ -46,60 +44,56 @@ compiler(".asn") -> {asn1ct, compile_asn};
compiler(".py") -> {asn1ct, compile_py};
compiler(_) -> no.
-%% Entry from command line.
-
-type cmd_line_arg() :: atom() | string().
+%% Run a compilation based on the command line arguments and then halt.
+%% Intended for one-off compilation by erlc.
-spec compile_cmdline() -> no_return().
-
compile_cmdline() ->
+ cmdline_init(),
List = init:get_plain_arguments(),
- case compile(List) of
- ok -> my_halt(0);
- error -> my_halt(1);
- _ -> my_halt(2)
+ compile_cmdline1(List).
+
+%% Run a compilation. Meant to be used by the compilation server.
+-spec compile(list(), file:filename()) -> 'ok' | {'error', binary()}.
+compile(Args, Cwd) ->
+ try compile1(Args, #options{outdir=Cwd,cwd=Cwd}) of
+ ok ->
+ ok
+ catch
+ throw:{error, Output} ->
+ {error, unicode:characters_to_binary(Output)};
+ C:E:Stk ->
+ {crash, {C,E,Stk}}
end.
--spec my_halt(_) -> no_return().
-my_halt(Reason) ->
- erlang:halt(Reason).
-
-%% Run the the compiler in a separate process, trapping EXITs.
-
-compile(List) ->
- process_flag(trap_exit, true),
- Pid = spawn_link(compiler_runner(List)),
+%% Run the the compiler in a separate process.
+compile_cmdline1(Args) ->
+ {ok, Cwd} = file:get_cwd(),
+ {Pid,Ref} = spawn_monitor(fun() -> exit(compile(Args, Cwd)) end),
receive
- {'EXIT', Pid, {compiler_result, Result}} ->
- Result;
- {'EXIT', Pid, {compiler_error, Error}} ->
- io:put_chars(?STDERR, Error),
- io:nl(?STDERR),
- error;
- {'EXIT', Pid, Reason} ->
- io:format(?STDERR, "Runtime error: ~tp~n", [Reason]),
- error
+ {'DOWN', Ref, process, Pid, Result} ->
+ case Result of
+ ok ->
+ halt(0);
+ {error, Output} ->
+ io:put_chars(standard_error, Output),
+ halt(1);
+ {crash, {C,E,Stk}} ->
+ io:format(standard_error, "Crash: ~p:~tp\n~tp\n",
+ [C,E,Stk]),
+ halt(2)
+ end
end.
--spec compiler_runner([cmd_line_arg()]) -> fun(() -> no_return()).
-
-compiler_runner(List) ->
- fun() ->
- %% We don't want the current directory in the code path.
- %% Remove it.
- Path = [D || D <- code:get_path(), D =/= "."],
- true = code:set_path(Path),
- exit({compiler_result, compile1(List)})
- end.
-
-%% Parses the first part of the option list.
-
-compile1(Args) ->
- {ok, Cwd} = file:get_cwd(),
- compile1(Args, #options{outdir=Cwd,cwd=Cwd}).
-
-%% Parses all options.
+cmdline_init() ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ ok.
+%% Parse all options.
compile1(["--"|Files], Opts) ->
compile2(Files, Opts);
compile1(["-"++Option|T], Opts) ->
@@ -132,12 +126,8 @@ parse_generic_option("I"++Opt, T0, #options{cwd=Cwd}=Opts) ->
AbsDir = filename:absname(Dir, Cwd),
compile1(T, Opts#options{includes=[AbsDir|Opts#options.includes]});
parse_generic_option("M"++Opt, T0, #options{specific=Spec}=Opts) ->
- case parse_dep_option(Opt, T0) of
- error ->
- error;
- {SpecOpts,T} ->
- compile1(T, Opts#options{specific=SpecOpts++Spec})
- end;
+ {SpecOpts,T} = parse_dep_option(Opt, T0),
+ compile1(T, Opts#options{specific=SpecOpts++Spec});
parse_generic_option("o"++Opt, T0, #options{cwd=Cwd}=Opts) ->
{Dir,T} = get_option("o", Opt, T0),
AbsName = filename:absname(Dir, Cwd),
@@ -181,8 +171,7 @@ parse_generic_option("P", T, #options{specific=Spec}=Opts) ->
parse_generic_option("S", T, #options{specific=Spec}=Opts) ->
compile1(T, Opts#options{specific=['S'|Spec]});
parse_generic_option(Option, _T, _Opts) ->
- io:format(?STDERR, "Unknown option: -~ts\n", [Option]),
- usage().
+ usage(io_lib:format("Unknown option: -~ts\n", [Option])).
parse_dep_option("", T) ->
{[makedep,{makedep_output,standard_io}],T};
@@ -204,10 +193,14 @@ parse_dep_option("T"++Opt, T0) ->
{Target,T} = get_option("MT", Opt, T0),
{[{makedep_target,Target}],T};
parse_dep_option(Opt, _T) ->
- io:format(?STDERR, "Unknown option: -M~ts\n", [Opt]),
- usage().
+ usage(io_lib:format("Unknown option: -M~ts\n", [Opt])).
+
+-spec usage() -> no_return().
usage() ->
+ usage("").
+
+usage(Error) ->
H = [{"-b type","type of output file (e.g. beam)"},
{"-d","turn on debugging of erlc itself"},
{"-Dname","define name"},
@@ -238,18 +231,18 @@ usage() ->
{"-S","generate assembly listing (Erlang compiler)"},
{"-P","generate listing of preprocessed code (Erlang compiler)"},
{"+term","pass the Erlang term unchanged to the compiler"}],
- io:put_chars(?STDERR,
- ["Usage: erlc [Options] file.ext ...\n",
- "Options:\n",
- [io_lib:format("~-14s ~s\n", [K,D]) || {K,D} <- H]]),
- error.
+ Msg = [Error,
+ "Usage: erlc [Options] file.ext ...\n",
+ "Options:\n",
+ [io_lib:format("~-14s ~s\n", [K,D]) || {K,D} <- H]],
+ throw({error, Msg}).
get_option(_Name, [], [[C|_]=Option|T]) when C =/= $- ->
{Option,T};
get_option(_Name, [_|_]=Option, T) ->
{Option,T};
get_option(Name, _, _) ->
- exit({compiler_error,"No value given to -"++Name++" option"}).
+ throw({error, "No value given to -"++Name++" option\n"}).
split_at_equals([$=|T], Acc) ->
{lists:reverse(Acc),T};
@@ -266,14 +259,10 @@ compile2(Files, #options{cwd=Cwd,includes=Incl,outfile=Outfile}=Opts0) ->
{[_|_], 1} ->
compile3(Files, Cwd, Opts);
{[_|_], _N} ->
- io:put_chars(?STDERR,
- "Output file name given, "
- "but more than one input file.\n"),
- error
+ throw({error, "Output file name given, but more than one input file.\n"})
end.
-%% Compiles the list of files, until done or compilation fails.
-
+%% Compile the list of files, until done or compilation fails.
compile3([File|Rest], Cwd, Options) ->
Ext = filename:extension(File),
Root = filename:rootname(File),
@@ -285,43 +274,37 @@ compile3([File|Rest], Cwd, Options) ->
Outfile ->
filename:rootname(Outfile)
end,
- case compile_file(Ext, InFile, OutFile, Options) of
- ok ->
- compile3(Rest, Cwd, Options);
- Other ->
- Other
- end;
+ compile_file(Ext, InFile, OutFile, Options),
+ compile3(Rest, Cwd, Options);
compile3([], _Cwd, _Options) -> ok.
-%% Invokes the appropriate compiler, depending on the file extension.
-
+%% Invoke the appropriate compiler, depending on the file extension.
compile_file("", Input, _Output, _Options) ->
- io:format(?STDERR, "File has no extension: ~ts~n", [Input]),
- error;
+ throw({error, io_lib:format("File has no extension: ~ts~n", [Input])});
compile_file(Ext, Input, Output, Options) ->
case compiler(Ext) of
no ->
- io:format(?STDERR, "Unknown extension: '~ts'\n", [Ext]),
- error;
+ Error = io_lib:format("Unknown extension: '~ts'\n", [Ext]),
+ throw({error, Error});
{M, F} ->
- case catch M:F(Input, Output, Options) of
- ok -> ok;
- error -> error;
- {'EXIT',Reason} ->
- io:format(?STDERR,
- "Compiler function ~w:~w/3 failed:\n~p~n",
- [M,F,Reason]),
- error;
+ try M:F(Input, Output, Options) of
+ ok ->
+ ok;
+ error ->
+ throw({error, ""});
Other ->
- io:format(?STDERR,
- "Compiler function ~w:~w/3 returned:\n~p~n",
- [M,F,Other]),
- error
+ Error = io_lib:format("Compiler function ~w:~w/3 returned:\n~tp~n",
+ [M,F,Other]),
+ throw({error, Error})
+ catch
+ throw:Reason:Stk ->
+ Error = io_lib:format("Compiler function ~w:~w/3 failed:\n~tp\n~tp\n",
+ [M,F,Reason,Stk]),
+ throw({error, Error})
end
end.
-%% Guesses if a give name refers to a file or a directory.
-
+%% Guess whether a given name refers to a file or a directory.
file_or_directory(Name) ->
case file:read_file_info(Name) of
{ok, #file_info{type=regular}} ->
@@ -335,18 +318,16 @@ file_or_directory(Name) ->
end
end.
-%% Makes an Erlang term given a string.
-
-make_term(Str) ->
+%% Make an Erlang term given a string.
+make_term(Str) ->
case erl_scan:string(Str) of
- {ok, Tokens, _} ->
+ {ok, Tokens, _} ->
case erl_parse:parse_term(Tokens ++ [{dot, erl_anno:new(1)}]) of
- {ok, Term} -> Term;
+ {ok, Term} ->
+ Term;
{error, {_,_,Reason}} ->
- io:format(?STDERR, "~ts: ~ts~n", [Reason, Str]),
- throw(error)
+ throw({error, io_lib:format("~ts: ~ts~n", [Reason, Str])})
end;
{error, {_,_,Reason}, _} ->
- io:format(?STDERR, "~ts: ~ts~n", [Reason, Str]),
- throw(error)
+ throw({error, io_lib:format("~ts: ~ts~n", [Reason, Str])})
end.
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 3f14894b55..4e9ba1cc16 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -281,11 +281,11 @@ start(EscriptOptions) ->
end
catch
throw:Str ->
- io:format("escript: ~ts\n", [Str]),
+ put_chars(io_lib:format("escript: ~ts\n", [Str])),
my_halt(127);
_:Reason:Stk ->
- io:format("escript: Internal error: ~tp\n", [Reason]),
- io:format("~tp\n", [Stk]),
+ put_chars(io_lib:format("escript: Internal error: ~tp\n", [Reason])),
+ put_chars(io_lib:format("~tp\n", [Stk])),
my_halt(127)
end.
@@ -885,13 +885,22 @@ format_exception(Class, Reason, StackTrace) ->
erl_error:format_exception(1, Class, Reason, StackTrace, StackFun, PF, Enc).
encoding() ->
- [{encoding, Encoding}] = enc(),
- Encoding.
+ case io:getopts() of
+ {error, _}=_Err ->
+ latin1;
+ Opts ->
+ case lists:keyfind(encoding, 1, Opts) of
+ false -> latin1;
+ {encoding, Encoding} -> Encoding
+ end
+ end.
-enc() ->
- case lists:keyfind(encoding, 1, io:getopts()) of
- false -> [{encoding,latin1}]; % should never happen
- Enc -> [Enc]
+put_chars(String) ->
+ try
+ io:put_chars(String)
+ catch
+ _:_ ->
+ erlang:display(lists:flatten(String))
end.
a0() ->
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 29f907ad73..e926e4fcaf 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -174,7 +174,7 @@ info(_) ->
-spec info(Tab, Item) -> Value | undefined when
Tab :: tab(),
- Item :: compressed | fixed | heir | id | keypos | memory
+ Item :: binary | compressed | fixed | heir | id | keypos | memory
| name | named_table | node | owner | protection
| safe_fixed | safe_fixed_monotonic_time | size | stats | type
| write_concurrency | read_concurrency,
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 49911eac2c..105b2a4577 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -176,7 +176,17 @@
{'state_timeout', % Set the state_timeout option
Time :: state_timeout(),
EventContent :: term(),
- Options :: (timeout_option() | [timeout_option()])}.
+ Options :: (timeout_option() | [timeout_option()])} |
+ timeout_cancel_action() |
+ timeout_update_action().
+-type timeout_cancel_action() ::
+ {'timeout', 'cancel'} |
+ {{'timeout', Name :: term()}, 'cancel'} |
+ {'state_timeout', 'cancel'}.
+-type timeout_update_action() ::
+ {'timeout', 'update', EventContent :: term()} |
+ {{'timeout', Name :: term()}, 'update', EventContent :: term()} |
+ {'state_timeout', 'update', EventContent :: term()}.
-type reply_action() ::
{'reply', % Reply to a caller
From :: from(), Reply :: term()}.
@@ -420,11 +430,9 @@ timeout_event_type(Type) ->
{state_data = {undefined,undefined} ::
{State :: term(),Data :: term()},
postponed = [] :: [{event_type(),term()}],
- timers = {#{},#{}} ::
- {%% TimerRef => TimeoutType
- TimerRefs :: #{reference() => timeout_event_type()},
- %% TimeoutType => TimerRef
- TimeoutTypes :: #{timeout_event_type() => reference()}},
+ timers = #{} ::
+ #{TimeoutType :: timeout_event_type() =>
+ {TimerRef :: reference(), TimeoutMsg :: term()}},
hibernate = false :: boolean()
}).
@@ -807,13 +815,14 @@ format_status(
Opt,
[PDict,SysState,Parent,Debug,
{#params{name = Name} = P,
- #state{postponed = Postponed} = S}]) ->
+ #state{postponed = Postponed, timers = Timers} = S}]) ->
Header = gen:format_status_header("Status for state machine", Name),
Log = sys:get_log(Debug),
[{header,Header},
{data,
[{"Status",SysState},
{"Parent",Parent},
+ {"Time-outs",list_timeouts(Timers)},
{"Logged Events",Log},
{"Postponed",Postponed}]} |
case format_status(Opt, PDict, update_parent(P, Parent), S) of
@@ -860,6 +869,14 @@ print_event(Dev, SystemEvent, Name) ->
io:format(
Dev, "*DBG* ~tp enter in state ~tp~n",
[Name,State]);
+ {start_timer,Action,State} ->
+ io:format(
+ Dev, "*DBG* ~tp start_timer ~tp in state ~tp~n",
+ [Name,Action,State]);
+ {insert_timeout,Event,State} ->
+ io:format(
+ Dev, "*DBG* ~tp insert_timeout ~tp in state ~tp~n",
+ [Name,Event,State]);
{terminate,Reason,State} ->
io:format(
Dev, "*DBG* ~tp terminate ~tp in state ~tp~n",
@@ -945,15 +962,12 @@ loop_receive(
{'$gen_cast',Cast} ->
loop_receive_result(P, Debug, S, {cast,Cast});
%%
- {timeout,TimerRef,TimeoutMsg} ->
- {TimerRefs,TimeoutTypes} = S#state.timers,
- case TimerRefs of
- #{TimerRef := TimeoutType} ->
+ {timeout,TimerRef,TimeoutType} ->
+ case S#state.timers of
+ #{TimeoutType := {TimerRef,TimeoutMsg}} = Timers ->
%% Our timer
- Timers =
- {maps:remove(TimerRef, TimerRefs),
- maps:remove(TimeoutType, TimeoutTypes)},
- S_1 = S#state{timers = Timers},
+ Timers_1 = maps:remove(TimeoutType, Timers),
+ S_1 = S#state{timers = Timers_1},
loop_receive_result(
P, Debug, S_1, {TimeoutType,TimeoutMsg});
#{} ->
@@ -1514,20 +1528,20 @@ loop_actions_timeout(
true ->
case listify(TimeoutOpts) of
%% Optimization cases
- [] when ?relative_timeout(Time) ->
- RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
+ [{abs,true}] when ?absolute_timeout(Time) ->
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
- [RelativeTimeout|TimeoutsR], Postpone,
+ [Timeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
- [{abs,true}] when ?absolute_timeout(Time) ->
+ [{abs,false}] when ?relative_timeout(Time) ->
+ RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
- [Timeout|TimeoutsR], Postpone,
+ [RelativeTimeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
- [{abs,false}] when ?relative_timeout(Time) ->
+ [] when ?relative_timeout(Time) ->
RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
@@ -1544,14 +1558,13 @@ loop_actions_timeout(
[Timeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
false when ?relative_timeout(Time) ->
- RelativeTimeout =
- {TimeoutType,Time,TimeoutMsg},
+ RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
[RelativeTimeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
- badarg ->
+ _ ->
terminate(
error,
{bad_action_from_state_function,Timeout},
@@ -1576,10 +1589,12 @@ loop_actions_timeout(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postpone,
CallEnter, StateCall, Actions,
- {TimeoutType,Time,_} = Timeout) ->
+ {TimeoutType,Time,_TimeoutMsg} = Timeout) ->
%%
case timeout_event_type(TimeoutType) of
- true when ?relative_timeout(Time) ->
+ true
+ when ?relative_timeout(Time);
+ Time =:= update ->
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
@@ -1598,15 +1613,40 @@ loop_actions_timeout(
loop_actions_timeout(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postpone,
- CallEnter, StateCall, Actions, Time) ->
+ CallEnter, StateCall, Actions,
+ {TimeoutType,cancel} = Action) ->
+ %%
+ case timeout_event_type(TimeoutType) of
+ true ->
+ Timeout = {TimeoutType,infinity,undefined},
+ loop_actions_list(
+ P, Debug, S, Q, NextState_NewData,
+ NextEventsR, Hibernate,
+ [Timeout|TimeoutsR], Postpone,
+ CallEnter, StateCall, Actions);
+ false ->
+ terminate(
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(), P, Debug,
+ S#state{
+ state_data = NextState_NewData,
+ hibernate = Hibernate},
+ Q)
+ end;
+loop_actions_timeout(
+ P, Debug, S, Q, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postpone,
+ CallEnter, StateCall, Actions,
+ Time) ->
%%
if
?relative_timeout(Time) ->
- RelativeTimeout = {timeout,Time,Time},
+ Timeout = {timeout,Time,Time},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
- [RelativeTimeout|TimeoutsR], Postpone,
+ [Timeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
true ->
terminate(
@@ -1683,23 +1723,20 @@ loop_state_transition(
%% State transition to the same state
%%
loop_keep_state(
- P, Debug, #state{timers = {TimerRefs,TimeoutTypes} = Timers} = S,
+ P, Debug, #state{timers = Timers} = S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed) ->
%%
%% Cancel event timeout
%%
- case TimeoutTypes of
- %% Optimization
- %% - only cancel timer when there is a timer to cancel
- #{timeout := TimerRef} ->
+ case Timers of
+ #{timeout := {TimerRef,_TimeoutMsg}} ->
%% Event timeout active
loop_next_events(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- cancel_timer_by_ref_and_type(
- TimerRef, timeout, TimerRefs, TimeoutTypes));
+ cancel_timer(timeout, TimerRef, Timers));
_ ->
%% No event timeout active
loop_next_events(
@@ -1741,34 +1778,32 @@ loop_state_change(
end.
%%
loop_state_change(
- P, Debug, #state{timers = {TimerRefs,TimeoutTypes} = Timers} = S,
+ P, Debug, #state{timers = Timers} = S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR) ->
%%
%% Cancel state and event timeout
%%
- case TimeoutTypes of
+ case Timers of
%% Optimization
- %% - only cancel timeout when there is an active timeout
+ %% - only cancel timeout when it is active
%%
- #{state_timeout := TimerRef} ->
+ #{state_timeout := {TimerRef,_TimeoutMsg}} ->
%% State timeout active
%% - cancel event timeout too since it is faster than inspecting
loop_next_events(
P, Debug, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, [],
- cancel_timer_by_type(
+ cancel_timer(
timeout,
- cancel_timer_by_ref_and_type(
- TimerRef, state_timeout, TimerRefs, TimeoutTypes)));
- #{timeout := TimerRef} ->
+ cancel_timer(state_timeout, TimerRef, Timers)));
+ #{timeout := {TimerRef,_TimeoutMsg}} ->
%% Event timeout active but not state timeout
%% - cancel event timeout only
loop_next_events(
P, Debug, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, [],
- cancel_timer_by_ref_and_type(
- TimerRef, timeout, TimerRefs, TimeoutTypes));
+ cancel_timer(timeout, TimerRef, Timers));
_ ->
%% No state nor event timeout active.
loop_next_events(
@@ -1778,7 +1813,7 @@ loop_state_change(
end.
%% Continue state transition with processing of
-%% inserted events and timeout events
+%% timeouts and inserted events
%%
loop_next_events(
P, Debug, S,
@@ -1786,7 +1821,7 @@ loop_next_events(
NextEventsR, Hibernate, [], Postponed,
Timers) ->
%%
- %% Optimization when there are no timeout actions
+ %% Optimization when there are no timeouts
%% hence no timeout zero events to append to Events
%% - avoid loop_timeouts
loop_done(
@@ -1811,7 +1846,8 @@ loop_next_events(
NextEventsR, Hibernate, TimeoutsR, Postponed,
Timers, Seen, TimeoutEvents).
-%% Continue state transition with processing of timeout events
+%% Continue state transition with processing of timeouts
+%% and finally inserted events
%%
loop_timeouts(
P, Debug, S,
@@ -1819,6 +1855,8 @@ loop_timeouts(
NextEventsR, Hibernate, [], Postponed,
Timers, _Seen, TimeoutEvents) ->
%%
+ %% End of timeouts
+ %%
S_1 =
S#state{
state_data = NextState_NewData,
@@ -1854,37 +1892,28 @@ loop_timeouts(
NextEventsR, Hibernate, [Timeout|TimeoutsR], Postponed,
Timers, Seen, TimeoutEvents) ->
%%
- case Timeout of
- {TimeoutType,Time,TimeoutMsg} ->
- %% Relative timeout
- case Seen of
- #{TimeoutType := _} ->
- %% Type seen before - ignore
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
- NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers, Seen, TimeoutEvents);
- #{} ->
- loop_timeouts(
+ TimeoutType = element(1, Timeout),
+ case Seen of
+ #{TimeoutType := _} ->
+ %% Type seen before - ignore
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents);
+ #{} ->
+ case Timeout of
+ {_,Time,TimeoutMsg} ->
+ %% Relative timeout or update
+ loop_timeouts_start(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
Timers, Seen, TimeoutEvents,
- TimeoutType, Time, TimeoutMsg, [])
- end;
- {TimeoutType,Time,TimeoutMsg,TimeoutOpts} ->
- %% Absolute timeout
- case Seen of
- #{TimeoutType := _} ->
- %% Type seen before - ignore
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
- NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers, Seen, TimeoutEvents);
- #{} ->
- loop_timeouts(
+ TimeoutType, Time, TimeoutMsg, []);
+ {_,Time,TimeoutMsg,TimeoutOpts} ->
+ %% Absolute timeout
+ loop_timeouts_start(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
@@ -1892,8 +1921,10 @@ loop_timeouts(
TimeoutType, Time, TimeoutMsg, listify(TimeoutOpts))
end
end.
+
+%% Loop helper to start or restart a timeout
%%
-loop_timeouts(
+loop_timeouts_start(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
@@ -1920,51 +1951,79 @@ loop_timeouts(
NextEventsR, Hibernate, TimeoutsR, Postponed,
Timers, Seen, [{TimeoutType,TimeoutMsg}|TimeoutEvents],
TimeoutType);
+ update ->
+ loop_timeouts_update(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimeoutMsg);
_ ->
%% (Re)start the timer
TimerRef =
- erlang:start_timer(Time, self(), TimeoutMsg, TimeoutOpts),
- {TimerRefs,TimeoutTypes} = Timers,
- case TimeoutTypes of
- #{TimeoutType := OldTimerRef} ->
- %% Cancel the running timer,
- %% update the timeout type,
- %% insert the new timer ref,
- %% and remove the old timer ref
- Timers_1 =
- {maps:remove(
- OldTimerRef,
- TimerRefs#{TimerRef => TimeoutType}),
- TimeoutTypes#{TimeoutType := TimerRef}},
- cancel_timer(OldTimerRef),
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
+ erlang:start_timer(Time, self(), TimeoutType, TimeoutOpts),
+ case Debug of
+ ?not_sys_debug ->
+ loop_timeouts_register(
+ P, Debug, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers_1, Seen#{TimeoutType => true}, TimeoutEvents);
- #{} ->
- %% Insert the new timer type and ref
- Timers_1 =
- {TimerRefs#{TimerRef => TimeoutType},
- TimeoutTypes#{TimeoutType => TimerRef}},
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimerRef, TimeoutMsg);
+ _ ->
+ {State,_Data} = NextState_NewData,
+ Debug_1 =
+ sys_debug(
+ Debug, P#params.name,
+ {start_timer,
+ {TimeoutType,Time,TimeoutMsg,TimeoutOpts},
+ State}),
+ loop_timeouts_register(
+ P, Debug_1, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers_1, Seen#{TimeoutType => true}, TimeoutEvents)
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimerRef, TimeoutMsg)
end
end.
+%% Loop helper to register a newly started timer
+%% and to cancel any running timer
+%%
+loop_timeouts_register(
+ P, Debug, S, Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimerRef, TimeoutMsg) ->
+ %%
+ case Timers of
+ #{TimeoutType := {OldTimerRef,_OldTimeoutMsg}} ->
+ %% Cancel the running timer,
+ %% and update timer type and ref
+ cancel_timer(OldTimerRef),
+ Timers_1 = Timers#{TimeoutType := {TimerRef,TimeoutMsg}},
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers_1, Seen#{TimeoutType => true}, TimeoutEvents);
+ #{} ->
+ %% Insert the new timer type and ref
+ Timers_1 = Timers#{TimeoutType => {TimerRef,TimeoutMsg}},
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers_1, Seen#{TimeoutType => true}, TimeoutEvents)
+ end.
+
%% Loop helper to cancel a timeout
%%
loop_timeouts_cancel(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- {TimerRefs,TimeoutTypes} = Timers, Seen, TimeoutEvents,
- TimeoutType) ->
+ Timers, Seen, TimeoutEvents, TimeoutType) ->
%% This function body should have been:
- %% Timers_1 = cancel_timer_by_type(TimeoutType, Timers),
+ %% Timers_1 = cancel_timer(TimeoutType, Timers),
%% loop_timeouts(
%% P, Debug, S,
%% Events, NextState_NewData,
@@ -1974,14 +2033,12 @@ loop_timeouts_cancel(
%% Explicitly separate cases to get separate code paths for when
%% the map key exists vs. not, since otherwise the external call
%% to erlang:cancel_timer/1 and to map:remove/2 within
- %% cancel_timer_by_type/2 would cause all live registers
+ %% cancel_timer/2 would cause all live registers
%% to be saved to and restored from the stack also for
%% the case when the map key TimeoutType does not exist
- case TimeoutTypes of
- #{TimeoutType := TimerRef} ->
- Timers_1 =
- cancel_timer_by_ref_and_type(
- TimerRef, TimeoutType, TimerRefs, TimeoutTypes),
+ case Timers of
+ #{TimeoutType := {TimerRef,_TimeoutMsg}} ->
+ Timers_1 = cancel_timer(TimeoutType, TimerRef, Timers),
loop_timeouts(
P, Debug, S,
Events, NextState_NewData,
@@ -1995,6 +2052,36 @@ loop_timeouts_cancel(
Timers, Seen#{TimeoutType => true}, TimeoutEvents)
end.
+%% Loop helper to update the timeout message,
+%% or insert an event if no timer is running
+%%
+loop_timeouts_update(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimeoutMsg) ->
+ %%
+ case Timers of
+ #{TimeoutType := {TimerRef,_OldTimeoutMsg}} ->
+ Timers_1 = Timers#{TimeoutType := {TimerRef,TimeoutMsg}},
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers_1, Seen#{TimeoutType => true},
+ TimeoutEvents);
+ #{} ->
+ TimeoutEvents_1 =
+ [{TimeoutType,TimeoutMsg}|TimeoutEvents],
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen#{TimeoutType => true},
+ TimeoutEvents_1)
+ end.
+
%% Continue state transition with prepending timeout zero events
%% before event queue reversal i.e appending timeout zero events
%%
@@ -2056,13 +2143,13 @@ parse_timeout_opts_abs(Opts, Abs) ->
%% Enqueue immediate timeout events (timeout 0 events)
%%
-%% Event timer timeout 0 events gets special treatment since
-%% an event timer is cancelled by any received event,
-%% so if there are enqueued events before the event timer
-%% timeout 0 event - the event timer is cancelled hence no event.
+%% Event timeout 0 events gets special treatment since
+%% an event timeout is cancelled by any received event,
+%% so if there are enqueued events before the event
+%% timeout 0 event - the event timeout is cancelled hence no event.
%%
%% Other (state_timeout and {timeout,Name}) timeout 0 events
-%% that are after an event timer timeout 0 event are considered to
+%% that occur after an event timer timeout 0 event are considered to
%% belong to timers that were started after the event timer
%% timeout 0 event fired, so they do not cancel the event timer.
%%
@@ -2079,7 +2166,8 @@ prepend_timeout_events(
{State,_Data} = S#state.state_data,
Debug_1 =
sys_debug(
- Debug, P#params.name, {in,TimeoutEvent,State}),
+ Debug, P#params.name,
+ {insert_timeout,TimeoutEvent,State}),
prepend_timeout_events(
P, Debug_1, S, TimeoutEvents, [TimeoutEvent])
end;
@@ -2099,7 +2187,8 @@ prepend_timeout_events(
{State,_Data} = S#state.state_data,
Debug_1 =
sys_debug(
- Debug, P#params.name, {in,TimeoutEvent,State}),
+ Debug, P#params.name,
+ {insert_timeout,TimeoutEvent,State}),
prepend_timeout_events(
P, Debug_1, S, TimeoutEvents, [TimeoutEvent|EventsR])
end.
@@ -2190,7 +2279,9 @@ error_info(
name = Name,
callback_mode = CallbackMode,
state_enter = StateEnter} = P,
- #state{postponed = Postponed} = S,
+ #state{
+ postponed = Postponed,
+ timers = Timers} = S,
Q) ->
Log = sys:get_log(Debug),
?LOG_ERROR(#{label=>{gen_statem,terminate},
@@ -2200,6 +2291,7 @@ error_info(
callback_mode=>CallbackMode,
state_enter=>StateEnter,
state=>format_status(terminate, get(), P, S),
+ timeouts=>list_timeouts(Timers),
log=>Log,
reason=>{Class,Reason,Stacktrace},
client_info=>client_stacktrace(Q)},
@@ -2238,6 +2330,7 @@ format_log(#{label:={gen_statem,terminate},
callback_mode:=CallbackMode,
state_enter:=StateEnter,
state:=FmtData,
+ timeouts:=Timeouts,
log:=Log,
reason:={Class,Reason,Stacktrace},
client_info:=ClientInfo}) ->
@@ -2293,6 +2386,10 @@ format_log(#{label:={gen_statem,terminate},
[] -> "";
_ -> "** Stacktrace =~n** ~tp~n"
end ++
+ case Timeouts of
+ {0,_} -> "";
+ _ -> "** Time-outs: ~p~n"
+ end ++
case Log of
[] -> "";
_ -> "** Log =~n** ~tp~n"
@@ -2317,6 +2414,10 @@ format_log(#{label:={gen_statem,terminate},
[] -> [];
_ -> [error_logger:limit_term(FixedStacktrace)]
end ++
+ case Timeouts of
+ {0,_} -> [];
+ _ -> [error_logger:limit_term(Timeouts)]
+ end ++
case Log of
[] -> [];
_ -> [[error_logger:limit_term(T) || T <- Log]]
@@ -2370,40 +2471,55 @@ listify(Item) ->
[Item].
--define(cancel_timer(TimerRef),
- case erlang:cancel_timer(TimerRef) of
- false ->
- %% No timer found and we have not seen the timeout message
- receive
- {timeout,(TimerRef),_} ->
- ok
- end;
- _ ->
- %% Timer was running
- ok
- end).
-
+-define(
+ cancel_timer(TimerRef),
+ case erlang:cancel_timer(TimerRef) of
+ false ->
+ %% No timer found and we have not seen the timeout message
+ receive
+ {timeout,(TimerRef),_} ->
+ ok
+ end;
+ _ ->
+ %% Timer was running
+ ok
+ end).
+%%
+%% Cancel timer and consume timeout message
+%%
-compile({inline, [cancel_timer/1]}).
cancel_timer(TimerRef) ->
?cancel_timer(TimerRef).
+-define(
+ cancel_timer(TimeoutType, TimerRef, Timers),
+ begin
+ ?cancel_timer(TimerRef),
+ maps:remove(begin TimeoutType end, begin Timers end)
+ end).
+%%
+%% Cancel timer and remove from Timers
+%%
+-compile({inline, [cancel_timer/3]}).
+cancel_timer(TimeoutType, TimerRef, Timers) ->
+ ?cancel_timer(TimeoutType, TimerRef, Timers).
+
%% Cancel timer if running, otherwise no op
%%
%% Remove the timer from Timers
--compile({inline, [cancel_timer_by_type/2]}).
-cancel_timer_by_type(TimeoutType, {TimerRefs,TimeoutTypes} = Timers) ->
- case TimeoutTypes of
- #{TimeoutType := TimerRef} ->
- ?cancel_timer(TimerRef),
- {maps:remove(TimerRef, TimerRefs),
- maps:remove(TimeoutType, TimeoutTypes)};
+-compile({inline, [cancel_timer/2]}).
+cancel_timer(TimeoutType, Timers) ->
+ case Timers of
+ #{TimeoutType := {TimerRef, _TimeoutMsg}} ->
+ ?cancel_timer(TimeoutType, TimerRef, Timers);
#{} ->
Timers
end.
--compile({inline, [cancel_timer_by_ref_and_type/4]}).
-cancel_timer_by_ref_and_type(
- TimerRef, TimeoutType, TimerRefs, TimeoutTypes) ->
- ?cancel_timer(TimerRef),
- {maps:remove(TimerRef, TimerRefs),
- maps:remove(TimeoutType, TimeoutTypes)}.
+%% Return a list of all pending timeouts
+list_timeouts(Timers) ->
+ {maps:size(Timers),
+ maps:fold(
+ fun(TimeoutType, {_TimerRef,TimeoutMsg}, Acc) ->
+ [{TimeoutType,TimeoutMsg}|Acc]
+ end, [], Timers)}.
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index e6c42b9aac..5b7d2218c7 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -108,7 +108,7 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-15831:OTP-15836:OTP-15889@","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.5","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 0c270e9dd5..0f87d1e52a 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -41,7 +41,8 @@
{<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.9$">>,[restart_new_emulator]},
{<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
- {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
+ {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.9\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
[{<<"^3\\.5$">>,[restart_new_emulator]},
{<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -56,4 +57,5 @@
{<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.9$">>,[restart_new_emulator]},
{<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
- {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
+ {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.9\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 6ff9aa33b4..93bf4743d2 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -51,6 +51,8 @@
| {'code_change', Event :: _, State :: _}
| {'postpone', Event :: _, State :: _, NextState :: _}
| {'consume', Event :: _, State :: _, NextState :: _}
+ | {'start_timer', Action :: _, State :: _}
+ | {'insert_timeout', Event :: _, State :: _}
| {'enter', State :: _}
| {'terminate', Reason :: _, State :: _}
| term().
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 0b9106a99c..8ffd01f2b2 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,8 @@
foldl/1,
overflow/1,
verify_sections/3,
- unicode/1
+ unicode/1,
+ bad_io_server/1
]).
-include_lib("common_test/include/ct.hrl").
@@ -53,7 +54,7 @@ all() ->
emulator_flags_no_shebang, two_lines,
module_script, beam_script, archive_script, epp,
create_and_extract, foldl, overflow,
- archive_script_file_access, unicode].
+ archive_script_file_access, unicode, bad_io_server].
groups() ->
[].
@@ -950,6 +951,16 @@ overflow(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% OTP-16006, ERL-992
+bad_io_server(Config) when is_list(Config) ->
+ Data = proplists:get_value(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ run(Dir, "bad_io_server",
+ [<<"\"escript: exception error: an error occurred when evaluating"
+ " an arithmetic expression\\n in operator '/'/2\\n "
+ "called as '\\x{400}' / 0\\n\"\r\nExitCode:127">>]),
+ ok.
+
run(Dir, Cmd0, Expected0) ->
Expected = iolist_to_binary(expected_output(Expected0, Dir)),
Cmd = case os:type() of
diff --git a/lib/stdlib/test/escript_SUITE_data/bad_io_server b/lib/stdlib/test/escript_SUITE_data/bad_io_server
new file mode 100755
index 0000000000..4a6e81c935
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/bad_io_server
@@ -0,0 +1,11 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+-export([main/1]).
+
+main(_) ->
+ ok = io:setopts([{encoding,unicode}]),
+ _D = erlang:system_flag(backtrace_depth, 0),
+ group_leader(spawn(fun() -> ok end), self()),
+ _ = '\x{400}'/0,
+ ok.
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 09238ae2b4..4fcc0e65ea 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -33,6 +33,7 @@
-export([slot/1]).
-export([match1/1, match2/1, match_object/1, match_object2/1]).
-export([dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
+-export([info_binary_stress/1]).
-export([tab2file/1, tab2file2/1, tabfile_ext1/1,
tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1, badfile/1]).
-export([heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]).
@@ -179,7 +180,7 @@ groups() ->
{match, [],
[match1, match2, match_object, match_object2]},
{misc, [],
- [misc1, safe_fixtable, info, dups, tab2list]},
+ [misc1, safe_fixtable, info, info_binary_stress, dups, tab2list]},
{files, [],
[tab2file, tab2file2, tabfile_ext1,
tabfile_ext2, tabfile_ext3, tabfile_ext4, badfile]},
@@ -960,7 +961,7 @@ t_delete_all_objects_do(Opts) ->
%% Test delete_all_objects is atomic
T2 = ets_new(t_delete_all_objects, [public | Opts]),
Self = self(),
- Inserters = [spawn_link(fun() -> inserter(T2, 100*1000, 1, Self) end) || _ <- [1,2,3,4]],
+ Inserters = [spawn_link(fun() -> inserter(T2, 1, Self) end) || _ <- [1,2,3,4]],
[receive {Ipid, running} -> ok end || Ipid <- Inserters],
ets:delete_all_objects(T2),
@@ -991,23 +992,26 @@ t_delete_all_objects_do(Opts) ->
ets:delete(T2).
-inserter(_, 0, _, _) ->
- ok;
-inserter(T, N, Next, Papa) ->
- case Next of
- 10*1000 ->
- Papa ! {self(), running};
- _ ->
- ok
- end,
+inserter(T, Next, Papa) ->
+ Wait = case Next of
+ 10*1000 ->
+ Papa ! {self(), running},
+ 0;
+ 100*1000 -> %% We most often don't reach this far
+ io:format("Inserter ~p reached ~p objects\n",
+ [self(), Next]),
+ infinity;
+ _ ->
+ 0
+ end,
ets:insert(T, {{Next, self()}}),
receive
stop ->
Papa ! {self(), stopped, Next},
ok
- after 0 ->
- inserter(T, N-1, Next+1, Papa)
+ after Wait ->
+ inserter(T, Next+1, Papa)
end.
@@ -4379,9 +4383,14 @@ safe_fixtable_do(Opts) ->
end,
ok.
+-define(ets_info(Tab,Item,SlavePid), ets_info(Tab, Item, SlavePid, ?LINE)).
+
%% Tests ets:info result for required tuples.
info(Config) when is_list(Config) ->
- repeat_for_opts_all_table_types(fun info_do/1).
+ repeat_for_opts(fun info_do/1,
+ [[void, set, bag, duplicate_bag, ordered_set],
+ [void, private, protected, public],
+ write_concurrency, read_concurrency, compressed]).
info_do(Opts) ->
EtsMem = etsmem(),
@@ -4421,6 +4430,15 @@ info_do(Opts) ->
ThisNode=node(),
Tab = ets_new(foobar, [{keypos, 2} | Opts]),
+ %% Start slave to also do ets:info from a process not owning the table.
+ SlavePid = spawn_link(fun Slave() ->
+ receive
+ {Master, Item} ->
+ Master ! {self(), Item, ets:info(Tab, Item)}
+ end,
+ Slave()
+ end),
+
%% Note: ets:info/1 used to return a tuple, but from R11B onwards it
%% returns a list.
Res = ets:info(Tab),
@@ -4435,6 +4453,32 @@ info_do(Opts) ->
{value, {protection, Protection}} =
lists:keysearch(protection, 1, Res),
{value, {id, Tab}} = lists:keysearch(id, 1, Res),
+
+ %% Test 'binary'
+ [] = ?ets_info(Tab, binary, SlavePid),
+ BinSz = 100,
+ RefcBin = list_to_binary(lists:seq(1,BinSz)),
+ ets:insert(Tab, {RefcBin,key}),
+ [{BinPtr,BinSz,2}] = ?ets_info(Tab,binary, SlavePid),
+ ets:insert(Tab, {RefcBin,key2}),
+ [{BinPtr,BinSz,3}, {BinPtr,BinSz,3}] = ?ets_info(Tab,binary,SlavePid),
+ ets:delete(Tab, key),
+ [{BinPtr,BinSz,2}] = ?ets_info(Tab,binary, SlavePid),
+ case TableType of
+ ordered_set ->
+ ets:delete(Tab, key2);
+ _ ->
+ ets:safe_fixtable(Tab, true),
+ ets:delete(Tab, key2),
+ [{BinPtr,BinSz,2}] = ?ets_info(Tab,binary, SlavePid),
+ ets:safe_fixtable(Tab, false)
+ end,
+ [] = ?ets_info(Tab,binary, SlavePid),
+ RefcBin = id(RefcBin), % keep alive
+
+ unlink(SlavePid),
+ exit(SlavePid,kill),
+
true = ets:delete(Tab),
undefined = ets:info(non_existing_table_xxyy),
undefined = ets:info(non_existing_table_xxyy,type),
@@ -4444,6 +4488,79 @@ info_do(Opts) ->
undefined = ets:info(non_existing_table_xxyy,safe_fixed),
verify_etsmem(EtsMem).
+ets_info(Tab, Item, SlavePid, _Line) ->
+ R = ets:info(Tab, Item),
+ %%io:format("~p: ets:info(~p) -> ~p\n", [_Line, Item, R]),
+ SlavePid ! {self(), Item},
+ {SlavePid, Item, R} = receive M -> M end,
+ R.
+
+
+
+info_binary_stress(_Config) ->
+ repeat_for_opts(fun info_binary_stress_do/1,
+ [[set,bag,duplicate_bag,ordered_set],
+ compressed]).
+
+info_binary_stress_do(Opts) ->
+ Tab = ets_new(info_binary_stress, [public, {write_concurrency,true} | Opts]),
+
+ KeyRange = 1000,
+ ValueRange = 3,
+ RefcBin = list_to_binary(lists:seq(1,100)),
+ InitF = fun (_) -> #{insert => 0, delete => 0, delete_object => 0}
+ end,
+ ExecF = fun (Counters) ->
+ Key = rand:uniform(KeyRange),
+ Value = rand:uniform(ValueRange),
+ Op = element(rand:uniform(4),{insert,insert,delete,delete_object}),
+ case Op of
+ insert ->
+ ets:insert(Tab, {Key,Value,RefcBin});
+ delete ->
+ ets:delete(Tab, Key);
+ delete_object ->
+ ets:delete_object(Tab, {Key,Value,RefcBin})
+ end,
+ Acc = incr_counter(Op, Counters),
+
+ receive stop ->
+ [end_of_work | Acc]
+ after 0 ->
+ Acc
+ end
+ end,
+ FiniF = fun (Acc) -> Acc end,
+ Pids = run_sched_workers(InitF, ExecF, FiniF, infinite),
+ timer:send_after(500, stop),
+
+ Rounds = fun Loop(N, Fix) ->
+ ets:info(Tab, binary),
+ ets:safe_fixtable(Tab, Fix),
+ receive
+ stop ->
+ ets:safe_fixtable(Tab, false),
+ false = ets:info(Tab, fixed),
+ N
+ after 0 ->
+ Loop(N+1, not Fix)
+ end
+ end (1, true),
+ [P ! stop || P <- Pids],
+ Results = wait_pids(Pids),
+ Size = ets:info(Tab,size),
+ io:format("Ops = ~p\n", [maps_sum(Results)]),
+ io:format("Size = ~p\n", [Size]),
+ io:format("Stats = ~p\n", [ets:info(Tab,stats)]),
+ io:format("Rounds = ~p\n", [Rounds]),
+ Size = length(ets:info(Tab, binary)),
+
+ ets:delete_all_objects(Tab),
+ [] = ets:info(Tab, binary),
+ true = ets:delete(Tab),
+ ok.
+
+
size_loop(_T, 0, _, _) ->
ok;
size_loop(T, I, PrevSize, WhatToTest) ->
@@ -8014,7 +8131,7 @@ repeat_for_opts_atom2list(all_non_stim_types) -> [set,ordered_set,cat_ord_set,ba
repeat_for_opts_atom2list(all_non_stim_set_types) -> [set,ordered_set,cat_ord_set];
repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}];
repeat_for_opts_atom2list(read_concurrency) -> [{read_concurrency,false},{read_concurrency,true}];
-repeat_for_opts_atom2list(compressed) -> [compressed,void].
+repeat_for_opts_atom2list(compressed) -> [void,compressed].
is_redundant_opts_combo(Opts) ->
(lists:member(stim_cat_ord_set, Opts) orelse
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 16cf8f43f9..aa4d258cbf 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap,{minutes,1}}].
+ {timetrap,{seconds,10}}].
all() ->
[{group, start},
@@ -38,7 +38,8 @@ all() ->
{group, abnormal},
{group, abnormal_handle_event},
shutdown, stop_and_reply, state_enter, event_order,
- state_timeout, event_types, generic_timers, code_change,
+ state_timeout, timeout_cancel_and_update,
+ event_types, generic_timers, code_change,
{group, sys},
hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
undef_in_terminate].
@@ -518,10 +519,11 @@ abnormal2(Config) ->
?MODULE, start_arg(Config, []), [{debug,[log]}]),
%% bad return value in the gen_statem loop
- {{{bad_return_from_state_function,badreturn},_},_} =
+ Cause = bad_return_from_state_function,
+ {{{Cause,badreturn},_},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
receive
- {'EXIT',Pid,{{bad_return_from_state_function,badreturn},_}} -> ok
+ {'EXIT',Pid,{{Cause,badreturn},_}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -538,10 +540,11 @@ abnormal3(Config) ->
?MODULE, start_arg(Config, []), [{debug,[log]}]),
%% bad return value in the gen_statem loop
- {{{bad_action_from_state_function,badaction},_},_} =
+ Cause = bad_action_from_state_function,
+ {{{Cause,badaction},_},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badaction), Reason),
receive
- {'EXIT',Pid,{{bad_action_from_state_function,badaction},_}} -> ok
+ {'EXIT',Pid,{{Cause,badaction},_}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -559,10 +562,11 @@ abnormal4(Config) ->
%% bad return value in the gen_statem loop
BadTimeout = {badtimeout,4711,ouch},
- {{{bad_action_from_state_function,BadTimeout},_},_} =
- ?EXPECT_FAILURE(gen_statem:call(Pid, BadTimeout), Reason),
+ Cause = bad_action_from_state_function,
+ {{{Cause,BadTimeout},_},_} =
+ ?EXPECT_FAILURE(gen_statem:call(Pid, {badtimeout,BadTimeout}), Reason),
receive
- {'EXIT',Pid,{{bad_action_from_state_function,BadTimeout},_}} -> ok
+ {'EXIT',Pid,{{Cause,BadTimeout},_}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -822,7 +826,8 @@ state_timeout(_Config) ->
self() ! message_to_self,
{next_state, state1, {Time,From},
%% Verify that internal events goes before external
- [{state_timeout,Time,1},
+ [{timeout,Time,1}, % Exercise different cancel code path
+ {state_timeout,Time,1},
{next_event,internal,1}]}
end,
state1 =>
@@ -872,8 +877,9 @@ state_timeout(_Config) ->
{reply,From,ok}}
end},
- {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
- sys:trace(STM, true),
+ {ok,STM} =
+ gen_statem:start_link(
+ ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
TRef = erlang:start_timer(1000, self(), kull),
ok = gen_statem:call(STM, {go,500}),
ok = gen_statem:call(STM, check),
@@ -899,6 +905,88 @@ state_timeout(_Config) ->
+timeout_cancel_and_update(_Config) ->
+ process_flag(trap_exit, true),
+ %%
+ Machine =
+ #{init =>
+ fun () ->
+ {ok,start,0}
+ end,
+ start =>
+ fun
+ ({call,From}, test, 0) ->
+ self() ! message_to_self,
+ {next_state, state1, From,
+ %% Verify that internal events goes before external
+ [{state_timeout,17,1},
+ {next_event,internal,1}]}
+ end,
+ state1 =>
+ fun
+ (internal, 1, _) ->
+ {keep_state_and_data,
+ [{state_timeout,cancel},
+ {{timeout,a},17,1}]};
+ (info, message_to_self, _) ->
+ {keep_state_and_data,
+ [{{timeout,a},update,a}]};
+ ({timeout,a}, a, Data) ->
+ {next_state,state2,Data,
+ [{state_timeout,17,2},
+ {next_event,internal,2}]}
+ end,
+ state2 =>
+ fun
+ (internal, 2, _) ->
+ receive after 50 -> ok end,
+ %% Now state_timeout 17 should have triggered
+ {keep_state_and_data,
+ [{state_timeout,update,b},
+ {timeout,17,2}]};
+ (state_timeout, b, From) ->
+ {next_state,state3,3,
+ [{reply,From,ok},
+ 17000]}
+ end,
+ state3 =>
+ fun
+ ({call,From}, stop, 3) ->
+ {stop_and_reply, normal,
+ [{reply,From,ok}]}
+ end
+ },
+ %%
+ {ok,STM} =
+ gen_statem:start_link(
+ ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
+ ok = gen_statem:call(STM, test),
+ {status, STM, {module,gen_statem}, Info} = sys:get_status(STM),
+ ct:log("Status info: ~p~n", [Info]),
+ {_,Timeouts} = dig_data_tuple(Info),
+ {_, {1,[{timeout,17000}]}} = lists:keyfind("Time-outs", 1, Timeouts),
+ %%
+ ok = gen_statem:call(STM, stop),
+ receive
+ {'EXIT',STM,normal} ->
+ ok
+ after 500 ->
+ ct:fail(did_not_stop)
+ end,
+ %%
+ verify_empty_msgq().
+
+dig_data_tuple([{data,_} = DataTuple|_]) -> DataTuple;
+dig_data_tuple([H|T]) when is_list(H) ->
+ case dig_data_tuple(H) of
+ false -> dig_data_tuple(T);
+ DataTuple -> DataTuple
+ end;
+dig_data_tuple([_|T]) -> dig_data_tuple(T);
+dig_data_tuple([]) -> false.
+
+
+
%% Test that all event types can be sent with {next_event,EventType,_}
event_types(_Config) ->
process_flag(trap_exit, true),
@@ -1042,7 +1130,8 @@ generic_timers(_Config) ->
sys1(Config) ->
{ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
- {status, Pid, {module,gen_statem}, _} = sys:get_status(Pid),
+ {status, Pid, {module,gen_statem}, Info} = sys:get_status(Pid),
+ ct:log("Status info: ~p~n", [Info]),
sys:suspend(Pid),
Parent = self(),
Tag = make_ref(),
@@ -1892,7 +1981,7 @@ idle({call,_From}, badreturn, _Data) ->
badreturn;
idle({call,_From}, badaction, Data) ->
{keep_state, Data, [badaction]};
-idle({call,_From}, {badtimeout,_,_} = BadTimeout, Data) ->
+idle({call,_From}, {badtimeout,BadTimeout}, Data) ->
{keep_state, Data, BadTimeout};
idle({call,From}, {delayed_answer,T}, Data) ->
receive
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index 7685c17967..b3d8a7e5d3 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -1257,12 +1257,17 @@ short_jump(Config) when is_list(Config) ->
fun ({Alg,AlgState}) ->
{Alg,rand:exro928_jump_2pow20(AlgState)}
end),
- short_jump(
- crypto:rand_seed_alg_s(crypto_aes, integer_to_list(Seed)),
- fun ({Alg,AlgState}) ->
- {Alg,crypto:rand_plugin_aes_jump_2pow20(AlgState)}
- end),
- ok.
+ try crypto:strong_rand_bytes(1) of
+ _ ->
+ short_jump(
+ crypto:rand_seed_alg_s(crypto_aes, integer_to_list(Seed)),
+ fun ({Alg,AlgState}) ->
+ {Alg,crypto:rand_plugin_aes_jump_2pow20(AlgState)}
+ end),
+ ok
+ catch error:undef ->
+ {skip,no_crypto}
+ end.
short_jump({#{bits := Bits},_} = State_0, Jump2Pow20) ->
Range = 1 bsl Bits,
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 9370067910..36751c641b 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -2380,10 +2380,10 @@ scale_start_stop_many_children() ->
[N2, StartT2 div 1000, StopT2 div 1000]),
%% Scaling should be more or less linear, but allowing a bit more
- %% to avoid false alarms
+ %% to avoid false alarms (add 1 to avoid div zero)
ScaleLimit = (N2 div N1) * 10,
- StartScale = StartT2 div StartT1,
- StopScale = StopT2 div StopT1,
+ StartScale = StartT2 div (StartT1+1),
+ StopScale = StopT2 div (StopT1+1),
ct:log("Scale limit: ~w~nStart scale: ~w~nStop scale: ~w",
[ScaleLimit, StartScale, StopScale]),
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index c2f586fef5..e2ed11a3d2 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.9.2
+STDLIB_VSN = 3.10
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index a2dd78f280..31b3e45016 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Add missing calls to <c>erl_syntax:unwrap/1</c>. The
+ nodes concerned represent names and values of maps and
+ map types. </p>
+ <p>
+ Own Id: OTP-16012 Aux Id: PR-2348 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -60,6 +76,22 @@
</section>
+<section><title>Syntax_Tools 2.1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Add missing calls to <c>erl_syntax:unwrap/1</c>. The
+ nodes concerned represent names and values of maps and
+ map types. </p>
+ <p>
+ Own Id: OTP-16012 Aux Id: PR-2348 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -786,4 +818,3 @@
<p>Miscellaneous changes.</p>
</section>
</chapter>
-
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 0ace11772d..9e6967d45d 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 2.2
+SYNTAX_TOOLS_VSN = 2.2.1
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 09ae5ef04a..c1e664b10f 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 3.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p><c>cover</c> would fail to start if two processes
+ tried to start it at the exact same time.</p>
+ <p>
+ Own Id: OTP-15813 Aux Id: ERL-943 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 3.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 191a458c62..022332e840 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 3.2
+TOOLS_VSN = 3.2.1
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 0c3374091d..4de771d209 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.8.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a driver bug that could crashes when allocating
+ memory.</p>
+ <p>
+ Own Id: OTP-15883 Aux Id: PR-2261 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.8.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index 91d15de3a9..ec07f7b691 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.8.8
+WX_VSN = 1.8.9
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 9fb4a430e5..4355b7114a 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.22</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>xmerl_sax_parser</c> crashed during charset detection
+ when the xml declarations attribute values was missing
+ the closing quotation (&apos; or &quot;).</p>
+ <p>
+ Own Id: OTP-15826</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.21</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 08696606e6..fc73964773 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.21
+XMERL_VSN = 1.3.22
diff --git a/make/otp.mk.in b/make/otp.mk.in
index cc76f00e7e..63748c2f2b 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -321,5 +321,5 @@ $(MAN6DIR)/%.6 $(MAN6DIR)/%.7: $(XMLDIR)/%_app.xml
$(XMLDIR)/%.xml: $(XMLDIR)/%.xmlsrc
$(gen_verbose)escript $(DOCGEN)/priv/bin/codeline_preprocessing.escript $(shell pwd) $< $@
-.fo.pdf:
+$(PDFDIR)/%.pdf: %.fo
$(FOP) -c $(FOP_CONFIG) -cache $(ERL_TOP)/make/$(TARGET)/fop-fonts.cache -fo $< -pdf $@
diff --git a/make/otp_patch_solve_forward_merge_version b/make/otp_patch_solve_forward_merge_version
index b4de394767..48082f72f0 100644
--- a/make/otp_patch_solve_forward_merge_version
+++ b/make/otp_patch_solve_forward_merge_version
@@ -1 +1 @@
-11
+12
diff --git a/otp_versions.table b/otp_versions.table
index 47b95b7ee1..d55b21470d 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-22.1 : common_test-1.18 compiler-7.4.5 crypto-4.6 dialyzer-4.1 erl_docgen-0.10 erl_interface-3.13 erts-10.5 eunit-2.3.8 ftp-1.0.3 inets-7.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 os_mon-2.5.1 public_key-1.7 runtime_tools-1.14 sasl-3.4.1 snmp-5.4 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 # asn1-5.0.9 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 et-1.6.4 hipe-3.19.1 odbc-2.12.4 parsetools-2.1.8 reltool-0.8 tftp-1.0.1 :
OTP-22.0.7 : compiler-7.4.4 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 dialyzer-4.0.3 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19.1 inets-7.0.9 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.5 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.6 : compiler-7.4.3 dialyzer-4.0.3 hipe-3.19.1 ssl-9.3.5 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 inets-7.0.9 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.5 : dialyzer-4.0.2 erts-10.4.4 inets-7.0.9 ssl-9.3.4 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
@@ -6,6 +7,7 @@ OTP-22.0.3 : compiler-7.4.2 dialyzer-4.0.1 erts-10.4.2 ssl-9.3.2 stdlib-3.9.2 #
OTP-22.0.2 : compiler-7.4.1 crypto-4.5.1 erts-10.4.1 stdlib-3.9.1 # asn1-5.0.9 common_test-1.17.3 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.1 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.1 : ssl-9.3.1 # asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0 : asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3 stdlib-3.9 syntax_tools-2.2 tools-3.2 wx-1.8.8 xmerl-1.3.21 # diameter-2.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 parsetools-2.1.8 tftp-1.0.1 :
+OTP-21.3.8.7 : erts-10.3.5.5 inets-7.0.7.1 kernel-6.3.1.3 ssh-4.7.6.1 syntax_tools-2.1.7.1 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.3.5 stdlib-3.8.2.2 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
OTP-21.3.8.6 : ssl-9.2.3.5 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 erts-10.3.5.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1.2 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
OTP-21.3.8.5 : erts-10.3.5.4 ssl-9.2.3.4 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1.2 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
OTP-21.3.8.4 : common_test-1.17.2.1 erts-10.3.5.3 kernel-6.3.1.2 public_key-1.6.6.1 ssl-9.2.3.3 stdlib-3.8.2.2 # asn1-5.0.8 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
diff --git a/scripts/build-otp b/scripts/build-otp
index 55023ba7d8..afab1f9136 100755
--- a/scripts/build-otp
+++ b/scripts/build-otp
@@ -42,6 +42,7 @@ if [ ! -d "logs" ]; then
mkdir logs
fi
+export ERLC_USE_SERVER=yes
do_and_log "Autoconfing" ./otp_build autoconf
do_and_log "Configuring" ./otp_build configure
echo Configure result:
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index 23e9054547..360bf958ad 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -108,7 +108,9 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<item>
Co-located callback code for each state,
for all
- <seealso marker="#Event Types"><em>Event Types</em></seealso>
+ <seealso marker="#Event Types and Event Content">
+ <em>Event Types</em>
+ </seealso>
(such as <em>call</em>, <em>cast</em> and <em>info</em>)
</item>
<item>
@@ -136,7 +138,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
(<seealso marker="#State Time-Outs">State Time-Outs</seealso>,
<seealso marker="#Event Time-Outs">Event Time-Outs</seealso>
and
- <seealso marker="#Generic Time-Outs">Generic Time-outs</seealso>
+ <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>
(named time-outs))
</item>
</list>
@@ -353,9 +355,10 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</taglist>
<p>
The state is either the name of the function itself or an argument to it.
- The other arguments are the <c>EventType</c> described in section
- <seealso marker="#Event Types">Event Types</seealso>,
- the event dependent <c>EventContent</c>,
+ The other arguments are the <c>EventType</c>
+ and the event dependent <c>EventContent</c>,
+ both described in section
+ <seealso marker="#Event Types and Event Content">Event Types and Event Content</seealso>,
and the current server <c>Data</c>.
</p>
<p>
@@ -561,34 +564,48 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-state_timeout">
- <c>{state_timeout, EventContent, Time}</c>
+ <c>{state_timeout, Time, EventContent}</c>
+ </seealso>
+ <br />
+ <c>{state_timeout, Time, EventContent, Opts}</c><br />
+ <seealso marker="stdlib:gen_statem#type-timeout_update_action">
+ <c>{state_timeout, update, EventContent}</c>
</seealso>
<br />
- <c>{state_timeout, EventContent, Time, Opts}</c>
+ <seealso marker="stdlib:gen_statem#type-timeout_cancel_action">
+ <c>{state_timeout, cancel}</c>
+ </seealso>
</tag>
<item>
- Start a state time-out, read more in sections
+ Start, update or cancel a state time-out, read more in sections
<seealso marker="#Time-Outs">Time-Outs</seealso> and
<seealso marker="#State Time-Outs">State Time-Outs</seealso>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-generic_timeout">
- <c>{{timeout, Name}, EventContent, Time}</c>
+ <c>{{timeout, Name}, Time, EventContent}</c>
+ </seealso>
+ <br />
+ <c>{{timeout, Name}, Time, EventContent, Opts}</c><br />
+ <seealso marker="stdlib:gen_statem#type-timeout_update_action">
+ <c>{{timeout, Name}, update, EventContent}</c>
</seealso>
<br />
- <c>{{timeout, Name}, EventContent, Time, Opts}</c>
+ <seealso marker="stdlib:gen_statem#type-timeout_cancel_action">
+ <c>{{timeout, Name}, cancel}</c>
+ </seealso>
</tag>
<item>
- Start a generic time-out, read more in sections
+ Start, update or cancel a generic time-out, read more in sections
<seealso marker="#Time-Outs">Time-Outs</seealso> and
<seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-event_timeout">
- <c>{timeout, EventContent, Time}</c>
+ <c>{timeout, Time, EventContent}</c>
</seealso>
<br />
- <c>{timeout, EventContent, Time, Opts}</c><br />
+ <c>{timeout, Time, EventContent, Opts}</c><br />
<c>Time</c>
</tag>
<item>
@@ -624,19 +641,42 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
and set a time-out to use absolute instead of relative time
(using the <c>Opts</c> field).
</p>
+ <p>
+ Among these <em>transition actions</em> only to reply to a caller
+ is an immediate action. The others are collected and handled
+ later during the <em>state transition</em>.
+ <seealso marker="#Inserted Events">Inserted Events</seealso>
+ are stored and inserted all together,
+ and the rest set transition options
+ where the last of a specific type override the previous.
+ See the description of a <em>state transition</em>
+ in the <c>gen_statem(3)</c> manual page for type
+ <seealso marker="stdlib:gen_statem#type-transition_option"><c>transition_option()</c></seealso>.
+ </p>
+ <p>
+ The different
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#Inserted Events"><c>next_event</c></seealso>
+ actions generate new events with corresponding
+ <seealso marker="#Event Types and Event Content">
+ Event Types and Event Content
+ </seealso>.
+ </p>
</section>
<!-- =================================================================== -->
<section>
- <marker id="Event Types" />
- <title>Event Types</title>
+ <marker id="Event Types and Event Content" />
+ <title>Event Types and Event Content</title>
<p>
Events are categorized in different
<seealso marker="stdlib:gen_statem#type-event_type"><em>event types</em></seealso>.
Events of all types are for a given state
handled in the same callback function, and that function gets
<c>EventType</c> and <c>EventContent</c> as arguments.
+ The meaning of the <c>EventContent</c>
+ depends on the <c>EventType</c>.
</p>
<p>
The following is a complete list of <em>event types</em> and where
@@ -650,7 +690,10 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</tag>
<item>
Generated by
- <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast</c></seealso>.
+ <seealso marker="stdlib:gen_statem#cast/2">
+ <c>gen_statem:cast(ServerRef, Msg)</c>
+ </seealso>
+ where <c>Msg</c> becomes the <c>EventContent</c>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-external_event_type">
@@ -659,11 +702,17 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</tag>
<item>
Generated by
- <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>,
- where <c>From</c> is the reply address to use
+ <seealso marker="stdlib:gen_statem#call/2">
+ <c>gen_statem:call(ServerRef, Request)</c>
+ </seealso>
+ where <c>Request</c> becomes the <c>EventContent</c>.
+ <c>From</c> is the reply address to use
when replying either through the <em>transition action</em>
- <c>{reply,From,Msg}</c> or by calling
- <seealso marker="stdlib:gen_statem#reply/1"><c>gen_statem:reply</c></seealso>.
+ <c>{reply,From,Reply}</c> or by calling
+ <seealso marker="stdlib:gen_statem#reply/1">
+ <c>gen_statem:reply(From, Reply)</c>
+ </seealso>
+ from the <em>callback module</em>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-external_event_type">
@@ -673,6 +722,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<item>
Generated by any regular process message sent to
the <c>gen_statem</c> process.
+ The process message becomes the <c>EventContent</c>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-timeout_event_type">
@@ -724,7 +774,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</tag>
<item>
Generated by <em>transition action</em>
- <seealso marker="stdlib:gen_statem#type-enter_action"><c>{next_event,internal,EventContent}</c></seealso>.
+ <seealso marker="stdlib:gen_statem#type-action"><c>{next_event,internal,EventContent}</c></seealso>.
All <em>event types</em> above can also be generated using
the <c>next_event</c> action:
<c>{next_event,EventType,EventContent}</c>.
@@ -793,7 +843,7 @@ StateName(EventType, EventContent, Data) ->
<section>
<marker id="Time-Outs" />
- <title>Time-outs</title>
+ <title>Time-Outs</title>
<p>
Time-outs in <c>gen_statem</c> are started from a
<seealso marker="#Transition Actions">
@@ -844,9 +894,9 @@ StateName(EventType, EventContent, Data) ->
</item>
</taglist>
<p>
- When a time-out is started any running time-out with the same tag,
+ When a time-out is started any running time-out of the same type;
<c>state_timeout</c>, <c>{timeout, Name}</c> or <c>timeout</c>,
- is cancelled, that is the time-out is restarted with the new time.
+ is cancelled, that is, the time-out is restarted with the new time.
</p>
<p>
All time-outs has got an <c>EventContent</c> that is part of the
@@ -869,6 +919,39 @@ StateName(EventType, EventContent, Data) ->
The <c>EventContent</c> will in this case be ignored,
so why not set it to <c>undefined</c>.
</p>
+ <p>
+ A more explicit way to cancel a timer is to use a
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ on the form
+ <seealso marker="stdlib:gen_statem#type-timeout_cancel_action">
+ <c>{TimeoutType, cancel}</c>
+ </seealso>
+ which is a feature introduced in OTP 22.1.
+ </p>
+ </section>
+ <section>
+ <marker id="Updating a Time-Out" />
+ <title>Updating a Time-Out</title>
+ <p>
+ While a time-out is running, its <c>EventContent</c>
+ can be updated using a
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ on the form
+ <seealso marker="stdlib:gen_statem#type-timeout_update_action">
+ <c>{TimeoutType, update, NewEventContent}</c>
+ </seealso>
+ which is a feature introduced in OTP 22.1.
+ </p>
+ <p>
+ If this feature is used while no such <c>TimeoutType</c>
+ is running then a time-out event is immediately delivered
+ as when starting a
+ <seealso marker="#Time-Out Zero">Time-Out Zero</seealso>.
+ </p>
</section>
<section>
<marker id="Time-Out Zero" />
@@ -1200,10 +1283,12 @@ open(state_timeout, lock, Data) ->
<p>
The timer for a state time-out is automatically cancelled
when the state machine does a <em>state change</em>.
- You can restart a state time-out by setting it to a new time,
- which cancels the running timer and starts a new.
- This implies that you can cancel a state time-out
- by restarting it with time <c>infinity</c>.
+ </p>
+ <p>
+ You can restart, cancel or update a state time-out.
+ See section
+ <seealso marker="#Time-Outs">Time-Outs</seealso>
+ for details.
</p>
</section>
@@ -1472,12 +1557,13 @@ locked(
<p>
An event time-out is cancelled by any other event so you either
get some other event or the time-out event. It is therefore
- not possible nor needed to cancel or restart an event time-out.
- Whatever event you act on has already cancelled
- the event time-out...
+ not possible nor needed to cancel, restart or update an event time-out.
+ Whatever event you act on has already cancelled the event time-out,
+ so there is never a running event time-out
+ while the <em>state callback</em> executes.
</p>
<p>
- Note that an event time-out does not work well with
+ Note that an event time-out does not work well
when you have for example a status call as in section
<seealso marker="#All State Events">All State Events</seealso>,
or handle unknown events, since all kinds of events
@@ -1548,6 +1634,12 @@ open(cast, {button,_}, Data) ->
a late time-out event can be handled by ignoring it
if it arrives in a state where it is known to be late.
</p>
+ <p>
+ You can restart, cancel or update a generic time-out.
+ See section
+ <seealso marker="#Time-Outs">Time-Outs</seealso>
+ for details.
+ </p>
</section>
<!-- =================================================================== -->
@@ -1858,7 +1950,7 @@ open(state_timeout, lock, Data) ->
<seealso marker="#Transition Actions">
<em>transition action</em>
</seealso>
- <c>{next_event,EventType,EventContent}</c>.
+ <seealso marker="stdlib:gen_statem#type-action"><c>{next_event,EventType,EventContent}</c></seealso>.
</p>
<p>
You can generate events of any existing