summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--OTP_VERSION2
-rw-r--r--erts/doc/src/erl_nif.xml2
-rw-r--r--erts/doc/src/notes.xml361
-rw-r--r--erts/doc/src/socket.xml36
-rw-r--r--erts/emulator/beam/erl_db_hash.c6
-rw-r--r--erts/emulator/beam/erl_process.c12
-rw-r--r--erts/emulator/beam/erl_process.h1
-rw-r--r--erts/emulator/beam/external.c4
-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/etc/unix/cerl.src9
-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/ct_netconfc.erl1564
-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/notes.xml35
-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.xml6
-rw-r--r--lib/kernel/doc/src/notes.xml113
-rw-r--r--lib/kernel/src/kernel.appup.src6
-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/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/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/ssl.erl4
-rw-r--r--lib/ssl/vsn.mk2
-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_lint.erl8
-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/erl_lint_SUITE.erl22
-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
-rw-r--r--system/doc/design_principles/statem.xml158
114 files changed, 4666 insertions, 2725 deletions
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/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 2283cee2b5..4eae346ead 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -52,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
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/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index c6b6e66e3e..5937bd64ec 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -3216,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) {
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index a564a1d7c5..ac0a4f8902 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -12073,6 +12073,7 @@ 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);
@@ -12119,10 +12120,12 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
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;
@@ -12334,6 +12337,7 @@ 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;
@@ -12373,10 +12377,12 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
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;
@@ -12799,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);
@@ -12901,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;
}
@@ -12916,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;
@@ -13001,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 13cc2ce1fa..5886b576e0 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -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/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/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/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index 59de9bdec8..d85a36acd4 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -315,8 +315,13 @@ if [ "x$GDB" = "x" ]; then
ncpu=`cat /proc/cpuinfo | grep -w processor | wc -l`
# Choose a random core in order to not collide with any other valgrind
# run on the same machine.
- taskset1=$((1 << (`shuf -i 1-$ncpu -n 1` - 1) ))
- taskset1="taskset $taskset1"
+ cpu=`shuf -i 1-$ncpu -n 1`
+ mask=1
+ while [ $cpu -gt 1 ]; do
+ mask=`expr $mask \* 2`
+ cpu=`expr $cpu - 1`
+ done
+ taskset1="taskset $mask"
sched_arg="-S$ncpu:$ncpu"
else
taskset1=
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/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/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/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/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 3f03104734..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>
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/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/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/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/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/ssl.erl b/lib/ssl/src/ssl.erl
index c6c94ee853..0d6501b5ee 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -1609,7 +1609,9 @@ handle_options(Opts0, Role, Host) ->
proplists:delete(Key, PropList)
end, Opts, ?SSL_OPTIONS ++
[ssl_imp, %% TODO: remove ssl_imp
- client_preferred_next_protocols]), %% next_protocol_selector
+ client_preferred_next_protocols, %% next_protocol_selector
+ log_alert,
+ cb_info]),
{Sock, Emulated} = emulated_options(Protocol, SockOpts),
ConnetionCb = connection_cb(Opts),
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/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_lint.erl b/lib/stdlib/src/erl_lint.erl
index 0cd0aef124..d6cb57e392 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -3371,9 +3371,11 @@ handle_comprehension(E, Qs, Vt0, St0) ->
Vt3 = vtmerge(vtsubtract(Vt2, Uvt), Uvt),
%% Don't export local variables.
Vt4 = vtold(Vt3, Vt0),
- %% Forget about old variables which were not used.
- Vt5 = vt_no_unused(Vt4),
- {Vt5,St}.
+ %% Forget about old variables which were not used as well as unsafe
+ %% variables, preventing them from being marked as used and bound by
+ %% icrt_export/4.
+ Vt = vt_no_unsafe(vt_no_unused(Vt4)),
+ {Vt, St}.
%% lc_quals(Qualifiers, ImportVarTable, State) ->
%% {VarTable,ShadowedVarTable,State}
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 22d2c65cf6..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:OTP-16002@","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/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index e7882e0daf..a8ed4b19db 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1018,7 +1018,27 @@ unsafe_vars(Config) when is_list(Config) ->
{errors,[{24,erl_lint,{unsafe_var,'A',{'catch',4}}},
{24,erl_lint,{unsafe_var,'B',{'case',2}}},
{24,erl_lint,{unsafe_var,'D',{'case',2}}}],
- []}}
+ []}},
+ {unsafe_comprehension,
+ <<"foo() ->
+ case node() of
+ P when is_tuple(P) ->
+ P;
+ _ ->
+ ok
+ end,
+ Y = try
+ ok
+ catch _C:_R ->
+ [1 || _ <- []]
+ end,
+ case Y of
+ P ->
+ P
+ end.
+ ">>,
+ [],
+ {errors,[{14,erl_lint,{unsafe_var,'P',{'case',2}}}],[]}}
],
[] = run(Config, Ts),
ok.
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/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